Merge feature/deploy-inline-checklist into develop

This commit is contained in:
Bastien Chanot
2026-07-05 20:20:32 +02:00
6 changed files with 91 additions and 55 deletions
+1
View File
@@ -330,3 +330,4 @@ rules:
- Built /tour skill (grouped sweep clean+security+reconcile+doc, auto, 1..N projects, convergence loop bounded 3×) via writing-skills TDD + skill-creator guidance: RED 6 gaps → GREEN 6/6 closed disk-verified → REFACTOR 2 holes (scratch self-block, BREAKING tag). [[BDR-052]] [[LRN-099]] [[LRN-100]] [[EVAL-014]]. Merged feature/tour-skill → develop + release/1.0.0 on user GO. settings.json /model side-effect reverted (Opus 4.8 1M default restored, attribution backstop kept). - Built /tour skill (grouped sweep clean+security+reconcile+doc, auto, 1..N projects, convergence loop bounded 3×) via writing-skills TDD + skill-creator guidance: RED 6 gaps → GREEN 6/6 closed disk-verified → REFACTOR 2 holes (scratch self-block, BREAKING tag). [[BDR-052]] [[LRN-099]] [[LRN-100]] [[EVAL-014]]. Merged feature/tour-skill → develop + release/1.0.0 on user GO. settings.json /model side-effect reverted (Opus 4.8 1M default restored, attribution backstop kept).
- /deploy first real run (bchanot-cv): bootstrap→mark full cycle, live-proven (full security-header stack live — tour→prod closed, tag deploy/2026-07-05). Skill patched post-run on user UX feedback: session-style NEXT.sh (one command per line) + hand-back prints the checklist inline ([[EVAL-016]]); template + generated runbook restyled. impeccable chain + Node 24 baseline shipped develop+RC, pushed. settings.json: +inputNeededNotifEnabled committed (layout unchanged). - /deploy first real run (bchanot-cv): bootstrap→mark full cycle, live-proven (full security-header stack live — tour→prod closed, tag deploy/2026-07-05). Skill patched post-run on user UX feedback: session-style NEXT.sh (one command per line) + hand-back prints the checklist inline ([[EVAL-016]]); template + generated runbook restyled. impeccable chain + Node 24 baseline shipped develop+RC, pushed. settings.json: +inputNeededNotifEnabled committed (layout unchanged).
- /deploy pass 2 (user feedback live): checklist DISPLAY-ONLY — NEXT.sh file eliminated (throwaway artifact, PENDING+runbook regenerate anywhere), hand-back ends the turn with the checklist as final text (a print above AskUserQuestion never reached the user, [[LRN-102]]). Skill+template+CHANGELOG patched; legacy NEXT.sh removed from bchanot-cv; deploy run 2 (residuals b24c58b) re-handed-back inline.
+9
View File
@@ -119,6 +119,7 @@ rules:
| LRN-097 | 2026-07-04 | community blog pattern ≠ official feature — "contexts dir" doesn't exist in Claude Code; verify feature against official docs (claude-code-guide) BEFORE building infra; the intent was already covered by real mechanisms (agents/skills/rules) | any "add support for X" request naming a Claude Code feature | | LRN-097 | 2026-07-04 | community blog pattern ≠ official feature — "contexts dir" doesn't exist in Claude Code; verify feature against official docs (claude-code-guide) BEFORE building infra; the intent was already covered by real mechanisms (agents/skills/rules) | any "add support for X" request naming a Claude Code feature |
| LRN-099 | 2026-07-05 | auto-orchestrator autonomy boundary: git discipline transfers naturally (branch, no-merge), declared-state discipline does NOT — baseline silently rewrote target TODO + authored registries + scope-crept | designing any auto/headless flow — enumerate declared surfaces, mark each read-only or gated | | LRN-099 | 2026-07-05 | auto-orchestrator autonomy boundary: git discipline transfers naturally (branch, no-merge), declared-state discipline does NOT — baseline silently rewrote target TODO + authored registries + scope-crept | designing any auto/headless flow — enumerate declared surfaces, mark each read-only or gated |
| LRN-100 | 2026-07-05 | tool gated on clean tree must clean its OWN scratch (else self-DoS next run); contract-changing auto-fix needs structural BREAKING flag in the reviewed artifact | any recurring tool w/ cleanliness precondition; any auto-fix touching an API contract | | LRN-100 | 2026-07-05 | tool gated on clean tree must clean its OWN scratch (else self-DoS next run); contract-changing auto-fix needs structural BREAKING flag in the reviewed artifact | any recurring tool w/ cleanliness precondition; any auto-fix touching an API contract |
| LRN-102 | 2026-07-05 | deliverable text placed BEFORE a tool call may never render — only the turn's FINAL text is guaranteed displayed; a checklist printed above AskUserQuestion was invisible to the user | any flow whose deliverable is conversational text (checklist, commands, report): end the turn with it, blocking questions come before, never after |
--- ---
@@ -1041,3 +1042,11 @@ rules:
- **context**: 2026-07-04 /tour GREEN on fixture; both patched at REFACTOR (SKILL.md STEP 3). Additions template-structural, NOT re-run through 3rd full pass (cost) — re-test first real use. - **context**: 2026-07-04 /tour GREEN on fixture; both patched at REFACTOR (SKILL.md STEP 3). Additions template-structural, NOT re-run through 3rd full pass (cost) — re-test first real use.
- **future application**: any recurring tool gated on repo cleanliness → audit what IT leaves behind; any auto-applied fix changing a contract → structural BREAKING flag in the human-reviewed artifact. - **future application**: any recurring tool gated on repo cleanliness → audit what IT leaves behind; any auto-applied fix changing a contract → structural BREAKING flag in the human-reviewed artifact.
- **cousin**: [[LRN-099]] same chantier; [[LRN-071]] swallowed-failure class (silent residue ≈ masked state). - **cousin**: [[LRN-099]] same chantier; [[LRN-071]] swallowed-failure class (silent residue ≈ masked state).
## LRN-102 — Deliverable text before a tool call may never render: the turn's FINAL text is the only guaranteed display
- **pattern**: /deploy hand-back printed the full checklist in the assistant message, then called AskUserQuestion. The user saw ONLY the question UI — the checklist never reached them ("là on a rien, je dois ouvrir le fichier"). The harness renders reliably only the LAST text of a turn; text between/before tool calls can be swallowed by the tool UI.
- **why**: a skill whose deliverable is conversational (commands to copy-paste, a report) fails silently if any tool call follows the print — the user experiences "nothing displayed" while the transcript technically contains it. Structural fix: the deliverable IS the turn's final text; collect answers BEFORE printing, or let the reply arrive as the next user message.
- **context**: 2026-07-05 /deploy run 2 (bchanot-cv). Skill patched same turn: checklist display-only (no NEXT.sh file at all — user: throwaway once deployed) + hand-back ends the turn, no tool call after.
- **future application**: designing any skill/flow output meant to be read+used from the conversation — put it LAST; never sandwich a deliverable between tool calls; prefer plain-text report requests over blocking question tools after a deliverable.
- **cousin**: [[LRN-100]] same skill lineage; CLAUDE.md communication doctrine (final message carries everything).
+10 -2
View File
@@ -11,8 +11,16 @@ gouverne le bloc entier.
- [x] bchanot-cv runbook restylé, committé, pushé (bd7f6e4, develop sync) - [x] bchanot-cv runbook restylé, committé, pushé (bd7f6e4, develop sync)
- [x] settings.json +inputNeededNotifEnabled (layout committé inchangé) - [x] settings.json +inputNeededNotifEnabled (layout committé inchangé)
- [x] Capitalize EVAL-016 + journal - [x] Capitalize EVAL-016 + journal
- [ ] Re-dogfood au prochain /deploy réel (edit de skill non re-testé par run — - [x] Re-dogfood run 2 (résidus bchanot-cv) : le print inline AVANT
dette Iron Law assumée, même statut que la note d'authoring du skill) AskUserQuestion ne s'affichait PAS → leçon [[LRN-102]] (texte avant un
tool call peut ne jamais rendre ; le dernier texte du tour est le seul
affichage garanti)
- [x] PASS 2 (feature/deploy-inline-checklist) : checklist DISPLAY-ONLY —
plus de fichier NEXT.sh du tout (jetable, PENDING+runbook régénèrent
partout) ; hand-back TERMINE le tour par la checklist, aucun tool call
après ; resume à froid = régénère + ré-affiche. Skill+template+CHANGELOG.
- [ ] Re-dogfood pass 2 : la fin du deploy run 2 en cours (résidus b24c58b)
exerce le nouveau hand-back ; resume à froid + STEP 4 toujours vierges
## 2026-07-05 — impeccable install chain (feature/impeccable-install) ## 2026-07-05 — impeccable install chain (feature/impeccable-install)
Décision (user a délégué) : COMPLÉMENTAIRES → les deux. frontend-design garde Décision (user a délégué) : COMPLÉMENTAIRES → les deux. frontend-design garde
+1 -1
View File
@@ -8,7 +8,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
### Changed ### Changed
- graphify skill dist refreshed 0.8.45 → 0.9.6 (out-of-band `make plugin`; SKILL.md + query/extraction references updated by the generator). - graphify skill dist refreshed 0.8.45 → 0.9.6 (out-of-band `make plugin`; SKILL.md + query/extraction references updated by the generator).
- `/deploy` NEXT.sh reshaped on first-real-run feedback: runbook steps are **one command per line, interactive-session style** (an early step opens the ssh session; later lines run on the box; local steps say "from your machine") instead of folded `ssh host "cd … && …"` one-liners, and the **hand-back prints the full checklist inline** in the conversation (also on every re-hand-back) so the user never has to open `NEXT.sh` to know what to run. Step = comment header + command lines up to the next blank line; a `@delta:` directive governs the whole block. Template `templates/deploy/PROCEDURE.md` restyled to match. - `/deploy` checklist reshaped on first-real-run feedback, in two passes: runbook steps are **one command per line, interactive-session style** (an early step opens the ssh session; later lines run on the box; local steps say "from your machine") instead of folded `ssh host "cd … && …"` one-liners — step = comment header + command lines up to the next blank line, a `@delta:` directive governs the whole block; and the checklist is now **display-only** — `NEXT.sh` is no longer written at all (throwaway artifact; `PENDING.json` + the live runbook regenerate it in any session) and every hand-back **ends the turn with the full checklist as the final text, no tool call after it** (a checklist printed above a blocking question tool was observed never reaching the user). Template `templates/deploy/PROCEDURE.md` restyled to match.
- `settings.json`: `inputNeededNotifEnabled: true` adopted (harness notification toggle); committed layout otherwise unchanged. - `settings.json`: `inputNeededNotifEnabled: true` adopted (harness notification toggle); committed layout otherwise unchanged.
### Added ### Added
+69 -51
View File
@@ -22,8 +22,9 @@ disk in `.claude/deploy/`, never in conversation context. Never reconstruct the
deploy from memory, commit messages, or `git describe`. deploy from memory, commit messages, or `git describe`.
**Claude never runs the deploy.** Prod commands run by hand, out-of-band. This **Claude never runs the deploy.** Prod commands run by hand, out-of-band. This
skill only writes the checklist (`NEXT.sh`), reacts to the user's report, and skill only composes the checklist — **displayed in the conversation, never
records the outcome. written to a file** (it is throwaway: valid for one delta, worthless after) —
reacts to the user's report, and records the outcome.
## The two-moment contract — cold cross-session resume ## The two-moment contract — cold cross-session resume
@@ -31,7 +32,7 @@ This is the skill's defining form. No other skill resumes with the context gone.
| | | | | |
|---|---| |---|---|
| **Moment 1 (BEFORE)** | STEP 0–2: detect the delta, instantiate `NEXT.sh`, write the `PENDING.json` bridge, hand back. | | **Moment 1 (BEFORE)** | STEP 0–2: detect the delta, instantiate the checklist, write the `PENDING.json` bridge, hand back. |
| **the gap** | The user deploys by hand. May take minutes or days. **May cross sessions.** | | **the gap** | The user deploys by hand. May take minutes or days. **May cross sessions.** |
| **Moment 2 (AFTER)** | STEP 3–5: on the user's report, react — mark success, or learn from a failure and re-hand-back. | | **Moment 2 (AFTER)** | STEP 3–5: on the user's report, react — mark success, or learn from a failure and re-hand-back. |
@@ -61,7 +62,7 @@ jq dependency.
| Configure deployment settings | `/setup-deploy` | | Configure deployment settings | `/setup-deploy` |
| Document a release after shipping | `/document-release`, `/doc` | | Document a release after shipping | `/document-release`, `/doc` |
## Artifacts — `.claude/deploy/` (five files) ## Artifacts — `.claude/deploy/` (four files)
| File | Committed? | Role | | File | Committed? | Role |
|------|-----------|------| |------|-----------|------|
@@ -69,7 +70,11 @@ jq dependency.
| `INCIDENTS.md` | yes | `DEP-NNN` ledger, append-only; read at instantiation for pre-warns | | `INCIDENTS.md` | yes | `DEP-NNN` ledger, append-only; read at instantiation for pre-warns |
| `STATE.json` | yes | deploy oracle — the SHA deployed up to here | | `STATE.json` | yes | deploy oracle — the SHA deployed up to here |
| `PENDING.json` | **no (gitignored)** | in-flight bridge; written at hand-back, deleted on success | | `PENDING.json` | **no (gitignored)** | in-flight bridge; written at hand-back, deleted on success |
| `NEXT.sh` | **no (gitignored)** | instantiated checklist; run BY HAND, never `bash NEXT.sh` |
The instantiated checklist is **NOT a file**: it is displayed in the
conversation (run BY HAND, step by step, never executed by Claude) and
regenerated on demand from `PENDING.json` + the live runbook. Throwaway by
design — once deployed, it has no value.
**Schemas (document of record — recover the shapes from here):** **Schemas (document of record — recover the shapes from here):**
@@ -85,13 +90,13 @@ jq dependency.
"runbook_rev": "<PROCEDURE.md commit sha>" } "runbook_rev": "<PROCEDURE.md commit sha>" }
``` ```
`step_reached` = where the next `NEXT.sh` must start: `"awaiting-user"` = run from `step_reached` = where the next checklist must start: `"awaiting-user"` = run
the top. A numeric `X` is used **transiently within a learn** to regenerate from from the top. A numeric `X` is used **transiently within a learn** to regenerate
step X; **persisted on disk it is always `"awaiting-user"`** — STEP 4 resets to from step X; **persisted on disk it is always `"awaiting-user"`** — STEP 4
`awaiting-user` at re-hand-back, and the `runbook_rev` staleness guard is the real resets to `awaiting-user` at re-hand-back.
cold-resume regenerate trigger. `runbook_rev` = the commit sha of `PROCEDURE.md` at instantiation; on resume, a
`runbook_rev` = the commit sha of `PROCEDURE.md` at instantiation; a mismatch mismatch versus the live runbook means the runbook changed mid-flight — flag it
versus the live runbook means `NEXT.sh` is stale and must be regenerated. and regenerate the checklist against the LIVE runbook.
## `@delta:` grammar (PROCEDURE.md) ## `@delta:` grammar (PROCEDURE.md)
@@ -129,11 +134,13 @@ Read `.claude/deploy/PENDING.json` **first** (it is the only memory between runs
"A deploy started `<started_at>` is awaiting your report (target `<target_sha>`)." "A deploy started `<started_at>` is awaiting your report (target `<target_sha>`)."
**Do not** recompute the delta, re-read HEAD, or re-instantiate from scratch — **Do not** recompute the delta, re-read HEAD, or re-instantiate from scratch —
the bridge is authoritative. the bridge is authoritative.
- *Staleness guard:* if `NEXT.sh` is absent **OR** `runbook_rev` ≠ the live - *Cold resume without a report yet* (the user just re-invoked /deploy):
regenerate the checklist from the bridge + the live runbook (STEP 2's
expansion, from `step_reached`) and RE-DISPLAY it — the checklist is not
a file, the conversation that held it is gone. If `runbook_rev` ≠ the live
runbook commit (`git log -1 --format=%H -- .claude/deploy/PROCEDURE.md`), runbook commit (`git log -1 --format=%H -- .claude/deploy/PROCEDURE.md`),
the on-disk `NEXT.sh` is stale or missing (a patch landed, or a cold say so: the runbook changed mid-flight and the regenerated checklist
resume without regeneration) — regenerate it from `step_reached` follows the LIVE version.
(STEP 2's expansion) before reacting.
- **`PENDING.json` absent + `PROCEDURE.md` absent → BOOTSTRAP.** No runbook yet: - **`PENDING.json` absent + `PROCEDURE.md` absent → BOOTSTRAP.** No runbook yet:
interview the project and scaffold an annotated `PROCEDURE.md` (or adopt one interview the project and scaffold an annotated `PROCEDURE.md` (or adopt one
the user pastes), then continue at STEP 1. *(See STEP 0-B below.)* the user pastes), then continue at STEP 1. *(See STEP 0-B below.)*
@@ -165,7 +172,7 @@ Author a runbook, seed the incident ledger, commit both, then proceed to STEP 1.
2. Prepend the standard header: 2. Prepend the standard header:
``` ```
#!/usr/bin/env bash #!/usr/bin/env bash
# === deploy runbook (reference) — NOT run directly. Instantiated to NEXT.sh per delta. === # === deploy runbook (reference) — NOT run directly. Instantiated into the deploy checklist per delta. ===
# Fixed steps run every deploy; annotated steps (@delta lines) re-instantiate from the delta. # Fixed steps run every deploy; annotated steps (@delta lines) re-instantiate from the delta.
# @config push_deploy_tags=false # @config push_deploy_tags=false
``` ```
@@ -226,9 +233,9 @@ Present the full draft `PROCEDURE.md`.
1. Write `.claude/deploy/PROCEDURE.md` (Write tool — the approved draft). 1. Write `.claude/deploy/PROCEDURE.md` (Write tool — the approved draft).
2. Seed `.claude/deploy/INCIDENTS.md` from `templates/deploy/INCIDENTS.md` (Write tool). 2. Seed `.claude/deploy/INCIDENTS.md` from `templates/deploy/INCIDENTS.md` (Write tool).
3. Ensure the target project's `.gitignore` contains `.claude/deploy/NEXT.sh` and 3. Ensure the target project's `.gitignore` contains
`.claude/deploy/PENDING.json` (append both if missing — these are the transient `.claude/deploy/PENDING.json` (append if missing — the transient bridge must
artifacts that must not be committed). not be committed).
4. Check that `.claude/deploy/` is NOT git-ignored: `git check-ignore -q .claude/deploy/PROCEDURE.md` 4. Check that `.claude/deploy/` is NOT git-ignored: `git check-ignore -q .claude/deploy/PROCEDURE.md`
(rc 0 = ignored). If ignored — e.g. the project has `.claude/` in its `.gitignore` wholesale — (rc 0 = ignored). If ignored — e.g. the project has `.claude/` in its `.gitignore` wholesale —
**ABORT bootstrap**: warn the user that the runbook/oracle/ledger cannot be committed, **ABORT bootstrap**: warn the user that the runbook/oracle/ledger cannot be committed,
@@ -270,7 +277,7 @@ Set the base, compute the changed-file list, capture the target.
## STEP 2 — INSTANTIATE + [GATE] + HAND BACK ## STEP 2 — INSTANTIATE + [GATE] + HAND BACK
**Build `NEXT.sh` (the recipe — it IS this shape):** **Build the checklist (the recipe — it IS this shape):**
1. Walk `PROCEDURE.md` in order. For each step: 1. Walk `PROCEDURE.md` in order. For each step:
- un-annotated (fixed) → emit verbatim; - un-annotated (fixed) → emit verbatim;
@@ -281,15 +288,17 @@ Set the base, compute the changed-file list, capture the target.
- `@delta:…when=` → emit verbatim only if the delta intersects a pattern. - `@delta:…when=` → emit verbatim only if the delta intersects a pattern.
2. Read `INCIDENTS.md`; for each `DEP-NNN` whose step matches an emitted step, 2. Read `INCIDENTS.md`; for each `DEP-NNN` whose step matches an emitted step,
prepend `# PRE-WARN: DEP-NNN <one-line summary>` above it. prepend `# PRE-WARN: DEP-NNN <one-line summary>` above it.
3. Keep every `# VERIFY:` gate. Header the file: *"Run by hand, step by step. 3. Keep every `# VERIFY:` gate. Header the checklist: *"Run by hand, step by
Never `bash NEXT.sh` unattended."* step. Never executed by Claude."* + base → target SHAs + the delta.
4. Preserve the runbook's shape: one command per line, session style (see the 4. Preserve the runbook's shape: one command per line, session style (see the
`@delta:` grammar section) — instantiation never re-folds lines. `@delta:` grammar section) — instantiation never re-folds lines.
5. Write `.claude/deploy/NEXT.sh`. 5. **Write NO file.** The checklist exists in the conversation only —
`PENDING.json` is the sole on-disk artifact of the wait, and any future
session regenerates the checklist from it + the live runbook.
**[GATE] — present `NEXT.sh` → `all / edit / skip-all`.** **[GATE] — present the checklist → `all / edit / skip-all`.**
- `all` → proceed. `edit` → revise the listed steps, re-present. - `all` → proceed. `edit` → revise the listed steps, re-present.
- `skip-all` → abort: write no `PENDING.json`, discard the draft `NEXT.sh`, stop. - `skip-all` → abort: write no `PENDING.json`, discard the draft, stop.
**On approve:** write `.claude/deploy/PENDING.json`: **On approve:** write `.claude/deploy/PENDING.json`:
```jsonc ```jsonc
@@ -298,15 +307,17 @@ Set the base, compute the changed-file list, capture the target.
"started_at": "<now, ISO-8601>", "started_at": "<now, ISO-8601>",
"runbook_rev": "<git log -1 --format=%H -- .claude/deploy/PROCEDURE.md>" } "runbook_rev": "<git log -1 --format=%H -- .claude/deploy/PROCEDURE.md>" }
``` ```
**Then HAND BACK — the checklist lands in the conversation, not just on disk.** **Then HAND BACK — the checklist IS the last text of the turn.** End the turn
Print the FULL final `NEXT.sh` content inline (fenced code block) so the user with the FULL final checklist in a fenced code block, followed only by the
sees exactly what to run without opening the file — the gate preview is not one-line report request: *"Run it step by step against prod, then report:
enough (an `edit` round may have changed it; the hand-back shows the final **Deployed OK** / **Failed at step X: <err>** / **Not yet**."* **No tool call
text). Then (AskUserQuestion): *"Run NEXT.sh step by step against prod. comes after the print — none.** Do NOT wrap the report request in a blocking
Report back: **Deployed OK** / **Failed at step X: <err>** / **Not yet**."* Then question tool: text printed before a tool call may never reach the user
**stop** — control is the user's; `PENDING.json` on disk now marks the wait. (observed live — a checklist printed above an AskUserQuestion was invisible;
The same rule applies to every re-hand-back (STEP 4.3): regenerated `NEXT.sh` the user had to open the file this rule exists to make unnecessary). The report
⇒ reprinted in full. arrives as the user's next message; `PENDING.json` on disk marks the wait.
The same rule applies to every re-hand-back (STEP 4.3) and every cold-resume
re-display: regenerated checklist ⇒ full print as the turn's final text.
## STEP 3 — RESUME / REACT ## STEP 3 — RESUME / REACT
@@ -357,13 +368,12 @@ fix (patch + incident committed atomically). Recover later via
Then: Then:
1. Bump `PENDING.json.runbook_rev` to `git rev-parse HEAD` (full sha — not the helper's short-hash stdout); keep `step_reached` = `X`. 1. Bump `PENDING.json.runbook_rev` to `git rev-parse HEAD` (full sha — not the helper's short-hash stdout); keep `step_reached` = `X`.
2. **Regenerate `NEXT.sh` from `step_reached` against the PATCHED runbook** 2. **Regenerate the checklist from `step_reached` against the PATCHED runbook**
(steps X…end — X+1…end never ran). This is NOT replaying one step: the bumped (steps X…end — X+1…end never ran). This is NOT replaying one step: the
`runbook_rev` is exactly the staleness trigger — runbook changed ⇒ prior runbook changed ⇒ the prior checklist is stale ⇒ regenerate.
`NEXT.sh` is stale ⇒ regenerate. 3. Re-present via **STEP 2's [GATE] + hand-back** (the regenerated checklist,
3. Re-present via **STEP 2's [GATE] + hand-back** (the regenerated `NEXT.sh`; full print as the turn's final text; `PENDING.json` keeps
`PENDING.json` keeps `base/target/delta`, `step_reached` back to `base/target/delta`, `step_reached` back to `awaiting-user`).
`awaiting-user`).
## STEP 5 — MARK (success) ## STEP 5 — MARK (success)
@@ -388,8 +398,9 @@ The deploy succeeded. Lay the oracle and close out.
bash lib/deploy-commit.sh commit "chore(deploy): mark <date> @ <short>" \ bash lib/deploy-commit.sh commit "chore(deploy): mark <date> @ <short>" \
.claude/deploy/STATE.json .claude/deploy/STATE.json
``` ```
6. **Delete `.claude/deploy/PENDING.json` and `.claude/deploy/NEXT.sh`** — the 6. **Delete `.claude/deploy/PENDING.json`** — the deploy is no longer in
deploy is no longer in flight; the bridge is consumed. flight; the bridge is consumed. (Also remove any legacy `NEXT.sh` left by
an older skill version.)
7. Report: deployed SHA, tag (+ push result), state committed, any `DEP-NNN` 7. Report: deployed SHA, tag (+ push result), state committed, any `DEP-NNN`
learned this deploy. Then offer to capitalize per CLAUDE.md (recurring failure learned this deploy. Then offer to capitalize per CLAUDE.md (recurring failure
pattern → `learnings.md`; deploy verdict → `evals.md`), gated, never silent. pattern → `learnings.md`; deploy verdict → `evals.md`), gated, never silent.
@@ -404,10 +415,13 @@ The deploy succeeded. Lay the oracle and close out.
- Delta is `git diff --name-only <base> HEAD` (two endpoints). No `rev-list`, no - Delta is `git diff --name-only <base> HEAD` (two endpoints). No `rev-list`, no
three-dot, no date ranges. three-dot, no date ranges.
- First-deploy / fresh detection is file existence only — never `git describe`. - First-deploy / fresh detection is file existence only — never `git describe`.
- Claude never executes the deploy. `NEXT.sh` is hand-run; `# VERIFY:` gates stay. - Claude never executes the deploy. The checklist is hand-run; `# VERIFY:`
gates stay.
- The checklist is displayed, never written to a file; every hand-back and
re-display ends the turn with it — no tool call after the print.
- Patch + incident commit **atomically**, one `deploy-commit.sh` call, both files. - Patch + incident commit **atomically**, one `deploy-commit.sh` call, both files.
- A learn bumps `runbook_rev` and **regenerates** `NEXT.sh` from `step_reached`; - A learn bumps `runbook_rev` and **regenerates** the checklist from
it never replays a single step. `step_reached`; it never replays a single step.
- Tag push is best-effort; `STATE.json` is the oracle. - Tag push is best-effort; `STATE.json` is the oracle.
- JSON is read natively (Read tool), never parsed with `jq`/shell. - JSON is read natively (Read tool), never parsed with `jq`/shell.
- `STATE.json` written only on confirmed success (STEP 5). A failed/partial deploy - `STATE.json` written only on confirmed success (STEP 5). A failed/partial deploy
@@ -420,9 +434,11 @@ The deploy succeeded. Lay the oracle and close out.
| On resume, recomputing delta from current HEAD | HEAD moved during the gap. Use `PENDING.json.{base,target,delta}` verbatim. | | On resume, recomputing delta from current HEAD | HEAD moved during the gap. Use `PENDING.json.{base,target,delta}` verbatim. |
| `git describe` to detect first deploy | Errors with no tag. Detect by `STATE.json` / `PENDING.json` existence. | | `git describe` to detect first deploy | Errors with no tag. Detect by `STATE.json` / `PENDING.json` existence. |
| `git rev-list` or three-dot for the delta | Phantom/undercounted deltas. Two-dot `<base> HEAD` only. | | `git rev-list` or three-dot for the delta | Phantom/undercounted deltas. Two-dot `<base> HEAD` only. |
| `bash NEXT.sh` to "just run it" | Claude never deploys. Hand back; user runs by hand with `# VERIFY:` gates. | | Executing the checklist yourself to "just run it" | Claude never deploys. Hand back; user runs by hand with `# VERIFY:` gates. |
| Committing the patch without the incident (or vice versa) | Coupling invariant. One atomic `deploy-commit.sh` call, both files. | | Committing the patch without the incident (or vice versa) | Coupling invariant. One atomic `deploy-commit.sh` call, both files. |
| Replaying only the failed step after a patch | Steps X…end never ran. Regenerate `NEXT.sh` from `step_reached`. | | Replaying only the failed step after a patch | Steps X…end never ran. Regenerate the checklist from `step_reached`. |
| Ending a hand-back with a blocking question tool after the checklist | Text before a tool call may never render. The checklist is the turn's FINAL text; the report comes as the user's next message. |
| Writing the checklist to a file "for reference" | Throwaway artifact — display only; PENDING.json + the runbook regenerate it anywhere. |
| Writing `STATE.json` before the user confirms success | Oracle marks success only. Failed deploy leaves it untouched. | | Writing `STATE.json` before the user confirms success | Oracle marks success only. Failed deploy leaves it untouched. |
| Setting `deployed_sha` to HEAD at MARK time | Use `PENDING.target_sha` — the SHA actually deployed. | | Setting `deployed_sha` to HEAD at MARK time | Use `PENDING.target_sha` — the SHA actually deployed. |
| Parsing the JSON bridges with `jq` | Read them natively. No jq dependency. | | Parsing the JSON bridges with `jq` | Read them natively. No jq dependency. |
@@ -432,7 +448,9 @@ The deploy succeeded. Lay the oracle and close out.
- About to recompute the delta or re-read HEAD while a `PENDING.json` exists. - About to recompute the delta or re-read HEAD while a `PENDING.json` exists.
- About to run `git describe`, `git rev-list`, or a three-dot diff for the delta. - About to run `git describe`, `git rev-list`, or a three-dot diff for the delta.
- About to `bash NEXT.sh` or run any prod command yourself. - About to execute the checklist or run any prod command yourself.
- About to call ANY tool after printing the checklist in a hand-back.
- About to write the checklist to a file.
- About to commit `PROCEDURE.md` without `INCIDENTS.md` in the same call. - About to commit `PROCEDURE.md` without `INCIDENTS.md` in the same call.
- About to write `STATE.json` before the user reported "Deployed OK". - About to write `STATE.json` before the user reported "Deployed OK".
- About to replay one failed step instead of regenerating from `step_reached`. - About to replay one failed step instead of regenerating from `step_reached`.
@@ -446,7 +464,7 @@ from it without conversation memory — the `audit-delta` "state file is the onl
memory between runs" convention, extended to a *mid-flow* pause. The forms here memory between runs" convention, extended to a *mid-flow* pause. The forms here
match the failure modes the design identified: **discipline** failures match the failure modes the design identified: **discipline** failures
(recompute-on-resume, run-the-deploy, advance-the-oracle-early) get the (recompute-on-resume, run-the-deploy, advance-the-oracle-early) get the
rationalization table + red flags; the **shape** of `NEXT.sh` and the schemas get rationalization table + red flags; the **shape** of the checklist and the schemas get
positive recipes; the patch↔incident **omission** is a structural atomic-commit positive recipes; the patch↔incident **omission** is a structural atomic-commit
requirement. Pressure-scenario baseline testing per the writing-skills Iron Law requirement. Pressure-scenario baseline testing per the writing-skills Iron Law
is a follow-up — the failure modes were taken from the design spec, not a fresh is a follow-up — the failure modes were taken from the design spec, not a fresh
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# === deploy runbook (reference) — NOT run directly. Instantiated to NEXT.sh per delta. === # === deploy runbook (reference) — NOT run directly. Instantiated into the deploy checklist per delta. ===
# Fixed steps run every deploy; # @delta: steps re-instantiate from the delta. # Fixed steps run every deploy; # @delta: steps re-instantiate from the delta.
# @config push_deploy_tags=false # @config push_deploy_tags=false
# NOTE grammar: glob=<pat>:each repeats the command per matching file (e.g. psql -f <each>); # NOTE grammar: glob=<pat>:each repeats the command per matching file (e.g. psql -f <each>);