Files
claude_mac/.claude/tasks/plans/2026-10-07-manual-push-skills-c2-1325.md
T

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. If ahead ≠ 0 and origin exists: bash "$HOME/.claude/lib/gitflow.sh" push-mode → anything other than auto is treated like manual (stderr line kept verbatim when invalid). 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 or unknown → pending — you: ! git push -u origin <br> + reason: push mode manual → (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 runs git push itself." 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 is pendingthe 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 "Workflow runs on your push…"; whenon origin, keep "Push has been done. …". After option A "Deployed" (~648): "Re-run PUSH STATE READ; still pending→ 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 ~65 3. 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 "main and develop are already on origin" through the hold line (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 auto from the verb (manual, invalid, empty, usage error) OR either count ≠ 0 or unknown → 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: say main/develop not on origin (no remote-tracking ref or the lib's push did not land); no origin remote (git remote get-url origin fails): say add an origin remote first).
    • push mode auto and 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-tags on every lib and hook push)." Overview lines ~27-28 (multi-line anchor and 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).
  • 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 pushed mainanddevelopthrough 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 a push 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; else local 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 returns BRANCH: `. 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 mode on 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 -C too; 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 config in skills; zero git push inside 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).