11 KiB
PLAN — manual-push-skills-c2 — REVISED r2 (3 lenses + correctness confirmation)
Contract: .claude/tasks/contracts/2026-10-07-manual-push-skills-c2-1325.md
Context
Same pattern as C1: since BDR-095 the hooks push every commit in auto-push mode, so a skill's own git push (and the question that gates it) gates nothing in auto mode, and in manual/invalid mode push-guard denies it. Truth about "on origin" comes from a FACT read after the fact — git rev-list --count origin/<br>..<br> (0 = on origin; >0 = not; unknown = no remote-tracking ref) — never from the mode word (the lib still pushes on an invalid value until run D). The verb gitflow.sh push-mode (C1) only WORDS the explanation (manual vs push FAILED) and is read in its own Bash call; no shell variable crosses calls. Where a user must push, the hint is a complete ! git … command with -u and, for multi-repo flows, -C <abs path>.
Checklist
- agents/client-handover-writer.md — define ONE reusable paragraph "PUSH STATE READ" (insert it once, right after the commit-change dispatch in STEP 5, and REFER to it elsewhere): "Three separate Bash calls, never combined, read-only:
git branch --show-current→<br>;git remote get-url origin >/dev/null 2>&1 && echo origin || echo no-origin;git rev-list --count origin/<br>..<br> 2>/dev/null || echo unknown→ahead. Ifahead≠ 0 and origin exists:bash "$HOME/.claude/lib/gitflow.sh" push-mode→ anything other thanautois treated likemanual(stderr line kept verbatim wheninvalid). State:ahead= 0 →on origin; no commits were made this run or the gitflow fallback left changes uncommitted →nothing to push (no commits this run)/uncommitted changes (no gitflow model): publish by hand;no-origin→not on origin (no origin remote: add one first);ahead> 0 orunknown→pending — you: ! git push -u origin <br>+ reason: push modemanual→(manual push mode),auto→(not on origin: no remote-tracking ref or the hook push did not land),invalid→(<verb stderr line verbatim>). The pipeline never runsgit pushitself." Then: STEP 5: replace ONLY lines ~570-578 (from "Then, before pushing, STOP and ask for an explicit GO" through the "Only on A … then continue." paragraph) with the PUSH STATE READ paragraph followed by: "pending→ tell the user NOW:Commits are local only. Push first: ! git push -u origin <br>." KEEP the red-flag box (~580-582) and reword it (multi-line old_string, exact current text:> **Red flag — STOP:** never \git push` without option-A GO; never\n> `gitflow finish`/`merge`. This pipeline commits and (on GO) pushes a working\n> branch — it never integrates into a protected branch.) →> Red flag — STOP: never `git push` (the hooks push in auto-push mode;\n> otherwise the user does); never `gitflow finish`/`merge`. This pipeline\n> commits a working branch — it never integrates into a protected branch.Then DELETE lines ~584-598 (theCURRENT_BRANCH=…/git push originbash block and the "If push fails …" AskUserQuestion block). STEP 6: FIRST line of STEP 6 (before "Skip if PROJECT_TYPE != web"): "Re-run PUSH STATE READ (every path reaches STEP 6, some without STEP 5's read)." Deploy brief (lines ~626-631, multi-line anchors:"Push has been\n done. The platform deploys automatically — usually 1-3 min. Watch the\n dashboard.): when the state ispendingthe brief OPENS withFirst push: ! git push -u origin; the Vercel/Netlify/Cloudflare line reads "The platform deploys automatically after your push (a working branch gives a preview at most; production builds from the production branch) — usually 1-3 min…"; the CI line "Workflowruns on your push…"; whenon origin, keep "Push has been done. …". After option A "Deployed" (~648): "Re-run PUSH STATE READ; stillpending→ ask again (the live site cannot hold these commits)." Reports: PIPELINE STOPPED template (~795-810) gains a line at column 0Push:after the Score table; the 9.7 user report gains a bullet- Push:; both re-run PUSH STATE READ right before printing (never a STEP 5 snapshot). Line ~653. Commit + push if files changed.→3. Commit if files changed (the hooks push in auto-push mode; the push state is read, never assumed).; lines ~686-687(mini-commit\n+ push)→(mini-commit; push state read, never assumed)`. - skills/client-handover/SKILL.md step 4 —
run /commit-change (atomic logical commits) then \git push`.→run /commit-change (atomic logical commits); the gitflow hooks push in auto-push mode, otherwise (manual push mode, or a hook push that failed) the agent tells the user to push with `! git push -u origin ` BEFORE the deploy pause.` - skills/release-candidate/SKILL.md STEP 6 — replace the paragraph from "
mainanddevelopare already on origin" through theholdline (lines ~96-108) with: "Read the state, separate Bash calls:git rev-list --count origin/main..main 2>/dev/null || echo unknown,git rev-list --count origin/develop..develop 2>/dev/null || echo unknown,bash "$HOME/.claude/lib/gitflow.sh" push-mode.- anything other than
autofrom the verb (manual, invalid, empty, usage error) OR either count ≠ 0 orunknown→ Claude pushes nothing (push-guard would refuse it in manual mode; a failed lib push is the user's call, BDR-095). Print ONE command for the user and STOP, no question:! git push --atomic origin main develop v<X.Y.Z>(invalid: quote the verb's stderr line verbatim; auto with a count ≠ 0 or unknown: saymain/develop not on origin (no remote-tracking ref or the lib's push did not land); no origin remote (git remote get-url originfails): sayadd an origin remote first). - push mode
autoand both counts 0 → main and develop are on origin; only the tag is left. STOP. On explicit go only (LRN-069) — run the tag push HERE, never delegated:AskUserQuestion: Push tag v<X.Y.Z> to origin? — go / hold. Go →bash\ngit push origin v<X.Y.Z>\n.hold→ stop; the release is on origin (main + develop), the tag stays local until the next push of main (--follow-tagson every lib and hook push)." Overview lines ~27-28 (multi-line anchorand the two human gates (when to release, and\nthe tag push).) → append " (auto-push mode; in manual push mode the user pushes main, develop and the tag in one command)". Common-mistakes bullet list: add- Pushing anything in manual push mode → print the one user command, push nothing.Frontmatter description ("tag it, and push") and the STEP 6 heading stay (frozen, residuals).
- anything other than
- agents/release-executor.md — lines ~80-82 (multi-line anchor:
Finish has already pushed \main` and\n `develop` through the lib's hooks (BDR-095); the tag stays local until\n the dispatcher's tag-push gate.) → "In auto-push mode finish has already pushedmainanddevelopthrough the lib (BDR-095); in manual push mode they stay local. The tag stays local"; lines ~85-86 (anchor`main`/`develop` ride the lib's hook\npushes during finish;) → "main/develop` ride the lib's pushes during finish in auto-push mode". - skills/tour/SKILL.md — Rules (lines ~273-275, multi-line anchor:
The chore branch's own commits are pushed by the gitflow hooks\n (BDR-095); a \push FAILED` hook warning is a report residual, fixed\n with a plain `git push -u origin chore/tour-`.) → " The gitflow hooks push the chore branch in auto-push mode only; when it is not on origin (manual push mode, or apush FAILEDwarning) the USER pushes it —! git -C push -u origin— the tour never pushes or retries." STEP 3 per-project closing list (~228-239): add item 5 AFTER thedocs(tour): reportcommit (3.3, the last commit): "5. Push state, one read-only call:git -C rev-list --count --not --remotes 2>/dev/null || echo unknown(= the namegitflow startreturned, suffixed-2/-3on a same-day re-run — never the barechore/tour-). 0 →on origin; elselocal only → ! git -C push -u origin(no origin remote →local only (no origin remote))." Summary row format (~258-259): after, commitsappend| on originor| local only → ! git -C push -u origin. Runner prompt (~92-100) unchanged: the row format carries the field and the runner already returnsBRANCH: `. No verb read in the tour.
Edge cases
unknown(never fetched) → "not on origin (no remote-tracking ref)"; no origin remote → "add an origin remote first" (the! git push … origin …hint would fail); never "push FAILED".ahead= 0 → "on origin" with no claim about WHO pushed (in manual mode it was the user).- Every
Push:/deploy-brief statement re-reads the fact right before it prints (a STEP 5 snapshot is stale once the user pushed); STEP 6 reads it first because three paths reach STEP 6 without STEP 5's read (no pending changes; gitflow fallback;--skip-audits). - AC substrings must each sit on ONE physical line (line-based greps);
Push:at column 0 inside the PIPELINE STOPPED fence;auto-push modeon two distinct lines in release-executor.md. - Invalid value: never "not pushed" from the mode; the counts decide; the verb's stderr is quoted verbatim (it may say "could not read" without a value).
- Release command is
--atomic: a non-fast-forward on main rejects the whole set, so the tag never lands without its merge. - client-handover-writer runs INLINE in the main loop (SKILL.md:29-33): the prose reaches the pusher. commit-change and handover-doc-writer never push.
- Tour runners are sub-agents using
git -C <abs project>: the fact call uses-Ctoo; the user hint carries the path (same branch name across projects). - Removed gates (client-handover GO question, release "on origin" claim) were gating nothing in auto mode: the hooks had pushed already (same redundancy C1 removed in /close). LRN-069's push gate now means: Claude never pushes in these flows except the release tag on explicit go in auto mode.
- Residual (frozen by AC6): release-candidate frontmatter "tag it, and push", STEP 6 heading "Tag push GATE (ASK)" — true in auto mode; listed in the CHANGELOG at doc-sync.
Disposition
- honors BDR-095 (truth from the remote state; a failed push is the user's decision), BDR-111/BDR-112 (verb for wording only, read in its own call; zero
git configin skills; zerogit pushinside a Bash call reachable in manual mode), BDR-042 (tag + its gate stay in the dispatcher), LRN-069 (explicit go kept for the one push Claude still makes: the tag, auto mode), LRN-193 (fresh confirmation pass after this revision), LRN-104 (every user-facing string is in the skill text; no runtime test exists for prose — AC6 is the reading gate).