Compare commits
No commits in common. "e4f7d3e82a4712318344b26d71f080953bfb1f1a" and "bb341d3c87c40ed30431dddb0d634de79837ccac" have entirely different histories.
e4f7d3e82a
...
bb341d3c87
@ -27,8 +27,6 @@ rules:
|
|||||||
| BLK-005 | 2026-05-21 | gstack submodule rename (checkpoint→context-save) breaks profile entries | resolved |
|
| 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-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-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 |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -119,23 +117,3 @@ 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.
|
- **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.
|
- **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]].
|
- **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]].
|
|
||||||
|
|||||||
@ -46,7 +46,6 @@ rules:
|
|||||||
| BDR-023 | 2026-06-19 | Merge /close into /capitalize — 2 modes + TODO reconcile; /close alias | accepted |
|
| 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-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-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 |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -566,19 +565,3 @@ 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.
|
- 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]].
|
- 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]].
|
- **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]].
|
|
||||||
|
|||||||
@ -28,7 +28,6 @@ rules:
|
|||||||
| EVAL-005 | 2026-06-23 | Obsolete `claude --effort max` alias missed across Step 9 edits | correct |
|
| 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-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-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 |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -97,12 +96,3 @@ 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 `<hash>` would have been aspirational) → added `bbef41c` (hash→stdout, diag→stderr), proven by T6. Both caught by exec/review, not assumed.
|
- **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 `<hash>` would have been aspirational) → added `bbef41c` (hash→stdout, diag→stderr), proven by T6. Both caught by exec/review, not assumed.
|
||||||
- **Action**: keep.
|
- **Action**: keep.
|
||||||
- **Reference**: commits `58cb91d`..`df60df6`.
|
- **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.
|
|
||||||
|
|||||||
@ -207,7 +207,3 @@ 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.
|
- 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.
|
- 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.
|
- 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.
|
|
||||||
|
|||||||
@ -56,9 +56,6 @@ 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-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-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-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 |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -709,27 +706,3 @@ 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.
|
- **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.
|
- **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]].
|
- **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]].
|
|
||||||
|
|||||||
@ -1,90 +0,0 @@
|
|||||||
# 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): <IDs> — <flow>` | `docs: <summary> — <flow>` | 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 — <summary of what changed>`,
|
|
||||||
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 -- <no-match>` ABORTS the whole commit; `git add` tolerates). A clean/absent passed path is dropped, not fatal.
|
|
||||||
- Partial-commit safety: `git commit -- <changed-paths>` 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: <summary> — <flow>`.
|
|
||||||
- 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.
|
|
||||||
@ -246,21 +246,4 @@ 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 5 — init-project founding-decisions capitalize (F5) — df60df6
|
||||||
- [x] Task 6 — behavioral verify + shellcheck + CHANGELOG + BDR/LRN — this commit
|
- [x] Task 6 — behavioral verify + shellcheck + CHANGELOG + BDR/LRN — this commit
|
||||||
- [ ] v2 (deferred) — Stop hook (non-blocking, BDR-033 style) reusing the detector
|
- [ ] v2 (deferred) — Stop hook (non-blocking, BDR-033 style) reusing the detector
|
||||||
- [~] twin chantier — doc-sync → own plan (2026-06-27). NOTE: "reorder before FINISH" REFUTED — doc-syncer commits nothing, needs reorder + NEW doc-commit mechanism.
|
- [ ] twin chantier (deferred) — doc-sync reorder before FINISH (same PR bug)
|
||||||
|
|
||||||
## 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)
|
|
||||||
|
|||||||
@ -10,7 +10,6 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Coupled-capitalize: dev flows (feat / hotfix / bugfix / commit-change, ship-feature, init-project) auto-commit their memory in the same breath, via shared `lib/capitalize-commit.md` + `lib/memory-commit.sh` (surgical — `.claude/memory` + `.claude/tasks` only, never `git add -A`)
|
- Coupled-capitalize: dev flows (feat / hotfix / bugfix / commit-change, ship-feature, init-project) auto-commit their memory in the same breath, via shared `lib/capitalize-commit.md` + `lib/memory-commit.sh` (surgical — `.claude/memory` + `.claude/tasks` only, never `git add -A`)
|
||||||
- Coupled doc-sync: dev flows (feat / bugfix / hotfix, ship-feature, init-project) auto-commit the public docs `doc-syncer` patches, via shared `lib/doc-commit.md` + `lib/doc-commit.sh` (surgical — only the patched files, never `git add -A`, never `.claude/` / `CLAUDE.md`; refuses an out-of-scope path loudly with exit 4). `doc-syncer` surfaces `PATCHED_FILES` (one path per line) as the handoff
|
|
||||||
- `/audit-delta` — recurring multi-axis audit (norms / bugs / dead code / security) scoped to changes since last run, with per-axis SHA markers
|
- `/audit-delta` — recurring multi-axis audit (norms / bugs / dead code / security) scoped to changes since last run, with per-axis SHA markers
|
||||||
- `/capitalize` — flush uncapitalized context to the memory registries before `/clear` or `/compact`
|
- `/capitalize` — flush uncapitalized context to the memory registries before `/clear` or `/compact`
|
||||||
- `/prune-memory` — curate and compress the `.claude/memory/` registries
|
- `/prune-memory` — curate and compress the `.claude/memory/` registries
|
||||||
@ -27,7 +26,6 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|||||||
### Changed
|
### Changed
|
||||||
- `/ship-feature`: capitalize + memory commit moved before FINISH (was after) — fixes memory committed after a push/PR and stranded outside it
|
- `/ship-feature`: capitalize + memory commit moved before FINISH (was after) — fixes memory committed after a push/PR and stranded outside it
|
||||||
- `/init-project`: new STEP 10b captures founding architecture decisions as BDRs before FINISH
|
- `/init-project`: new STEP 10b captures founding architecture decisions as BDRs before FINISH
|
||||||
- `/ship-feature` + `/init-project`: DOC SYNC moved before FINISH (was after) — fixes public docs patched then left uncommitted and stranded outside the push/PR (ship-feature STEP 9→8, init-project STEP 12→10c; GSD 13→12)
|
|
||||||
- `/validate` renamed to `/web-validate` — clearer scoped name (W3C + WCAG); routing, skill profiles, cross-references, and the client-deliverable leak-guard updated (the guard still matches legacy `/validate` so older client docs stay covered)
|
- `/validate` renamed to `/web-validate` — clearer scoped name (W3C + WCAG); routing, skill profiles, cross-references, and the client-deliverable leak-guard updated (the guard still matches legacy `/validate` so older client docs stay covered)
|
||||||
- `/seo` split into parallel `seo` + `geo` agents with shared resources
|
- `/seo` split into parallel `seo` + `geo` agents with shared resources
|
||||||
- `/onboard` rewritten: archetype-aware pipeline (orchestrator + config-only agent), security audit archetype-aware
|
- `/onboard` rewritten: archetype-aware pipeline (orchestrator + config-only agent), security audit archetype-aware
|
||||||
|
|||||||
@ -149,9 +149,8 @@ cd my-existing-project/
|
|||||||
# → STEP 0: plugin check
|
# → STEP 0: plugin check
|
||||||
# → STEP 1-2: brainstorm + plan (superpowers)
|
# → STEP 1-2: brainstorm + plan (superpowers)
|
||||||
# → STEP 3: validation gate — user approval required
|
# → STEP 3: validation gate — user approval required
|
||||||
# → STEP 4-7: implement (TDD) → review → capitalize (memory)
|
# → STEP 4-7: implement (TDD) → review → finish
|
||||||
# → STEP 8: sync README (doc-sync)
|
# → STEP 8: sync README
|
||||||
# → STEP 9: finish (merge / PR)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
For small features (1-5 files), use `/feat` instead — no orchestration overhead.
|
For small features (1-5 files), use `/feat` instead — no orchestration overhead.
|
||||||
|
|||||||
15
USAGE.md
15
USAGE.md
@ -123,7 +123,7 @@ Tu veux...
|
|||||||
|
|
||||||
| Commande | Quand | Notes |
|
| Commande | Quand | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `/init-project` | Nouveau projet from scratch | 11-12 steps, deux gates obligatoires |
|
| `/init-project` | Nouveau projet from scratch | 12-13 steps, deux gates obligatoires |
|
||||||
| `/ship-feature` | Feature sur projet existant | Pipeline 9 steps, une gate |
|
| `/ship-feature` | Feature sur projet existant | Pipeline 9 steps, une gate |
|
||||||
| `/feat` | Petite feature (1-5 fichiers) | Léger, pas d'orchestration lourde |
|
| `/feat` | Petite feature (1-5 fichiers) | Léger, pas d'orchestration lourde |
|
||||||
| `/bugfix` | Bug avec investigation root cause | Hypothèses, diagnostic, fix minimal |
|
| `/bugfix` | Bug avec investigation root cause | Hypothèses, diagnostic, fix minimal |
|
||||||
@ -192,10 +192,9 @@ Hotfix/quick fix → tout OFF sauf superpowers
|
|||||||
# → STEP 1 : interview (skip si prompt complet)
|
# → STEP 1 : interview (skip si prompt complet)
|
||||||
# → STEP 4 : ★ GATE — valider l'architecture
|
# → STEP 4 : ★ GATE — valider l'architecture
|
||||||
# → STEP 7 : ★ GATE — valider le plan d'implémentation
|
# → STEP 7 : ★ GATE — valider le plan d'implémentation
|
||||||
# → STEP 8-10 : implémentation TDD + review
|
# → STEP 8-11: implémentation TDD + review + finish
|
||||||
# → STEP 10b-c: capitalize mémoire + sync README (avant finish)
|
# → STEP 12 : sync README
|
||||||
# → STEP 11 : finish (merge / commit initial)
|
# → STEP 13 : propose GSD v2 si multi-session détecté
|
||||||
# → STEP 12 : propose GSD v2 si multi-session détecté
|
|
||||||
|
|
||||||
# 3. Features suivantes
|
# 3. Features suivantes
|
||||||
/ship-feature "description de la feature"
|
/ship-feature "description de la feature"
|
||||||
@ -204,7 +203,7 @@ Hotfix/quick fix → tout OFF sauf superpowers
|
|||||||
### Pattern B — Projet long (multi-session, plusieurs jours) · ~1500-2500t/session CC
|
### Pattern B — Projet long (multi-session, plusieurs jours) · ~1500-2500t/session CC
|
||||||
|
|
||||||
```
|
```
|
||||||
# Même départ que Pattern A, mais au STEP 12 :
|
# Même départ que Pattern A, mais au STEP 13 :
|
||||||
# → Répondre "yes" à "Initialize GSD v2?"
|
# → Répondre "yes" à "Initialize GSD v2?"
|
||||||
# → ROADMAP.md est créé avec les milestones
|
# → ROADMAP.md est créé avec les milestones
|
||||||
|
|
||||||
@ -400,7 +399,7 @@ Verify : npx expo export --platform web --output-dir /tmp/expo-check --clear
|
|||||||
|
|
||||||
**Si le projet devient long (plusieurs features sur semaines) :**
|
**Si le projet devient long (plusieurs features sur semaines) :**
|
||||||
```
|
```
|
||||||
# STEP 12 propose GSD v2 : répondre "yes"
|
# STEP 13 propose GSD v2 : répondre "yes"
|
||||||
# Puis dans terminal :
|
# Puis dans terminal :
|
||||||
gsd
|
gsd
|
||||||
/gsd auto
|
/gsd auto
|
||||||
@ -501,7 +500,7 @@ Convention: snake_case Python, camelCase TypeScript."
|
|||||||
|
|
||||||
**Workflow long avec GSD v2 :**
|
**Workflow long avec GSD v2 :**
|
||||||
```
|
```
|
||||||
# Après /init-project (STEP 12 → "yes")
|
# Après /init-project (STEP 13 → "yes")
|
||||||
# Le ROADMAP.md généré contient :
|
# Le ROADMAP.md généré contient :
|
||||||
# Milestone 1: Boutique in-app + Stripe
|
# Milestone 1: Boutique in-app + Stripe
|
||||||
# Milestone 2: PvP + matchmaking
|
# Milestone 2: PvP + matchmaking
|
||||||
|
|||||||
@ -173,12 +173,6 @@ Load `$HOME/.claude/agents/doc-syncer.md`.
|
|||||||
Execute in automatic mode:
|
Execute in automatic mode:
|
||||||
`auto-mode scope: <list of files modified during this session>`
|
`auto-mode scope: <list of files modified during this session>`
|
||||||
|
|
||||||
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
|
|
||||||
ONLY the files doc-syncer patched (its `PATCHED_FILES` output), never `git add -A`, never
|
|
||||||
`.claude/`/`CLAUDE.md` (rc 4 = a loud BDR-022 anomaly, not a silent skip), and no-ops when
|
|
||||||
nothing was patched — the common case for a trivial change. No FINISH in an inline flow, so
|
|
||||||
it just commits the docs on the current branch (no ordering concern).
|
|
||||||
|
|
||||||
## STEP 7 — CAPITALIZE (memory registries)
|
## STEP 7 — CAPITALIZE (memory registries)
|
||||||
|
|
||||||
A bugfix with an understood root cause is almost always worth one entry:
|
A bugfix with an understood root cause is almost always worth one entry:
|
||||||
|
|||||||
@ -719,16 +719,8 @@ CREATED : <count> files
|
|||||||
REMOVED : <count> files / sections
|
REMOVED : <count> files / sections
|
||||||
HUMAN PENDING: <count> items (see report above)
|
HUMAN PENDING: <count> items (see report above)
|
||||||
SKIPPED : <count> (user declined)
|
SKIPPED : <count> (user declined)
|
||||||
PATCHED_FILES: (one real path per LINE below; "(none)" if no write)
|
|
||||||
<path created or modified this run>
|
|
||||||
<path created or modified this run>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`PATCHED_FILES` is the machine-readable handle the doc-commit step (`lib/doc-commit.md`)
|
|
||||||
consumes — every public-doc file created or modified this run, ONE PATH PER LINE. Each line
|
|
||||||
is passed as a SEPARATE argument to `lib/doc-commit.sh` (newline split, space-safe). It NEVER
|
|
||||||
lists `.claude/**` or `CLAUDE.md` (never targets, BDR-022). Empty / `(none)` → doc-commit no-ops.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## AUTO MODE
|
## AUTO MODE
|
||||||
@ -790,9 +782,8 @@ Categorize:
|
|||||||
|
|
||||||
### STEP A4 — ACT
|
### STEP A4 — ACT
|
||||||
|
|
||||||
- **NONE** → exit completely silent. No output (no `PATCHED_FILES` → the doc-commit step
|
- **NONE** → exit completely silent. No output.
|
||||||
sees an empty list and no-ops).
|
- **MINOR** → patch silently. One-line confirmation:
|
||||||
- **MINOR** → patch silently. One-line confirmation per file:
|
|
||||||
`doc-sync: patched <file> (<what changed>)`
|
`doc-sync: patched <file> (<what changed>)`
|
||||||
- **SIGNIFICANT** → surface to user before patching:
|
- **SIGNIFICANT** → surface to user before patching:
|
||||||
```
|
```
|
||||||
@ -802,16 +793,6 @@ Categorize:
|
|||||||
```
|
```
|
||||||
Wait for approval.
|
Wait for approval.
|
||||||
|
|
||||||
After writing in MINOR or approved-SIGNIFICANT, emit the machine-readable handle the
|
|
||||||
doc-commit step (`lib/doc-commit.md`) consumes — ONE real path PER LINE:
|
|
||||||
```
|
|
||||||
PATCHED_FILES:
|
|
||||||
<path created or modified this run>
|
|
||||||
<path created or modified this run>
|
|
||||||
```
|
|
||||||
Emit ONLY when something was written; NONE stays silent. Never lists `.claude/**` or
|
|
||||||
`CLAUDE.md` (never targets, BDR-022).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## RULES
|
## RULES
|
||||||
|
|||||||
@ -141,12 +141,6 @@ Load `$HOME/.claude/agents/doc-syncer.md`.
|
|||||||
Execute in automatic mode:
|
Execute in automatic mode:
|
||||||
`auto-mode scope: <list of files modified during this session>`
|
`auto-mode scope: <list of files modified during this session>`
|
||||||
|
|
||||||
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
|
|
||||||
ONLY the files doc-syncer patched (its `PATCHED_FILES` output), never `git add -A`, never
|
|
||||||
`.claude/`/`CLAUDE.md` (rc 4 = a loud BDR-022 anomaly, not a silent skip), and no-ops when
|
|
||||||
nothing was patched — the common case for a trivial change. No FINISH in an inline flow, so
|
|
||||||
it just commits the docs on the current branch (no ordering concern).
|
|
||||||
|
|
||||||
## STEP 6 — CAPITALIZE (memory registries)
|
## STEP 6 — CAPITALIZE (memory registries)
|
||||||
|
|
||||||
A small feature may or may not involve a design choice. Scan the work for:
|
A small feature may or may not involve a design choice. Scan the work for:
|
||||||
|
|||||||
@ -118,12 +118,6 @@ Load `$HOME/.claude/agents/doc-syncer.md`.
|
|||||||
Execute in automatic mode:
|
Execute in automatic mode:
|
||||||
`auto-mode scope: <list of files modified during this session>`
|
`auto-mode scope: <list of files modified during this session>`
|
||||||
|
|
||||||
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
|
|
||||||
ONLY the files doc-syncer patched (its `PATCHED_FILES` output), never `git add -A`, never
|
|
||||||
`.claude/`/`CLAUDE.md` (rc 4 = a loud BDR-022 anomaly, not a silent skip), and no-ops when
|
|
||||||
nothing was patched — the common case for a trivial hotfix. No FINISH in an inline flow, so
|
|
||||||
it just commits the docs on the current branch (no ordering concern).
|
|
||||||
|
|
||||||
## STEP 5 — CAPITALIZE (memory registries, lightweight)
|
## STEP 5 — CAPITALIZE (memory registries, lightweight)
|
||||||
|
|
||||||
Hotfixes are often trivial (typo, config, import) — skip by default. But if the fix revealed something non-obvious:
|
Hotfixes are often trivial (typo, config, import) — skip by default. But if the fix revealed something non-obvious:
|
||||||
|
|||||||
@ -1,114 +0,0 @@
|
|||||||
# DOC-COMMIT — couple the public-doc commit to the dev flow
|
|
||||||
|
|
||||||
Inline snippet. Include at the END of an orchestrator's DOC SYNC step, AFTER doc-syncer
|
|
||||||
has patched the public docs (and any SIGNIFICANT gate is resolved). It commits ONLY the
|
|
||||||
files doc-sync patched, surgically, so the flow never leaves patched docs uncommitted —
|
|
||||||
and, run BEFORE FINISH, so those docs reach the merge/PR instead of stranding.
|
|
||||||
|
|
||||||
This is the TAIL of doc-sync, not a replacement: doc-syncer's DRIFT DETECTION + PATCH
|
|
||||||
(and its SIGNIFICANT gate) are unchanged and run before this snippet. Only the COMMIT of
|
|
||||||
the already-patched files is automated here. Twin of `capitalize-commit.md` (memory TAIL).
|
|
||||||
|
|
||||||
## WHEN TO RUN
|
|
||||||
|
|
||||||
At the doc-sync step, once doc-syncer has applied its patches (AUTO MODE MINOR auto-patch
|
|
||||||
and any SIGNIFICANT-gated patch), with the code already committed.
|
|
||||||
|
|
||||||
- Orchestrators (ship-feature / init-project): run it BEFORE the FINISH step — otherwise
|
|
||||||
the doc commit strands outside the merge/PR (the exact bug this fixes). See ORDERING.
|
|
||||||
|
|
||||||
doc-syncer runs IN-THREAD (the orchestrator loads it), so the list of files it patched is
|
|
||||||
already in hand — surfaced as `PATCHED_FILES:` in doc-syncer's OUTPUT, ONE PATH PER LINE.
|
|
||||||
Pass each line as a SEPARATE argument (see DO step 3).
|
|
||||||
|
|
||||||
## DO
|
|
||||||
|
|
||||||
1. Collect `PATCHED_FILES` — the public-doc paths doc-syncer wrote this run (its OUTPUT
|
|
||||||
block, ONE PATH PER LINE). Empty → nothing to commit; the helper no-ops.
|
|
||||||
|
|
||||||
2. Compose — from the patch context the AGENT holds (doc-syncer ran in-thread, so the
|
|
||||||
agent knows exactly what changed) — BOTH artifacts:
|
|
||||||
- the COMMIT MESSAGE, repo style `docs: <summary> — <flow>`
|
|
||||||
(`docs: README features + USAGE flags — ship-feature dark-mode`);
|
|
||||||
- the CHANGE SUMMARY for the rc 0 surface (e.g. "README features section + USAGE
|
|
||||||
--export flag").
|
|
||||||
Both are the AGENT's to write — the helper produces NEITHER (its only stdout is the
|
|
||||||
hash). This is the load-bearing point of the visible surface: see the rc 0 row.
|
|
||||||
|
|
||||||
3. Commit surgically via the helper, passing EXACTLY the patched files — each path as a
|
|
||||||
SEPARATE argument (split `PATCHED_FILES` on NEWLINES only), capturing the hash:
|
|
||||||
|
|
||||||
doc_hash=$(bash "$HOME/.claude/lib/doc-commit.sh" commit "<message>" "<path-1>" "<path-2>" …)
|
|
||||||
rc=$?
|
|
||||||
|
|
||||||
SEPARATOR — the helper takes argv (no in-band separator), so a path with spaces
|
|
||||||
(`docs/My Guide.md`) survives as ONE argument and commits correctly (proven, T7). NEVER
|
|
||||||
flatten the list into a single space-joined string and re-split it: that mis-splits a
|
|
||||||
spaced path into garbage args, which the helper's `[ -e ]` filter then silently drops —
|
|
||||||
the spaced doc would strand. Newline is doc-syncer's OUTPUT format (paths carry no
|
|
||||||
newlines); argv is the handoff to the helper.
|
|
||||||
|
|
||||||
4. REPORT BY (rc, doc_hash) — handle EVERY exit, not just success:
|
|
||||||
|
|
||||||
| rc | doc_hash | meaning | what to do |
|
|
||||||
|----|----------|---------|------------|
|
|
||||||
| 0 | non-empty | docs committed | `✅ committed <files> — <one-line of what changed>` — VISIBLE surface. `<files>` = the paths the agent passed (also echoed on the helper's stderr); `<one-line>` = the CHANGE SUMMARY the AGENT composed in DO step 2, NOT returned by the helper (stdout is the hash only). That summary is what makes the surface REPLACE the MINOR gate — a bare file count degenerates it back to what we removed. No summary → don't report success silently; name what changed. |
|
|
||||||
| 0 | empty | helper no-op (nothing pending) | `DOC SYNC: docs already current — nothing to commit`. doc-sync found no drift, or patched nothing. |
|
|
||||||
| 3 | empty | unsafe git state (detached / merge / rebase) | docs stay in the working tree for a manual commit; surface the helper's stderr. Do NOT retry blindly — the tree is mid-operation. |
|
|
||||||
| 4 | empty | **SCOPE VIOLATION — upstream anomaly** | doc-syncer surfaced a `.claude/**` or `CLAUDE.md` path in `PATCHED_FILES`, which it must NEVER patch (BDR-022). STOP. Signal: `⚠️ doc-commit REFUSED — doc-syncer listed a forbidden path (<offender, from stderr>); this violates BDR-022 upstream. Investigate why doc-syncer touched/listed it before re-running.` Do NOT swallow it, do NOT hand-commit the rest — the refusal IS the alarm. |
|
|
||||||
| 2 | empty | usage error (no message / bad invocation) | internal bug in this include — fix the call, don't paper over it. |
|
|
||||||
|
|
||||||
`<doc_hash>` is the DOC commit (the one that adds the patched docs). Docs carry NO
|
|
||||||
code-commit hash (unlike memory entries, LRN-052) — there is no second hash to report.
|
|
||||||
|
|
||||||
## HARD RULE — surgical, dynamic scope
|
|
||||||
|
|
||||||
The helper stages and commits ONLY the patched files passed as args, filtered to those
|
|
||||||
with real changes (LRN-051) — never `git add -A` / `git add .` / `git commit -a`. Automation
|
|
||||||
removes the human diff review, so the scope IS the safety. Two guards live in the helper;
|
|
||||||
do NOT bypass them:
|
|
||||||
- DYNAMIC pathspec: only the passed docs, changed-filtered.
|
|
||||||
- INVERSE exclusion (fail-closed, exit 4): a `.claude/**` / `CLAUDE.md` path aborts the
|
|
||||||
WHOLE commit, loudly. Mirror-image of memory-commit (which TARGETS `.claude/`): doc-commit
|
|
||||||
must never touch it (BDR-022). The refusal surfaces an upstream bug — treat it as rc 4
|
|
||||||
above, never filter-and-commit-the-rest.
|
|
||||||
|
|
||||||
## ORDERING (orchestrators)
|
|
||||||
|
|
||||||
`finishing-a-development-branch` merges/pushes COMMITTED history only — it never commits
|
|
||||||
working-tree changes. A doc patch left uncommitted (or committed AFTER it) never reaches
|
|
||||||
the merge/PR. So this snippet runs BEFORE FINISH: the doc commit lands on the branch FINISH
|
|
||||||
integrates. Consumption is MECHANICAL (LRN-057 case a, like the memory commit) — production
|
|
||||||
on the branch = consumption by the merge, automatic.
|
|
||||||
|
|
||||||
## ACKNOWLEDGMENTS (conscious, not glossed)
|
|
||||||
|
|
||||||
- **MINOR doc content is non-gated yet auto-committed.** doc-syncer AUTO MODE patches MINOR
|
|
||||||
drift silently (factual: command/param/path/version/dead-link — same bar as AUTO). This
|
|
||||||
snippet commits it without a blocking gate, BY CHOICE. NOT the memory case: memory CONTENT
|
|
||||||
was always gated, so its auto-commit only embarked approved entries. Here the VISIBLE
|
|
||||||
surface (rc 0 row, agent-composed summary) REPLACES the gate as the review surface — name
|
|
||||||
files + summarize, and the PR diff re-shows it. Strengthening the MINOR gate itself =
|
|
||||||
separate doc-syncer chantier.
|
|
||||||
- **Partial init-project fix.** This commits the docs doc-sync patched. It does NOT commit the
|
|
||||||
scaffold or the STEP 5b bootstrap README (no deterministic owner — [[BLK-010]]); ramassing
|
|
||||||
them would re-create the over-reach we ban. ship-feature ends fully fixed; init-project's
|
|
||||||
scaffold/bootstrap stays open.
|
|
||||||
|
|
||||||
## WHAT THIS DOES NOT DO
|
|
||||||
|
|
||||||
- Does NOT push / merge — FINISH does, AFTER this.
|
|
||||||
- Does NOT decide WHAT to sync — doc-syncer's drift detection + patch own that; this commits
|
|
||||||
what was already patched.
|
|
||||||
- Does NOT commit code or memory — those are upstream (implementation step; capitalize-commit).
|
|
||||||
- Does NOT produce the surface's change summary — the helper returns only the hash; the AGENT
|
|
||||||
composes `<files>` + `<one-line>` from the patch context (DO step 2). Surface ownership = agent.
|
|
||||||
- Does NOT reference a code-commit hash — docs anchor none (LRN-052); reports only the doc hash.
|
|
||||||
- Does NOT silently drop a forbidden path — rc 4 is an alarm, not a filter.
|
|
||||||
- Does NOT run if you hand-roll `git add` / `git commit` — bypassing the helper drops the
|
|
||||||
dynamic-scope + inverse-exclusion guarantees. Always call the helper.
|
|
||||||
|
|
||||||
## IDEMPOTENT
|
|
||||||
|
|
||||||
Safe to run when docs are already current: empty list or clean tree → helper no-ops (exit 0,
|
|
||||||
empty stdout, no commit). Running twice creates at most one commit.
|
|
||||||
@ -1,147 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# doc-commit.sh — surgically commit ONLY the PUBLIC-DOC files doc-sync patched.
|
|
||||||
#
|
|
||||||
# Twin of memory-commit.sh, INVERSE scope: memory-commit TARGETS .claude/; this
|
|
||||||
# one commits public docs and must NEVER touch .claude/ or CLAUDE.md (BDR-022).
|
|
||||||
# Safety lives in the (dynamic) PATHSPEC + a fail-closed scope guard, never in a
|
|
||||||
# human diff review — automation removes that review, so the scope must be airtight.
|
|
||||||
#
|
|
||||||
# The scope guard is fail-CLOSED and LOUD: a forbidden path (.claude/** or
|
|
||||||
# CLAUDE.md) in the list is an UPSTREAM bug — doc-syncer must never patch those
|
|
||||||
# (BDR-022). Seeing one, ABORT THE WHOLE COMMIT and signal; do NOT silently filter
|
|
||||||
# it and commit the rest. A half-commit with no alert would MASK the violation.
|
|
||||||
# Caller passes EXACTLY the files doc-sync patched this run.
|
|
||||||
#
|
|
||||||
# Usage (CLI):
|
|
||||||
# doc-commit.sh pending <file>... # exit 0 if any passed file has changes, 1 if clean
|
|
||||||
# doc-commit.sh commit "<message>" <file>... # surgical commit
|
|
||||||
#
|
|
||||||
# Exit codes (commit): 0 ok/no-op · 2 usage · 3 unsafe git state · 4 scope violation.
|
|
||||||
# Output contract: diagnostics → stderr; on a real commit the short hash of the doc
|
|
||||||
# commit is the ONLY thing on stdout (empty on no-op/abort), so callers can capture
|
|
||||||
# it: doc_hash=$(doc-commit.sh commit "msg" README.md USAGE.md).
|
|
||||||
#
|
|
||||||
# Sourceable: docs_pending and commit_docs for the v2 hook.
|
|
||||||
|
|
||||||
set -uo pipefail
|
|
||||||
|
|
||||||
_in_git_repo() { git rev-parse --git-dir >/dev/null 2>&1; }
|
|
||||||
|
|
||||||
# True (0) when the repo is in a state where we must NOT auto-commit:
|
|
||||||
# detached HEAD, or a merge/rebase/cherry-pick in progress.
|
|
||||||
_unsafe_state() {
|
|
||||||
local gitdir
|
|
||||||
gitdir="$(git rev-parse --git-dir 2>/dev/null)" || return 0
|
|
||||||
if [ -e "$gitdir/MERGE_HEAD" ] || [ -e "$gitdir/rebase-merge" ] ||
|
|
||||||
[ -e "$gitdir/rebase-apply" ] || [ -e "$gitdir/CHERRY_PICK_HEAD" ]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
git symbolic-ref -q HEAD >/dev/null 2>&1 || return 0 # detached HEAD
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# True (0) when a path is OUT OF SCOPE for a doc commit: anything under .claude/
|
|
||||||
# (any depth) or a CLAUDE.md (root or nested). These are doc-syncer's read-only
|
|
||||||
# context, never sync targets (BDR-022) — their presence is an upstream anomaly.
|
|
||||||
_forbidden_path() {
|
|
||||||
case "$1" in
|
|
||||||
.claude | .claude/* | */.claude/* | CLAUDE.md | */CLAUDE.md) return 0 ;;
|
|
||||||
*) return 1 ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# Print every forbidden path in the argument list, one per line (empty = none).
|
|
||||||
_scope_violations() {
|
|
||||||
local p
|
|
||||||
for p in "$@"; do
|
|
||||||
_forbidden_path "$p" && printf '%s\n' "$p"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Of the passed paths, those that EXIST and have real pending changes. A clean or
|
|
||||||
# absent path is dropped (not fatal): `git commit -- <no-match>` aborts the WHOLE
|
|
||||||
# commit, while `git add` tolerates it — so scope = only paths git would accept.
|
|
||||||
_changed_paths() {
|
|
||||||
local p
|
|
||||||
for p in "$@"; do
|
|
||||||
[ -e "$p" ] || continue
|
|
||||||
[ -n "$(git status --porcelain -- "$p" 2>/dev/null)" ] && printf '%s\n' "$p"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# 0 if any passed path has pending changes, 1 if all clean / absent.
|
|
||||||
docs_pending() {
|
|
||||||
_in_git_repo || return 1
|
|
||||||
local changed
|
|
||||||
mapfile -t changed < <(_changed_paths "$@")
|
|
||||||
[ "${#changed[@]}" -gt 0 ]
|
|
||||||
}
|
|
||||||
|
|
||||||
# Surgical commit of the passed doc paths only. Returns 0 (ok/no-op), 3 (unsafe),
|
|
||||||
# 4 (scope violation). On a real commit, prints the doc-commit short hash to stdout.
|
|
||||||
commit_docs() {
|
|
||||||
local msg="${1:?commit message required}"
|
|
||||||
shift
|
|
||||||
_in_git_repo || {
|
|
||||||
echo "doc-commit: not a git repo — skip" >&2
|
|
||||||
return 3
|
|
||||||
}
|
|
||||||
if _unsafe_state; then
|
|
||||||
echo "doc-commit: detached HEAD or merge/rebase in progress — skip (no commit)" >&2
|
|
||||||
return 3
|
|
||||||
fi
|
|
||||||
# FAIL-CLOSED scope guard. A forbidden path is an upstream BDR-022 violation
|
|
||||||
# (doc-syncer must never patch .claude/ or CLAUDE.md). Abort the WHOLE commit and
|
|
||||||
# name the offenders — never filter-and-commit-the-rest (that masks the bug).
|
|
||||||
local violations
|
|
||||||
mapfile -t violations < <(_scope_violations "$@")
|
|
||||||
if [ "${#violations[@]}" -gt 0 ]; then
|
|
||||||
{
|
|
||||||
echo "doc-commit: REFUSED — out-of-scope path(s) in the doc list (upstream BDR-022 violation):"
|
|
||||||
printf ' - %s\n' "${violations[@]}"
|
|
||||||
echo "doc-commit: NOTHING committed. doc-syncer must never patch .claude/ or CLAUDE.md —" \
|
|
||||||
"investigate why these surfaced before retrying."
|
|
||||||
} >&2
|
|
||||||
return 4
|
|
||||||
fi
|
|
||||||
local changed
|
|
||||||
mapfile -t changed < <(_changed_paths "$@")
|
|
||||||
if [ "${#changed[@]}" -eq 0 ]; then
|
|
||||||
echo "doc-commit: nothing pending — no-op" >&2
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
# Re-stage working-tree content over any stale index entry, then commit ONLY
|
|
||||||
# those paths. The pathspec on `git commit` makes it partial: other staged files
|
|
||||||
# (dangling code) are not recorded.
|
|
||||||
git add -- "${changed[@]}"
|
|
||||||
if git diff --cached --quiet -- "${changed[@]}"; then
|
|
||||||
echo "doc-commit: only ignored/no-op changes — no-op" >&2
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
git commit -q -m "$msg" -- "${changed[@]}"
|
|
||||||
printf 'doc-commit: committed %d file(s): %s\n' "${#changed[@]}" "${changed[*]}" >&2
|
|
||||||
git rev-parse --short HEAD
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
|
||||||
local cmd="${1:-}"
|
|
||||||
case "$cmd" in
|
|
||||||
pending)
|
|
||||||
shift
|
|
||||||
docs_pending "$@"
|
|
||||||
;;
|
|
||||||
commit)
|
|
||||||
shift
|
|
||||||
commit_docs "$@"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "usage: doc-commit.sh {pending <file>... | commit <message> <file>...}" >&2
|
|
||||||
return 2
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# Run main only when executed, not when sourced.
|
|
||||||
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
|
|
||||||
main "$@"
|
|
||||||
fi
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
# Behavioral check — doc-sync coupled, end-to-end
|
|
||||||
|
|
||||||
The deterministic suite (`run-doc-commit.sh`, T1–T7) proves `doc-commit.sh` in
|
|
||||||
isolation. This is the in-vivo whole-chain check: a real dev-flow shape — code
|
|
||||||
commit, then doc-syncer patches public docs, then the include commits them — with
|
|
||||||
dangling code AND a forbidden `.claude/` path present, proving the doc commit is
|
|
||||||
coupled, surgical, AND fail-closed on an upstream scope violation.
|
|
||||||
|
|
||||||
## Scenario A — coupled + surgical (the happy path)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
R="$(mktemp -d)"; cd "$R"
|
|
||||||
git init -q && git config user.email t@t.t && git config user.name t
|
|
||||||
mkdir -p .claude/memory docs src
|
|
||||||
printf '# Proj\n' > README.md
|
|
||||||
printf 'baseline\n' > .claude/memory/decisions.md
|
|
||||||
git add -A && git commit -qm baseline
|
|
||||||
|
|
||||||
# 1) the flow commits CODE
|
|
||||||
printf 'feature code\n' > src/feature.txt
|
|
||||||
git add -- src/feature.txt && git commit -qm "feat: the feature"
|
|
||||||
|
|
||||||
# 2) doc-syncer patches public docs (a modified README + a created docs page).
|
|
||||||
# It would surface PATCHED_FILES, ONE PATH PER LINE:
|
|
||||||
# README.md
|
|
||||||
# docs/usage.md
|
|
||||||
printf '\n## New feature\nUse --export.\n' >> README.md
|
|
||||||
printf 'usage guide\n' > docs/usage.md
|
|
||||||
|
|
||||||
# 3) a code file is left dangling (must NOT be embarked)
|
|
||||||
printf 'WIP do not commit\n' > src/dangling.txt
|
|
||||||
|
|
||||||
# 4) the include passes EACH PATCHED_FILES line as a SEPARATE arg (argv, space-safe)
|
|
||||||
doc_hash="$(bash "$HOME/.claude/lib/doc-commit.sh" commit "docs: README + usage" "README.md" "docs/usage.md")"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Expected (assert)
|
|
||||||
- Exactly TWO commits after baseline: the code commit, then the doc commit.
|
|
||||||
- The doc commit (`$doc_hash`) contains ONLY `README.md` + `docs/usage.md` — never
|
|
||||||
`src/feature.txt` (already committed) or `src/dangling.txt` (WIP).
|
|
||||||
- `src/dangling.txt` is still untracked after the doc commit.
|
|
||||||
- No `.claude/**` path in the doc commit (doc-syncer never patches it; the helper
|
|
||||||
guards it regardless).
|
|
||||||
|
|
||||||
## Scenario B — fail-closed guard (the upstream-anomaly path)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# A bug upstream surfaces a forbidden path in PATCHED_FILES (doc-syncer must never
|
|
||||||
# patch .claude/ — BDR-022). The include passes it through; the helper must REFUSE.
|
|
||||||
printf 'x\n' >> .claude/memory/decisions.md # make the forbidden path dirty
|
|
||||||
printf '\n## later\n' >> README.md # a legit doc also changed
|
|
||||||
bash "$HOME/.claude/lib/doc-commit.sh" commit "docs: mixed" "README.md" ".claude/memory/decisions.md"
|
|
||||||
echo "rc=$?"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Expected (assert)
|
|
||||||
- `rc=4` (scope violation), NOTHING committed — `README.md` is NOT half-committed.
|
|
||||||
- stderr is loud (`REFUSED …`) and NAMES the offender (`.claude/memory/decisions.md`).
|
|
||||||
- The include treats rc 4 as an upstream BDR-022 anomaly to investigate — not a
|
|
||||||
silent skip. The refusal IS the alarm.
|
|
||||||
|
|
||||||
If Scenario A holds, the chain is coupled (docs committed in the same breath as the
|
|
||||||
flow) and surgical (no dangling code embarked). If Scenario B holds, the guard is
|
|
||||||
fail-closed and loud. This mirrors what feat / bugfix / hotfix do at their DOC SYNC
|
|
||||||
step (inline-branch commit, no FINISH), and what ship-feature / init-project do at
|
|
||||||
their DOC SYNC step BEFORE FINISH (so the doc commit reaches the merge/PR).
|
|
||||||
@ -1,179 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Deterministic tests for lib/doc-commit.sh.
|
|
||||||
#
|
|
||||||
# Proves the contract on REAL git behavior (not assumed). Load-bearing deltas vs
|
|
||||||
# memory-commit, each tested by what it must REFUSE, not only what it accepts:
|
|
||||||
# T1 inverse-exclusion scope guard (BDR-022) — fail-CLOSED and LOUD:
|
|
||||||
# T1a forbidden path ALONE (.claude/ and CLAUDE.md) → exit 4, nothing committed
|
|
||||||
# T1b legit docs only → commits cleanly
|
|
||||||
# T1c MIXED legit + forbidden → exit 4, NOTHING committed (the trap)
|
|
||||||
# T2 dynamic pathspec — a clean passed path is filtered, commit does NOT abort
|
|
||||||
# T3 dangling code (untracked OR pre-staged) never embarked
|
|
||||||
# T4 stale-staged doc (version A) → commit carries working-tree version B
|
|
||||||
# T5 idempotent — empty list / clean tree → no-op exit 0
|
|
||||||
# T6 unsafe git state (detached HEAD) → exit 3, no commit
|
|
||||||
# T7 path WITH A SPACE passed as one arg → committed (argv is space-safe, no separator)
|
|
||||||
#
|
|
||||||
# No -e: run every test and report, even after a failure.
|
|
||||||
set -uo pipefail
|
|
||||||
|
|
||||||
HERE="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
HELPER="$HERE/../doc-commit.sh"
|
|
||||||
ERRFILE="$(mktemp)"
|
|
||||||
PASS=0
|
|
||||||
FAIL=0
|
|
||||||
|
|
||||||
ok() { printf ' \033[32m✓\033[0m %s\n' "$1"; PASS=$((PASS + 1)); }
|
|
||||||
ko() { printf ' \033[31m✗\033[0m %s\n' "$1"; FAIL=$((FAIL + 1)); }
|
|
||||||
|
|
||||||
# Fresh throwaway repo: public docs + forbidden context + code, all tracked.
|
|
||||||
new_repo() {
|
|
||||||
local d
|
|
||||||
d="$(mktemp -d)"
|
|
||||||
git -C "$d" init -q
|
|
||||||
git -C "$d" config user.email t@t.t
|
|
||||||
git -C "$d" config user.name tester
|
|
||||||
mkdir -p "$d/.claude/memory" "$d/src"
|
|
||||||
printf 'readme baseline\n' >"$d/README.md"
|
|
||||||
printf 'usage baseline\n' >"$d/USAGE.md"
|
|
||||||
printf 'deploy baseline\n' >"$d/DEPLOY.md"
|
|
||||||
printf 'claude-md baseline\n' >"$d/CLAUDE.md"
|
|
||||||
printf 'decisions baseline\n' >"$d/.claude/memory/decisions.md"
|
|
||||||
printf 'src baseline\n' >"$d/src/app.txt"
|
|
||||||
git -C "$d" add -A
|
|
||||||
git -C "$d" commit -qm baseline
|
|
||||||
printf '%s' "$d"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Files recorded in HEAD, sorted (stable compare).
|
|
||||||
head_files() { git -C "$1" diff-tree --no-commit-id --name-only -r HEAD | sort | tr '\n' ' '; }
|
|
||||||
|
|
||||||
# run <repo> <args...> → sets RC (exit), OUT (stdout = hash), ERR (stderr = diag).
|
|
||||||
run() {
|
|
||||||
local r="$1"; shift
|
|
||||||
OUT="$( (cd "$r" && "$HELPER" "$@") 2>"$ERRFILE" )"; RC=$?
|
|
||||||
ERR="$(cat "$ERRFILE")"
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "T1a — forbidden path ALONE → REFUSE loud (exit 4), nothing committed"
|
|
||||||
R="$(new_repo)"
|
|
||||||
BEFORE="$(git -C "$R" rev-parse HEAD)"
|
|
||||||
printf 'dirtied\n' >>"$R/.claude/memory/decisions.md"
|
|
||||||
run "$R" commit "docs: T1a-claude" ".claude/memory/decisions.md"
|
|
||||||
printf ' rc=%s out=[%s]\n' "$RC" "$OUT"
|
|
||||||
printf ' err: %s\n' "$(printf '%s' "$ERR" | head -1)"
|
|
||||||
if [ "$RC" -eq 4 ]; then ok ".claude/ alone → exit 4"; else ko "expected 4, got $RC"; fi
|
|
||||||
if [ "$(git -C "$R" rev-parse HEAD)" = "$BEFORE" ]; then ok "no commit created"; else ko "a commit was created"; fi
|
|
||||||
if [ -z "$OUT" ]; then ok "stdout empty (no hash)"; else ko "stdout leaked: [$OUT]"; fi
|
|
||||||
if printf '%s' "$ERR" | grep -qi 'REFUSED'; then ok "stderr is loud (REFUSED)"; else ko "stderr not loud"; fi
|
|
||||||
printf 'dirtied\n' >>"$R/CLAUDE.md"
|
|
||||||
run "$R" commit "docs: T1a-claudemd" "CLAUDE.md"
|
|
||||||
printf ' [CLAUDE.md] rc=%s out=[%s]\n' "$RC" "$OUT"
|
|
||||||
if [ "$RC" -eq 4 ]; then ok "CLAUDE.md alone → exit 4"; else ko "expected 4, got $RC"; fi
|
|
||||||
rm -rf "$R"
|
|
||||||
|
|
||||||
echo "T1b — legit docs only → commits cleanly"
|
|
||||||
R="$(new_repo)"
|
|
||||||
printf 'feature added\n' >>"$R/README.md"
|
|
||||||
printf 'cmd changed\n' >>"$R/USAGE.md"
|
|
||||||
run "$R" commit "docs: T1b update README + USAGE" "README.md" "USAGE.md"
|
|
||||||
COMMITTED="$(head_files "$R")"
|
|
||||||
printf ' rc=%s out(hash)=[%s]\n' "$RC" "$OUT"
|
|
||||||
printf ' committed: [%s]\n' "$COMMITTED"
|
|
||||||
if [ "$RC" -eq 0 ]; then ok "exit 0"; else ko "expected 0, got $RC"; fi
|
|
||||||
if [ "$COMMITTED" = "README.md USAGE.md " ]; then ok "committed exactly README + USAGE"; else ko "got [$COMMITTED]"; fi
|
|
||||||
if [ -n "$OUT" ]; then ok "hash on stdout"; else ko "no hash printed"; fi
|
|
||||||
if [ -z "$(git -C "$R" status --porcelain -- .claude CLAUDE.md)" ]; then ok ".claude/CLAUDE.md untouched"; else ko "forbidden paths touched"; fi
|
|
||||||
rm -rf "$R"
|
|
||||||
|
|
||||||
echo "T1c — MIXED legit + forbidden → exit 4, NOTHING committed (the trap)"
|
|
||||||
R="$(new_repo)"
|
|
||||||
BEFORE="$(git -C "$R" rev-parse HEAD)"
|
|
||||||
printf 'feature added\n' >>"$R/README.md"
|
|
||||||
printf 'dirtied\n' >>"$R/.claude/memory/decisions.md"
|
|
||||||
run "$R" commit "docs: T1c mixed" "README.md" ".claude/memory/decisions.md"
|
|
||||||
printf ' rc=%s out=[%s]\n' "$RC" "$OUT"
|
|
||||||
printf ' err: %s\n' "$(printf '%s' "$ERR" | grep -i decisions | head -1)"
|
|
||||||
if [ "$RC" -eq 4 ]; then ok "mixed → exit 4"; else ko "expected 4, got $RC"; fi
|
|
||||||
if [ "$(git -C "$R" rev-parse HEAD)" = "$BEFORE" ]; then ok "NOTHING committed (README not half-committed)"; else ko "a commit slipped through"; fi
|
|
||||||
if printf '%s' "$ERR" | grep -q '.claude/memory/decisions.md'; then ok "stderr names the offender"; else ko "offender not named"; fi
|
|
||||||
if git -C "$R" status --porcelain | grep -q ' M README.md'; then ok "README left dirty (not embarked)"; else ko "README state wrong"; fi
|
|
||||||
rm -rf "$R"
|
|
||||||
|
|
||||||
echo "T2 — dynamic pathspec: clean passed path filtered, no abort"
|
|
||||||
R="$(new_repo)"
|
|
||||||
printf 'feature added\n' >>"$R/README.md"
|
|
||||||
printf 'cmd changed\n' >>"$R/USAGE.md"
|
|
||||||
# DEPLOY.md passed but NOT modified → must be filtered, must not abort the commit.
|
|
||||||
run "$R" commit "docs: T2" "README.md" "USAGE.md" "DEPLOY.md"
|
|
||||||
COMMITTED="$(head_files "$R")"
|
|
||||||
printf ' rc=%s committed=[%s]\n' "$RC" "$COMMITTED"
|
|
||||||
if [ "$RC" -eq 0 ]; then ok "exit 0 (clean DEPLOY.md did not abort)"; else ko "expected 0, got $RC"; fi
|
|
||||||
if [ "$COMMITTED" = "README.md USAGE.md " ]; then ok "committed README + USAGE only (DEPLOY filtered)"; else ko "got [$COMMITTED]"; fi
|
|
||||||
rm -rf "$R"
|
|
||||||
|
|
||||||
echo "T3 — dangling code (untracked + pre-staged) NOT embarked"
|
|
||||||
R="$(new_repo)"
|
|
||||||
printf 'feature added\n' >>"$R/README.md"
|
|
||||||
printf 'untracked junk\n' >"$R/src/dangling.txt"
|
|
||||||
printf 'staged junk\n' >"$R/src/staged.txt"; git -C "$R" add src/staged.txt
|
|
||||||
run "$R" commit "docs: T3" "README.md"
|
|
||||||
COMMITTED="$(head_files "$R")"
|
|
||||||
STATUS="$(git -C "$R" status --porcelain)"
|
|
||||||
printf ' committed=[%s]\n' "$COMMITTED"
|
|
||||||
if [ "$COMMITTED" = "README.md " ]; then ok "only README committed"; else ko "got [$COMMITTED]"; fi
|
|
||||||
if printf '%s\n' "$STATUS" | grep -q '^?? src/dangling.txt$'; then ok "untracked code left untracked"; else ko "untracked code embarked"; fi
|
|
||||||
if printf '%s\n' "$STATUS" | grep -q '^A src/staged.txt$'; then ok "pre-staged code stays staged"; else ko "pre-staged code embarked"; fi
|
|
||||||
rm -rf "$R"
|
|
||||||
|
|
||||||
echo "T4 — stale-staged doc (A) → commit carries working-tree (B)"
|
|
||||||
R="$(new_repo)"
|
|
||||||
printf 'VERSION A\n' >>"$R/README.md"; git -C "$R" add README.md # stage A
|
|
||||||
printf 'VERSION B\n' >>"$R/README.md" # working tree = A+B
|
|
||||||
run "$R" commit "docs: T4" "README.md"
|
|
||||||
HEADCONTENT="$(git -C "$R" show HEAD:README.md)"
|
|
||||||
printf ' HEAD README tail: %s\n' "$(printf '%s' "$HEADCONTENT" | tail -1)"
|
|
||||||
if printf '%s\n' "$HEADCONTENT" | grep -q 'VERSION B'; then ok "commit contains working-tree B (re-stage neutralized stale index)"; else ko "stale index A leaked"; fi
|
|
||||||
rm -rf "$R"
|
|
||||||
|
|
||||||
echo "T5 — idempotent: empty list / clean tree → no-op exit 0"
|
|
||||||
R="$(new_repo)"
|
|
||||||
BEFORE="$(git -C "$R" rev-parse HEAD)"
|
|
||||||
run "$R" commit "docs: T5 empty" # no files at all
|
|
||||||
printf ' [no files] rc=%s err=%s\n' "$RC" "$(printf '%s' "$ERR" | head -1)"
|
|
||||||
if [ "$RC" -eq 0 ]; then ok "empty list → exit 0"; else ko "expected 0, got $RC"; fi
|
|
||||||
run "$R" commit "docs: T5 clean" "README.md" # passed but clean
|
|
||||||
printf ' [clean README] rc=%s\n' "$RC"
|
|
||||||
if [ "$RC" -eq 0 ]; then ok "clean path → exit 0"; else ko "expected 0, got $RC"; fi
|
|
||||||
if [ "$(git -C "$R" rev-parse HEAD)" = "$BEFORE" ]; then ok "no commit created"; else ko "a commit was created"; fi
|
|
||||||
rm -rf "$R"
|
|
||||||
|
|
||||||
echo "T6 — unsafe state (detached HEAD) → exit 3, no commit"
|
|
||||||
R="$(new_repo)"
|
|
||||||
git -C "$R" checkout --detach -q
|
|
||||||
BEFORE="$(git -C "$R" rev-parse HEAD)"
|
|
||||||
printf 'feature added\n' >>"$R/README.md"
|
|
||||||
run "$R" commit "docs: T6" "README.md"
|
|
||||||
printf ' rc=%s err=%s\n' "$RC" "$(printf '%s' "$ERR" | head -1)"
|
|
||||||
if [ "$RC" -eq 3 ]; then ok "detached HEAD → exit 3"; else ko "expected 3, got $RC"; fi
|
|
||||||
if [ "$(git -C "$R" rev-parse HEAD)" = "$BEFORE" ]; then ok "no commit created"; else ko "a commit was created"; fi
|
|
||||||
rm -rf "$R"
|
|
||||||
|
|
||||||
echo "T7 — path WITH A SPACE passed as one arg → committed (argv is space-safe)"
|
|
||||||
R="$(new_repo)"
|
|
||||||
mkdir -p "$R/docs"
|
|
||||||
printf 'guide baseline\n' >"$R/docs/My Guide.md"
|
|
||||||
git -C "$R" add -A; git -C "$R" commit -qm "add spaced doc"
|
|
||||||
printf 'feature added\n' >>"$R/docs/My Guide.md"
|
|
||||||
run "$R" commit "docs: T7 spaced" "docs/My Guide.md"
|
|
||||||
printf ' rc=%s out(hash)=[%s]\n' "$RC" "$OUT"
|
|
||||||
if [ "$RC" -eq 0 ]; then ok "exit 0"; else ko "expected 0, got $RC"; fi
|
|
||||||
if [ -n "$OUT" ]; then ok "hash printed (commit made)"; else ko "no hash"; fi
|
|
||||||
if git -C "$R" cat-file -e "HEAD:docs/My Guide.md" 2>/dev/null; then ok "spaced path present in HEAD"; else ko "spaced path not committed"; fi
|
|
||||||
if [ -z "$(git -C "$R" status --porcelain -- "docs/My Guide.md")" ]; then ok "spaced doc clean (embarked as ONE file, not split)"; else ko "spaced doc still dirty"; fi
|
|
||||||
rm -rf "$R"
|
|
||||||
|
|
||||||
rm -f "$ERRFILE"
|
|
||||||
echo ""
|
|
||||||
printf 'RESULT: %d passed, %d failed\n' "$PASS" "$FAIL"
|
|
||||||
[ "$FAIL" -eq 0 ]
|
|
||||||
@ -15,10 +15,10 @@ $ARGUMENTS
|
|||||||
## PROGRESS PROTOCOL
|
## PROGRESS PROTOCOL
|
||||||
|
|
||||||
Every STEP must announce itself with a header BEFORE its work block, so the
|
Every STEP must announce itself with a header BEFORE its work block, so the
|
||||||
user always sees where they are in the 12-step pipeline:
|
user always sees where they are in the 13-step pipeline:
|
||||||
|
|
||||||
```
|
```
|
||||||
━━━ STEP <N>/12 — <TITLE> ━━━ (~<estimated minutes>)
|
━━━ STEP <N>/13 — <TITLE> ━━━ (~<estimated minutes>)
|
||||||
why: <one sentence — what's at risk if this step is skipped>
|
why: <one sentence — what's at risk if this step is skipped>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -212,30 +212,13 @@ surgically commits the approved founding decisions (`.claude/memory` +
|
|||||||
STEP 11 FINISH so the memory is integrated with the branch, not stranded. If
|
STEP 11 FINISH so the memory is integrated with the branch, not stranded. If
|
||||||
nothing was capitalized, the helper no-ops — no commit.
|
nothing was capitalized, the helper no-ops — no commit.
|
||||||
|
|
||||||
## STEP 10c — DOC SYNC
|
|
||||||
Run BEFORE STEP 11 FINISH (moved here from post-FINISH). doc-syncer PATCHES public docs but
|
|
||||||
does NOT commit them, and `finishing-a-development-branch` integrates only COMMITTED history
|
|
||||||
— so a patch left uncommitted never reaches the merge/PR. Same PR-stranding class as the
|
|
||||||
STEP 10b capitalize fix (BDR-034).
|
|
||||||
|
|
||||||
Load `$HOME/.claude/agents/doc-syncer.md` (AUTO MODE, scope: files changed this session).
|
|
||||||
Detect drift, update cmds/vars/structure, add recent changes entry.
|
|
||||||
|
|
||||||
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
|
|
||||||
ONLY the files doc-syncer patched (its `PATCHED_FILES` output, one path per line → one argv
|
|
||||||
arg each), never `git add -A`, never `.claude/`/`CLAUDE.md`, and no-ops if nothing was
|
|
||||||
patched. Report per its rc table — rc 4 = a LOUD upstream BDR-022 anomaly, not a silent skip.
|
|
||||||
|
|
||||||
> **Partial fix (conscious).** This commits the docs doc-sync patched, so they reach the
|
|
||||||
> merge/PR. It does NOT fix the scaffold (STEP 5) + bootstrap-README (STEP 5b) commit gap
|
|
||||||
> (BLK-010: no deterministic commit owner; `git worktree add -b` on an unborn HEAD) — a
|
|
||||||
> separate chantier. After this, init-project's doc-sync is fixed but the scaffold/bootstrap
|
|
||||||
> commit gap stays open; GSD STEP 12 still creates ROADMAP.md post-FINISH (BLK-011) — also separate.
|
|
||||||
|
|
||||||
## STEP 11 — FINISH
|
## STEP 11 — FINISH
|
||||||
Invoke `superpowers:finishing-a-development-branch`. Tests pass, build clean, no placeholders, initial commit ready.
|
Invoke `superpowers:finishing-a-development-branch`. Tests pass, build clean, no placeholders, initial commit ready.
|
||||||
|
|
||||||
## STEP 12 — GSD v2 INIT (optional)
|
## STEP 12 — SYNC README
|
||||||
|
Load `$HOME/.claude/agents/doc-syncer.md` (AUTO MODE, scope: files changed this session). Detect drift, update cmds/vars/structure, add recent changes entry.
|
||||||
|
|
||||||
|
## STEP 13 — GSD v2 INIT (optional)
|
||||||
If `multi-session` signal was detected in STEP 0 OR the project has >3 planned milestones:
|
If `multi-session` signal was detected in STEP 0 OR the project has >3 planned milestones:
|
||||||
Ask: "Initialize GSD v2 for multi-session management? (yes / skip)"
|
Ask: "Initialize GSD v2 for multi-session management? (yes / skip)"
|
||||||
- `yes` →
|
- `yes` →
|
||||||
|
|||||||
@ -156,7 +156,7 @@ Load `$HOME/.claude/agents/analyzer.md`. Check: no regressions, no stale code, n
|
|||||||
Invoke `superpowers:requesting-code-review`. Fix all CRITICAL before proceeding.
|
Invoke `superpowers:requesting-code-review`. Fix all CRITICAL before proceeding.
|
||||||
|
|
||||||
## STEP 7 — CAPITALIZE (memory registries)
|
## STEP 7 — CAPITALIZE (memory registries)
|
||||||
Feature shipped implies at least one design decision worth capturing. Run this BEFORE STEP 9 FINISH — the implementation commits (STEP 4) already exist, so the entries' hash references are valid, and the memory commit lands on the branch that FINISH integrates (otherwise it strands outside the PR):
|
Feature shipped implies at least one design decision worth capturing. Run this BEFORE STEP 8 FINISH — the implementation commits (STEP 4) already exist, so the entries' hash references are valid, and the memory commit lands on the branch that FINISH integrates (otherwise it strands outside the PR):
|
||||||
|
|
||||||
1. Scan conversation context for:
|
1. Scan conversation context for:
|
||||||
- **Design / architecture choices with rationale** → candidate for `decisions.md` (BDR-XXX).
|
- **Design / architecture choices with rationale** → candidate for `decisions.md` (BDR-XXX).
|
||||||
@ -186,28 +186,20 @@ If nothing substantive to log → print `CAPITALIZE: nothing substantive to log`
|
|||||||
**Then commit the memory** — follow `$HOME/.claude/lib/capitalize-commit.md`: it
|
**Then commit the memory** — follow `$HOME/.claude/lib/capitalize-commit.md`: it
|
||||||
surgically commits what capitalize just wrote (`.claude/memory` + `.claude/tasks`
|
surgically commits what capitalize just wrote (`.claude/memory` + `.claude/tasks`
|
||||||
only, never `git add -A`) as one `chore(memory)` commit, reports the memory-commit
|
only, never `git add -A`) as one `chore(memory)` commit, reports the memory-commit
|
||||||
hash, and no-ops if nothing was written. It runs BEFORE STEP 9 FINISH so the
|
hash, and no-ops if nothing was written. It runs BEFORE STEP 8 FINISH so the
|
||||||
memory is integrated with the branch, not stranded outside the PR.
|
memory is integrated with the branch, not stranded outside the PR.
|
||||||
|
|
||||||
## STEP 8 — DOC SYNC
|
## STEP 8 — FINISH
|
||||||
Run BEFORE STEP 9 FINISH. doc-syncer PATCHES public docs but does NOT commit them, and
|
|
||||||
`finishing-a-development-branch` integrates only COMMITTED history — so a patch left
|
|
||||||
uncommitted (or committed after) never reaches the merge/PR. Same PR-stranding class as the
|
|
||||||
STEP 7 capitalize fix (BDR-034).
|
|
||||||
|
|
||||||
Load `$HOME/.claude/agents/doc-syncer.md`. Execute in automatic mode:
|
|
||||||
`auto-mode scope: <list of files modified during this session>`
|
|
||||||
|
|
||||||
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
|
|
||||||
ONLY the files doc-syncer patched (its `PATCHED_FILES` output, one path per line → one argv
|
|
||||||
arg each), never `git add -A`, never `.claude/`/`CLAUDE.md`, and no-ops if nothing was
|
|
||||||
patched. Report per its rc table — rc 4 = a LOUD upstream BDR-022 anomaly (doc-syncer
|
|
||||||
surfaced a forbidden path), not a silent skip. It runs BEFORE FINISH so the doc commit lands
|
|
||||||
on the branch FINISH integrates.
|
|
||||||
|
|
||||||
## STEP 9 — FINISH
|
|
||||||
Invoke `superpowers:finishing-a-development-branch`. Tests pass, build clean, ready to merge.
|
Invoke `superpowers:finishing-a-development-branch`. Tests pass, build clean, ready to merge.
|
||||||
|
|
||||||
|
## STEP 9 — DOC SYNC
|
||||||
|
<!-- Stays post-FINISH = TWIN CHANTIER: doc-sync has the same PR-stranding bug
|
||||||
|
capitalize just fixed (artifacts written after integration). Deferred to
|
||||||
|
v-next; move it before FINISH then. Not fixed here to keep v1 scoped. -->
|
||||||
|
Load `$HOME/.claude/agents/doc-syncer.md`.
|
||||||
|
Execute in automatic mode:
|
||||||
|
`auto-mode scope: <list of files modified during this session>`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## RULES
|
## RULES
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user