diff --git a/.abcd/development/brief/04-surfaces/17-guard.md b/.abcd/development/brief/04-surfaces/17-guard.md index cba0d1c..4f9bc51 100644 --- a/.abcd/development/brief/04-surfaces/17-guard.md +++ b/.abcd/development/brief/04-surfaces/17-guard.md @@ -99,6 +99,6 @@ promoted into the bundled defaults through the admission gate. ## References - Plugin command: [`commands/abcd/guard.md`](../../../../commands/abcd/guard.md) -- Spec: [`spc-16`](../../specs/open/spc-16-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md) -- Intent: [`itd-103`](../../intents/planned/itd-103-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md) +- Spec: [`spc-16`](../../specs/closed/spc-16-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md) +- Intent: [`itd-103`](../../intents/shipped/itd-103-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md) - Install/health surface: [`01-ahoy.md`](01-ahoy.md) diff --git a/.abcd/development/brief/04-surfaces/18-ideate.md b/.abcd/development/brief/04-surfaces/18-ideate.md index 1544bc6..8f4737a 100644 --- a/.abcd/development/brief/04-surfaces/18-ideate.md +++ b/.abcd/development/brief/04-surfaces/18-ideate.md @@ -93,6 +93,6 @@ later session trusts it. ## References - Plugin command: [`commands/abcd/ideate.md`](../../../../commands/abcd/ideate.md) -- Intent: [`itd-104`](../../intents/planned/itd-104-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md) -- Spec: [`spc-18`](../../specs/open/spc-18-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md) +- Intent: [`itd-104`](../../intents/shipped/itd-104-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md) +- Spec: [`spc-18`](../../specs/closed/spc-18-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md) - Routing-help neighbours: [`05-intent.md`](05-intent.md), [`06-capture.md`](06-capture.md) diff --git a/.abcd/development/brief/04-surfaces/19-identity.md b/.abcd/development/brief/04-surfaces/19-identity.md index e345529..30ac076 100644 --- a/.abcd/development/brief/04-surfaces/19-identity.md +++ b/.abcd/development/brief/04-surfaces/19-identity.md @@ -79,7 +79,7 @@ the recorded three-variant tagline drift this check exists to catch. ## References - Plugin command: [`commands/abcd/identity.md`](../../../../commands/abcd/identity.md) -- Spec: [`spc-19`](../../specs/open/spc-19-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md) -- Intent: [`itd-102`](../../intents/planned/itd-102-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md) +- Spec: [`spc-19`](../../specs/closed/spc-19-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md) +- Intent: [`itd-102`](../../intents/shipped/itd-102-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md) - Onboarding consumer: [`15-prepare-this-repo.md`](15-prepare-this-repo.md) - Conformance surface: [`16-audit.md`](16-audit.md) diff --git a/.abcd/development/intents/planned/itd-102-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md b/.abcd/development/intents/planned/itd-102-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md deleted file mode 100644 index 05cbcce..0000000 --- a/.abcd/development/intents/planned/itd-102-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -id: itd-102 -slug: your-repo-says-the-same-thing-about-itself-everywhere-becaus -spec_id: spc-19 -kind: standalone -suggested_kind: null -reclassification_history: [] -builds_on: [] -severity: minor -impact: additive ---- - -# **Your repo says the same thing about itself everywhere — because abcd asked you once and holds every surface to it.** A project's positioning fragments silently: the README strapline, the package or plugin manifest description, and the agent conventions file are each edited at different moments, and soon three surfaces tell three stories about what the project is. When abcd prepares a repo it interviews the user for the project's identity — title, tagline, and a short elevator pitch — and records the answers in one canonical home in the development record. Every surface rendering derives from that home, and a deterministic positioning check compares the surfaces against it on every audit: a mismatch is highlighted with the exact drifted line, and the fix is either re-render from the record or a deliberate, recorded identity change — never a silent extra variant, and never a silent rewrite by abcd. "I changed my tagline once, in one place, and abcd chased it everywhere else," said Alice, a solo founder. "When a stray edit crept into the README, the next audit pointed at the exact line instead of letting the drift settle in." - -## Press Release - -> _Seeded from a quoted-text intent capture. Expand into the full press-release narrative before planning._ - -## Why This Matters - -**Your repo says the same thing about itself everywhere — because abcd asked you once and holds every surface to it.** A project's positioning fragments silently: the README strapline, the package or plugin manifest description, and the agent conventions file are each edited at different moments, and soon three surfaces tell three stories about what the project is. When abcd prepares a repo it interviews the user for the project's identity — title, tagline, and a short elevator pitch — and records the answers in one canonical home in the development record. Every surface rendering derives from that home, and a deterministic positioning check compares the surfaces against it on every audit: a mismatch is highlighted with the exact drifted line, and the fix is either re-render from the record or a deliberate, recorded identity change — never a silent extra variant, and never a silent rewrite by abcd. "I changed my tagline once, in one place, and abcd chased it everywhere else," said Alice, a solo founder. "When a stray edit crept into the README, the next audit pointed at the exact line instead of letting the drift settle in." - -## Acceptance Criteria - -- Given repo onboarding runs (the install or prepare flow), when the identity interview completes, then title, tagline, and pitch land in a parseable markdown identity block (fixed bullet shape) whose location the repo's abcd config records — markdown stays the single source of truth. -- Given a rendered surface (README strapline, manifest description, conventions-file opening) diverges from the identity block, when the positioning check runs, then a warn-tier finding names the exact drifted line; per-repo config may upgrade the finding to blocker. -- Given drift is found, then abcd never rewrites a surface autonomously — re-rendering from the block is always a proposed diff a human adopts. -- Given abcd-cli itself, then the check points at the brief product chapter's existing Identity section unchanged, and iss-143 (the three-variant drift) is the acceptance corpus the check must catch. - -## Open Questions - -- Interview wording and which surfaces are registered for checking by default beyond the canonical three. -- Whether the pitch is required or optional at onboarding (title and tagline are required). - -## Grill Settlements (2026-07-27) - -- Identity home is a parseable markdown block, not a structured JSON file — consistent with markdown-as-single-source-of-truth; the config records only where the block lives. -- The check is warn-tier by default (highlight, never gate) and per-repo upgradeable; autonomous rewriting is permanently out. - -## Audit Notes - -_Empty. Populated by intent-fidelity-reviewer when intent moves to shipped/._ diff --git a/.abcd/development/intents/planned/itd-103-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md b/.abcd/development/intents/planned/itd-103-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md deleted file mode 100644 index 6732e30..0000000 --- a/.abcd/development/intents/planned/itd-103-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -id: itd-103 -slug: abcd-teaches-repo-agents-the-shell-commands-they-must-never -spec_id: spc-16 -kind: standalone -suggested_kind: null -reclassification_history: [] -builds_on: [] -severity: minor -impact: additive ---- - -# **abcd teaches repo agents the shell commands they must never run — and blocks them when they try.** An agent that runs `cd scratch && rm -rf *` is one failed `cd` away from deleting the working tree, and the facilitator watching the session usually has no way to know that. abcd ships a hazard registry: each entry a dangerous command pattern, its severity, the safe successor, and a plain-language why. One registry, two planes. The rules loader injects the matched safety rules before shell-heavy work, so the agent is taught the safe form up front; a deterministic guard — a core verb any harness hook can call — refuses a matching command at execution time and replies with the safe successor, so the block itself is the lesson. Hosts without hook support still get the teaching plane; hosts with hooks get both, wired in at install. The registry grows from reality: a facilitator who sees something scary needs to know exactly one move — capture it — and recurring captures promote patterns into the bundled defaults, each entry shipping with a fixture that proves its guard fires. "I couldn't have told you why that command was dangerous," said Nia, a facilitator. "I didn't have to. The guard refused it, told the agent what to run instead, and told me why in words I understood. The one time something new scared me, I captured it and moved on." - -## Press Release - -> _Seeded from a quoted-text intent capture. Expand into the full press-release narrative before planning._ - -## Why This Matters - -**abcd teaches repo agents the shell commands they must never run — and blocks them when they try.** An agent that runs `cd scratch && rm -rf *` is one failed `cd` away from deleting the working tree, and the facilitator watching the session usually has no way to know that. abcd ships a hazard registry: each entry a dangerous command pattern, its severity, the safe successor, and a plain-language why. One registry, two planes. The rules loader injects the matched safety rules before shell-heavy work, so the agent is taught the safe form up front; a deterministic guard — a core verb any harness hook can call — refuses a matching command at execution time and replies with the safe successor, so the block itself is the lesson. Hosts without hook support still get the teaching plane; hosts with hooks get both, wired in at install. The registry grows from reality: a facilitator who sees something scary needs to know exactly one move — capture it — and recurring captures promote patterns into the bundled defaults, each entry shipping with a fixture that proves its guard fires. "I couldn't have told you why that command was dangerous," said Nia, a facilitator. "I didn't have to. The guard refused it, told the agent what to run instead, and told me why in words I understood. The one time something new scared me, I captured it and moved on." - -## Acceptance Criteria - -- Given the guard hook cannot execute the abcd binary, when any command runs, then the guard fails OPEN with an unmissable in-session warning, and guard health is reported by ahoy status — never a silent no-op, never a bricked session. -- Given a command matches a blocker-tier registry entry, then it is refused with the safe successor as the block message; warn-tier matches pass with the warning injected; no in-session override exists — the only escape is a committed per-repo config override. -- Given a hazard pattern appears inside a quoted string argument (for example a ledger capture whose text mentions a dangerous command), when the guard evaluates, then it does not fire: matching is shell-token-aware and applies in command position only, including cd-chain structure across compound separators. -- Given a registry entry is proposed for the bundled defaults, then it ships with known-bad and known-good fixtures (known-good at least 40% of its corpus) and clears a declared true-negative-rate floor before admission; string payloads inside eval or shell -c are a documented v1 gap, not a silent one. - -## Open Questions - -- The registry config's file home (extend rules.json with a guard domain vs a dedicated committed file) — a spec-time decision. -- Which non-shell tool calls (if any) the guard family later covers. - -## Grill Settlements (2026-07-27) - -- Fail-open-loud on guard breakage; blocker/warn tiers mirror the docs-lint family; overrides are committed and reviewable only. -- Shell-token-aware matching in command position was chosen precisely because a raw-regex guard would have blocked this repo's own incident-capture command — the known-good corpus is not optional. - -## Audit Notes - -_Empty. Populated by intent-fidelity-reviewer when intent moves to shipped/._ diff --git a/.abcd/development/intents/planned/itd-104-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md b/.abcd/development/intents/planned/itd-104-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md deleted file mode 100644 index 3af5963..0000000 --- a/.abcd/development/intents/planned/itd-104-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -id: itd-104 -slug: abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea -spec_id: spc-18 -kind: standalone -suggested_kind: null -reclassification_history: [] -builds_on: [] -severity: minor -impact: additive ---- - -# **abcd gates a new idea before it becomes a record entry: research it against primary sources, grill it against the existing record, and let an independent adversary try to kill it.** A product thinker's exciting idea is cheapest to kill in its first hour — and most expensive to kill after it has minted intents, specs, and branches. `/abcd:ideate` runs the admission interview: SOTA research in which every load-bearing claim is checked against its primary source; a record grill — does the brief, an intent, an ADR, or a principle already cover, contradict, or supersede this?; and an independent adversarial review, evaluator outside the loop. The output is a verdict plus a recorded decision with rejected alternatives; only a surviving idea graduates to a draft intent. The protocol is already proven by hand: three ideas entered it on 2026-07-14 — two were killed (one by a measured null result its premise could not survive, one by three independently fatal methodological defects) and one was reframed by adversarial review before a line was built. The manual run also named the two load-bearing steps automation must keep: check the record first (one idea was already written and superseded), and open the primary (three secondary-source claims were falsified, one of them fabricated by tool extraction). "The ideas I'm proudest of are the ones abcd killed in an afternoon," said Iris, a product thinker. "Each one left a recorded reason behind, so I never talk myself back into them six months later." - -## Press Release - -> _Seeded from a quoted-text intent capture. Expand into the full press-release narrative before planning._ - -## Why This Matters - -**abcd gates a new idea before it becomes a record entry: research it against primary sources, grill it against the existing record, and let an independent adversary try to kill it.** A product thinker's exciting idea is cheapest to kill in its first hour — and most expensive to kill after it has minted intents, specs, and branches. `/abcd:ideate` runs the admission interview: SOTA research in which every load-bearing claim is checked against its primary source; a record grill — does the brief, an intent, an ADR, or a principle already cover, contradict, or supersede this?; and an independent adversarial review, evaluator outside the loop. The output is a verdict plus a recorded decision with rejected alternatives; only a surviving idea graduates to a draft intent. The protocol is already proven by hand: three ideas entered it on 2026-07-14 — two were killed (one by a measured null result its premise could not survive, one by three independently fatal methodological defects) and one was reframed by adversarial review before a line was built. The manual run also named the two load-bearing steps automation must keep: check the record first (one idea was already written and superseded), and open the primary (three secondary-source claims were falsified, one of them fabricated by tool extraction). "The ideas I'm proudest of are the ones abcd killed in an afternoon," said Iris, a product thinker. "Each one left a recorded reason behind, so I never talk myself back into them six months later." - -## Acceptance Criteria - -- Given a one-line idea, when the user runs the intent or capture verb, then no ideate step is required or suggested as blocking — ideate is an optional verb, never a pre-capture gate; the routing help names it for big, unproven ideas. -- Given /abcd:ideate runs, then it executes three legs in order: primary-source research (every load-bearing claim checked against its primary), a record grill (does the brief, an intent, an ADR, or a principle already cover, contradict, or supersede this?), and an adversarial review that is fresh-context and off-policy — conducted by a session that did not do the research, receiving the idea as an artefact of unknown authorship. -- Given a verdict, then the decision is recorded with rejected alternatives whether the idea survives or dies; only a surviving idea graduates to a draft intent. -- Given the record grill leg runs, then a hit on an existing record entry (covered, contradicted, or superseded) is cited by id in the verdict — the check-the-record-first step that killed one of the three ideas in the proven manual run. - -## Open Questions - -- The verdict's schema and where it is stored (ledger entry vs a dedicated ideate record family) — a spec-time decision. -- Whether ideate can consume a lifeboat or external document as the idea source. - -## Grill Settlements (2026-07-27) - -- Optional verb, never a gate: capture friction stays at one line, and the routing help carries the nudge. -- The adversarial leg codifies the fresh-context, off-policy, unknown-authorship pattern — the only measured debiasing effect, per the salvaged 2026-07-14 research record. - -## Audit Notes - -_Empty. Populated by intent-fidelity-reviewer when intent moves to shipped/._ diff --git a/.abcd/development/intents/shipped/itd-102-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md b/.abcd/development/intents/shipped/itd-102-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md new file mode 100644 index 0000000..0f372b5 --- /dev/null +++ b/.abcd/development/intents/shipped/itd-102-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md @@ -0,0 +1,89 @@ +--- +id: itd-102 +slug: your-repo-says-the-same-thing-about-itself-everywhere-becaus +spec_id: spc-19 +kind: standalone +suggested_kind: null +reclassification_history: [] +builds_on: [] +severity: minor +impact: additive +--- + +# **Your repo says the same thing about itself everywhere — because abcd asked you once and holds every surface to it.** A project's positioning fragments silently: the README strapline, the package or plugin manifest description, and the agent conventions file are each edited at different moments, and soon three surfaces tell three stories about what the project is. When abcd prepares a repo it interviews the user for the project's identity — title, tagline, and a short elevator pitch — and records the answers in one canonical home in the development record. Every surface rendering derives from that home, and a deterministic positioning check compares the surfaces against it on every audit: a mismatch is highlighted with the exact drifted line, and the fix is either re-render from the record or a deliberate, recorded identity change — never a silent extra variant, and never a silent rewrite by abcd. "I changed my tagline once, in one place, and abcd chased it everywhere else," said Alice, a solo founder. "When a stray edit crept into the README, the next audit pointed at the exact line instead of letting the drift settle in." + +## Press Release + +> _Seeded from a quoted-text intent capture. Expand into the full press-release narrative before planning._ + +## Why This Matters + +**Your repo says the same thing about itself everywhere — because abcd asked you once and holds every surface to it.** A project's positioning fragments silently: the README strapline, the package or plugin manifest description, and the agent conventions file are each edited at different moments, and soon three surfaces tell three stories about what the project is. When abcd prepares a repo it interviews the user for the project's identity — title, tagline, and a short elevator pitch — and records the answers in one canonical home in the development record. Every surface rendering derives from that home, and a deterministic positioning check compares the surfaces against it on every audit: a mismatch is highlighted with the exact drifted line, and the fix is either re-render from the record or a deliberate, recorded identity change — never a silent extra variant, and never a silent rewrite by abcd. "I changed my tagline once, in one place, and abcd chased it everywhere else," said Alice, a solo founder. "When a stray edit crept into the README, the next audit pointed at the exact line instead of letting the drift settle in." + +## Acceptance Criteria + +- Given repo onboarding runs (the install or prepare flow), when the identity interview completes, then title, tagline, and pitch land in a parseable markdown identity block (fixed bullet shape) whose location the repo's abcd config records — markdown stays the single source of truth. +- Given a rendered surface (README strapline, manifest description, conventions-file opening) diverges from the identity block, when the positioning check runs, then a warn-tier finding names the exact drifted line; per-repo config may upgrade the finding to blocker. +- Given drift is found, then abcd never rewrites a surface autonomously — re-rendering from the block is always a proposed diff a human adopts. +- Given abcd-cli itself, then the check points at the brief product chapter's existing Identity section unchanged, and iss-143 (the three-variant drift) is the acceptance corpus the check must catch. + +## Open Questions + +- Interview wording and which surfaces are registered for checking by default beyond the canonical three. +- Whether the pitch is required or optional at onboarding (title and tagline are required). + +## Grill Settlements (2026-07-27) + +- Identity home is a parseable markdown block, not a structured JSON file — consistent with markdown-as-single-source-of-truth; the config records only where the block lives. +- The check is warn-tier by default (highlight, never gate) and per-repo upgradeable; autonomous rewriting is permanently out. + +## Audit Notes + + +Fidelity review — receipt rcp-52648dea351e (verifier abcd:intent-fidelity-reviewer claude-fable-5). + +Provenance: abcd:intent-fidelity-reviewer@claude-fable-5 · rubric_hash sha256:d401343707d6fa0f8fabad4c67380b1ec356ffd47f1b72a4e1b56ed320e25a4a · prompt_hash sha256:95792472ae74ca0469f69a51c618946e0d33cb1380032460099ed4b469d67e86 +Input attestations: diff:PR#158 feat/itd-102-repo-identity @ ca1459805dd4cc3d48adc39433bbd34b098736dd (base main): internal/core/positioning/, internal/core/audit/rule_positioning.go, internal/surface/cli/identity.go, commands/abcd/identity.md, commands/abcd/prepare-this-repo.md, .abcd/positioning.json, internal/fsutil os.Root containment@-; rubric:.abcd/.work.local/reviews/rcp-52648dea351e.request.md@sha256:d401343707d6fa0f8fabad4c67380b1ec356ffd47f1b72a4e1b56ed320e25a4a; + +Acceptance rollup: MET 4 · MET_WITH_CONCERNS 0 · NOT_MET 0 · INCONCLUSIVE 0 + +Per-criterion verdicts: +- ac-1 — MET: The onboarding interview (prepare-this-repo, detect-then-interview) calls `abcd identity init`, which writes the identity into a fixed-bullet markdown block and records its location in the repo's positioning registry — markdown is the single source of truth, the registry only points at it. + evidence: internal/core/positioning/init.go:159 — "fmt.Fprintf(&b, \"- **Title:** %s\\n\", title) … - **Tagline:** … - **Pitch:** (fixed bullet shape)" + evidence: internal/core/positioning/init.go:141 — "if err := writeConfig(r, DefaultConfig(loc)); err != nil — records where the block lives in ConfigRelPath" + evidence: commands/abcd/prepare-this-repo.md:118 — "Identity — detect first, interview only if there is nothing to detect … abcd identity init --title \"…\" --tagline \"…\" [--pitch \"…\"]" + evidence: internal/core/positioning/block.go:85 — "bulletRe = regexp.MustCompile(`^ {0,3}[-*]\\s+\\*\\*([A-Za-z]+):\\*\\*\\s*(.*)$`) — parses the fixed bullet shape back out" +- ac-2 — MET: checkSurface flags a diverged surface StatusDrifted; the audit rule emits a finding naming the file, the 1-based line, the exact drifted text and the canonical line; it is SeverityWarn by default and severityFor upgrades the whole family to SeverityError when the registry sets severity=blocker — both halves are test-enforced (TestPositioningDriftIsAWarnFindingNamingTheExactLine, TestPositioningSeverityUpgradesToBlocker) and passed. + evidence: internal/core/audit/rule_positioning.go:87 — "surface %q no longer carries the canonical %s — it says %q; the identity block (%s) says %q (File+Line set from s.File/s.Line)" + evidence: internal/core/audit/rule_positioning.go:107 — "func severityFor … if EqualFold(cfg.Severity, SeverityBlocker) { return SeverityError } default SeverityWarn" + evidence: cmd: go test -run 'TestPositioningSeverityUpgradesToBlocker' ./internal/core/audit/ (via full pkg run) — "ok github.com/REPPL/abcd-cli/internal/core/audit — AC2 second half: per-repo config upgrades the whole family to blocker" +- ac-3 — MET: The only re-render path is Propose, which returns unified diffs and performs no writes ('It performs no writes whatsoever'); the CLI `abcd identity render` writes nothing and no flag makes it, and the command doc states it plainly — a deliberate identity change is an edit to the block, never an autonomous rewrite. + evidence: internal/core/positioning/render.go:48 — "It performs no writes whatsoever." + evidence: internal/surface/cli/identity.go:51 — "Print the proposed correction for every drifted surface as a unified diff (writes nothing)" + evidence: commands/abcd/identity.md:43 — "prints a unified diff per drifted surface. It **writes nothing**, and no flag" +- ac-4 — MET: abcd-cli's own .abcd/positioning.json points the block at the brief product chapter's pre-existing 'Identity (canonical)' section in .abcd/development/brief/01-product/README.md, which PR#158 does not touch (its last change was the earlier iss-143 consolidation commit 48a3524, not this positioning work); and the iss-143 three-variant drift corpus is captured verbatim and caught — one-at-a-time and all-three-together tests pass. + evidence: .abcd/positioning.json:4 — "\"file\": \".abcd/development/brief/01-product/README.md\", \"heading\": \"Identity (canonical)\"" + evidence: cmd: gh pr diff 158 --name-only | grep 01-product — "NOT in PR diff — the brief Identity section is pointed at unchanged (last touched by 48a3524, not PR#158)" + evidence: internal/core/positioning/check_test.go:140 — "func TestCheckCatchesTheIss143DriftCorpus — the three real drifted variants (readme-strapline, manifest-description, conventions-opening) each caught StatusDrifted" + evidence: cmd: go test -run 'TestCheckCatchesTheIss143DriftCorpus|TestCheckCatchesAllThreeVariantsTogether' -v ./internal/core/positioning/ — "--- PASS: TestCheckCatchesTheIss143DriftCorpus / --- PASS: TestCheckCatchesAllThreeVariantsTogether (Drifted()==3)" + +Gap audit: +- honoured: + - One canonical markdown identity block is the single home; the config records only where it lives + evidence: internal/core/positioning/init.go:151 — "func blockSection … The single recorded home for how this project names itself" + evidence: .abcd/positioning.json:2 — "\"block\": { \"file\": …, \"heading\": \"Identity (canonical)\" }" + - Every registered surface is compared to the block on every audit; a mismatch names the exact drifted line and the canonical line + evidence: internal/core/audit/rule_positioning.go:87 — "it says %q; the identity block (%s) says %q" + evidence: internal/core/positioning/check.go:141 — "res.Status = StatusOK … if len(res.Missing) > 0 { res.Status = StatusDrifted }" + - The fix is a proposed diff or a deliberate recorded block change — never a silent extra variant, never a silent rewrite + evidence: internal/surface/cli/identity.go:164 — "%d proposed change(s), nothing written. Adopt what you agree with." + evidence: internal/core/positioning/init.go:91 — "ErrAlreadyAdopted … change the block there, or edit %s deliberately — re-running onboarding never repoints the canon" + - iss-143's three-variant tagline drift is the acceptance corpus and is caught + evidence: internal/core/positioning/check_test.go:55 — "The iss-143 acceptance corpus: the three real drifted variants the repo was carrying" + - Rule is wired into the production audit path (not dead scaffolding) + evidence: internal/core/audit/rules.go:12 — "identityPositioning{} — registered in DefaultRules()" + evidence: internal/surface/cli/cli.go:97 — "root.AddCommand(newIdentityCommand(&asJSON))" +- diverged: + - Onboarding is described as an install-or-prepare interview, but abcd currently onboards only via the prepare-this-repo bridge (host asks the questions); there is no separate install-time interview beyond the CLI `identity init` it drives — a narrower-than-worded but functionally complete path + evidence: internal/core/positioning/init.go:35 — "InitRequest is the onboarding interview's outcome. The host asks the questions (the prepare surface carries the wording); this is what it hands over." +- missing: (none) \ No newline at end of file diff --git a/.abcd/development/intents/shipped/itd-103-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md b/.abcd/development/intents/shipped/itd-103-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md new file mode 100644 index 0000000..268f39b --- /dev/null +++ b/.abcd/development/intents/shipped/itd-103-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md @@ -0,0 +1,92 @@ +--- +id: itd-103 +slug: abcd-teaches-repo-agents-the-shell-commands-they-must-never +spec_id: spc-16 +kind: standalone +suggested_kind: null +reclassification_history: [] +builds_on: [] +severity: minor +impact: additive +--- + +# **abcd teaches repo agents the shell commands they must never run — and blocks them when they try.** An agent that runs `cd scratch && rm -rf *` is one failed `cd` away from deleting the working tree, and the facilitator watching the session usually has no way to know that. abcd ships a hazard registry: each entry a dangerous command pattern, its severity, the safe successor, and a plain-language why. One registry, two planes. The rules loader injects the matched safety rules before shell-heavy work, so the agent is taught the safe form up front; a deterministic guard — a core verb any harness hook can call — refuses a matching command at execution time and replies with the safe successor, so the block itself is the lesson. Hosts without hook support still get the teaching plane; hosts with hooks get both, wired in at install. The registry grows from reality: a facilitator who sees something scary needs to know exactly one move — capture it — and recurring captures promote patterns into the bundled defaults, each entry shipping with a fixture that proves its guard fires. "I couldn't have told you why that command was dangerous," said Nia, a facilitator. "I didn't have to. The guard refused it, told the agent what to run instead, and told me why in words I understood. The one time something new scared me, I captured it and moved on." + +## Press Release + +> _Seeded from a quoted-text intent capture. Expand into the full press-release narrative before planning._ + +## Why This Matters + +**abcd teaches repo agents the shell commands they must never run — and blocks them when they try.** An agent that runs `cd scratch && rm -rf *` is one failed `cd` away from deleting the working tree, and the facilitator watching the session usually has no way to know that. abcd ships a hazard registry: each entry a dangerous command pattern, its severity, the safe successor, and a plain-language why. One registry, two planes. The rules loader injects the matched safety rules before shell-heavy work, so the agent is taught the safe form up front; a deterministic guard — a core verb any harness hook can call — refuses a matching command at execution time and replies with the safe successor, so the block itself is the lesson. Hosts without hook support still get the teaching plane; hosts with hooks get both, wired in at install. The registry grows from reality: a facilitator who sees something scary needs to know exactly one move — capture it — and recurring captures promote patterns into the bundled defaults, each entry shipping with a fixture that proves its guard fires. "I couldn't have told you why that command was dangerous," said Nia, a facilitator. "I didn't have to. The guard refused it, told the agent what to run instead, and told me why in words I understood. The one time something new scared me, I captured it and moved on." + +## Acceptance Criteria + +- Given the guard hook cannot execute the abcd binary, when any command runs, then the guard fails OPEN with an unmissable in-session warning, and guard health is reported by ahoy status — never a silent no-op, never a bricked session. +- Given a command matches a blocker-tier registry entry, then it is refused with the safe successor as the block message; warn-tier matches pass with the warning injected; no in-session override exists — the only escape is a committed per-repo config override. +- Given a hazard pattern appears inside a quoted string argument (for example a ledger capture whose text mentions a dangerous command), when the guard evaluates, then it does not fire: matching is shell-token-aware and applies in command position only, including cd-chain structure across compound separators. +- Given a registry entry is proposed for the bundled defaults, then it ships with known-bad and known-good fixtures (known-good at least 40% of its corpus) and clears a declared true-negative-rate floor before admission; string payloads inside eval or shell -c are a documented v1 gap, not a silent one. + +## Open Questions + +- The registry config's file home (extend rules.json with a guard domain vs a dedicated committed file) — a spec-time decision. +- Which non-shell tool calls (if any) the guard family later covers. + +## Grill Settlements (2026-07-27) + +- Fail-open-loud on guard breakage; blocker/warn tiers mirror the docs-lint family; overrides are committed and reviewable only. +- Shell-token-aware matching in command position was chosen precisely because a raw-regex guard would have blocked this repo's own incident-capture command — the known-good corpus is not optional. + +## Audit Notes + + +Fidelity review — receipt rcp-c09c73d6adc5 (verifier abcd:intent-fidelity-reviewer claude-fable-5). + +Provenance: abcd:intent-fidelity-reviewer@claude-fable-5 · rubric_hash sha256:e96fe6d7a3f6bfe3908171b52f5bd42de194298ead0093ffab6b377adc9dd3bc · prompt_hash sha256:f4de8b23c6b6c7a54133bf2042b6b89829c579412cf26f1f66a65c36d57aa6fe +Input attestations: diff:main..feat/itd-103-shell-hazard-registry (PR #156, merge 49cffa05): internal/core/guard/, internal/surface/cli/guard.go, internal/core/ahoy/guard_health.go, commands/abcd/guard.md, hooks/hooks.json@sha256:49cffa05e6216d9cbb29dc601961464781e6541d; rubric:.abcd/.work.local/reviews/rcp-c09c73d6adc5.request.md@sha256:e96fe6d7a3f6bfe3908171b52f5bd42de194298ead0093ffab6b377adc9dd3bc; + +Acceptance rollup: MET 3 · MET_WITH_CONCERNS 1 · NOT_MET 0 · INCONCLUSIVE 0 + +Per-criterion verdicts: +- ac-1 — MET: Every non-decision path in the hook adapter routes through one failOpen that exits 1 (non-zero, non-blocking) and prints 'NOT CHECKED — ... runs UNGUARDED'; the hooks.json wrapper catches even an unexpected exit and fails open loudly; and guard health is a first-class ahoy field wired into the status render — never a silent no-op, never a bricked session. + evidence: internal/surface/cli/guard.go:145 — "\"abcd guard: NOT CHECKED — \"+format+\". This command runs UNGUARDED.\\n\" ... return &exitError{Code: 1}" + evidence: hooks/hooks.json:13 — "[ $s -eq 0 ] || [ $s -eq 1 ] || [ $s -eq 2 ] || { echo \"abcd guard: FAILED TO RUN (exit $s) — shell commands run UNGUARDED ...\" >&2; exit 1; }" + evidence: internal/core/ahoy/detect.go:78 — "res.Guard = detectGuardHealth(abs, pluginRoot, pluginOK)" + evidence: internal/surface/cli/cli.go:1453 — "fmt.Fprintf(w, \" guard: %s\\n\", guardHealthLine(res.Guard))" +- ac-2 — MET_WITH_CONCERNS: A blocker refuses with the successor built into the block message and a warn passes with the warning surfaced, and the only disable route is the per-repo .abcd/guard.json config (no in-session flag or env bypass exists) — CONCERN: that override takes effect from the WORKING TREE before it is committed or reviewed (config.Load reads .abcd/guard.json off disk; iss-147 records this exactly), so it is reviewable-in-a-diff but the criterion's 'committed' qualifier is not actually enforced. + evidence: internal/core/guard/guard.go:300 — "return fmt.Sprintf(\"%s by the abcd guard (%s): %s Run instead: %s\", lead, e.ID, e.Why, e.Successor)" + evidence: internal/surface/cli/guard.go:198 — "case guard.VerdictBlock: ... fmt.Fprintln(cmd.ErrOrStderr(), termsafe.Sanitize(dec.Message)); return &exitError{Code: 2}" + evidence: internal/core/guard/config.go:34 — "data, err := fsutil.ReadGuarded(filepath.Join(repoRoot, \".abcd\", \"guard.json\"), maxGuardFileBytes)" + evidence: .abcd/work/issues/open/iss-147-guard-load-reads-abcd-guard-json-from-the-working-tree-so-a.md:4 — "guard-load-reads-abcd-guard-json-from-the-working-tree" +- ac-3 — MET: tokenize honours shell quoting and recognises operators outside quotes only, so a hazard inside a quoted argument stays one non-command token; matching runs in command position via commandOf/matchSegment; the cd-chain is enforced by AfterCD + precededByCD scanning earlier segments of the SAME chain across &&/;/||/| separators; and TestIncidentCaptureIsKnownGoodFixtureOne pins this repo's own quoted 'cd scratch && rm -rf *' capture as a known-good that must not fire. + evidence: internal/core/guard/tokenize.go:23 — "Operators and comments are recognised OUTSIDE quotes only, which is exactly what keeps a hazard named inside a quoted argument from ever reaching command position." + evidence: internal/core/guard/match.go:41 — "if matchSegment(p, s) && (p.AfterCD == nil || !*p.AfterCD || precededByCD(segs[:i], s.chain))" + evidence: internal/core/guard/match.go:51 — "func precededByCD(before []segment, chain int) bool { ... if cmd, _ := commandOf(s); cmd == \"cd\"" + evidence: internal/core/guard/fixtures_test.go:104 — "if !containsAll(first, \"capture\", \"cd scratch && rm -rf *\")" +- ac-4 — MET: The admission gate TestBundledEntriesPassAdmissionGate fails the build unless every bundled entry ships known-bad AND known-good fixtures, known-good is at least 40% of the corpus (10*good < 4*(good+bad)), and every known-good fires nothing (a declared 100% true-negative floor); the 6 shipped entries all clear it (e.g. rm-rf-after-cd-chain 8/17 good = 47%); and the eval / sh -c string-payload gap is documented, not silent, in the tokenizer, the CLI reference, and the surface brief. + evidence: internal/core/guard/fixtures_test.go:41 — "if 10*good < 4*(good+bad) { t.Fatalf(\"entry %s: known-good is %d of %d fixtures, below the 40%% floor\" ...)" + evidence: internal/core/guard/fixtures_test.go:66 — "if d.Verdict != VerdictAllow { t.Fatalf(\"known-good %q fired %v ...; the true-negative floor is 100%%\" ...)" + evidence: internal/core/guard/tokenize.go:26 — "v1 GAP (documented, not silent): ... A command string carried as a DATA argument — `sh -c ''`, `eval ''` ... its payload is never parsed" + evidence: .abcd/development/brief/04-surfaces/17-guard.md:86 — "a command string handed to an interpreter (`eval`, `sh -c`)" + +Gap audit: +- honoured: + - A deterministic guard, a core verb any harness hook can call, refuses a matching command and replies with the safe successor so the block itself is the lesson + evidence: internal/core/guard/guard.go:246 — "func (r Registry) Check(command string) (Decision, error)" + evidence: internal/surface/cli/guard.go:122 — "newGuardHookCommand builds `guard hook`: the adapter between a host's pre-tool-use hook payload and the core decision" + - Two tiers mirroring the docs-lint family: a blocker refuses, a warn passes with the warning attached + evidence: internal/core/guard/guard.go:19 — "TierBlocker = \"blocker\" ... TierWarn = \"warn\"" + evidence: internal/surface/cli/guard.go:200 — "case guard.VerdictWarn: fmt.Fprintln(cmd.ErrOrStderr(), termsafe.Sanitize(dec.Message)); return nil" + - Each entry ships a fixture that proves its guard fires, enforced as a build-time admission gate + evidence: internal/core/guard/fixtures_test.go:19 — "func TestBundledEntriesPassAdmissionGate" + evidence: internal/core/guard/defaults/guard.json:1 — "6 entries, each carrying known_bad + known_good fixtures" + - The known-good corpus is not optional: this repo's own incident-capture command must not be blocked + evidence: internal/core/guard/fixtures_test.go:95 — "func TestIncidentCaptureIsKnownGoodFixtureOne" +- diverged: + - The only escape is a COMMITTED per-repo config override — delivered as a per-repo .abcd/guard.json read from the working tree, so it activates before commit/review (reviewable in a diff, but 'committed' is not enforced) + evidence: internal/core/guard/config.go:34 — "fsutil.ReadGuarded(filepath.Join(repoRoot, \".abcd\", \"guard.json\") ...)" + evidence: internal/surface/cli/guard.go:275 — "the file is read from the working tree, so this can be true before anyone has reviewed the edit that made it true (iss-147)" +- missing: + - Press-release 'two planes': the teaching plane — the rules loader injecting the matched safety rules before shell-heavy work — is not wired in this delivery; only the execution-time guard plane ships (a guard/safety rules domain does not exist in internal/core/rules/) + evidence: .abcd/development/intents/shipped/itd-103-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md:13 — "The rules loader injects the matched safety rules before shell-heavy work" + evidence: internal/core/rules/rules.go:401 — "no guard/safety/hazard domain is registered — the only `guard` here is the stemming short-token guard" \ No newline at end of file diff --git a/.abcd/development/intents/shipped/itd-104-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md b/.abcd/development/intents/shipped/itd-104-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md new file mode 100644 index 0000000..15d4e73 --- /dev/null +++ b/.abcd/development/intents/shipped/itd-104-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md @@ -0,0 +1,89 @@ +--- +id: itd-104 +slug: abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea +spec_id: spc-18 +kind: standalone +suggested_kind: null +reclassification_history: [] +builds_on: [] +severity: minor +impact: additive +--- + +# **abcd gates a new idea before it becomes a record entry: research it against primary sources, grill it against the existing record, and let an independent adversary try to kill it.** A product thinker's exciting idea is cheapest to kill in its first hour — and most expensive to kill after it has minted intents, specs, and branches. `/abcd:ideate` runs the admission interview: SOTA research in which every load-bearing claim is checked against its primary source; a record grill — does the brief, an intent, an ADR, or a principle already cover, contradict, or supersede this?; and an independent adversarial review, evaluator outside the loop. The output is a verdict plus a recorded decision with rejected alternatives; only a surviving idea graduates to a draft intent. The protocol is already proven by hand: three ideas entered it on 2026-07-14 — two were killed (one by a measured null result its premise could not survive, one by three independently fatal methodological defects) and one was reframed by adversarial review before a line was built. The manual run also named the two load-bearing steps automation must keep: check the record first (one idea was already written and superseded), and open the primary (three secondary-source claims were falsified, one of them fabricated by tool extraction). "The ideas I'm proudest of are the ones abcd killed in an afternoon," said Iris, a product thinker. "Each one left a recorded reason behind, so I never talk myself back into them six months later." + +## Press Release + +> _Seeded from a quoted-text intent capture. Expand into the full press-release narrative before planning._ + +## Why This Matters + +**abcd gates a new idea before it becomes a record entry: research it against primary sources, grill it against the existing record, and let an independent adversary try to kill it.** A product thinker's exciting idea is cheapest to kill in its first hour — and most expensive to kill after it has minted intents, specs, and branches. `/abcd:ideate` runs the admission interview: SOTA research in which every load-bearing claim is checked against its primary source; a record grill — does the brief, an intent, an ADR, or a principle already cover, contradict, or supersede this?; and an independent adversarial review, evaluator outside the loop. The output is a verdict plus a recorded decision with rejected alternatives; only a surviving idea graduates to a draft intent. The protocol is already proven by hand: three ideas entered it on 2026-07-14 — two were killed (one by a measured null result its premise could not survive, one by three independently fatal methodological defects) and one was reframed by adversarial review before a line was built. The manual run also named the two load-bearing steps automation must keep: check the record first (one idea was already written and superseded), and open the primary (three secondary-source claims were falsified, one of them fabricated by tool extraction). "The ideas I'm proudest of are the ones abcd killed in an afternoon," said Iris, a product thinker. "Each one left a recorded reason behind, so I never talk myself back into them six months later." + +## Acceptance Criteria + +- Given a one-line idea, when the user runs the intent or capture verb, then no ideate step is required or suggested as blocking — ideate is an optional verb, never a pre-capture gate; the routing help names it for big, unproven ideas. +- Given /abcd:ideate runs, then it executes three legs in order: primary-source research (every load-bearing claim checked against its primary), a record grill (does the brief, an intent, an ADR, or a principle already cover, contradict, or supersede this?), and an adversarial review that is fresh-context and off-policy — conducted by a session that did not do the research, receiving the idea as an artefact of unknown authorship. +- Given a verdict, then the decision is recorded with rejected alternatives whether the idea survives or dies; only a surviving idea graduates to a draft intent. +- Given the record grill leg runs, then a hit on an existing record entry (covered, contradicted, or superseded) is cited by id in the verdict — the check-the-record-first step that killed one of the three ideas in the proven manual run. + +## Open Questions + +- The verdict's schema and where it is stored (ledger entry vs a dedicated ideate record family) — a spec-time decision. +- Whether ideate can consume a lifeboat or external document as the idea source. + +## Grill Settlements (2026-07-27) + +- Optional verb, never a gate: capture friction stays at one line, and the routing help carries the nudge. +- The adversarial leg codifies the fresh-context, off-policy, unknown-authorship pattern — the only measured debiasing effect, per the salvaged 2026-07-14 research record. + +## Audit Notes + + +Fidelity review — receipt rcp-37a162874f75 (verifier abcd:intent-fidelity-reviewer claude-fable-5). + +Provenance: abcd:intent-fidelity-reviewer@claude-fable-5 · rubric_hash sha256:1aa9117683cdc84ea4bcbae37c95edab738938331fdb6d39d5d00ea051db4141 · prompt_hash sha256:95792472ae74ca0469f69a51c618946e0d33cb1380032460099ed4b469d67e86 +Input attestations: diff:PR 157 main..e05a58ef44b970f7d8db65d7502675bf9a46c3d9: internal/core/ideate/ (ideate.go, record.go, render.go), internal/core/recordid/resolve.go, internal/surface/cli/ideate.go + cli.go ideate wiring, commands/abcd/ideate.md, routing help in commands/abcd/{intent,capture}.md@sha256:e05a58ef44b970f7d8db65d7502675bf9a46c3d9; rubric:.abcd/.work.local/reviews/rcp-37a162874f75.request.md@sha256:1aa9117683cdc84ea4bcbae37c95edab738938331fdb6d39d5d00ea051db4141; + +Acceptance rollup: MET 3 · MET_WITH_CONCERNS 1 · NOT_MET 0 · INCONCLUSIVE 0 + +Per-criterion verdicts: +- ac-1 — MET: The bare intent and capture status renders both print ideateRoutingRule (an optional pointer for a big, unproven idea) with no gate wording, and nothing in the ideate package is reachable from capture or intent-create; TestIdeateRoutingHintIsAPointerNotAGate enforces both facts and passes. + evidence: internal/surface/cli/cli.go:1255 — "const ideateRoutingRule = \" a big, unproven idea? `abcd ideate` runs the optional admission gauntlet and records the verdict either way\\n\"" + evidence: internal/surface/cli/cli.go:1119 — "fmt.Fprint(w, ideateRoutingRule) // bare `abcd intent` render; mirrored at cli.go:1746 for `abcd capture`" + evidence: internal/surface/cli/ideate_surface_test.go:177 — "for _, forbidden := range []string{\"must\", \"required\", \"before you\", \"warning\"} { ... presents ideate as a gate" +- ac-2 — MET_WITH_CONCERNS: The three legs and their required order are binary-enforced — legOrder is [research, record-grill, adversarial-review] and validate() refuses any leg out of position or carrying another leg's evidence — but the adversarial leg's defining fresh-context/off-policy/unknown-authorship property is delegated to the ideate.md prompt and is by design unverifiable at ingest (validateAdversarial only checks non-empty kill attempts and outcomes), so that debiasing property rests on prompt compliance, not on the recorded artefact. + evidence: internal/core/ideate/ideate.go:145 — "var legOrder = []LegKind{LegResearch, LegRecordGrill, LegAdversarialReview}" + evidence: internal/core/ideate/record.go:274 — "if leg.Kind != legOrder[i] { ... the legs run in order (%s), and the order is what each leg is looking at" + evidence: internal/core/ideate/record.go:367 — "The leg's fresh-context, off-policy, unknown-authorship conduct is the orchestrating prompt's obligation — the binary cannot observe how an agent was run" + evidence: commands/abcd/ideate.md:71 — "Strip every authorship signal before handing the artefact over ... This is the only measured debiasing effect in the whole protocol" +- ac-3 — MET: Record() writes the durable verdict for every registered outcome (survives/killed/reframed) and refuses a payload whose rejected-alternatives arrive empty by omission (the explicit no_rejected_alternatives marker is required), while graduation is gated on survival — Result.Graduates is verdict==survives and renderOutcome offers the draft-intent path only for survives, ideate minting no intent itself. + evidence: internal/core/ideate/record.go:405 — "if !p.NoRejectedAlternatives { ... the verdict records no rejected alternatives and does not say so explicitly — nothing was written" + evidence: internal/core/ideate/record.go:474 — "Graduates: v.verdict == VerdictSurvives," + evidence: internal/core/ideate/render.go:119 — "case VerdictSurvives: ... may graduate to a draft intent through\\nthe ordinary quoted-text create (`abcd intent \"\"`). Ideate mints no\\nintent itself" +- ac-4 — MET: resolveCitations builds one recordid.Resolver over the repository and refuses the whole verdict (CitationError, nothing written) if any grill hit cites an id that does not resolve; each hit is shape-checked against CitedIDRe first, and the resolved set is carried into the record as CitedRecords — the check-the-record-first gate, exercised green by the surface test that feeds an unresolvable itd-9999. + evidence: internal/core/ideate/record.go:454 — "if _, ok := r.Lookup(h.Record); !ok { unresolved = append(unresolved, h.Record); continue }" + evidence: internal/core/ideate/record.go:348 — "if !recordid.CitedIDRe.MatchString(h.Record) { ... which is not a record id (want adr-N, itd-N, iss-N, or spc-N)" + evidence: internal/core/recordid/resolve.go:76 — "func NewResolver(repoRoot string) (*Resolver, error)" + evidence: internal/surface/cli/ideate_surface_test.go:117 — "\"ideate\", \"record\", \"the-ideate-gate\", \"--verdict-json\", writeVerdict(t, ideateVerdictJSON(\"itd-9999\"))" + +Gap audit: +- honoured: + - Ideate is an optional verb named in the routing help, never a pre-capture gate + evidence: internal/surface/cli/cli.go:1255 — "a big, unproven idea? `abcd ideate` runs the optional admission gauntlet" + evidence: internal/core/ideate/ideate.go:19 — "NEVER A GATE. Nothing here is reachable from capture or intent-create." + - The record is written whether the idea survives or dies, and rejected alternatives cannot arrive silently empty + evidence: internal/core/ideate/render.go:123 — "case VerdictKilled: ... The idea is closed. Before proposing it again, read this record" + evidence: internal/core/ideate/record.go:405 — "the verdict records no rejected alternatives and does not say so explicitly — nothing was written" + - Every record-grill hit is cited by id and validated against the live repository, refusing the whole verdict on an unresolvable id + evidence: internal/core/ideate/record.go:460 — "if len(unresolved) > 0 { ... return nil, &CitationError{Unresolved: unresolved}" + - Only a survivor graduates; ideate mints no intent itself + evidence: internal/core/ideate/record.go:474 — "Graduates: v.verdict == VerdictSurvives," + - Wired on both planes: the abcd ideate verb (CLI) and the /abcd:ideate command surface + evidence: internal/surface/cli/cli.go:150 — "root.AddCommand(newIdeateCommand(&asJSON))" + evidence: commands/abcd/ideate.md:7 — "# `/abcd:ideate` — the idea-admission gauntlet" +- diverged: + - The adversarial leg's fresh-context/off-policy/unknown-authorship conduct is codified in the orchestrating prompt but not enforceable by the binary at ingest — the recorded artefact cannot prove the evaluator was a fresh, off-policy session + evidence: internal/core/ideate/record.go:367 — "the binary cannot observe how an agent was run, and pretending to check it would be theatre" + evidence: commands/abcd/ideate.md:66 — "The evaluator must not be the session that ran legs 1 and 2. Dispatch it as a separate agent with its own context." +- missing: (none) \ No newline at end of file diff --git a/.abcd/development/specs/open/spc-16-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md b/.abcd/development/specs/closed/spc-16-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md similarity index 100% rename from .abcd/development/specs/open/spc-16-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md rename to .abcd/development/specs/closed/spc-16-abcd-teaches-repo-agents-the-shell-commands-they-must-never.md diff --git a/.abcd/development/specs/open/spc-18-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md b/.abcd/development/specs/closed/spc-18-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md similarity index 100% rename from .abcd/development/specs/open/spc-18-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md rename to .abcd/development/specs/closed/spc-18-abcd-gates-a-new-idea-before-it-becomes-a-record-entry-resea.md diff --git a/.abcd/development/specs/open/spc-19-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md b/.abcd/development/specs/closed/spc-19-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md similarity index 100% rename from .abcd/development/specs/open/spc-19-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md rename to .abcd/development/specs/closed/spc-19-your-repo-says-the-same-thing-about-itself-everywhere-becaus.md diff --git a/.abcd/work/issues/open/iss-151-itd-103-shipped-only-one-of-its-two-promised-planes-the-exec.md b/.abcd/work/issues/open/iss-151-itd-103-shipped-only-one-of-its-two-promised-planes-the-exec.md new file mode 100644 index 0000000..fffa305 --- /dev/null +++ b/.abcd/work/issues/open/iss-151-itd-103-shipped-only-one-of-its-two-promised-planes-the-exec.md @@ -0,0 +1,11 @@ +--- +schema_version: 1 +id: "iss-151" +slug: "itd-103-shipped-only-one-of-its-two-promised-planes-the-exec" +severity: "minor" +category: "observation" +source: "user-observation" +found_during: "manual-capture" +--- + +itd-103 shipped only ONE of its two promised planes: the execution-time guard plane is fully wired (abcd guard check/hook, ahoy health), but the TEACHING plane — the rules loader injecting matched shell-safety rules before shell-heavy work, per spc-16 'Two planes, one registry' — is not built; there is no guard/safety/hazard domain in internal/core/rules/. All four itd-103 ACs concern the guard plane only, so the fidelity review is 3 MET/1 MWC, but the intent headline ('two planes') is only half delivered. Wire the teaching plane as a rules-loader safety domain sourced from the same bundled hazard registry, or amend the intent's scope. \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 24c4782..756e494 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ called out in a **Breaking** section. ## [Unreleased] +## [0.4.1] - 2026-07-28 + ### Added - **`abcd identity` — one canonical identity block, and every surface held to