From 345e43731c4af60c6e829bffcec6c00ce289cf1d Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Sat, 27 Jun 2026 03:22:12 +0200 Subject: [PATCH] =?UTF-8?q?chore(memory):=20BDR-036=20+=20LRN-058..060=20+?= =?UTF-8?q?=20EVAL-008=20=E2=80=94=20doc-sync=20coupled=20close?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Capitalize the doc-sync coupled chantier: BDR-036 (the invariant, 3 honesties engraved — built-not-reordered, MINOR non-gated surface-replaces-gate, init-project partial + sweep scope-expansion); LRN-058 (same bug-class != same fix — verify the twin's precondition); LRN-059 (swap flips meanings, sweep caught prior-chantier debt README:153 != letter-suffix insertion); LRN-060 (fail-closed guard proven by what it refuses, loudly; argv not separator-string); EVAL-008 (28/28 real-exec, anomalies surfaced). Journal 2026-06-27. BLK-010/011 flags + the frozen plan + TODO checkmarks. BLK-011 record left at STEP 13 (append-only); only the TODO locator moved to STEP 12 (live locator vs immutable record). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Ho5EQCFTSvYamuRtVZpp2d --- .claude/memory/blockers.md | 22 +++++ .claude/memory/decisions.md | 17 ++++ .claude/memory/evals.md | 10 +++ .claude/memory/journal.md | 4 + .claude/memory/learnings.md | 27 ++++++ .claude/tasks/2026-06-27-doc-sync-coupled.md | 90 ++++++++++++++++++++ .claude/tasks/TODO.md | 19 ++++- 7 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 .claude/tasks/2026-06-27-doc-sync-coupled.md diff --git a/.claude/memory/blockers.md b/.claude/memory/blockers.md index f40dc74..b74c5f5 100644 --- a/.claude/memory/blockers.md +++ b/.claude/memory/blockers.md @@ -27,6 +27,8 @@ rules: | BLK-005 | 2026-05-21 | gstack submodule rename (checkpoint→context-save) breaks profile entries | resolved | | BLK-006 | 2026-05-21 | `profile.sh current` false-negative via `~/.claude` symlink (`cd` not `cd -P`) | resolved | | BLK-007 | 2026-06-02 | 6 gstack source skills (ios-*, spec) unlinked post-bump — invisible to profiles + `gstack on` | resolved | +| BLK-010 | 2026-06-27 | init-project: scaffold (STEP 5) + bootstrap README (5b) have no deterministic commit owner; worktree `add -b` on unborn HEAD | open | +| BLK-011 | 2026-06-27 | init-project STEP 13 GSD post-FINISH creates ROADMAP.md → stranded doc (3rd post-FINISH artifact) | open | --- @@ -117,3 +119,23 @@ rules: - **Probe method**: 3-file probe — `_probe.md` (`paths: ["**/*.probe"]`, sentinel `SENTINEL_USER_RULE_LOADED`), `_probe_ctl.md` (NO `paths`, control sentinel `CONTROL_NOPATHS_LOADED`), `_probe_target.probe` (target, read in a fresh session). Result: control sentinel PRESENT in session context, path-scoped sentinel ABSENT → the path-scoped rule did not load. Probe files removed after. - **Status**: upstream, open. Workaround: don't rely on user-level path-scoping → keep global guidance unconditional + COMPRESSED ([[BDR-031]]). Side-note: native auto-memory = "on" but writes nothing yet (fresh machine). Re-test on CC upgrades. - **Reference**: GitHub #21858. Linked to [[BDR-031]], [[LRN-044]]. + +--- + +## BLK-010 — init-project scaffold + bootstrap README have no deterministic commit owner; worktree on unborn HEAD + +- **Date**: 2026-06-27 +- **Friction**: init-project scaffold (STEP 5 — CLAUDE.md, settings, config, entry points, `.gitignore`, `.env.example`, `.claude/`) + bootstrap README (STEP 5b) never get an explicit commit. Pipeline's only commits = STEP 10b memory (helper) + STEP 8 per-task implementer commits. Whether scaffold/README land in a commit = emergent: implementer-prompt.md says only "4. Commit your work", scope undefined. Greenfield deeper: STEP 8 `subagent-driven-development` requires `using-git-worktrees` → `git worktree add -b` branches from HEAD, but post-`git init` HEAD is UNBORN → add fails; the worktree skill has no unborn-HEAD path. +- **Real cause**: no deterministic commit step between `git init` (STEP 5) and FINISH (STEP 11). scaffolder + doc-syncer both write-only (zero `git commit`). implementer commit scope unspecified. `using-git-worktrees` assumes a born HEAD. +- **Solution**: open — own chantier (real technical weight: unborn HEAD + worktree). Candidate: explicit initial scaffold commit after STEP 5/5b before STEP 8, OR handle unborn HEAD in the worktree step. NOT cured by the doc-sync coupled chantier — that commits ONLY doc-sync's patched files and (correctly) excludes scaffold. Consequence: after doc-sync coupled, ship-feature fully fixed, init-project PARTIAL (doc-sync ok, scaffold/bootstrap still open). +- **Status**: open +- **Reference**: discovered in doc-sync-coupled analysis (2026-06-27). Distinct from the doc-sync twin [[BDR-034]]. Sibling [[BLK-011]]. Surfaces via analyze-before-plan bookend on any init-project commit-flow work. + +## BLK-011 — init-project STEP 13 GSD post-FINISH creates ROADMAP.md → stranded doc + +- **Date**: 2026-06-27 +- **Friction**: init-project STEP 13 (GSD v2 init) runs post-FINISH (STEP 11). `gsd init` creates `.gsd/` + `ROADMAP.md` (a public doc). Created AFTER FINISH integrates → ROADMAP never in the merge/PR. Same PR-stranding class as the doc-sync twin, 3rd post-FINISH artifact. +- **Real cause**: artifact-producing step ordered after FINISH (= BDR-034 class). `gsd init` is a CLI mechanism distinct from doc-syncer; ROADMAP is sync-only for doc-syncer (never created by it, BDR-022 rules), so the doc-sync coupled chantier does not touch it. +- **Solution**: open — separate thread. Candidate: reorder GSD before FINISH, or commit ROADMAP after `gsd init`. Out of scope for doc-sync coupled (different mechanism). +- **Status**: open +- **Reference**: discovered in doc-sync-coupled analysis (2026-06-27). Sibling [[BLK-010]] + twin [[BDR-034]]. diff --git a/.claude/memory/decisions.md b/.claude/memory/decisions.md index e583814..c1075e8 100644 --- a/.claude/memory/decisions.md +++ b/.claude/memory/decisions.md @@ -46,6 +46,7 @@ rules: | BDR-023 | 2026-06-19 | Merge /close into /capitalize — 2 modes + TODO reconcile; /close alias | accepted | | BDR-034 | 2026-06-26 | Coupled-capitalize invariant v1 — memory commit auto per dev flow (Frame 2) | accepted | | BDR-035 | 2026-06-26 | Analyze-before-plan invariant v1 — read-before bookend of coupled-capitalize | accepted | +| BDR-036 | 2026-06-27 | Doc-sync coupled invariant — commit docs doc-syncer patches (twin of BDR-034, BUILT not reordered) | accepted | --- @@ -565,3 +566,19 @@ rules: - Extend analyzer only — inline flows (feat/bugfix/hotfix) never call analyzer pre-plan → would close Gap B for none. Needed both: include + analyzer RELATED MEMORY section. - PASS-2 skip-if-already-in-context — no deterministic oracle for "in context"; reintroduces the behavioral guard. See [[LRN-054]]. - **Reference**: commit `67c6a81`, `lib/analyze-before-plan.md`, `agents/analyzer.md`. Bookend of [[BDR-034]]. See [[LRN-053]], [[LRN-054]], [[LRN-055]], [[LRN-056]], [[LRN-057]]. + +## BDR-036 — Doc-sync coupled invariant — commit the docs doc-syncer patches (twin of BDR-034, BUILT not reordered) + +- **Date**: 2026-06-27 +- **Status**: accepted +- **Decision**: doc-sync flows now COMMIT the public docs doc-syncer patches, via new `lib/doc-commit.sh` (helper) + `lib/doc-commit.md` (include) — mirror of memory-commit/capitalize-commit, 4 DELTAS: (Δ1) dynamic scope = patched files as argv, not a fixed pathspec; (Δ2) INVERSE exclusion = fail-closed + loud guard rejecting `.claude/**`+`CLAUDE.md` (dedicated exit 4), opposite of memory-commit which TARGETS `.claude/`; (Δ3) no hash anchoring (docs carry no SHA, [[LRN-052]]); (Δ4) `docs:` msg. doc-syncer emits `PATCHED_FILES` (one path/line) → agent splits on newline → each as DISTINCT argv (space-safe, [[LRN-060]]). 2 orchestrators reordered DOC SYNC before FINISH (ship-feature STEP 9→8, init-project STEP 12→10c, GSD 13→12); 3 inline flows wired (feat/bugfix/hotfix DOC SYNC). Consumption MECHANICAL ([[LRN-057]] case a, = BDR-034). +- **Why**: doc-syncer PATCHED docs but COMMITTED nothing (grep-proven, zero git commit) → push/PR path = docs stranded outside PR (orchestrators); inline = docs left dirty. Twin of [[BDR-034]] but NOT same fix: memory ALREADY had a commit helper (only mis-timed); doc-sync had NONE → had to BUILD the mechanism, not just reorder. "Reorder alone" (the deferred note's framing) REFUTED in read-phase ([[LRN-058]]). +- **Honest scope/choices** (engraved, not glossed): + - (a) MINOR doc content stays NON-gated yet auto-committed — CONSCIOUS, not memory's always-gated content; the VISIBLE surface (files + AGENT-composed change summary, not a bare count) REPLACES the gate as the review surface. Strengthening the MINOR gate = separate doc-syncer chantier. + - (b) init-project PARTIAL — scaffold + bootstrap-README commit gap ([[BLK-010]], unborn HEAD + worktree) + GSD ROADMAP post-FINISH ([[BLK-011]]) deferred = NEW work, not replication. + - (c) scope EXPANDED mid-chantier via the ref-sweep to 3 inline flows — asymmetry vs memory (BDR-034 wired ALL flows) was the decider. +- **Alternatives rejected**: + - Reorder-only (the deferred note) — refuted: doc-syncer commits nothing, reordering uncommitted docs still misses the merge. + - Static-glob scope (`*.md`/`docs/`) — over-reach onto a user-edited doc / `MIGRATION.md`; chose touched-files argv (in-thread list already in hand). + - Silent-filter the forbidden path — masks an upstream BDR-022 bug; guard must REFUSE-ALL loudly ([[LRN-060]]). +- **Reference**: commits `ae1f218` (helper+tests) · `4a54a65` (include) · `fb1f359` (doc-syncer PATCHED_FILES) · `636b491` (ship-feature reorder) · `e81f629` (init-project reorder) · `1b01b95` (3 inline flows). See [[BDR-034]], [[LRN-058]], [[LRN-059]], [[LRN-060]], [[BLK-010]], [[BLK-011]], [[EVAL-008]]. diff --git a/.claude/memory/evals.md b/.claude/memory/evals.md index bce6710..417494f 100644 --- a/.claude/memory/evals.md +++ b/.claude/memory/evals.md @@ -28,6 +28,7 @@ rules: | EVAL-005 | 2026-06-23 | Obsolete `claude --effort max` alias missed across Step 9 edits | correct | | EVAL-006 | 2026-06-25 | prune-memory v1.1 TDD — 6 guards (0a3e766), validated on real data | keep | | EVAL-007 | 2026-06-26 | Coupled-capitalize machinery — TDD 13 + e2e, surgical scope proven | keep | +| EVAL-008 | 2026-06-27 | Doc-sync coupled machinery — 28/28 real-exec, swap-sweep caught prior debt | keep | --- @@ -96,3 +97,12 @@ rules: - **Anomalies**: (1) `git commit -- pathspec` strict-on-no-match — caught by real-exec, would have silently aborted the commit on the majority of flows (any run where one scoped path is clean) → fixed by `_changed_paths` BEFORE integration ([[LRN-051]]). (2) v1 helper emitted no clean hash → caught at include design (the reported `` would have been aspirational) → added `bbef41c` (hash→stdout, diag→stderr), proven by T6. Both caught by exec/review, not assumed. - **Action**: keep. - **Reference**: commits `58cb91d`..`df60df6`. + +## EVAL-008 — Doc-sync coupled machinery (helper + include + 2 reorders + 3 inline) + +- **Date**: 2026-06-27 +- **What checked**: `lib/doc-commit.sh` + `lib/doc-commit.md` include + doc-syncer `PATCHED_FILES` output + 2 orchestrator reorders (ship-feature, init-project) + 3 inline wirings (feat/bugfix/hotfix). +- **Method**: 28/28 real-exec deterministic (`run-doc-commit.sh` T1a/b/c + T2–T7 — incl. inverse-exclusion REFUSE, MIXED refuse-all, argv space-safe T7), shellcheck clean, behavioral check doc (`run-doc-behavioral.md`, 2 scenarios), full external ref-sweep + per-ref verification. +- **Output**: 6 surgical commits `ae1f218` · `4a54a65` · `fb1f359` · `636b491` · `e81f629` · `1b01b95`. Caught + fixed a PRIOR-chantier latent ref bug (README:153, stale since e8eff7e's swap). Scope expanded mid-chantier (sweep found the inline-flow gap → 3 flows wired). +- **Anomalies**: (1) the deferred note ("reorder only") was WRONG → corrected in read-phase before any code ([[LRN-058]]). (2) init-project PARTIAL — [[BLK-010]]/[[BLK-011]] deferred = NEW work, surfaced not papered over. Both engraved in [[BDR-036]]. +- **Action**: keep. BLK-010 (scaffold/unborn-HEAD) + BLK-011 (GSD ROADMAP post-FINISH) + MINOR-gate strengthening = separate chantiers. diff --git a/.claude/memory/journal.md b/.claude/memory/journal.md index 5524577..d2e4314 100644 --- a/.claude/memory/journal.md +++ b/.claude/memory/journal.md @@ -207,3 +207,7 @@ rules: - Caught `git commit -- pathspec` strict-on-no-match by real-exec test (would silent-abort on majority of flows) → `_changed_paths` filter (LRN-051). ship-feature reordered CAPITALIZE→before FINISH (fixes memory stranded outside PR). init-project STEP 10b founding decisions (no hash by nature, LRN-052). Hook v2 + doc-sync twin chantier deferred. - TDD: 13 deterministic + in-vivo e2e, shellcheck clean (EVAL-007). Pre-existing Index drift (decisions 11, learnings 21 rows missing) noted for /prune-memory — not backfilled here. - analyze-before-plan v1 — read-before bookend of coupled-capitalize. Include `lib/analyze-before-plan.md` (two-pass on `## ID` headings, disposition-not-reading invariant, guarded no-op). Wired: ship-feature 0d (inject+reconcile gate), bugfix 2.5, feat 0.6, hotfix opt-in; init/onboard no-op (test-backed). Index drift measured exact: decisions 11/34, learnings 21/52, blockers 2/9. Code commit 67c6a81. BDR-035, LRN-053/054/055/056/057. + +## 2026-06-27 +- Doc-sync coupled invariant (twin of BDR-034, BUILT not reordered): new `lib/doc-commit.sh` (inverse-scope surgical, fail-closed exit 4 on `.claude/`) + `lib/doc-commit.md` include; doc-syncer emits `PATCHED_FILES` (one path/line) → agent → distinct argv (space-safe). 2 orchestrators reordered DOC SYNC before FINISH (ship-feature 9→8, init-project 12→10c, GSD 13→12), 3 inline flows wired (feat/bugfix/hotfix). 6 commits `ae1f218` · `4a54a65` · `fb1f359` · `636b491` · `e81f629` · `1b01b95`. 28/28 real-exec, shellcheck clean. BDR-036, LRN-058/059/060, EVAL-008. +- Sweep caught PRIOR-chantier debt (README:153 stale since e8eff7e's swap) + expanded scope to 3 inline flows (asymmetry vs memory was decider). Swap flips meanings ≠ letter-insertion (LRN-059). Deferred note "reorder only" refuted in read-phase — doc-syncer commits nothing (LRN-058). BLK-010 (scaffold/unborn HEAD + worktree) + BLK-011 (GSD ROADMAP post-FINISH) deferred = new work. diff --git a/.claude/memory/learnings.md b/.claude/memory/learnings.md index d4f1ee2..8c500f7 100644 --- a/.claude/memory/learnings.md +++ b/.claude/memory/learnings.md @@ -56,6 +56,9 @@ rules: | LRN-055 | 2026-06-26 | Body `## ID —` headings = drift-immune index; the `## Index` table is not | choosing a substrate to index/select over | | LRN-056 | 2026-06-26 | `grep PAT dir/*.md` on absent dir ERRORS (exit 2), not no-op → guard `[ -d ]` | any glob-fed scan that must no-op on nothing | | LRN-057 | 2026-06-26 | Match consumption mechanism to consumer (mechanical / external-cognitive / inline) | wiring any produce→consume invariant | +| LRN-058 | 2026-06-27 | Same bug-class ≠ same fix — verify the twin shares the fix's PRECONDITION before replicating | porting a fix to a "same bug" twin | +| LRN-059 | 2026-06-27 | Step-number SWAP flips meanings (sweep refs) ≠ letter-suffix insertion (shifts nothing) | any pipeline renumber | +| LRN-060 | 2026-06-27 | Fail-closed guard proven by what it REFUSES (loudly); pass dynamic lists as argv not separator-string | automated scoped-commit / destructive guards | --- @@ -706,3 +709,27 @@ rules: - **Context**: analyze-before-plan ([[BDR-035]]). ship-feature brainstorm = external-cognitive → STEP 0d injection + STEP 3 expose-for-review gate; feat/bugfix = inline-cognitive → natural + trace, no injection. The asymmetry vs [[BDR-034]] (mechanical merge) was the chantier's hardest point. - **Future application**: wiring ANY produce→consume invariant — classify the consumer first (mechanical / external-cognitive / inline-cognitive), pick the lightest sufficient mechanism. Stops reflexively importing orchestrator-grade injection+gate where an inline trace would do. - **Reference**: `skills/ship-feature/SKILL.md` STEP 0d/1/2/3, `agents/bugfixer.md`+`feater.md`. Contrast [[BDR-034]] (mechanical). See [[BDR-035]], [[LRN-053]]. + +## LRN-058 — Same bug-class ≠ same fix: verify the twin shares the fix's PRECONDITION before replicating + +- **Date**: 2026-06-27 +- **Pattern**: A deferred "twin" fix ("doc-sync = same PR bug → reorder before FINISH like memory") REFUTED on inspection: memory's reorder worked because memory ALREADY committed (helper existed, only timing wrong); doc-syncer committed NOTHING → reordering uncommitted docs still misses the merge. The fix relied on a PRECONDITION (artifact already committed) the twin did NOT share. "Same symptom" ≠ "same mechanism". A read-phase grep (zero git commit in doc-syncer) caught it before any code — saved shipping an illusion-of-fix. +- **Context**: doc-sync coupled ([[BDR-036]]). The chantier's central lesson; the user named the trap upfront ("même bug ≠ même fix"). +- **Future application**: any "fix X like we fixed Y" — NAME Y's load-bearing precondition, CONFIRM X has it, before replicating. Cheap read-phase check beats a shipped non-fix. +- **Reference**: [[BDR-036]], [[BDR-034]]. + +## LRN-059 — A step-number SWAP flips meanings → sweep external refs; a letter-suffix insertion shifts nothing + +- **Date**: 2026-06-27 +- **Pattern**: Renumbering a pipeline has two shapes, opposite ref-risk. (1) SWAP (STEP 8↔9 = FINISH↔DOC SYNC) flips what each number MEANS → every external ref can go silently false OR accidentally true; grep the WHOLE repo, read each hit individually. PROVEN: ship-feature's swap silently broke README:153 — which a PRIOR chantier's swap had ALSO broken (e8eff7e moved DOC SYNC 8→9, missed the ref) → debt COMPOUNDS across chantiers. (2) LETTER-SUFFIX insertion (10b, 0d) shifts NO existing number → breaks nothing (init-project's 10b left zero stale refs). Discipline: prefer letter-suffix insertions; on a swap do a full external sweep + per-ref verify; COMPLETE an accidentally-true ref (don't lean on the coincidence — it re-breaks at the next swap). +- **Context**: doc-sync coupled ([[BDR-036]]). The Task-6 sweep caught README:153 (prior debt) + verified 5 USAGE refs post-swap. +- **Future application**: any pipeline renumber — classify swap vs insertion; swap → grep+read every ref. The external sweep catches PAST chantiers' debt, not only the current one. +- **Reference**: [[BDR-036]]. Sibling [[LRN-002]], [[LRN-045]] (grep reads not just writes). + +## LRN-060 — A fail-closed guard is proven by what it REFUSES (loudly); pass dynamic lists as argv, not a separator-string + +- **Date**: 2026-06-27 +- **Pattern**: Two robustness lessons from doc-commit. (a) The inverse-`.claude/` exclusion is a SECURITY guard (BDR-022) → test it by what it must REFUSE (forbidden path ALONE, and MIXED with legit), not only what it accepts; and refuse LOUDLY (dedicated exit 4, names the offender, refuse-ALL on mixed) — silent-filtering would MASK an upstream violation (doc-syncer surfaced a `.claude/` it must never patch). The refusal IS the alarm. (b) Pass a dynamic file list as ARGV, never a separator-joined string: argv has no in-band delimiter → a path with spaces survives as one element (proven, T7); newline is only the producer's text format the agent maps to argv. Space-join-then-resplit would mis-split + the `[ -e ]` filter then silently drops it. +- **Context**: doc-commit.sh ([[BDR-036]]), T1a/b/c (refuse paths) + T7 (argv space-safe), all real-exec. +- **Future application**: any automated scoped-commit / destructive guard — test the REFUSAL path + refuse loud; pass lists as argv. Same family as [[LRN-046]] (deterministic oracle for a destructive guard). +- **Reference**: [[BDR-036]], [[LRN-051]] (changed-paths filter), [[LRN-046]]. diff --git a/.claude/tasks/2026-06-27-doc-sync-coupled.md b/.claude/tasks/2026-06-27-doc-sync-coupled.md new file mode 100644 index 0000000..82c40e9 --- /dev/null +++ b/.claude/tasks/2026-06-27-doc-sync-coupled.md @@ -0,0 +1,90 @@ +# Doc-sync Coupled — Implementation Plan (v1) + +> Frozen 2026-06-27. Twin of coupled-capitalize ([[BDR-034]]), same PR-stranding +> class — but NOT the same fix. The deferred note ("reorder before FINISH") was +> REFUTED in analysis: doc-syncer commits NOTHING (proven, zero `git commit`/`add`), +> so reordering uncommitted docs still misses the merge/PR. Real fix = REORDER **+** +> CREATE a doc-commit mechanism (it does not exist; memory already had one). +> NO `git add -A` ever — safety lives in the (dynamic) pathspec. + +**Goal:** every orchestrator flow that syncs public docs also COMMITS those docs +automatically, on the branch, BEFORE FINISH integrates it — so doc patches reach +the merge/PR instead of stranding uncommitted in the working tree. + +**Architecture:** `lib/doc-commit.sh` (surgical commit of ONLY the files doc-sync +patched this run, passed as args, filtered to changed paths, never `-A`, hard-guard +excluding `.claude/`+`CLAUDE.md`) + include `lib/doc-commit.md` referenced by the 2 +orchestrators at their doc-sync step. Both orchestrators reordered (doc-sync before +FINISH). Mirror of `memory-commit.sh` + `capitalize-commit.md` with **4 deltas**: + +| # | memory-commit (twin) | doc-commit (this) | Why | +|---|---|---|---| +| Δ1 | fixed scope `.claude/memory`+`.claude/tasks` | **dynamic** — patched-file list as args | docs scatter across an enumerable set; commit only what was touched | +| Δ2 | TARGETS `.claude/` | **hard-guard EXCLUDES** `.claude/`+`CLAUDE.md` | BDR-022 — inverse scope; defense-in-depth | +| Δ3 | 2-hash dance (code hash anchored in entries) | **no hash** — docs carry no SHA | LRN-052 — anchoring N-A to docs | +| Δ4 | `chore(memory): ` | `docs: ` | separate concern, mirror | + +**Consumption = MECHANICAL** ([[LRN-057]] case a, = BDR-034): commit on the branch +before FINISH, the merge carries it. No external-cognitive injection needed. + +## Conscious acknowledgments (state them, don't paper over) +- **Partial init-project fix.** After this chantier: ship-feature FULLY fixed; + init-project PARTIAL — doc-sync ok, but scaffold + 5b-bootstrap-README commit + gap stays open ([[BLK-010]]). doc-commit must NOT ramasse the bootstrap README — + not its job (would re-create the over-reach we ban). Do NOT believe init-project + repaired while the scaffold hole remains. +- **MINOR doc content is non-gated yet auto-committed.** This is a CONSCIOUS choice, + NOT "same as memory": memory CONTENT was always gated, so auto-commit only ever + embarked approved entries. doc-sync auto-mode patches MINOR silently (no gate). + Resolution: surface-don't-block. MINOR is factual (command/param/path/version/dead + link — same bar as AUTO patches); a blocking gate = friction disproportionate, and + the PR diff re-shows it. The doc-commit's **visible** surface REPLACES the gate as + the review surface — `✅ committed README, USAGE — `, + NOT a bare count. Strengthening the MINOR gate itself = separate doc-syncer chantier. + +## Global Constraints (verbatim, apply to every task) +- Stage/commit ONLY the files doc-sync patched this run, passed as args. NEVER `git add -A` / `git add .` / `git commit -a`. NEVER stage anything under `.claude/**` or `CLAUDE.md` (hard guard — BDR-022, inverse of memory-commit). +- Dynamic pathspec: filter the passed list to paths with real pending changes (LRN-051 — `git commit -- ` ABORTS the whole commit; `git add` tolerates). A clean/absent passed path is dropped, not fatal. +- Partial-commit safety: `git commit -- ` ignores the rest of the index → dangling code (untracked OR pre-staged) is never embarked. +- Idempotent: empty list / clean tree → no-op, exit 0, no commit. +- Fail-closed: detached HEAD / merge / rebase / cherry-pick in progress → no commit, skip (exit 3). +- NO hash anchoring — docs carry no SHA (LRN-052). Commit msg `docs: `. +- Surface is VISIBLE: report committed files + a one-line change summary, not just a count. +- shellcheck clean on `lib/doc-commit.sh` + test harness. + +## File Structure +| Action | File | Responsibility | +|---|---|---| +| Create | `lib/doc-commit.sh` | dynamic-scope surgical doc commit (CLI + sourceable), inverse-exclusion guard | +| Create | `lib/doc-commit.md` | include protocol (mirror capitalize-commit, 4 deltas, visible surface) | +| Create | `lib/tests/run-doc-commit.sh` | TDD: inverse-exclusion + dynamic-pathspec + dangling + stale-index + idempotent + unsafe, real git fixture | +| Modify | `agents/doc-syncer.md` | add `PATCHED_FILES:` block to OUTPUT (STEP 9) + AUTO MODE (STEP A4); no logic change, callers unaffected | +| Modify | `agents/feater.md` · `bugfixer.md` · `hotfixer.md` | **Task 6b (sweep-found scope expansion)** — wire doc-commit into each DOC SYNC step | +| Modify | `skills/ship-feature/SKILL.md` | reorder DOC SYNC before FINISH + doc-commit include; DELETE HTML comment (lines 196–198); renumber FAILURE PATHS / FINAL OUTPUT | +| Modify | `skills/init-project/SKILL.md` | move SYNC README before FINISH (new STEP 10c) + doc-commit include; renumber `/13` PROGRESS PROTOCOL headers; conscious partial-fix note ([[BLK-010]]) | +| Modify | `README.md` · `USAGE.md` · `CHANGELOG.md` | ref-sweep (README:153, USAGE:196/256) + changelog entry | +| Append | `.claude/memory/` (BDR + LRN) | document the invariant at close | +| Append | `.claude/memory/blockers.md` | BLK-010 scaffold gap, BLK-011 GSD ROADMAP — **done this turn** | + +## Tasks +- **Task 1** — `lib/doc-commit.sh` (TDD, same hard requirement as memory-commit: + every test REALLY EXECUTED with real outputs reported before Task 2 — no presumed + git behavior; Δ2 + Δ1 especially must be proven on real git). + - **T1 inverse exclusion (Δ2):** a passed path under `.claude/` (e.g. `.claude/memory/x.md`) or `CLAUDE.md` is REJECTED — not committed, guard fires. The load-bearing delta vs memory-commit; prove it on real git. + - **T2 dynamic pathspec (Δ1):** pass `[README.md, USAGE.md, DEPLOY.md]` where only README+USAGE changed → commit contains exactly README+USAGE; the clean DEPLOY.md path is filtered, commit does NOT abort (LRN-051). + - **T3 dangling not embarked:** untracked AND pre-staged non-doc code (`src/x`) NOT in the doc commit, stays untracked/staged. + - **T4 stale-index:** doc staged as version A, working-tree version B → commit contains B (`git add --` re-stage neutralizes stale index). Mirror of memory T2-bis. + - **T5 idempotent:** empty list / clean → no-op exit 0, no commit. **T6 unsafe:** detached HEAD / merge in progress → exit 3, no commit. +- **Task 2** — `lib/doc-commit.md` include (WHEN / DO / HARD RULE surgical / ORDERING before FINISH / IDEMPOTENT / VISIBLE-SURFACE report / no-hash note / inverse-scope vs capitalize-commit). State the 2 conscious acknowledgments inline. +- **Task 3** — `agents/doc-syncer.md`: add `PATCHED_FILES:` (newline-separated real paths, or empty) to STEP 9 OUTPUT + AUTO MODE STEP A4. Additive, no logic change, `auto-mode scope:` contract unchanged → callers unaffected (BDR-022 preserved). Future-proofs the isolated-subagent invocation; in-thread, the list is already in hand. +- **Task 4** — ship-feature reorder: STEP 8 = DOC SYNC (was 9, + doc-commit include), STEP 9 = FINISH (was 8). DELETE the twin-chantier HTML comment. Renumber FAILURE PATHS + FINAL OUTPUT refs. Pipeline stays "9-step". +- **Task 5** — init-project reorder: new STEP 10c = DOC SYNC (moved from 12, + doc-commit include) after STEP 10b CAPITALIZE, before STEP 11 FINISH; old STEP 13 GSD → STEP 12. Update PROGRESS PROTOCOL `/13` headers. Add the conscious partial-fix note pointing at [[BLK-010]] (scaffold/bootstrap still open) + [[BLK-011]] (GSD ROADMAP). +- **Task 6** — ref-sweep: README:153, USAGE:196, USAGE:256, CHANGELOG. Grep READS not just WRITES (LRN-002/LRN-045). Result: live refs all fixed in Task 4/5, no old headers survive, historicals left. **Sweep also caught the inline-flow gap → Task 6b.** +- **Task 6b — SCOPE EXPANSION (sweep-found, NOT in the original frozen plan — honesty).** feat/bugfix/hotfix each have a DOC SYNC step that patched docs but committed nothing → docs left dirty (milder than PR-strand, same class; the asymmetry vs memory is the decider — BDR-034 wired ALL flows). Wire doc-commit into each (1-line include + paragraph, mirror of capitalize-commit). Set = the 3 flows that doc-sync, NOT all 4 capitalize flows: commit-change has no DOC SYNC. hotfix IS wired (its DOC SYNC is unconditional; only its CAPITALIZE is skip-by-default; the include no-ops on empty). We extend the mechanical REPLICATION of a built+tested mechanism; we defer NEW work (BLK-010/011). +- **Task 7** — behavioral verify doc (`lib/tests/` end-to-end check, ship-feature + init-project paths) + shellcheck + CHANGELOG entry + close with BDR + LRN. The closing BDR states the surface-replaces-gate choice HONESTLY (MINOR non-gated auto-committed) — not glossed as memory-equivalent. + +## Deferred / flagged separate (by design, not omission) +- **[[BLK-010]]** scaffold + bootstrap-README commit gap (init-project; unborn HEAD + worktree) → own chantier. **Flagged this turn.** +- **[[BLK-011]]** GSD STEP 13 ROADMAP.md post-FINISH (3rd post-FINISH artifact) → own thread. **Flagged this turn.** +- Strengthening doc-sync's MINOR gate → separate doc-syncer chantier. +- doc-sync as isolated subagent (vs in-thread) → `PATCHED_FILES:` already future-proofs it; no work now. diff --git a/.claude/tasks/TODO.md b/.claude/tasks/TODO.md index 0a42cb8..f82c22e 100644 --- a/.claude/tasks/TODO.md +++ b/.claude/tasks/TODO.md @@ -246,4 +246,21 @@ doc-sync twin chantier deferred. Safety in the pathspec, never `git add -A`. - [x] Task 5 — init-project founding-decisions capitalize (F5) — df60df6 - [x] Task 6 — behavioral verify + shellcheck + CHANGELOG + BDR/LRN — this commit - [ ] v2 (deferred) — Stop hook (non-blocking, BDR-033 style) reusing the detector -- [ ] twin chantier (deferred) — doc-sync reorder before FINISH (same PR bug) +- [~] twin chantier — doc-sync → own plan (2026-06-27). NOTE: "reorder before FINISH" REFUTED — doc-syncer commits nothing, needs reorder + NEW doc-commit mechanism. + +## 2026-06-27 — doc-sync coupled (twin of coupled-capitalize) +Plan: [.claude/tasks/2026-06-27-doc-sync-coupled.md](2026-06-27-doc-sync-coupled.md) +Goal: orchestrators commit the docs doc-sync patched, on the branch, BEFORE FINISH. +Same PR-bug class as memory, NOT same fix: doc-syncer commits nothing (proven) → +reorder + CREATE doc-commit.sh/.md (mirror memory-commit, 4 deltas). Surface-don't-block. +- [x] Task 1 — `lib/doc-commit.sh` + `lib/tests/run-doc-commit.sh` — 24/24 real-exec pass, shellcheck clean. T1a/b/c (guard catches .claude/+CLAUDE.md, mixed→refuse-all-loud) + T2 dynamic pathspec + T3/T4/T5/T6. Exit taxonomy 0/2/3/4 (4=scope violation). +- [x] Task 2 — `lib/doc-commit.md` include — 4a54a65. 4-exit report table (rc 4 = loud upstream anomaly), visible surface w/ agent-composed summary (attribution locked 3×), 2 conscious acks. +- [x] Task 3 — `agents/doc-syncer.md` `PATCHED_FILES:` OUTPUT — fb1f359. Newline (one path/line), both STEP 9 + AUTO A4; NONE silent. Separator contract aligned producer↔consumer, argv space-safe, T7 proves it (28/28). Additive, callers unaffected. +- [x] Task 4 — ship-feature reorder — 636b491. DOC SYNC 9→8 (+doc-commit), FINISH 8→9, HTML comment deleted. Ref-coherence: 159/189 STEP 8→9 FINISH + README:152-153 illustration completed (stale since e8eff7e). Historical records left (append-only). +- [x] Task 5 — init-project reorder — e81f629. SYNC README 12→10c (+doc-commit), GSD 13→12, /13→/12. Order 10b→10c→11→12. Ref-coherence: USAGE ×5 (table, illustration, 3 GSD refs) each verified post-swap. Latent-bug check: none (10b was non-shifting). BLK-011 record left (append-only), TODO locator→12. +- [x] Task 6 — ref-sweep — clean (no old headers; live refs fixed in Task 4/5; historicals left; USAGE:256 non-ordering). Caught inline-flow gap → Task 6b. +- [x] Task 6b — wire doc-commit into feat/bugfix/hotfix DOC SYNC — 1b01b95. commit-change exempt (no DOC SYNC); hotfix wired (include no-ops on empty). +- [x] Task 7 — close: `run-doc-behavioral.md` + shellcheck clean + 28/28 + CHANGELOG + BDR-036 / LRN-058-060 / EVAL-008. surface-replaces-gate + partial-init + scope-expansion engraved honestly. +- [ ] flagged separate — [[BLK-010]] scaffold/bootstrap commit gap (init-project, unborn HEAD + worktree) +- [ ] flagged separate — [[BLK-011]] GSD ROADMAP.md post-FINISH (now STEP 12 after Task 5 renumber; BLK-011 record itself left at STEP 13 — append-only) +- [ ] flagged separate — strengthen doc-sync MINOR gate (own doc-syncer chantier)