chore(memory): prune-memory 2026-09-24 — index backfill (66 rows), 15 headings normalised, 4 statuses flagged, 6 merges (LRN-163..168), 23 entries compressed
D: every body entry now has an Index row; BDR-074..085, LRN-136, EVAL-026/027 were filed under ### and invisible to the engine and to /reconcile. A: BDR-011/015/031/038 index statuses reflect their supersession; LRN-010 dated path update. B: LRN-147+148, 106+113, 105+107, 142+144, 116+117, 131+132 merged into LRN-163..168, sources kept verbatim and marked superseded. C: tier-1 caveman pass on 23 entries under the negation guard (-5% words: most sentences carry a negation and stay verbatim). Fidelity census: file-level token counts never drop; the per-entry flags on BDR-073 and EVAL-025 are attribution artifacts of the ### fix (bodies byte-identical).
This commit is contained in:
@@ -37,6 +37,11 @@ rules:
|
|||||||
| BLK-015 | 2026-07-03 | `gitflow_finish` ignored its `<type> <name>` args → merged the CHECKED-OUT branch not the one named → wrong-branch merge (audit LOT3) | resolved |
|
| BLK-015 | 2026-07-03 | `gitflow_finish` ignored its `<type> <name>` args → merged the CHECKED-OUT branch not the one named → wrong-branch merge (audit LOT3) | resolved |
|
||||||
| BLK-016 | 2026-07-04 | rtk compression PATH-dead 30 days — 6/5070 Bash commands compressed (~460K tokens missed); installer sources cargo env so its own check passes, Claude tool shell never gets ~/.cargo/bin | resolved |
|
| BLK-016 | 2026-07-04 | rtk compression PATH-dead 30 days — 6/5070 Bash commands compressed (~460K tokens missed); installer sources cargo env so its own check passes, Claude tool shell never gets ~/.cargo/bin | resolved |
|
||||||
| BLK-017 | 2026-07-17 | Bing Webmaster API unusable for a multi-client agency: OAuth swamp (localhost redirect refused, rotated single-use refresh tokens race our parallel dispatch), API key = wrong model (client-owned sites) | open/deferred |
|
| BLK-017 | 2026-07-17 | Bing Webmaster API unusable for a multi-client agency: OAuth swamp (localhost redirect refused, rotated single-use refresh tokens race our parallel dispatch), API key = wrong model (client-owned sites) | open/deferred |
|
||||||
|
| BLK-018 | 2026-07-20 | release-executor finish span blocked by permission classifier (human signal invisible to subagent) — 2026-07-… | open |
|
||||||
|
| BLK-019 | 2026-09-01 | notify-attention bell silent, toast OK (VS Code client default) — 2026-09-01 | resolved |
|
||||||
|
| BLK-020 | 2026-09-02 | notify-attention: both channels dead on one VS Code client — 2026-09-02 | resolved |
|
||||||
|
| BLK-021 | 2026-09-22 | Bash tool dead mid-session ("every command exits 1"): /tmp usrquota blown by a dead session's probe HOMEs — 2… | open |
|
||||||
|
| BLK-022 | 2026-09-22 | `hooks/guard-bash.sh` withheld by the safety classifier; executable spec shipped instead — 2026-09-22 | open |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+47
-34
@@ -32,11 +32,11 @@ rules:
|
|||||||
| BDR-008 | 2026-05-04 | Profile system v2: extend to plugins + MCPs + CLIs (web/seo/web-full/backend) | accepted |
|
| BDR-008 | 2026-05-04 | Profile system v2: extend to plugins + MCPs + CLIs (web/seo/web-full/backend) | accepted |
|
||||||
| BDR-009 | 2026-05-05 | Mandate caveman format on .claude/memory/ registries | accepted |
|
| BDR-009 | 2026-05-05 | Mandate caveman format on .claude/memory/ registries | accepted |
|
||||||
| BDR-010 | 2026-05-07 | Gate GEO independently at ≥17/20 in client-handover pipeline | accepted |
|
| BDR-010 | 2026-05-07 | Gate GEO independently at ≥17/20 in client-handover pipeline | accepted |
|
||||||
| BDR-011 | 2026-05-07 | Client handover deliverable: 4-chapter structure + ZenQuality branded HTML/PDF | superseded by BDR-013 |
|
| BDR-011 | 2026-05-07 | Client handover deliverable: 4-chapter structure + ZenQuality branded HTML/PDF | superseded by BDR-013 (6-chapter doc) |
|
||||||
| BDR-012 | 2026-05-07 | client-handover cover: white bg + green accents + PNG logo default | accepted |
|
| BDR-012 | 2026-05-07 | client-handover cover: white bg + green accents + PNG logo default | accepted |
|
||||||
| BDR-013 | 2026-05-11 | client-handover: 6-chapter doc — promote scores §2 + NAP §4 | accepted |
|
| BDR-013 | 2026-05-11 | client-handover: 6-chapter doc — promote scores §2 + NAP §4 | accepted |
|
||||||
| BDR-014 | 2026-05-11 | Personal SKILL.md descriptions: "Use when [triggers]…" pattern + 1024-char spec limit | accepted |
|
| BDR-014 | 2026-05-11 | Personal SKILL.md descriptions: "Use when [triggers]…" pattern + 1024-char spec limit | accepted |
|
||||||
| BDR-015 | 2026-05-12 | Exclude broken gstack symlinks from /darwin-skill scope (external ownership) | accepted |
|
| BDR-015 | 2026-05-12 | Exclude broken gstack symlinks from /darwin-skill scope (external ownership) | accepted · trigger cleared by BDR-043 |
|
||||||
| BDR-016 | 2026-05-15 | doc-syncer: README AUTO+unconditional, DEPLOY.md prod-only + 14-section VPS template | accepted |
|
| BDR-016 | 2026-05-15 | doc-syncer: README AUTO+unconditional, DEPLOY.md prod-only + 14-section VPS template | accepted |
|
||||||
| BDR-017 | 2026-05-18 | `full` profile = web-full + plan + dev superset for /init-project MVP | accepted |
|
| BDR-017 | 2026-05-18 | `full` profile = web-full + plan + dev superset for /init-project MVP | accepted |
|
||||||
| BDR-018 | 2026-06-02 | `profile gstack on/off` verb — toggle gstack keeping active-profile label | accepted |
|
| BDR-018 | 2026-06-02 | `profile gstack on/off` verb — toggle gstack keeping active-profile label | accepted |
|
||||||
@@ -52,14 +52,14 @@ rules:
|
|||||||
| BDR-028 | 2026-06-27 | Hand-curated config install-immutable (auto-revert guard) + de-vendor installer-managed skills | accepted |
|
| BDR-028 | 2026-06-27 | Hand-curated config install-immutable (auto-revert guard) + de-vendor installer-managed skills | accepted |
|
||||||
| BDR-029 | 2026-06-27 | Installer auto-fixes gstack browser on an OS newer than its pinned Playwright supports | accepted |
|
| BDR-029 | 2026-06-27 | Installer auto-fixes gstack browser on an OS newer than its pinned Playwright supports | accepted |
|
||||||
| BDR-030 | 2026-06-27 | gstack skills activated ON-DEMAND per profile, not pre-installed; OFF by default stays | accepted |
|
| BDR-030 | 2026-06-27 | gstack skills activated ON-DEMAND per profile, not pre-installed; OFF by default stays | accepted |
|
||||||
| BDR-031 | 2026-06-27 | global CLAUDE.md lightening = COMPRESSION, not path-scope / externalization | accepted |
|
| BDR-031 | 2026-06-27 | global CLAUDE.md lightening = COMPRESSION, not path-scope / externalization | accepted · 275-line target superseded by BDR-062 |
|
||||||
| BDR-032 | 2026-06-27 | skill `/validate` → `/web-validate` (rename user surface, keep internals) | accepted |
|
| BDR-032 | 2026-06-27 | skill `/validate` → `/web-validate` (rename user surface, keep internals) | accepted |
|
||||||
| BDR-033 | 2026-06-27 | design-gate §4: anim-lib suggestion — suggest-only, non-blocking, stateless 1-line | accepted |
|
| BDR-033 | 2026-06-27 | design-gate §4: anim-lib suggestion — suggest-only, non-blocking, stateless 1-line | 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 |
|
| BDR-036 | 2026-06-27 | Doc-sync coupled invariant — commit docs doc-syncer patches (twin of BDR-034, BUILT not reordered) | accepted |
|
||||||
| BDR-037 | 2026-06-27 | v2 capitalize Stop-hook rejected → wire /capitalize+/close to the include | accepted |
|
| BDR-037 | 2026-06-27 | v2 capitalize Stop-hook rejected → wire /capitalize+/close to the include | accepted |
|
||||||
| BDR-038 | 2026-06-27 | deploy skill: per-project learning runbook, two-moment cold-resume | accepted |
|
| BDR-038 | 2026-06-27 | deploy skill: per-project learning runbook, two-moment cold-resume | superseded by BDR-054 (NEXT.sh, hand-back) |
|
||||||
| BDR-039 | 2026-06-29 | Gitea branch protection = Option-1 owner-pushable, not require-PR | accepted |
|
| BDR-039 | 2026-06-29 | Gitea branch protection = Option-1 owner-pushable, not require-PR | accepted |
|
||||||
| BDR-040 | 2026-06-29 | doc-syncer MINOR-shape oracle: deterministic floor under LLM's MINOR call | accepted |
|
| BDR-040 | 2026-06-29 | doc-syncer MINOR-shape oracle: deterministic floor under LLM's MINOR call | accepted |
|
||||||
| BDR-041 | 2026-06-30 | /reconcile = deterministic declared-vs-real engine + thin gated skill (reconciler, not lister) | accepted |
|
| BDR-041 | 2026-06-30 | /reconcile = deterministic declared-vs-real engine + thin gated skill (reconciler, not lister) | accepted |
|
||||||
@@ -74,6 +74,7 @@ rules:
|
|||||||
| BDR-050 | 2026-07-03 | universal pipeline (contract→dev inline→fresh verify→fresh security, loops bounded 3× in main loop) with per-flow weighting; hotfix failure = revert not loop | accepted |
|
| BDR-050 | 2026-07-03 | universal pipeline (contract→dev inline→fresh verify→fresh security, loops bounded 3× in main loop) with per-flow weighting; hotfix failure = revert not loop | accepted |
|
||||||
| BDR-051 | 2026-07-04 | contract enrich-at-gate: the contract grows ONLY at a human micro-gate ([gated] marker); the verifier judges the ENRICHED contract, not the seed | accepted |
|
| BDR-051 | 2026-07-04 | contract enrich-at-gate: the contract grows ONLY at a human micro-gate ([gated] marker); the verifier judges the ENRICHED contract, not the seed | accepted |
|
||||||
| BDR-052 | 2026-07-05 | /tour auto mode = branch-as-gate: no mid-run approval gates; unmerged chore branch + per-project TOUR.md = deferred human gate; reconcile report-only; loop bounded 3× | accepted |
|
| BDR-052 | 2026-07-05 | /tour auto mode = branch-as-gate: no mid-run approval gates; unmerged chore branch + per-project TOUR.md = deferred human gate; reconcile report-only; loop bounded 3× | accepted |
|
||||||
|
| BDR-053 | 2026-07-06 | ctx7 single surface: keep find-docs skill, kill context7.md rule | accepted |
|
||||||
| BDR-054 | 2026-07-06 | supersede BDR-038 NEXT.sh/hand-back artifacts — shipped impl removed both (52f6678, LRN-102) | accepted |
|
| BDR-054 | 2026-07-06 | supersede BDR-038 NEXT.sh/hand-back artifacts — shipped impl removed both (52f6678, LRN-102) | accepted |
|
||||||
| BDR-055 | 2026-07-07 | job5: delete memory-commit/doc-commit `pending` verbs — v2 hook rejected (BDR-037), J4-17 closed MOOT | accepted |
|
| BDR-055 | 2026-07-07 | job5: delete memory-commit/doc-commit `pending` verbs — v2 hook rejected (BDR-037), J4-17 closed MOOT | accepted |
|
||||||
| BDR-056 | 2026-07-07 | job6: deps policy = latest gated by integration, not KEEP-PINNED by default | accepted |
|
| BDR-056 | 2026-07-07 | job6: deps policy = latest gated by integration, not KEEP-PINNED by default | accepted |
|
||||||
@@ -87,11 +88,22 @@ rules:
|
|||||||
| BDR-064 | 2026-07-14 | global memory split: repo file → CLAUDE.global.md (deployed name unchanged), CLAUDE.md freed for project scope; consumer/maintainer wording rule | accepted |
|
| BDR-064 | 2026-07-14 | global memory split: repo file → CLAUDE.global.md (deployed name unchanged), CLAUDE.md freed for project scope; consumer/maintainer wording rule | accepted |
|
||||||
| BDR-065 | 2026-07-14 | transient planning artifacts (superpowers spec/plan): committed during run, deleted post-merge; git history = archive; codified in project CLAUDE.md | accepted |
|
| BDR-065 | 2026-07-14 | transient planning artifacts (superpowers spec/plan): committed during run, deleted post-merge; git history = archive; codified in project CLAUDE.md | accepted |
|
||||||
| BDR-066 | 2026-07-15 | Model routing: reflection inline (session big model) + sonnet-pinned executors + blocking gate | accepted |
|
| BDR-066 | 2026-07-15 | Model routing: reflection inline (session big model) + sonnet-pinned executors + blocking gate | accepted |
|
||||||
|
| BDR-067 | 2026-07-16 | first public release: versioning reset to v1.0.0 (override "never restart at v1.0.0") — 2026-07-16 | SHIPPED |
|
||||||
|
| BDR-068 | 2026-07-16 | /capitalize + /close auto-persist memory (finish→develop + push); scoped LRN-069 exception — 2026-07-16 | implemented on feature/close-auto-persist |
|
||||||
|
| BDR-069 | 2026-07-16 | permissions deny: keep broad `.env.*` glob, keep `.env.example` name (option A) — 2026-07-16 | implemented on chore/fix-inert-write-deny-rules |
|
||||||
| BDR-070 | 2026-07-17 | claude-seo: cherry-pick scripts into our tree, never install; /seo stays sole entry | accepted |
|
| BDR-070 | 2026-07-17 | claude-seo: cherry-pick scripts into our tree, never install; /seo stays sole entry | accepted |
|
||||||
| BDR-071 | 2026-07-17 | No viable free backlink source → Off-page axis stays brand-mentions-only (FINAL, not placeholder) | accepted |
|
| BDR-071 | 2026-07-17 | No viable free backlink source → Off-page axis stays brand-mentions-only (FINAL, not placeholder) | accepted |
|
||||||
| BDR-072 | 2026-07-17 | SPA: honest refuse (On-page N/A, not zero), no headless browser (R2 over R1) | accepted |
|
| BDR-072 | 2026-07-17 | SPA: honest refuse (On-page N/A, not zero), no headless browser (R2 over R1) | accepted |
|
||||||
| BDR-073 | 2026-07-17 | Scoring: LLM judges findings+severity, engine does the arithmetic (deterministic /20) | accepted |
|
| BDR-073 | 2026-07-17 | Scoring: LLM judges findings+severity, engine does the arithmetic (deterministic /20) | accepted |
|
||||||
|
| BDR-074 | 2026-07-17 | Remove config-protection edit-block guardrail | accepted |
|
||||||
|
| BDR-075 | 2026-07-17 | Framework-wide 3-way adversarial plan-challenge phase | accepted |
|
||||||
|
| BDR-076 | 2026-07-19 | Dispatched judgment agents pinned OPUS; session model = orchestration + inline reflection ONLY | accepted |
|
||||||
|
| BDR-077 | 2026-07-19 | Model-tiering v2: 4-tier explicit routing, mode-based splits, no-inherit dispatches | accepted |
|
||||||
|
| BDR-078 | 2026-07-20 | ctx7 coverage: central fast-libs list + once-per-session reminder hook; every code path covered | accepted |
|
||||||
|
| BDR-079 | 2026-07-20 | profile `set` symmetric on managed externals + MCPs | accepted |
|
||||||
| BDR-080 | 2026-07-21 | Bug routing inverted: /bugfix primary, /investigate explicit-only | accepted |
|
| BDR-080 | 2026-07-21 | Bug routing inverted: /bugfix primary, /investigate explicit-only | accepted |
|
||||||
|
| BDR-081 | 2026-07-30 | Config recalibrated for Claude 5 family (Opus 5 dispatch tier) | accepted |
|
||||||
|
| BDR-082 | 2026-08-02 | seo/geo analyzers de-prescribed for Opus 5 (C1) | accepted |
|
||||||
| BDR-083 | 2026-08-24 | Contract gates: deterministic floor (GATE 0) under the fresh verifier | accepted |
|
| BDR-083 | 2026-08-24 | Contract gates: deterministic floor (GATE 0) under the fresh verifier | accepted |
|
||||||
| BDR-084 | 2026-08-24 | /tour multi-project: parallel runners (LRN-083 derogation, bounded), runner inherits session model | accepted |
|
| BDR-084 | 2026-08-24 | /tour multi-project: parallel runners (LRN-083 derogation, bounded), runner inherits session model | accepted |
|
||||||
| BDR-085 | 2026-08-25 | User permanent rules: writing-style always-on in rules/, web build+security path-scoped | accepted |
|
| BDR-085 | 2026-08-25 | User permanent rules: writing-style always-on in rules/, web build+security path-scoped | accepted |
|
||||||
@@ -103,6 +115,11 @@ rules:
|
|||||||
| BDR-091 | 2026-09-16 | Ask, don't guess: open-choice sweep (3 classes) at plan step + mid-run CLASS channel supersede "one question upfront" | accepted |
|
| BDR-091 | 2026-09-16 | Ask, don't guess: open-choice sweep (3 classes) at plan step + mid-run CLASS channel supersede "one question upfront" | accepted |
|
||||||
| BDR-092 | 2026-09-16 | docker + node framed by the classifier via autoMode.allow + soft_deny; ask entries retired | accepted |
|
| BDR-092 | 2026-09-16 | docker + node framed by the classifier via autoMode.allow + soft_deny; ask entries retired | accepted |
|
||||||
| BDR-093 | 2026-09-22 | 21st.dev: magic MCP → CLI + skill pack; staged install past the ~/.claude/skills symlink; CLI = gate's required-manual | accepted |
|
| BDR-093 | 2026-09-22 | 21st.dev: magic MCP → CLI + skill pack; staged install past the ~/.claude/skills symlink; CLI = gate's required-manual | accepted |
|
||||||
|
| BDR-094 | 2026-09-22 | impeccable: global-scope install through the repo symlinks, pin + @latest fallback, output-read failure check | accepted |
|
||||||
|
| BDR-095 | 2026-09-22 | Data-loss guardrails: static deny for transfer/destructive tools, push every commit, brief ≠ user authority | accepted |
|
||||||
|
| BDR-096 | 2026-09-24 | Branch deletion guard: lib-only delete after verified merge, main/develop undeletable at the ref layer | accepted |
|
||||||
|
| BDR-097 | 2026-09-24 | graphify from 200 tracked code files: the banner informs, the user decides | accepted |
|
||||||
|
| BDR-098 | 2026-09-24 | CLAUDE.global.md density pass 352 → 270: compression only, three name-obvious routing lines dropped | accepted |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -502,17 +519,16 @@ rules:
|
|||||||
---
|
---
|
||||||
|
|
||||||
## BDR-026 — Secret source-of-truth outside the repo (`~/.claude/.env`) reached via a `repo/.env` symlink
|
## BDR-026 — Secret source-of-truth outside the repo (`~/.claude/.env`) reached via a `repo/.env` symlink
|
||||||
|
|
||||||
- **Date**: 2026-06-21
|
- **Date**: 2026-06-21
|
||||||
- **Status**: accepted
|
- **Status**: accepted
|
||||||
- **Decision**: real secret lives in `~/.claude/.env` (outside the git tree); `repo/.env` is a symlink → it. `source "$REPO/.env"` follows the symlink transparently → ZERO change to any read path (`toggle-external.sh` `load_env`, `install-plugins.sh` check, gate). `link.sh` `link_env()` creates the symlink defensively: links only when `repo/.env` is absent or already the right link; a residual REAL `repo/.env` is left untouched with a migrate hint — never clobbered, so the secret can't be destroyed. Idempotent. `.gitignore` hardened to `.env` + `.env.*` + `!.env.example`. Messages point at `~/.claude/.env` (the canonical edit location).
|
- **Decision**: real secret lives in `~/.claude/.env` (outside git tree); `repo/.env` = symlink → it. `source "$REPO/.env"` follows symlink → ZERO change to any read path (`toggle-external.sh` `load_env`, `install-plugins.sh` check, gate). `link.sh` `link_env()` defensive: links only when `repo/.env` absent or already the right link; a residual REAL `repo/.env` is left untouched with a migrate hint — never clobbered, so the secret can't be destroyed. Idempotent. `.gitignore` hardened to `.env` + `.env.*` + `!.env.example`. Messages point at `~/.claude/.env` (canonical edit location).
|
||||||
- **Why**: secret never enters the git tree — not as content (it's a link) nor by accident (gitignored). Even a stray `git add .` can't stage the real key. Repo stays usable: the symlink is visible/editable from the repo. Read paths follow the link → no script logic changed.
|
- **Why**: secret never enters the git tree — not as content (it's a link) nor by accident (gitignored). Even a stray `git add .` can't stage the real key. Repo stays usable: symlink visible/editable from repo. Read paths follow the link → no script logic changed.
|
||||||
- **Alternatives rejected**:
|
- **Alternatives rejected**:
|
||||||
- Secret in `repo/.env`, gitignored (status quo) — one `git add -f` or a `.gitignore` slip leaks it; the secret physically sits in the tree.
|
- Secret in `repo/.env`, gitignored (status quo) — one `git add -f` or `.gitignore` slip leaks it; secret sits in tree.
|
||||||
- Scripts read `~/.claude/.env` directly — makes the symlink redundant but rewrites every read path and loses repo-local visibility.
|
- Scripts read `~/.claude/.env` directly — symlink redundant, rewrites every read path, loses repo-local visibility.
|
||||||
- **Reference**: `link.sh` `link_env()`, `.gitignore`, `lib/toggle-external.sh`, `install-plugins.sh`, `.env.example`, commits 131d0bc / f9cc866. Linked to [[BDR-025]] (magic's `MAGIC_API_KEY`, consumed by the gate's required-but-manual class).
|
- **Reference**: `link.sh` `link_env()`, `.gitignore`, `lib/toggle-external.sh`, `install-plugins.sh`, `.env.example`, commits 131d0bc / f9cc866. Linked to [[BDR-025]] (magic's `MAGIC_API_KEY`, consumed by the gate's required-but-manual class).
|
||||||
- **Update 2026-07-02 (incident — copies of secrets)**: `claude mcp add --env` MATERIALIZES the key into `~/.claude.json` (`mcpServers.magic.env`) — a 2nd live copy OUTSIDE the `~/.claude/.env` canonical and outside the repo deny rules' reach. An audit query printed it into a session transcript → key rotated (21st.dev). Rule: secrets have COPIES (tool configs, transcripts, caches) — protect/audit the copies, not just the canonical; when inspecting MCP config, filter env fields (`jq 'del(.. | .env?)'`). Same audit: `~/.claude/.env` hardened 0664→0600.
|
- **Update 2026-07-02 (incident — copies of secrets)**: `claude mcp add --env` MATERIALIZES the key into `~/.claude.json` (`mcpServers.magic.env`) — 2nd live copy OUTSIDE `~/.claude/.env` canonical and outside repo deny rules' reach. Audit query printed it into a session transcript → key rotated (21st.dev). Rule: secrets have COPIES (tool configs, transcripts, caches) — protect/audit the copies, not just the canonical; when inspecting MCP config, filter env fields (`jq 'del(.. | .env?)'`). Same audit: `~/.claude/.env` hardened 0664→0600.
|
||||||
- **Update 2026-07-07 (job7 — backup vector closed)**: the `~/.claude.json` copy from the 2026-07-02 incident kept re-leaking into `~/.claude/backups/.claude.json.backup.*` (native Claude Code auto-backup, ring-buffer of 5, plaintext each time) — every backup taken while the live file held the value was a fresh copy, so scrubbing existing backups alone would have recurred forever. Closed at the source instead ([[BDR-057]]): `~/.claude.json`'s `mcpServers.magic.env.API_KEY` rewritten to `"${MAGIC_API_KEY}"` (Claude Code `${VAR}` expansion, confirmed supported at user scope), `lib/toggle-external.sh` writes the reference form for future `enable magic` runs, var reaches `claude` only via a scoped `~/.bashrc` wrapper (never the ambient shell). New backups taken after the fix carry the reference, not the value — confirmed empirically (2 of 5 rotating backups mid-fix still had the old value; scrubbed once, not expected to recur). MAGIC_API_KEY itself still needs rotation (this closes the storage vector, not the already-exposed value).
|
- **Update 2026-07-07 (job7 — backup vector closed)**: `~/.claude.json` copy from 2026-07-02 kept re-leaking into `~/.claude/backups/.claude.json.backup.*` (native auto-backup, ring-buffer of 5, plaintext) — every backup taken while live file held the value = fresh copy; scrubbing backups alone would recur forever. Closed at the source instead ([[BDR-057]]): `~/.claude.json`'s `mcpServers.magic.env.API_KEY` rewritten to `"${MAGIC_API_KEY}"` (Claude Code `${VAR}` expansion, confirmed supported at user scope), `lib/toggle-external.sh` writes the reference form for future `enable magic` runs, var reaches `claude` only via a scoped `~/.bashrc` wrapper (never the ambient shell). New backups taken after the fix carry the reference, not the value — confirmed empirically (2 of 5 rotating backups mid-fix still had the old value; scrubbed once, not expected to recur). MAGIC_API_KEY itself still needs rotation (this closes the storage vector, not the already-exposed value).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -850,11 +866,10 @@ rules:
|
|||||||
- **Reference**: lib/verify-secure-loop.md + wired feater/bugfixer/hotfixer + lib/tests/loops-light.test.sh (27 locks) — feature/verify-loops `0f0162d`. Behavioral GREEN (feat fixture): CONFORME→BLOCK(1) SQLi→fix→re-verify CONFORME→re-scan PASS, order invariant held. Builds on [[BDR-048]] [[BDR-049]]. Conditions [[LRN-083]] [[LRN-095]].
|
- **Reference**: lib/verify-secure-loop.md + wired feater/bugfixer/hotfixer + lib/tests/loops-light.test.sh (27 locks) — feature/verify-loops `0f0162d`. Behavioral GREEN (feat fixture): CONFORME→BLOCK(1) SQLi→fix→re-verify CONFORME→re-scan PASS, order invariant held. Builds on [[BDR-048]] [[BDR-049]]. Conditions [[LRN-083]] [[LRN-095]].
|
||||||
|
|
||||||
## BDR-051 — Contract enrich-at-gate: the contract grows only at a human micro-gate
|
## BDR-051 — Contract enrich-at-gate: the contract grows only at a human micro-gate
|
||||||
|
|
||||||
- **Date**: 2026-07-04
|
- **Date**: 2026-07-04
|
||||||
- **Decision**: the CONTRACT's REQUEST is immutable, but ACCEPTANCE CRITERIA + FILE SCOPE may GROW — exclusively at a human gate, each added entry tagged `[gated <date>]`. In the heavy flows (ship-feature STEP 3, init-project GATE #1) the approved DESIGN appends design-derived criteria to the contract; the fresh verifier then judges the diff against the ENRICHED contract, never the seed. Same mechanism as the out-of-scope micro-gate ([[BDR-049]]) — a dev never enriches; only the human validating a gate does.
|
- **Decision**: CONTRACT's REQUEST immutable; ACCEPTANCE CRITERIA + FILE SCOPE may GROW — exclusively at a human gate, each added entry tagged `[gated <date>]`. Heavy flows (ship-feature STEP 3, init-project GATE #1): approved DESIGN appends design-derived criteria to the contract; the fresh verifier then judges the diff against the ENRICHED contract, never the seed. Same mechanism as the out-of-scope micro-gate ([[BDR-049]]) — a dev never enriches; only the human validating a gate does.
|
||||||
- **Rationale**: the raw request underspecifies (a one-line "add validation" hides the schema-rejection requirement the design surfaces). If the verifier judged only the seed, every design decision would be unverified. Gating the growth keeps the contract honest (no silent scope creep) AND complete (design criteria are verified). The only flow where the contract is mutable mid-run — bounded to gate moments.
|
- **Rationale**: raw request underspecifies (one-line "add validation" hides the schema-rejection requirement the design surfaces); verifier judging only the seed → every design decision unverified. Gating the growth keeps the contract honest (no silent scope creep) AND complete (design criteria are verified). Only flow where the contract is mutable mid-run — bounded to gate moments.
|
||||||
- **Alternatives rejected**: freeze the contract at creation (design criteria unverified — the seed is too thin); let the dev enrich (the [[BDR-049]] failure mode — dev justifies everything, scope constrains nothing); a second contract per design (loses the single-reference property).
|
- **Alternatives rejected**: freeze contract at creation (design criteria unverified — seed too thin); let dev enrich ([[BDR-049]] failure mode — dev justifies everything, scope constrains nothing); second contract per design (loses single-reference property).
|
||||||
- **Reference**: ship-feature STEP 0e+3, init-project STEP 1+4, feature/verify-loops `1c69de2`. Behavioral GREEN: a `[gated 2026-07-04]` design criterion (reject unknown config keys) was read + judged NOT-MET by a fresh verifier across 3 rounds (dogfood). Builds on [[BDR-049]] [[BDR-050]].
|
- **Reference**: ship-feature STEP 0e+3, init-project STEP 1+4, feature/verify-loops `1c69de2`. Behavioral GREEN: a `[gated 2026-07-04]` design criterion (reject unknown config keys) was read + judged NOT-MET by a fresh verifier across 3 rounds (dogfood). Builds on [[BDR-049]] [[BDR-050]].
|
||||||
|
|
||||||
## BDR-052 — /tour auto mode: branch-as-gate, declared state read-only
|
## BDR-052 — /tour auto mode: branch-as-gate, declared state read-only
|
||||||
@@ -906,14 +921,13 @@ rules:
|
|||||||
---
|
---
|
||||||
|
|
||||||
## BDR-057 — job7: secrets by reference not by value; redact at capture, not just at rest
|
## BDR-057 — job7: secrets by reference not by value; redact at capture, not just at rest
|
||||||
|
|
||||||
- **Date**: 2026-07-07
|
- **Date**: 2026-07-07
|
||||||
- **Status**: accepted
|
- **Status**: accepted
|
||||||
- **Decision**: two-part posture from the job7 triage (`.audit/job7/ALL-REDACTED.json`, 5+ leak classes across `~/.claude` and repos). (1) Wherever the consuming tool supports it, wire secrets BY REFERENCE (`${VAR}` expansion), not by value — closed the concrete case: `lib/toggle-external.sh`'s `claude mcp add magic --env API_KEY="$MAGIC_API_KEY"` materialized the key as plaintext into `~/.claude.json` (a 2nd copy outside the `~/.claude/.env` canonical); fixed to `--env 'API_KEY=${MAGIC_API_KEY}'`, with the var reaching `claude` only via a scoped `~/.bashrc` wrapper function (subshell + exec — never the ambient shell). (2) Redact AT THE CAPTURE POINT, not just after the fact: `hooks/rtk-rewrite.sh` now appends a redaction pipe to bare `printenv`/`env` dumps before they can reach stdout/the transcript (the GITEA leak's actual vector), instead of relying solely on scrubbing artifacts after the fact.
|
- **Decision**: two-part posture from the job7 triage (`.audit/job7/ALL-REDACTED.json`, 5+ leak classes across `~/.claude` and repos). (1) Wherever the consuming tool supports it, wire secrets BY REFERENCE (`${VAR}` expansion), not by value — closed the concrete case: `lib/toggle-external.sh`'s `claude mcp add magic --env API_KEY="$MAGIC_API_KEY"` materialized the key as plaintext into `~/.claude.json` (a 2nd copy outside the `~/.claude/.env` canonical); fixed to `--env 'API_KEY=${MAGIC_API_KEY}'`, with the var reaching `claude` only via a scoped `~/.bashrc` wrapper function (subshell + exec — never the ambient shell). (2) Redact AT THE CAPTURE POINT, not just after the fact: `hooks/rtk-rewrite.sh` now appends a redaction pipe to bare `printenv`/`env` dumps before they can reach stdout/the transcript (the GITEA leak's actual vector), instead of relying solely on scrubbing artifacts after the fact.
|
||||||
- **Why**: the job6 incident ([[LRN-107]]) and the GITEA leak both trace back to a secret VALUE existing somewhere it didn't strictly need to (a config field, a raw env dump) rather than a reference/redacted form. Fixing storage-at-rest (scrub backups) treats the symptom and must be redone every time a new copy appears (5 rotating `.claude.json.backup.*` files, 2 of 5 still had it live mid-job7 despite the canonical fix already applied) — fixing the SOURCE (don't materialize the value; redact before the dump leaves the process) is the only version that doesn't need repeating.
|
- **Why**: the job6 incident ([[LRN-107]]) and the GITEA leak both trace back to a secret VALUE existing somewhere it didn't strictly need to (a config field, a raw env dump) rather than a reference/redacted form. Fixing storage-at-rest (scrub backups) treats the symptom and must be redone every time a new copy appears (5 rotating `.claude.json.backup.*` files, 2 of 5 still had it live mid-job7 despite the canonical fix already applied) — fixing the SOURCE (don't materialize the value; redact before the dump leaves the process) is the only version that doesn't need repeating.
|
||||||
- **Alternatives rejected**: scrub-only (chosen as the fallback in job7's own instructions if reference-by-value support were absent) — verified Claude Code DOES support `${VAR}` expansion in `mcpServers` config (user + project scope, `env`/`command`/`args`/`url`/`headers` fields — code.claude.com/docs/en/mcp.md), so the reference form was available and preferred; global `export MAGIC_API_KEY` in `~/.bashrc` — works but broadens the secret's exposure to every subprocess of every shell session, defeating the point of the redaction hook (rejected by user in favor of the scoped wrapper).
|
- **Alternatives rejected**: scrub-only (job7's own fallback if reference support were absent) — verified Claude Code DOES support `${VAR}` expansion in `mcpServers` config (user + project scope, `env`/`command`/`args`/`url`/`headers` fields — code.claude.com/docs/en/mcp.md) → reference form available, preferred; global `export MAGIC_API_KEY` in `~/.bashrc` — works but exposes the secret to every subprocess of every shell, defeats the redaction hook (user rejected, scoped wrapper kept).
|
||||||
- **Reference**: `lib/toggle-external.sh:191-192`, `hooks/rtk-rewrite.sh`, `README.md` "Adding an MCP server that needs a secret", `.gitleaks.toml`, `lib/gitflow.sh` `_gitflow_emit_pre_commit`, `Makefile` `scan-secrets`; commits `b9300c3`/`3340c7d`/`17bdd08`/`5d5b386`. Linked to [[BDR-026]] (canonical vault this closes a leak vector against), [[LRN-108]] (the `claude mcp add --env` trap).
|
- **Reference**: `lib/toggle-external.sh:191-192`, `hooks/rtk-rewrite.sh`, `README.md` "Adding an MCP server that needs a secret", `.gitleaks.toml`, `lib/gitflow.sh` `_gitflow_emit_pre_commit`, `Makefile` `scan-secrets`; commits `b9300c3`/`3340c7d`/`17bdd08`/`5d5b386`. Linked to [[BDR-026]] (canonical vault this closes a leak vector against), [[LRN-108]] (the `claude mcp add --env` trap).
|
||||||
- **Caveat — contradicts job6's own finding same day**: job6's journal (2026-07-07, earlier same day) states "`${VAR}` env-expansion confirmed unsupported at `~/.claude.json` user scope after 2 rounds of sourced doc lookup". job7's doc lookup (claude-code-guide agent, same day) found it IS supported at user scope, citing code.claude.com/docs/en/mcp.md + a v2.1.161 changelog entry. Not reconciled — could be a version bump between the two lookups, or job6's research being wrong. The `${MAGIC_API_KEY}` rewrite is live (`claude mcp list` recognizes the reference and reports the var missing, which requires the CLI to have at least PARSED the `${...}` syntax) but full end-to-end confirmation (restart terminal + Claude Code, verify magic MCP reconnects) is still a residual the user needs to do — see BDR-057's own commit message.
|
- **Caveat — contradicts job6's own finding same day**: job6 journal (2026-07-07, earlier same day): "`${VAR}` env-expansion confirmed unsupported at `~/.claude.json` user scope after 2 rounds of sourced doc lookup". job7 lookup (claude-code-guide agent, same day): IS supported at user scope, citing code.claude.com/docs/en/mcp.md + a v2.1.161 changelog entry. Not reconciled — could be a version bump between the two lookups, or job6's research being wrong. `${MAGIC_API_KEY}` rewrite live (`claude mcp list` recognizes the reference, reports the var missing → CLI PARSED the `${...}` syntax); end-to-end confirmation (restart terminal + Claude Code, magic MCP reconnects) still a user residual — see BDR-057's own commit message.
|
||||||
|
|
||||||
## BDR-058 — job8: darwin-skill reinstall full pinned tree, detached HEAD
|
## BDR-058 — job8: darwin-skill reinstall full pinned tree, detached HEAD
|
||||||
|
|
||||||
@@ -954,12 +968,11 @@ rules:
|
|||||||
- **Reference**: `agents/seo-analyzer.md` STEP 12, `agents/geo-analyzer.md` STEP 13, `skills/seo/SKILL.md` STEP 1.5, `skills/geo/SKILL.md`, `agents/validator-analyzer.md` (reference contract), `.audit/job9-report.md` §6 option (b); commits `a5a7b54`/`6df42e4`/`c498b93`/`70fb3b4`. Linked to [[BDR-060]] (nesting floor), [[LRN-112]] (nesting mechanics).
|
- **Reference**: `agents/seo-analyzer.md` STEP 12, `agents/geo-analyzer.md` STEP 13, `skills/seo/SKILL.md` STEP 1.5, `skills/geo/SKILL.md`, `agents/validator-analyzer.md` (reference contract), `.audit/job9-report.md` §6 option (b); commits `a5a7b54`/`6df42e4`/`c498b93`/`70fb3b4`. Linked to [[BDR-060]] (nesting floor), [[LRN-112]] (nesting mechanics).
|
||||||
|
|
||||||
## BDR-062 — supersede BDR-031's 275-line CLAUDE.md target: 305 is the assumed reality
|
## BDR-062 — supersede BDR-031's 275-line CLAUDE.md target: 305 is the assumed reality
|
||||||
|
|
||||||
- **Date**: 2026-07-08
|
- **Date**: 2026-07-08
|
||||||
- **Status**: accepted (supersedes the 275-line density TARGET of [[BDR-031]] only; BDR-031's core principle — lightening = compression, not path-scope/externalization — stands unchanged)
|
- **Status**: accepted (supersedes the 275-line density TARGET of [[BDR-031]] only; BDR-031's core principle — lightening = compression, not path-scope/externalization — stands unchanged)
|
||||||
- **Decision**: The global CLAUDE.md sits at 305 lines and stays there. job1's density pass took it 319→305 and no later job re-inflated it; the extraction BDR-031 called for is done. Reaching the old 275 target (or even the 280 guard threshold) now costs clarity more than it saves tokens. The `hooks/session-start.sh` guard threshold is realigned 280→320: still catches genuine regression (real bloat past 320) but stops firing a permanent "density pass requis" warning on an assumed-final 305.
|
- **Decision**: global CLAUDE.md sits at 305 lines, stays there. job1's density pass took it 319→305 and no later job re-inflated it; the extraction BDR-031 called for is done. Old 275 target (or the 280 guard) now costs clarity more than it saves tokens. The `hooks/session-start.sh` guard threshold is realigned 280→320: still catches genuine regression (real bloat past 320) but stops firing a permanent "density pass requis" warning on an assumed-final 305.
|
||||||
- **Why**: the review (`.audit/review-release-1.0.0.md` A6) found the guard had warned every session since job1 without the target ever being met — a self-inflicted permanent warning, not an actionable signal. A gate that never goes green trains you to ignore it. Realign to reality; keep a 15-line margin so real regressions still surface.
|
- **Why**: the review (`.audit/review-release-1.0.0.md` A6) found the guard had warned every session since job1 without the target ever being met — a self-inflicted permanent warning, not an actionable signal. A gate that never goes green trains you to ignore it. Realign to reality; 15-line margin keeps real regressions visible.
|
||||||
- **Alternatives rejected**: (a) finish the compression 305→≤275 — the remaining lines are load-bearing constraints, not filler; further squeeze loses clarity for a marginal token gain on a solo repo. (b) leave the guard at 280 and accept the permanent warning — a permanently-red non-blocking gate is noise. (c) rewrite BDR-031 — registries are append-only; supersede the target, keep the principle.
|
- **Alternatives rejected**: (a) finish the compression 305→≤275 — the remaining lines are load-bearing constraints, not filler; further squeeze loses clarity for a marginal token gain on a solo repo. (b) leave the guard at 280 and accept the permanent warning — a permanently-red non-blocking gate is noise. (c) rewrite BDR-031 — registries append-only; supersede the target, keep the principle.
|
||||||
- **Reference**: `hooks/session-start.sh:202-211`; supersedes the 275 target in [[BDR-031]] (principle kept). Review remediation A6, 2026-07-08.
|
- **Reference**: `hooks/session-start.sh:202-211`; supersedes the 275 target in [[BDR-031]] (principle kept). Review remediation A6, 2026-07-08.
|
||||||
|
|
||||||
## BDR-063 — GSC multi-account: OAuth2 installed-app flow + label-keyed token store
|
## BDR-063 — GSC multi-account: OAuth2 installed-app flow + label-keyed token store
|
||||||
@@ -1060,38 +1073,38 @@ rules:
|
|||||||
- **Makes computable (was prose)**: "N/A is not a zero" (R2 on-page, I1 off-page) → axis excluded + weights renormalised, verified all-20 with 2 N/A → global 20.0. Prevalence: affected/sampled shift severity ONE step (≥50% escalate, single de-escalate).
|
- **Makes computable (was prose)**: "N/A is not a zero" (R2 on-page, I1 off-page) → axis excluded + weights renormalised, verified all-20 with 2 N/A → global 20.0. Prevalence: affected/sampled shift severity ONE step (≥50% escalate, single de-escalate).
|
||||||
- **Files**: lib/seo-data/score.py (4818c61).
|
- **Files**: lib/seo-data/score.py (4818c61).
|
||||||
|
|
||||||
### BDR-074 — Remove config-protection edit-block guardrail [accepted] (2026-07-17)
|
## BDR-074 — Remove config-protection edit-block guardrail [accepted] (2026-07-17)
|
||||||
Deleted hooks/config-protection.sh + its settings.json PreToolUse registration + lib/tests/config-protection.test.sh. Hook blocked model Edit/Write on quality-gate files (settings.json, gitflow.sh, .githooks, doctor.sh, hooks, lib/tests, lint) via one-shot .claude/.config-edit-ok sentinel. Removed per user req — friction editing own config > guardrail value; user = human operator. Residual: gitflow pre-commit guard + Gitea branch protection still block direct code commits main/develop; only edit-time block gone. Alts rejected: warn-only (exit0+log), targeted relaxation. Supersedes any prior config-protection decision.
|
Deleted hooks/config-protection.sh + its settings.json PreToolUse registration + lib/tests/config-protection.test.sh. Hook blocked model Edit/Write on quality-gate files (settings.json, gitflow.sh, .githooks, doctor.sh, hooks, lib/tests, lint) via one-shot .claude/.config-edit-ok sentinel. Removed per user req — friction editing own config > guardrail value; user = human operator. Residual: gitflow pre-commit guard + Gitea branch protection still block direct code commits main/develop; only edit-time block gone. Alts rejected: warn-only (exit0+log), targeted relaxation. Supersedes any prior config-protection decision.
|
||||||
|
|
||||||
### BDR-075 — Framework-wide 3-way adversarial plan-challenge phase [accepted] (2026-07-17)
|
## BDR-075 — Framework-wide 3-way adversarial plan-challenge phase [accepted] (2026-07-17)
|
||||||
After a plan/reflection elaborated + before execution, 3 fresh blind sub-agents (correctness/robustness/simplicity) attack it; main loop RE-THINKS every aspect a BLOCKER lands (named change or [deferred]) + re-challenges once if plan materially changed. Reusable lib/challenge-plan.md + new agents/plan-challenger.md (read-only, big-model per [[BDR-066]] — audit judgment, NOT sonnet). Fail-safe (never fail open: mute→retry→escalate), severity-driven (any single-lens BLOCKER=must-address, NOT consensus — lenses orthogonal), advisory into existing human gate. KIND tunes lenses: build-plan/proposals/fix-bundle. Wired 11 orchestrators: ship-feature/init-project/feat/bugfix + onboard/audit-delta/code-clean + seo/geo/harden/web-validate. Excluded (no real plan): hotfix/tour/analyze/client-handover/release-candidate/spec. Audit found 0 repo-owned plan-challengers pre-existing (only vendored gstack autoplan, sequential+unwired). See [[EVAL-026]].
|
After a plan/reflection elaborated + before execution, 3 fresh blind sub-agents (correctness/robustness/simplicity) attack it; main loop RE-THINKS every aspect a BLOCKER lands (named change or [deferred]) + re-challenges once if plan materially changed. Reusable lib/challenge-plan.md + new agents/plan-challenger.md (read-only, big-model per [[BDR-066]] — audit judgment, NOT sonnet). Fail-safe (never fail open: mute→retry→escalate), severity-driven (any single-lens BLOCKER=must-address, NOT consensus — lenses orthogonal), advisory into existing human gate. KIND tunes lenses: build-plan/proposals/fix-bundle. Wired 11 orchestrators: ship-feature/init-project/feat/bugfix + onboard/audit-delta/code-clean + seo/geo/harden/web-validate. Excluded (no real plan): hotfix/tour/analyze/client-handover/release-candidate/spec. Audit found 0 repo-owned plan-challengers pre-existing (only vendored gstack autoplan, sequential+unwired). See [[EVAL-026]].
|
||||||
|
|
||||||
### BDR-075 amendment (2026-07-18) — hotfix INCLUDED via logic-only guard
|
### BDR-075 amendment (2026-07-18) — hotfix INCLUDED via logic-only guard
|
||||||
Supersedes the "Excluded: hotfix" clause of [[BDR-075]]. hotfix now wired (STEP 1.8, Option B): GUARD skips purely cosmetic fixes (CSS/copy/typo), fires the 3-lens challenge ONLY when the fix touches control flow/behaviour (off-by-one, wrong operator, behaviour-changing config, execution-altering import); a BLOCKER → escalate to /bugfix (its STEP 3b runs the full phase). 12 orchestrators wired. Still excluded (no forward plan): tour/analyze/client-handover/release-candidate/spec. Per user (Option B). Branch feature/hotfix-challenge-guard, unmerged.
|
Supersedes the "Excluded: hotfix" clause of [[BDR-075]]. hotfix now wired (STEP 1.8, Option B): GUARD skips purely cosmetic fixes (CSS/copy/typo), fires the 3-lens challenge ONLY when the fix touches control flow/behaviour (off-by-one, wrong operator, behaviour-changing config, execution-altering import); a BLOCKER → escalate to /bugfix (its STEP 3b runs the full phase). 12 orchestrators wired. Still excluded (no forward plan): tour/analyze/client-handover/release-candidate/spec. Per user (Option B). Branch feature/hotfix-challenge-guard, unmerged.
|
||||||
|
|
||||||
### BDR-076 — Dispatched judgment agents pinned OPUS; session model = orchestration + inline reflection ONLY [accepted] (2026-07-19)
|
## BDR-076 — Dispatched judgment agents pinned OPUS; session model = orchestration + inline reflection ONLY [accepted] (2026-07-19)
|
||||||
Reverses the BDR-066 rejected alternative "opus pins on audit agents (session-independent)". Context changed: session default now Fable (Mythos tier, /model 2026-07-19) — inherit meant every dispatched audit/challenge burned Fable quota, exactly the waste BDR-066 killed for executors. New rule: Fable does ONLY main-loop orchestration + reflection (brainstorm, plan, contract, synthesis, gates); EVERY dispatched subagent pinned. Pinned `model: opus` (big tier, session-independent; NEVER sonnet — silent audit downgrade, the thing old §F5 guarded): analyzer, plan-challenger, seo-analyzer, geo-analyzer, validator-analyzer + onboard's 6 general-purpose audit dispatches (`model="opus"`) + tour Phase B. NOT pinned (justified deviation from approved "7 agents"): interviewer + client-handover-writer — inline-load only, never dispatched → frontmatter pin inert + misleading (BDR-066 wave-4 precedent: its inert opus pin was dropped); they ARE the main loop = Fable per the rule. Explore built-in stays inherit (wave-3 decision conserved: no owned prompt, search feeds inline reflection). Local session pin `opus-4-8[1m]` dropped from `.claude/settings.local.json` (gitignored) — Fable default from settings.json now applies in this repo too. model-gate.md unchanged (still guards inline reflection, Fable-or-Opus = big). Census: model-routing.test.sh §3 flip + §11 (61 pass), loops-light 35 pass, full `make test` green. User directives via gate: "Opus partout" + "Supprimer le pin". Branch feature/opus-pin-audit-agents, unmerged.
|
Reverses the BDR-066 rejected alternative "opus pins on audit agents (session-independent)". Context changed: session default now Fable (Mythos tier, /model 2026-07-19) — inherit meant every dispatched audit/challenge burned Fable quota, exactly the waste BDR-066 killed for executors. New rule: Fable does ONLY main-loop orchestration + reflection (brainstorm, plan, contract, synthesis, gates); EVERY dispatched subagent pinned. Pinned `model: opus` (big tier, session-independent; NEVER sonnet — silent audit downgrade, the thing old §F5 guarded): analyzer, plan-challenger, seo-analyzer, geo-analyzer, validator-analyzer + onboard's 6 general-purpose audit dispatches (`model="opus"`) + tour Phase B. NOT pinned (justified deviation from approved "7 agents"): interviewer + client-handover-writer — inline-load only, never dispatched → frontmatter pin inert + misleading (BDR-066 wave-4 precedent: its inert opus pin was dropped); they ARE the main loop = Fable per the rule. Explore built-in stays inherit (wave-3 decision conserved: no owned prompt, search feeds inline reflection). Local session pin `opus-4-8[1m]` dropped from `.claude/settings.local.json` (gitignored) — Fable default from settings.json now applies in this repo too. model-gate.md unchanged (still guards inline reflection, Fable-or-Opus = big). Census: model-routing.test.sh §3 flip + §11 (61 pass), loops-light 35 pass, full `make test` green. User directives via gate: "Opus partout" + "Supprimer le pin". Branch feature/opus-pin-audit-agents, unmerged.
|
||||||
|
|
||||||
### BDR-077 — Model-tiering v2: 4-tier explicit routing, mode-based splits, no-inherit dispatches [accepted] (2026-07-19)
|
## BDR-077 — Model-tiering v2: 4-tier explicit routing, mode-based splits, no-inherit dispatches [accepted] (2026-07-19)
|
||||||
Supersedes BDR-076 scope + amends BDR-066. Doctrine: session model (Fable) = main-loop reflection/orchestration/planning/logic ONLY; main-loop retention criteria = interactive | conversation-context access | orchestration decision | dispatch overhead > step cost. NOTHING dispatched inherits: typed agents = frontmatter pin, built-ins = `model=` at every call site (`fable` for skill-runner reflection children, else complexity tier). Spike+smoke proven: `model:"fable"` resolves claude-fable-5 (enum-validated, loud fail, no silent fallback); call-site override BEATS a typed pin (sonnet-pinned verifier ran haiku). Fail-safe pin rule: mixed-mode agents keep the HIGH tier as pin, overrides go DOWN — forgotten override over-tiers (cost), never downgrades judgment. Mode-based splits (commit-changer precedent generalized; file splits rejected): doc-syncer audit(opus)/patch(sonnet) — ALSO fixed a latent defect: /doc dispatched an agent whose STEP 8 interactive gate could never fire; gates hoisted to a DISPATCHER PROTOCOL section; handover-doc-writer synthesize(opus)/render(sonnet) via run-scoped `.audit/handover-draft-<RUNID>.md` + DRAFT COMPLETE sentinel; seo/geo collect(sonnet)/judge(OPUS PIN)/template(sonnet) via `.audit/*-signals-<RUNID>.md` + COLLECTION COMPLETE + fail-closed judge + dispatcher ERROR contract (mute/ERROR judge NEVER carried into templating; retry once, escalate). File split only for a genuinely new role: plugin-probe (sonnet, facts-only) + plugin-advisor repinned opus reasoner (fail-closed on missing PROBE REPORT) + lib/plugin-gate.md (checkpoint + apply gate, doc-commit ×N include pattern). Inline→dispatch conversions: scaffolder, onboarder, doc-commit steps ×5 flows — their sonnet pins were INERT since creation, now live; CHANGE SUMMARY crosses the doc dispatch into doc-commit (LRN-126 wire). Tier moves: validator-analyzer opus→sonnet (deterministic runner); commit-changer propose=opus/apply=pin; ship-feature/init-project code-review dispatches = opus explicit (WAS an inherit leak); client-handover-writer's 7 skill-runners = model:"fable". Every wave shipped an IN-WAVE planted-input smoke as its merge gate — all PASSED disk-verified. Census §12-18 (125 pass; one vacuous line-wrapped lock self-caught = LRN-093 live). 6 waves, branches feature/model-tiering-w1..w6, merged on user standing signal. Plan: challenged 3 blind lenses + 1 confirmation (1 BLOCKER closed by spike, 8 MAJORs + 8 MINORs closed by named changes, 0 deferred). Refs: `.claude/tasks/plans/2026-07-19-model-tiering-v2-{analysis,plan}.md`.
|
Supersedes BDR-076 scope + amends BDR-066. Doctrine: session model (Fable) = main-loop reflection/orchestration/planning/logic ONLY; main-loop retention criteria = interactive | conversation-context access | orchestration decision | dispatch overhead > step cost. NOTHING dispatched inherits: typed agents = frontmatter pin, built-ins = `model=` at every call site (`fable` for skill-runner reflection children, else complexity tier). Spike+smoke proven: `model:"fable"` resolves claude-fable-5 (enum-validated, loud fail, no silent fallback); call-site override BEATS a typed pin (sonnet-pinned verifier ran haiku). Fail-safe pin rule: mixed-mode agents keep the HIGH tier as pin, overrides go DOWN — forgotten override over-tiers (cost), never downgrades judgment. Mode-based splits (commit-changer precedent generalized; file splits rejected): doc-syncer audit(opus)/patch(sonnet) — ALSO fixed a latent defect: /doc dispatched an agent whose STEP 8 interactive gate could never fire; gates hoisted to a DISPATCHER PROTOCOL section; handover-doc-writer synthesize(opus)/render(sonnet) via run-scoped `.audit/handover-draft-<RUNID>.md` + DRAFT COMPLETE sentinel; seo/geo collect(sonnet)/judge(OPUS PIN)/template(sonnet) via `.audit/*-signals-<RUNID>.md` + COLLECTION COMPLETE + fail-closed judge + dispatcher ERROR contract (mute/ERROR judge NEVER carried into templating; retry once, escalate). File split only for a genuinely new role: plugin-probe (sonnet, facts-only) + plugin-advisor repinned opus reasoner (fail-closed on missing PROBE REPORT) + lib/plugin-gate.md (checkpoint + apply gate, doc-commit ×N include pattern). Inline→dispatch conversions: scaffolder, onboarder, doc-commit steps ×5 flows — their sonnet pins were INERT since creation, now live; CHANGE SUMMARY crosses the doc dispatch into doc-commit (LRN-126 wire). Tier moves: validator-analyzer opus→sonnet (deterministic runner); commit-changer propose=opus/apply=pin; ship-feature/init-project code-review dispatches = opus explicit (WAS an inherit leak); client-handover-writer's 7 skill-runners = model:"fable". Every wave shipped an IN-WAVE planted-input smoke as its merge gate — all PASSED disk-verified. Census §12-18 (125 pass; one vacuous line-wrapped lock self-caught = LRN-093 live). 6 waves, branches feature/model-tiering-w1..w6, merged on user standing signal. Plan: challenged 3 blind lenses + 1 confirmation (1 BLOCKER closed by spike, 8 MAJORs + 8 MINORs closed by named changes, 0 deferred). Refs: `.claude/tasks/plans/2026-07-19-model-tiering-v2-{analysis,plan}.md`.
|
||||||
|
|
||||||
### BDR-078 — ctx7 coverage: central fast-libs list + once-per-session reminder hook; every code path covered [accepted] (2026-07-20)
|
## BDR-078 — ctx7 coverage: central fast-libs list + once-per-session reminder hook; every code path covered [accepted] (2026-07-20)
|
||||||
Refines BDR-053 (single surface). Audit 2026-07-20: coverage PARTIAL — find-docs fired on user doc-questions only; ship-feature 0c / init-project 5c pre-fetched; /feat //bugfix executors + ad-hoc coding NEVER consulted ctx7; fast-libs list hardcoded 3× (drift risk). 4 closures shipped: (a) find-docs description += BEFORE-writing-code trigger (fast-moving lib, even without doc question, unless fresh cache) + cache-first rule in body (tee fetched docs to .ctx7-cache/); (b) feater+bugfixer briefs += fast-lib docs rule — read fresh `.ctx7-cache/<lib>*.md`, else `npx ctx7@latest` fetch max 2 topics, else `ctx7 cache miss: <lib>` in NOTES + proceed (executors lack Skill tool → Bash path); (c) hooks/ctx7-reminder.sh UserPromptSubmit — ONE fire/session (sentinel on session_id), only when project manifest carries fast-libs; reports cache state; skips <task-notification> turns; always exit 0; (d) lib/fast-libs.sh = SINGLE SOURCE (detect / cache-status verbs, JS package.json anchored full-key match + Python requirements/pyproject, 7-day freshness, LC_ALL=C sort locale-independent) consumed by hook + 3 pipeline skills + 2 briefs. 2nd session surface DELIBERATE, not a BDR-053 reversal: 053 killed a 490-tok ALWAYS-ON rule duplicate; hook costs ~0 quiet, 1 line once when fast-libs present. Alternatives rejected: PreToolUse Edit/Write gate (fires per-edit = noise); description-only fix (probabilistic, executors unreachable). Tests: lib/tests/fast-libs.test.sh 11 checks (anchored/near-miss/py/none, cache fresh/stale/missing, hook fire/sentinel/quiet×2); shellcheck + full make test green. Branch feature/ctx7-coverage, unmerged (human gate).
|
Refines BDR-053 (single surface). Audit 2026-07-20: coverage PARTIAL — find-docs fired on user doc-questions only; ship-feature 0c / init-project 5c pre-fetched; /feat //bugfix executors + ad-hoc coding NEVER consulted ctx7; fast-libs list hardcoded 3× (drift risk). 4 closures shipped: (a) find-docs description += BEFORE-writing-code trigger (fast-moving lib, even without doc question, unless fresh cache) + cache-first rule in body (tee fetched docs to .ctx7-cache/); (b) feater+bugfixer briefs += fast-lib docs rule — read fresh `.ctx7-cache/<lib>*.md`, else `npx ctx7@latest` fetch max 2 topics, else `ctx7 cache miss: <lib>` in NOTES + proceed (executors lack Skill tool → Bash path); (c) hooks/ctx7-reminder.sh UserPromptSubmit — ONE fire/session (sentinel on session_id), only when project manifest carries fast-libs; reports cache state; skips <task-notification> turns; always exit 0; (d) lib/fast-libs.sh = SINGLE SOURCE (detect / cache-status verbs, JS package.json anchored full-key match + Python requirements/pyproject, 7-day freshness, LC_ALL=C sort locale-independent) consumed by hook + 3 pipeline skills + 2 briefs. 2nd session surface DELIBERATE, not a BDR-053 reversal: 053 killed a 490-tok ALWAYS-ON rule duplicate; hook costs ~0 quiet, 1 line once when fast-libs present. Alternatives rejected: PreToolUse Edit/Write gate (fires per-edit = noise); description-only fix (probabilistic, executors unreachable). Tests: lib/tests/fast-libs.test.sh 11 checks (anchored/near-miss/py/none, cache fresh/stale/missing, hook fire/sentinel/quiet×2); shellcheck + full make test green. Branch feature/ctx7-coverage, unmerged (human gate).
|
||||||
Amendment (same session): skills/find-docs = machine-owned dist (gitignored, ctx7 regenerates on fresh clone) → durable copy of closure (a) lives in install-plugins.sh STEP ctx7 (idempotent grep-guarded python patch, fixture-verified); live SKILL.md carries the same edit uncommitted by design.
|
Amendment (same session): skills/find-docs = machine-owned dist (gitignored, ctx7 regenerates on fresh clone) → durable copy of closure (a) lives in install-plugins.sh STEP ctx7 (idempotent grep-guarded python patch, fixture-verified); live SKILL.md carries the same edit uncommitted by design.
|
||||||
|
|
||||||
### BDR-079 — profile `set` symmetric on managed externals + MCPs [accepted] (2026-07-20)
|
## BDR-079 — profile `set` symmetric on managed externals + MCPs [accepted] (2026-07-20)
|
||||||
Audit (user ask "profile toggles externals both ways?"): ASYMMETRIC. Enable side OK — gstack on-demand from submodule when pack off (shared `skills-disabled/gstack__*` convention with toggle-external.sh, interoperable), externals restored from parked, magic delegated to toggle-external. Disable side MISSING: `cmd_set` trimmed only gstack + MANAGED_PLUGINS → `set backend` left emil/frontend-design/design-motion/impeccable active + magic registered; SKILL.md claimed both-ways toggle (true only at enable). Shipped: (1) `MANAGED_EXTERNALS` (emil-design-eng, frontend-design, design-motion-principles, impeccable = exact union of profile `external` usage; darwin-skill excluded — not task-type-driven) + `MANAGED_MCPS` (magic) allowlists, same doctrine as MANAGED_PLUGINS; (2) cmd_set refactored to 4 trim helpers (`disable_{gstack,plugins,externals,mcps}_not_in`) — symmetric, nothing outside allowlists ever auto-touched; (3) enable_skill external += from-source fallback (`ln -sf skills-external/<name>`, mirrors toggle-external) — closes the "missing symlink" warn; (4) stale usage() NOTE ("NOT toggled automatically") + SKILL.md fixed. Hermetic test profile-set-managed.test.sh 16 checks: fixture repo (both *_REPO_OVERRIDE), fake `claude` shim on PATH logging calls + flat-file MCP registry — gstack on-demand, external from-source, park/restore round-trip, magic add/remove calls, non-managed untouched. shellcheck + make test green. Branch feature/profile-managed-externals, unmerged (human gate).
|
Audit (user ask "profile toggles externals both ways?"): ASYMMETRIC. Enable side OK — gstack on-demand from submodule when pack off (shared `skills-disabled/gstack__*` convention with toggle-external.sh, interoperable), externals restored from parked, magic delegated to toggle-external. Disable side MISSING: `cmd_set` trimmed only gstack + MANAGED_PLUGINS → `set backend` left emil/frontend-design/design-motion/impeccable active + magic registered; SKILL.md claimed both-ways toggle (true only at enable). Shipped: (1) `MANAGED_EXTERNALS` (emil-design-eng, frontend-design, design-motion-principles, impeccable = exact union of profile `external` usage; darwin-skill excluded — not task-type-driven) + `MANAGED_MCPS` (magic) allowlists, same doctrine as MANAGED_PLUGINS; (2) cmd_set refactored to 4 trim helpers (`disable_{gstack,plugins,externals,mcps}_not_in`) — symmetric, nothing outside allowlists ever auto-touched; (3) enable_skill external += from-source fallback (`ln -sf skills-external/<name>`, mirrors toggle-external) — closes the "missing symlink" warn; (4) stale usage() NOTE ("NOT toggled automatically") + SKILL.md fixed. Hermetic test profile-set-managed.test.sh 16 checks: fixture repo (both *_REPO_OVERRIDE), fake `claude` shim on PATH logging calls + flat-file MCP registry — gstack on-demand, external from-source, park/restore round-trip, magic add/remove calls, non-managed untouched. shellcheck + make test green. Branch feature/profile-managed-externals, unmerged (human gate).
|
||||||
|
|
||||||
### BDR-080 — bug routing inverted: /bugfix primary, /investigate explicit-only [accepted] (2026-07-21)
|
## BDR-080 — bug routing inverted: /bugfix primary, /investigate explicit-only [accepted] (2026-07-21)
|
||||||
Old routing "Bug → investigate (bugfix if gstack off)" + gstack ON by default → every bug took path bypassing own quality pipeline (gitflow aiguillage, contract, fresh verifier + security gates, doc-sync, `.claude/memory` registries) — /bugfix relegated to near-never fallback. Skill comparison: same core doctrine (root-cause iron law, hypothesis loop, regression test, 3-strike stop, >5-files alert) but incompatible wrappers — investigate monolithic (same context investigates+fixes+verifies, ~1075-line SKILL.md w/ gstack preamble/telemetry/onboarding, capitalizes to `~/.gstack` learnings.jsonl framework never reads at session start); bugfix orchestrator (reflection inline, sonnet bugfixer executor, fresh gates — BDR-066, LRN-083). Composition rejected: skills superpose in context, don't compose — invoking investigate inside bugfix = two full workflows, two completion protocols, two memory systems loaded at once. Decision: CLAUDE.global.md routing line inverted — bugfix primary; investigate ONLY on explicit ask for gstack ecosystem (cross-project learnings, /freeze scope lock, long no-commit investigation). Alternatives rejected: keep investigate primary (bypasses framework), embed investigate inside bugfix (context conflict, dual memory). Known drift noted at write time: Index table rows BDR-074..079 missing (pre-existing, /prune-memory scope).
|
Old routing "Bug → investigate (bugfix if gstack off)" + gstack ON by default → every bug took path bypassing own quality pipeline (gitflow aiguillage, contract, fresh verifier + security gates, doc-sync, `.claude/memory` registries) — /bugfix relegated to near-never fallback. Skill comparison: same core doctrine (root-cause iron law, hypothesis loop, regression test, 3-strike stop, >5-files alert) but incompatible wrappers — investigate monolithic (same context investigates+fixes+verifies, ~1075-line SKILL.md w/ gstack preamble/telemetry/onboarding, capitalizes to `~/.gstack` learnings.jsonl framework never reads at session start); bugfix orchestrator (reflection inline, sonnet bugfixer executor, fresh gates — BDR-066, LRN-083). Composition rejected: skills superpose in context, don't compose — invoking investigate inside bugfix = two full workflows, two completion protocols, two memory systems loaded at once. Decision: CLAUDE.global.md routing line inverted — bugfix primary; investigate ONLY on explicit ask for gstack ecosystem (cross-project learnings, /freeze scope lock, long no-commit investigation). Alternatives rejected: keep investigate primary (bypasses framework), embed investigate inside bugfix (context conflict, dual memory). Known drift noted at write time: Index table rows BDR-074..079 missing (pre-existing, /prune-memory scope).
|
||||||
|
|
||||||
### BDR-081 — Config recalibrated for Claude 5 family (Opus 5 dispatch tier) [accepted] (2026-07-30)
|
## BDR-081 — Config recalibrated for Claude 5 family (Opus 5 dispatch tier) [accepted] (2026-07-30)
|
||||||
Opus 5 (released 2026-07-24) now backs every `model: opus` pin (BDR-076/077) + any `/model opus` session. Research (official migration guide + web + registries): Opus 5 OVER-delegates (inverts LRN-030 Opus 4.8 trait that CLAUDE.global.md:43-47 compensated), self-verifies (explicit verify instructions → over-verification, "removing them reduces wasted tokens with no loss in quality"), literal following (conservative-reporting clauses depress recall; MUST/CRITICAL over-triggers), scope expansion = named regression, written deliverables +30-40%. Claude Code injects Opus-5-only anti-delegation prompt sections (heron_brook + subagent_steer_delegation, issue #80988, server-gated, no opt-out) — prose caps would triple-stack. Shipped: delegation block → model-neutral WHEN-guidance + explicit gates carve-out (verifier/security/challenge still dispatch as written); "staff engineer" self-check bar dropped; finish-whole-task clause folded into Deviations (gone-WRONG→STOP still wins); deliverable-length rule; design hook `\bux\b` dropped (`\bui\b` KEPT — 0 FP, 1 logged TP, lock-tested); plan-challenger grounded-doubt→[MINOR] in-place reword (grammar byte-identical). Plan challenged by 3 blind Opus 5 plan-challengers: correctness CONCERNS(4) / robustness FATAL(5, BLOCKER: all surfaces symlink-deployed LIVE — gates fire post-deployment) / simplicity CONCERNS(4); every fix adopted as prescribed (scratch-validation before live hook write, minimal diffs, ux-only, MINOR-routing). Alternatives rejected: leave as-is (nudge actively counter-productive); hard spawn caps in prose (harness injects one); confidence axis on challenger grammar (consumer unwired); dropping \bui\b (no evidence). NOT touched: verify-secure-loop + fresh gates (harness architecture BDR-049/050, ≠ model self-check prose); Security/Architecture sections (BDR-021); settings effortLevel xhigh (user pref — Opus 5 carry-over trap → LRN-139); superpowers plugin wording (external upstream). Plan+synthesis: .claude/tasks/plans/2026-07-30-opus5-config-tuning-1238.md. Branch feature/opus5-config-tuning, unmerged (human gate).
|
Opus 5 (released 2026-07-24) now backs every `model: opus` pin (BDR-076/077) + any `/model opus` session. Research (official migration guide + web + registries): Opus 5 OVER-delegates (inverts LRN-030 Opus 4.8 trait that CLAUDE.global.md:43-47 compensated), self-verifies (explicit verify instructions → over-verification, "removing them reduces wasted tokens with no loss in quality"), literal following (conservative-reporting clauses depress recall; MUST/CRITICAL over-triggers), scope expansion = named regression, written deliverables +30-40%. Claude Code injects Opus-5-only anti-delegation prompt sections (heron_brook + subagent_steer_delegation, issue #80988, server-gated, no opt-out) — prose caps would triple-stack. Shipped: delegation block → model-neutral WHEN-guidance + explicit gates carve-out (verifier/security/challenge still dispatch as written); "staff engineer" self-check bar dropped; finish-whole-task clause folded into Deviations (gone-WRONG→STOP still wins); deliverable-length rule; design hook `\bux\b` dropped (`\bui\b` KEPT — 0 FP, 1 logged TP, lock-tested); plan-challenger grounded-doubt→[MINOR] in-place reword (grammar byte-identical). Plan challenged by 3 blind Opus 5 plan-challengers: correctness CONCERNS(4) / robustness FATAL(5, BLOCKER: all surfaces symlink-deployed LIVE — gates fire post-deployment) / simplicity CONCERNS(4); every fix adopted as prescribed (scratch-validation before live hook write, minimal diffs, ux-only, MINOR-routing). Alternatives rejected: leave as-is (nudge actively counter-productive); hard spawn caps in prose (harness injects one); confidence axis on challenger grammar (consumer unwired); dropping \bui\b (no evidence). NOT touched: verify-secure-loop + fresh gates (harness architecture BDR-049/050, ≠ model self-check prose); Security/Architecture sections (BDR-021); settings effortLevel xhigh (user pref — Opus 5 carry-over trap → LRN-139); superpowers plugin wording (external upstream). Plan+synthesis: .claude/tasks/plans/2026-07-30-opus5-config-tuning-1238.md. Branch feature/opus5-config-tuning, unmerged (human gate).
|
||||||
|
|
||||||
### BDR-082 — seo/geo analyzers de-prescribed for Opus 5 (C1) [accepted] (2026-08-02)
|
## BDR-082 — seo/geo analyzers de-prescribed for Opus 5 (C1) [accepted] (2026-08-02)
|
||||||
BDR-081 N5 follow-on, user-directed apparatus (plan+3-lens challenge+census+dogfood). Method: audience×mode-range invariant — dedup ONLY verbatim same-audience (spec rule / bundle-item payload / phase-local caveat) same-mode-range repeats; cross-mode + agent↔dispatcher twins stay (standalone paths need them). Census-FIRST: lib/tests/seo-geo-contract.test.sh 71 locks (verdict grammar, sentinels, ALL STEP headers incl. interiors, item fields, score labels, envelope keys), flip-proven 7 mutations→7 FAILs, committed BEFORE reword. Shipped: self-output verification removed (":970 run twice"→conditional integrity guard; ":1217"→single-shot-scoped), 2 pre-BDR-061 vestigials fixed, caps softened (P0-rule/MANDATORY/ALWAYS→plain content rules), 2 essays compressed, checklist :1309→routing map rows verbatim (challenger caught it = routing table, NOT self-check), true same-range dups only (seo Handoff+landing-page blocks; geo ZERO — all claimed pairs distinct on inspection). FROZEN: guard-first url-guard orderings, :550 denominator-before-sampling (ordering IS the honesty mechanism), R2/NAP/COVERAGE/citation invariants, external-freshness checks (world drift ≠ self-verification). Deltas: seo 1528→1503 l ("P0 rule" 2→0, ALWAYS 1→0, MUST 5→4, NEVER 9→9 = class-B bans kept); geo 1106→1107 (MANDATORY 1→0, MUST 4→3). Plan challenged correctness FATAL / robustness FATAL(3 BLOCKER) / simplicity CONCERNS + confirmation FATAL(9) — every BLOCKER closed by named change (§5bis record). Dogfood before/after on frozen zenquality copy: judge-replay on frozen signals (zero collect variance) + templates + fresh collects + e2e judge + 42/42 assert battery BOTH sets + blind reader "interchangeable; all deltas = presentation variance both directions OR after MORE spec-conformant". Alternatives rejected: keyword dedup (challengers proved audience/range-blind — most annex "twins" were distinct obligations), FULL/aggressive dogfood (billing gate killed nested CLI; left as user option), banner/shape locks (LLM-convention layers wobble — lock strings only). Evidence: .audit/dogfood-baseline/ (18 artifacts + DOGFOOD-VERDICT.md), plan .claude/tasks/plans/2026-07-30-seo-geo-deprescription-1402.md. Branch feature/seo-geo-deprescription, UNMERGED (human gate).
|
BDR-081 N5 follow-on, user-directed apparatus (plan+3-lens challenge+census+dogfood). Method: audience×mode-range invariant — dedup ONLY verbatim same-audience (spec rule / bundle-item payload / phase-local caveat) same-mode-range repeats; cross-mode + agent↔dispatcher twins stay (standalone paths need them). Census-FIRST: lib/tests/seo-geo-contract.test.sh 71 locks (verdict grammar, sentinels, ALL STEP headers incl. interiors, item fields, score labels, envelope keys), flip-proven 7 mutations→7 FAILs, committed BEFORE reword. Shipped: self-output verification removed (":970 run twice"→conditional integrity guard; ":1217"→single-shot-scoped), 2 pre-BDR-061 vestigials fixed, caps softened (P0-rule/MANDATORY/ALWAYS→plain content rules), 2 essays compressed, checklist :1309→routing map rows verbatim (challenger caught it = routing table, NOT self-check), true same-range dups only (seo Handoff+landing-page blocks; geo ZERO — all claimed pairs distinct on inspection). FROZEN: guard-first url-guard orderings, :550 denominator-before-sampling (ordering IS the honesty mechanism), R2/NAP/COVERAGE/citation invariants, external-freshness checks (world drift ≠ self-verification). Deltas: seo 1528→1503 l ("P0 rule" 2→0, ALWAYS 1→0, MUST 5→4, NEVER 9→9 = class-B bans kept); geo 1106→1107 (MANDATORY 1→0, MUST 4→3). Plan challenged correctness FATAL / robustness FATAL(3 BLOCKER) / simplicity CONCERNS + confirmation FATAL(9) — every BLOCKER closed by named change (§5bis record). Dogfood before/after on frozen zenquality copy: judge-replay on frozen signals (zero collect variance) + templates + fresh collects + e2e judge + 42/42 assert battery BOTH sets + blind reader "interchangeable; all deltas = presentation variance both directions OR after MORE spec-conformant". Alternatives rejected: keyword dedup (challengers proved audience/range-blind — most annex "twins" were distinct obligations), FULL/aggressive dogfood (billing gate killed nested CLI; left as user option), banner/shape locks (LLM-convention layers wobble — lock strings only). Evidence: .audit/dogfood-baseline/ (18 artifacts + DOGFOOD-VERDICT.md), plan .claude/tasks/plans/2026-07-30-seo-geo-deprescription-1402.md. Branch feature/seo-geo-deprescription, UNMERGED (human gate).
|
||||||
|
|
||||||
### BDR-083 — contract gates: deterministic floor (GATE 0) under the verifier [accepted] (2026-08-24)
|
## BDR-083 — contract gates: deterministic floor (GATE 0) under the verifier [accepted] (2026-08-24)
|
||||||
User asked what to take from `unlazy` skill (Leonxlnx/unlazy 2.1.0, MIT). Verdict on its verification ARCHITECTURE: teaches nothing we lack — contract + fresh blind verifier + bounded loops + order invariant already shipped (BDR-049/050/066, LRN-083). Real gap found elsewhere: between executor and GATE 1, NO deterministic floor. GATE 1 = LLM dispatch; verifier's mandatory `PROOF:` line = a line the verifier WRITES — nothing structurally stops it being produced without executing anything (LRN-048 demands a pass prove it looked; the proof is self-reported prose). Decision: import unlazy's gate ledger INTO the existing contract, never alongside it. Palier 2, user-chosen over doctrine-only / defer.
|
User asked what to take from `unlazy` skill (Leonxlnx/unlazy 2.1.0, MIT). Verdict on its verification ARCHITECTURE: teaches nothing we lack — contract + fresh blind verifier + bounded loops + order invariant already shipped (BDR-049/050/066, LRN-083). Real gap found elsewhere: between executor and GATE 1, NO deterministic floor. GATE 1 = LLM dispatch; verifier's mandatory `PROOF:` line = a line the verifier WRITES — nothing structurally stops it being produced without executing anything (LRN-048 demands a pass prove it looked; the proof is self-reported prose). Decision: import unlazy's gate ledger INTO the existing contract, never alongside it. Palier 2, user-chosen over doctrine-only / defer.
|
||||||
TAKEN: criterion carries an oracle (indented `CHECK:` cmd + `EXPECT:` success-only marker + `EVIDENCE:` slot); fail-closed = exit 0 AND marker (a nonzero process never passes because its error text carries the token); evidence persisted INTO the contract → the fresh verifier reads fact, not the executor's report; `ABANDON: <id> <non-blank reason>` = impossible criterion never deleted, blocks CONFORME, routes to human gate (new verdict token `ABANDONED(n)` — distinct routing from ECARTS ⇒ distinct token, not a sub-line to re-derive); 4 gate-authoring rules (observe the named artifact / success-only marker / positive control before any absence check / recompute supplied numbers, never copy one into EXPECT); 4-pass executor discipline (feater full; bugfixer narrowed to fix+test under "keep the fix minimal", pass 3 = negative control proving the regression test fails without the fix).
|
TAKEN: criterion carries an oracle (indented `CHECK:` cmd + `EXPECT:` success-only marker + `EVIDENCE:` slot); fail-closed = exit 0 AND marker (a nonzero process never passes because its error text carries the token); evidence persisted INTO the contract → the fresh verifier reads fact, not the executor's report; `ABANDON: <id> <non-blank reason>` = impossible criterion never deleted, blocks CONFORME, routes to human gate (new verdict token `ABANDONED(n)` — distinct routing from ECARTS ⇒ distinct token, not a sub-line to re-derive); 4 gate-authoring rules (observe the named artifact / success-only marker / positive control before any absence check / recompute supplied numbers, never copy one into EXPECT); 4-pass executor discipline (feater full; bugfixer narrowed to fix+test under "keep the fix minimal", pass 3 = negative control proving the regression test fails without the fix).
|
||||||
REFUSED + why: Stop hook `decision:"block"` — contradicts "STOP + human escalation", "gone WRONG → STOP re-plan", "merge only on explicit human signal"; a hook FORCING continuation is the inverse of our gates; its 6-block release either traps the session or gives up; each block = an agent continuation = real tokens. Approval store `~/.unlazy/approved` (binds ledger+cmd+CWD+shell+timeout+platform+full PATH) — exists to execute ledgers INHERITED from untrusted repos; our contracts are authored by our own orchestrator in our own repo ⇒ biggest chunk of their 28k checker closes zero threat here. `.unlazy/<scope>/` tree (PLAN+GATES+gates/+status.log+session+hook-state+locks/) — a 4th bookkeeping tree beside .claude/tasks/{contracts,plans} + memory/ + audits/. `tree N` Depth-Tree effort arithmetic — disowned by unlazy's OWN research/validation-protocol.md (v1 six-run figures unreproducible), while the repo DESCRIPTION still advertises the retracted claim. Node checker (28k .mjs + 54k .mjs tests) — lib stack is 100% bash, Health Stack = `shellcheck *.sh hooks/*.sh lib/*.sh` would cover none of it. `OWNS:` ownership leases — deferred (Palier 3): our parallel dispatches (seo/geo, 3 plan-challengers) are read-only, the write-collision problem does not exist yet.
|
REFUSED + why: Stop hook `decision:"block"` — contradicts "STOP + human escalation", "gone WRONG → STOP re-plan", "merge only on explicit human signal"; a hook FORCING continuation is the inverse of our gates; its 6-block release either traps the session or gives up; each block = an agent continuation = real tokens. Approval store `~/.unlazy/approved` (binds ledger+cmd+CWD+shell+timeout+platform+full PATH) — exists to execute ledgers INHERITED from untrusted repos; our contracts are authored by our own orchestrator in our own repo ⇒ biggest chunk of their 28k checker closes zero threat here. `.unlazy/<scope>/` tree (PLAN+GATES+gates/+status.log+session+hook-state+locks/) — a 4th bookkeeping tree beside .claude/tasks/{contracts,plans} + memory/ + audits/. `tree N` Depth-Tree effort arithmetic — disowned by unlazy's OWN research/validation-protocol.md (v1 six-run figures unreproducible), while the repo DESCRIPTION still advertises the retracted claim. Node checker (28k .mjs + 54k .mjs tests) — lib stack is 100% bash, Health Stack = `shellcheck *.sh hooks/*.sh lib/*.sh` would cover none of it. `OWNS:` ownership leases — deferred (Palier 3): our parallel dispatches (seo/geo, 3 plan-challengers) are read-only, the write-collision problem does not exist yet.
|
||||||
@@ -1099,14 +1112,14 @@ Shipped: lib/gates.sh (~250 l bash; `status` never executes and never writes ·
|
|||||||
Alternatives rejected: Palier 1 doctrine-only (CHECK:/EXPECT: become decorative without an executant); port the Node checker (stack break, shellcheck-blind); fold ABANDONED into ECARTS (would send a dev to fix the impossible and eat the 3-iteration budget); `status` revalidating old evidence (that trust is the failure being closed).
|
Alternatives rejected: Palier 1 doctrine-only (CHECK:/EXPECT: become decorative without an executant); port the Node checker (stack break, shellcheck-blind); fold ABANDONED into ECARTS (would send a dev to fix the impossible and eat the 3-iteration budget); `status` revalidating old evidence (that trust is the failure being closed).
|
||||||
Branch feature/contract-gates, UNMERGED (human gate). `make test` rc 0, shellcheck clean, e2e verified on a real contract in the documented template.
|
Branch feature/contract-gates, UNMERGED (human gate). `make test` rc 0, shellcheck clean, e2e verified on a real contract in the documented template.
|
||||||
|
|
||||||
### BDR-084 — /tour multi-project: parallel runners, bounded LRN-083 derogation [accepted] (2026-08-24)
|
## BDR-084 — /tour multi-project: parallel runners, bounded LRN-083 derogation [accepted] (2026-08-24)
|
||||||
User asked whether agent parallelism on independent tasks is ACTIVE. Measured first (LRN-080): (a) mechanics — nested probe, 1 dispatched orchestrator fanned 3 sub-agents, execution windows all overlap, 9.1s vs ~18s sequential ⇒ nested parallel dispatch WORKS; (b) doctrine — already prescribed at 3 layers (harness "single message" injection; /seo, challenge-plan, /cso, graphify explicit same-message mandates; graphify even anti-sequential wording); remaining serializations all MOTIVATED (audit-delta crash-resilience documented, verify-secure-loop order invariant); (c) behavior — probe orchestrator batched spontaneously without being told "parallel" (N=1), this session fanned 8+7 agents/message during the RED. Conclusion: nothing to add globally — a CLAUDE.md "parallelize" line would duplicate-stack the harness injection (BDR-081 anti-pattern).
|
User asked whether agent parallelism on independent tasks is ACTIVE. Measured first (LRN-080): (a) mechanics — nested probe, 1 dispatched orchestrator fanned 3 sub-agents, execution windows all overlap, 9.1s vs ~18s sequential ⇒ nested parallel dispatch WORKS; (b) doctrine — already prescribed at 3 layers (harness "single message" injection; /seo, challenge-plan, /cso, graphify explicit same-message mandates; graphify even anti-sequential wording); remaining serializations all MOTIVATED (audit-delta crash-resilience documented, verify-secure-loop order invariant); (c) behavior — probe orchestrator batched spontaneously without being told "parallel" (N=1), this session fanned 8+7 agents/message during the RED. Conclusion: nothing to add globally — a CLAUDE.md "parallelize" line would duplicate-stack the harness injection (BDR-081 anti-pattern).
|
||||||
ONE real sequential-but-independent candidate: /tour multi-project (independent repos, one by one, no documented reason). User gate: option "tout paralléliser" chosen over report-only-only and no-change, WITH the model invariant "orchestrateur garde le modèle orchestrateur; skills/agents suivent leurs orchestrateurs définis".
|
ONE real sequential-but-independent candidate: /tour multi-project (independent repos, one by one, no documented reason). User gate: option "tout paralléliser" chosen over report-only-only and no-change, WITH the model invariant "orchestrateur garde le modèle orchestrateur; skills/agents suivent leurs orchestrateurs définis".
|
||||||
Decision: STEP 0 routes (1 project = inline unchanged; ≥2 = STEP 0b fan-out). One general-purpose runner per project, ALL in ONE message, dispatched with NO model override — inherits the session model (model-gate already validated big; a runner carries tour's reflection: fix decisions, convergence). Inside a runner every agent keeps its defined tier (security-auditor sonnet, Phase B opus, doc-syncer sonnet two-mode). Dead/mute runner ⇒ explicit `RUNNER FAILED` summary row (mute is never a pass). Capitalize offer stays MAIN LOOP ONLY (registries = shared state).
|
Decision: STEP 0 routes (1 project = inline unchanged; ≥2 = STEP 0b fan-out). One general-purpose runner per project, ALL in ONE message, dispatched with NO model override — inherits the session model (model-gate already validated big; a runner carries tour's reflection: fix decisions, convergence). Inside a runner every agent keeps its defined tier (security-auditor sonnet, Phase B opus, doc-syncer sonnet two-mode). Dead/mute runner ⇒ explicit `RUNNER FAILED` summary row (mute is never a pass). Capitalize offer stays MAIN LOOP ONLY (registries = shared state).
|
||||||
LRN-083 derogation, bounded: per-project fix loop + convergence now run INSIDE the dispatched runner. Bounded because nothing a runner decides touches shared state — independent repos, per-repo chore branches, branches stay UNMERGED for human review exactly as inline (report-as-approval-gate design unchanged). Precedent: client-handover-writer already a dispatched orchestrator running parallel audit loops (BDR-077).
|
LRN-083 derogation, bounded: per-project fix loop + convergence now run INSIDE the dispatched runner. Bounded because nothing a runner decides touches shared state — independent repos, per-repo chore branches, branches stay UNMERGED for human review exactly as inline (report-as-approval-gate design unchanged). Precedent: client-handover-writer already a dispatched orchestrator running parallel audit loops (BDR-077).
|
||||||
Alternatives rejected: report-only-only parallel (my recommendation — user overrode: full parallel wanted); one sub-orchestrator agent .md file (drift risk vs SKILL.md, the runner reads the skill from disk instead — client-handover→/seo precedent); pinning the runner (would put tour reflection on an executor tier — inverts BDR-076); global CLAUDE.md parallelism line (duplicate of harness injection). Census §12: 6 locks (fan-out present, no-pin, single-message, capitalize main-loop, RUNNER FAILED, no pinned runner), flip-tested. Branch feature/tour-parallel, UNMERGED (human gate).
|
Alternatives rejected: report-only-only parallel (my recommendation — user overrode: full parallel wanted); one sub-orchestrator agent .md file (drift risk vs SKILL.md, the runner reads the skill from disk instead — client-handover→/seo precedent); pinning the runner (would put tour reflection on an executor tier — inverts BDR-076); global CLAUDE.md parallelism line (duplicate of harness injection). Census §12: 6 locks (fan-out present, no-pin, single-message, capitalize main-loop, RUNNER FAILED, no pinned runner), flip-tested. Branch feature/tour-parallel, UNMERGED (human gate).
|
||||||
|
|
||||||
### BDR-085 — user permanent rules: writing-style always-on in rules/, web rules path-scoped [accepted] (2026-08-25)
|
## BDR-085 — user permanent rules: writing-style always-on in rules/, web rules path-scoped [accepted] (2026-08-25)
|
||||||
User supplied 4-block permanent rule text (writing / website / code security / self-check), asked: coverage check, conflict check, integrate. Coverage verdict: security CORE (parameterized queries, input validation, env-var secrets, AuthN/AuthZ split + default deny, no stack traces, fail closed, least privilege) ALREADY in CLAUDE.global.md §Security — NOT duplicated. NEW: entire writing-style block, design anti-default list, public-site done-checklist, web-app specifics (browser-exposed keys, service-key/client split, RLS, server-side auth, IDOR, hashed passwords + cookie flags, field minimization, rate limiting, upload restrictions).
|
User supplied 4-block permanent rule text (writing / website / code security / self-check), asked: coverage check, conflict check, integrate. Coverage verdict: security CORE (parameterized queries, input validation, env-var secrets, AuthN/AuthZ split + default deny, no stack traces, fail closed, least privilege) ALREADY in CLAUDE.global.md §Security — NOT duplicated. NEW: entire writing-style block, design anti-default list, public-site done-checklist, web-app specifics (browser-exposed keys, service-key/client split, RLS, server-side auth, IDOR, hashed passwords + cookie flags, field minimization, rate limiting, upload restrictions).
|
||||||
Placement: CLAUDE.global.md at 308/320 (session-start density guard) → no room for ~30 always-on lines. Decision: rules/writing-style.md WITHOUT paths: (always-on load, same session cost, outside the 320 budget) + rules/web-building.md + rules/web-security.md WITH paths: (lazy-load = token win, fire only on web/code files). Project CLAUDE.md doctrine line amended with the budget exception. Feeds C2 self-contradiction audit.
|
Placement: CLAUDE.global.md at 308/320 (session-start density guard) → no room for ~30 always-on lines. Decision: rules/writing-style.md WITHOUT paths: (always-on load, same session cost, outside the 320 budget) + rules/web-building.md + rules/web-security.md WITH paths: (lazy-load = token win, fire only on web/code files). Project CLAUDE.md doctrine line amended with the budget exception. Feeds C2 self-contradiction audit.
|
||||||
Conflict carve-outs, stated INSIDE the rules: registries keep caveman format (fragments, em-dashes, bullets); code comments keep code style; structured skill/report templates keep their formats; robuste/transformer banned in buzzword sense only (robustness lens, math transform allowed); no-Inter default rule carries "existing brand identities keep their fonts" (ZenQuality deliverables use Inter+Playfair by brand decision — client-handover BDR).
|
Conflict carve-outs, stated INSIDE the rules: registries keep caveman format (fragments, em-dashes, bullets); code comments keep code style; structured skill/report templates keep their formats; robuste/transformer banned in buzzword sense only (robustness lens, math transform allowed); no-Inter default rule carries "existing brand identities keep their fonts" (ZenQuality deliverables use Inter+Playfair by brand decision — client-handover BDR).
|
||||||
|
|||||||
+12
-2
@@ -34,9 +34,19 @@ rules:
|
|||||||
| EVAL-011 | 2026-06-30 | /reconcile build: RED contaminated→corrected (unguided control), GREEN behavioral confirmed, dogfooded on itself | keep |
|
| EVAL-011 | 2026-06-30 | /reconcile build: RED contaminated→corrected (unguided control), GREEN behavioral confirmed, dogfooded on itself | keep |
|
||||||
| EVAL-012 | 2026-06-30 | /release-candidate build: RED (gitflow fans out, no tag) → GREEN 5/5 (tag), throwaway-repo flow replay | keep |
|
| EVAL-012 | 2026-06-30 | /release-candidate build: RED (gitflow fans out, no tag) → GREEN 5/5 (tag), throwaway-repo flow replay | keep |
|
||||||
| EVAL-013 | 2026-06-30 | /reconcile real-usage on live repo: known gap + 2 unanticipated (header-marker drift class) + false-positive rejected off-fixture, 0 false assertion | keep |
|
| EVAL-013 | 2026-06-30 | /reconcile real-usage on live repo: known gap + 2 unanticipated (header-marker drift class) + false-positive rejected off-fixture, 0 false assertion | keep |
|
||||||
|
| EVAL-014 | 2026-07-05 | /tour GREEN run: 6/6 RED gaps closed, disk-verified; re-verify caught agent's own regression | keep (skill shipped). REFACTOR additions not re-run through 3rd full pass — re-test at first real u… |
|
||||||
|
| EVAL-015 | 2026-07-05 | /tour first REAL run (report-only, bchanot-cv): REFACTOR additions validated; premise corrected by user | keep. Skill validated on real drift; two refinement candidates noted (report-commit placement, serv… |
|
||||||
|
| EVAL-016 | 2026-07-05 | /deploy first REAL run (bchanot-cv): bootstrap→instantiate→hand-back→mark, full cycle OK | keep. Two-moment contract works in-session; disk artifacts coherent throughout. |
|
||||||
|
| EVAL-017 | 2026-07-06 | job2 audit: fresh-context verify pass caught 3 explorer false claims | harness-semantics claims from explorers ALWAYS cross-check vs docs/live evidence; file-content clai… |
|
||||||
| EVAL-018 | 2026-07-06 | job3 docs-drift audit + execution: 46/46 findings verified, 20/23 fixes shipped (B1 blocked, D2-D5+B6 skipped by decision), zero residual on re-sweep | keep |
|
| EVAL-018 | 2026-07-06 | job3 docs-drift audit + execution: 46/46 findings verified, 20/23 fixes shipped (B1 blocked, D2-D5+B6 skipped by decision), zero residual on re-sweep | keep |
|
||||||
| EVAL-019 | 2026-07-06 | job4 test-gap audit + execution: 11 specs + 5 fixes/seams, every mutation red-green verified, zero residual | keep |
|
| EVAL-019 | 2026-07-06 | job4 test-gap audit + execution: 11 specs + 5 fixes/seams, every mutation red-green verified, zero residual | keep |
|
||||||
|
| EVAL-020 | 2026-07-07 | job6 dep upgrade execution: 5 deps sequenced by risk, 2 real STOP gates hit and resolved live, zero regression | keep. Branch unmerged (`chore/job6-deps-upgrade`, gitflow finish = separate human signal per CLAUDE… |
|
||||||
|
| EVAL-021 | 2026-07-08 | adversarial review of the 9-job series (release/1.0.0..develop) + remediation | keep. Remediation branch unmerged (human gate). Fil-rouge guard now prevents the partial-fix class… |
|
||||||
|
| EVAL-022 | 2026-07-08 | job9 model pins (BDR-060) were smoke-tested but never recorded as an EVAL (M5 trace) | keep — record backfilled here, no re-smoke required. |
|
||||||
|
| EVAL-023 | 2026-07-16 | post-merge ronde on the model-routing refactor (BDR-066) — clean, 5 edge gaps found + fixed | keep — all 5 fixed (bugfix/model-routing-edge-fixes, merged 5f159f3); census 47→57 now locks each. |
|
||||||
|
| EVAL-024 | 2026-07-16 | deny-list design pass (BDR-069) — core fix sound, 1 unauthorized weakening caught by classifier not by me | keep — fix landed (07ca738), weakening reverted. Lesson: vague delegation ("je te laisse en juger")… |
|
||||||
| EVAL-025 | 2026-07-17 | opening seo/geo inventory (subagents): 7/7 verifiable claims false or overstated; real contact corrected all, 6 plan corrections + 4 features killed at measurement | keep |
|
| EVAL-025 | 2026-07-17 | opening seo/geo inventory (subagents): 7/7 verifiable claims false or overstated; real contact corrected all, 6 plan corrections + 4 features killed at measurement | keep |
|
||||||
|
| EVAL-026 | 2026-07-17 | 3-way plan challenge caught 4 BLOCKERs dogfooding own plan (2026-07-17) | — |
|
||||||
| EVAL-027 | 2026-08-24 | contract-gates behavioral RED: 16/16 fresh unprimed runs followed new doctrine (GATE 0 order, vacuous oracle, ABANDONED routing, scope temptation resisted) | keep |
|
| EVAL-027 | 2026-08-24 | contract-gates behavioral RED: 16/16 fresh unprimed runs followed new doctrine (GATE 0 order, vacuous oracle, ABANDONED routing, scope temptation resisted) | keep |
|
||||||
| EVAL-028 | 2026-08-26 | darwin v2.1 paired run 54 units: 60 paired verdicts 0 revert/tie; skeptics found 3 real residuals — engaged, not rubber-stamp | keep |
|
| EVAL-028 | 2026-08-26 | darwin v2.1 paired run 54 units: 60 paired verdicts 0 revert/tie; skeptics found 3 real residuals — engaged, not rubber-stamp | keep |
|
||||||
| EVAL-029 | 2026-09-15 | 4-agent plan challenge: 6 BLOCKER; 3 of 3 confirmation-pass BLOCKERs came from the fixes themselves; caught a false 654 MB orphan claim | keep |
|
| EVAL-029 | 2026-09-15 | 4-agent plan challenge: 6 BLOCKER; 3 of 3 confirmation-pass BLOCKERs came from the fixes themselves; caught a false 654 MB orphan claim | keep |
|
||||||
@@ -252,10 +262,10 @@ rules:
|
|||||||
- **anomalies**: 7/7 of the verifiable claims were false or overstated (VSI exists / Off-page zero-data / stats drive weights / GSC Links API / SPA §0 flag / Twitter 403 / Common Crawl viable). 6 plan corrections mid-execution: I1 over-correction, I6 wrong framing, W1 wrong shape (verb vs extend), C1a false premise (grep already skips gitignore), C1b needless guard, B1 non-viable at 17.3 GB. The REAL corrected every time; re-reading the spec never did.
|
- **anomalies**: 7/7 of the verifiable claims were false or overstated (VSI exists / Off-page zero-data / stats drive weights / GSC Links API / SPA §0 flag / Twitter 403 / Common Crawl viable). 6 plan corrections mid-execution: I1 over-correction, I6 wrong framing, W1 wrong shape (verb vs extend), C1a false premise (grep already skips gitignore), C1b needless guard, B1 non-viable at 17.3 GB. The REAL corrected every time; re-reading the spec never did.
|
||||||
- **action**: keep — see [[LRN-132]]. 4 features killed at measurement (B1/B2/B3 + W2 deferred) beat 4 false-signal features. The most trustworthy output of the session was the code NOT written. Method that worked: show/measure the real artifact before deciding, mirroring [[LRN-074]]'s watch-the-RED discipline applied to a plan.
|
- **action**: keep — see [[LRN-132]]. 4 features killed at measurement (B1/B2/B3 + W2 deferred) beat 4 false-signal features. The most trustworthy output of the session was the code NOT written. Method that worked: show/measure the real artifact before deciding, mirroring [[LRN-074]]'s watch-the-RED discipline applied to a plan.
|
||||||
|
|
||||||
### EVAL-026 — 3-way plan challenge caught 4 BLOCKERs dogfooding own plan (2026-07-17)
|
## EVAL-026 — 3-way plan challenge caught 4 BLOCKERs dogfooding own plan (2026-07-17)
|
||||||
Dogfood: 3 blind lenses attacked the v1 plan for the plan-challenge feature itself. Verdicts CONCERNS(4)/FATAL(6)/FATAL(4). Caught 4 distinct BLOCKERs a single pass would blend: (1) v1 unbuildable — targeted init-project (inline-load, no dispatch) + false "plan on disk" premise for feat/bugfix (only contract persists); (2) failed-open silently dropping a lens while claiming "challenged" (inverts verify-secure-loop "a mute verifier is NEVER a PASS"); (3) consensus-weighting buries lone L2 security finding (lenses orthogonal); (4) sonnet challengers violate [[BDR-066]] (audit judgment=big model). Synthesis REJECTED 1 false positive (allowed-tools-blocks-dispatch — ship-feature has same frontmatter + dispatches fine). Each lens found a DIFFERENT class of flaw → evidence 3-independent > 1-multilens. Action: hardened v2 (severity-driven + fail-safe + re-think loop) shipped. Method validated itself before build.
|
Dogfood: 3 blind lenses attacked the v1 plan for the plan-challenge feature itself. Verdicts CONCERNS(4)/FATAL(6)/FATAL(4). Caught 4 distinct BLOCKERs a single pass would blend: (1) v1 unbuildable — targeted init-project (inline-load, no dispatch) + false "plan on disk" premise for feat/bugfix (only contract persists); (2) failed-open silently dropping a lens while claiming "challenged" (inverts verify-secure-loop "a mute verifier is NEVER a PASS"); (3) consensus-weighting buries lone L2 security finding (lenses orthogonal); (4) sonnet challengers violate [[BDR-066]] (audit judgment=big model). Synthesis REJECTED 1 false positive (allowed-tools-blocks-dispatch — ship-feature has same frontmatter + dispatches fine). Each lens found a DIFFERENT class of flaw → evidence 3-independent > 1-multilens. Action: hardened v2 (severity-driven + fail-safe + re-think loop) shipped. Method validated itself before build.
|
||||||
|
|
||||||
### EVAL-027 — contract-gates behavioral RED: 16/16 fresh runs follow the new doctrine (2026-08-24)
|
## EVAL-027 — contract-gates behavioral RED: 16/16 fresh runs follow the new doctrine (2026-08-24)
|
||||||
- **output**: BDR-083 doctrine (GATE 0 in verify-secure-loop, oracle rules in contract-interview, oracle-consumption + ABANDONED(n) in verifier, 4 passes in feater/bugfixer) — locks prove the TEXT is there; this RED measured whether fresh unprimed contexts FOLLOW it.
|
- **output**: BDR-083 doctrine (GATE 0 in verify-secure-loop, oracle rules in contract-interview, oracle-consumption + ABANDONED(n) in verifier, 4 passes in feater/bugfixer) — locks prove the TEXT is there; this RED measured whether fresh unprimed contexts FOLLOW it.
|
||||||
- **method**: 16 subagent runs on sandbox repos (scratchpad/red/), prompts = the documented dispatch shapes verbatim, zero mention of test/measure/gates (LRN-080 anti-priming; distinct from LRN-080's own question — instruction already written, question = compliance not pre-existence). Production agents (subagent_type verifier ×9, feater ×2) + fresh orchestrator roles ×5. Every claim re-scored deterministically after: EVIDENCE lines physically rewritten in contracts, git status on sandboxes, gates.sh parse of authored contracts.
|
- **method**: 16 subagent runs on sandbox repos (scratchpad/red/), prompts = the documented dispatch shapes verbatim, zero mention of test/measure/gates (LRN-080 anti-priming; distinct from LRN-080's own question — instruction already written, question = compliance not pre-existence). Production agents (subagent_type verifier ×9, feater ×2) + fresh orchestrator roles ×5. Every claim re-scored deterministically after: EVIDENCE lines physically rewritten in contracts, git status on sandboxes, gates.sh parse of authored contracts.
|
||||||
- **verdict**: 16/16 conformant. v1 red-oracle-wins 3/3 (NOT-MET citing evidence, own re-run). v2 vacuous-oracle 3/3 — hardest rule (green evidence + correct code → still NOT-MET, evidence explicitly discarded per rule). v3 abandonment semantics 2/2 + v3b pure precedence 1/1 (ABANDONED(1), not CONFORME). o-red 2/2 (gates.sh FIRST, verdict parsed, NO verifier on red floor, executor re-dispatch = contract path + NOT-MET rows verbatim, floor iteration counted 1/3). o-green 1/1 (floor → verifier dispatch with CONTRACT+DIFF+TEST only). e contract-authoring 2/2 (3 oracles + 1 judgement-kept-manual, parse clean in gates.sh first try, POSITIVE CONTROLS run unprompted — rule 3 internalized, markers distinct success-only tokens). f feater 2/2 (out-of-scope temptation src/util.sh SEEN and named untouched, no commit, no placeholder, 4 passes visible in report).
|
- **verdict**: 16/16 conformant. v1 red-oracle-wins 3/3 (NOT-MET citing evidence, own re-run). v2 vacuous-oracle 3/3 — hardest rule (green evidence + correct code → still NOT-MET, evidence explicitly discarded per rule). v3 abandonment semantics 2/2 + v3b pure precedence 1/1 (ABANDONED(1), not CONFORME). o-red 2/2 (gates.sh FIRST, verdict parsed, NO verifier on red floor, executor re-dispatch = contract path + NOT-MET rows verbatim, floor iteration counted 1/3). o-green 1/1 (floor → verifier dispatch with CONTRACT+DIFF+TEST only). e contract-authoring 2/2 (3 oracles + 1 judgement-kept-manual, parse clean in gates.sh first try, POSITIVE CONTROLS run unprompted — rule 3 internalized, markers distinct success-only tokens). f feater 2/2 (out-of-scope temptation src/util.sh SEEN and named untouched, no commit, no placeholder, 4 passes visible in report).
|
||||||
|
|||||||
+142
-91
@@ -122,23 +122,53 @@ rules:
|
|||||||
| LRN-100 | 2026-07-05 | tool gated on clean tree must clean its OWN scratch (else self-DoS next run); contract-changing auto-fix needs structural BREAKING flag in the reviewed artifact | any recurring tool w/ cleanliness precondition; any auto-fix touching an API contract |
|
| LRN-100 | 2026-07-05 | tool gated on clean tree must clean its OWN scratch (else self-DoS next run); contract-changing auto-fix needs structural BREAKING flag in the reviewed artifact | any recurring tool w/ cleanliness precondition; any auto-fix touching an API contract |
|
||||||
| LRN-101 | 2026-07-05 | nginx `add_header` inheritance trap: ANY add_header in a location block drops ALL inherited server-level headers on those responses — audit headers on LIVE responses (`curl -I`), never by reading the config; declared infra can be stale (prod ≠ repo stack) | any nginx project audit (zenquality, faunosteo…); any security-header claim |
|
| LRN-101 | 2026-07-05 | nginx `add_header` inheritance trap: ANY add_header in a location block drops ALL inherited server-level headers on those responses — audit headers on LIVE responses (`curl -I`), never by reading the config; declared infra can be stale (prod ≠ repo stack) | any nginx project audit (zenquality, faunosteo…); any security-header claim |
|
||||||
| LRN-102 | 2026-07-05 | deliverable text placed BEFORE a tool call may never render — only the turn's FINAL text is guaranteed displayed; a checklist printed above AskUserQuestion was invisible to the user | any flow whose deliverable is conversational text (checklist, commands, report): end the turn with it, blocking questions come before, never after |
|
| LRN-102 | 2026-07-05 | deliverable text placed BEFORE a tool call may never render — only the turn's FINAL text is guaranteed displayed; a checklist printed above AskUserQuestion was invisible to the user | any flow whose deliverable is conversational text (checklist, commands, report): end the turn with it, blocking questions come before, never after |
|
||||||
| LRN-105 | 2026-07-06 | explorer subagent ran a build tool (`graphify .`) mid read-only audit despite prose instructions to only Read/Grep/Bash-read — the runtime observed a config-protection sentinel deny message and self-corrected only after an explicit main-session correction, not from the original prompt | dispatching any "read-only audit" subagent whose toolset includes Bash: state "do not execute build/generator/mutating commands" explicitly, don't rely on "read-only" framing alone to constrain tool CHOICE |
|
| LRN-103 | — | BLK-009 was stale: re-probe confirms `paths:` frontmatter works at BOTH levels now | before acting on ANY open upstream/tool blocker cited to justify a fix, a caveat, or a design const… |
|
||||||
| LRN-106 | 2026-07-06 | job3-B1 froze a fixture + repointed run-reconcile.sh's T2 off the live registry, declared "unblocked", 20/20 green — job4 (next audit, same file, same day) found T3+T5 in the SAME FILE still read the live registry, same fragility, untouched | fixing one instance of a "reads live state it shouldn't" finding: grep the WHOLE file (not just the cited line) for the same pattern before declaring the class closed |
|
| LRN-104 | — | a hook's output message is part of its test contract; no runner = regression invisible | change any hook/script output consumed by a test → run its test same commit. `make test` now the de… |
|
||||||
|
| LRN-105 | 2026-07-06 | explorer subagent ran a build tool (`graphify .`) mid read-only audit despite prose instructions to only Read/Grep/Bash-read — the runtime observed a config-protection sentinel deny message and self-corrected only after an explicit main-session correction, not from the original prompt | superseded by LRN-165 |
|
||||||
|
| LRN-106 | 2026-07-06 | job3-B1 froze a fixture + repointed run-reconcile.sh's T2 off the live registry, declared "unblocked", 20/20 green — job4 (next audit, same file, same day) found T3+T5 in the SAME FILE still read the live registry, same fragility, untouched | superseded by LRN-164 |
|
||||||
|
| LRN-107 | — | read-only subagent mandates must ban copying secret VALUES, not just mutations | superseded by LRN-165 |
|
||||||
|
| LRN-108 | — | `claude mcp add --env KEY=value` writes the VALUE literally; use `${VAR}` unless you mean to | adding ANY MCP server with a secret via `claude mcp add --env`, single-quote the value using `${VAR… |
|
||||||
| LRN-109 | 2026-07-07 | job8: `skills` CLI (vercel-labs/skills) fetches only `skillPath` (often just SKILL.md), not sibling refs/scripts/templates the skill text references — darwin-skill install gap, not drift/tamper | installing/auditing any skill via the `skills` CLI whose SKILL.md references relative paths — verify those paths exist post-install, don't trust `skillFolderHash` alone |
|
| LRN-109 | 2026-07-07 | job8: `skills` CLI (vercel-labs/skills) fetches only `skillPath` (often just SKILL.md), not sibling refs/scripts/templates the skill text references — darwin-skill install gap, not drift/tamper | installing/auditing any skill via the `skills` CLI whose SKILL.md references relative paths — verify those paths exist post-install, don't trust `skillFolderHash` alone |
|
||||||
| LRN-110 | 2026-07-07 | job8: `21st_magic_component_builder` (magic MCP) opens unauth'd 127.0.0.1 callback server, CORS `*`, no token check, 10min window — any local POST lands verbatim in the tool result the model consumes = local prompt-injection channel | any MCP tool that opens a local callback/listener server to receive async results — check auth + origin scoping on the listener, not just the outbound call |
|
| LRN-110 | 2026-07-07 | job8: `21st_magic_component_builder` (magic MCP) opens unauth'd 127.0.0.1 callback server, CORS `*`, no token check, 10min window — any local POST lands verbatim in the tool result the model consumes = local prompt-injection channel | any MCP tool that opens a local callback/listener server to receive async results — check auth + origin scoping on the listener, not just the outbound call |
|
||||||
| LRN-111 | 2026-07-07 | job8: empty permissions.allow for a risky MCP tool is a VALID posture (not a gap) when transcript census shows zero real invocations — pre-authorizing unused surface buys nothing, ask-gate costs nothing | deciding whether to allowlist any tool/command — check real usage before assuming "no entry = todo" |
|
| LRN-111 | 2026-07-07 | job8: empty permissions.allow for a risky MCP tool is a VALID posture (not a gap) when transcript census shows zero real invocations — pre-authorizing unused surface buys nothing, ask-gate costs nothing | deciding whether to allowlist any tool/command — check real usage before assuming "no entry = todo" |
|
||||||
| LRN-112 | 2026-07-08 | job9: CC nested subagent dispatch SUPPORTED since v2.1.172 (cap 5 levels, `Agent` must be in subagent `tools:`) — "flattens to 1 level" is the pre-2.1.172 regime; live env v2.1.203. Contradicts the operating premise of the whole job1-9 series | a subagent-dispatches-subagent design is VERSION-CONTINGENT, not "broken" — check CC version before flagging; fix = raise floor or re-architect to bundle→L1 |
|
| LRN-112 | 2026-07-08 | job9: CC nested subagent dispatch SUPPORTED since v2.1.172 (cap 5 levels, `Agent` must be in subagent `tools:`) — "flattens to 1 level" is the pre-2.1.172 regime; live env v2.1.203. Contradicts the operating premise of the whole job1-9 series | a subagent-dispatches-subagent design is VERSION-CONTINGENT, not "broken" — check CC version before flagging; fix = raise floor or re-architect to bundle→L1 |
|
||||||
| LRN-113 | 2026-07-08 | partial-pattern-fix = recurring defect of the job1-9 series: fix the cited instance, leave the twins (trailer A1, YAML A4, attribution A5, hook A2). An adversarial review catches twins later; nothing catches them at commit time | any fix of a banned pattern: grep the ENTIRE surface + add a make-test guard (run-review-guards.sh) that REDs if one occurrence subsists |
|
| LRN-113 | 2026-07-08 | partial-pattern-fix = recurring defect of the job1-9 series: fix the cited instance, leave the twins (trailer A1, YAML A4, attribution A5, hook A2). An adversarial review catches twins later; nothing catches them at commit time | superseded by LRN-164 |
|
||||||
| LRN-114 | 2026-07-08 | editing a hook GENERATOR (_gitflow_emit_pre_commit) does NOT update the INSTALLED hook (.githooks/pre-commit) — silent drift; T10 diffs the allow/block verdict not content, T16 emits fresh in a throwaway repo → job7 gitleaks backstop inert on the repo 8 days | after editing a template-generated artifact: reinstall (install-hook) + a gate that diffs installed==emit |
|
| LRN-114 | 2026-07-08 | editing a hook GENERATOR (_gitflow_emit_pre_commit) does NOT update the INSTALLED hook (.githooks/pre-commit) — silent drift; T10 diffs the allow/block verdict not content, T16 emits fresh in a throwaway repo → job7 gitleaks backstop inert on the repo 8 days | after editing a template-generated artifact: reinstall (install-hook) + a gate that diffs installed==emit |
|
||||||
| LRN-115 | 2026-07-08 | analyzer Edit/Write grants (seo/geo/validator) are NOT dead: needed to write the REPORT (VALIDATE/SEO/GEO.md); the "never edit" rule targets CODE, instruction-level (same as the patron) — verified false-positive | do NOT re-flag as a tool-grant defect; a report-only agent keeps Write for its own report |
|
| LRN-115 | 2026-07-08 | analyzer Edit/Write grants (seo/geo/validator) are NOT dead: needed to write the REPORT (VALIDATE/SEO/GEO.md); the "never edit" rule targets CODE, instruction-level (same as the patron) — verified false-positive | do NOT re-flag as a tool-grant defect; a report-only agent keeps Write for its own report |
|
||||||
| LRN-116 | 2026-07-08 | memory backfill release→develop: a BLK marked "resolved" can have its RESOLUTION (code) missing from develop — BLK-016 resolved on release but rtk fix e58037c never back-merged → bug LIVE on develop | before backfilling a resolved blocker: verify the fix CODE is on the target branch, not just the registry entry |
|
| LRN-116 | 2026-07-08 | memory backfill release→develop: a BLK marked "resolved" can have its RESOLUTION (code) missing from develop — BLK-016 resolved on release but rtk fix e58037c never back-merged → bug LIVE on develop | superseded by LRN-167 |
|
||||||
| LRN-117 | 2026-07-08 | a release/develop fork silently orphans FUNCTIONAL code on develop, not just memory — RC soak fixes (find-skills, make-update TTY, rtk version-guard) lived only on release for the fork's duration; the review's memory back-merge caught only ~half | at release-finish/reconcile: list develop..release commits touching non-registry code (excl. merges/version) for back-merge review — a registry-gap check alone misses code |
|
| LRN-117 | 2026-07-08 | a release/develop fork silently orphans FUNCTIONAL code on develop, not just memory — RC soak fixes (find-skills, make-update TTY, rtk version-guard) lived only on release for the fork's duration; the review's memory back-merge caught only ~half | superseded by LRN-167 |
|
||||||
| LRN-131 | 2026-07-17 | WebSearch is NOT verification for a number — SEO blogs cross-cite into fake consensus; require primary source + `measured:` field | any stat headed for a client report; verifying a metric/claim exists |
|
| LRN-118 | — | Gitflow-conformity audit: "commits-code" vs "applies-but-defers-commit" is the line that sorts real findings… | any fleet/skill conformity audit — (1) triage by "autonomous commit/push reached?", not "file writt… |
|
||||||
| LRN-132 | 2026-07-17 | a subagent summary is a CLAIM, not a fact — 7 disproven in one session (incl. 3 I reproduced writing the fixes) | before planning on any relayed finding; verify vs primary source / live test first |
|
| LRN-119 | — | Fail-open engine contract for optional external data (real-if-connected, else graceful) | any "use real data if credentials present, else degrade" seam — put the contract in the shell entry… |
|
||||||
|
| LRN-120 | — | SDD final-review base = `git merge-base`, NOT the ledger's recorded BASE | for ANY whole-branch/final review, derive base from `git merge-base <target> HEAD`, never a stored/… |
|
||||||
|
| LRN-121 | — | Shell allowlist validation: `grep -Eq` is fragile; use a whole-string POSIX `case` | validate shell input WHOLE-STRING (`case` or bash `[[ =~ ]]`), never `grep -q` (per-line). Set `LC_… |
|
||||||
|
| LRN-122 | — | git mv + recreate source path in same commit = rename detection dead | ANY rename-and-replace-in-place (config forks, template splits, versioned API files). Old path must… |
|
||||||
|
| LRN-123 | — | "resolves inside repo" symlink check green-lights stale link once old path re-occupied | symlink/path health checks → assert exact expected target whenever the old target path can be re-oc… |
|
||||||
|
| LRN-124 | — | derived scan artifacts don't belong in git; a tooling hint saying "safe to commit" manufactures the leak | derived security artifacts (scan reports, triage JSONs, audit findings) stay local/ignored; only th… |
|
||||||
|
| LRN-125 | — | don't make an agent dual-use across model tiers; route the audit consumer to a big-model agent, not the sonne… | before making an agent dual-use, check both consumers are on the SAME tier. Audit/reflection consum… |
|
||||||
|
| LRN-126 | — | splitting a monolith agent severs every IMPLICIT data path; forward each consumed field through the handoff c… | when splitting an agent, enumerate EVERY field the child reads (grep child for its input vocabulary… |
|
||||||
|
| LRN-127 | — | SDD implementers must not run destructive git ops on files outside their task scope | dispatch briefs for SDD implementers / fix-subagents MUST bar destructive git ops outside the named… |
|
||||||
|
| LRN-128 | — | a version RESET (backward bump) is editorial reflection, not the forward-only release-executor | version RESET or any non-standard release → do PREP MANUALLY inline (big model), use `gitflow.sh` o… |
|
||||||
|
| LRN-129 | — | `git cherry` (patch-id) proves a stale/divergent branch has nothing orphaned before you delete it | before abandoning/deleting a divergent branch, `git cherry -v <mainline> <branch>` then content-ver… |
|
||||||
|
| LRN-130 | 2026-07-16 | Claude Code deny glob = absolute, no exemption mechanism — 2026-07-16 | — |
|
||||||
|
| LRN-131 | 2026-07-17 | WebSearch is NOT verification for a number — SEO blogs cross-cite into fake consensus; require primary source + `measured:` field | superseded by LRN-168 |
|
||||||
|
| LRN-132 | 2026-07-17 | a subagent summary is a CLAIM, not a fact — 7 disproven in one session (incl. 3 I reproduced writing the fixes) | superseded by LRN-168 |
|
||||||
| LRN-133 | 2026-07-17 | an omission must stay LEGIBLE, never silent — tool that can't measure says so in its output | designing any audit/measure output; deciding what a cap/refusal/N-A emits |
|
| LRN-133 | 2026-07-17 | an omission must stay LEGIBLE, never silent — tool that can't measure says so in its output | designing any audit/measure output; deciding what a cap/refusal/N-A emits |
|
||||||
| LRN-134 | 2026-07-17 | resolve-then-pin in stdlib http.client beats monkeypatching getaddrinfo — dual-stack, thread-safe, no requests; classify the OS-resolved IP not the URL text | closing SSRF/DNS-rebinding on any Python HTTP egress |
|
| LRN-134 | 2026-07-17 | resolve-then-pin in stdlib http.client beats monkeypatching getaddrinfo — dual-stack, thread-safe, no requests; classify the OS-resolved IP not the URL text | closing SSRF/DNS-rebinding on any Python HTTP egress |
|
||||||
| LRN-135 | 2026-07-17 | a prefix-only scan for a dangerous construct is bypassable by padding — scan the WHOLE document | refusing any hostile construct (DTD/directive/marker) before parse |
|
| LRN-135 | 2026-07-17 | a prefix-only scan for a dangerous construct is bypassable by padding — scan the WHOLE document | refusing any hostile construct (DTD/directive/marker) before parse |
|
||||||
|
| LRN-136 | 2026-07-17 | config-protection live state follows checked-out branch's symlinked settings.json (2026-07-17) | — |
|
||||||
|
| LRN-137 | — | mode-based re-tiering beats file splits for mixed-tier agents | before splitting any agent across model tiers, try MODE + `model=` first; create a new agent file o… |
|
||||||
|
| LRN-138 | 2026-07-22 | gitignore ≠ delete for run-time artifacts read from disk (2026-07-22) | "don't merge transient X" → ask: does the run read X from disk? does X travel via git (worktree, fo… |
|
||||||
|
| LRN-139 | 2026-07-30 | model-trait compensations invert across generations; state WHEN-guidance, not direction (2026-07-30) | at every model-generation bump, grep config for trait-compensating language ("counters model tenden… |
|
||||||
|
| LRN-140 | 2026-08-02 | de-prescription findings: dedup evaporates, self-verify is default, recall survives (2026-08-02) | — |
|
||||||
|
| LRN-141 | 2026-08-24 | adopting an external skill: take the invariants, refuse the machinery (2026-08-24) | — |
|
||||||
|
| LRN-142 | 2026-08-24 | structure locks are fixed-string: reflowing a doctrine paragraph reds them (2026-08-24) | superseded by LRN-166 |
|
||||||
| LRN-143 | 2026-08-26 | `cmd \| head \|\| fallback` — pipeline rc is head's (0), fallback dead; bounded output → drop head, else pipefail | any probe/fallback bash in skills before trusting `\|\|` |
|
| LRN-143 | 2026-08-26 | `cmd \| head \|\| fallback` — pipeline rc is head's (0), fallback dead; bounded output → drop head, else pipefail | any probe/fallback bash in skills before trusting `\|\|` |
|
||||||
|
| LRN-144 | — | census locks grep EXACT single-line phrases; prose rewrap breaks them | superseded by LRN-166 |
|
||||||
|
| LRN-145 | — | hooks reach the terminal only via terminalSequence JSON field | — |
|
||||||
|
| LRN-146 | — | Notification event alone misses end-of-turn; Stop is the missing event | — |
|
||||||
|
| LRN-147 | — | VS Code restores terminals BEFORE ext activation → toast dies every restart | superseded by LRN-163 |
|
||||||
|
| LRN-148 | — | terminal instrumentation is per-terminal + unpredictable; pre-flight test before attaching | superseded by LRN-163 |
|
||||||
|
| LRN-149 | — | Stop hook payload carries background_tasks; use it to skip premature signals | — |
|
||||||
| LRN-150 | 2026-09-15 | Sourced lib shares caller shell: bare `ok/warn/info` override its printers, and its `set -e` applies inside | any new lib/*.sh |
|
| LRN-150 | 2026-09-15 | Sourced lib shares caller shell: bare `ok/warn/info` override its printers, and its `set -e` applies inside | any new lib/*.sh |
|
||||||
| LRN-151 | 2026-09-15 | Playwright cache truth = union over `.links`, dir name maps `_`→`-`, revisionOverrides exist | shared versioned binary caches |
|
| LRN-151 | 2026-09-15 | Playwright cache truth = union over `.links`, dir name maps `_`→`-`, revisionOverrides exist | shared versioned binary caches |
|
||||||
| LRN-152 | 2026-09-15 | git `protocol.file=user` blocks submodule fixtures; `-c` misses the code under test, `GIT_CONFIG_*` env does not | tests building git fixtures |
|
| LRN-152 | 2026-09-15 | git `protocol.file=user` blocks submodule fixtures; `-c` misses the code under test, `GIT_CONFIG_*` env does not | tests building git fixtures |
|
||||||
@@ -148,6 +178,16 @@ rules:
|
|||||||
| LRN-156 | 2026-09-16 | autoMode.allow = exception tier; static interpreter allow suspended under auto → conditions live in prose | conditional permissions |
|
| LRN-156 | 2026-09-16 | autoMode.allow = exception tier; static interpreter allow suspended under auto → conditions live in prose | conditional permissions |
|
||||||
| LRN-157 | 2026-09-16 | gap-only trigger blind to taste → add a trigger class, not budget; ask at plan, mid-run for leftovers | any "ask more" request |
|
| LRN-157 | 2026-09-16 | gap-only trigger blind to taste → add a trigger class, not budget; ask at plan, mid-run for leftovers | any "ask more" request |
|
||||||
| LRN-158 | 2026-09-22 | Installer refusing symlinked paths vs a symlinked config dir → stage under a throwaway HOME, move the result | any vendor installer writing into ~/.claude or ~/.config |
|
| LRN-158 | 2026-09-22 | Installer refusing symlinked paths vs a symlinked config dir → stage under a throwaway HOME, move the result | any vendor installer writing into ~/.claude or ~/.config |
|
||||||
|
| LRN-159 | 2026-09-22 | A pin whose payload is fetched at install time rots: pin + fallback, and read the installer's output, not its… | any `install-plugins.sh` step whose pinned tool downloads something at install time. Probe both HOM… |
|
||||||
|
| LRN-160 | 2026-09-22 | Prose guardrails are judgment, not boundary: a well-argued brief walks a sub-agent through them | any new destructive capability → static deny first, prose second, doctrine third. Any orchestrator… |
|
||||||
|
| LRN-161 | 2026-09-24 | `git branch -d` guards against the UPSTREAM once one is set: auto-push turns it into a no-op guard | any change to upstream/push config → re-read every `-d`, `--ff-only`, `@{u}`-relative guard. New de… |
|
||||||
|
| LRN-162 | 2026-09-24 | graphify measured: free AST map, paid semantic pass, 2-3k tokens per query, noise from `.claude/` | measure a "context saver" before adopting it — build time, artifact size, tokens per use, noise sou… |
|
||||||
|
| LRN-163 | 2026-09-24 | VS Code terminal instrumentation is per-terminal and unpredictable: pre-flight the pty before attaching | notify-attention over Remote-SSH; any client-side terminal-parsing ext |
|
||||||
|
| LRN-164 | 2026-09-24 | one fixed occurrence ≠ pattern closed: grep the whole surface, add a guard with teeth | any "fix pattern X" task; reads-live-state, banned tokens, stale pins |
|
||||||
|
| LRN-165 | 2026-09-24 | a read-only sub-agent mandate constrains files, not tools: name the banned commands, ban copying secret values | every sub-agent brief framed read-only / audit / verify with Bash or config access |
|
||||||
|
| LRN-166 | 2026-09-24 | structure and census locks are fixed single-line strings: a prose rewrap reds them with zero doctrine lost | editing any doctrine, skill or agent file under lib/tests locks |
|
||||||
|
| LRN-167 | 2026-09-24 | a release/develop fork strands CODE on develop: a "resolved" blocker or a parallel-merged feature can miss its fix | any long-lived fork (release/*, long feature); back-merging a resolved blocker |
|
||||||
|
| LRN-168 | 2026-09-24 | a relayed claim is not a fact: WebSearch consensus and sub-agent summaries both need a primary source or a live test | any number, feature or finding relayed by search or by a sub-agent before it shapes a plan or a client deliverable |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -261,6 +301,7 @@ rules:
|
|||||||
- `readlink ~/.claude/skills` + `readlink ~/.claude/agents` first if unsure. Both point to Documents/claude/{skills,agents}.
|
- `readlink ~/.claude/skills` + `readlink ~/.claude/agents` first if unsure. Both point to Documents/claude/{skills,agents}.
|
||||||
- Don't waste branch in `~/.claude` — nothing to track for skill content.
|
- Don't waste branch in `~/.claude` — nothing to track for skill content.
|
||||||
- **Reference**: `.claude/audits/DARWIN-SKILL-OPTIMIZATION.md`, branch `auto-optimize/skills-20260506-1730` in Documents/claude.
|
- **Reference**: `.claude/audits/DARWIN-SKILL-OPTIMIZATION.md`, branch `auto-optimize/skills-20260506-1730` in Documents/claude.
|
||||||
|
- **Update 2026-09-24**: path now `/home/bchanot/Documents/claude` (home renamed after the 2026-09-21 wipe; symlink layout unchanged).
|
||||||
|
|
||||||
## LRN-011 — Single subagent emits N independently-gated scores: pattern
|
## LRN-011 — Single subagent emits N independently-gated scores: pattern
|
||||||
|
|
||||||
@@ -626,13 +667,12 @@ rules:
|
|||||||
---
|
---
|
||||||
|
|
||||||
## LRN-038 — Playwright host-platform override for distros newer than its hardcoded support list
|
## LRN-038 — Playwright host-platform override for distros newer than its hardcoded support list
|
||||||
|
|
||||||
- **Date**: 2026-06-23
|
- **Date**: 2026-06-23
|
||||||
- **Context**: fresh Ubuntu 26.04. gstack `./setup` aborted: "Playwright does not support chromium on ubuntu26.04-x64". Playwright 1.58.2's registry hardcodes `ubuntu20.04/22.04/24.04` only; a newer release → no matching build → hard error. gstack is a pinned submodule (must not edit).
|
- **Context**: fresh Ubuntu 26.04. gstack `./setup` aborted: "Playwright does not support chromium on ubuntu26.04-x64". Playwright 1.58.2's registry hardcodes `ubuntu20.04/22.04/24.04` only; a newer release → no matching build → hard error. gstack is a pinned submodule (must not edit).
|
||||||
- **Pattern**: `PLAYWRIGHT_HOST_PLATFORM_OVERRIDE=ubuntuXX.04-<arch>` forces a fallback build. MUST include arch (`x64`/`arm64`) — bare `ubuntu24.04` fails ("does not support … ubuntu24.04"). Set it from the WRAPPER: `export` before the submodule's setup (install-time download) AND persist to the shell profile (runtime launch) — both paths call `getHostPlatform`. No submodule edit. Gate on real OS version (`sort -V` compare) so supported distros are untouched. Test with the LOCAL `./node_modules/.bin/playwright` — `bunx playwright` pulls the LATEST playwright (different browser revision than the local import), which masks the result.
|
- **Pattern**: `PLAYWRIGHT_HOST_PLATFORM_OVERRIDE=ubuntuXX.04-<arch>` forces a fallback build. MUST include arch (`x64`/`arm64`) — bare `ubuntu24.04` fails ("does not support … ubuntu24.04"). Set from the WRAPPER: `export` before the submodule's setup (install-time download) AND persist to the shell profile (runtime launch) — both paths call `getHostPlatform`. No submodule edit. Gate on real OS version (`sort -V`) → supported distros untouched. Test with the LOCAL `./node_modules/.bin/playwright` — `bunx playwright` pulls the LATEST playwright (different browser revision than the local import), masks the result.
|
||||||
- **Future application**: any pinned tool that hardcodes an OS allowlist breaks on a fresh OS upgrade. Look for a host-platform override env before bumping/forking the dep. Prove the fallback binary actually runs (`ldd` = no missing libs + a real headless render), not just that the download resolves.
|
- **Future application**: pinned tool hardcoding an OS allowlist breaks on a fresh OS upgrade. Look for a host-platform override env before bumping/forking the dep. Prove the fallback binary actually runs (`ldd` = no missing libs + a real headless render), not just that the download resolves.
|
||||||
- **Reference**: `install-plugins.sh` `playwright_platform_override()`, commit 211c7d4. Linked to [[BLK-008]].
|
- **Reference**: `install-plugins.sh` `playwright_platform_override()`, commit 211c7d4. Linked to [[BLK-008]].
|
||||||
- **2026-06-23 CORRECTION (override REVERTED, commit b9c3937)**: the override is NOT a usable fix on Ubuntu 26.04. It makes `playwright install` switch to the ubuntu24.04 fallback build, which downloads to 100% then HANGS at extraction (chrome binary never materializes; real machine + sandbox). Turned a 0.5s fast-fail into an install-blocking hang. The isolated proof (`ldd` + headless render) PASSED but used an already-extracted sibling build (rev 1228) — it masked the install-path hang in the real flow (rev 1208). **Sharpened lesson**: proving the binary launches in isolation is NOT proving the install path works — run the ACTUAL install command end-to-end (it must COMPLETE, not just "download resolves" nor "a binary launches"). The override technique stays valid in general, but the EXTRACTION/COMPLETE step is part of "does it work".
|
- **2026-06-23 CORRECTION (override REVERTED, commit b9c3937)**: the override is NOT a usable fix on Ubuntu 26.04. It makes `playwright install` switch to the ubuntu24.04 fallback build, which downloads to 100% then HANGS at extraction (chrome binary never materializes; real machine + sandbox). 0.5s fast-fail → install-blocking hang. Isolated proof (`ldd` + headless render) PASSED on an already-extracted sibling build (rev 1228) — masked the install-path hang in the real flow (rev 1208). **Sharpened lesson**: proving the binary launches in isolation is NOT proving the install path works — run the ACTUAL install command end-to-end (it must COMPLETE, not just "download resolves" nor "a binary launches"). Override technique stays valid in general; the EXTRACTION/COMPLETE step is part of "does it work".
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -647,11 +687,10 @@ rules:
|
|||||||
---
|
---
|
||||||
|
|
||||||
## LRN-040 — OS newer than a pinned tool supports = TWO distinct layers (version build + security policy)
|
## LRN-040 — OS newer than a pinned tool supports = TWO distinct layers (version build + security policy)
|
||||||
|
|
||||||
- **Date**: 2026-06-23
|
- **Date**: 2026-06-23
|
||||||
- **Context**: gstack browser on fresh Ubuntu 26.04. Layer 1 = Playwright 1.58.2 ships no browser build for 26.04 → install errors (the host-platform override "fixes" the error but its fallback build HANGS at extraction — dead end, [[BLK-008]]). Layer 2 = even with Playwright 1.61 (native 26.04 build that launches fine in isolation), the real browse path aborts "No usable sandbox" because Ubuntu 24.04+ restricts unprivileged user namespaces via AppArmor.
|
- **Context**: gstack browser on fresh Ubuntu 26.04. Layer 1 = Playwright 1.58.2 ships no browser build for 26.04 → install errors (the host-platform override "fixes" the error but its fallback build HANGS at extraction — dead end, [[BLK-008]]). Layer 2 = even with Playwright 1.61 (native 26.04 build that launches fine in isolation), the real browse path aborts "No usable sandbox" because Ubuntu 24.04+ restricts unprivileged user namespaces via AppArmor.
|
||||||
- **Pattern**: (a) bump the tool PAST the OS-support threshold — don't force the OS to look older (overrides/fallbacks are fragile; prove the install COMPLETES, not just that a binary launches). For a pinned submodule dep: `bun add X@latest` in the submodule, automatable in the installer, idempotent by grepping the dep's support list for the running OS tag before bumping. (b) SEPARATELY handle OS security hardening: Chromium needs `--no-sandbox` where `sysctl kernel.apparmor_restrict_unprivileged_userns=1`; gstack exposes `GSTACK_CHROMIUM_NO_SANDBOX=1` (#1562). Gate persistence on the sysctl, not an OS-version guess.
|
- **Pattern**: (a) bump the tool PAST the OS-support threshold — don't force the OS to look older (overrides/fallbacks are fragile; prove the install COMPLETES, not just that a binary launches). Pinned submodule dep: `bun add X@latest` in the submodule, automatable in the installer, idempotent via grep of the dep's support list for the running OS tag before bumping. (b) SEPARATELY handle OS security hardening: Chromium needs `--no-sandbox` where `sysctl kernel.apparmor_restrict_unprivileged_userns=1`; gstack exposes `GSTACK_CHROMIUM_NO_SANDBOX=1` (#1562). Gate persistence on the sysctl, not an OS-version guess.
|
||||||
- **Future application**: "tool X broke after an OS upgrade" → check BOTH (1) does X ship a build / support entry for the new OS (bump if not), and (2) does the new OS's hardening (userns/AppArmor/SELinux) block X at runtime (needs an opt-out flag). Fix one without the other and it still fails. Verify the FULL runtime path (drive a real page) — here the isolated `chromium.launch()` PASSED while the real `browse` path failed on the sandbox.
|
- **Future application**: "tool X broke after an OS upgrade" → check BOTH (1) does X ship a build / support entry for the new OS (bump if not), and (2) does the new OS's hardening (userns/AppArmor/SELinux) block X at runtime (needs an opt-out flag). Fix one without the other → still fails. Verify the FULL runtime path (drive a real page) — isolated `chromium.launch()` PASSED while the real `browse` path failed on the sandbox.
|
||||||
- **Reference**: `install-plugins.sh`, `.bashrc` `GSTACK_CHROMIUM_NO_SANDBOX=1`, gstack `browse/src/browser-manager.ts` `shouldEnableChromiumSandbox()`, commit 3b8ffb1. Linked to [[BDR-029]], [[BLK-008]], [[LRN-038]].
|
- **Reference**: `install-plugins.sh`, `.bashrc` `GSTACK_CHROMIUM_NO_SANDBOX=1`, gstack `browse/src/browser-manager.ts` `shouldEnableChromiumSandbox()`, commit 3b8ffb1. Linked to [[BDR-029]], [[BLK-008]], [[LRN-038]].
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -777,11 +816,10 @@ rules:
|
|||||||
- **Reference**: `lib/analyze-before-plan.md` (THE INVARIANT). Conditions [[LRN-046]], [[LRN-034]], [[BDR-033]]. See [[BDR-035]].
|
- **Reference**: `lib/analyze-before-plan.md` (THE INVARIANT). Conditions [[LRN-046]], [[LRN-034]], [[BDR-033]]. See [[BDR-035]].
|
||||||
|
|
||||||
## LRN-055 — Body `## ID —` headings are a drift-immune index; the maintained `## Index` table is not
|
## LRN-055 — Body `## ID —` headings are a drift-immune index; the maintained `## Index` table is not
|
||||||
|
|
||||||
- **Date**: 2026-06-26
|
- **Date**: 2026-06-26
|
||||||
- **Pattern**: When a registry keeps both per-entry `## ID — title` headings AND a hand-maintained `## Index` table, the Index DRIFTS (entries land in the body, the manual update lapses) while headings cannot (an entry IS its heading — 100% coverage by construction). Measured: decisions 11/34 (32%), learnings 21/52 (40%), blockers 2/9 (22%) missing from the Index — scattered in large blocks (e.g. decisions BDR-024–033 unindexed while the newer BDR-034 is), not an old/new split. The manual Index-update step is simply unreliable. Key any selector/scan off `grep '^## <PREFIX>-'`, never the convenience Index. Backfill (prune-memory passe D) = human-TOC hygiene, NOT a selector dependency.
|
- **Pattern**: When a registry keeps both per-entry `## ID — title` headings AND a hand-maintained `## Index` table, the Index DRIFTS (entries land in the body, the manual update lapses) while headings cannot (an entry IS its heading — 100% coverage by construction). Measured: decisions 11/34 (32%), learnings 21/52 (40%), blockers 2/9 (22%) missing from the Index — scattered in large blocks (e.g. decisions BDR-024–033 unindexed while the newer BDR-034 is), not an old/new split. Manual Index-update step unreliable. Key any selector/scan off `grep '^## <PREFIX>-'`, never the convenience Index. Backfill (prune-memory passe D) = human-TOC hygiene, NOT a selector dependency.
|
||||||
- **Context**: analyze-before-plan ([[BDR-035]]) two-pass. First instinct "reuse the Index capitalize maintains"; measuring the drift killed it — the convenient artifact was the unreliable one, the guaranteed one (headings) sat free.
|
- **Context**: analyze-before-plan ([[BDR-035]]) two-pass. First instinct "reuse the Index capitalize maintains"; measuring the drift killed it — convenient artifact unreliable, guaranteed one (headings) free.
|
||||||
- **Future application**: choosing a substrate to index/select over — prefer what the STRUCTURE guarantees over what a step PROMISES to maintain. Verify maintained-artifact completeness before depending on it.
|
- **Future application**: choosing a substrate to index/select over: prefer what the STRUCTURE guarantees over what a step PROMISES to maintain. Verify maintained-artifact completeness before depending on it.
|
||||||
- **Reference**: `lib/analyze-before-plan.md` (PASS 1). `skills/prune-memory` passe D. See [[BDR-035]].
|
- **Reference**: `lib/analyze-before-plan.md` (PASS 1). `skills/prune-memory` passe D. See [[BDR-035]].
|
||||||
|
|
||||||
## LRN-056 — `grep PAT dir/*.md` on an absent dir ERRORS (exit 2), it does not no-op → guard with `[ -d ]`
|
## LRN-056 — `grep PAT dir/*.md` on an absent dir ERRORS (exit 2), it does not no-op → guard with `[ -d ]`
|
||||||
@@ -793,11 +831,10 @@ rules:
|
|||||||
- **Reference**: `lib/analyze-before-plan.md` (PASS 1 guard). Sibling to [[LRN-051]] (exec-test tool behavior, never assume). See [[BDR-035]].
|
- **Reference**: `lib/analyze-before-plan.md` (PASS 1 guard). Sibling to [[LRN-051]] (exec-test tool behavior, never assume). See [[BDR-035]].
|
||||||
|
|
||||||
## LRN-057 — Match the consumption mechanism to the consumer (mechanical / external-cognitive / inline-cognitive)
|
## LRN-057 — Match the consumption mechanism to the consumer (mechanical / external-cognitive / inline-cognitive)
|
||||||
|
|
||||||
- **Date**: 2026-06-26
|
- **Date**: 2026-06-26
|
||||||
- **Pattern**: When a produced artifact must be CONSUMED downstream, the mechanism depends on the consumer: (a) MECHANICAL (git merge integrating a branch) — production on the shared substrate = consumption, automatic ([[BDR-034]]'s "commit before FINISH"); (b) EXTERNAL-COGNITIVE (an unmodifiable skill like `superpowers:brainstorming`) — "produced before" ≠ "consumed"; INJECT the artifact into the consumer's INPUT at the invocation boundary (orchestrator = adapter) + a RECONCILIATION gate that EXPOSES the disposition for review (not auto-detect); (c) INLINE-COGNITIVE (same agent reads then plans) — reader=planner, same context → natural consumption, just force the trace ([[LRN-053]]). Don't import (b)'s machinery where (c) suffices, nor assume (a)'s automatism when the consumer is cognitive.
|
- **Pattern**: When a produced artifact must be CONSUMED downstream, the mechanism depends on the consumer: (a) MECHANICAL (git merge integrating a branch) — production on the shared substrate = consumption, automatic ([[BDR-034]]'s "commit before FINISH"); (b) EXTERNAL-COGNITIVE (an unmodifiable skill like `superpowers:brainstorming`) — "produced before" ≠ "consumed"; INJECT the artifact into the consumer's INPUT at the invocation boundary (orchestrator = adapter) + a RECONCILIATION gate that EXPOSES the disposition for review (not auto-detect); (c) INLINE-COGNITIVE (same agent reads then plans) — reader=planner, same context → natural consumption, just force the trace ([[LRN-053]]). Don't import (b)'s machinery where (c) suffices, nor assume (a)'s automatism when the consumer is cognitive.
|
||||||
- **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. Asymmetry vs [[BDR-034]] (mechanical merge) = 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 reflexive import of 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
|
## LRN-058 — Same bug-class ≠ same fix: verify the twin shares the fix's PRECONDITION before replicating
|
||||||
@@ -825,10 +862,9 @@ rules:
|
|||||||
- **Reference**: [[BDR-036]], [[LRN-051]] (changed-paths filter), [[LRN-046]].
|
- **Reference**: [[BDR-036]], [[LRN-051]] (changed-paths filter), [[LRN-046]].
|
||||||
|
|
||||||
## LRN-061 — Runtime net proposed for an unwired skill → check the wiring first
|
## LRN-061 — Runtime net proposed for an unwired skill → check the wiring first
|
||||||
|
|
||||||
- **Date**: 2026-06-27
|
- **Date**: 2026-06-27
|
||||||
- **Pattern**: Tempted to build a runtime guard/hook/monitor that watches for a bad OUTCOME (memory written but uncommitted)? First ask if the outcome is a MISSING WIRING, not a behavioral lapse. A per-turn Stop-hook was proposed to catch "dirty memory" — but the cause was `/capitalize`+`/close` not calling the commit include (they predate it). Fix for an unwired skill = WIRE it (deterministic, zero-noise, at source); a monitor over a wiring hole pays RECURRING cost to detect a ONE-TIME omission, and a frequent ignored nag is itself a risk ([[LRN-047]]). **NOT "runtime nets are bad"** — the split is by DETERMINISM: a MISSING WIRING is deterministic → repair structurally; a genuinely NON-DETERMINISTIC aléa → a runtime net IS the right tool. Good counter-example: [[BDR-033]] anim-lib nudge — "will the user want motion?" is unknowable statically → a stateless 1-line suggestion is correct. Same determinism test as [[LRN-046]]/[[LRN-049]], applied to the build-or-not question.
|
- **Pattern**: Tempted to build a runtime guard/hook/monitor that watches for a bad OUTCOME (memory written but uncommitted)? First ask if the outcome is a MISSING WIRING, not a behavioral lapse. A per-turn Stop-hook was proposed to catch "dirty memory" — but the cause was `/capitalize`+`/close` not calling the commit include (they predate it). Fix for an unwired skill = WIRE it (deterministic, zero-noise, at source); a monitor over a wiring hole pays RECURRING cost for a ONE-TIME omission; a frequent ignored nag is itself a risk ([[LRN-047]]). **NOT "runtime nets are bad"** — the split is by DETERMINISM: a MISSING WIRING is deterministic → repair structurally; a genuinely NON-DETERMINISTIC aléa → a runtime net IS the right tool. Good counter-example: [[BDR-033]] anim-lib nudge — "will the user want motion?" is unknowable statically → a stateless 1-line suggestion is correct. Same determinism test as [[LRN-046]]/[[LRN-049]], applied to the build-or-not question.
|
||||||
- **Context**: deferred "v2 capitalize hook" ([[BDR-037]]). Read-phase killed it before code: git proved skills predate the include (oubli), memory already committed by hand 35×, orphans self-heal via `commit_memory`. The hook would've been disabled within an hour (frequent ignored nag).
|
- **Context**: deferred "v2 capitalize hook" ([[BDR-037]]). Read-phase killed it before code: git proved skills predate the include (oubli), memory committed by hand 35×, orphans self-heal via `commit_memory`. Hook would've been disabled within an hour (frequent ignored nag).
|
||||||
- **Future application**: any "build a hook/watcher/lint to catch when X isn't done" — first grep whether X is even WIRED at its source. Deterministic/structural gap (missing include/call) → fix structurally; reserve runtime nets for non-deterministic lapses, never to complete a rollout. Classify by determinism BEFORE building.
|
- **Future application**: any "build a hook/watcher/lint to catch when X isn't done" — first grep whether X is even WIRED at its source. Deterministic/structural gap (missing include/call) → fix structurally; reserve runtime nets for non-deterministic lapses, never to complete a rollout. Classify by determinism BEFORE building.
|
||||||
- **Reference**: [[BDR-037]], [[BDR-034]] (rollout this completes), [[BDR-033]] (the GOOD net — contrast). Conditions [[LRN-047]], [[LRN-049]], [[LRN-054]].
|
- **Reference**: [[BDR-037]], [[BDR-034]] (rollout this completes), [[BDR-033]] (the GOOD net — contrast). Conditions [[LRN-047]], [[LRN-049]], [[LRN-054]].
|
||||||
|
|
||||||
@@ -856,9 +892,9 @@ rules:
|
|||||||
- **future application**: any helper relying on `git status --porcelain` to detect changes — add a `git check-ignore` guard; a path that must persist but is ignored has to fail loud, not no-op.
|
- **future application**: any helper relying on `git status --porcelain` to detect changes — add a `git check-ignore` guard; a path that must persist but is ignored has to fail loud, not no-op.
|
||||||
|
|
||||||
## LRN-067 — a pipeline that looks 2-level can finish at the SAME level; a human-mediated step masks the collision until automated
|
## LRN-067 — a pipeline that looks 2-level can finish at the SAME level; a human-mediated step masks the collision until automated
|
||||||
- **pattern**: an orchestrator delegating to a sub-skill can LOOK two-level (sub assembles parts, orchestrator integrates) yet the sub's TERMINAL node operates at the SAME level as the orchestrator's own finish → double-integration. `subagent-driven-development` assembles tasks on ONE branch (no per-task sub-branches — true) BUT its last flowchart node IS `finishing-a-development-branch` = feature→base merge, the SAME act as the orchestrator's FINISH. init-project (STEP 8 SDD + STEP 11 finish) AND ship-feature (STEP 4 SDD + STEP 9 finish) BOTH invoked finish TWICE. Latent, not visibly broken: SDD's terminal finish is INTERACTIVE (menu → human picks "keep as-is"), so the human SILENTLY de-duplicated. Collision SURFACES the moment the orchestrator's finish becomes DETERMINISTIC (gitflow finish) → real double-merge. Fix = scope the sub-skill by instruction to stop before its terminal step (NO fork — the finish is a flowchart node the controller follows, not a script; verified by reading SDD's scripts). Pressure-test: RED agent chained the finish ("literal next node in the flowchart"); GREEN with the scope instruction stopped + returned.
|
- **pattern**: an orchestrator delegating to a sub-skill can LOOK two-level (sub assembles, orchestrator integrates) yet the sub's TERMINAL node operates at the SAME level as the orchestrator's finish → double-integration. `subagent-driven-development` assembles tasks on ONE branch (no per-task sub-branches — true) BUT its last flowchart node IS `finishing-a-development-branch` = feature→base merge, the SAME act as the orchestrator's FINISH. init-project (STEP 8 SDD + STEP 11 finish) AND ship-feature (STEP 4 SDD + STEP 9 finish) BOTH invoked finish TWICE. Latent, not visibly broken: SDD's terminal finish is INTERACTIVE (menu → human picks "keep as-is"), so the human SILENTLY de-duplicated. Collision SURFACES when the orchestrator's finish becomes DETERMINISTIC (gitflow finish) → real double-merge. Fix = scope the sub-skill by instruction to stop before its terminal step (NO fork — the finish is a flowchart node the controller follows, not a script; verified by reading SDD's scripts). Pressure-test: RED agent chained the finish ("literal next node in the flowchart"); GREEN with the scope instruction stopped + returned.
|
||||||
- **context**: gitflow chantier, wiring orchestrators onto `gitflow finish`. Mapping (premise #6) caught it by READING the real (SDD `SKILL.md` + `scripts/`) BEFORE coding — the seam-bug class `deploy` hit, caught earlier this time. Two human-gate backstops survive a missed instruction: SDD's interactive menu + the `gitflow finish` human gate ([[LRN-054]] — no oracle; deterministic layer carries the dangerous case).
|
- **context**: gitflow chantier, wiring orchestrators onto `gitflow finish`. Mapping (premise #6) caught it by READING the real (SDD `SKILL.md` + `scripts/`) BEFORE coding — seam-bug class `deploy` hit, caught earlier this time. Two human-gate backstops survive a missed instruction: SDD's interactive menu + the `gitflow finish` human gate ([[LRN-054]] — no oracle; deterministic layer carries the dangerous case).
|
||||||
- **future application**: before replacing an interactive/human-mediated step with a deterministic one, check whether a delegated sub-skill's TERMINAL step operates at the same level — the human gate may have been silently de-duplicating a double-action. Read the sub-skill's real flow (nodes + scripts), don't assume "distinct levels".
|
- **future application**: before replacing an interactive/human-mediated step with a deterministic one, check whether a delegated sub-skill's TERMINAL step operates at the same level — the human gate may have silently de-duplicated a double-action. Read the sub-skill's real flow (nodes + scripts), don't assume "distinct levels".
|
||||||
|
|
||||||
## LRN-068 — enforcement-bootstrap must be transactional: activate the guard LAST and gate it on the bootstrap commit succeeding
|
## LRN-068 — enforcement-bootstrap must be transactional: activate the guard LAST and gate it on the bootstrap commit succeeding
|
||||||
- **pattern**: a routine that BOTH installs an enforcement guard (pre-commit hook, branch protection, lock) AND makes a bootstrap commit must be transactional, else a partial run strands it. Two teeth: (a) precheck preconditions (git identity, clean tree) and fail LOUD before ANY mutation; (b) the guard-activation step must NOT run if the guarded bootstrap commit failed — order activation LAST and gate it on commit success. A `cmd_a || cmd_b` form SWALLOWS cmd_b's failure when a later stmt returns 0 → the failure never propagates; use explicit `if ! …; then … || return 1; fi`.
|
- **pattern**: a routine that BOTH installs an enforcement guard (pre-commit hook, branch protection, lock) AND makes a bootstrap commit must be transactional, else a partial run strands it. Two teeth: (a) precheck preconditions (git identity, clean tree) and fail LOUD before ANY mutation; (b) the guard-activation step must NOT run if the guarded bootstrap commit failed — order activation LAST and gate it on commit success. A `cmd_a || cmd_b` form SWALLOWS cmd_b's failure when a later stmt returns 0 → the failure never propagates; use explicit `if ! …; then … || return 1; fi`.
|
||||||
@@ -882,9 +918,9 @@ rules:
|
|||||||
- **future application**: any helper whose RETURN VALUE gates a downstream "success" — audit that EVERY fallible internal op propagates its failure, ESPECIALLY the load-bearing commit. `set -uo pipefail` without `-e` does NOT abort mid-function; an unchecked failing command followed by a returning-0 line exits 0 and lies. Check `cmd || other` forms, no-`-e` blocks, every "report success after the op" line. Test the partial-failure path (commit-blocked repo) → must fail loud, empty, non-zero.
|
- **future application**: any helper whose RETURN VALUE gates a downstream "success" — audit that EVERY fallible internal op propagates its failure, ESPECIALLY the load-bearing commit. `set -uo pipefail` without `-e` does NOT abort mid-function; an unchecked failing command followed by a returning-0 line exits 0 and lies. Check `cmd || other` forms, no-`-e` blocks, every "report success after the op" line. Test the partial-failure path (commit-blocked repo) → must fail loud, empty, non-zero.
|
||||||
|
|
||||||
## LRN-072 — a stranded-artifact bug can be fixed by NOT creating the artifact (negative diff), not by plumbing its commit
|
## LRN-072 — a stranded-artifact bug can be fixed by NOT creating the artifact (negative diff), not by plumbing its commit
|
||||||
- **pattern**: 3rd member of the post-FINISH-artifact class (memory, docs, GSD ROADMAP) — but UNLIKE the first two (real artifacts ALWAYS produced → couple a commit), the GSD artifact came from a SPECULATIVE, opt-in, rarely-used producer (init-project auto-bootstrapping a multi-session engine at project creation). The reflex fix (reorder + build `gsd-commit.sh` + tests) would have added machinery to faithfully commit an artifact nobody uses. The right fix was a NEGATIVE diff: delete the producer → orphan never created → bug dissolves, zero new code (BLK-011).
|
- **pattern**: 3rd member of the post-FINISH-artifact class (memory, docs, GSD ROADMAP) — but UNLIKE the first two (real artifacts ALWAYS produced → couple a commit), the GSD artifact came from a SPECULATIVE, opt-in, rarely-used producer (init-project auto-bootstrapping a multi-session engine at project creation). Reflex fix (reorder + build `gsd-commit.sh` + tests) = machinery to faithfully commit an artifact nobody uses. The right fix was a NEGATIVE diff: delete the producer → orphan never created → bug dissolves, zero new code (BLK-011).
|
||||||
- **the refutation that got there**: the framing "ROADMAP redundant with TODO" was WRONG (gsd ≫ roadmap = state machine/crash-recovery/cost/parallel/worktree; TODO ≠ gsd ROADMAP = different altitude + consumer). Reading REFUTED both premises, yet the CONCLUSION (remove the step) held for a STRONGER reason: speculatively scaffolding a heavy engine the sole user doesn't use, at creation, is bad per se. Right answer, reason corrected before engraving — change the QUESTION before changing the code.
|
- **the refutation that got there**: framing "ROADMAP redundant with TODO" WRONG (gsd ≫ roadmap = state machine/crash-recovery/cost/parallel/worktree; TODO ≠ gsd ROADMAP = different altitude + consumer). Reading REFUTED both premises, yet the CONCLUSION (remove the step) held for a STRONGER reason: speculatively scaffolding a heavy engine the sole user doesn't use, at creation, is bad per se. Right answer, reason corrected before engraving — change the QUESTION before changing the code.
|
||||||
- **future application**: a stranded / duplicated / uncommitted-artifact bug → BEFORE building machinery to handle the artifact, ask whether the step that PRODUCES it is actually used / wanted / non-speculative. Speculative or unused (esp. a personal/single-user repo) → DELETE the producer; the cleanest fix is the absent one. Distinguish speculative-at-creation (REMOVE) from deliberate-on-demand (KEEP). Family: [[BLK-010]], [[BLK-011]], [[BDR-036]].
|
- **future application**: stranded / duplicated / uncommitted-artifact bug → BEFORE building machinery for the artifact, ask whether the step that PRODUCES it is used / wanted / non-speculative. Speculative or unused (esp. personal/single-user repo) → DELETE the producer; cleanest fix = the absent one. Distinguish speculative-at-creation (REMOVE) from deliberate-on-demand (KEEP). Family: [[BLK-010]], [[BLK-011]], [[BDR-036]].
|
||||||
|
|
||||||
## LRN-073 — a skill's worked-example must use FICTIONAL ids, never live registry ids (they prime real-data behavior)
|
## LRN-073 — a skill's worked-example must use FICTIONAL ids, never live registry ids (they prime real-data behavior)
|
||||||
- **pattern**: prune-memory's STEP-2 plan example named real LRN-014 + LRN-016 ("merge these"). A real-data run merged exactly that pair — though they're COMPLEMENTARY (header-ids vs checkbox-CSS), a merge its own rule forbids. Example ids that match live entries, in context at audit time, PRIME the action: you can't tell "judged correctly" from "pattern-matched its own example".
|
- **pattern**: prune-memory's STEP-2 plan example named real LRN-014 + LRN-016 ("merge these"). A real-data run merged exactly that pair — though they're COMPLEMENTARY (header-ids vs checkbox-CSS), a merge its own rule forbids. Example ids that match live entries, in context at audit time, PRIME the action: you can't tell "judged correctly" from "pattern-matched its own example".
|
||||||
@@ -910,15 +946,15 @@ rules:
|
|||||||
## LRN-077 — test fixtures must carry NEUTRAL names (pass for the right reason)
|
## LRN-077 — test fixtures must carry NEUTRAL names (pass for the right reason)
|
||||||
- **Date**: 2026-06-30
|
- **Date**: 2026-06-30
|
||||||
- **pattern**: a baseline agent on a worktree named `wt-pre-reconcile` read "pre-reconcile" FROM THE DIR NAME and inferred staleness — reasoning for the WRONG reason (the name), not the right one (verify git). Fixtures + the GREEN test were re-frozen under NEUTRAL names so the engine reaches truth by querying git, never by reading a path hint.
|
- **pattern**: a baseline agent on a worktree named `wt-pre-reconcile` read "pre-reconcile" FROM THE DIR NAME and inferred staleness — reasoning for the WRONG reason (the name), not the right one (verify git). Fixtures + the GREEN test were re-frozen under NEUTRAL names so the engine reaches truth by querying git, never by reading a path hint.
|
||||||
- **meta — same symptom, distinct cause as [[LRN-074]]**: 074 = a COMMAND-ASSUMPTION (ugrep parsed `-9..` → false green); 077 = a LEAKY FIXTURE (name telegraphs the answer). Different mechanisms, SAME symptom: the test passes/fails for the wrong reason. Cross-cutting lesson = verify a test passes for the RIGHT reason, not merely that it passes — whether the false signal comes from an assumed command (074) or a leaky fixture (077).
|
- **meta — same symptom, distinct cause as [[LRN-074]]**: 074 = COMMAND-ASSUMPTION (ugrep parsed `-9..` → false green); 077 = LEAKY FIXTURE (name telegraphs the answer). Different mechanisms, SAME symptom: test passes/fails for the wrong reason. Cross-cutting lesson = verify a test passes for the RIGHT reason, not merely that it passes — whether the false signal comes from an assumed command (074) or a leaky fixture (077).
|
||||||
- **future application**: name fixtures/paths neutrally; for any green, ask "did it pass because the subject did the work, or because something leaked the answer?"
|
- **future application**: name fixtures/paths neutrally; for any green, ask "did it pass because the subject did the work, or because something leaked the answer?"
|
||||||
- **corroboration 2026-07-02 (T6c)**: 3rd family member — test truth borrowed from TRANSIENT env state. run-reconcile T6c asserted `$MEM/../skills/darwin-skill` = `.claude/skills/` (the [[LRN-042]] parasite dir), not canonical `skills/`; born green because the parasite still existed, red since the same-day cleanup, unnoticed until the 2026-07-02 audit re-ran the suite ([[EVAL-011]]'s "20/20" silently 19/1 for 2 days). Oracles target CANONICAL paths (never derived `X/../Y`); re-run suites after ANY env cleanup tests may have silently depended on; "green at build" ≠ "green now".
|
- **corroboration 2026-07-02 (T6c)**: 3rd family member — test truth borrowed from TRANSIENT env state. run-reconcile T6c asserted `$MEM/../skills/darwin-skill` = `.claude/skills/` (the [[LRN-042]] parasite dir), not canonical `skills/`; born green because the parasite still existed, red since the same-day cleanup, unnoticed until the 2026-07-02 audit re-ran the suite ([[EVAL-011]]'s "20/20" silently 19/1 for 2 days). Oracles target CANONICAL paths (never derived `X/../Y`); re-run suites after ANY env cleanup tests may have silently depended on; "green at build" ≠ "green now".
|
||||||
|
|
||||||
## LRN-078 — semver number DERIVES from the change nature; "breaking" = requires a migration
|
## LRN-078 — semver number DERIVES from the change nature; "breaking" = requires a migration
|
||||||
- **Date**: 2026-06-30
|
- **Date**: 2026-06-30
|
||||||
- **pattern**: framing a release as "it's 4.0.0 → find the breaking changes to justify it" is backwards. Semver runs the other way: the number FOLLOWS the nature of the changes. The real question = "is there a breaking change?", not "how do I justify the target". Solo / mono-user repo, no public API ⇒ "breaking" = casse mon propre usage / EXIGE une migration de ma part.
|
- **pattern**: framing a release as "it's 4.0.0 → find the breaking changes to justify it" is backwards; the number FOLLOWS the nature of the changes. The real question = "is there a breaking change?", not "how do I justify the target". Solo / mono-user repo, no public API ⇒ "breaking" = casse mon propre usage / EXIGE une migration de ma part.
|
||||||
- **applied (v4.0.0)**: gitflow universal = a TRUE breaking workflow change (master→main, mandatory branches, hook, 6-repo migration) → MAJOR on its own. caveman removal = VERIFIED nothing invoked it (grep: only the kept memory format-rule + frozen fixtures, settings/hooks clean) → a clean `### Removed` (capability gone, nothing breaks, no migration), NOT breaking. The MAJOR rests on gitflow alone; don't mislabel a removal as breaking.
|
- **applied (v4.0.0)**: gitflow universal = TRUE breaking workflow change (master→main, mandatory branches, hook, 6-repo migration) → MAJOR on its own. caveman removal = VERIFIED nothing invoked it (grep: only the kept memory format-rule + frozen fixtures, settings/hooks clean) → a clean `### Removed` (capability gone, nothing breaks, no migration), NOT breaking. The MAJOR rests on gitflow alone; don't mislabel a removal as breaking.
|
||||||
- **future application**: pick MAJOR/MINOR/PATCH from the changes, then the lineage gives the digits. Verify "does X actually break / require migration?" from the refs (grep), not from the size of the change or the desire for a round number.
|
- **future application**: pick MAJOR/MINOR/PATCH from the changes; the lineage gives the digits. Verify "does X actually break / require migration?" from the refs (grep), not from the size of the change or the desire for a round number.
|
||||||
|
|
||||||
## LRN-079 — orchestrator-skill TDD: replay the flow on a throwaway repo, RED = flow minus the new step
|
## LRN-079 — orchestrator-skill TDD: replay the flow on a throwaway repo, RED = flow minus the new step
|
||||||
- **Date**: 2026-06-30
|
- **Date**: 2026-06-30
|
||||||
@@ -949,16 +985,15 @@ rules:
|
|||||||
|
|
||||||
## LRN-083 — Subagents are an INVALID instrument for measuring MAIN-LOOP spontaneous routing
|
## LRN-083 — Subagents are an INVALID instrument for measuring MAIN-LOOP spontaneous routing
|
||||||
- **Date**: 2026-06-30
|
- **Date**: 2026-06-30
|
||||||
- **pattern**: to measure whether the MAIN loop self-invokes a skill on implicit intent, dispatched subagents are non-discriminating — SUBAGENT-STOP tells them to SKIP the L1 routing mandate, and a delegated-execute framing suppresses meta-routing → they hand-do the task regardless of how strong/weak the main-loop prose is. Result pins to the no-route FLOOR (artifact, not signal). Complement of [[LRN-028]] (there subagents OVER-saw installed skills, invalidating a no-skill baseline; here they UNDER-route, invalidating a routing-measurement) — both = subagent ≠ main-loop condition.
|
- **pattern**: measuring whether the MAIN loop self-invokes a skill on implicit intent: dispatched subagents are non-discriminating — SUBAGENT-STOP tells them to SKIP the L1 routing mandate, delegated-execute framing suppresses meta-routing → they hand-do the task regardless of main-loop prose strength. Result pins to the no-route FLOOR (artifact, not signal). Complement of [[LRN-028]] (there subagents OVER-saw installed skills, invalidating a no-skill baseline; here they UNDER-route, invalidating a routing-measurement) — both = subagent ≠ main-loop condition.
|
||||||
- **why it matters**: a 0/N subagent RED reads as "under-triggers → build the chantier" but is the [[LRN-028]] trap — the instrument can't tell strong prose from weak. Concluding from it = a pass/fail for the WRONG reason ([[LRN-074]]/[[LRN-077]]).
|
- **why it matters**: a 0/N subagent RED reads as "under-triggers → build the chantier" but is the [[LRN-028]] trap — the instrument can't tell strong prose from weak. Concluding from it = pass/fail for the WRONG reason ([[LRN-074]]/[[LRN-077]]).
|
||||||
- **context**: 2026-06-30 auto-skill-dispatch RED. 6 subagents on toy implicit-intent tasks → 0/6 routed → RETIRED as non-discriminating, NOT reported as a number. Reframed; measured instead in REAL fresh main-loop sessions.
|
- **context**: 2026-06-30 auto-skill-dispatch RED. 6 subagents on toy implicit-intent tasks → 0/6 routed → RETIRED as non-discriminating, NOT reported as a number. Reframed; measured in REAL fresh main-loop sessions.
|
||||||
- **future application**: measure main-loop spontaneous routing/discernment in FRESH main-loop sessions (full L0–L4, no SUBAGENT-STOP, real user-turn). Observable instrument = the HUMAN typing the prompts + watching live — cron/schedule-spawned fresh sessions are the right CONDITION but UNOBSERVABLE to the orchestrator (they notify the owner, not the dispatcher), so they can't be the measurement vehicle. Never substitute a subagent for a fresh session in a routing RED. See [[LRN-028]], [[LRN-075]], [[LRN-080]].
|
- **future application**: measure main-loop spontaneous routing/discernment in FRESH main-loop sessions (full L0–L4, no SUBAGENT-STOP, real user-turn). Observable instrument = the HUMAN typing the prompts + watching live — cron/schedule-spawned fresh sessions are the right CONDITION but UNOBSERVABLE to the orchestrator (they notify the owner, not the dispatcher), so they can't be the measurement vehicle. Never substitute a subagent for a fresh session in a routing RED. See [[LRN-028]], [[LRN-075]], [[LRN-080]].
|
||||||
|
|
||||||
## LRN-084 — A protection hook enforces PROD safety, not the full branch-flow — the exemption masked the rule-vs-guard divergence
|
## LRN-084 — A protection hook enforces PROD safety, not the full branch-flow — the exemption masked the rule-vs-guard divergence
|
||||||
|
|
||||||
- **Date**: 2026-07-01
|
- **Date**: 2026-07-01
|
||||||
- **pattern**: the gitflow pre-commit hook is a PROTECTION guard (block code on main/develop), NOT a flow enforcer. It exempts `.claude/**` and can only test "on a protected base" — it can NEVER verify "branched FROM develop" (no base knowledge). So "every change via a branch from develop" is only HALF-encoded by the hook; the base half lives solely upstream in `gitflow_start`. The exemption is scoped to the SIDE-CAR ([[BDR-034]]); it has no branch to follow when memory IS the work → standalone memory fell back to `main`.
|
- **pattern**: the gitflow pre-commit hook is a PROTECTION guard (block code on main/develop), NOT a flow enforcer. It exempts `.claude/**` and can only test "on a protected base" — it can NEVER verify "branched FROM develop" (no base knowledge). "Every change via a branch from develop" is only HALF-encoded by the hook; the base half lives upstream in `gitflow_start`. The exemption is scoped to the SIDE-CAR ([[BDR-034]]); it has no branch to follow when memory IS the work → standalone memory fell back to `main`.
|
||||||
- **why it matters**: a multi-repo raccord committed 5 `chore(memory)` direct on `main` and NOTHING flagged it — nothing was violated, the exemption worked as designed. The divergence was guard (declares PROD protection) vs intended rule (all via branch); the exemption MASKED it, the raccord revealed it by violating the unencoded half. A guard encoding only PART of the intent reads as full enforcement — a false-green.
|
- **why it matters**: multi-repo raccord committed 5 `chore(memory)` direct on `main`, NOTHING flagged it — nothing violated, exemption worked as designed. Divergence = guard (declares PROD protection) vs intended rule (all via branch); exemption MASKED it, raccord revealed it by violating the unencoded half. A guard encoding only PART of the intent reads as full enforcement — a false-green.
|
||||||
- **future application**: when a guard exempts a class or checks one predicate, ask what it does NOT encode and whether a human leans on it for MORE than it enforces. Enforce the unencoded half where it actually lives (the aiguillage at skill start, [[BDR-045]]), do not push it into a guard that structurally can't hold it. Verify the guard's real scope against the rule's full scope before trusting "it would have caught it." See [[BDR-034]], [[BDR-045]], [[LRN-034]].
|
- **future application**: when a guard exempts a class or checks one predicate, ask what it does NOT encode and whether a human leans on it for MORE than it enforces. Enforce the unencoded half where it actually lives (the aiguillage at skill start, [[BDR-045]]), do not push it into a guard that structurally can't hold it. Verify the guard's real scope against the rule's full scope before trusting "it would have caught it." See [[BDR-034]], [[BDR-045]], [[LRN-034]].
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -1036,16 +1071,16 @@ rules:
|
|||||||
- **cousin**: [[LRN-047]] noisy gate = ignored; [[LRN-077]] non-deterministic gate; conditions [[BDR-048]].
|
- **cousin**: [[LRN-047]] noisy gate = ignored; [[LRN-077]] non-deterministic gate; conditions [[BDR-048]].
|
||||||
|
|
||||||
## LRN-095 — Orthogonal gates don't contaminate: a conformity check must pass correct-but-insecure code
|
## LRN-095 — Orthogonal gates don't contaminate: a conformity check must pass correct-but-insecure code
|
||||||
- **pattern**: when a pipeline has distinct gates (request-conformity, security), each judges ONLY its dimension. A conformity verifier must return CONFORME on code that is correct-but-insecure — the vuln is the SECURITY gate's job, not a conformity gap. Proven live: a `get_item` feature satisfying its contract but carrying a `%`-interpolation SQLi → verifier CONFORME, security-auditor BLOCK(1). Fusing the two into one "quality" gate makes each worse: the conformity check starts hunting vulns (scope creep, misses conformity), the security check starts judging feature-completeness (dilutes).
|
- **pattern**: pipeline with distinct gates (request-conformity, security): each judges ONLY its dimension. A conformity verifier must return CONFORME on code that is correct-but-insecure — the vuln is the SECURITY gate's job, not a conformity gap. Proven live: `get_item` feature satisfying its contract with a `%`-interpolation SQLi → verifier CONFORME, security-auditor BLOCK(1). Fusing both into one "quality" gate makes each worse: conformity check hunts vulns (scope creep, misses conformity), security check judges feature-completeness (dilutes).
|
||||||
- **context**: lot 4 verify-secure-loop dogfood 2026-07-03. The orthogonality is WHY the order invariant matters (re-verify request before re-scan security) — two independent axes re-checked independently.
|
- **context**: lot 4 verify-secure-loop dogfood 2026-07-03. Orthogonality is WHY the order invariant matters (re-verify request before re-scan security): two independent axes re-checked independently.
|
||||||
- **future application**: any multi-dimension gate (review lenses, verify+audit, correctness+perf) — keep each gate single-axis and let a finding on axis B pass axis A's gate; compose verdicts in the orchestrator, don't merge the judges.
|
- **future application**: any multi-dimension gate (review lenses, verify+audit, correctness+perf) — keep each gate single-axis and let a finding on axis B pass axis A's gate; compose verdicts in the orchestrator, don't merge the judges.
|
||||||
- **cousin**: [[BDR-050]] the pipeline; [[BDR-049]] fresh verifier; conditions [[LRN-083]].
|
- **cousin**: [[BDR-050]] the pipeline; [[BDR-049]] fresh verifier; conditions [[LRN-083]].
|
||||||
|
|
||||||
## LRN-096 — A backstop is code: prove it can FAIL (flip-test) before trusting its green
|
## LRN-096 — A backstop is code: prove it can FAIL (flip-test) before trusting its green
|
||||||
- **pattern**: a deterministic guard built to replace a forgettable advisory is itself code, and an UNPROVEN guard is a vacuous guard — [[LRN-048]] (a pass must prove it looked) applied to guards themselves. The LRN-093 backstop (refuse `\n` in grep/tf patterns) shipped with a regex requiring whitespace before `tf` → it silently MISSED `tf` at line start (exactly where the real locks sit). A flip-test (feed the guard a KNOWN offender, assert it bites) caught the hole; without it the guard would have green-lit the very class it was built to kill. So: a flip-test is MANDATORY at guard creation, part of the guard, not optional QA.
|
- **pattern**: deterministic guard replacing a forgettable advisory is itself code; UNPROVEN guard = vacuous guard — [[LRN-048]] (a pass must prove it looked) applied to guards. LRN-093 backstop (refuse `\n` in grep/tf patterns) shipped with a regex requiring whitespace before `tf` → silently MISSED `tf` at line start (where the real locks sit). Flip-test (feed the guard a KNOWN offender, assert it bites) caught the hole; without it the guard would have green-lit the very class it was built to kill. So: a flip-test is MANDATORY at guard creation, part of the guard, not optional QA.
|
||||||
- **why it matters**: the whole point of a backstop is that it fires on the bad case; a guard that can't fail proves nothing and is WORSE than the advisory it replaced (false confidence). The advisory→backstop move ([[LRN-047]] [[LRN-091]], own doctrine) is only sound if the backstop is itself verified against a real miss.
|
- **why it matters**: the whole point of a backstop is that it fires on the bad case; a guard that can't fail proves nothing and is WORSE than the advisory it replaced (false confidence). Advisory→backstop move ([[LRN-047]] [[LRN-091]]) is sound only if the backstop is verified against a real miss.
|
||||||
- **context**: lot 5 `lib/tests/no-vacuous-locks.test.sh` 2026-07-04. Built the guard, its flip-test RED'd (regex too weak, missed line-start `tf`), fixed the regex, flip-test green. The guard now ships WITH the flip-test inline so it self-proves on every run.
|
- **context**: lot 5 `lib/tests/no-vacuous-locks.test.sh` 2026-07-04. Built the guard, flip-test RED'd (regex too weak, missed line-start `tf`), fixed the regex, flip-test green. Guard ships WITH the flip-test inline, self-proves on every run.
|
||||||
- **future application**: building any guard/lint/census/backstop — bundle a flip-test (a synthetic offender the guard must catch) in the same file; a guard whose failure path was never exercised is untrusted. Corroborates [[LRN-047]]/[[LRN-091]] (advisory→deterministic) — this is the *quality bar* on the deterministic replacement.
|
- **future application**: building any guard/lint/census/backstop — bundle a flip-test (a synthetic offender the guard must catch) in the same file; a guard whose failure path was never exercised is untrusted. Corroborates [[LRN-047]]/[[LRN-091]] (advisory→deterministic): the *quality bar* on the deterministic replacement.
|
||||||
- **cousin**: [[LRN-048]] prove it looked; [[LRN-093]] the class this guards; [[LRN-046]] deterministic-oracle discipline.
|
- **cousin**: [[LRN-048]] prove it looked; [[LRN-093]] the class this guards; [[LRN-046]] deterministic-oracle discipline.
|
||||||
|
|
||||||
## LRN-097 — Community blog pattern ≠ official feature: verify against docs before building infra
|
## LRN-097 — Community blog pattern ≠ official feature: verify against docs before building infra
|
||||||
@@ -1089,9 +1124,8 @@ rules:
|
|||||||
- **backmerge**: from release/1.0.0 (74d3804) — 2026-07-08 review remediation A3.
|
- **backmerge**: from release/1.0.0 (74d3804) — 2026-07-08 review remediation A3.
|
||||||
|
|
||||||
## LRN-102 — Deliverable text before a tool call may never render: the turn's FINAL text is the only guaranteed display
|
## LRN-102 — Deliverable text before a tool call may never render: the turn's FINAL text is the only guaranteed display
|
||||||
|
- **pattern**: /deploy hand-back printed the full checklist, then called AskUserQuestion. The user saw ONLY the question UI — the checklist never reached them ("là on a rien, je dois ouvrir le fichier"). Harness reliably renders only the LAST text of a turn; text before a tool call can be swallowed by the tool UI.
|
||||||
- **pattern**: /deploy hand-back printed the full checklist in the assistant message, then called AskUserQuestion. The user saw ONLY the question UI — the checklist never reached them ("là on a rien, je dois ouvrir le fichier"). The harness renders reliably only the LAST text of a turn; text between/before tool calls can be swallowed by the tool UI.
|
- **why**: conversational deliverable (commands to copy-paste, a report) fails silently if any tool call follows the print — user sees "nothing displayed" while the transcript contains it. Structural fix: the deliverable IS the turn's final text; collect answers BEFORE printing, or let the reply arrive as the next user message.
|
||||||
- **why**: a skill whose deliverable is conversational (commands to copy-paste, a report) fails silently if any tool call follows the print — the user experiences "nothing displayed" while the transcript technically contains it. Structural fix: the deliverable IS the turn's final text; collect answers BEFORE printing, or let the reply arrive as the next user message.
|
|
||||||
- **context**: 2026-07-05 /deploy run 2 (bchanot-cv). Skill patched same turn: checklist display-only (no NEXT.sh file at all — user: throwaway once deployed) + hand-back ends the turn, no tool call after.
|
- **context**: 2026-07-05 /deploy run 2 (bchanot-cv). Skill patched same turn: checklist display-only (no NEXT.sh file at all — user: throwaway once deployed) + hand-back ends the turn, no tool call after.
|
||||||
- **future application**: designing any skill/flow output meant to be read+used from the conversation — put it LAST; never sandwich a deliverable between tool calls; prefer plain-text report requests over blocking question tools after a deliverable.
|
- **future application**: designing any skill/flow output meant to be read+used from the conversation — put it LAST; never sandwich a deliverable between tool calls; prefer plain-text report requests over blocking question tools after a deliverable.
|
||||||
- **cousin**: [[LRN-100]] same skill lineage; CLAUDE.md communication doctrine (final message carries everything).
|
- **cousin**: [[LRN-100]] same skill lineage; CLAUDE.md communication doctrine (final message carries everything).
|
||||||
@@ -1153,10 +1187,9 @@ rules:
|
|||||||
- **cousin**: [[BDR-058]] (this job's fix), darwin-skill's OVERSCOPED git-commit finding (job8 report — 3rd-party code, not patched, accepted risk under human-checkpoint gating, twin of [[LRN-105]]'s no-execute mandate for OUR read-only audits).
|
- **cousin**: [[BDR-058]] (this job's fix), darwin-skill's OVERSCOPED git-commit finding (job8 report — 3rd-party code, not patched, accepted risk under human-checkpoint gating, twin of [[LRN-105]]'s no-execute mandate for OUR read-only audits).
|
||||||
|
|
||||||
## LRN-110 — magic MCP `component_builder`'s local callback server = unauthenticated prompt-injection channel
|
## LRN-110 — magic MCP `component_builder`'s local callback server = unauthenticated prompt-injection channel
|
||||||
|
- **context**: job8 audit read `dist/utils/callback-server.js:36` (+ `create-ui.js:35-38`) in the installed `@21st-dev/magic` package. `21st_magic_component_builder` opens a plain HTTP server on `127.0.0.1:9221+`, `Access-Control-Allow-Origin: *`, no token/origin check, staying open up to 10 minutes per call. Any POST body to `/data` is injected VERBATIM into the tool result the model consumes — any local process or open browser tab on the machine can win the race against the legitimate browser hand-back.
|
||||||
- **context**: job8 audit read `dist/utils/callback-server.js:36` (+ `create-ui.js:35-38`) in the installed `@21st-dev/magic` package. `21st_magic_component_builder` opens a plain HTTP server on `127.0.0.1:9221+`, `Access-Control-Allow-Origin: *`, no token/origin check, staying open up to 10 minutes per call. Whatever body a POST to `/data` carries gets injected VERBATIM into the tool result the model then consumes — any local process or an open browser tab on the same machine can win the race against the legitimate browser hand-back.
|
|
||||||
- **future application**: this is in the third-party package's code, not our config — don't try to patch a vendored/npx-installed dependency. The only real lever is on OUR side of the boundary: never allowlist a tool with this shape, keep it `ask`-gated so a human sees every invocation (see [[BDR-059]]). Applies to any MCP tool whose implementation opens a listener to receive async results, not just this one — check the listener's auth/origin scoping when auditing MCP server code, the tool's *description* text tells you nothing about it.
|
- **future application**: this is in the third-party package's code, not our config — don't try to patch a vendored/npx-installed dependency. The only real lever is on OUR side of the boundary: never allowlist a tool with this shape, keep it `ask`-gated so a human sees every invocation (see [[BDR-059]]). Applies to any MCP tool whose implementation opens a listener to receive async results, not just this one — check the listener's auth/origin scoping when auditing MCP server code, the tool's *description* text tells you nothing about it.
|
||||||
- **cousin**: [[BDR-059]] (the settings fix), [[LRN-111]] (why the allowlist stays empty), job8 report §2 surface 1 finding A#0.
|
- **cousin**: [[BDR-059]] (the settings fix), [[LRN-111]] (why the allowlist stays empty), job8 report §2 surface 1 finding A#0. Magic MCP retired 2026-09-22 ([[BDR-093]]).
|
||||||
|
|
||||||
## LRN-111 — empty allowlist is a valid, deliberate posture when real usage is zero, not a leftover gap
|
## LRN-111 — empty allowlist is a valid, deliberate posture when real usage is zero, not a leftover gap
|
||||||
|
|
||||||
@@ -1225,9 +1258,9 @@ rules:
|
|||||||
- **cousin**: [[LRN-119]] (same GSC+CrUX build); SDD skill's own "never HEAD~1" warning (same base-selection bug class).
|
- **cousin**: [[LRN-119]] (same GSC+CrUX build); SDD skill's own "never HEAD~1" warning (same base-selection bug class).
|
||||||
|
|
||||||
## LRN-121 — Shell allowlist validation: `grep -Eq` is fragile; use a whole-string POSIX `case`
|
## LRN-121 — Shell allowlist validation: `grep -Eq` is fragile; use a whole-string POSIX `case`
|
||||||
- **pattern**: guarding a user-supplied label to shell-safe ASCII with `printf '%s' "$v" | grep -Eq '^[A-Za-z0-9._-]+$'` failed 3 adversarial gate passes in a row: (1) command-injection framing (label interpolated into an agent-composed Bash line); (2) parser differential — the guard pre-scanned argv for the literal token `--label` while the downstream `argparse` ALSO accepts `--label=v` and abbreviations (`--labe`, `allow_abbrev=True`), so those forms reached the parser unchecked; (3) `grep -q` matches PER LINE, so a label with an embedded newline (`ok\nrm -rf`) passes because its FIRST line matches. Fix = replace the whole mechanism, don't patch again: `_label_safe() ( LC_ALL=C; case "$1" in ''|[!A-Za-z0-9]*|*[!A-Za-z0-9._-]*) exit 1;; esac )` — POSIX `case`, whole-string, C-locale subshell. No grep (no per-line), no regex, no second grammar to differ from; a newline is just a non-allowed byte caught by `*[!...]*`; `LC_ALL=C` stops UTF-8 collation widening `[A-Za-z0-9]` to homoglyphs (U+FF11, Kelvin U+212A).
|
- **pattern**: guarding a user-supplied label to shell-safe ASCII with `printf '%s' "$v" | grep -Eq '^[A-Za-z0-9._-]+$'` failed 3 adversarial gate passes: (1) command-injection framing (label interpolated into an agent-composed Bash line); (2) parser differential — the guard pre-scanned argv for the literal `--label` while the downstream `argparse` ALSO accepts `--label=v` and abbreviations (`--labe`, `allow_abbrev=True`), those forms reached the parser unchecked; (3) `grep -q` matches PER LINE, a label with an embedded newline (`ok\nrm -rf`) passes on its FIRST line. Fix = replace the whole mechanism, don't patch again: `_label_safe() ( LC_ALL=C; case "$1" in ''|[!A-Za-z0-9]*|*[!A-Za-z0-9._-]*) exit 1;; esac )` — POSIX `case`, whole-string, C-locale subshell. No grep (no per-line), no regex, no second grammar to differ from; a newline is just a non-allowed byte caught by `*[!...]*`; `LC_ALL=C` stops UTF-8 collation widening `[A-Za-z0-9]` to homoglyphs (U+FF11, Kelvin U+212A).
|
||||||
- **why it matters**: three distinct bypasses of the SAME guard = the approach was wrong, not each patch. `grep`'s line-orientation + locale-sensitive ranges, plus argv-prescan-vs-real-parser grammar drift, are the three classic ways an allowlist "passes" a string it shouldn't. Whole-string `case` in C locale closes all three at once. These were defense-in-depth (downstream used `"$2"`/`"$@"`/JSON-key, never `sh -c`/`eval` → not exploitable in the real exec chain) — but the backstop still took a categorical rewrite, and 3 security-gate BLOCKs to get there.
|
- **why it matters**: three distinct bypasses of the SAME guard = the approach was wrong, not each patch. `grep`'s line-orientation + locale-sensitive ranges, plus argv-prescan-vs-real-parser grammar drift, are the three classic ways an allowlist "passes" a string it shouldn't. Whole-string `case` in C locale closes all three at once. These were defense-in-depth (downstream used `"$2"`/`"$@"`/JSON-key, never `sh -c`/`eval` → not exploitable in the real exec chain) — but the backstop still took a categorical rewrite, and 3 security-gate BLOCKs to get there.
|
||||||
- **future application**: validate shell input WHOLE-STRING (`case` or bash `[[ =~ ]]`), never `grep -q` (per-line). Set `LC_ALL=C` for byte-wise ranges. A guard that pre-scans argv must be STRICTER than the downstream parser (reject `=`-joined/abbrev) or validate post-parse against the value the parser settled on. When a fix is bypassed twice → STOP patching, replace the mechanism (re-plan, not whack-a-mole).
|
- **future application**: validate shell input WHOLE-STRING (`case` or bash `[[ =~ ]]`), never `grep -q` (per-line). Set `LC_ALL=C` for byte-wise ranges. Argv pre-scan guard must be STRICTER than the downstream parser (reject `=`-joined/abbrev) or validate post-parse against the value the parser settled on. When a fix is bypassed twice → STOP patching, replace the mechanism (re-plan, not whack-a-mole).
|
||||||
- **cousin**: [[LRN-119]] (fail-open engine this hardens), [[BDR-063]] (token store whose labels these guard), [[LRN-045]] (renaming-command leak-guard regexes — same charset-guard family).
|
- **cousin**: [[LRN-119]] (fail-open engine this hardens), [[BDR-063]] (token store whose labels these guard), [[LRN-045]] (renaming-command leak-guard regexes — same charset-guard family).
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -1265,10 +1298,9 @@ rules:
|
|||||||
- **cousin**: [[BDR-066]] (model routing: reflection/audit big, execution sonnet), [[LRN-113]] (consumer-staleness sweep on a pattern fix).
|
- **cousin**: [[BDR-066]] (model routing: reflection/audit big, execution sonnet), [[LRN-113]] (consumer-staleness sweep on a pattern fix).
|
||||||
|
|
||||||
## LRN-126 — splitting a monolith agent severs every IMPLICIT data path; forward each consumed field through the handoff contract
|
## LRN-126 — splitting a monolith agent severs every IMPLICIT data path; forward each consumed field through the handoff contract
|
||||||
|
- **pattern**: wave-4 split (client-handover-writer monolith → reflection parent + sonnet doc-writer child) silently dropped 2 inputs the extracted STEPs consumed. `DEPLOY_HINTS` (detected in parent STEP 2, consumed by child STEP 14) + `--skip-seo` flag (parsed from `$ARGUMENTS`, gated child STEP 13) worked in the monolith by shared scope; after the split they were dead — never added to the PACKAGE. Child rendered a §8 without platform tailoring; `--skip-seo` became a silent no-op. Caught only by the opus whole-branch review, not the census.
|
||||||
- **pattern**: wave-4 redaction-only split (client-handover-writer monolith → reflection-parent + sonnet doc-writer child) silently dropped 2 inputs the extracted STEPs consumed. `DEPLOY_HINTS` (detected in parent STEP 2, consumed by child STEP 14) + `--skip-seo` flag (parsed from `$ARGUMENTS`, gated child STEP 13) worked in the monolith by shared scope; after the split they were dead — never added to the PACKAGE. Child rendered a §8 without platform tailoring; `--skip-seo` became a silent no-op. Caught only by the opus whole-branch review, not the census.
|
- **why**: monolith: `$ARGUMENTS`, detected vars, STEP-N side-outputs share one scope, later STEPs read them free. Split turns each free read into a data path that MUST cross the parent→child contract explicitly; every implicit read is a severed wire unless forwarded.
|
||||||
- **why**: in a monolith, `$ARGUMENTS`, detected vars, and STEP-N side-outputs are all in one scope — a later STEP reads them for free. The split turns that free read into a data path that MUST cross the parent→child contract explicitly. Every implicit read becomes a severed wire unless forwarded.
|
- **future application**: splitting an agent: enumerate EVERY field the child reads (grep child for `PACKAGE.`, bare var names, `$ARGUMENTS` flags), diff against what the parent SETS before dispatch. Any child-consumed field the parent never populates = severed path = renders a hole or a silent no-op. A census that checks shape (model pin, gate-free) will NOT catch this — needs a data-flow read.
|
||||||
- **future application**: when splitting an agent, enumerate EVERY field the child reads (grep child for its input vocabulary — `PACKAGE.`, bare var names, `$ARGUMENTS` flags) and diff against what the parent SETS before dispatch. Any child-consumed field the parent never populates = severed path = renders a hole or a silent no-op. A census that checks shape (model pin, gate-free) will NOT catch this — needs a data-flow read.
|
|
||||||
- **cousin**: [[LRN-125]] (route consumer to right tier on a split), [[BDR-066]] (reflection/execution split), [[LRN-113]] (sweep ALL consumers). Distinct: 113/125 = WHICH agent/tier a consumer routes to; this = WHICH fields must cross the contract.
|
- **cousin**: [[LRN-125]] (route consumer to right tier on a split), [[BDR-066]] (reflection/execution split), [[LRN-113]] (sweep ALL consumers). Distinct: 113/125 = WHICH agent/tier a consumer routes to; this = WHICH fields must cross the contract.
|
||||||
|
|
||||||
## LRN-127 — SDD implementers must not run destructive git ops on files outside their task scope
|
## LRN-127 — SDD implementers must not run destructive git ops on files outside their task scope
|
||||||
@@ -1315,42 +1347,16 @@ rules:
|
|||||||
- **future**: the system already HAD the invariant (code-ceiling, §14 Annexe) but applied it in spots. Generalised it. A false signal is worse than a declared gap — the 4 features KILLED at measurement (B1/B2/B3/W2) beat 4 false-signal features. See [[LRN-131]]/[[LRN-132]] (same session, the verification discipline that feeds it).
|
- **future**: the system already HAD the invariant (code-ceiling, §14 Annexe) but applied it in spots. Generalised it. A false signal is worse than a declared gap — the 4 features KILLED at measurement (B1/B2/B3/W2) beat 4 false-signal features. See [[LRN-131]]/[[LRN-132]] (same session, the verification discipline that feeds it).
|
||||||
|
|
||||||
## LRN-134 — resolve-then-pin in stdlib beats monkeypatching getaddrinfo — 2026-07-17
|
## LRN-134 — resolve-then-pin in stdlib beats monkeypatching getaddrinfo — 2026-07-17
|
||||||
- **pattern**: to close SSRF/DNS-rebinding on Python HTTP egress, resolve the
|
- **pattern**: close SSRF/DNS-rebinding on Python HTTP egress: resolve the host ONCE, validate every returned IP (`ipaddress`, dual-stack v4+v6), refuse if ANY is non-public (the multi-A vector), connect to the exact pinned IP via an `http.client.HTTPSConnection` subclass whose `connect()` does `create_connection((pinned_ip, port))` + `wrap_socket(sock, server_hostname=real_host)` — SNI + cert stay bound to the real host. No second resolution to poison. `safe_fetch.py`.
|
||||||
host ONCE, validate every returned IP (`ipaddress`, dual-stack v4+v6), refuse
|
- **context**: the load-bearing property — classify the IP the OS RESOLVED (`sockaddr[0]`), NEVER the URL text. That defeats octal/hex/decimal literals, IPv4-mapped IPv6, NAT64, 6to4 structurally, not by enumeration (confirmed by the security review's fuzz). `is_global` = decisive gate (catches CGNAT 100.64/10 the per-flags miss); small extra-deny for special-use ranges it passes (192.88.99.0/24 6to4-relay). Redirects: re-validate EACH hop — urlopen followed them blind.
|
||||||
if ANY is non-public (the multi-A vector), then connect to the exact pinned IP
|
- **future**: beats claude-seo url_safety.py on 3 axes: dual-stack (theirs IPv4-only), thread-safe by construction (theirs monkeypatches getaddrinfo behind a global lock), stdlib-only (theirs `requests`). A name-level guard (url-guard.sh) cannot see a rebind; this is the layer that can. Shell `curl` stays unpinnable from here → `curl --resolve`, separate.
|
||||||
via an `http.client.HTTPSConnection` subclass whose `connect()` does
|
|
||||||
`create_connection((pinned_ip, port))` and `wrap_socket(sock,
|
|
||||||
server_hostname=real_host)` — SNI + cert stay bound to the real host. No
|
|
||||||
second resolution to poison. `safe_fetch.py`.
|
|
||||||
- **context**: the load-bearing property — classify the IP the OS RESOLVED
|
|
||||||
(`sockaddr[0]`), NEVER the URL text. That defeats octal/hex/decimal literals,
|
|
||||||
IPv4-mapped IPv6, NAT64, 6to4 structurally, not by enumeration (confirmed by
|
|
||||||
the security review's fuzz). `is_global` is the decisive gate (catches CGNAT
|
|
||||||
100.64/10 the per-flags miss); add a small extra-deny for special-use ranges
|
|
||||||
it passes (192.88.99.0/24 6to4-relay). Redirects: re-validate EACH hop —
|
|
||||||
urlopen followed them blind.
|
|
||||||
- **future**: beats claude-seo url_safety.py on 3 axes — dual-stack (theirs
|
|
||||||
IPv4-only), thread-safe by construction (theirs monkeypatches getaddrinfo
|
|
||||||
behind a global lock), stdlib-only (theirs `requests`). A name-level guard
|
|
||||||
(url-guard.sh) cannot see a rebind; this is the layer that can. Shell `curl`
|
|
||||||
stays unpinnable from here → `curl --resolve`, separate.
|
|
||||||
|
|
||||||
## LRN-135 — a prefix-only scan for a dangerous construct is bypassable by padding — 2026-07-17
|
## LRN-135 — a prefix-only scan for a dangerous construct is bypassable by padding — 2026-07-17
|
||||||
- **pattern**: to refuse a hostile construct (DTD, directive, marker) before
|
- **pattern**: to refuse a hostile construct (DTD, directive, marker) before parsing, scan the WHOLE document, never a bounded prefix.
|
||||||
parsing, scan the WHOLE document, never a bounded prefix.
|
- **context**: `_refuse_dtd` (C1b) scanned only `raw[:4096]` → sitemap with >4 KB of leading comment pushed `<!DOCTYPE` past the window while `ET.fromstring` parsed AND EXPANDED the entities (`&lol2;` → "lollollollollol", proven). Billion-laughs reopened on my own already-merged code. Found by the security review of the rebinding diff, not by me — fixed there rather than filed (root-cause discipline).
|
||||||
- **context**: `_refuse_dtd` (C1b) scanned only `raw[:4096]` → a sitemap with
|
- **future**: over ≤20 MB a full `re.search` is microseconds — no perf excuse for a bounded scan. Corollary of [[LRN-133]]: if you refuse a construct, refuse it EVERYWHERE, not just where you look first. Fresh adversarial reviewer attacking diff A routinely surfaces a real hole in already-shipped code B — see [[EVAL-020]].
|
||||||
>4 KB of leading comment pushed `<!DOCTYPE` past the window while
|
|
||||||
`ET.fromstring` still parsed AND EXPANDED the entities (`&lol2;` →
|
|
||||||
"lollollollollol", proven). Billion-laughs reopened on my own already-merged
|
|
||||||
code. Found by the security review of the rebinding diff, not by me — fixed
|
|
||||||
there rather than filed (root-cause discipline).
|
|
||||||
- **future**: over ≤20 MB a full `re.search` is microseconds — no perf excuse
|
|
||||||
for a bounded scan. Corollary of [[LRN-133]]: if you refuse a construct,
|
|
||||||
refuse it EVERYWHERE, not just where you look first. A fresh adversarial
|
|
||||||
reviewer attacking diff A routinely surfaces a real hole in already-shipped
|
|
||||||
code B — see [[EVAL-020]].
|
|
||||||
|
|
||||||
### LRN-136 — config-protection live state follows checked-out branch's symlinked settings.json (2026-07-17)
|
## LRN-136 — config-protection live state follows checked-out branch's symlinked settings.json (2026-07-17)
|
||||||
~/.claude/settings.json is a SYMLINK to the repo settings.json; Claude Code hot-reloads settings on change → the config-protection PreToolUse hook's active/inactive state tracks the CURRENT branch's settings.json. On feature/drop-config-protection (hook deregistered) a protected edit passed silently, sentinel unconsumed; after gitflow-switch to a branch off develop (hook still registered) the SAME class of edit was blocked. Apply: a change that removes a settings-registered hook is live only on that branch until merged; use the one-shot sentinel for protected edits on any branch that still registers it. ([[BDR-074]] context.)
|
~/.claude/settings.json is a SYMLINK to the repo settings.json; Claude Code hot-reloads settings on change → the config-protection PreToolUse hook's active/inactive state tracks the CURRENT branch's settings.json. On feature/drop-config-protection (hook deregistered) a protected edit passed silently, sentinel unconsumed; after gitflow-switch to a branch off develop (hook still registered) the SAME class of edit was blocked. Apply: a change that removes a settings-registered hook is live only on that branch until merged; use the one-shot sentinel for protected edits on any branch that still registers it. ([[BDR-074]] context.)
|
||||||
|
|
||||||
## LRN-137 — mode-based re-tiering beats file splits for mixed-tier agents
|
## LRN-137 — mode-based re-tiering beats file splits for mixed-tier agents
|
||||||
@@ -1527,3 +1533,48 @@ Rule: when editing a doctrine file under structure locks, grep the test's lock s
|
|||||||
- **Pattern**: (a) code-only build 2.3 s, 0 tokens, 3141 nodes / 7241 edges / 199 communities; hubs correct without any LLM (Auth, Database, Router, PDO, PHPMailer). Incremental update 1.9 s. `graphify-out/` = 8 MB (graph.json 4.4 + graph.html 3.5) → gitignore it. (b) one `graphify query` ≈ 2000-3000 tokens (default budget 2000, over-budget answers spill; truncation at 70/245 nodes on broad questions) = the price of two file reads; it maps (name, file:line), it does not replace reading the file you edit. Value = localisation, not editing. (c) it indexed `.claude/` (contracts, PROCEDURE.md, registries): an "authentication" query surfaced a mobile-nav contract → `.graphifyignore` (`.claude/`, `docs/superpowers/`; gitignore semantics, can only exclude more). (d) 13 `.sql` files contributed nothing: `tree_sitter_sql` missing → `pipx inject graphifyy "graphifyy[sql]"`. (e) `update` refuses to write a graph with FEWER nodes unless `--force`/`GRAPHIFY_FORCE=1` → after a refactor that deletes code, an automated update goes stale silently. (f) `graphify hook install` targets the repo's hooks dir; under our global `core.hooksPath` it is inert → our generated post-commit hook is the only integration point. (g) `graphify claude install` = PreToolUse nudges on every Read/Glob + CLAUDE.md rewrite — the context tax itself. (h) the semantic pass (docs/papers) runs on the host agent = session tokens; code-only stays free.
|
- **Pattern**: (a) code-only build 2.3 s, 0 tokens, 3141 nodes / 7241 edges / 199 communities; hubs correct without any LLM (Auth, Database, Router, PDO, PHPMailer). Incremental update 1.9 s. `graphify-out/` = 8 MB (graph.json 4.4 + graph.html 3.5) → gitignore it. (b) one `graphify query` ≈ 2000-3000 tokens (default budget 2000, over-budget answers spill; truncation at 70/245 nodes on broad questions) = the price of two file reads; it maps (name, file:line), it does not replace reading the file you edit. Value = localisation, not editing. (c) it indexed `.claude/` (contracts, PROCEDURE.md, registries): an "authentication" query surfaced a mobile-nav contract → `.graphifyignore` (`.claude/`, `docs/superpowers/`; gitignore semantics, can only exclude more). (d) 13 `.sql` files contributed nothing: `tree_sitter_sql` missing → `pipx inject graphifyy "graphifyy[sql]"`. (e) `update` refuses to write a graph with FEWER nodes unless `--force`/`GRAPHIFY_FORCE=1` → after a refactor that deletes code, an automated update goes stale silently. (f) `graphify hook install` targets the repo's hooks dir; under our global `core.hooksPath` it is inert → our generated post-commit hook is the only integration point. (g) `graphify claude install` = PreToolUse nudges on every Read/Glob + CLAUDE.md rewrite — the context tax itself. (h) the semantic pass (docs/papers) runs on the host agent = session tokens; code-only stays free.
|
||||||
- **Future application**: measure a "context saver" before adopting it — build time, artifact size, tokens per use, noise sources. Threshold rule [[BDR-097]]: propose from 200 tracked code files, never below. Pilot recipe when the user says go: `graphify update .` + `.graphifyignore` + gitignore `graphify-out/` + `GRAPHIFY_FORCE=1 graphify update .` in the post-commit hook, guarded by `[ -f graphify-out/graph.json ]`.
|
- **Future application**: measure a "context saver" before adopting it — build time, artifact size, tokens per use, noise sources. Threshold rule [[BDR-097]]: propose from 200 tracked code files, never below. Pilot recipe when the user says go: `graphify update .` + `.graphifyignore` + gitignore `graphify-out/` + `GRAPHIFY_FORCE=1 graphify update .` in the post-commit hook, guarded by `[ -f graphify-out/graph.json ]`.
|
||||||
- **Reference**: [[BDR-097]], [[BDR-028]], `lib/graphify-gate.sh`, graphify 0.9.65 (`detect.py` `_SKIP_DIRS`, `.graphifyignore`; `hooks.py` core.hooksPath handling; `__main__.py` PreToolUse nudge payloads).
|
- **Reference**: [[BDR-097]], [[BDR-028]], `lib/graphify-gate.sh`, graphify 0.9.65 (`detect.py` `_SKIP_DIRS`, `.graphifyignore`; `hooks.py` core.hooksPath handling; `__main__.py` PreToolUse nudge payloads).
|
||||||
|
|
||||||
|
## LRN-163 — VS Code terminal instrumentation is per-terminal and unpredictable: pre-flight the pty before attaching
|
||||||
|
- **Date**: 2026-09-24 (merge of [[LRN-147]] + [[LRN-148]], both 2026-09-03)
|
||||||
|
- **Context**: notify-attention over Remote-SSH. Incident 1: re-attach from a RESTORED terminal → bell OK, toast dead; probe on that pty: OSC 777 unique + repeated + OSC 9 all silent, BEL rang ⇒ bytes arrive, ext not hooked to that terminal. LRN-147 blamed `terminal.integrated.enablePersistentSessions` (terminals restored BEFORE lazy ext activation). Incident 2, same day, refuted that as sole cause: two terminals, SAME window, pts/3 (born 01:58:33) instrumented, pts/7 (born 01:59:29, LATER) deaf; ext GLOBAL (marketplace Enable/Disable only), shells identical on every server-side measurable (`VSCODE_INJECTION=1`, TERM, TERM_PROGRAM, same `--init-file`). Trigger NOT identified.
|
||||||
|
- **Pattern**: `wenbopan.vscode-terminal-osc-notifier` instruments a terminal only if it exists AFTER ext activation, and can still silently skip a later one. Treat instrumentation as a per-terminal property that fails for unknown reasons. Pre-flight before committing a long-lived session: `printf '\a\a\033]777;notify;NEUF;test\033\\'` typed IN that terminal. Toast → instrumented, attach. Bell only → deaf, open another. 5 s, replaces an hour of pty archaeology.
|
||||||
|
- **Recovery**: deaf terminal never repairs. Fresh terminal, pre-flight, `dtach -a ~/.dtach/<session>`; dtach broadcasts, old client may stay, session never at risk. Client setting `"terminal.integrated.enablePersistentSessions": false` removes the restored-terminal case, not the unknown one.
|
||||||
|
- **Diagnostic split (holds)**: bell alive + toast dead = terminal instrumentation. Toast alive + bell dead = client audio ([[BLK-020]] fault B). Neither = bytes never arrive. Check which channel survives first.
|
||||||
|
- **Future application**: verify instrumentation on the ACTUAL attached pty after every restart; never assume yesterday's terminal. Do NOT assert the born-before-activation cause as established — it fits the first incident, not the second; unknown trigger is the honest state.
|
||||||
|
- **Reference**: supersedes [[LRN-147]], [[LRN-148]] (bodies kept). Links [[BLK-019]], [[BLK-020]], [[BDR-087]], [[LRN-145]], [[LRN-146]], [[LRN-149]].
|
||||||
|
|
||||||
|
## LRN-164 — one fixed occurrence ≠ pattern closed: grep the whole surface, add a guard with teeth
|
||||||
|
- **Date**: 2026-09-24 (merge of [[LRN-106]] 2026-07-06 + [[LRN-113]] 2026-07-08)
|
||||||
|
- **Pattern**: fixer greps the reported line, fixes it, stops; twins survive one file or one agent over. job3-B1: `blockers-snapshot.md` fixture frozen, T2 repointed, "B1 UNBLOCKED", suite 20/20 GREEN — job4, same day, found T3 and T5 in the SAME FILE still reading live `$MEM/decisions.md`. Job1-9 review found 4 more: trailer stripped from commit-changer only (twins bugfixer/feater/hotfixer); YAML quoted elsewhere, seo/security-auditor left broken; attribution scrubbed on 3 skills, geo-analyzer missed; gitleaks added to the hook generator, installed hook not regenerated.
|
||||||
|
- **Why**: "suite green" + "named finding fixed" don't imply "no other instance of the same root cause survives nearby." Nothing enumerates the pattern across the full surface at commit time; an adversarial review catches the twins later.
|
||||||
|
- **Fix**: every pattern-fix ends with (1) a whole-surface grep proving zero residue (agents/ lib/ hooks/ templates/ skills/), (2) a deterministic make-test guard that REDs if any occurrence returns. Shipped `lib/tests/run-review-guards.sh`: G1 trailer, G2 false attribution, G3 strict-YAML, G4 reconcile hermeticity, G5 hook-drift, teeth-verified (planted violation REDs). job4 closure: T3/T5 repointed at `decisions-snapshot.md`, `$MEM` deleted, `grep -c '$MEM' == 0` gate.
|
||||||
|
- **Future application**: after fixing one instance of a generic finding, grep the WHOLE FILE and the whole surface class before declaring the class closed; add or extend a review-guard with teeth.
|
||||||
|
- **Reference**: `.audit/job4-report.md` J4-10, `lib/tests/run-reconcile.sh`, `lib/tests/run-review-guards.sh`. Supersedes [[LRN-106]], [[LRN-113]] (bodies kept). Cousins [[LRN-077]], [[LRN-114]], [[LRN-047]], [[BDR-041]].
|
||||||
|
|
||||||
|
## LRN-165 — a read-only sub-agent mandate constrains files, not tools: name the banned commands, ban copying secret values
|
||||||
|
- **Date**: 2026-09-24 (merge of [[LRN-105]] 2026-07-06 + [[LRN-107]] 2026-07-07)
|
||||||
|
- **Pattern**: "read-only" frames FILES; the model does not map it onto every tool call. (a) job3 docs-drift explorer (Bash + Read/Grep, "audit BODIES — do NOT modify any file") ran `graphify .` to check CLI behavior — a real build, stray `graphify-out/` at repo root. The prompt never named the command class to avoid; running the subject's own CLI read as investigation. Fixed only by a mid-run main-session correction. (b) job6 explorer under an explicit no-execute mandate copied the plaintext `MAGIC_API_KEY` into its own scratch file: copying a value into a NEW file mutates nothing that existed, so it passes the "don't mutate" mental model while creating a fresh copy of the secret ([[BDR-026]] class). Harness flagged it, main session redacted, contained to the scratchpad.
|
||||||
|
- **Future application**: any sub-agent dispatch framed read-only / audit / verify that grants Bash → explicitly ban executing the subject-under-test's CLI/build/generator and name the safe alternative in the same sentence (read installed source, grep docs). Any mandate touching config or env files → explicitly ban copying a secret's VALUE into output or scratch: "reference by name/location, never paste the value"; filter env fields (`jq 'del(.. | .env?)'`) over raw `cat`. Don't rely on the word "read-only" alone.
|
||||||
|
- **Reference**: `.audit/job3-report.md` A1/A2 header incident, `.audit/job6-report.md` "Incident (contained)", explorer-C.md redacted. Supersedes [[LRN-105]], [[LRN-107]] (bodies kept). Extended by [[LRN-160]] (prose guardrails are judgment). Cousins [[LRN-100]], [[BDR-026]].
|
||||||
|
|
||||||
|
## LRN-166 — structure and census locks are fixed single-line strings: a prose rewrap reds them with zero doctrine lost
|
||||||
|
- **Date**: 2026-09-24 (merge of [[LRN-142]] 2026-08-24 + [[LRN-144]] 2026-08-26)
|
||||||
|
- **Context**: contract-gates ([[BDR-083]]): editing lib/verify-secure-loop.md rewrapped 5 locked phrases across line breaks ("Max 3 conformity iterations", "Max 3 security iterations", "re-verify the REQUEST first", "always re-checked BEFORE security", "one verifier dispatch + one security dispatch") → loops-light.test.sh 30 pass / 5 fail, ZERO doctrine dropped. darwin 2026-08-26: hotfix RULES rewrap split "No verifier is dispatched at hotfix weight" → same lock RED, caught post-edit by make test.
|
||||||
|
- **Pattern**: lib/tests/*.test.sh lock sentences verbatim, single-line. Locks cannot distinguish "clause deleted" from "clause rewrapped"; that conservative bias is correct — fuzzy matching would miss real deletions.
|
||||||
|
- **Rule**: before editing prose under locks, grep lib/tests/ for the lock strings in the touched region, then re-flow AROUND them — each locked phrase stays on one unbroken line. Fix the DOC, not the lock, unless the doctrine genuinely changed. Run make test BEFORE dispatching judges, not after. Under locks: verify-secure-loop.md, contract-interview.md, verifier / security-auditor / plan-challenger agents, seo+geo (71 locks), hotfix RULES.
|
||||||
|
- **Reference**: `lib/tests/loops-light.test.sh`, `lib/tests/seo-geo-contract.test.sh`. Supersedes [[LRN-142]], [[LRN-144]] (bodies kept). Links [[BDR-083]], [[LRN-093]], [[LRN-096]].
|
||||||
|
|
||||||
|
## LRN-167 — a release/develop fork strands CODE on develop: a "resolved" blocker or a parallel-merged feature can miss its fix
|
||||||
|
- **Date**: 2026-09-24 (merge of [[LRN-116]] + [[LRN-117]], release/1.0.0 review, 2026-07)
|
||||||
|
- **Pattern**: cutting release/1.0.0 while develop moved on, RC-branch fixes landed ONLY on release: rtk install bridge `e58037c`, find-skills drop `095d881`, make-update TTY guard `a1093ca`, rtk update-path guard `4c5e862`, SC1091 lint `e65796f`. Live-broken on develop for the whole fork (rtk compression dead, ~460K tokens/30d; `make update` dies non-interactively). BLK-016 read "resolved via e58037c" and backfilled cleanly into develop while the fix was absent there: a resolved status is a claim about CODE state on the target branch, safe only for the TEXT.
|
||||||
|
- **Why it hides**: registry-sequence gaps (missing LRN/BLK/EVAL ids) are easy to detect; orphaned CODE has no sequence. A feature parallel-merged to both branches while its RC fix commit is never back-merged trips nothing.
|
||||||
|
- **Fix**: before back-merging a resolved blocker, grep the target for the fix's code signature — e58037c ported to develop first, THEN BLK-016 backfilled. At release-finish / in /reconcile, list `develop..release/*` commits touching functional files (exclude merges, `.claude/**`, version.txt/CHANGELOG) for back-merge review. Advisory, NOT a hard make-test gate — cherry-picks land with new SHAs so the source commit stays in the range; automatic "already-ported?" equivalence is unreliable. Backlogged.
|
||||||
|
- **Future application**: any long-lived fork → audit CODE divergence, not only declared/registry state ([[LRN-034]] narrated ≠ ground truth, applied to branches). `git cherry` before deleting a divergent branch ([[LRN-129]]).
|
||||||
|
- **Reference**: `install-plugins.sh` rtk bridge, release/1.0.0..develop review. Supersedes [[LRN-116]], [[LRN-117]] (bodies kept). Cousins [[LRN-036]], [[LRN-047]], [[BDR-054]].
|
||||||
|
|
||||||
|
## LRN-168 — a relayed claim is not a fact: WebSearch consensus and sub-agent summaries both need a primary source or a live test
|
||||||
|
- **Date**: 2026-09-24 (merge of [[LRN-131]] + [[LRN-132]], both 2026-07-17)
|
||||||
|
- **Pattern**: plausible RECOMBINATION is the failure mode — what a model half-remembering a search produces, and what a sub-agent relays. "Cross-check via WebSearch" LAUNDERS blog consensus instead of catching it. Treat every relayed finding as a claim to verify against a primary source or a live test. A statistic reaches a client only as `<claim> — <source, year, venue|vendor> — measured: <what the source ACTUALLY measured> — <link>`; the `measured:` field catches the error.
|
||||||
|
- **Context**: seo/geo 2026-07-17. "VSI (Visual Stability Index) — new 2026 Core Web Vital" sat in seo-analyzer as a threshold; it does NOT exist — absent from the CrUX API metric list AND web.dev, 10 SEO blogs cross-cited it into consensus. EVERY stat in agents/resources/ was real but grafted onto the wrong subject (Aggarwal 40% = ALL methods; AccuraCast 58.9% = Person-schema PREVALENCE pinned on QAPage lift, meaning inverted; LLMrefs 3x = brand-mentions-vs-backlinks pinned on freshness decay). 7 sub-agent claims disproven in one session: "Off-page has ZERO data" (brand mentions ARE gathered, STEP 6); "the stats drive axis weights" (no citations); "GSC Links API is available" (endpoint doesn't exist); "SPA §0 flag compensates" (never existed); "X/Twitter returns 403" (200, live-tested); Common Crawl "nearest free source" (17.3 GB dead end); the whole opening inventory behind the 20-point plan. The same error reproduced 3× while WRITING the fixes; contact with the REAL corrected it every time — sitemap, repo, curl, primary doc.
|
||||||
|
- **Future application**: an API's metric list (developer.chrome.com/docs/crux) is decisive: a metric the API can't return is one you can't score. Measure-first before building on a relayed summary; never re-read the spec as verification. Corroborates [[LRN-074]] (watch the RED go red), [[LRN-034]] (narrated ≠ ground truth).
|
||||||
|
- **Reference**: `agents/seo-analyzer.md`, `agents/resources/`, [[EVAL-025]]. Supersedes [[LRN-131]], [[LRN-132]] (bodies kept).
|
||||||
|
|||||||
Reference in New Issue
Block a user