Merge feature/deploy-next-style into release/1.0.0 (user GO: /deploy UX + settings key ship in 1.0.0)

# Conflicts:
#	.claude/memory/evals.md
#	.claude/memory/journal.md
#	.claude/tasks/TODO.md
#	CHANGELOG.md
This commit is contained in:
Bastien Chanot
2026-07-05 19:46:38 +02:00
7 changed files with 64 additions and 16 deletions
+7
View File
@@ -160,3 +160,10 @@ rules:
- **method**: real repo, no fixture. Deferred re-test executed: STEP 3.2 cleanup HELD (no self-block for next run), BREAKING tag correctly N/A (zero fixes in report-only). Cross-checks: cso live-confirmed SEC-2 (zero security headers served) — config-only review would have missed it ([[LRN-101]]). - **method**: real repo, no fixture. Deferred re-test executed: STEP 3.2 cleanup HELD (no self-block for next run), BREAKING tag correctly N/A (zero fixes in report-only). Cross-checks: cso live-confirmed SEC-2 (zero security headers served) — config-only review would have missed it ([[LRN-101]]).
- **anomalies**: (1) skill gap — report-only + clean tree has no branch, so the report commit lands on develop via the `.claude/**` exemption; works, but the placement is a judgment call the SKILL.md doesn't specify → candidate patch (needs its own failing test per Iron Law). (2) premise corrected by USER after the run: prod = native nginx, NOT the repo's Docker stack → container findings (SEC-1/4) latent, live header fix (SEC-2/3) belongs to VPS config outside the repo; audit scoping must confirm the serving stack first ([[LRN-101]] corollary). (3) parallel-phases deviation from the skill's sequential A→D held safely (report-only ⇒ no mutations between phases). - **anomalies**: (1) skill gap — report-only + clean tree has no branch, so the report commit lands on develop via the `.claude/**` exemption; works, but the placement is a judgment call the SKILL.md doesn't specify → candidate patch (needs its own failing test per Iron Law). (2) premise corrected by USER after the run: prod = native nginx, NOT the repo's Docker stack → container findings (SEC-1/4) latent, live header fix (SEC-2/3) belongs to VPS config outside the repo; audit scoping must confirm the serving stack first ([[LRN-101]] corollary). (3) parallel-phases deviation from the skill's sequential A→D held safely (report-only ⇒ no mutations between phases).
- **action**: keep. Skill validated on real drift; two refinement candidates noted (report-commit placement, serving-stack precheck), neither blocking. - **action**: keep. Skill validated on real drift; two refinement candidates noted (report-commit placement, serving-stack precheck), neither blocking.
## EVAL-016 — /deploy first REAL run (bchanot-cv): bootstrap→instantiate→hand-back→mark, full cycle OK
- **Date**: 2026-07-05
- **output**: bootstrap Path B (4-field interview → @delta-annotated PROCEDURE.md + seeded INCIDENTS, commit `5fe8b41` via deploy-commit.sh rc=0) → first deploy: base null → delta = full tree (26 files), `@delta:rebuild when=` matched → NEXT.sh 3 steps → GATE all → PENDING.json bridge → hand-back → user "Deployed OK" → MARK: STATE.json (`deployed_sha` = bridge target, NOT HEAD), local tag `deploy/2026-07-05`, oracle commit `395c77b`, bridge consumed, tree clean.
- **method**: real prod deploy (VPS). Independent live proof post-mark: curl bchanot.fr → 200 + nosniff + X-Frame-Options + CSP + HSTS + versionless server — tour SEC-2 fixed end-to-end, tour→prod loop closed.
- **anomalies**: (1) NOT exercised: cold cross-session resume + STEP 4 learn (0 incidents) — natural test at next deploy/failure. (2) UX gap, user feedback: compound `ssh host "cd … && …"` one-liners ≠ wanted session style (one command per line), and the checklist lived only on disk — skill patched same day (step=block grammar, shape rule, hand-back prints NEXT.sh inline; template + bchanot-cv runbook restyled). Re-dogfood at next deploy.
- **action**: keep. Two-moment contract works in-session; disk artifacts coherent throughout.
+1
View File
@@ -335,3 +335,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).
- /tour first real run (report-only, bchanot-cv): 14 findings (1 high, 3 med — top value: nginx add_header inheritance wipes all security headers live, [[LRN-101]]), reconcile zero-drift, REFACTOR additions validated ([[EVAL-015]]). User corrected infra premise post-run: prod = NATIVE nginx, Docker stack latent → auto tour rescoped (ask-prod-info gate authorized by user). - /tour first real run (report-only, bchanot-cv): 14 findings (1 high, 3 med — top value: nginx add_header inheritance wipes all security headers live, [[LRN-101]]), reconcile zero-drift, REFACTOR additions validated ([[EVAL-015]]). User corrected infra premise post-run: prod = NATIVE nginx, Docker stack latent → auto tour rescoped (ask-prod-info gate authorized by user).
- /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).
+14 -1
View File
@@ -7,9 +7,22 @@ Wording patched to "deleted at release finish" — actual deletion pending. At
release finish: release finish:
- [ ] `git tag -d v4.0.0 && git push origin :refs/tags/v4.0.0` - [ ] `git tag -d v4.0.0 && git push origin :refs/tags/v4.0.0`
- [ ] confirm v1.0.0 tag sorts latest → update-check banner stops offering v4.0.0 - [ ] confirm v1.0.0 tag sorts latest → update-check banner stops offering v4.0.0
- [ ] back-merge develop : dédoublonner les entrées /tour + impeccable du - [ ] back-merge develop : dédoublonner les entrées /tour + impeccable + /deploy-UX + settings du
CHANGELOG (develop les liste sous [Unreleased], release les a shippées CHANGELOG (develop les liste sous [Unreleased], release les a shippées
sous [1.0.0] — merges 07-05) sous [1.0.0] — merges 07-05)
## 2026-07-05 — /deploy UX patch (feature/deploy-next-style)
Feedback user au 1er run réel (bchanot-cv, [[EVAL-016]]) : NEXT.sh une commande
par ligne (style session — ssh ouvre la box, la suite s'exécute dessus, local =
"(from your machine)") + hand-back AFFICHE la checklist inline (aussi aux
re-hand-back). Step = bloc (header + lignes jusqu'à ligne vide), @delta
gouverne le bloc entier.
- [x] skills/deploy/SKILL.md — grammaire bloc-étape + shape rule + print inline
- [x] templates/deploy/PROCEDURE.md — restylé session
- [x] bchanot-cv runbook restylé, committé, pushé (bd7f6e4, develop sync)
- [x] settings.json +inputNeededNotifEnabled (layout committé inchangé)
- [x] Capitalize EVAL-016 + journal
- [ ] Re-dogfood au prochain /deploy réel (edit de skill non re-testé par run —
dette Iron Law assumée, même statut que la note d'authoring du skill)
## 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
+2 -1
View File
@@ -33,7 +33,8 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
### Changed ### Changed
- **Versioning reset to 1.0.0** — see the note above; the pre-reset `v4.0.0` tag is retired and the lineage restarts here. - **Versioning reset to 1.0.0** — see the note above; the pre-reset `v4.0.0` tag is retired and the lineage restarts here.
- `settings.json`: the default model is pinned to **Opus 4.8 (1M context)** (`claude-opus-4-8[1m]`). - `/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.
- `settings.json`: the default model is pinned to **Opus 4.8 (1M context)** (`claude-opus-4-8[1m]`), and `inputNeededNotifEnabled: true` is adopted (harness notification toggle) — committed layout otherwise unchanged.
- **AI attribution trailers disabled** (`attribution` in `settings.json`): `Co-Authored-By` + `Claude-Session` lines are no longer emitted on commits and PRs — they leaked session URLs and cluttered messages. - **AI attribution trailers disabled** (`attribution` in `settings.json`): `Co-Authored-By` + `Claude-Session` lines are no longer emitted on commits and PRs — they leaked session URLs and cluttered messages.
### Fixed ### Fixed
+2 -1
View File
@@ -315,5 +315,6 @@
}, },
"effortLevel": "xhigh", "effortLevel": "xhigh",
"remoteControlAtStartup": true, "remoteControlAtStartup": true,
"skipAutoPermissionPrompt": true "skipAutoPermissionPrompt": true,
"inputNeededNotifEnabled": true
} }
+18 -2
View File
@@ -99,6 +99,14 @@ A directive sits on the comment line **above** the step it governs; patterns are
matched against the delta file list. Un-annotated step = **fixed**, always matched against the delta file list. Un-annotated step = **fixed**, always
emitted verbatim. emitted verbatim.
**A step is a block**: its `# n)` comment header plus every command line below
it, up to the next blank line. A directive governs the whole block. Steps are
written **one command per line, interactive-session style** — an early fixed
step opens the box (`ssh "$DEPLOY_HOST"`), the lines after it run *on* the box
as you would type them; a step that runs locally says `(from your machine)` in
its header. Never fold `ssh host "cd … && …"` compounds: the user copy-pastes
line by line. Each `# VERIFY:` sits at the end of the command line it gates.
| Directive | Meaning | Instantiation | | Directive | Meaning | Instantiation |
|-----------|---------|---------------| |-----------|---------|---------------|
| `# @delta:<kind> glob=<pat>:each` | per-file command | repeat the command once **per** matching delta file (file substituted in) | | `# @delta:<kind> glob=<pat>:each` | per-file command | repeat the command once **per** matching delta file (file substituted in) |
@@ -275,7 +283,9 @@ Set the base, compute the changed-file list, capture the target.
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 file: *"Run by hand, step by step.
Never `bash NEXT.sh` unattended."* Never `bash NEXT.sh` unattended."*
4. Write `.claude/deploy/NEXT.sh`. 4. Preserve the runbook's shape: one command per line, session style (see the
`@delta:` grammar section) — instantiation never re-folds lines.
5. Write `.claude/deploy/NEXT.sh`.
**[GATE] — present `NEXT.sh` → `all / edit / skip-all`.** **[GATE] — present `NEXT.sh` → `all / edit / skip-all`.**
- `all` → proceed. `edit` → revise the listed steps, re-present. - `all` → proceed. `edit` → revise the listed steps, re-present.
@@ -288,9 +298,15 @@ 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** (AskUserQuestion): *"Run NEXT.sh step by step against prod. **Then HAND BACK — the checklist lands in the conversation, not just on disk.**
Print the FULL final `NEXT.sh` content inline (fenced code block) so the user
sees exactly what to run without opening the file — the gate preview is not
enough (an `edit` round may have changed it; the hand-back shows the final
text). Then (AskUserQuestion): *"Run NEXT.sh step by step against prod.
Report back: **Deployed OK** / **Failed at step X: <err>** / **Not yet**."* Then Report back: **Deployed OK** / **Failed at step X: <err>** / **Not yet**."* Then
**stop** — control is the user's; `PENDING.json` on disk now marks the wait. **stop** — control is the user's; `PENDING.json` on disk now marks the wait.
The same rule applies to every re-hand-back (STEP 4.3): regenerated `NEXT.sh`
⇒ reprinted in full.
## STEP 3 — RESUME / REACT ## STEP 3 — RESUME / REACT
+20 -11
View File
@@ -4,22 +4,31 @@
# @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>);
# glob=<pat>:list runs once + lists matching files as VERIFY items; when=<pat,...> is conditional. # glob=<pat>:list runs once + lists matching files as VERIFY items; when=<pat,...> is conditional.
# Style: one command per line, as typed in an interactive session — step 1 opens
# the ssh session, later steps run ON the box; local steps say "(from your machine)".
# 1) backup BEFORE any forward-only migration # 1) connect + pull the desired branch (fixed)
ssh "$DEPLOY_HOST" 'pg_dump "$DB" > ~/backups/pre-deploy-$(date +%F-%H%M).sql' # VERIFY: dump size > 0 ssh "$DEPLOY_HOST"
cd "$APP_DIR"
git pull # VERIFY: HEAD == target sha
# 2) backup BEFORE any forward-only migration
pg_dump "$DB" > ~/backups/pre-deploy-$(date +%F-%H%M).sql # VERIFY: dump size > 0
# @delta:migrations glob=supabase/migrations/*.sql:list # @delta:migrations glob=supabase/migrations/*.sql:list
# 2) apply NEW migrations (one command; skill lists the delta migrations to VERIFY) # 3) apply NEW migrations (one command; the skill lists the delta migrations to VERIFY)
ssh "$DEPLOY_HOST" 'supabase migration up' # VERIFY: "Applied" for each supabase migration up # VERIFY: "Applied" for each
# @delta:rebuild when=docker-compose*.yml,Dockerfile,Dockerfile.* # @delta:rebuild when=docker-compose*.yml,Dockerfile,Dockerfile.*
# 3) rebuild + restart services (only if build inputs changed) # 4) rebuild + restart services (only if build inputs changed)
ssh "$DEPLOY_HOST" 'docker compose up -d --build' # VERIFY: docker compose ps healthy docker compose up -d --build # VERIFY: docker compose ps healthy
# @delta:deps when=package.json,*lock*,requirements.txt,pyproject.toml # @delta:deps when=package.json,*lock*,requirements.txt,pyproject.toml
# 4) install deps (only if manifests changed) # 5) install deps (only if manifests changed)
ssh "$DEPLOY_HOST" 'cd app && npm ci' # VERIFY: exit 0 cd app
npm ci # VERIFY: exit 0
# 5) reload cache + smoke test (fixed) # 6) reload + smoke test
ssh "$DEPLOY_HOST" 'systemctl reload app' systemctl reload app
curl -fsS https://$DEPLOY_HOST/health # VERIFY: HTTP 200 # (from your machine)
curl -fsS https://$DEPLOY_HOST/health # VERIFY: HTTP 200