Compare commits
10 Commits
8536c733ec
...
07e846f0a6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07e846f0a6 | ||
|
|
037e14dacc | ||
|
|
ce34270e47 | ||
|
|
bbfdf10425 | ||
|
|
df60df66b9 | ||
|
|
e8eff7ebcf | ||
|
|
27636789a9 | ||
|
|
b44791bb1b | ||
|
|
bbef41cebf | ||
|
|
58cb91d2b7 |
@ -44,6 +44,7 @@ rules:
|
||||
| BDR-020 | 2026-06-11 | `/audit-delta`: per-axis SHA markers + always-on fix gate + unreachable-first-run = full report-only | accepted |
|
||||
| BDR-022 | 2026-06-18 | doc-syncer scoped to public docs; `.claude/` + `CLAUDE.md` read-only context, never targets; conventions + clean mode | 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 |
|
||||
|
||||
---
|
||||
|
||||
@ -538,3 +539,16 @@ rules:
|
||||
- Blocking yes/skip prompt à la `/onboard` STEP 2.5 — a 2nd STOP mid-build on an optional dep.
|
||||
- Prose "agent remembers not to re-suggest" — fragile behavioral guard, contradicts [[LRN-046]]/[[LRN-047]].
|
||||
- **Reference**: commit `11792cc`, `lib/design-gate.md` §4 + §DETECTION (`+motion`/`+animate`). Helper `lib/animation-lib-check.sh` unchanged. Live via symlink (`~/.claude/lib/`→repo). Builds on [[BDR-005]]. See [[LRN-049]].
|
||||
|
||||
## BDR-034 — Coupled-capitalize invariant v1 — memory commit auto per dev flow (Frame 2)
|
||||
|
||||
- **Date**: 2026-06-26
|
||||
- **Status**: accepted
|
||||
- **Decision**: Dev flows committing code now auto-commit memory same breath, via include `lib/capitalize-commit.md` + helper `lib/memory-commit.sh` (surgical: stages+commits `.claude/memory`+`.claude/tasks` only, pathspec, never `git add -A`). 4 inline flows (feat/hotfix/bugfix/commit-change) ref the include at their capitalize step; ship-feature reordered (CAPITALIZE STEP 7 before FINISH STEP 8 — fixes memory committed after push/PR + stranded outside it); init-project gains STEP 10b founding-decisions capitalize. 1 memory commit/flow (F3). Capitalize CONTENT keeps its approval gate — only the COMMIT of approved entries is automated.
|
||||
- **Why**: Real pain = the 2nd (memory) commit forgotten/manual — ~42% of recent history (17/40 commits) was emergent `chore(memory)`. Frame chosen = "couplé après-code" not "avant commit": keeps hash-anchoring (>50% entries carry `Reference: commit`) + code/memory concern separation; attacks the forgetting, not the ordering. "Capitalize before commit" rejected — inverts a deliberate property AND can't anchor the code hash (hash exists only post-commit).
|
||||
- **Alternatives rejected**:
|
||||
- (a) each orchestrator calls capitalize-before-commit — duplicated across 5+ flows (each has bespoke inline capitalize), breaks hash-anchoring, forgettable on next skill added.
|
||||
- (b) commit-change as the single gate — not on the path of feat/hotfix/bugfix/ship-feature/init-project (they commit inline or via external superpowers); can't detect "pending capitalize".
|
||||
- (c) single commit chokepoint — doesn't exist; 3 distinct commit mechanisms, one external/unmodifiable (`superpowers:finishing-a-development-branch`).
|
||||
- Frame 3 (single unified commit, drop hash) — sacrifices >50% entries' anchoring for history aesthetics.
|
||||
- **Reference**: commits `58cb91d` (helper+tests) · `bbef41c` (hash/stdout + T6/T7) · `b44791b` (include) · `2763678` (4 flows) · `e8eff7e` (ship-feature reorder) · `df60df6` (init-project). Hook (v2, Stop-hook non-blocking BDR-033-style) + doc-sync twin chantier (same PR bug, reorder before FINISH) deferred. See [[LRN-051]], [[LRN-052]], [[EVAL-007]].
|
||||
|
||||
@ -27,6 +27,7 @@ rules:
|
||||
| EVAL-004 | 2026-06-11 | darwin eval 26 perso skills + 4-bug fix round | keep |
|
||||
| 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 |
|
||||
|
||||
---
|
||||
|
||||
@ -86,3 +87,12 @@ rules:
|
||||
- **Method**: run-deterministic all-green (RED-1/2/5/6); RED-3/4 by single faithful subagent runs on throwaway fixtures (deterministic oracles — byte-identical + layer-(a) substring; N=6 tolerance-zero fleet documented in run-behavioral.md, NOT exhausted — 1 run sufficed to prove presence then closure); REAL-data re-test on live learnings.md (602 l): fidelity 0 false-positive vs old line-grep 13, census PROVEN counting both sides (HEAD/WORK not=107/114, no=64/71, never=34/34 — no category dropped). Scope held (only learnings.md), reverted after (measurement, not a kept prune). Clean tree verified first; git + cp backup.
|
||||
- **Anomalies**: (1) SAFE ≠ USEFUL — compression (pass C) marginal on already-caveman dense content (~3.6% trim, registry GREW); real value = index-drift (D found 19 missing Index rows on the real learnings.md, measured then reverted) + merge (B), not C on dense. (2) RED-8 OPEN — fidelity proves "no negation DELETED", not "none ADDED wrongly"; visible empirically (not/no +7 in the measurement, passed under the drop-radar); remote (compression subtracts) but real. (3) RED-7 OPEN — merge LRN-014+016 maybe PRIMED by the SKILL.md example, not real overlap; verify. (4) two guard bugs caught by VALIDATION not logic: awk `\<` unsupported (mawk) → not/no counted 0; `NR==FNR` blind when working census empty = the deletion case → both fixed + re-validated. (5) REAL ANCHOR FOR PASS D — this very evals.md had EVAL-005's Index row MISSING (pre-existing drift), exactly what the skill's D pass auto-corrects; hand-backfilled in a separate `fix(memory)` commit. learnings.md likewise carries 19 missing Index rows (deferred to an intentional prune). D is NOT theoretical.
|
||||
- **Action**: keep — safe, useful for B/D, compression marginal on dense (documented limit). `Fixed in v1.1 (TDD found it)` WAS the RED-1 defect (claim of a verify never run); TDD note now TRUE (real suite passes). Patterns → LRN-046/047/048; open items → tests/BACKLOG.md (RED-7/RED-8).
|
||||
|
||||
## EVAL-007 — Coupled-capitalize machinery (helper + include + 6 flows)
|
||||
|
||||
- **Date**: 2026-06-26
|
||||
- **Output**: `lib/memory-commit.sh` + `lib/capitalize-commit.md` + wiring of 6 dev flows for the coupled-capitalize invariant (BDR-034).
|
||||
- **Method**: TDD — RED harness first (helper absent → fail), then 13 deterministic tests (`lib/tests/run-deterministic.sh`): T1/T2 dangling code (untracked + pre-staged) not embarked, T2-bis stale-staged memory → working-tree committed, T3 idempotent no-op, T4 fail-closed on broken git state (exit 3), T5 TODO.md in scope, T6 stdout contract 3-case (hash/empty/empty), T7 double-run = at most one commit. Plus in-vivo e2e (code commit → capitalize writes memory → include commits it: 3 commits, memory commit `.claude/` only, dangling untouched, mem_hash ≠ code_hash). `shellcheck lib/*.sh` clean.
|
||||
- **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.
|
||||
- **Reference**: commits `58cb91d`..`df60df6`.
|
||||
|
||||
@ -201,3 +201,8 @@ rules:
|
||||
- TDD'd `/prune-memory` (only destructive skill, untested + carried a false `Fixed in v1.1 (TDD found it)` claim): 6 dangers (RED-1..6) closed by deterministic guards, skill `0a3e766`. Real-data run on learnings.md exposed SAFE≠USEFUL (compression marginal on dense; value = index/merge, not C) + a 13/13-false-positive line-grep fidelity guard → replaced by a per-entry count census (0 FP, proven counting both sides). RED-7 (example-priming) + RED-8 (added-negation) filed in BACKLOG. EVAL-006, LRN-046/047/048.
|
||||
- Wired `design-gate.md` §4: anim-lib suggestion when a design task hits a motion signal — suggest-only, non-blocking, stateless 1-line (no marker). `motion`/`animate` added to §DETECTION (source). Chose stateless-minimal over a state marker, conditional on stakes: a 1-line cosmetic note's re-fire is annoyance not risk → no marker-grade infra (unlike LRN-046/047's destructive context). Helper unchanged, no 3rd copy of the lib list. Live via symlink. BDR-033, LRN-049.
|
||||
- Process: caught "write-before-show" twice this session on a live (symlinked) file → on edit=deploy targets the pre-write diff is the only control gate → inverted to show→validate→write. LRN-050.
|
||||
|
||||
## 2026-06-26
|
||||
- Coupled-capitalize invariant v1: dev flows auto-commit memory via include `lib/capitalize-commit.md` + helper `lib/memory-commit.sh` (surgical pathspec, never `-A`; hash→stdout). Frame 2 (après-code-couplé, hash-anchoring kept, 2 commits, memory commit automatic per flow). 6 commits `58cb91d..df60df6`. BDR-034, LRN-051/052, EVAL-007.
|
||||
- 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.
|
||||
|
||||
@ -49,6 +49,8 @@ rules:
|
||||
| LRN-046 | 2026-06-25 | Destructive skill: deterministic oracle (byte-identical / count census) > semantic judge | any destructive/irreversible skill; behavioral-oracle TDD |
|
||||
| LRN-047 | 2026-06-25 | A noisy safety guard (13/13 FP) = a guard you learn to ignore = risk → refine, don't tolerate | any guard/alert/lint that can false-positive |
|
||||
| LRN-048 | 2026-06-25 | A "0/OK/pass" must prove it LOOKED (counted both sides), else verify hard-wired to pass | any verify/test/lint reporting success |
|
||||
| LRN-051 | 2026-06-26 | `git commit -- pathspec` strict on no-match → filter scoped commits to changed paths | any scoped-commit automation |
|
||||
| LRN-052 | 2026-06-26 | Hash-anchoring: 2 cases it does NOT apply (pre-code founding, squash-merge) | capitalizing founding/arch decisions; squash repos |
|
||||
|
||||
---
|
||||
|
||||
@ -643,3 +645,19 @@ rules:
|
||||
- **Context**: this session twice wrote-then-showed on `lib/design-gate.md` (live via symlink). Both harmless (non-destructive), but the pattern would bite on a destructive live edit. User flagged it → inverted to show→validate→write.
|
||||
- **Future application**: before editing any file, check if it is live (`readlink -f`, compare to `~/.claude/`); if live, treat the pre-write diff as a mandatory approval gate, not an optional preview. Generalizes to any "edit = deploy" target (dotfiles, served config, hot-reloaded sources).
|
||||
- **Reference**: `lib/design-gate.md` (symlink → `~/.claude/lib/`). Sibling to [[LRN-044]] (write-through-symlink → resolve real path). Linked to [[BDR-033]].
|
||||
|
||||
## LRN-051 — `git commit -- <pathspec>` strict on no-match → filter scoped commits to changed paths
|
||||
|
||||
- **Date**: 2026-06-26
|
||||
- **Pattern**: Automating a scoped commit (commit only subtree X), pass to `git add`/`git commit` ONLY paths with real pending changes. `git add -- <pathspec>` TOLERATES a no-match pathspec (rc 0, stages the matching ones); `git commit -- <pathspec>` is STRICT — one no-match pathspec ABORTS the whole commit (`error: pathspec '<x>' did not match any file(s) known to git`). So a clean scoped path (e.g. empty `.claude/tasks`) silently aborts the commit on most runs. Filter via `git status --porcelain -- <path>` to changed paths only. Bonus: `git commit -- pathspec` = PARTIAL commit (working-tree of those paths, ignores rest of index) → surgical-scope safety: dangling code (untracked OR pre-staged) never embarked.
|
||||
- **Context**: building `lib/memory-commit.sh`. Naive `git commit -- .claude/memory .claude/tasks` aborted whenever `.claude/tasks` was clean. Caught by real-exec test (T1/T2/T2-bis), NOT by assuming git's behavior — `add` and `commit` are NOT symmetric on pathspecs.
|
||||
- **Future application**: any "commit only subtree X" automation — filter to changed paths; rely on partial-commit for surgical scope; never assume tool behavior symmetric across sibling subcommands — exec-test it.
|
||||
- **Reference**: commit `58cb91d` (`_changed_paths` filter + T1/T2/T2-bis), `bbef41c` (stdout hash contract). See [[BDR-034]].
|
||||
|
||||
## LRN-052 — Hash-anchoring applicability — 2 cases where `Reference: commit <hash>` does NOT apply
|
||||
|
||||
- **Date**: 2026-06-26
|
||||
- **Pattern**: The anchoring convention (`Reference: commit <hash>`) means "the commit that IMPLEMENTS this decision" (BDR-033 → 11792cc). It does NOT apply in 2 cases: (1) a FOUNDING decision made pre-code (at design time) — attested by no implementing commit; anchoring it to the unrelated scaffold commit is a FALSE anchor. (2) a SQUASH-MERGED PR — the anchored commit ceases to exist post-squash. Forcing a hash in either case dilutes what "anchored" means everywhere else. Rule: pre-code founding decisions carry NO hash (path+date suffice); squash-merge workflows can't anchor.
|
||||
- **Context**: building init-project STEP 10b (capitalize founding architecture decisions). A founding "Astro not Next" has no implementing commit. Surfaced the BOUNDARY of the anchoring convention — completes it, not contradicts it.
|
||||
- **Future application**: capitalizing founding/architecture decisions, or working in squash-merge repos — do NOT fabricate a hash; the anchor only means something when a real implementing commit exists.
|
||||
- **Reference**: commit `df60df6` (init-project STEP 10b hash rule), `lib/capitalize-commit.md` (2-hash non-confusion). See [[BDR-034]], [[BDR-033]].
|
||||
|
||||
60
.claude/tasks/2026-06-26-coupled-capitalize-invariant.md
Normal file
60
.claude/tasks/2026-06-26-coupled-capitalize-invariant.md
Normal file
@ -0,0 +1,60 @@
|
||||
# Coupled-capitalize Invariant — Implementation Plan (v1)
|
||||
|
||||
> Frozen 2026-06-26. Frame 2 (capitalize AFTER code commit, hash anchoring kept,
|
||||
> 2 commits kept) — make the memory commit automatic & never-forgotten, coupled
|
||||
> per dev flow. Hook = v2 (separate). doc-sync = twin chantier (same PR bug,
|
||||
> queued). NO `git add -A` ever — safety lives in the pathspec.
|
||||
|
||||
**Goal:** every dev flow that commits code also capitalizes AND commits its
|
||||
memory automatically, in the same breath, via a shared include — without
|
||||
duplicating logic and without ever embarking dangling code.
|
||||
|
||||
**Architecture:** `lib/memory-commit.sh` (detect + surgical commit scoped to
|
||||
`.claude/memory` + `.claude/tasks`, never `-A`) + include `lib/capitalize-commit.md`
|
||||
referenced by the 6 flows (design-gate.md pattern). ship-feature reordered
|
||||
(capitalize before FINISH) to fix the PR-stranding bug.
|
||||
|
||||
## Global Constraints (verbatim, apply to every task)
|
||||
- Stage: `git add -- .claude/memory .claude/tasks`. Commit: `git commit -m <msg> -- .claude/memory .claude/tasks`. NEVER `git add -A` / `git add .` / `git commit -a`.
|
||||
- Idempotent: clean tree → no-op, exit 0, no commit.
|
||||
- Fail-closed on broken state: detached HEAD / merge / rebase / cherry-pick in progress → no commit, skip (exit 3).
|
||||
- Registries always English. Commit msg style: `chore(memory): <IDs> — <flow> <short>`.
|
||||
- Capitalize CONTENT keeps its approval gate (unchanged); only the COMMIT of approved entries becomes automatic.
|
||||
- shellcheck clean on `lib/memory-commit.sh` + test harness.
|
||||
|
||||
## File Structure
|
||||
| Action | File | Responsibility |
|
||||
|---|---|---|
|
||||
| Create | `lib/memory-commit.sh` | detect + surgical commit (CLI + sourceable) |
|
||||
| Create | `lib/capitalize-commit.md` | include protocol |
|
||||
| Create | `lib/tests/run-deterministic.sh` | T1–T5 + T2-bis, git fixture via mktemp |
|
||||
| Create | `lib/tests/run-behavioral.md` | end-to-end manual per-flow check |
|
||||
| Modify | `agents/feater.md` | STEP 6 → reference include |
|
||||
| Modify | `agents/hotfixer.md` | STEP 5 → reference include |
|
||||
| Modify | `agents/bugfixer.md` | STEP 7 → reference include |
|
||||
| Modify | `agents/commit-changer.md` | Phase 4 → reference include |
|
||||
| Modify | `skills/ship-feature/SKILL.md` | reorder capitalize before FINISH + reference |
|
||||
| Modify | `skills/init-project/SKILL.md` | net-new capitalize founding decisions (F5) before FINISH |
|
||||
| Modify | `CHANGELOG.md` + `.claude/memory/` | document the invariant (BDR + LRN) |
|
||||
|
||||
## Tasks
|
||||
- **Task 1** — `lib/memory-commit.sh` (TDD). Tests T1, T2, **T2-bis**, T3, T4, T5
|
||||
MUST be REALLY EXECUTED with real outputs reported before Task 2 (user hard
|
||||
requirement — no presumed git behavior).
|
||||
- T1: untracked dangling code NOT embarked.
|
||||
- T2: pre-staged dangling code NOT embarked, remains staged.
|
||||
- **T2-bis: stale-staged memory (version A) vs working-tree (version B) →
|
||||
commit must contain B (what capitalize wrote), not A.** Proves `git add --`
|
||||
re-stage neutralizes stale index. If raw pathspec-commit took A → guard needed.
|
||||
- T3: idempotence (clean → no-op exit 0). T4: unsafe state → skip exit 3.
|
||||
T5: TODO.md embarked.
|
||||
- **Task 2** — `lib/capitalize-commit.md` include (WHEN / DO / HARD RULE / ORDERING / IDEMPOTENT).
|
||||
- **Task 3** — wire feater/hotfixer/bugfixer/commit-changer (1-line reference each).
|
||||
- **Task 4** — ship-feature reorder: 7=CAPITALIZE(+include), 8=FINISH, 9=DOC SYNC; renumber FAILURE PATHS; doc-sync stays post-FINISH (twin chantier note).
|
||||
- **Task 5** — init-project: add STEP 10.6 CAPITALIZE FOUNDING DECISIONS (F5 filter table: structuring decision = capitalize, scaffold detail = skip) before STEP 11 FINISH.
|
||||
- **Task 6** — behavioral verify + shellcheck + CHANGELOG + BDR/LRN (dogfood the include).
|
||||
|
||||
## Deferred (explicit, by design not by omission)
|
||||
- Hook (Stop, non-blocking, stateless, BDR-033 style) → v2.
|
||||
- doc-sync PR-stranding (same reorder-before-FINISH) → twin chantier.
|
||||
- hash-anchoring vs squash-merge → known blind spot, out of scope.
|
||||
@ -233,3 +233,17 @@ Version 3.4.0 → 3.5.0.
|
||||
- [x] PHASE 3: install-plugins.sh STEP 5.5; update-all.sh block; plugins.lock.json; doctor.sh; lib/detect-plugins.sh; lib/profile.sh; plugin-advisor.md; skills/profile/SKILL.md
|
||||
- [x] PHASE 4: README row; USAGE always-on line; CHANGELOG; CLAUDE.md cmd ref; skills/capitalize+prune-memory cmd refs; version.txt
|
||||
- [x] PHASE 5: shellcheck clean (SC1091 info only); full diff reviewed → committed + merged to master
|
||||
|
||||
## 2026-06-26 — coupled-capitalize invariant v1 (Frame 2)
|
||||
Plan: [.claude/tasks/2026-06-26-coupled-capitalize-invariant.md](2026-06-26-coupled-capitalize-invariant.md)
|
||||
Goal: every dev flow commits its memory automatically (1 commit/flow) via shared
|
||||
include; ship-feature reordered (capitalize before FINISH = PR-bug fix). Hook v2,
|
||||
doc-sync twin chantier deferred. Safety in the pathspec, never `git add -A`.
|
||||
- [x] Task 1 — `lib/memory-commit.sh` + tests T1/T2/T2-bis/T3/T4/T5/T6/T7 (real exec, outputs reported) — 58cb91d + bbef41c
|
||||
- [x] Task 2 — `lib/capitalize-commit.md` include — b44791b
|
||||
- [x] Task 3 — wire feater/hotfixer/bugfixer/commit-changer — 2763678
|
||||
- [x] Task 4 — ship-feature reorder (capitalize before FINISH) — e8eff7e
|
||||
- [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)
|
||||
|
||||
@ -9,6 +9,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
||||
<!-- DRAFT (doc-syncer): grounded in commits since 3.4.0; review wording + completeness before release. -->
|
||||
|
||||
### 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`)
|
||||
- `/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`
|
||||
- `/prune-memory` — curate and compress the `.claude/memory/` registries
|
||||
@ -23,6 +24,8 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
||||
- `.claude/{tasks,memory,audits}/` governance layout + 5 memory registries (decisions, learnings, blockers, journal, evals)
|
||||
|
||||
### Changed
|
||||
- `/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
|
||||
- `/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
|
||||
- `/onboard` rewritten: archetype-aware pipeline (orchestrator + config-only agent), security audit archetype-aware
|
||||
|
||||
@ -184,6 +184,11 @@ A bugfix with an understood root cause is almost always worth one entry:
|
||||
|
||||
If the bug was trivial and the root cause not transferable → skip with `CAPITALIZE: trivial, skip`.
|
||||
|
||||
**Then commit the memory** — follow `$HOME/.claude/lib/capitalize-commit.md`: it
|
||||
surgically commits what capitalize just wrote (`.claude/memory` + `.claude/tasks`
|
||||
only, never `git add -A`) as one `chore(memory)` commit, reports the memory-commit
|
||||
hash, and no-ops if nothing was written.
|
||||
|
||||
---
|
||||
|
||||
## RULES
|
||||
|
||||
@ -152,3 +152,9 @@ Append approved entries + update the Index of each registry file. Add a line to
|
||||
**Language rule**: written entries are ALWAYS in English (see CLAUDE.md "Memory registries" § Language). The interactive gate may mirror the user's language; the appended entries must not.
|
||||
|
||||
If all commits are pure chore/docs/style with nothing to log → skip with `CAPITALIZE: nothing to log`.
|
||||
|
||||
**Then commit the memory** — follow `$HOME/.claude/lib/capitalize-commit.md`: it
|
||||
surgically commits what capitalize just wrote (`.claude/memory` + `.claude/tasks`
|
||||
only, never `git add -A`) as one `chore(memory)` commit, reports the memory-commit
|
||||
hash, and no-ops if nothing was written. This is a separate commit from the Phase 3
|
||||
code commits — their hashes are already anchored inside the entries.
|
||||
|
||||
@ -152,6 +152,11 @@ Always append a 1-line entry to today's heading in `.claude/memory/journal.md`.
|
||||
|
||||
If no substantive capture candidate → skip with `CAPITALIZE: nothing to log`.
|
||||
|
||||
**Then commit the memory** — follow `$HOME/.claude/lib/capitalize-commit.md`: it
|
||||
surgically commits what capitalize just wrote (`.claude/memory` + `.claude/tasks`
|
||||
only, never `git add -A`) as one `chore(memory)` commit, reports the memory-commit
|
||||
hash, and no-ops if nothing was written.
|
||||
|
||||
---
|
||||
|
||||
## RULES
|
||||
|
||||
@ -124,6 +124,12 @@ Always append a 1-line entry to today's heading in `.claude/memory/journal.md` (
|
||||
|
||||
**Language rule**: the journal line and any proposed BLK/LRN entries are ALWAYS written in English (see CLAUDE.md "Memory registries" § Language).
|
||||
|
||||
**Then commit the memory** — follow `$HOME/.claude/lib/capitalize-commit.md`: it
|
||||
surgically commits what capitalize just wrote (`.claude/memory` + `.claude/tasks`
|
||||
only, never `git add -A`) as one `chore(memory)` commit, reports the memory-commit
|
||||
hash, and no-ops if nothing was written. The always-on journal line means a
|
||||
trivial hotfix still produces a `chore(memory): journal — …` commit (Frame 2 / F3).
|
||||
|
||||
---
|
||||
|
||||
## RULES
|
||||
|
||||
89
lib/capitalize-commit.md
Normal file
89
lib/capitalize-commit.md
Normal file
@ -0,0 +1,89 @@
|
||||
# CAPITALIZE-COMMIT — couple the memory commit to the dev flow
|
||||
|
||||
Inline snippet. Include at the END of any dev flow's CAPITALIZE step, AFTER the
|
||||
approved registry / journal / TODO entries are written. It commits ONLY the
|
||||
memory, surgically, so the flow never leaves an uncommitted `.claude/memory`
|
||||
behind and never embarks dangling code.
|
||||
|
||||
This is the TAIL of capitalize, not a replacement: the SCAN + APPROVAL GATE that
|
||||
decide WHAT to write are unchanged and run before this snippet. Only the COMMIT
|
||||
of the already-approved entries is automated here.
|
||||
|
||||
## WHEN TO RUN
|
||||
|
||||
At the capitalize step, once the approved entries are written to
|
||||
`.claude/memory/*` (and any `.claude/tasks/TODO.md` reconcile is done), with the
|
||||
code already committed.
|
||||
|
||||
- Inline-commit flows (feat / hotfix / bugfix / commit-change): run it right
|
||||
after writing the entries, on the current branch.
|
||||
- Orchestrators that integrate via `superpowers:finishing-a-development-branch`
|
||||
(ship-feature / init-project): run it BEFORE the FINISH step — otherwise the
|
||||
memory commit strands outside the merge/PR. See ORDERING.
|
||||
|
||||
This snippet commits whatever is PENDING under `.claude/memory` + `.claude/tasks`;
|
||||
it does NOT decide content. A flow whose gate wrote only a journal line yields a
|
||||
`chore(memory): journal — …` commit (one memory commit per flow — Frame 2 / F3).
|
||||
If a flow should stay fully silent on a trivial run, that is the FLOW's gate
|
||||
policy (e.g. hotfix skipping the journal line), not this snippet's concern —
|
||||
tune it there.
|
||||
|
||||
## DO
|
||||
|
||||
1. Compose the message from the IDs just written + the flow, matching repo style
|
||||
`chore(memory): <IDs> — <flow> <short>`. Examples:
|
||||
- `chore(memory): BDR-034 + LRN-051 — feat dark-mode toggle`
|
||||
- `chore(memory): BLK-010 resolved — bugfix profile path`
|
||||
- `chore(memory): journal — hotfix copy typo` (journal-only, nothing else)
|
||||
|
||||
2. Commit surgically via the helper, capturing the memory-commit hash it prints:
|
||||
|
||||
mem_hash=$(bash "$HOME/.claude/lib/memory-commit.sh" commit "<message>")
|
||||
rc=$?
|
||||
|
||||
3. Report by (rc, mem_hash):
|
||||
- rc 0, mem_hash non-empty → `✅ mémoire committée <mem_hash>`
|
||||
- rc 0, mem_hash empty → `CAPITALIZE: rien à committer` (helper no-op)
|
||||
- rc 3 → unsafe git state (detached / merge in progress);
|
||||
memory stays in the working tree for a manual
|
||||
commit — surface the helper's stderr.
|
||||
|
||||
`<mem_hash>` is the MEMORY commit (the one that ADDS the entries). It is NOT
|
||||
the code-commit hash that capitalize anchored INSIDE the entries
|
||||
(`Reference: commit <code-hash>`). Two commits, two hashes — never report the
|
||||
code hash here.
|
||||
|
||||
## HARD RULE — surgical scope
|
||||
|
||||
The helper stages and commits ONLY `.claude/memory` + `.claude/tasks`, filtered
|
||||
to paths with real changes, via pathspec — never `git add -A` / `git add .` /
|
||||
`git commit -a`. Automation removes the human diff review that would catch an
|
||||
accidental stage, so the scope IS the safety. Do NOT bypass the helper with a
|
||||
manual `git add` / `git commit`: that reintroduces the exact risk the helper
|
||||
removes (proven: dangling code, untracked or pre-staged, is never embarked; a
|
||||
no-match pathspec is filtered, not fatal).
|
||||
|
||||
## ORDERING (orchestrators only)
|
||||
|
||||
`finishing-a-development-branch` may merge-and-delete the branch or push a PR. A
|
||||
memory commit created AFTER it lands outside the integrated history — stranded
|
||||
on the PR path. So in ship-feature / init-project this snippet runs BEFORE
|
||||
FINISH. The code commits already exist (implementation step), so the entries'
|
||||
hash references are valid at this point.
|
||||
|
||||
## WHAT THIS DOES NOT DO
|
||||
|
||||
- Does NOT commit code — the code commit happened upstream (implementation step).
|
||||
- Does NOT push — pushing / merging is FINISH's job, which runs AFTER this in
|
||||
orchestrators.
|
||||
- Does NOT decide WHAT to capitalize — the scan + approval gate upstream own
|
||||
that; this only commits what was already approved and written.
|
||||
- Does NOT reference or echo the code-commit hash anchored in the entries — it
|
||||
reports only the memory-commit hash the helper returns.
|
||||
- Does NOT run if you hand-roll `git add` / `git commit` instead of the helper —
|
||||
bypassing it drops the surgical-scope guarantee. Always call the helper.
|
||||
|
||||
## IDEMPOTENT
|
||||
|
||||
Safe to run when memory is already clean: the helper no-ops (exit 0, empty
|
||||
stdout, no commit). Running it twice creates at most one commit.
|
||||
108
lib/memory-commit.sh
Executable file
108
lib/memory-commit.sh
Executable file
@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env bash
|
||||
# memory-commit.sh — surgically commit ONLY .claude/memory + .claude/tasks.
|
||||
#
|
||||
# Used by the dev-flow capitalize step (and, later, the v2 Stop hook) to couple
|
||||
# the memory commit to the flow. Safety lives in the PATHSPEC, never in a human
|
||||
# diff review — automation removes that review, so the scope must be airtight:
|
||||
# code that happens to be dirty or staged is NEVER embarked.
|
||||
#
|
||||
# Usage (CLI):
|
||||
# memory-commit.sh pending # exit 0 if memory/tasks have changes, 1 if clean
|
||||
# memory-commit.sh commit "<message>" # surgical commit; exit 0 ok/no-op, 3 unsafe state
|
||||
#
|
||||
# Output contract for `commit`: diagnostics go to stderr; on a real commit the
|
||||
# short hash of the MEMORY commit is the ONLY thing on stdout (empty on no-op or
|
||||
# unsafe), so callers can capture it: `mem_hash=$(memory-commit.sh commit "msg")`.
|
||||
#
|
||||
# Sourceable: `memory_pending` and `commit_memory` for the v2 hook.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
MC_PATHS=(".claude/memory" ".claude/tasks")
|
||||
|
||||
_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
|
||||
}
|
||||
|
||||
# Scoped paths that have actual pending changes. A bare/empty path (e.g. an
|
||||
# empty .claude/tasks dir) is excluded: `git commit -- <pathspec>` aborts the
|
||||
# WHOLE commit on a pathspec that matches no known file, even though `git add`
|
||||
# tolerates it. So scope = only paths git would accept.
|
||||
_changed_paths() {
|
||||
local p
|
||||
for p in "${MC_PATHS[@]}"; do
|
||||
[ -e "$p" ] || continue
|
||||
[ -n "$(git status --porcelain -- "$p" 2>/dev/null)" ] && printf '%s\n' "$p"
|
||||
done
|
||||
}
|
||||
|
||||
# 0 if something is pending under the scoped paths, 1 if clean / absent.
|
||||
memory_pending() {
|
||||
_in_git_repo || return 1
|
||||
local changed
|
||||
mapfile -t changed < <(_changed_paths)
|
||||
[ "${#changed[@]}" -gt 0 ]
|
||||
}
|
||||
|
||||
# Surgical commit of the scoped paths only. Returns 0 (ok or no-op), 3 (unsafe).
|
||||
# On a real commit, prints the memory-commit short hash to stdout (stderr = diag).
|
||||
commit_memory() {
|
||||
local msg="${1:?commit message required}"
|
||||
_in_git_repo || {
|
||||
echo "memory-commit: not a git repo — skip" >&2
|
||||
return 3
|
||||
}
|
||||
if _unsafe_state; then
|
||||
echo "memory-commit: detached HEAD or merge/rebase in progress — skip (no commit)" >&2
|
||||
return 3
|
||||
fi
|
||||
local changed
|
||||
mapfile -t changed < <(_changed_paths)
|
||||
if [ "${#changed[@]}" -eq 0 ]; then
|
||||
echo "memory-commit: nothing pending — no-op" >&2
|
||||
return 0
|
||||
fi
|
||||
# Re-stage working-tree content of the scoped paths over any stale index entry,
|
||||
# then commit ONLY those paths. The pathspec on `git commit` makes it a partial
|
||||
# commit: other staged files (dangling code) are not recorded.
|
||||
git add -- "${changed[@]}"
|
||||
if git diff --cached --quiet -- "${changed[@]}"; then
|
||||
echo "memory-commit: only ignored/no-op changes — no-op" >&2
|
||||
return 0
|
||||
fi
|
||||
# Contract: diagnostics go to stderr; on success ONLY the memory-commit short
|
||||
# hash goes to stdout, so a caller can do `mem_hash=$(... commit "msg")`.
|
||||
git commit -q -m "$msg" -- "${changed[@]}"
|
||||
git rev-parse --short HEAD
|
||||
}
|
||||
|
||||
main() {
|
||||
local cmd="${1:-}"
|
||||
case "$cmd" in
|
||||
pending) memory_pending ;;
|
||||
commit)
|
||||
shift
|
||||
commit_memory "${1:-}"
|
||||
;;
|
||||
*)
|
||||
echo "usage: memory-commit.sh {pending | commit <message>}" >&2
|
||||
return 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Run main only when executed, not when sourced.
|
||||
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
|
||||
main "$@"
|
||||
fi
|
||||
45
lib/tests/run-behavioral.md
Normal file
45
lib/tests/run-behavioral.md
Normal file
@ -0,0 +1,45 @@
|
||||
# Behavioral check — coupled-capitalize, end-to-end
|
||||
|
||||
The deterministic suite (`run-deterministic.sh`, T1–T7) proves `memory-commit.sh`
|
||||
in isolation. This is the in-vivo whole-chain check: a real dev-flow shape —
|
||||
code commit, then capitalize writes memory, then the include commits it — with
|
||||
dangling code present, proving the memory commit is coupled AND surgical.
|
||||
|
||||
## Scenario (run on a throwaway repo)
|
||||
|
||||
```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 .claude/tasks src
|
||||
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"
|
||||
code_hash="$(git rev-parse --short HEAD)"
|
||||
|
||||
# 2) capitalize writes the approved entry (referencing the code hash) + journal
|
||||
printf '\n## BDR-099 — example\n- Reference: commit %s\n' "$code_hash" >> .claude/memory/decisions.md
|
||||
printf -- '- did the thing\n' >> .claude/tasks/TODO.md
|
||||
|
||||
# 3) a code file is left dangling (must NOT be embarked)
|
||||
printf 'WIP do not commit\n' > src/dangling.txt
|
||||
|
||||
# 4) the include commits the memory surgically
|
||||
mem_hash="$(bash "$HOME/.claude/lib/memory-commit.sh" commit "chore(memory): BDR-099 — example")"
|
||||
```
|
||||
|
||||
## Expected (assert)
|
||||
|
||||
- Exactly TWO commits after baseline: the code commit, then the memory commit.
|
||||
- The memory commit (`$mem_hash`) contains ONLY `.claude/memory/decisions.md`
|
||||
and `.claude/tasks/TODO.md` — never `src/feature.txt` (already committed) or
|
||||
`src/dangling.txt` (WIP).
|
||||
- `src/dangling.txt` is still untracked after the memory commit.
|
||||
- `$mem_hash` (the memory commit) ≠ `$code_hash` (anchored inside the entry).
|
||||
|
||||
If all hold, the chain is coupled (memory committed in the same breath as the
|
||||
flow) and surgical (no dangling code embarked). This mirrors what feat / hotfix /
|
||||
bugfix / commit-change do via their capitalize step, and what ship-feature /
|
||||
init-project do before FINISH.
|
||||
146
lib/tests/run-deterministic.sh
Executable file
146
lib/tests/run-deterministic.sh
Executable file
@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deterministic tests for lib/memory-commit.sh.
|
||||
#
|
||||
# Proves the surgical-scope safety contract on REAL git behavior (not assumed):
|
||||
# - dangling code (untracked OR pre-staged) is NEVER embarked in a memory commit
|
||||
# - stale-staged memory (version A) yields the WORKING-TREE version (B) that
|
||||
# capitalize just wrote — `git add --` re-stage neutralizes the stale index
|
||||
# - clean tree → no-op ; broken git state → skip ; TODO.md is in scope
|
||||
#
|
||||
# No -e: run every test and report, even after a failure.
|
||||
set -uo pipefail
|
||||
|
||||
HERE="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
HELPER="$HERE/../memory-commit.sh"
|
||||
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 with a baseline commit (.claude/memory tracked, src/ 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/.claude/tasks" "$d/src"
|
||||
printf '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 (no diff noise).
|
||||
head_files() { git -C "$1" diff-tree --no-commit-id --name-only -r HEAD; }
|
||||
|
||||
echo "T1 — untracked dangling code is NOT embarked"
|
||||
R="$(new_repo)"
|
||||
printf 'NEW DECISION\n' >>"$R/.claude/memory/decisions.md"
|
||||
printf 'junk\n' >"$R/src/dangling.txt"
|
||||
(cd "$R" && "$HELPER" commit "chore(memory): T1") >/dev/null 2>&1
|
||||
committed="$(head_files "$R")"
|
||||
status="$(git -C "$R" status --porcelain)"
|
||||
printf ' committed: [%s]\n' "$committed"
|
||||
printf ' status : [%s]\n' "$status"
|
||||
if [ "$committed" = ".claude/memory/decisions.md" ]; then ok "only memory committed"; else ko "expected only memory, got [$committed]"; fi
|
||||
if printf '%s\n' "$status" | grep -q '^?? src/dangling.txt$'; then ok "dangling code still untracked"; else ko "dangling code not left untracked"; fi
|
||||
rm -rf "$R"
|
||||
|
||||
echo "T2 — PRE-STAGED dangling code is NOT embarked, stays staged"
|
||||
R="$(new_repo)"
|
||||
printf 'NEW DECISION\n' >>"$R/.claude/memory/decisions.md"
|
||||
printf 'junk\n' >"$R/src/dangling.txt"
|
||||
git -C "$R" add src/dangling.txt
|
||||
(cd "$R" && "$HELPER" commit "chore(memory): T2") >/dev/null 2>&1
|
||||
committed="$(head_files "$R")"
|
||||
status="$(git -C "$R" status --porcelain)"
|
||||
printf ' committed: [%s]\n' "$committed"
|
||||
printf ' status : [%s]\n' "$status"
|
||||
if [ "$committed" = ".claude/memory/decisions.md" ]; then ok "only memory committed"; else ko "expected only memory, got [$committed]"; fi
|
||||
if printf '%s\n' "$status" | grep -q '^A src/dangling.txt$'; then ok "pre-staged code remained staged, not embarked"; else ko "pre-staged code state wrong"; fi
|
||||
rm -rf "$R"
|
||||
|
||||
echo "T2-bis — stale-staged memory (A) vs working tree (B): commit must take B"
|
||||
R="$(new_repo)"
|
||||
printf 'STALE STAGED VERSION A\n' >>"$R/.claude/memory/decisions.md"
|
||||
git -C "$R" add .claude/memory/decisions.md # index = A
|
||||
printf 'baseline\nFRESH WORKING B\n' >"$R/.claude/memory/decisions.md" # working = B (no 'A' line)
|
||||
(cd "$R" && "$HELPER" commit "chore(memory): T2bis") >/dev/null 2>&1
|
||||
blob="$(git -C "$R" show HEAD:.claude/memory/decisions.md)"
|
||||
printf ' committed blob:\n'
|
||||
printf '%s\n' "$blob" | sed 's/^/ | /'
|
||||
if printf '%s' "$blob" | grep -q 'FRESH WORKING B' && ! printf '%s' "$blob" | grep -q 'STALE STAGED VERSION A'; then
|
||||
ok "working-tree (B) committed, stale staged (A) discarded"
|
||||
else
|
||||
ko "stale staged version (A) leaked into the commit"
|
||||
fi
|
||||
rm -rf "$R"
|
||||
|
||||
echo "T3 — clean tree → no-op, exit 0, HEAD unchanged"
|
||||
R="$(new_repo)"
|
||||
before="$(git -C "$R" rev-parse HEAD)"
|
||||
(cd "$R" && "$HELPER" commit "chore(memory): T3")
|
||||
rc=$?
|
||||
after="$(git -C "$R" rev-parse HEAD)"
|
||||
printf ' exit=%s HEAD %s\n' "$rc" "$([ "$before" = "$after" ] && echo unchanged || echo CHANGED)"
|
||||
if [ "$rc" -eq 0 ] && [ "$before" = "$after" ]; then ok "no-op, exit 0, HEAD unchanged"; else ko "expected no-op exit 0, got exit $rc"; fi
|
||||
rm -rf "$R"
|
||||
|
||||
echo "T4 — broken git state (mid-merge) → skip, exit 3, no commit"
|
||||
R="$(new_repo)"
|
||||
printf 'CHANGE\n' >>"$R/.claude/memory/decisions.md"
|
||||
: >"$R/.git/MERGE_HEAD"
|
||||
before="$(git -C "$R" rev-parse HEAD)"
|
||||
(cd "$R" && "$HELPER" commit "chore(memory): T4")
|
||||
rc=$?
|
||||
after="$(git -C "$R" rev-parse HEAD)"
|
||||
printf ' exit=%s HEAD %s\n' "$rc" "$([ "$before" = "$after" ] && echo unchanged || echo CHANGED)"
|
||||
if [ "$rc" -eq 3 ] && [ "$before" = "$after" ]; then ok "unsafe state skipped, exit 3, no commit"; else ko "expected skip exit 3, got exit $rc"; fi
|
||||
rm -rf "$R"
|
||||
|
||||
echo "T5 — .claude/tasks/TODO.md is in scope"
|
||||
R="$(new_repo)"
|
||||
printf -- '- [ ] task\n' >"$R/.claude/tasks/TODO.md"
|
||||
(cd "$R" && "$HELPER" commit "chore(memory): T5") >/dev/null 2>&1
|
||||
committed="$(head_files "$R")"
|
||||
printf ' committed: [%s]\n' "$committed"
|
||||
if printf '%s\n' "$committed" | grep -q '^.claude/tasks/TODO.md$'; then ok "TODO.md embarked"; else ko "TODO.md not embarked"; fi
|
||||
rm -rf "$R"
|
||||
|
||||
echo "T6 — stdout contract: commit→hash, no-op→empty, unsafe→empty"
|
||||
R="$(new_repo)"
|
||||
printf 'CHG\n' >>"$R/.claude/memory/decisions.md"
|
||||
out="$( (cd "$R" && "$HELPER" commit "chore(memory): T6") 2>/dev/null )"
|
||||
expected="$(git -C "$R" rev-parse --short HEAD)"
|
||||
printf ' commit stdout=[%s] HEAD=[%s]\n' "$out" "$expected"
|
||||
if [ -n "$out" ] && [ "$out" = "$expected" ]; then ok "commit emits the memory-commit hash on stdout"; else ko "hash mismatch [$out] != [$expected]"; fi
|
||||
out="$( (cd "$R" && "$HELPER" commit "chore(memory): T6-noop") 2>/dev/null )"
|
||||
printf ' no-op stdout=[%s]\n' "$out"
|
||||
if [ -z "$out" ]; then ok "no-op emits nothing on stdout"; else ko "no-op leaked stdout [$out]"; fi
|
||||
printf 'CHG2\n' >>"$R/.claude/memory/decisions.md"
|
||||
: >"$R/.git/MERGE_HEAD"
|
||||
out="$( (cd "$R" && "$HELPER" commit "chore(memory): T6-unsafe") 2>/dev/null )"
|
||||
rc=$?
|
||||
printf ' unsafe stdout=[%s] exit=%s\n' "$out" "$rc"
|
||||
if [ -z "$out" ] && [ "$rc" -eq 3 ]; then ok "unsafe emits nothing on stdout, exit 3"; else ko "unsafe leaked stdout [$out] or rc $rc"; fi
|
||||
rm -rf "$R"
|
||||
|
||||
echo "T7 — double run: at most one commit (real run, not by construction)"
|
||||
R="$(new_repo)"
|
||||
printf 'ONCE\n' >>"$R/.claude/memory/decisions.md"
|
||||
base="$(git -C "$R" rev-list --count HEAD)"
|
||||
h1="$( (cd "$R" && "$HELPER" commit "chore(memory): T7-run1") 2>/dev/null )"
|
||||
after1="$(git -C "$R" rev-list --count HEAD)"
|
||||
h2="$( (cd "$R" && "$HELPER" commit "chore(memory): T7-run2") 2>/dev/null )"
|
||||
after2="$(git -C "$R" rev-list --count HEAD)"
|
||||
printf ' counts base=%s after1=%s after2=%s ; h1=[%s] h2=[%s]\n' "$base" "$after1" "$after2" "$h1" "$h2"
|
||||
if [ "$after1" -eq "$((base + 1))" ] && [ -n "$h1" ]; then ok "run1 created exactly one commit (hash emitted)"; else ko "run1 commit count wrong"; fi
|
||||
if [ "$after2" -eq "$after1" ] && [ -z "$h2" ]; then ok "run2 is a no-op (no 2nd commit, empty stdout)"; else ko "run2 was not a no-op"; fi
|
||||
rm -rf "$R"
|
||||
|
||||
echo
|
||||
printf 'RESULT: %d passed, %d failed\n' "$PASS" "$FAIL"
|
||||
[ "$FAIL" -eq 0 ]
|
||||
@ -161,6 +161,57 @@ Load `$HOME/.claude/agents/analyzer.md`. Check: no regressions, no deviations, n
|
||||
## STEP 10 — CODE REVIEW
|
||||
Invoke `superpowers:requesting-code-review`. Fix all CRITICAL before proceeding.
|
||||
|
||||
## STEP 10b — CAPITALIZE FOUNDING DECISIONS (memory registries)
|
||||
A greenfield's founding architecture decisions are the highest-value BDRs — the
|
||||
"why Astro not Next", the SPA-ban for a public site, the API-versioning policy.
|
||||
Losing them means losing the rationale of the foundations. Capture them BEFORE
|
||||
STEP 11 FINISH so the memory commit lands on the branch FINISH integrates.
|
||||
|
||||
Capture ONLY structuring decisions, not scaffold detail:
|
||||
|
||||
| Capitalize — founding (BDR) | Skip — scaffold detail |
|
||||
|--------------------------------------------------------------|-----------------------------------|
|
||||
| Stack / framework choice + why (Astro vs Next) | directory names, scaffolded files |
|
||||
| Architecture pattern, data-flow shape | dev-tooling / formatter config |
|
||||
| Doctrinal exclusions (public site = no SPA, API /v1 day one) | which template files were copied |
|
||||
| Security defaults adopted; conventions binding future code | anything reversible / obvious |
|
||||
|
||||
Source the candidates from: the PROJECT BRIEF (STEP 1), the DESIGN's resolved
|
||||
decisions (STEP 3), and the validated STACK / CONVENTIONS / EXCEPTIONS (STEP 4
|
||||
gate). These ARE the founding decisions — the user just approved them.
|
||||
|
||||
**No decision → no entry.** A trivial project with no genuine structuring choice
|
||||
capitalizes NOTHING. Do NOT fabricate a BDR to fill the step. Print
|
||||
`CAPITALIZE: no founding decision to log`; the memory commit below then no-ops.
|
||||
|
||||
1. Pre-fill a BDR-XXX entry per founding decision (id, date, title, decision,
|
||||
why, alternatives rejected — from the DESIGN's rejected options).
|
||||
2. Present grouped:
|
||||
```
|
||||
CAPITALIZE — founding decisions proposées
|
||||
[ decisions.md ] BDR-XXX — <decision> — <1-line why>
|
||||
Valider lesquels ? (all / <IDs> / edit / skip)
|
||||
```
|
||||
3. Append approved entries + update the Index. Append a journal line under today.
|
||||
|
||||
**Hash rule — founding decisions carry NO commit hash; use path + date only.**
|
||||
This is by nature, not an omission: a founding decision is made at DESIGN
|
||||
(STEP 3), BEFORE any code, attested by no implementing commit — there is no hash
|
||||
to anchor. Anchoring it to the unrelated scaffold commit would be a FALSE anchor
|
||||
that dilutes what `Reference: commit <hash>` means everywhere else (the commit
|
||||
that IMPLEMENTS the decision, e.g. BDR-033 → 11792cc). This is the SECOND case
|
||||
where hash-anchoring does not apply — the first being a squash-merged PR, whose
|
||||
anchored commit ceases to exist.
|
||||
|
||||
**Language rule**: written entries are ALWAYS in English (CLAUDE.md "Memory
|
||||
registries"). The gate may mirror the user's language; entries must not.
|
||||
|
||||
**Then commit the memory** — follow `$HOME/.claude/lib/capitalize-commit.md`: it
|
||||
surgically commits the approved founding decisions (`.claude/memory` +
|
||||
`.claude/tasks` only, never `git add -A`) as one `chore(memory)` commit, BEFORE
|
||||
STEP 11 FINISH so the memory is integrated with the branch, not stranded. If
|
||||
nothing was capitalized, the helper no-ops — no commit.
|
||||
|
||||
## STEP 11 — FINISH
|
||||
Invoke `superpowers:finishing-a-development-branch`. Tests pass, build clean, no placeholders, initial commit ready.
|
||||
|
||||
|
||||
@ -110,16 +110,8 @@ Load `$HOME/.claude/agents/analyzer.md`. Check: no regressions, no stale code, n
|
||||
## STEP 6 — CODE REVIEW
|
||||
Invoke `superpowers:requesting-code-review`. Fix all CRITICAL before proceeding.
|
||||
|
||||
## STEP 7 — FINISH
|
||||
Invoke `superpowers:finishing-a-development-branch`. Tests pass, build clean, ready to merge.
|
||||
|
||||
## STEP 8 — DOC SYNC
|
||||
Load `$HOME/.claude/agents/doc-syncer.md`.
|
||||
Execute in automatic mode:
|
||||
`auto-mode scope: <list of files modified during this session>`
|
||||
|
||||
## STEP 9 — CAPITALIZE (memory registries)
|
||||
Feature shipped implies at least one design decision worth capturing. Run this before declaring done:
|
||||
## STEP 7 — CAPITALIZE (memory registries)
|
||||
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:
|
||||
- **Design / architecture choices with rationale** → candidate for `decisions.md` (BDR-XXX).
|
||||
@ -146,6 +138,23 @@ Feature shipped implies at least one design decision worth capturing. Run this b
|
||||
|
||||
If nothing substantive to log → print `CAPITALIZE: nothing substantive to log` and skip.
|
||||
|
||||
**Then commit the memory** — follow `$HOME/.claude/lib/capitalize-commit.md`: it
|
||||
surgically commits what capitalize just wrote (`.claude/memory` + `.claude/tasks`
|
||||
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 8 FINISH so the
|
||||
memory is integrated with the branch, not stranded outside the PR.
|
||||
|
||||
## STEP 8 — FINISH
|
||||
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
|
||||
@ -171,7 +180,7 @@ The pipeline must handle these without aborting silently:
|
||||
| STEP 4 — subagent crashes (tool error, not test failure) | Treat as STEP 4b error path, present hypothesis-led gate. |
|
||||
| STEP 4b — option A retried 2× still failing | Force fall-through to B or C. Do not loop a 3rd time. |
|
||||
| STEP 6 — review returns CRITICAL items | Loop back to STEP 4 for those items only. Cap at 2 review-cycle iterations; if still CRITICAL, escalate. |
|
||||
| STEP 9 — `.claude/memory/` missing | Create the registry files from `~/.claude/templates/memory/` first, then proceed. |
|
||||
| STEP 7 — `.claude/memory/` missing | Create the registry files from `~/.claude/templates/memory/` first, then proceed. |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user