docs(skills): invalid autopush value is fail-closed everywhere; prose aligned

Run D3 of manual-push mode (BDR-114). With every reader now failing
closed, the skill prose stops saying the lib and hooks still push on an
invalid value:

- capitalize STEP 5C / STEP 6: the invalid outcome is split on the ahead
  count (nothing pushed vs pushed anyway by a stale fail-open hook or a
  manual push); the verb's stderr line is quoted verbatim; neighbouring
  closing lines carry push-mode qualifiers so none shadows the invalid
  case; the --no-push lines follow the same rule.
- client-handover: "COMMIT + PUSH" labels become "COMMIT + PUSH STATE
  READ"; the STEP 5 residual sentences no longer imply the pipeline
  pushes; the invalid value is named as a case where the user pushes.
- release-executor: prep span checks the version format by reading the
  string (never in a Bash command); manual mode and an invalid value
  both leave main/develop local.
This commit is contained in:
bchanot
2026-10-07 17:11:58 +02:00
parent 3c59333fcf
commit 64ca0f8e09
4 changed files with 21 additions and 16 deletions
+7 -7
View File
@@ -533,7 +533,7 @@ After loops finish (success, stall, or override), capture:
--- ---
## STEP 5 — COMMIT + PUSH (only if files changed) ## STEP 5 — COMMIT + PUSH STATE READ (only if files changed)
```bash ```bash
CHANGED_DURING_PIPELINE=$(git diff --name-only "$PIPELINE_BASE_SHA"..HEAD) CHANGED_DURING_PIPELINE=$(git diff --name-only "$PIPELINE_BASE_SHA"..HEAD)
@@ -542,18 +542,18 @@ PENDING_CHANGES=$(git status --porcelain)
If both empty → skip to STEP 6. If both empty → skip to STEP 6.
**Gitflow precondition (report-only fallback).** Before any commit or push, **Gitflow precondition (report-only fallback).** Before any commit,
confirm this is a gitflow repo: confirm this is a gitflow repo (the pipeline never pushes):
```bash ```bash
git rev-parse --verify -q develop >/dev/null 2>&1 && echo DEVELOP_OK git rev-parse --verify -q develop >/dev/null 2>&1 && echo DEVELOP_OK
[ -f "$HOME/.claude/lib/gitflow.sh" ] && echo LIB_OK [ -f "$HOME/.claude/lib/gitflow.sh" ] && echo LIB_OK
``` ```
If `develop` is missing OR the gitflow lib is unavailable → **do NOT commit, If `develop` is missing OR the gitflow lib is unavailable → **do NOT commit
do NOT push.** Leave the changes in the working tree and record in the STEP 8 (and never push).** Leave the changes in the working tree and record in the
summary: "Commit/push skipped — no gitflow model in this repo; publish the STEP 8 summary: "Commit skipped — no gitflow model in this repo; publish the
listed changes manually before deploy." Continue to STEP 6. listed changes by hand before deploy." Continue to STEP 6.
If `PENDING_CHANGES` non-empty → invoke /commit-change skill via subagent: If `PENDING_CHANGES` non-empty → invoke /commit-change skill via subagent:
+4 -1
View File
@@ -31,6 +31,9 @@ stop and report — never chain into the other span yourself.
### Input ### Input
`<X.Y.Z>`: the version number, already decided by the dispatcher before `<X.Y.Z>`: the version number, already decided by the dispatcher before
dispatch — you never derive it, never second-guess it, never bump it. dispatch — you never derive it, never second-guess it, never bump it.
Format check only, by reading the string (never inside a Bash command):
<X.Y.Z> must match ^[0-9]+\.[0-9]+\.[0-9]+$ (literal regex text, single
backslashes); anything else → STATUS: BLOCKED, nothing created.
### Steps ### Steps
1. `bash "$HOME/.claude/lib/gitflow.sh" start release <X.Y.Z>` — forks from 1. `bash "$HOME/.claude/lib/gitflow.sh" start release <X.Y.Z>` — forks from
@@ -80,7 +83,7 @@ actual branch; never finish whatever happens to be checked out.
main's release-merge commit). In auto-push mode finish pushes `main` main's release-merge commit). In auto-push mode finish pushes `main`
and `develop` (best effort: the lib warns and returns 0 on a failed and `develop` (best effort: the lib warns and returns 0 on a failed
push; the dispatcher re-verifies with ahead counts) (BDR-095); in push; the dispatcher re-verifies with ahead counts) (BDR-095); in
manual push mode they stay local. The tag stays local until the manual push mode, or with an invalid gitflow.autopush, they stay local. The tag stays local until the
dispatcher's tag-push gate. dispatcher's tag-push gate.
### Forbidden in this span ### Forbidden in this span
+9 -7
View File
@@ -343,7 +343,8 @@ Otherwise, from the `chore/<name>` branch, THREE separate Bash calls, never comb
Outcomes, evaluated IN THIS ORDER (finish rc 0, or rc 5/2/6 with the branch merged — the wording then starts with `merged, branch not deleted (rc <n>) —` instead of `merged to develop —`): Outcomes, evaluated IN THIS ORDER (finish rc 0, or rc 5/2/6 with the branch merged — the wording then starts with `merged, branch not deleted (rc <n>) —` instead of `merged to develop —`):
- **push mode `invalid`** → `merged to develop — gitflow.autopush=<value from stderr> is not a boolean: the lib and hooks still push on an invalid value until run D (origin/develop is <ahead> commit(s) behind, or unknown); fix the value by hand`. - **push mode `invalid`, `ahead` > 0 or unknown** → `merged to develop — <verb stderr line verbatim>: treated as manual push mode by every reader, nothing pushed (origin/develop is <ahead> commit(s) behind, or unknown). Fix the value by hand, then: ! git push origin develop` (append ` once a remote exists` when `ahead` is unknown).
- **push mode `invalid`, `ahead` = 0** → `merged to develop — <verb stderr line verbatim>: pushed anyway, a hook in this repo still fails open (likely a stale .githooks/: a session-start reconcile refreshes it, commit the refresh) or a manual push. Fix the value by hand.`
- **`ahead` = 0** → `develop <short> pushed` (auto-push mode did it). - **`ahead` = 0** → `develop <short> pushed` (auto-push mode did it).
- **`ahead` = unknown** → `merged to develop — not on origin (no origin/develop ref; no remote or never fetched)`; push mode manual → add `You: ! git push origin develop once a remote exists`. - **`ahead` = unknown** → `merged to develop — not on origin (no origin/develop ref; no remote or never fetched)`; push mode manual → add `You: ! git push origin develop once a remote exists`.
- **`ahead` > 0, push mode `manual`** → `merged to develop — manual push mode: not pushed. You: ! git push origin develop`. - **`ahead` > 0, push mode `manual`** → `merged to develop — manual push mode: not pushed. You: ! git push origin develop`.
@@ -360,7 +361,7 @@ CAPITALIZE COMPLETE — <YYYY-MM-DD> (<pre-wipe flush | session-close>)
TODO.md : checked <N>, added <M> TODO.md : checked <N>, added <M>
journal.md : +1 line under ## <date> journal.md : +1 line under ## <date>
committed : <mem_hash> (chore(memory): …) | ⚠️ NOT committed (rc 3 — see closing line) committed : <mem_hash> (chore(memory): …) | ⚠️ NOT committed (rc 3 — see closing line)
persisted : develop <short> pushed | merged, manual push mode: not pushed | merged, not on origin (no origin/develop) | merged, push FAILED | merged, gitflow.autopush invalid (<ahead> behind) | finish rc <n>, not merged | merged, branch not deleted (rc <n>) | on chore/<name>, not merged (--no-push) persisted : develop <short> pushed | merged, manual push mode: not pushed | merged, not on origin (no origin/develop) | merged, push FAILED | merged, autopush invalid, nothing pushed (<ahead> behind) | merged, autopush invalid, pushed anyway (stale hook or manual push) | finish rc <n>, not merged | merged, branch not deleted (rc <n>) | on chore/<name>, not merged (--no-push)
dropped as already-captured: LRN-023, BLK-006 dropped as already-captured: LRN-023, BLK-006
ignored as noise: push/tag release ignored as noise: push/tag release
``` ```
@@ -368,15 +369,16 @@ CAPITALIZE COMPLETE — <YYYY-MM-DD> (<pre-wipe flush | session-close>)
Then the closing line — pick by the STEP 5C persist result (`<mode>` = `Context Then the closing line — pick by the STEP 5C persist result (`<mode>` = `Context
flushed` for pre-wipe, `Session closed` for ritual): flushed` for pre-wipe, `Session closed` for ritual):
- **auto-persisted (5C: finish rc 0 AND `ahead` = 0)** → `✅ <mode> + persisted to origin/develop (<short>). Next session: read .claude/memory/ at startup.` - **auto-persisted (push mode `auto`, finish rc 0 AND `ahead` = 0)** → `✅ <mode> + persisted to origin/develop (<short>). Next session: read .claude/memory/ at startup.`
On the `--no-push` path ONLY read TWO facts first, each its own Bash call: `bash "$HOME/.claude/lib/gitflow.sh" push-mode` and `git rev-list --count origin/chore/<name>..chore/<name> 2>/dev/null || echo unknown` (`branch_ahead`). `<push mode>` below is the verb's word. The WORKING-branch and rc 3 paths have no `chore/<name>` and never use the mode. On the `--no-push` path ONLY read TWO facts first, each its own Bash call: `bash "$HOME/.claude/lib/gitflow.sh" push-mode` and `git rev-list --count origin/chore/<name>..chore/<name> 2>/dev/null || echo unknown` (`branch_ahead`). `<push mode>` below is the verb's word. The WORKING-branch and rc 3 paths have no `chore/<name>` and never use the mode.
- **--no-push, `branch_ahead` = 0** → `✅ <mode> + committed on chore/<name> — pushed to origin by the hooks (auto-push mode), NOT merged (--no-push). Merge when ready.` - **--no-push, `branch_ahead` = 0** → `✅ <mode> + committed on chore/<name> — pushed to origin by the hooks (auto-push mode), NOT merged (--no-push). Merge when ready.` With push mode `invalid`, replace `(auto-push mode)` with `(<verb stderr line verbatim>: pushed anyway, a hook still fails open, likely stale, or a manual push; fix the value by hand, commit the .githooks refresh)`.
- **--no-push, `branch_ahead` > 0 or unknown** → `✅ <mode> + committed on chore/<name> — this disk only, not pushed (<push mode manual | no origin/chore ref>), NOT merged. You: ! git push -u origin chore/<name>; merge when ready.` With push mode `invalid`, append ` gitflow.autopush=<value> is not a boolean: fix it by hand`. - **--no-push, `branch_ahead` > 0 or unknown** → `✅ <mode> + committed on chore/<name> — this disk only, not pushed (<push mode manual | no origin/chore ref>), NOT merged. You: ! git push -u origin chore/<name>; merge when ready.` With push mode `invalid`, append ` <verb stderr line verbatim>: treated as manual push mode, nothing pushed; fix the value by hand`.
- **manual (merged, `ahead` > 0)** → `✅ <mode> + merged to develop — manual push mode: not pushed. You: ! git push origin develop` - **manual (merged, `ahead` > 0)** → `✅ <mode> + merged to develop — manual push mode: not pushed. You: ! git push origin develop`
- **not on origin (merged, `ahead` unknown)** → `✅ <mode> + merged to develop — not on origin (no origin/develop ref).` Push mode manual → add `You: ! git push origin develop once a remote exists`. - **not on origin (push mode not `invalid`, merged, `ahead` unknown)** → `✅ <mode> + merged to develop — not on origin (no origin/develop ref).` Push mode manual → add `You: ! git push origin develop once a remote exists`.
- **invalid (merged)** → `⚠️ <mode> + merged to develop — gitflow.autopush=<value> is not a boolean; lib/hooks still push on it until run D (origin/develop <ahead> behind). Fix the value by hand.` - **invalid (merged, ahead > 0 or unknown)** → `⚠️ <mode> + merged to develop — <verb stderr line verbatim>: treated as manual push mode by every reader, nothing pushed (origin/develop <ahead> behind). Fix the value by hand, then: ! git push origin develop` (+ ` once a remote exists` when unknown)
- **invalid (merged, ahead = 0)** → `⚠️ <mode> + merged to develop — <verb stderr line verbatim>: pushed anyway, a hook still fails open, likely stale (refreshed by the next session-start reconcile; commit the refresh) or a manual push. Fix the value by hand.`
- **push failed after merge** → `✅ <mode> + merged to develop — ⚠️ push FAILED (<reason>); merged locally, push manually.` - **push failed after merge** → `✅ <mode> + merged to develop — ⚠️ push FAILED (<reason>); merged locally, push manually.`
- **finish failed** → `⚠️ <mode> + finish rc <n>: <stderr> — chore/<name> kept, NOT merged; resolve by hand.` (rc 1/4 only; rc 5/2/6 with the branch merged use the outcome lines above with the `merged, branch not deleted (rc <n>)` prefix, so the push state is still reported.) - **finish failed** → `⚠️ <mode> + finish rc <n>: <stderr> — chore/<name> kept, NOT merged; resolve by hand.` (rc 1/4 only; rc 5/2/6 with the branch merged use the outcome lines above with the `merged, branch not deleted (rc <n>)` prefix, so the push state is still reported.)
- **WORKING branch (rode a feature branch)** → `✅ <mode> + committed <mem_hash> on <branch>. Integrates when the branch merges.` - **WORKING branch (rode a feature branch)** → `✅ <mode> + committed <mem_hash> on <branch>. Integrates when the branch merges.`
+1 -1
View File
@@ -45,7 +45,7 @@ The agent runs a **ship-and-handover pipeline** with explicit gates:
- Re-invoke the audit subagent in audit mode: it re-scores and returns the next FIX BUNDLE; it applies nothing (a dispatched child cannot hold a gate). - Re-invoke the audit subagent in audit mode: it re-scores and returns the next FIX BUNDLE; it applies nothing (a dispatched child cannot hold a gate).
- Repeat up to `MAX_ITERATIONS` (default 5). - Repeat up to `MAX_ITERATIONS` (default 5).
- If still < 17/20 after cap → escalate to user with concrete remaining issues; user decides continue / stop / manual intervention. - If still < 17/20 after cap → escalate to user with concrete remaining issues; user decides continue / stop / manual intervention.
4. **COMMIT + PUSH** — If files changed during fix loops, 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 <branch>` BEFORE the deploy pause. 4. **COMMIT + PUSH STATE READ** — If files changed during fix loops, run /commit-change (atomic logical commits); the gitflow hooks push in auto-push mode, otherwise (manual push mode, an invalid gitflow.autopush, or a hook push that failed) the agent tells the user to push with `! git push -u origin <branch>` BEFORE the deploy pause.
5. **DEPLOY PAUSE** — List exact deploy artifacts: changed files since baseline, deploy hints from project (vercel.json, netlify.toml, Dockerfile, .github/workflows/deploy.yml, etc.), and the deploy process in plain words. Use AskUserQuestion: "Deploy done? (Yes / Not yet / Skip validate)". Block until Yes or Skip. 5. **DEPLOY PAUSE** — List exact deploy artifacts: changed files since baseline, deploy hints from project (vercel.json, netlify.toml, Dockerfile, .github/workflows/deploy.yml, etc.), and the deploy process in plain words. Use AskUserQuestion: "Deploy done? (Yes / Not yet / Skip validate)". Block until Yes or Skip.
6. **/web-validate (live site)** — Run validator-analyzer against the deployed URL. Capture `SCORE_VALIDATE`. 6. **/web-validate (live site)** — Run validator-analyzer against the deployed URL. Capture `SCORE_VALIDATE`.
7. **GATE — per-axis threshold ≥17/20** — Compute final `SCORE_*_AFTER` for SEO classique, GEO (IA), HARDEN, VALIDATE. If ANY < 17/20: STOP. Generate `.claude/audits/HANDOVER-ROADMAP.md` with prioritized analysis of what's blocking each below-threshold axis. Do NOT write the client deliverable. Report to user. 7. **GATE — per-axis threshold ≥17/20** — Compute final `SCORE_*_AFTER` for SEO classique, GEO (IA), HARDEN, VALIDATE. If ANY < 17/20: STOP. Generate `.claude/audits/HANDOVER-ROADMAP.md` with prioritized analysis of what's blocking each below-threshold axis. Do NOT write the client deliverable. Report to user.