From a10635aa363aa6a882108a97e31d49311e9a95f3 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Sat, 27 Jun 2026 17:32:31 +0200 Subject: [PATCH] =?UTF-8?q?fix(deploy):=20drop=20resolved-by=20=E2=80=94?= =?UTF-8?q?=20resolution=20=3D=20introducing=20atomic=20commit=20(derive?= =?UTF-8?q?=20via=20git)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Ho5EQCFTSvYamuRtVZpp2d --- docs/plans/2026-06-27-deploy-skill.md | 8 ++++---- docs/specs/2026-06-27-deploy-skill-design.md | 2 +- skills/deploy/SKILL.md | 15 ++++++++------- templates/deploy/INCIDENTS.md | 4 ++-- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/plans/2026-06-27-deploy-skill.md b/docs/plans/2026-06-27-deploy-skill.md index 594a608..227af25 100644 --- a/docs/plans/2026-06-27-deploy-skill.md +++ b/docs/plans/2026-06-27-deploy-skill.md @@ -36,7 +36,7 @@ **§8 item 1 — tag push:** annotated tag `git tag -a deploy/ -m ""` laid in MARK (success). **Project knob `# @config push_deploy_tags=true|false`** in the `PROCEDURE.md` header (default `false`): when true, MARK runs `git push origin deploy/` — always **best-effort/non-fatal** (the push never blocks the deploy; tag is a bookmark, STATE.json is the oracle). Same-day re-deploy → suffix `-N`. -**§8 item 2 — INCIDENTS ID/name:** `.claude/deploy/INCIDENTS.md`, append-only, entries `DEP-NNN` (next = `grep '^## DEP-' | max+1`), fields mirror `blockers.md`: date, step, error (verbatim), root cause, fix, → resolving commit sha. Name confirmed `INCIDENTS.md` (not `ERRORS-LEARNED.md`). +**§8 item 2 — INCIDENTS ID/name:** `.claude/deploy/INCIDENTS.md`, append-only, entries `DEP-NNN` (next = `grep '^## DEP-' | max+1`), fields mirror `blockers.md`: date, step, error (verbatim), root cause, fix. Resolution derivable from git: the commit that adds the entry IS the fix (atomic patch+incident); recover via `git log -S 'DEP-NNN' -- .claude/deploy/INCIDENTS.md`. Name confirmed `INCIDENTS.md` (not `ERRORS-LEARNED.md`). **§8 item 3 — `@delta:` grammar:** directives on a runbook step's preceding comment line, patterns matched against the delta file list. `glob=` carries TWO required semantics (a single "checklist-only" reading was REJECTED — it breaks the game example, where step 3 runs `psql -f 0033` THEN `psql -f 0034` = one command PER file): - `# @delta: glob=:each` — **repeat**: emit the step's command once per delta file matching `` (e.g. `psql -f `). @@ -269,13 +269,13 @@ curl -fsS https://$DEPLOY_HOST/health # Deploy incidents (append-only) — DEP-NNN + + - fix: --> ``` - [ ] **Step 3: Record the JSON schemas** (no parsing in shell — Claude reads them in skill steps) @@ -332,7 +332,7 @@ git commit -m "feat(deploy): runbook/ledger templates + bridge schemas + gitigno - "Deployed OK" → STEP 5. - "Failed at step X: " → STEP 4. - "Not yet" → re-state pending, stop. -- [ ] **STEP 4 — LEARN + [GATE] + ATOMIC COMMIT.** Diagnose. Draft: (a) in-place `PROCEDURE.md` patch to step X; (b) `INCIDENTS.md` append `DEP-NNN` (error verbatim). **[GATE]** `all / pick / edit / skip-all` (significant edit). On approve: write both, then **one atomic** `bash lib/deploy-commit.sh commit "docs(deploy): patch — recovered from " .claude/deploy/PROCEDURE.md .claude/deploy/INCIDENTS.md`. Set `resolved-by` to the returned sha. Then bump `PENDING.json.runbook_rev` to the new `PROCEDURE.md` commit sha (keep `step_reached` at X). **Resume = REGENERATE `NEXT.sh` from `step_reached` against the PATCHED runbook** (steps X…end — X+1…end never ran), NOT replay a single step. The bumped `runbook_rev` is exactly the trigger: runbook changed ⇒ prior `NEXT.sh` is stale ⇒ regenerate. Re-present via STEP 2's hand-back. +- [ ] **STEP 4 — LEARN + [GATE] + ATOMIC COMMIT.** Diagnose. Draft: (a) in-place `PROCEDURE.md` patch to step X; (b) `INCIDENTS.md` append `DEP-NNN` (error verbatim). **[GATE]** `all / pick / edit / skip-all` (significant edit). On approve: write both, then **one atomic** `bash lib/deploy-commit.sh commit "docs(deploy): patch — recovered from " .claude/deploy/PROCEDURE.md .claude/deploy/INCIDENTS.md`. The commit that adds `DEP-NNN` IS its resolution (derive via git later). Then bump `PENDING.json.runbook_rev` to the new `PROCEDURE.md` commit sha (keep `step_reached` at X). **Resume = REGENERATE `NEXT.sh` from `step_reached` against the PATCHED runbook** (steps X…end — X+1…end never ran), NOT replay a single step. The bumped `runbook_rev` is exactly the trigger: runbook changed ⇒ prior `NEXT.sh` is stale ⇒ regenerate. Re-present via STEP 2's hand-back. - [ ] **STEP 5 — MARK (success).** Write `STATE.json` (`deployed_sha = PENDING.target_sha`, outcome ok, tag). `git tag -a deploy/ -m ""`; **if `@config push_deploy_tags=true`** then `git push origin deploy/` (best-effort, non-fatal). `bash lib/deploy-commit.sh commit "chore(deploy): mark @ " .claude/deploy/STATE.json`. **Delete `PENDING.json`** (+ `NEXT.sh`). Report. - [ ] **Verification scenarios** (dry-run walkthroughs, no prod): diff --git a/docs/specs/2026-06-27-deploy-skill-design.md b/docs/specs/2026-06-27-deploy-skill-design.md index 741cae2..a8bc964 100644 --- a/docs/specs/2026-06-27-deploy-skill-design.md +++ b/docs/specs/2026-06-27-deploy-skill-design.md @@ -34,7 +34,7 @@ Treated as settled corollaries: user executes out-of-band; a **new** `lib/deploy .claude/deploy/ PROCEDURE.md reference runbook — fixed shell + `# @delta:` annotated steps (edited IN-PLACE) INCIDENTS.md DEP-NNN incident ledger: date, step, error verbatim, root cause, - fix, -> resolving commit hash (APPEND-ONLY) + fix (APPEND-ONLY; resolution = introducing commit, derive via git) STATE deployed SHA + timestamp + outcome — the diff oracle (overwritten each deploy) NEXT.sh instantiated runbook — EPHEMERAL, not committed ; run STEP-BY-STEP (checklist, manual # VERIFY: gates) — never `bash NEXT.sh` unattended diff --git a/skills/deploy/SKILL.md b/skills/deploy/SKILL.md index 736a93a..71dfe92 100644 --- a/skills/deploy/SKILL.md +++ b/skills/deploy/SKILL.md @@ -197,7 +197,7 @@ Diagnose the root cause of the step-X failure, then draft a **coupled pair**: repeat the failure; - **(b)** an append to `INCIDENTS.md` — a new `DEP-NNN` (`next = grep '^## DEP-' INCIDENTS.md | max+1`) with date, step, **error - verbatim**, root cause, fix, and `resolved-by` (filled after commit). + verbatim**, root cause, and fix. **[GATE] — `all / pick / edit / skip-all`** (significant edit — it changes a prod path). @@ -217,16 +217,17 @@ changed — investigate, you should have written both) · **3** unsafe git state (detached/merge/rebase — STOP, tell the user) · **4** out-of-scope path (you passed a non-`.claude/deploy/` path — fix the call) · **2** usage error. +**This commit IS the resolution** — the commit that introduces `DEP-NNN` is its +fix (patch + incident committed atomically). Recover later via +`git log -S '' -- .claude/deploy/INCIDENTS.md`. No backfill needed. + Then: -1. Set the new `DEP-NNN`'s `resolved-by:` to the returned short-hash. (This is a - metadata-only line; it rides along on the next `INCIDENTS.md` commit — do not - churn a second commit for it.) -2. Bump `PENDING.json.runbook_rev` to that commit's sha; keep `step_reached` = `X`. -3. **Regenerate `NEXT.sh` from `step_reached` against the PATCHED runbook** +1. Bump `PENDING.json.runbook_rev` to that commit's sha; keep `step_reached` = `X`. +2. **Regenerate `NEXT.sh` from `step_reached` against the PATCHED runbook** (steps X…end — X+1…end never ran). This is NOT replaying one step: the bumped `runbook_rev` is exactly the staleness trigger — runbook changed ⇒ prior `NEXT.sh` is stale ⇒ regenerate. -4. Re-present via **STEP 2's [GATE] + hand-back** (the regenerated `NEXT.sh`; +3. Re-present via **STEP 2's [GATE] + hand-back** (the regenerated `NEXT.sh`; `PENDING.json` keeps `base/target/delta`, `step_reached` back to `awaiting-user`). diff --git a/templates/deploy/INCIDENTS.md b/templates/deploy/INCIDENTS.md index 0dfaaec..76c4680 100644 --- a/templates/deploy/INCIDENTS.md +++ b/templates/deploy/INCIDENTS.md @@ -1,10 +1,10 @@ # Deploy incidents (append-only) — DEP-NNN + + - fix: -->