From 5108dd99bfc888cae7a6bd8405a0fb04a3ea9fa4 Mon Sep 17 00:00:00 2001 From: REPPL <77722411+REPPL@users.noreply.github.com> Date: Wed, 22 Jul 2026 16:01:55 +0100 Subject: [PATCH 1/4] docs: reconcile the brief's surface chapters with the shipped binary The iss35 crosscheck at full depth found 102 discrepancies between the brief's 17 surface docs and what actually ships; adversarial triage confirmed 95 (7 refuted: duplicates, wrong-reality, legitimately staged). Fixes per adr-5's current-state contract: 68 rewrites to shipped reality, 14 missing-surface documentations (install override flags, session-start/session-end hooks, identity-pin write, git-identity detection), 8 deletions of abandoned-design prose (dev-sync, the agent-dispatch pack flow, disembark-to backgrounding, maxAgentTokens), 5 explicit staged markings for genuinely later-phase design. Orphaned cross-references and the stale disembark superseded-banner are stitched in the same pass. Resolves iss-121; the three merge-retro decisions and the session learnings land in DECISIONS.md. Assisted-by: Claude:claude-opus-4-8 Assisted-by: Claude:claude-fable-5 --- .../brief/01-product/02-context.md | 2 +- .../development/brief/04-surfaces/01-ahoy.md | 40 +++-- .../brief/04-surfaces/02-disembark.md | 160 ++++++------------ .../brief/04-surfaces/03-embark.md | 74 ++++---- .../brief/04-surfaces/04-launch.md | 15 +- .../brief/04-surfaces/05-intent.md | 65 ++++--- .../brief/04-surfaces/06-capture.md | 21 +-- .../brief/04-surfaces/07-memory.md | 20 +-- .../development/brief/04-surfaces/08-abcd.md | 23 +-- .../brief/04-surfaces/09-reflect.md | 7 +- .../development/brief/04-surfaces/10-docs.md | 12 +- .../brief/04-surfaces/11-history.md | 6 +- .../brief/04-surfaces/12-version.md | 4 +- .../brief/04-surfaces/15-prepare-this-repo.md | 26 +-- .../development/brief/04-surfaces/16-audit.md | 14 +- .abcd/development/brief/04-surfaces/README.md | 4 +- .../brief/05-internals/01-agents.md | 25 ++- .../brief/05-internals/03-configuration.md | 18 +- .../brief/05-internals/05-prompt-quality.md | 2 +- .../brief/05-internals/08-skills.md | 12 +- .abcd/work/DECISIONS.md | 27 +++ ...s35-crosscheck-scope-and-depth-unpinned.md | 12 ++ ...chapters-fail-iss35-crosscheck-at-depth.md | 14 ++ commands/README.md | 6 +- 24 files changed, 315 insertions(+), 294 deletions(-) create mode 100644 .abcd/work/issues/open/iss-122-iss35-crosscheck-scope-and-depth-unpinned.md create mode 100644 .abcd/work/issues/resolved/iss-121-brief-surface-chapters-fail-iss35-crosscheck-at-depth.md diff --git a/.abcd/development/brief/01-product/02-context.md b/.abcd/development/brief/01-product/02-context.md index bc461c1..21a6dde 100644 --- a/.abcd/development/brief/01-product/02-context.md +++ b/.abcd/development/brief/01-product/02-context.md @@ -8,7 +8,7 @@ abcd ships these user-facing commands: - **`/abcd:disembark to `** — *pack* a lifeboat by **reading** `` — any repository, including one abcd has never touched — and writing the artefact **out-of-tree** to the operator-chosen ``. The source is never written to; the destination must be absent, an empty directory, or one carrying a parseable `_provenance.json` ([adr-35](../../decisions/adrs/0035-lifeboat-as-coverage-experiment.md)). Bare `/abcd:disembark` shows status+help; `probe ` and `dry-run` sub-verbs preview without writing — `probe` reports **coverage** (which brief sections a repository can ground, and which come back blank). - **`/abcd:embark from `** — *unpack* the lifeboat at `` (wherever a disembark wrote it) into a (typically empty) target project. Bare `/abcd:embark` shows status+help; `scan` and `probe ` sub-verbs discover/inspect without unpacking. - **`/abcd:launch ship`** — cut a curated release from the single repo (`.abcd/**` excluded from the artifact by packaging), scrub for PII/secrets, stamp the version, update the marketplace entry. Bare `/abcd:launch` shows status+help; `dry-run` sub-verb runs the full pre-flight gate suite without writing the release artifact. -- **`/abcd:intent`** — bare quoted `/abcd:intent ""` is the canonical create (spc-30/itd-46), plus `refine` / `grill` / `plan` / `ship` / `review` / `consistency` / `shape` / `reclassify` / `link` sub-verbs (the `consistency` sub-verb shipped in spc-29 per itd-48, which superseded itd-31). There is no plain `list` sub-verb — it is folded into the bare render per SD001. Manages **intents** (press-release-format intent docs at `.abcd/development/intents/{drafts,planned,shipped,disciplines,superseded}/`). `plan` promotes an intent to `planned/` and plans the work as a spec on the native spec store ([adr-26](../../decisions/adrs/0026-native-spec-layer-ccpm-backend.md); the companion harness `ccpm` as the deeper backend); `ship` drives that spec to completion (or the full pipeline if from drafts/); a spec-close hook reconciles standalone/bundle intents planned → shipped automatically on a successful close (spc-28 `intent_lifecycle.reconcile`); disciplines move from drafts/ to disciplines/ on plan and stay there. Bare `/abcd:intent` shows status+help. +- **`/abcd:intent`** — bare quoted `/abcd:intent ""` is the canonical create (spc-30/itd-46), plus the `plan` / `ready` / `review` / `link` sub-verbs that ship (alongside the deprecated `new` alias), with `refine` / `grill` / `ship` / `consistency` / `shape` / `reclassify` remaining design targets (`consistency` per itd-48, which superseded itd-31). There is no plain `list` sub-verb — it is folded into the bare render per SD001. Manages **intents** (press-release-format intent docs at `.abcd/development/intents/{drafts,planned,shipped,disciplines,superseded}/`). `plan` promotes an intent to `planned/` and plans the work as a spec on the native spec store ([adr-26](../../decisions/adrs/0026-native-spec-layer-ccpm-backend.md); the companion harness `ccpm` as the deeper backend); `ship` drives that spec to completion (or the full pipeline if from drafts/); a spec-close hook reconciles standalone/bundle intents planned → shipped automatically on a successful close (spc-28 `intent_lifecycle.reconcile`); disciplines move from drafts/ to disciplines/ on plan and stay there. Bare `/abcd:intent` shows status+help. - **`/abcd:capture`** — capture / list / promote / resolve / wontfix issues (the structured `.abcd/work/issues/` ledger). Issues live at `.abcd/work/issues/{open,resolved,wontfix}/iss-N-.md`. See itd-4. The cross-corpus synthesist (`/abcd:dredge`) comes in a later phase as itd-25. - **`/abcd:memory`** — curate a queryable knowledge substrate (`ingest` external sources / `ask` queries / `lint` health-checks) from specs, ADRs, reviews, and memory. See itd-36 and [`05-internals/07-memory.md`](../05-internals/07-memory.md). diff --git a/.abcd/development/brief/04-surfaces/01-ahoy.md b/.abcd/development/brief/04-surfaces/01-ahoy.md index fff1723..2aa65c9 100644 --- a/.abcd/development/brief/04-surfaces/01-ahoy.md +++ b/.abcd/development/brief/04-surfaces/01-ahoy.md @@ -58,7 +58,7 @@ and itd-40), then `install` acts on the matching kind: | Folder kind | What `install` does | `.abcd/` written | |---|---|---| -| **`managed-repo`** | a git repo abcd already manages — run the repo install flow below (idempotent update) | repo-scope `.abcd/config.json` (with the `meta` setup block) + `.abcd/rules.json` per spc-16 T1 (no `meta.json` at repo scope; setup metadata lives at `config.json["meta"]`). Plus the CLAUDE.md/AGENTS.md marker block. See [`05-internals/03-configuration.md` § The two `.abcd/` scopes](../05-internals/03-configuration.md#the-two-abcd-scopes). | +| **`managed-repo`** | a git repo abcd already manages — run the repo install flow below (idempotent update) | repo-scope `.abcd/config.json` (with the `meta` setup block) + `.abcd/rules.json` per spc-16 T1 (no `meta.json` at repo scope; setup metadata lives at `config.json["meta"]`). Plus `.abcd/config/identity.json` when the git-identity pin is adopted (per iss-62 — a `config-change` write) and the CLAUDE.md/AGENTS.md marker block. See [`05-internals/03-configuration.md` § The two `.abcd/` scopes](../05-internals/03-configuration.md#the-two-abcd-scopes). | | **`unmanaged-repo`** | a git repo with no abcd yet — bare `/abcd:ahoy` offers `install` to adopt it; `install` runs the same repo flow | same as `managed-repo` | | **`unmanaged-folder`** | not a git repo — nothing to act on; reports and stops | none | @@ -138,7 +138,10 @@ Steps, run in parallel where independent: 4. **Identity** — `git rev-list --max-parents=0 HEAD` → root SHA. Cross-check against `~/.abcd/history/index.json`: is this SHA registered? Is there a sibling entry with a matching name/github but a *different* root - SHA (the re-founding signal — see below)? + SHA (the re-founding signal — see below)? Separately, compare the git author + identity a commit would use against the committed `.abcd/config/identity.json` + pin (iss-62), emitting a `git_identity.unpinned` / `.mismatch` / `.unset` / + `.uncheckable` `config-change` gap. 5. **History-store wiring** — does the `~/.abcd/history/` store (abcd's native local redacted transcript corpus, per [adr-29](../../decisions/adrs/0029-native-transcript-corpus.md)) exist at all @@ -157,7 +160,10 @@ Steps, run in parallel where independent: 9. **Hook manifest verification** (verify-only per spc-16 T1) — VERIFY that `hooks/hooks.json` is present in the plugin install AND contains the three required event entries (`UserPromptSubmit`, `SessionStart`, `PreCompact`) - each referencing the expected prompt-router hook commands. A missing or + each referencing the expected prompt-router hook commands. The shipped + manifest also wires `abcd hook session-start` (a second `SessionStart` + command) and `abcd hook session-end` (a fourth `SessionEnd` event); + verification covers only the three prompt-router commands above. A missing or malformed manifest surfaces as a non-resolvable `plugin-owned` diagnostic gap. Neither install nor uninstall ever mutates `hooks.json` — the manifest is plugin-static per spc-14 T7. @@ -170,7 +176,7 @@ a `scope`, a `title`, `detail`, and a `fix_hint`. Gaps are grouped by category: | `category` | Examples | Apply behaviour | |---|---|---| | `safe-autocreate` | `.abcd/` skeleton, history-store dirs (`.abcd/bin/` scripts in a later phase) | apply once category approved, no per-item prompt | -| `config-change` | visibility, oracle adapter, PATH symlink | transparent confirm; skip-if-set with "current value" notice | +| `config-change` | visibility, oracle adapter, PATH symlink, git-identity pin | transparent confirm; skip-if-set with "current value" notice | | `plugin-owned` | CLAUDE.md/AGENTS.md marker block (per itd-3); `hooks/hooks.json` manifest verification (verify-only per spc-16 T1) | silent overwrite on marker drift; non-resolvable diagnostic for malformed/missing manifest | | `dependency` | opt-in scanners: `gitleaks`, `trufflehog` install | offer brew with ONE category-level approval covering the opt-in scanners (per spc-16 T1 — per-CATEGORY, not per-tool) | | `user-state` | `index.json` entry, re-founding, stale/duplicate entries | guided; never auto-edit user-scope app-state, report extras read-only | @@ -191,6 +197,16 @@ that artefact ships — to canonical files under `defaults/` too. grouped by category, asks **one** category-level approval question per category present, and applies the approved categories' gaps. +Non-interactive override flags pre-answer the prompts: `--yes` approves every +resolvable category without prompting, `--adopt` / `--refuse-adopt` decide the +unmanaged-repo adoption question, `--docs-target` (`claude_md` | `agents_md` | +`both` | `skip`) sets the marker target, `--oracle-backend` +(`host-delegated` | `native` | `cli` | `api` | `mcp`) sets the oracle, +`--scan-deep` (`true` | `false`) toggles the deep scan, and `--visibility` +(`private` | `public`) sets repo visibility. `--yes` does not adopt an +unmanaged repo or pin an unset git identity — those still need `--adopt` and an +interactive confirmation. + 1. Run the detection pass (above). 2. **Dependency gaps** (`dependency`) — surface brew/pip commands for missing tools with ONE category-level approval (per spc-16 T1 — per-category, not @@ -295,9 +311,9 @@ acceptance criterion, not just prose — see § Acceptance. canonical `DetectionResult` envelope as JSON (per spc-16 T1 — JSON ONLY; no unified-diff renderer in this command surface). Exits without writing. The JSON envelope shape is `{folder_kind, adopted, root_sha, -plugin_root_status, repo_identity, signals, gaps}` so consumers can drive -the Claude Code skill's two-pass approval protocol off `folder_kind` + -`gaps`. +plugin_root_status, repo_identity, signals, gaps}` so the plugin command +(`commands/abcd/ahoy.md`) summarises state off `folder_kind` + `gaps` and +names `abcd ahoy install` for anything actionable. **Doctor (`/abcd:ahoy doctor`):** runs the detection pass and reports every gap read-only, grouped by category. Unlike bare invocation it shows full gap detail @@ -316,14 +332,16 @@ user-scope app-state. available sub-verbs with suggested next actions — and never mutates state. - **Given** a fresh repo with no `.abcd/` directory, **when** `/abcd:ahoy install` runs to completion, **then** the two-file repo carve-out - (`.abcd/config.json` + `.abcd/rules.json` per spc-16 T1) is written, the + (`.abcd/config.json` + `.abcd/rules.json` per spc-16 T1) is written, + `.abcd/config/identity.json` is pinned when the git-identity gate is adopted + (per iss-62), the visibility-driven `.gitignore` allowlist entries are present, the history-store dirs + `index.json` entry are present, the CLAUDE.md/AGENTS.md marker block from `internal/core/ahoy/defaults/claude-md-marker-block.md` is installed, and the - `hooks/hooks.json` check runs verify-only (never mutated) — the manifest - itself is a later-phase artefact; its absence surfaces as a non-resolvable - `plugin-owned` diagnostic gap. + `hooks/hooks.json` check runs verify-only (never mutated) — the manifest is + plugin-static per spc-14 T7, and a missing or malformed manifest surfaces as + a non-resolvable `plugin-owned` diagnostic gap. - **Given** a repo with `install` already run, **when** `/abcd:ahoy install` runs again with no state changes, **then** the detection pass reports zero actionable gaps, the message reads "already up to date", and nothing is written. diff --git a/.abcd/development/brief/04-surfaces/02-disembark.md b/.abcd/development/brief/04-surfaces/02-disembark.md index e385aa1..dc162fc 100644 --- a/.abcd/development/brief/04-surfaces/02-disembark.md +++ b/.abcd/development/brief/04-surfaces/02-disembark.md @@ -1,15 +1,6 @@ # `/abcd:disembark` — Pack a Lifeboat -> **⚠ Partly superseded by [adr-35](../../decisions/adrs/0035-lifeboat-as-coverage-experiment.md).** This chapter still describes the **in-tree** disembark model. Four of its claims are no longer the decision, and the reconciled prose lands with the packer (the `disembark` verb and `commands/abcd/disembark.md` now ship — with the out-of-tree, coverage-experiment model of adr-35, not this chapter's in-tree prose): -> -> | This chapter says | adr-35 decides | -> |---|---| -> | Packs into the current repo's `.abcd/lifeboat/`; `to home` is the ergonomic default | **Read-only and out-of-tree**: `disembark pack `. The source is **never** written to — a test hashes its tree before and after. There is no `home`. | -> | Operations log at `.abcd/development/voyage/` (in-tree, per adr-4) | `voyage/` moves to the **operator level**, `~/.abcd/voyage//`, keyed like the history store — never committed. This dissolves the `privacy-hygiene` collision (voyage records absolute source paths). | -> | Re-running overwrites the previous snapshot with a `.bak` safety net | A **destination safety gate**: refuse unless the destination is absent, empty, or carries a parseable `_provenance.json`. **Never overwrite a directory abcd did not produce.** | -> | The oracle returns a `"sufficient"` verdict | `"sufficient"` was a member of **no registered enum**. The oracle returns the registered `{SHIP, NEEDS_WORK, MAJOR_RETHINK}`. | -> -> adr-35 also inverts the build: **`probe` → a coverage report, across a corpus, before `pack` exists at all** (itd-88). The brief carries only what abcd could ground, each claim citing its source; a first-class `coverage.{json,md}` carries what is missing, what was searched, and the question a human must answer. +> **Model of record: [adr-35](../../decisions/adrs/0035-lifeboat-as-coverage-experiment.md).** The packer is read-only and out-of-tree (`disembark pack `; the source is never written to — a test hashes its tree before and after), the voyage log lives at the operator level (`~/.abcd/voyage//`, never committed), a destination safety gate refuses any directory abcd did not produce, and the oracle returns the registered `{SHIP, NEEDS_WORK, MAJOR_RETHINK}` verdicts. The coverage experiment (itd-88) leads: the brief carries only what abcd could ground, each claim citing its source; `coverage.{json,md}` carry what is missing, what was searched, and the question a human must answer. > **Phase ownership** ([adr-33](../../decisions/adrs/0033-launch-phase-ownership-tiered.md)): the packer and the round-trip ship in [Phase 6](../../roadmap/phases/phase-6-lifeboat.md). The **coverage experiment (itd-88) is pulled out of Phase 6** and sequenced ahead of it, per adr-35. @@ -17,155 +8,102 @@ ## Sub-verbs -Bare `/abcd:disembark` shows status + help only — never mutates state. Current sub-verbs (as the shipped binary exposes them): +Bare `/abcd:disembark` prints the sub-verb list and flags only — never mutates state. Current sub-verbs (as the shipped binary exposes them): -- **`/abcd:disembark pack `** — pack a lifeboat from `` to ``. Both paths are positional and required, and there is **no `home` shorthand**: the lifeboat lands out-of-tree at an operator-chosen destination and the source repo is never written to (adr-35). The flow described in § 1 below is this sub-verb's behaviour. -- **`/abcd:disembark probe `** — read-only inspection of the source: which brief sections it can ground, which come back blank, and what was searched. Emits the coverage report (`coverage.{json,md}`, adr-35); writes nothing into ``. Ships **ahead of the packer** (itd-88) and is the experiment's readout. -- **`/abcd:disembark plan `** — the dry-run: the full lifeboat file set a pack would write, without writing anything. +- **`/abcd:disembark pack `** — pack a lifeboat from `` to ``. Both paths are positional and required, and there is **no `home` shorthand**: the lifeboat lands out-of-tree at an operator-chosen destination and the source repo is never written to (adr-35). Its deterministic flow is described in § 1 below. +- **`/abcd:disembark probe [source-repo]`** — read-only inspection of the source (the repo argument is optional and defaults to the current directory): which brief sections it can ground, which come back blank, and what was searched. Renders the coverage report to stdout and writes nothing into ``; `coverage.{json,md}` are written only by `pack` (adr-35). It is the coverage experiment's readout (itd-88). +- **`/abcd:disembark plan [source-repo]`** — the dry-run (the repo argument is optional and defaults to the current directory): the full lifeboat file set a pack would write, without writing anything. - **`/abcd:disembark coverage …`** — aggregate probe reports into the cross-repo section×repo coverage table. - **Synthesis sub-verbs over a packed lifeboat** — `graveyard` (validate host-produced lesson JSON and write the survivors, cite-or-be-dropped), `oracle` (audit a packed lifeboat against its source repo — a registered verdict + cited findings), `press-release` (compose the lifeboat's press release), and `principles` (distil principles from the ADRs). Each is deterministic-or-validate-host-JSON. - **Later phase: `to-spec-kit `** — export shipped intents to GitHub Spec Kit format alongside the lifeboat (per itd-23); not yet shipped. -## 1. Architecture (Phase 0 dev-sync + three passes) +## 1. Architecture (a single deterministic pass) + +`disembark pack ` is one deterministic Go run. It reads the source (never writing to it) and lands every byte under ``: ``` -PHASE 0 — DEV-SYNC (see 05-internals/03-configuration.md § 2) - abcd dev-sync runs per-source enabled flags (each source is an opt-in adapter over a native default): - • reviews → render reviews via the wired oracle adapter (host-delegated - default; native / CLI / API / MCP opt-in) → .abcd/work/reviews/ - • memory → distil memory backend (Claude / OpenCode / ...) → .abcd/memory/ - • work → curate .work/ → .abcd/work/{issues,notes}/ - • rp → RP workspace pull (opt-in RP adapter only, per itd-7) → .abcd/rp/workspace.json - Idempotent (content-hash dedup). +INVENTORY (read-only) + walk the source and its record families → the planned lifeboat file set, + each brief section grounded from the source per internal/core/lifeboat/mapping.go, + plus a per-section coverage report (grounded | partial | blank) │ ▼ -PROBE & CONFIRM - the disembark probe entrypoint (internal/core) runs all adapters' probe() in parallel - → ActiveSources list (reads curated .abcd/work/, not raw sources) - → AskUserQuestion (transparent): confirm assets/docs sources +DESTINATION SAFETY GATE + refuse unless is absent, empty, or carries a parseable _provenance.json + → never overwrite a directory abcd did not produce (adr-35) │ ▼ -PASS A — settled artefacts (4 agents in parallel, newest-first, JSON-out) - • flow-essence → rescue/spec-essence.json - • decision-archaeologist → research/decisions-timeline.json - • review-collator → research/reviews-consolidated.json - • code-rescuer* → research/code-principles.json +SECRET SCAN (before any write) + scan the planned bytes; a hard-fail secret refuses the whole pack — never redact │ ▼ -PASS B — targeted chat fill (chat-distiller, one call per unresolved spine entry) - • git-blame spec windows → time-window index - • filtered transcripts → research/rationale-fills.json - research/unrecorded-decisions.json - delta research/pitfalls.json +WRITE + write to a staging directory, then swap it into ; _provenance.json is + written last — the commit marker and the gate key for a later re-pack │ ▼ -PASS C — distil, compose, audit (sequential) - • principle-distiller → principles.json - • artefact-curator → docs/, assets/_manifest.json - • brief-composer → README.json - • press-release-composer → press-release.json - ↳ oracle product audit → audit/press-release-oracle-.json - (host-delegated by default; opt-in oracle adapter when wired; findings appended to press-release.md) - • render → .md files from .json sources - • lifeboat-oracle → audit/oracle-.json - findings appended to README rendering - • documentation-auditor (subagent, pre-pack) → audit/documentation-audit-.json +VOYAGE LINE (operator-local) + append one line to ~/.abcd/voyage//disembark/history.jsonl; + a failed append never fails the pack — the written _provenance.json is authoritative ``` -\* code-rescuer reads source via a wired oracle adapter if available, else spec-driven git-window file selection. Outputs principles only — never copies code into the lifeboat (`--with-code` comes in a later phase as itd-8). +The pack dispatches no agents and runs no LLM passes. The synthesis artefacts — `press-release.{json,md}`, `principles.{json,md}`, the oracle audit, and the validated graveyard lessons — are written by the separate synthesis sub-verbs run over an already-packed lifeboat, each deterministic-or-validate-host-JSON. ## 2. Recency rule Hard rule: later structural artefacts supersede earlier ones — spec numbers, ADR `Superseded-By` headers, file mtimes, git log. **Never resolve recency semantically.** -When structure and content disagree (e.g., later spec mentions earlier decision approvingly without restating it), **route to chat-distiller in Pass B**: receives the relevant time-windowed transcripts, emits a finding for the unrecorded-decisions report. (Risk: Pass B becomes load-bearing for correctness; transcript signal density resolved in Phase 0 (`../../research/notes/transcript-sampling.md`); Pass B viability gates are a design target (itd-11, draft) — not yet encoded in the build sequence.) - -## 3. Agent context budget - -Each agent estimates input tokens before dispatch: - -- Under `disembark.maxAgentTokens` (default 100,000) → one shot -- Over → stream + summarise (map-reduce: per-input summary, then merge pass) - -Pass B's chat-distiller is exempt (already streams by per-spine-entry queries). Phase 0 sampling measures actual sizes on the corpus before locking the strategy. - -## 4. Backgrounded execution - -- `disembark to ` launches as a background task via `harness.run_background()`. -- After each agent run, writes checkpoint to `.abcd/logbook/disembark//_state.json` (forensic record only — see resume note below). -- `harness.schedule()` schedules a wake-up every ~5 minutes to surface a one-line summary ("Pass B: 12/30 spine entries resolved"). -- Ctrl-c interrupts cleanly; re-running `disembark to ` starts fresh. **Note:** abcd does not ship a resume verb. The checkpoint exists for forensic purposes (post-mortem on a failed run); a future intent may add a `resume` sub-verb if real-world friction emerges. -- Final report at completion. +When structure and content disagree (e.g., a later spec mentions an earlier decision approvingly without restating it), the shipped pack records the structural answer only. *(Design target, not yet shipped: route such disagreements to a transcript-reading distiller agent that emits a finding for the unrecorded-decisions report — viability gated on itd-11 (draft); transcript signal density per `../../research/notes/transcript-sampling.md`.)* ## 5. Output shape -> **The shape below is the packer's target and is not yet built** — the section list it assumes is exactly what the coverage experiment (itd-88) exists to revise, so expect it to change before the packer is written to it. Two rows are already settled by adr-35: `coverage.{json,md}` is first-class (what could *not* be grounded, what was searched, and the question a human must answer), and `graveyard/` is a section of its own. +`disembark plan [source-repo]` lists exactly what a pack writes, and the pack writes that same tree to ``. Each path derives from the brief-section → lifeboat-path mapping in `internal/core/lifeboat/mapping.go`: a section grounds into its file where the source supports it, and `coverage.{json,md}` record every section that stays blank, what was searched, and the question a human must answer. `graveyard/` is a section of its own (adr-35). ``` / # operator-chosen, outside the source repo (adr-35) -├── coverage.json # FIRST-CLASS: per-section status (grounded|partial|blank), evidence, what was searched +├── _provenance.json # the lifeboat marker and re-pack gate key (written last): sources probed, file manifest, manifest_sha256, schema_version +├── coverage.json # per-section status (grounded|partial|blank), evidence, what was searched ├── coverage.md # rendered -├── graveyard/ # what the project tried and abandoned (three layers, cite-or-be-dropped) -├── README.md # rendered from README.json (brief-composer + oracle findings) -├── README.json -├── press-release.md # rendered from press-release.json (+ product-audit findings appended) -├── press-release.json # the embark interview contract -├── principles.md # rendered from principles.json -├── principles.json -├── rescue/ -│ ├── extraction.md # only if --with-code (itd-8, a later phase) -│ ├── spec-plan.md # rendered (rebuild plan) -│ ├── spec-essence.json # spine -│ ├── spec-essence.md # rendered -│ └── specs/ # verbatim copies of the native spec store's spec files -├── research/ -│ ├── decisions-timeline.{json,md} -│ ├── pitfalls.{json,md} # source memory + Pass B deltas -│ ├── reviews-consolidated.{json,md} -│ ├── rationale-fills.{json,md} -│ ├── unrecorded-decisions.{json,md} -│ └── code-principles.{json,md} # from code-rescuer +├── brief/ # the brief, section by section, grounded from the source +│ ├── 01-product/ … 06-delivery/ # press-release, context, mental-model, scope, personas; constraints; evidence; surfaces; internals; delivery +│ └── glossary/ +├── graveyard/ # what the project tried and abandoned +│ ├── abandoned.json +│ └── archaeology.json +├── rescue/ # the spine: the intent corpus where one exists, else the commit history +│ ├── spine.md # commit-history spine, written where no record store exists +│ ├── intents/{drafts,planned,shipped,superseded,disciplines}/ # intent corpus, verbatim +│ └── specs/{open,closed}/ # spec store, verbatim ├── docs/ -│ ├── adrs/ # verbatim ADR copies -│ ├── terminology.md # rendered from .abcd/development/brief/glossary//.md (per itd-27 / spc-3, adr-30) -│ ├── claude-md-snapshot.md -│ └── tutorials/ guides/ reference/ explanation/ # verbatim from source docs/ -├── assets/ -│ ├── logos/ charts/ screenshots/ -│ └── _manifest.json # source path → lifeboat path, caption, provenance, classification (keep/adapt/drop) -├── audit/ -│ ├── oracle-.{json,md} # lifeboat-oracle (content fidelity) -│ ├── press-release-oracle-.{json,md} # press-release-composer's product audit -│ └── documentation-audit-.{json,md} # documentation-auditor (subagent pre-pack) -├── activity/ # curated issue ledger snapshot (per itd-4) -│ └── issues/{open,resolved,wontfix}/ -└── _provenance.json # current snapshot's provenance (sources probed, adapter runs, agent runs, oracle backend used, schema_version). Hash matches the manifest_sha256 in voyage/disembark/history.jsonl for this run; cumulative history lives in voyage/, not here. +│ └── adrs/ # ADRs copied verbatim +└── activity/ + └── issues/{open,resolved,wontfix}/ # curated issue ledger snapshot ``` -The lifeboat is written out-of-tree to `` (adr-35), so the source repo has nothing to gitignore. (Superseded: this line previously said the lifeboat is gitignored unless `visibility=private`, which assumed the in-tree home.) +The synthesis sub-verbs add the rest over an already-packed lifeboat: `press-release` writes `press-release.{json,md}`, `principles` writes `principles.{json,md}`, `oracle` writes the audit, and `graveyard` validates and writes the lesson JSON. None of these exist at pack time. + +The lifeboat is written out-of-tree to `` (adr-35), so the source repo has nothing to gitignore. ## 6. Per-phase acceptance Each phase passes when **both gates** succeed: 1. **Oracle gate**: `lifeboat-oracle` audit on phase outputs returns a registered review verdict (`SHIP` / `NEEDS_WORK` / `MAJOR_RETHINK`, per `05-internals/01-agents.md § Verdict-tag protocol`) with specific findings (not vague approval); the gate passes on `SHIP`. The oracle is **host-delegated by default** (per [adr-25](../../decisions/adrs/0025-host-delegated-llm-default.md)); an opt-in oracle adapter (native / CLI / API / MCP) runs the audit when wired — never blocks. -2. **Round-trip gate**: phase outputs feed cleanly into the next phase's expected inputs (e.g., Pass A's `spec-essence.json` validates against schema and is consumed by Pass B's chat-distiller without parse errors). +2. **Round-trip gate**: stage outputs feed cleanly into the next stage's expected inputs (e.g., a packed lifeboat verifies against its manifest and is consumed by the synthesis sub-verbs — `oracle`, `principles`, `press-release`, `graveyard` — without parse errors). Acceptance is checked across the validation corpus (`.abcd/corpus.json` — a design target; the file is not yet in the tree), with documented per-repo exemptions where a feature genuinely doesn't apply. ## 7. Acceptance -> **Open question (adr-35):** bare `/abcd:disembark` previously reported "where the lifeboat lives", which was answerable only because the home was fixed and in-tree. With an operator-chosen ``, the destination path must come from somewhere, and adr-35 pins the `history.jsonl` line to `manifest_sha256`, file list, oracle backend, and verdict — it does **not** say the line records the destination. **Decide:** does a `disembark/history.jsonl` entry carry the destination path (and does bare invocation resolve `` from the cwd, given disembark is otherwise not cwd-based)? The first bullet below assumes yes on both counts. +> **Open question (adr-35):** bare `/abcd:disembark` previously reported "where the lifeboat lives", which was answerable only because the home was fixed and in-tree. With an operator-chosen ``, the destination path must come from somewhere, and adr-35 pins the `history.jsonl` line to `manifest_sha256`, file list, oracle backend, and verdict — it does **not** say the line records the destination. **Decide:** does a `disembark/history.jsonl` entry carry the destination path (and does bare invocation resolve `` from the cwd, as `probe` and `plan` already default to it)? The first bullet below assumes yes on both counts. -- **Given** any abcd-aware terminal, **when** the user runs bare `/abcd:disembark`, **then** the dispatcher reads the voyage log at `~/.abcd/voyage//disembark/history.jsonl` and shows when the source last disembarked and where that snapshot was written, the available sub-verbs (`pack `, `probe`, `plan`; later phase: `to-spec-kit`), and suggested next actions — bare invocation never mutates state. +- **Given** any abcd-aware terminal, **when** the user runs bare `/abcd:disembark`, **then** the dispatcher reads the voyage log at `~/.abcd/voyage//disembark/history.jsonl` and shows when the source last disembarked and where that snapshot was written, the available sub-verbs (`pack `, `probe`, `plan`, `coverage`, `oracle`, `graveyard`, `press-release`, `principles`; later phase: `to-spec-kit`), and suggested next actions — bare invocation never mutates state. - **Given** any source repo, **when** any sub-verb runs against it (`probe`, `plan`, or a full pack), **then** the source tree is byte-for-byte identical afterwards — a test hashes the tree before and after (adr-35: disembark is read-only). There is no `home`, and no path under `` is ever a destination. - **Given** a corpus repo with an intent corpus, ADRs, and a memory backend present, **when** `/abcd:disembark pack ` runs to completion, **then** `` contains all sections in [§ 5](#5-output-shape) and the oracle audit returns a registered verdict of `SHIP` with specific findings. - **Given** a corpus repo where one source is sparse (e.g., a repo with no intent corpus), **when** a full pack runs, **then** the run **succeeds**: the affected section is omitted from the brief rather than fabricated, and `coverage.{json,md}` records it with `status: blank`, what was searched, and the question a human must answer — a blank is a first-class result, not a failure or an exemption footnote (adr-35). - **Given** the user runs `/abcd:disembark probe `, **when** the command completes, **then** all adapters' `probe()` runs in parallel, `coverage.{json,md}` is emitted with every brief section marked `grounded` / `partial` / `blank` plus what was searched, nothing is written into ``, and the run takes a small fraction of the time a full pack would take (no LLM dispatches). - **Given** `probe` is run across the validation corpus, **when** the per-repo coverage reports are aggregated, **then** the aggregate reports the section-coverage delta between a rich-record repo and a git-only repo — the experiment's readout, and the evidence the packer's section list is built to (itd-88, adr-35). -- **Given** the user runs `/abcd:disembark plan`, **when** the command completes, **then** the inventory + agent budget estimation runs end-to-end, the would-be writes are listed (file paths, agent dispatches, estimated tokens), nothing is written to `` or ``, and the report explicitly notes which Pass A/B/C agents would have been dispatched. +- **Given** the user runs `/abcd:disembark plan`, **when** the command completes, **then** the source inventory runs end-to-end, the would-be writes are listed as file paths, and nothing is written to `` or ``. - **Given** a destination that is neither absent, nor an empty directory, nor one carrying a parseable `_provenance.json`, **when** a pack targets it, **then** the run **refuses** and writes nothing — abcd never overwrites a directory it did not produce (adr-35's destination safety gate; there is no `.bak`). -- **Given** an agent's input estimate exceeds `disembark.maxAgentTokens`, **when** that agent dispatches, **then** the stream + summarise (map-reduce) path is taken instead of one-shot dispatch. -- **Given** Phase 0 dev-sync fails on any source, **when** Pass C runs, **then** the affected Pass C agent runs in degraded mode and the disembark report flags the degradation explicitly. -- **Given** any pack run completes, **when** the lifeboat is written, **then** a new line is appended to `~/.abcd/voyage//disembark/history.jsonl` with the run's `manifest_sha256`, file list, oracle backend used, and verdict (per [`03-embark.md § 7`](03-embark.md#7-voyage-layout-embarkdisembark-provenance-and-history)). The hash matches `_provenance.json` in the lifeboat for this run. +- **Given** any pack run completes, **when** the lifeboat is written, **then** a new line is appended to `~/.abcd/voyage//disembark/history.jsonl` with the run's `schema_version`, event, timestamp, `manifest_sha256`, source name, source root SHA, destination, file **count**, and bytes written (per [`03-embark.md § 7`](03-embark.md#7-voyage-layout-embarkdisembark-provenance-and-history)). The line carries no oracle backend or verdict — nothing produces them yet, and an empty field would be a lie (adr-35); the full file list lives in the lifeboat's `_provenance.json`, pinned by the hash, which matches this run's `_provenance.json`. - **Given** a `` carrying a parseable `_provenance.json` from a previous run, **when** a new snapshot lands there, **then** the previous snapshot is replaced AND its manifest remains in the voyage log — there is never a `lifeboat-v1/` / `lifeboat-v2/` directory; history is preserved in the manifest log, not in stale snapshots. diff --git a/.abcd/development/brief/04-surfaces/03-embark.md b/.abcd/development/brief/04-surfaces/03-embark.md index 3aa0026..543596f 100644 --- a/.abcd/development/brief/04-surfaces/03-embark.md +++ b/.abcd/development/brief/04-surfaces/03-embark.md @@ -29,7 +29,7 @@ Bare `/abcd:embark` shows status + help only — never mutates state. The two **shipped** sub-verbs: -- **`/abcd:embark from `** — unpack the lifeboat at `` into the current repo. Path is required, and it is always an explicit path to a destination a disembark wrote — **there is no `home` shorthand** (adr-35: there is no in-tree lifeboat home to expand it to). The round-trip / self-test case is `disembark pack ` followed by `embark from `. *(Design target, not yet shipped: the flag-shaped modifiers `--force` — override emptiness-rule refusal, `--archive` — copy input lifeboat verbatim to `~/.abcd/voyage//embark/from//` before unpacking, and `--refresh-audit` — re-run oracle product audit instead of trusting cached. The shipped `from` takes no flags.)* +- **`/abcd:embark from `** — unpack the lifeboat at `` into the current repo. Path is required, and it is always an explicit path to a destination a disembark wrote — **there is no `home` shorthand** (adr-35: there is no in-tree lifeboat home to expand it to). The round-trip / self-test case is `disembark pack ` followed by `embark from `. *(Design target, not yet shipped: the flag-shaped modifiers `--force` — override conflict refusal, `--archive` — copy input lifeboat verbatim to `~/.abcd/voyage//embark/from//` before unpacking, and `--refresh-audit` — re-run oracle product audit instead of trusting cached. The shipped `from` takes no flags.)* - **`/abcd:embark probe `** — inspect a lifeboat at `` without unpacking: show what would land where, run schema/audit checks, write nothing. Design-target sub-verb (not yet shipped): @@ -41,37 +41,32 @@ Design-target sub-verb (not yet shipped): ## 1. Source lookup -Per [`02-constraints/01-platform.md § Lifeboat path`](../02-constraints/01-platform.md#lifeboat-path), lifeboats are *output*, and they land **out-of-tree at an operator-chosen destination** — `disembark to ` never writes to the source repo. There is no in-tree lifeboat home and no repo-local registry of inbound lifeboats. Embark therefore always reads from an external source: the destination some disembark wrote to. +Per [`02-constraints/01-platform.md § Lifeboat path`](../02-constraints/01-platform.md#lifeboat-path), lifeboats are *output*, and they land **out-of-tree at an operator-chosen destination** — `disembark pack ` never writes to the source repo. There is no in-tree lifeboat home and no repo-local registry of inbound lifeboats. Embark therefore always reads from an external source: the destination some disembark wrote to. Path resolution under `from `: -1. `` → validate, use. There is no `home` shorthand (adr-35). The round-trip / self-test case is not special-cased either: it is `disembark to ` followed by `embark from `, the same explicit path as any other source. +1. `` → validate, use. There is no `home` shorthand (adr-35). The round-trip / self-test case is not special-cased either: it is `disembark pack ` followed by `embark from `, the same explicit path as any other source. 2. To *find* candidate lifeboats before running `from `, use `embark scan` (or `embark scan --deep`) — that's a separate sub-verb, not a flag on `from`. -**Provenance and `--archive`**: `embark from ` records `source_path` and `source_manifest_sha256` in `~/.abcd/voyage//embark/provenance.json` (see [§ 7](#7-voyage-layout-embarkdisembark-provenance-and-history)). Opt-in `embark from --archive` additionally copies the input lifeboat verbatim into `~/.abcd/voyage//embark/from//` for the case where the source repo will disappear. Off by default; `source_path` + hash is enough when the source repo persists. +**Provenance and `--archive`** *(design target, not yet shipped)*: `embark from ` records `source_path` and `source_manifest_sha256` in `~/.abcd/voyage//embark/provenance.json` (see [§ 7](#7-voyage-layout-embarkdisembark-provenance-and-history)). Opt-in `embark from --archive` additionally copies the input lifeboat verbatim into `~/.abcd/voyage//embark/from//` for the case where the source repo will disappear. Off by default; `source_path` + hash is enough when the source repo persists. No global `~/.abcd/archive/`. -## 2. Target emptiness +## 2. Conflict-based refusal -Hard stop unless only `.git/`, `.gitignore`, `LICENSE*`, `README.md`, `.github/` present. On the refusal path **core writes nothing** — it *returns* the conflicts it found, and the surface renders them (adr-35: a refusal that writes a file is a transport-agnostic-core violation). `embark from --force` proceeds to conflict resolution ([§ 4](#4-conflict-ux)). +There is no emptiness gate. A conflict is per-file: a target that merely carries unrelated files is not a conflict, and a planned file whose bytes already match is an idempotent skip. Embark refuses only when a planned target path already holds differing bytes, is a non-regular target, is a duplicate target, or sits under a non-directory parent. On **any** such conflict **core writes nothing** — it *returns* the conflict set it found, and the surface renders it (adr-35: a refusal that writes a file is a transport-agnostic-core violation). *(Design target, not yet shipped: `embark from --force` to proceed to conflict resolution ([§ 4](#4-conflict-ux)); the shipped `from` takes no flags.)* ## 3. Scaffold steps -0. **Read lifeboat:** `press-release.md`, `README.json`, `principles.json`, `spec-essence.json`, `decisions-timeline.json`, `code-principles.json`, plus the disembark-time `audit/press-release-oracle-*.md` and `audit/documentation-audit-*.md`. -1. **Press release interview (FIRST INTERACTION).** Show the user the press release + the disembark-time product audit findings. Ask: "Confirm / amend / reframe before scaffolding." Amended press release becomes the new repo's **initial brief**: written to `.abcd/development/brief/README.md` in the embarked target. Subsequent ahoy/work iterates on it like any other abcd-managed project's brief. **`embark from --refresh-audit`** flag re-runs the oracle product audit before showing the user (uses current oracle config; flags drift vs the disembark-time audit in the report). -2. Show scaffold summary (referencing the amended press release); transparent confirm to proceed. -3. Create dirs; copy ADRs, terminology, docs verbatim from lifeboat to canonical target locations. -4. Create specs from `rescue/spec-plan.md` in the native spec store (via `/abcd:intent plan` / `ship`, per [adr-26](../../decisions/adrs/0026-native-spec-layer-ccpm-backend.md)), or create a minimal native spec structure. -5. Write curated memory files to `.abcd/memory/` — one per principle, grouped by domain, filename `__.md` (e.g., `feedback_ui_full_box_hit_target.md`) with frontmatter conforming to the shipped memory-page schema — a typed `source:` provenance block (class/classes, licence, source_hash, weighting_note) that memory-lint's ML001 blocker requires; the shipped schema carries no `name`/`description` keys; it does require a `type` key (plus `domain`/`slug`/`body`/`source`) (`internal/core/memory/schema.go`, `lint.go`). Only the `__.md` filename grammar matches the shipped store (`ParsePageFilename`). The volatile memory backend's source store is left untouched in the new repo's environment — the harness (Claude Code, OpenCode, etc.) will populate it as the user works. -6. Inject principles into CLAUDE.md/AGENTS.md between BEGIN/END markers (idempotent). The marker block content is the modular-rules-loader block the embark scaffolder (`internal/core/...`) emits (per itd-3) — *not* a verbose copy of every principle. Principles are exposed via the rules-loader's domain rules, surfaced on demand by prompt-keyword recall. -7. Apply asset curation ([§ 5](#5-asset-curation-per-_manifestjson-classifications)). -8. **Documentation-auditor** (subagent) runs post-scaffold, before final report, to verify the target's user-facing docs (tutorials, guides, reference, explanation) are well-formed. -9. **If `.abcd/rp/workspace.json` exists in the lifeboat** (per itd-7): ask the user "Register this RP workspace with RepoPrompt now?" — if yes, write to the embarker's `~/Library/Application Support/RepoPrompt/Workspaces/`. If RP isn't installed, warn gracefully and continue. -10. **Write voyage provenance** (per [§ 7](#7-voyage-layout-embarkdisembark-provenance-and-history)): create `~/.abcd/voyage//embark/provenance.json` with `source_path`, `source_manifest_sha256`, `timestamp`, `files_written`, `press_release_amended_diff`, and (if `--refresh-audit`) `audit_drift`. If `embark --archive` was passed, also copy the input lifeboat verbatim to `~/.abcd/voyage//embark/from//`. -11. Write `embark-report.{json,md}` (includes the amended press release diff, audit drift if `--refresh-audit`, RP workspace registration status, voyage provenance path). - -`embark-scaffolder` agent emits a JSON scaffold plan; deterministic Python applies it. Agent does judgement (which principles where, how to phrase injection); Python does file creation. **Press release is treated as a hard input** to the scaffolder — if a principle or spec-essence entry contradicts the amended press release, the scaffolder flags the conflict in the embark report. +Embark is a deterministic Go run: it reads the lifeboat, plans, refuses on any conflict, then writes the record families plus the `CLAUDE.md` marker. No interactive scaffolder, no Python, and no LLM agent sit in the write path. + +0. **Read lifeboat:** the record families — ADRs (`docs/adrs/`), issues (`activity/issues/`), intents (`rescue/intents/`), specs (`rescue/specs/`) — plus the report-only files `_provenance.json`, `press-release.{json,md}`, `principles.{json,md}`, `coverage.{json,md}`, `brief/**`, `rescue/spine.md`, `graveyard/**`, and the post-pack `audit/**` oracle audit. The lifeboat is untrusted input: embark verifies its `manifest_sha256` against the on-disk tree and refuses a symlink or oversize file anywhere inside. +1. **Plan.** Map each record file to its target path and classify it `create`, `unchanged` (byte-identical), or a conflict. On **any** conflict embark writes nothing and refuses ([§ 4](#4-conflict-ux)). +2. **Write the record families verbatim** to their canonical target locations — ADRs to `.abcd/development/decisions/adrs/`, issues to `.abcd/work/issues/`, intents to `.abcd/development/intents/`, specs to `.abcd/development/specs/` — through two-layer containment (an `os.Root` boundary plus independent lexical path validation), skipping `unchanged` files. Bucketed families keep their source bucket (issues by state; intents into `drafts`/`planned`/`shipped`/`disciplines`/`superseded`; specs into `open`/`closed`). Terminology, docs, and `.abcd/memory/` are **not** embark families — they do not travel. +3. **Re-inject the current abcd marker block** into the target `CLAUDE.md` between BEGIN/END markers (idempotent) — never AGENTS.md, and never a verbatim copy of lifeboat prose. The block is the modular-rules-loader block (per itd-3); principles surface through the rules loader's domain rules on demand by prompt-keyword recall. +4. **Report** the outcome to the surface: the `written`/`unchanged` counts, the per-family counts, the marker action, the coverage blanks a human must answer, and the report-only files that informed the run but were not written. + +*(Design target, not yet shipped: write voyage provenance to `~/.abcd/voyage//embark/provenance.json` — see [§ 7](#7-voyage-layout-embarkdisembark-provenance-and-history).)* ## 4. Conflict UX @@ -95,24 +90,18 @@ How to resolve all conflicts? Single decision, transparent (shows scope before asking). The conflict list is a value core hands back; if the operator wants it on disk, the surface writes it — core does not. -## 5. Asset curation (per `_manifest.json` classifications) - -- **`keep`** → copy verbatim -- **`adapt`** → transparent prompt with curator's suggested adaptation; user accepts/edits/skips -- **`drop`** → skip silently - ## 6. Acceptance > **Scope:** criteria naming the design-target surface (the `scan` sub-verb and > its `--deep`, and the `from` flags `--force` / `--archive` / `--refresh-audit`) > are gated on that surface shipping — they describe the intended behaviour, not > the current binary (which ships `from` + `probe`, no flags). The shipped-surface -> criteria (`from`, `probe`, bare invocation, the emptiness refusal) hold today. +> criteria (`from`, `probe`, bare invocation, the conflict refusal) hold today. -- **Given** any abcd-aware terminal, **when** the user runs bare `/abcd:embark`, **then** the dispatcher shows whether a lifeboat is detectable in the current location, the available sub-verbs (`from `, `probe`; design target: `scan`; later phase: `from-spec-kit`), and suggested next actions — bare invocation never mutates state. -- **Given** a lifeboat at `` and an empty target repo (only `.git/`, `LICENSE`, `README.md`), **when** `/abcd:embark from ` runs, **then** the target receives the press-release interview as the first interaction, the amended press release becomes `.abcd/development/brief/README.md`, and all sections in [§ 3](#3-scaffold-steps) land at canonical locations. +- **Given** any abcd-aware terminal, **when** the user runs bare `/abcd:embark`, **then** the dispatcher prints help listing the shipped sub-verbs (`from`, `probe`) and the global `--json` flag — bare invocation never mutates state. +- **Given** a lifeboat at `` and a conflict-free target, **when** `/abcd:embark from ` runs, **then** the four record families (ADRs, issues, intents, specs) land at their canonical locations, the current abcd marker block is re-injected into the target `CLAUDE.md`, and everything else in the lifeboat informs the report but is never written. - **Given** a repo disembarked to ``, **when** `embark from ` runs in an empty target, **then** the round-trip completes with no shorthand and no special case — `` is an ordinary explicit path. (There is no `home`, and no sub-verb resolves one; matches disembark's parallel rule for sub-verb-agnostic resolution.) -- **Given** a non-empty target repo, **when** `/abcd:embark from ` runs without `--force`, **then** the command refuses, core returns the conflict list **without writing any file**, and the surface renders it. +- **Given** a target holding a file that conflicts with a planned write, **when** `/abcd:embark from ` runs, **then** the command refuses, core returns the conflict list **without writing any file**, and the surface renders it as one bulk report — a target that merely holds unrelated files is not a conflict. - **Given** a non-empty target with conflicts and `--force`, **when** `embark from --force` runs, **then** the bulk conflict prompt fires once with a summary of all conflicts, and the chosen resolution is applied uniformly. - **Given** the user runs `/abcd:embark scan`, **when** the command completes, **then** the directories found carrying a parseable `_provenance.json` are listed ranked by mtime with their detected source repo, no unpacking occurs, and the user is shown candidates ready to pass to `embark from `. *(What `scan` searches is settled; **where** it searches is the open question above, and this criterion cannot be made checkable until that is decided.)* - **Given** the user runs `/abcd:embark scan --deep`, **when** the command completes, **then** the search widens — the exact widening depends on the same open question. @@ -120,8 +109,7 @@ Single decision, transparent (shows scope before asking). The conflict list is a - **Given** a lifeboat containing `.abcd/rp/workspace.json` and RP installed on the embarker, **when** `embark from ` runs, **then** the user is asked whether to register the workspace with RP and the choice is applied. - **Given** a lifeboat containing `.abcd/rp/workspace.json` and RP *not* installed, **when** `embark from ` runs, **then** the command warns gracefully and continues without failing. - **Given** the user passes `--refresh-audit`, **when** `embark from --refresh-audit` runs, **then** the oracle product audit re-runs against the current lifeboat content and the drift vs the disembark-time audit is reported. -- **Given** an asset manifest entry classified as `adapt`, **when** `embark from ` applies asset curation, **then** the user is shown the curator's suggested adaptation and asked transparently to accept / edit / skip. -- **Given** an `embark from ` run completes, **then** `~/.abcd/voyage//embark/provenance.json` exists with `source_path`, `source_manifest_sha256`, `timestamp`, and `files_written` populated (per [§ 7](#7-voyage-layout-embarkdisembark-provenance-and-history)); `embark/from//` is absent unless `--archive` was passed. +- *(Design target, not yet shipped.)* **Given** an `embark from ` run completes, **then** `~/.abcd/voyage//embark/provenance.json` exists with `source_path`, `source_manifest_sha256`, `timestamp`, and `files_written` populated (per [§ 7](#7-voyage-layout-embarkdisembark-provenance-and-history)); `embark/from//` is absent unless `--archive` was passed. - **Given** an `embark from --archive` run completes, **then** the input lifeboat is copied verbatim to `~/.abcd/voyage//embark/from//` and the path is referenced from `provenance.json`. ## 7. Voyage layout — embark/disembark provenance and history @@ -130,7 +118,7 @@ Lifeboat *operations* (embark, disembark) write provenance and history to **`~/. ``` ~/.abcd/voyage// ← operator level, keyed like the history store; never committed -├── embark/ +├── embark/ ← design target, not yet shipped │ ├── provenance.json ← source path, manifest hash, timestamp, files written │ └── from// ← --archive: verbatim copy of input lifeboat (opt-in) └── disembark/ @@ -139,13 +127,12 @@ Lifeboat *operations* (embark, disembark) write provenance and history to **`~/. Keying on the source repo's root-commit SHA is what lets voyage survive a rename, a remote move, or the source repo being deleted entirely — and it is why voyage may hold absolute source paths without ever putting them in a committed file. -**`embark/provenance.json`** records, for the embark that bootstrapped this repo: +**`embark/provenance.json`** *(design target, not yet shipped)* records, for the embark that bootstrapped this repo: - `source_path` (the `` argument passed to `embark from `) - `source_manifest_sha256` (hash of input lifeboat's `_provenance.json` + file tree) - `timestamp` -- `files_written` (target paths created in `.abcd/development/` — including ADRs at `.abcd/development/decisions/adrs/` — the native spec store, `.abcd/memory/`, etc.) -- `press_release_amended_diff` (diff between input lifeboat's `press-release.md` and the brief that landed at `.abcd/development/brief/README.md` after the [§ 3 step 1](#3-scaffold-steps) interview) +- `files_written` (target paths created for the record families — ADRs at `.abcd/development/decisions/adrs/`, issues at `.abcd/work/issues/`, intents at `.abcd/development/intents/`, specs at `.abcd/development/specs/`) - `audit_drift` (only if `--refresh-audit`: drift vs disembark-time `audit/press-release-oracle-*`) **`embark/from//`** — opt-in via `embark --archive`. Verbatim copy of the input lifeboat at the moment of embark, for the case where the source repo will disappear. Off by default; the `source_path` + hash in `provenance.json` is sufficient when the source repo persists. @@ -154,15 +141,18 @@ Keying on the source repo's root-commit SHA is what lets voyage survive a rename ```json { - "timestamp": "2026-05-04T14:30:00Z", + "schema_version": 2, + "event": "disembark", + "at": "2026-05-04T14:30:00Z", "manifest_sha256": "abc123...", - "files": ["README.md", "press-release.md", "rescue/specs/spc-1-foo.md", ...], - "label": "post-itd-7-ship", - "oracle_backend": "host-delegated", - "oracle_verdict": "SHIP" + "source_name": "abcd-cli", + "source_root_sha": "def456...", + "dest": "../abcd-lifeboat", + "files": 214, + "bytes": 1048576 } ``` -> **`shared_with` is dropped** (adr-35): nothing produces it, and an empty field is a lie in a schema. Reinstate it when something actually populates it. +> **The entry is deliberately minimal** (adr-35): it carries the manifest identity, the source name and root SHA, the destination, and the file/byte counts — not a verdict, a label, or a `shared_with` recipient list. An empty field is a lie in a schema; add any of these when something actually populates it. Manifests are small (file list + hashes, not contents); the log answers "what did this repo's lifeboat look like at point T?" without keeping stale snapshots around. Acceptance criteria for voyage writes live in [`02-disembark.md § 7`](02-disembark.md#7-acceptance) (disembark-side history.jsonl) and [§ 6](#6-acceptance) above (embark-side provenance.json + optional --archive copy). diff --git a/.abcd/development/brief/04-surfaces/04-launch.md b/.abcd/development/brief/04-surfaces/04-launch.md index edbd52a..7248b50 100644 --- a/.abcd/development/brief/04-surfaces/04-launch.md +++ b/.abcd/development/brief/04-surfaces/04-launch.md @@ -6,8 +6,8 @@ The shipped verb surface is the `--dry-run` flag on `abcd launch` — a read-only preview of the bundle and gates. Bare `abcd launch` never mutates state: it refuses (exit 1) with a hint to pass `--dry-run`; a bare-as-status render is a design target, unshipped. The sub-verb design: -- **`/abcd:launch ship`** — **partly shipped: the RELEASE CUT only** (itd-73 derived versioning + itd-67's changelog slice). `abcd launch ship` derives the version and the record set from what shipped since the newest tag, runs the surface guardrail, and — with `--changelog-json` — validates the host-composed prose against the record set (the completeness bijection) and writes the dated `CHANGELOG.md` heading `.github/workflows/auto-release.yml` turns into a tag; `commands/abcd/launch.md` carries the emit → compose → ingest orchestration and the `release-changelog-composer` agent it dispatches. The ARTEFACT half stays a design target (itd-65 gate suite + itd-72 publishing): `internal/core/launch` carries the `Ship` engine, unwired, and the verb neither packages, commits, tags, nor publishes. The design for that half: cut a curated release artefact from the one repo: run pre-flight gates, filter the artefact (default-deny, `.abcd/**` excluded by packaging), stamp the version, and on a `v*` tag publish a GitHub Release ([adr-28](../../decisions/adrs/0028-single-repo-curated-release.md)). The flow described in §§ 1–6 below is this sub-verb's behaviour. Flag-shaped modifiers `--allow-dirty` and `--allow-doc-warnings` belong to this sub-verb's design; the shipped verb accepts only `--dry-run` and the global `--json`. There is no version flag — the version is derived, never authored ([adr-31](../../decisions/adrs/0031-derived-versioning-from-intents.md), see [§ 3](#3-versioning--marketplace)). -- **`/abcd:launch dry-run`** — shipped as the `--dry-run` flag (the plugin command `commands/abcd/launch.md` maps its `[dry-run]` argument hint onto `abcd launch --dry-run`; the binary has no `dry-run` subcommand). **Report-only preview, always exit-0** (a preview never blocks). It runs the parts of the pre-flight suite that exist today: as of spc-64 the **secret + PII scan gate** (the native scanners, see [§ 1](#1-pre-flight-gates)) runs for real in report-only mode and prints what it *would* refuse on (a finding, or a fail-closed reason such as "scanner unavailable"); the **installability smoke** runs for real at its light tier (see [§ 1](#1-pre-flight-gates)); the remaining gates (marker-block, documentation-auditor) are the gate-suite intent's (itd-65) and render as "(not yet implemented)". It also produces the would-be artefact manifest, without writing the release artefact. dry-run is **not** "ship minus publish": running the *full* gate suite and **hard-failing** on a finding (exit non-zero) is the full `ship` verb's behaviour (itd-65 + itd-72), not dry-run's. +- **`/abcd:launch ship`** — **partly shipped: the RELEASE CUT only** (itd-73 derived versioning + itd-67's changelog slice). `abcd launch ship` derives the version and the record set from what shipped since the newest tag, runs the surface guardrail, and — with `--changelog-json` — validates the host-composed prose against the record set (the completeness bijection) and writes the dated `CHANGELOG.md` heading `.github/workflows/auto-release.yml` turns into a tag; `commands/abcd/launch.md` carries the emit → compose → ingest orchestration and the `release-changelog-composer` agent it dispatches. The `Ship` engine is wired: `abcd launch ship` is a live subcommand, and `--payload-dir ` stages the versioned release payload — the derived version stamped into the payload's `plugin.json`/`marketplace.json` and lockstep-proved before return. Commit, tag, and publish stay a design target (itd-65 gate suite + itd-72 publishing): the verb neither commits, tags, nor publishes. The full-cut design: cut a curated release artefact from the one repo: run pre-flight gates, filter the artefact (default-deny, `.abcd/**` excluded by packaging), stamp the version, and on a `v*` tag publish a GitHub Release ([adr-28](../../decisions/adrs/0028-single-repo-curated-release.md)). The flow described in §§ 1–6 below is this sub-verb's behaviour. Flag-shaped modifiers `--allow-dirty` and `--allow-doc-warnings` belong to this sub-verb's design; the shipped `ship` accepts `--changelog-json` and `--payload-dir` (plus the global `--json`), and bare `abcd launch` accepts only `--dry-run` and the global `--json`. There is no version flag — the version is derived, never authored ([adr-31](../../decisions/adrs/0031-derived-versioning-from-intents.md), see [§ 3](#3-versioning--marketplace)). +- **`/abcd:launch dry-run`** — shipped as the `--dry-run` flag (the plugin command `commands/abcd/launch.md` maps the `dry-run` of its `[dry-run | ship]` argument hint onto `abcd launch --dry-run`; the binary has no `dry-run` subcommand). **Report-only preview, always exit-0** (a preview never blocks). It runs the parts of the pre-flight suite that exist today: as of spc-64 the **secret + PII scan gate** (the native scanners, see [§ 1](#1-pre-flight-gates)) runs for real in report-only mode and prints what it *would* refuse on (a finding, or a fail-closed reason such as "scanner unavailable"); the **installability smoke** runs for real at its light tier (see [§ 1](#1-pre-flight-gates)); the remaining gates (marker-block, documentation-auditor) are the gate-suite intent's (itd-65) and render as "(not yet implemented)". It also produces the would-be artefact manifest, without writing the release artefact. dry-run is **not** "ship minus publish": running the *full* gate suite and **hard-failing** on a finding (exit non-zero) is the full `ship` verb's behaviour (itd-65 + itd-72), not dry-run's. ## 1. Pre-flight gates @@ -29,7 +29,7 @@ Pre-flight report written to `.abcd/logbook/launch//preflight.{json,m ## 2. Curated release artefact (default-deny) -- **Include:** the shipped include list, pinned in `.abcd/config/launch-payload.json`: `.claude-plugin/` (holds both `plugin.json` and the ONE canonical `marketplace.json` — there is no root-level `marketplace.json`), `commands/`, `scripts/`, `docs/` (user-facing only), `README.md`, `LICENSE`, `.gitignore`. `skills/`, `agents/`, and `hooks/` are absent from the shipped include list (`agents/` and `hooks/` are present in the tree — `agents/` carries the reviewer/synthesis prompt catalog and `hooks/hooks.json` is load-bearing — but are not packaged into the release artefact; `skills/` does not exist in the tree at all). +- **Include:** the shipped include list, pinned in `.abcd/config/launch-payload.json`: `.claude-plugin/` (holds both `plugin.json` and the ONE canonical `marketplace.json` — there is no root-level `marketplace.json`), `commands/`, `agents/` (the reviewer/synthesis prompt catalog), `hooks/` (`hooks.json` is load-bearing), `scripts/`, `docs/` (user-facing only), `README.md`, `LICENSE`, `.gitignore`. `skills/` is absent from the include list and does not exist in the tree at all. - **Exclude:** `.abcd/` (entire namespace — `development/` (brief, decisions, intents, plans, principles, research, roadmap), plus the design-target tiers `memory/` and `logbook/` once they exist), and patterns from `.gitignore`. Per [adr-28](../../decisions/adrs/0028-single-repo-curated-release.md) the wholesale `.abcd/` exclusion (incl. `.abcd/memory/**`) is a **packaging filter over the one tree**, not a copy between two repos: the release artefact carries plugin code, never the project's design record or knowledge store. The lifeboat is **not** among the excluded tiers because it is not an in-tree tier at all: `/abcd:disembark` is read-only over the source repo and lands the lifeboat at an operator-chosen destination outside it, with its voyage log at the operator level under `~/.abcd/voyage/` ([adr-35](../../decisions/adrs/0035-lifeboat-as-coverage-experiment.md)) — so there is nothing for the payload filter to exclude. The spc-38 restrictive-licence gate is NOT this artefact's gate — its real consumer is the lifeboat (`/abcd:disembark`), the surface that publishes curated project memory/provenance ([adr-35](../../decisions/adrs/0035-lifeboat-as-coverage-experiment.md)). At launch the gate is future/inert; the shipped `dry-run` renders no licence verdicts (a diagnostic preview of the gate's verdicts in `dry-run` is part of the gate's own design, unshipped). - **Override:** the include list in `.abcd/config/launch-payload.json` is the packaging override (the only mechanism that can put a path *into* the release artefact). The deny is **structural** (`internal/core/launch/bundle.go`, per adr-28): no include entry can promote a denied namespace — a `.abcd/**` line is never promoted — so nothing can re-include `.abcd/memory/**`. The spc-38 gate's own evaluation-input allowlist belongs to that gate's design (future/inert, see above) and is documented-distinct from packaging: it re-includes files into the gate's *own evaluation input*, never into the release artefact — two mechanisms, never one name. @@ -98,11 +98,10 @@ version-writing refuses and the escalation stands. Concretely, `ship`: renderer stamps the version into the artefact content only — it never mutates the working-tree manifests. 3. Records the version + changelog entry in the marketplace metadata at the ONE - canonical `.claude-plugin/marketplace.json` (never a root-level copy). The - changelog entry conforms to - `changelog-entry.schema.json` - (validated programmatically by this bump step, per - [adr-20](../../decisions/adrs/0020-manifest-version-lockstep.md)). + canonical `.claude-plugin/marketplace.json` (never a root-level copy). + **Later phase** ([adr-20](../../decisions/adrs/0020-manifest-version-lockstep.md)): + the changelog entry conforms to a `changelog-entry.schema.json`, validated + programmatically by this bump step. 4. Refreshes any other version references generated from the config slug. **Anti-drift (present state).** The two manifests in the artefact describe one diff --git a/.abcd/development/brief/04-surfaces/05-intent.md b/.abcd/development/brief/04-surfaces/05-intent.md index cc18bc4..b99235f 100644 --- a/.abcd/development/brief/04-surfaces/05-intent.md +++ b/.abcd/development/brief/04-surfaces/05-intent.md @@ -110,11 +110,12 @@ Until then, `kind_notes` is the free-text descriptor. ``` 1. /abcd:intent "" (canonical bare quoted create) - ├─ Interview captures press release (headline, what user does, customer quote, scope) - ├─ Picks persona at random from .abcd/development/personas.json for the customer quote ├─ Assigns next itd-N ID (capture-stable once assigned) - ├─ Requires `## Acceptance Criteria` section (per itd-1) — refuses to write if missing/malformed - ├─ LLM classifier writes advisory `suggested_kind` to frontmatter (default: standalone) + ├─ Seeds the draft skeleton: frontmatter (kind: null, suggested_kind: null, + │ spec_id: null, reclassification_history: [], builds_on: [], severity: minor) + │ and a body carrying the seed text under `## Why This Matters`, with a + │ placeholder `## Acceptance Criteria` section (per itd-1) for the human to + │ fill before planning └─ Writes intents/drafts/itd-N-.md (no spec created yet) 2. /abcd:intent plan [...] (when ready to commit to work) @@ -145,13 +146,7 @@ Until then, `kind_notes` is the free-text descriptor. └─ Moves intents/drafts/itd-N-*.md → intents/disciplines/itd-N-*.md (no `status` field — the disciplines/ directory IS the active state) -3. /abcd:intent ship (kick off implementation; standalone + bundle only) - ├─ If intent is in drafts/: runs full pipeline (plan + plan-review first) - ├─ Kicks off implementation via the native ship/run seam - └─ Returns; spec continues in the native spec store - (Disciplines have no `ship` step — they are continuously active once in disciplines/.) - -4. Spec marked done in the native spec store (standalone + bundle: work complete) +3. Spec marked done in the native spec store (standalone + bundle: work complete) ├─ native spec-store `spec close` close-hook (spc-36) → intent lifecycle reconcile (spc-28) └─ Moves intents/planned/itd-N-*.md → intents/shipped/itd-N-*.md (+ enqueues a review) (For bundles, all member intents move together when the shared spec closes.) @@ -161,7 +156,7 @@ Until then, `kind_notes` is the free-text descriptor. └─ Compares as-shipped reality against original press release + acceptance criteria └─ Per-criterion verdicts (MET / MET_WITH_CONCERNS / NOT_MET / INCONCLUSIVE) written to the intent file's "Audit Notes" section (verdict of record), - plus a per-run audit/review-/ logbook report. + with the review request staged under .abcd/.work.local/reviews/. For bundles, review runs per-intent (each member's acceptance criteria checked separately against the same delivered reality). (spc-12 ships only this MANUAL review surface. spc-28 ships the on-close hook @@ -170,7 +165,7 @@ Until then, `kind_notes` is the free-text descriptor. it; spc-6 disowned auto-firing). Until then, the `## Audit Notes` of a freshly shipped intent stays empty until /abcd:intent review is run by hand.) -5. /abcd:intent reclassify --kind (late reclassification) +4. /abcd:intent reclassify --kind (late reclassification) ├─ Records the change in intent.reclassification_history (date + from-kind + to-kind + reason) ├─ Moves the file between directories (e.g., drafts/ → disciplines/) as needed ├─ For supersession: --kind superseded --by moves the file to superseded/, @@ -197,11 +192,12 @@ On demand: intent-fidelity-reviewer (shape-classification role) scans the corpus |---|---|---| | `/abcd:intent` (no args) | Help + status: lists intents grouped by directory (drafts / planned / shipped / disciplines / superseded), shows commands, surfaces shape suggestions from `intent-fidelity-reviewer`, suggests next actions for any intent in flight | — | | `/abcd:intent ""` | **Canonical create** (spc-30/itd-46): a leading quoted seed is the canonical create entry. Interview-driven capture (press release with persona quote + acceptance criteria); assigns `itd-N`; LLM classifier writes advisory `suggested_kind`. A leading quote always creates — never falls through to bare render | writes to `drafts/itd-N-.md` (no spec created) | -| `/abcd:intent refine ` | Interactive refinement of an existing intent (sharpen press release, fill open questions, update scope, add/edit acceptance criteria) | (stays in current state) | +| `/abcd:intent new ` | Deprecated alias for the quoted-text create (`abcd intent ""`); files a draft from the text | writes to `drafts/itd-N-.md` (no spec created) | | `/abcd:intent grill ` | Socratic adversarial interview that stress-tests an intent for vagueness, missing acceptance, hidden assumptions before planning. Glossary-aware once `terminology/` exists. `--brief-section ` flag for stress-testing a brief section instead. (per itd-27) | (stays in current state) | -| `/abcd:intent plan [...]` | Validates the frozen PRD exists (refuses if `prd_path` is null — the grill → PRD flow produces it first; GR002 blocker, suppressed-as-info for `prd_grandfathered`); lints acceptance criteria; proposes `kind` (standalone / bundle-member / discipline) based on `suggested_kind` + cross-references; user confirms or overrides; binds `kind:`; routes to the kind-specific lifecycle path (see § 5 freeze sequence) | varies by kind: `drafts/` → `planned/` (standalone, bundle) or `drafts/` → `disciplines/` (discipline) | -| `/abcd:intent ship ` | Kicks off implementation via the native ship/run seam. Only valid for `kind: standalone` or `kind: bundle-member`. If intent is still in `drafts/`, runs the full pipeline first (plan + plan-review). On spec completion the lifecycle hook completes the move automatically; this command can also force-move if hook missed. **Disciplines have no `ship` step** — they are continuously active once in `disciplines/`. | (eventually) → `shipped/` (or no-op for disciplines) | +| `/abcd:intent plan ` | Plans a draft: mints its native spec, injects the bidirectional link (intent `spec_id` ↔ spec `intent`), and moves the file `drafts/` → `planned/`. Single intent ID. | `drafts/` → `planned/` | +| `/abcd:intent ready ` | **Implement-readiness gate** (read-only): reports whether an intent is ready to implement — planned, with acceptance criteria and a written spec body. Exit 0 ready / 1 not ready / 2 fault. | (no move) | | `/abcd:intent review ` | **Role 1 — single-document fidelity.** Compares the intent's press release + acceptance criteria against delivered reality (code, configs, docs, tests). Per-criterion verdicts (`MET` / `MET_WITH_CONCERNS` / `NOT_MET` / `INCONCLUSIVE`) appended to the intent's `## Audit Notes`. Aligns with the spec store's `plan-review` / `impl-review` / `completion-review` vocabulary — same operation shape (adversarial second opinion), different opponent (press release vs engineering spec). spc-12 ships this **manual** verb; spc-28 ships the on-close hook (move `planned → shipped` + queue a review), but auto-running the reviewer off that queue is still deferred (no spec currently owns it; spc-6 disowned auto-firing). | (stays) | +| `/abcd:intent review ingest --verdict-json ` | Ingests a host-delegated intent-fidelity verdict JSON, validated fail-closed against the schema and the parked review request, and writes its per-criterion verdict into the shipped intent's `## Audit Notes` (or quarantines a bad payload). | (no move; updates `## Audit Notes`) | | `/abcd:intent consistency []` | **Role 2 — cross-document fidelity.** Surfaces five judgement categories (terminology drift, premise contradictions, scope leakage, sequencing impossibilities, naming conflicts) across briefs + intents. **Bare** scans the whole corpus; **with ``** narrows to one intent's relationship with the rest. Findings land in `.abcd/logbook/audit/consistency-/report.{json,md}`. The judgement half + on-demand verb are spc-29's; mechanical-half categories and pre-commit hook are deferred follow-ups (see `.abcd/work/issues/` `[spc-29 follow-up]` entries). | (stays) | | `/abcd:intent shape []` | **Role 3 — kind classification.** Examines whether an intent's declared `kind` (the noun) still fits the corpus. Surfaces *suggested* reclassifications across three live types: `kind_change`, `bundle`, `supersession`. **Bare** scans the corpus; **with ``** checks one intent. Pairs with `reclassify` (action verb that commits a `shape` finding). On-demand only per spc-29; findings land in `.abcd/logbook/audit/shape-/report.{json,md}`. Concurrency via `flock(2)` on `.abcd/coordination/shape.lock` (see § 7). Scheduled / continuous invocation is a deferred follow-up (see `.abcd/work/issues/` `[spc-29 follow-up]`). | (stays) | | `/abcd:intent reclassify --kind [--reason ]` | Late reclassification (e.g., a standalone intent realised to be a bundle-member; a draft realised to be a discipline; a shipped intent superseded by a later one). Records `reclassification_history` entry; moves the file between directories as the new kind dictates. `--kind superseded --by ` is the supersession path: the file moves to `superseded/`, frontmatter records `superseded_by: itd-M` AND `kind_at_supersession: ` so future readers know what shape the intent had when retired. | varies by destination kind | @@ -222,13 +218,17 @@ slug: # NOTE: no `status:` field. Lifecycle state is encoded by directory location only # (drafts/ | planned/ | shipped/ | disciplines/ | superseded/) — uniform across all kinds. # Per the 2026-05-08 directive: "directory IS the state, no cached mirror." -kind: null # set by /abcd:intent plan: "standalone" | "bundle-member" -suggested_kind: null # advisory, written by capture-time LLM classifier; can be ignored -bundle: null # for kind: bundle-member, the bundle ID +# The eight keys below are the canonical seed skeleton the quoted-text create writes: spec_id: null # or spc-N (set by /abcd:intent plan) -impact: null # "additive" | "breaking" | "fix" — the compatibility judgement the derived version is computed from. Never "internal" (a press-release-first intent is user-facing by definition), and required before the intent may move to shipped/ +kind: null # set by /abcd:intent plan: "standalone" | "bundle-member" +suggested_kind: null # advisory, written by a capture-time classifier; can be ignored reclassification_history: [] # appended to by /abcd:intent reclassify (kind changes only) -surface_history: [] # appended when an intent's user-facing surface shape changes (e.g., skill → sub-verb, top-level command → sub-verb, command → flag) WITHOUT changing kind. Distinct from reclassification_history. Schema: { date, from, to, reason }. Hand-edited or written by future tooling. +builds_on: [] # itd-N ids this intent builds on +severity: minor # seeded capture-grain severity of the draft +# Added later, not part of the seed skeleton: +# bundle: — for kind: bundle-member, the bundle ID +# impact: additive|breaking|fix — the compatibility judgement the derived version is computed from. Never "internal" (a press-release-first intent is user-facing by definition), and required before the intent may move to shipped/ +# surface_history: [] — appended when an intent's user-facing surface shape changes (e.g., skill → sub-verb, top-level command → sub-verb, command → flag) WITHOUT changing kind. Distinct from reclassification_history. Schema: { date, from, to, reason } --- # @@ -309,7 +309,7 @@ Both the press-release intent and the frozen PRD are immutable input artefacts p ## 6. Acceptance gates and bidirectional link verification -`internal/core/lint` (cross-cutting; its shipped wiring is `abcd docs lint` and the `cmd/record-lint` gate) gates `/abcd:intent plan` promotion. The shipped subset is the `intent_lifecycle` rule — the directory/kind/`spec_id` invariants and the `status:`-key ban below; the remaining gates (the `IL0xx` codes per [`05-internals/06-lint.md`](../05-internals/06-lint.md)) are plan-time design, a later phase. It verifies: +`internal/core/lint` (cross-cutting; its shipped wiring is `abcd docs lint` and the `cmd/record-lint` gate) gates `/abcd:intent plan` promotion. The shipped subset is the `intent_lifecycle` rule (the directory/kind/`spec_id` invariants and the `status:`-key ban below), the `intent_impact_valid` rule (the `impact:` field's legal value set), and `persona_registry` (press-release quote attributions resolve to the persona roster); the remaining gates (the `IL0xx` codes per [`05-internals/06-lint.md`](../05-internals/06-lint.md)) are plan-time design, a later phase. It verifies: - **Acceptance criteria present and well-formed** (per the itd-1 discipline): every intent in `drafts/`, `planned/`, and `disciplines/` has a `## Acceptance Criteria` section with at least one Given-When-Then bullet. Intents cannot be promoted from `drafts/` → `planned/` (or `drafts/` → `disciplines/`) without this. Hard block. - **`kind` is set on intents in `planned/`, `shipped/`, `disciplines/`, and `superseded/`.** Intents in `drafts/` may have `kind: null` (binding decision is at plan time). Lint blocks promotion from `drafts/` if `kind` cannot be inferred + confirmed. @@ -319,7 +319,7 @@ Both the press-release intent and the frozen PRD are immutable input artefacts p - **`kind: discipline` lives only in `disciplines/` or `superseded/`.** Discipline-kind intents in `drafts/` are an error (caught at plan time when the user picks a kind; rare). - **No intent has a `status` field — across any kind.** Lifecycle state is encoded by directory location only (`drafts/` / `planned/` / `shipped/` / `disciplines/` / `superseded/`). The 2026-05-08 directive removed the cached-mirror option: directory IS the state, no exceptions. Lint hard-blocks any frontmatter containing a `status:` key (shipped lint rule: `intent_lifecycle`, severity: blocker; templates and existing files were stripped in the 2026-05-08 sweep). The historical `status: draft | planned | shipped` field on standalone/bundle-member intents has been retired; uniform "directory is canonical" applies to all kinds. - Every intent in `drafts/` has `spec_id: null` (drafts have no plan yet). -- Every intent in `planned/` has non-null `spec_id` pointing to an existing native-spec-store `-*.md` whose frontmatter `intent` field matches the intent's `id` (or contains the intent's `id` as one of a list, for bundle-member intents). +- Every intent in `planned/` has `spec_id: null` (unscheduled) or a `spc-N` id; a non-null `spec_id` points to an existing native-spec-store `-*.md` whose frontmatter `intent` field matches the intent's `id` (or contains the intent's `id` as one of a list, for bundle-member intents). - Every intent in `shipped/` has a closed-status linked spec (or `spec_id: null` + a `manual_ship_reason` field for the no-spec case). - Discipline-kind intents have `spec_id: null` always (disciplines never get a spec; this is structurally enforced). - **Every intent in `superseded/` has both `superseded_by: ` AND `kind_at_supersession: `.** The first names the successor; the second preserves what shape the intent had when it was retired (standalone vs bundle-member vs discipline change the meaning of "superseded"). Both are required. If `kind_at_supersession: bundle-member`, the intent ALSO carries `bundle_at_supersession: ` — preserving the bundle membership at retirement time even though the active `bundle:` field is `null`. @@ -344,8 +344,8 @@ The verb `review` is chosen for Role 1 to align with the spec store's review voc This is product-tier review. The opponent is the codebase. Distinct from the spec store's `completion-review` (engineering-tier — code vs spec) — both can pass or fail independently, and disagreement between them is signal: the spec may have mistranslated the press release. **Two passes, two destinations.** Role 1 judges two document kinds and writes each verdict to its own destination: -- the **itd-1 acceptance pass** (a shipped intent) writes per-criterion verdicts into that intent's `## Audit Notes` (the verdict of record) **plus** a per-run `audit/review-/` logbook report; -- the **itd-37 `MG004` pass** (a native spec's `## Modification Grammar`) writes its `PASS` / `FAIL` verdict to an `audit/spec-mg-/` logbook receipt — native specs have no `## Audit Notes` section, so the verdict cannot land in-file. +- the **itd-1 acceptance pass** (a shipped intent) writes per-criterion verdicts into that intent's `## Audit Notes` (the verdict of record), with the review request staged under `.abcd/.work.local/reviews/`; +- the **itd-37 `MG004` pass** (a native spec's `## Modification Grammar`) writes its `PASS` / `FAIL` verdict to an `audit/spec-mg-/` receipt under the local ephemeral logs tier — native specs have no `## Audit Notes` section, so the verdict cannot land in-file (a later phase). **What spc-12 ships.** spc-12 ships the **discipline-judgement subset** of `/abcd:intent review` — the itd-1 per-criterion acceptance verdicts and the itd-37 `MG004` boilerplate check, with their writers and receipts. The broader **press-release prose review** (the `honoured` / `diverged` / `missing` buckets below) and other prose/terminology/PRD-fidelity outputs are **deferred** to a later spec. spc-12 also ships the **manual** review surface; spc-28 ships the on-close hook (move `planned → shipped` + queue a review on that transition). Auto-running the reviewer off that queue is still deferred — no spec currently owns it; spc-6 disowned auto-firing. @@ -441,19 +441,16 @@ The three live `suggestion_type` values this role produces: > **Deferred follow-up** (recorded in `.abcd/work/issues/` under `[spc-29 follow-up]`): pre-commit hook wiring for continuous shape scanning, and the `shape(...)` function's `mode="pre_commit"` parameter is preserved as a seam but no hook invokes it. Discipline subtype clustering ("once enough disciplines exist, surfaces 'three disciplines have similar `kind_notes`; consider formalising a subtype'") was named in earlier itd-34 drafts and is *not* shipped by spc-29 — it is not a live suggestion type. -### Logbook layout +### Review and audit trail layout -All review/audit verbs write to `.abcd/logbook/audit/-/`. The directory's name (`audit/`) reflects "this is the on-disk audit trail" regardless of which verb produced it; the sub-tier prefix names the verb. Verb-to-sub-tier mapping: +Shipped `/abcd:intent review` keeps its record in the intent file itself: `review ingest --verdict-json ` writes the per-criterion verdict into the shipped intent's `## Audit Notes` section (the verdict of record), and the emit path stages an ephemeral review request under `.abcd/.work.local/reviews/` (gitignored, report-only). Idempotency and review state live in that one committed place — directory/file-as-truth, no side database. -- `/abcd:intent review ` → `audit/review-/` (Role 1 itd-1 acceptance pass, single-document fidelity per itd-1) -- `/abcd:intent review` (MG004 boilerplate pass) → `audit/spec-mg-/` (Role 1 itd-37 `MG004` check on a native spec's `## Modification Grammar`; one per-run batch receipt, one `results[]` entry per spec — native specs have no `## Audit Notes` section, so the verdict lands here, per itd-37) +The later-phase review/audit verbs write their per-run receipts under the local ephemeral logs tier, `.abcd/.work.local/logs/audit/-/`, where the `audit/` name reflects "this is the on-disk audit trail" regardless of which verb produced it and the sub-tier prefix names the verb: + +- `/abcd:intent review` (MG004 pass) → `audit/spec-mg-/` (Role 1 itd-37 `MG004` check on a native spec's `## Modification Grammar`; one per-run batch receipt, one `results[]` entry per spec — native specs have no `## Audit Notes` section, so the verdict lands here, per itd-37 — a later phase) - `/abcd:intent consistency` → `audit/consistency-/` (Role 2, cross-document fidelity per itd-48, which superseded itd-31) - `/abcd:intent shape` → `audit/shape-/` (Role 3, shape classification per itd-34) - `/abcd:audit chain` → `audit/chain-/` (conversation/edit-history Merkle, default application per itd-16 — a later phase) - `/abcd:audit lifeboat ` → `audit/lifeboat-/` (lifeboat-artefact integrity per itd-35 — a later phase) -`chain` and `lifeboat` are sub-verbs of `/abcd:audit` (the umbrella verb — a later phase; its backing intents itd-16 and itd-35 sit in `intents/drafts/`); `review`, `consistency`, `shape` are sub-verbs of `/abcd:intent`. The `review` verb produces *two* sub-tiers — its itd-1 acceptance pass writes `review-/` (alongside the verdict-of-record in the intent's `## Audit Notes`) and its itd-37 `MG004` pass writes `spec-mg-/` — because Role 1 judges two document kinds (a shipped intent and a native spec) and only the intent has an in-file `## Audit Notes` section. Bare `/abcd:audit` and bare `/abcd:intent` are status+help only per the universal bare-command-as-help convention. Layout codified in `05-internals/04-universal-patterns.md § 6`. - -## 8. Reports - -`intent-report.{json,md}` in `.abcd/logbook/intent//` — one report per `/abcd:intent` invocation (ships with the `/abcd:intent` surface itself; see the delivery-state note at the top of this page). JSON has full detail; MD has skim summary. +`chain` and `lifeboat` are sub-verbs of `/abcd:audit` (the umbrella verb — a later phase; its backing intents itd-16 and itd-35 sit in `intents/drafts/`); `review`, `consistency`, `shape` are sub-verbs of `/abcd:intent`. Bare `/abcd:audit` and bare `/abcd:intent` are status+help only per the universal bare-command-as-help convention. diff --git a/.abcd/development/brief/04-surfaces/06-capture.md b/.abcd/development/brief/04-surfaces/06-capture.md index e261ce7..07badb1 100644 --- a/.abcd/development/brief/04-surfaces/06-capture.md +++ b/.abcd/development/brief/04-surfaces/06-capture.md @@ -8,13 +8,13 @@ See itd-4 for the full intent. Ledger schema lives in the Go binary (`internal/c | Subcommand | Purpose | File movement | |---|---|---| -| `/abcd:capture` (no args) | Help + status: shows the most recent open issues, suggests next actions (next-action hints are a design target — a later phase, not yet in the shipped render). Bare invocation owns the default status/help render — there is no implicit-default filtered list. | — | +| `/abcd:capture` (no args) | Help + status: shows the most recent open issues and closes with a capture-vs-intent routing hint. Bare invocation owns the default status/help render — there is no implicit-default filtered list. | — | | `/abcd:capture ""` | Fast path: appends a structured issue entry to the ledger with auto-assigned `iss-N`; provenance and taxonomy are caller-supplied flags, each with a default — `--severity`, `--category`, `--source`, `--found-during`, `--found-at`, `--slug`, `--blocked-by` (comma-separated `iss-N` dependency edges; blocked/priority status is derived from `blocked_by`, never stored) | writes `.abcd/work/issues/open/iss-N-.md` | | `/abcd:capture list --open` | Query the ledger for currently-open issues (flag immediately adjacent — earned SD001 exception) | — | | `/abcd:capture list --resolved` | Query the ledger for resolved issues | — | | `/abcd:capture list --wontfix` | Query the ledger for wontfix issues | — | | `/abcd:capture list --all` | Query the ledger across all three states | — | -| `/abcd:capture promote ` | **Design target (spc-30, itd-46 — itd-46 sits in `intents/planned/`; a later phase, not yet built).** Promote an issue to an intent draft: reads the issue, runs the agent-driven intent-new interview seeded with the issue body, and writes the four-field back-link (`source_issue`/`promoted_to` scalars + `related_issues` ↔ `related_intents` lists). Skill-orchestrated — never a `capture promote` CLI sub-verb. | (issue stays; intent created in `drafts/`) | +| `/abcd:capture promote ` | Promote an issue to an intent draft: skill-orchestrated (never a `capture promote` CLI sub-verb), it hands the issue body to `abcd intent ""`, which files a new draft under `intents/drafts/` seeded from that text. The reciprocal four-field back-link (`source_issue`/`promoted_to` scalars + `related_issues` ↔ `related_intents` lists) onto the `iss-N` record is written by hand — no engine verb writes that edge. | (issue stays; intent created in `drafts/`) | | `/abcd:capture resolve ""` | Mark issue resolved | `open/` → `resolved/` | | `/abcd:capture wontfix ""` | Explicit non-action decision | `open/` → `wontfix/` | @@ -24,8 +24,8 @@ message. The four flagged forms above are the only earned SD001 exception under the capture surface; each flag must appear immediately adjacent to `list`, never pipe-joined into a single token. There is no implicit-default filtered list — bare `/abcd:capture` is what renders -status and recent captures (next-action hints are a design target, a -later phase, not yet in the shipped render). +status and recent captures, closing with a capture-vs-intent routing +hint. ## 2. Ledger structure @@ -72,11 +72,11 @@ A later phase, not yet built — the migration rides the `abcd dev-sync work` su - **Given** an abcd-installed repo, **when** the user runs `/abcd:capture "review nitpick: T7 cache_ttl_days dead-config alternative"`, **then** a new file `.abcd/work/issues/open/iss-N-.md` exists with frontmatter populated and the captured text in the body. - **Given** an existing issue at `.abcd/work/issues/open/iss-3-foo.md`, **when** the user runs `/abcd:capture resolve iss-3 "fixed in spc-7 task 4"`, **then** the file moves to `.abcd/work/issues/resolved/iss-3-foo.md` with the resolution recorded. -- **Given** an existing issue, **when** the user runs `/abcd:capture promote iss-N` (design target per spc-30/itd-46 — a later phase, not yet built), **then** the agent-driven intent-new interview runs seeded with the issue body; the resulting draft intent's frontmatter has `source_issue: iss-N` and `related_issues: [iss-N]`; the issue's frontmatter has `promoted_to: itd-M` and `related_intents: [itd-M]` — the four-field back-link written transactionally by `link_promoted_issue`. +- **Given** an existing issue, **when** the user runs `/abcd:capture promote iss-N`, **then** the issue body is handed to `abcd intent ""`, which files a new draft intent under `intents/drafts/` seeded from that text; the reciprocal four-field back-link (`source_issue`/`promoted_to` scalars + `related_issues` ↔ `related_intents` lists) is recorded by hand, since no engine verb writes that edge. - **Given** a fresh `/abcd:ahoy` upgrade with an existing scratch buffer under `.abcd/.work.local/`, **when** `dev-sync` runs (a later phase, not yet built — § 3), **then** every entry in that scratch buffer is promoted to the structured ledger with provenance noting "migrated from `.abcd/.work.local/` scratch". - **Given** a ledger containing 5 open issues, **when** the user runs `/abcd:capture list --open` (the flag is explicit — there is no implicit default), **then** the output lists all 5 with id, state, severity, and slug, in derived-priority order — unblocked issues first, then severity (`critical` → `nitpick`); rows blocked by an open dependency are demoted and annotated with their open blockers. - **Given** a ledger with a mix of open, resolved, and wontfix issues, **when** the user runs `/abcd:capture list --all`, **then** every issue across all three states is listed; the equivalent unfiltered CLI form `abcd capture list` (no flag) instead exits 2 with a "choose a filter" message. -- **Given** an abcd-installed repo, **when** the user runs bare `/abcd:capture` (no args), **then** the output is a read-only status render — counts (`open N · resolved N · wontfix N`) and up to 10 most recent open issues (suggested next actions are a design target, a later phase, not yet in the shipped render) — and no `iss-*.md` file is created, moved, or field-mutated by the invocation itself. +- **Given** an abcd-installed repo, **when** the user runs bare `/abcd:capture` (no args), **then** the output is a read-only status render — counts (`open N · resolved N · wontfix N`), up to 10 most recent open issues, and a capture-vs-intent routing hint — and no `iss-*.md` file is created, moved, or field-mutated by the invocation itself. ## 5. Implementation status @@ -88,7 +88,8 @@ A later phase, not yet built — the migration rides the `abcd dev-sync work` su - **Command flow:** delivered by `spc-21-abcdcapture-command-flow-text-ingest`. - **Legacy `.abcd/.work.local/` scratch migration:** design target per `spc-22-workissuesmd-migration-promote-legacy` — a later phase, not yet built (rides the `dev-sync` surface, § 3). - **intent-fidelity-reviewer cross-check:** delivered by `spc-23-intent-fidelity-reviewer-extension`. -- **`promote ` bridge:** design target per `spc-30-symmetric-abcdintent-and-abcdcapture` - (itd-46; itd-46 sits in `intents/planned/` — a later phase, not yet built). - Skill-orchestrated issue → intent-new interview → four-field back-link, with - the `PR000`/`PR001`/`PR002` lint family guarding the contract. +- **`promote ` bridge:** the skill-orchestrated flow leans on + `abcd intent ""`, delivered by `spc-7-abcd-intent-quoted-text-create-symmetric` + (itd-46). The issue body is handed to that create path, which files a new draft; + the reciprocal four-field back-link onto the `iss-N` record is written by hand — + no engine verb writes that edge. diff --git a/.abcd/development/brief/04-surfaces/07-memory.md b/.abcd/development/brief/04-surfaces/07-memory.md index 8ecab4a..ffe1928 100644 --- a/.abcd/development/brief/04-surfaces/07-memory.md +++ b/.abcd/development/brief/04-surfaces/07-memory.md @@ -9,9 +9,9 @@ For the **substrate spec** (page-class enum, source-class taxonomy, curator beha Bare `/abcd:memory` shows status + help + render of current memory state — never mutates state. Per the [bare-command-as-render discipline](../02-constraints/04-naming.md). Current sub-verbs (each does something bare cannot): - **Bare `/abcd:memory`** — render: page count by class (e.g., "23 session_memory + 8 external_pdf + 4 oracle_review + 2 spec_modification_grammar"), last-ingest timestamp, recent contradictions surface, suggested next actions. No mutation. Quotation-budget headroom per source renders READ-ONLY from the spc-39 `.coverage_index.json`: when the index is present AND fingerprint-fresh (a read-only crawl recomputes the current fingerprint and matches the stored one) it shows per-source warn/block headroom; fingerprint drift shows a "stale — run /abcd:memory lint" hint; an absent index an info line; a malformed index or crawl failure a non-fatal "headroom unavailable" line. The bare render never rebuilds or mutates the index. -- **`/abcd:memory ingest `** — register an external source (transcript / article / URL; PDF is a later-phase seam — the binary rejects PDF sources with a clear error until a text-extraction dependency is wired) as typed entity/topic pages with citation frontmatter, appending to the ingest log. The host agent is the distiller: it reads the source, produces the `DistilledPage` JSON array, and passes it via the load-bearing `--pages-json ` flag (required for a new source; an already-known source re-ingests from the registry without it); the binary computes provenance, licence, and content hash, validates every page, and writes atomically. **Default: do NOT store original.** Flag-shaped modifier: `--keep-original` (opt-in storage at `.abcd/memory/sources/.`; the lifeboat licence gate — `/abcd:disembark`, NOT launch, per adr-18 — refuses publish without an explicit allowlist entry; launch excludes `.abcd/**` wholesale per [`04-launch.md § 2`](04-launch.md#2-curated-release-artefact-default-deny)). +- **`/abcd:memory ingest `** — register an external source (transcript / article / URL; PDF is a later-phase seam — the binary rejects PDF sources with a clear error until a text-extraction dependency is wired) as typed entity/topic pages with citation frontmatter, appending to the ingest log. The host agent is the distiller: it reads the source, produces the `DistilledPage` JSON array, and passes it via the load-bearing `--pages-json ` flag (required for a new source; an already-known source re-ingests from the registry without it); the binary computes provenance, licence, and content hash, validates every page, and writes atomically. **Default: do NOT store original.** Flag-shaped modifier: `--keep-original` (opt-in storage at `.abcd/memory/sources/.`; the later-phase lifeboat licence gate — `/abcd:disembark`, NOT launch, per adr-18 — is designed to refuse publish without an explicit allowlist entry; launch excludes `.abcd/**` wholesale per [`04-launch.md § 2`](04-launch.md#2-curated-release-artefact-default-deny)). - **`/abcd:memory ask `** — query memory by domain + class; synthesise an answer with citations (every citation references `source.class` + `citation` + `source_hash`); optionally file the result back as a new memory page (flag-driven: `--file-back` with `--page-json `, the host-produced answer page; `--top-n ` sets retrieval depth, 0 uses the pinned default). -- **`/abcd:memory lint` (spc-39)** — full-store curator health-check: per-page quotation budgets (`MQ001`), cumulative source coverage (`MQ002`), coverage-unavailable diagnostic (`MQ003`, info), source-class single-class advisory (`MS001`), cross-class without weighting note (`MS002`), missing licence on `external_*` (`ML001`). ALWAYS crawls the full repo store, rebuilds the regenerable `.coverage_index.json`, emits findings to `.abcd/logbook/memory/lint-/report.{json,md}`. Exit: blockers → nonzero; warn-only → 0 (curator advisory — see [`06-lint.md §2`](../05-internals/06-lint.md#2-severity-model)). Mutates no memory-store state (coverage index + logbook report are its only writes). Per ADR-13's write/lint split, the spc-38 write core ships ingest/ask/bare; spc-39 ships this lint family ONLY — contradictions are rendered by spc-38's reconciliation (surfaced by the bare render), orphan/stale-claim audits are deferred. +- **`/abcd:memory lint` (spc-39)** — full-store curator health-check: per-page quotation budgets (`MQ001`), cumulative source coverage (`MQ002`), coverage-unavailable diagnostic (`MQ003`, info), source-class single-class advisory (`MS001`), cross-class without weighting note (`MS002`), missing licence on `external_*` (`ML001`). ALWAYS crawls the full repo store, rebuilds the regenerable `.coverage_index.json`, emits findings to `.abcd/.work.local/logs/memory/lint-/report.{json,md}`. Exit: blockers → nonzero; warn-only → 0 (curator advisory — see [`06-lint.md §2`](../05-internals/06-lint.md#2-severity-model)). Mutates no memory-store state (coverage index + logbook report are its only writes). Per ADR-13's write/lint split, the spc-38 write core ships ingest/ask/bare; spc-39 ships this lint family ONLY — contradictions are rendered by spc-38's reconciliation (surfaced by the bare render), orphan/stale-claim audits are deferred. ## 1. Default flow — distil, cite, discard @@ -67,7 +67,7 @@ DISCARD ORIGINAL (default behaviour) line only (`## [YYYY-MM-DD HH:MM] | `) ``` -`--keep-original` opts the user into storing the original at `.abcd/memory/sources/.`. The spc-38 restrictive-licence gate refuses to publish anything under `.abcd/memory/sources/` unless `.abcd/launch-allowlist.json` explicitly names the file. Per adr-18 this gate is the **lifeboat's** (`/abcd:disembark`), NOT launch's — launch excludes `.abcd/**` wholesale and never publishes `.abcd/memory/sources/`; the gate is future/inert at launch. +`--keep-original` opts the user into storing the original at `.abcd/memory/sources/.`. Later phase: the spc-38 restrictive-licence gate refuses to publish anything under `.abcd/memory/sources/` unless `.abcd/launch-allowlist.json` explicitly names the file — the shipped ingest classifies restrictive licences but never gates. Per adr-18 this gate is the **lifeboat's** (`/abcd:disembark`), NOT launch's — launch excludes `.abcd/**` wholesale and never publishes `.abcd/memory/sources/`; the gate is future/inert at launch. ## 2. Acceptance Criteria (Given-When-Then, per itd-1) @@ -75,16 +75,16 @@ See [the full acceptance criteria](../../intents/planned/itd-36-memory-unificati - **Bare**: bare `/abcd:memory` renders current state; never mutates. - **Ingest default-no-original**: original NOT stored unless `--keep-original`; citation + source_hash recorded; quotation budget applied per page (enforced at lint time by spc-39's `MQ001`, never at ingest). -- **Ingest with `--keep-original`**: original stored at `.abcd/memory/sources/.`; the lifeboat licence gate (`/abcd:disembark`, not launch — adr-18) refuses publish without allowlist. +- **Ingest with `--keep-original`**: original stored at `.abcd/memory/sources/.`; the later-phase lifeboat licence gate (`/abcd:disembark`, not launch — adr-18) is designed to refuse publish without allowlist. - **Ask**: synthesises answer with per-citation provenance (class + citation + source_hash); optionally files result back. - **Lint (spc-39, not spc-38 behaviour)**: emits `MQ001` / `MQ002` / `MQ003` / `MS001` / `MS002` / `ML001` codes; cumulative coverage uses span-level dedup. spc-38 writes `licence: unknown` explicitly; spc-39's `ML001` is what lints it. - **Schema extension on existing**: existing flat-named pages preserved; `index.md` generated over them; `source.class: session_memory` backfilled as default. - **Cross-consumer registry**: the provenance substrate's `.abcd/memory/.sources_index.json` (per itd-36/spc-38, the provenance capability; not to be confused with the ahoy history store) is shared with itd-26 loot (a later phase, not yet built); same hash → same registry entry. -## 3. Logbook layout +## 3. Runtime-log layout ``` -.abcd/logbook/memory/ +.abcd/.work.local/logs/memory/ ├── ingest-/ # later phase — comes with the principle-distiller curator (Phase 6) │ ├── ingest-report.{json,md} # source path, sha256, distilled page count, citation, licence │ └── distil-trace.json # principle-distiller per-page output trace (debug) @@ -94,13 +94,13 @@ See [the full acceptance criteria](../../intents/planned/itd-36-memory-unificati └── report.{json,md} # lint findings: MQ001/MQ002/MS001/MS002/ML001 with locations ``` -Only `lint-/report.{json,md}` is written by the shipped store (spc-39's lint); ingest and ask write no logbook reports — the `ingest-` and `ask-` trees come in a later phase with the principle-distiller curator. Per universal pattern 6 (logbook-as-reports-only); coordination locks live at `.abcd/coordination/` not under logbook. +Only `lint-/report.{json,md}` is written by the shipped store (spc-39's lint); ingest and ask write no reports — the `ingest-` and `ask-` trees come in a later phase with the principle-distiller curator. Runtime artefacts (reports only) live in the gitignored `.abcd/.work.local/logs/` tier. ## 4. Composition with adjacent surfaces -- **`/abcd:disembark`** (unbuilt — spc-17 stubs) exports curated project memory/provenance into the lifeboat (designed behaviour; itd-36 doesn't change disembark's source-mapping). What the lifeboat is specified to carry is the curated provenance surface named in [`02-disembark.md §5`](02-disembark.md) — `research/pitfalls.{json,md}`, `assets/_manifest.json`'s provenance/classification, and root `_provenance.json` — **not** a verbatim `.abcd/memory/` payload; declaring an exact `.abcd/memory/`-verbatim payload is deferred to the disembark spec that wires the lifeboat packer (adr-18). The recovery-humility framing on disembark/embark applies: the lifeboat is the floor of recoverable theory, not theory itself. -- **`/abcd:embark`** (unbuilt — spc-17 stubs) unpacks `.abcd/memory/` into the receiving repo. Source-class enum carries forward; receiver runs `/abcd:memory lint` (spc-39) post-unpack to verify quotation budgets and licences haven't drifted. -- **`/abcd:launch`** does **not** consume the provenance substrate's licence gate (adr-18): the public launch payload excludes `.abcd/**` — including `.abcd/memory/**` — wholesale as policy, so launch never publishes the files the gate checks. The restrictive-licence gate's real consumer is the **lifeboat** (`/abcd:disembark`, above), the surface that publishes curated project memory/provenance; it refuses publish on restrictive-licence files and warns on `licence: unknown`. At launch the gate is future/inert — `/abcd:launch dry-run` renders its verdicts only as a diagnostic preview (see [`04-launch.md § 2`](04-launch.md#2-curated-release-artefact-default-deny) and adr-18). +- **`/abcd:disembark`** exports curated project memory/provenance into the lifeboat (designed behaviour; itd-36 doesn't change disembark's source-mapping). What the lifeboat is specified to carry is the curated provenance surface named in [`02-disembark.md §5`](02-disembark.md) — `research/pitfalls.{json,md}`, `assets/_manifest.json`'s provenance/classification, and root `_provenance.json` — **not** a verbatim `.abcd/memory/` payload; declaring an exact `.abcd/memory/`-verbatim payload is deferred to the disembark spec that wires the lifeboat packer (adr-18). The recovery-humility framing on disembark/embark applies: the lifeboat is the floor of recoverable theory, not theory itself. +- **`/abcd:embark`** unpacks a lifeboat's record families into the receiving repo; carrying `.abcd/memory/` forward is designed behaviour. Source-class enum carries forward; receiver runs `/abcd:memory lint` (spc-39) post-unpack to verify quotation budgets and licences haven't drifted. +- **`/abcd:launch`** does **not** consume the provenance substrate's licence gate (adr-18): the public launch payload excludes `.abcd/**` — including `.abcd/memory/**` — wholesale as policy, so launch never publishes the files the gate checks. The restrictive-licence gate's real consumer is the **lifeboat** (`/abcd:disembark`, above), the surface that publishes curated project memory/provenance; the later-phase gate is designed to refuse publish on restrictive-licence files and warn on `licence: unknown`. At launch the gate is future/inert — launch excludes `.abcd/**` wholesale, so `/abcd:launch dry-run` surfaces no licence-gate verdict (its dry-run gates are the secret/PII scan, marker-block, installability smoke, and documentation-auditor; see [`04-launch.md § 2`](04-launch.md#2-curated-release-artefact-default-deny) and adr-18). - **`/abcd:dredge`** (a later phase, itd-25) writes synthesis output to `.abcd/memory/__.md` with `source.class: dredge_synthesis`. Distinct verb (storage vs operation per dredge-pushback in idea-1 R4); shared destination namespace. - **Native** specs inherit from itd-37 modification grammar — at spec completion, `principle-distiller` (design target — Phase 6, per [`05-internals/01-agents.md`](../05-internals/01-agents.md); nothing ships this yet) extracts the spec's `## Modification Grammar` section into `spec_modification_grammar_.md` (append-only) and updates curator-merged `modification_grammar_.md` (compounding-curated). User does not invoke `/abcd:memory` for this — the extraction is designed to be automatic on spec completion. diff --git a/.abcd/development/brief/04-surfaces/08-abcd.md b/.abcd/development/brief/04-surfaces/08-abcd.md index c70b5d2..9ecce69 100644 --- a/.abcd/development/brief/04-surfaces/08-abcd.md +++ b/.abcd/development/brief/04-surfaces/08-abcd.md @@ -67,12 +67,11 @@ reconsidered — the gate is real, not a rubber stamp.) itd-20 was planned assuming a spc-17 probe stub for the bare top-level command would be *replaced*. Investigation found NO such stub: the spc-17 work is credited with probe/bare renders for the *sub-verb* surfaces. The `disembark`, -`embark`, `launch`, and `intent` CLI verbs all ship today; of their plugin -command files, `disembark`, `embark`, and `launch` are present while `intent` -has no `commands/abcd/intent.md` yet (iss-105 — the intent verb is CLI-only). -`commands/abcd/` ships `ahoy`, `audit`, `capture`, `consult`, `disembark`, -`docs`, `embark`, `history`, `ingest`, `launch`, `memory`, `prepare-this-repo`, -and `version` (13 files). The investigation found no spc-17 probe STUB behind the +`embark`, `launch`, and `intent` CLI verbs all ship today, and each has its +plugin command file present (`commands/abcd/disembark.md`, `embark.md`, +`launch.md`, `intent.md`). `commands/abcd/` ships `ahoy`, `audit`, `capture`, +`consult`, `disembark`, `docs`, `embark`, `history`, `ingest`, `intent`, +`launch`, `memory`, `prepare-this-repo`, and `version` (14 files). The investigation found no spc-17 probe STUB behind the top-level command to replace — the "stub replacement" premise is therefore recorded here as not-applicable. The top-level command file itself is present today (`commands/abcd.md`, referenced above and scaffolded with the repo), so a @@ -138,9 +137,10 @@ linked intents, or any spec store. ## Staleness thresholds (decided here) **Design target (itd-20; unbuilt).** No disembark-age, staleness, capping, or -timeout logic exists in the shipped binary — the status path is five `isDir` -checks (`.git`, `.abcd/development`, and the three work tiers; -`internal/core/core.go`) with no directory walks or thresholds. +timeout logic exists in the shipped binary — the status path is an existence +check on `.git` (so gitfile worktrees and submodules count as git repos) plus +`isDir` checks on `.abcd/development` and the three work tiers +(`internal/core/core.go`) with no directory walks or thresholds. | Signal | Threshold | Rationale | |--------|-----------|-----------| @@ -185,8 +185,9 @@ writes. **Design target (itd-20):** the two tests that would prove this — a static zero-mutation lint over the status-render module and an fs-snapshot test that asserts a render over a populated fixture repo mutates nothing at run time — are unbuilt. The shipped status path (`internal/core/core.go`, `Status`) -has only `TestStatusBareDir` and `TestStatusWithRecordAndGit` -(`internal/core/core_test.go`), which assert field values on temp dirs; there is +has `TestStatusBareDir`, `TestStatusGitfileWorktree`, and +`TestStatusWithRecordAndGit` (`internal/core/core_test.go`), which assert field +values on temp dirs; there is no distinct status-render module yet. ## Related documentation diff --git a/.abcd/development/brief/04-surfaces/09-reflect.md b/.abcd/development/brief/04-surfaces/09-reflect.md index 88028a0..41916bf 100644 --- a/.abcd/development/brief/04-surfaces/09-reflect.md +++ b/.abcd/development/brief/04-surfaces/09-reflect.md @@ -104,9 +104,10 @@ records `glossary_terms_used: core/voyage, core/persona`. ## Lifeboat forward requirement (grill Q6) The lifeboat must pack EVERY phase retrospective a voyage produced, so the full -reflection arc travels between voyages. disembark/embark are unbuilt (spc-17 -stubs), so this is a **documented forward requirement on the future disembark -spec** — NOT a behaviour this surface implements. It is recorded here and in the +reflection arc travels between voyages. Because `/abcd:reflect` is a design +target and produces no retrospectives yet, this is a **documented forward +requirement on the disembark pack** — NOT a behaviour this surface implements. +It is recorded here and in the itd-24 intent acceptance so a later reader treats it as a requirement, not a shipped capability. diff --git a/.abcd/development/brief/04-surfaces/10-docs.md b/.abcd/development/brief/04-surfaces/10-docs.md index 250ea94..37d7d51 100644 --- a/.abcd/development/brief/04-surfaces/10-docs.md +++ b/.abcd/development/brief/04-surfaces/10-docs.md @@ -15,8 +15,11 @@ The shipped verb surface is one sub-verb, `lint`: Bare `abcd docs` prints command usage (its one sub-verb, `lint`) — it does **not** render a status board; the bare-status convention is scoped to `ahoy`/`capture`/ -`memory` and bare `abcd`, not to `docs`. The global `--json` flag emits the -machine-readable finding list. +`memory`/`intent`/`spec` and bare `abcd`, not to `docs`. The global `--json` flag +emits the machine-readable finding list, and `docs lint` additionally accepts two +local flags: `--config` (path to the `docs-lint.json` it loads, default +`/.abcd/docs-lint.json`) and `--root` (repo root to lint, default the +current working directory). ## What it checks @@ -31,6 +34,11 @@ machine-readable finding list. - **Stray root markdown** — no stray markdown at the repo root (it belongs under `docs/`; the allowed root files are the fixed set — README, CHANGELOG, CONTRIBUTING, etc.). +- **Host-agnostic prose** — user-facing docs must not name a specific agent + harness or bundled tool. This repo's `.abcd/docs-lint.json` defines a family of + `harness/*` banned tokens (each a **blocker**) that catch such names, so the + published surface stays host-agnostic; the `` escape + covers the sanctioned exception (attribution). ## Output diff --git a/.abcd/development/brief/04-surfaces/11-history.md b/.abcd/development/brief/04-surfaces/11-history.md index dd42ca2..9d75852 100644 --- a/.abcd/development/brief/04-surfaces/11-history.md +++ b/.abcd/development/brief/04-surfaces/11-history.md @@ -5,8 +5,10 @@ redact-on-write archive of raw session transcripts, keyed on the repo's **root-commit SHA**. The store lives outside the repo at `~/.abcd/history//transcripts/`, with a per-repo `meta.json` (`root_commit`, `name`, `github`, and a corpus block) alongside it. `list` and -`show` **perform zero writes**; `capture` is the only write path, and it redacts -on write — no live secret or absolute home path survives capture. +`show` **perform zero writes**; the store has two write paths — the explicit +`capture` sub-verb and the automatic `abcd hook session-end` entrypoint wired on +the SessionEnd event (`hooks/hooks.json`) — and both redact on write, so no live +secret or absolute home path survives capture. ## Sub-verbs diff --git a/.abcd/development/brief/04-surfaces/12-version.md b/.abcd/development/brief/04-surfaces/12-version.md index 85a37e6..46823d1 100644 --- a/.abcd/development/brief/04-surfaces/12-version.md +++ b/.abcd/development/brief/04-surfaces/12-version.md @@ -13,8 +13,8 @@ emits `{ "name": "abcd", "version": "" }`. The plugin command (`commands/abcd/version.md`) reads the JSON and tells the user the `name` and `version`. Without `--json`, bare `abcd version` prints the version string only (e.g. `abcd dev` in a development build) — it does **not** render a status board; -the bare-status convention is scoped to `ahoy`/`capture`/`memory` and bare -`abcd`, not to `version`. +the bare-status convention is scoped to `ahoy`/`capture`/`intent`/`memory`/`spec` +and bare `abcd`, not to `version`. ## Where the version comes from diff --git a/.abcd/development/brief/04-surfaces/15-prepare-this-repo.md b/.abcd/development/brief/04-surfaces/15-prepare-this-repo.md index 1d786c2..4b33de3 100644 --- a/.abcd/development/brief/04-surfaces/15-prepare-this-repo.md +++ b/.abcd/development/brief/04-surfaces/15-prepare-this-repo.md @@ -7,11 +7,12 @@ the three-tier `.abcd/` layout, a nameless working-conventions section in manage repositories directly, so the command does by hand what the CLI will later take over, in a shape the CLI can adopt without unpicking. -It is a **host-delegated command**: no Go verb backs it, there is no bare-status -render and no binary sub-verbs. The whole workflow runs in the host agent from -the markdown in [`commands/abcd/prepare-this-repo.md`](../../../../commands/abcd/prepare-this-repo.md); -the abcd binary is never invoked. It takes no argument — it always operates on -the current repository. +It is a **host-delegated command**: no dedicated Go verb backs it and there is +no bare-status render. The workflow runs in the host agent from the markdown in +[`commands/abcd/prepare-this-repo.md`](../../../../commands/abcd/prepare-this-repo.md), +invoking the binary's read-only `abcd audit --json` for the engine-backed +conformance core. It takes no argument — it always operates on the current +repository. ## What it does @@ -30,23 +31,22 @@ the current repository. ## Flow -Five phases, each gated on the one before: +Four phases, each gated on the one before: -1. **Refuse unless owned** — origin-remote ownership check; stop if it fails. -2. **Orient** — read the abcd record from `$ABCD` (the abcd checkout, three +0. **Refuse unless owned** — origin-remote ownership check; stop if it fails. +1. **Orient** — read the abcd record from `$ABCD` (the abcd checkout, three levels up from the command file): the three-tier README, the brief, principles, ADRs, intents, `docs/` Diátaxis rules, and the lint configs as patterns. -3. **Audit** — run `abcd audit --json` for the engine-backed conformance core +2. **Audit** — run `abcd audit --json` for the engine-backed conformance core (the five convention rules), supplement it with the structure/principles judgement the binary does not make, write the gap report to the target's `.abcd/.work.local/scratch/`, and present it before any change. -4. **Adopt** — create the three tiers with a repo-specific `CONTEXT.md`, migrate +3. **Adopt** — create the three tiers with a repo-specific `CONTEXT.md`, migrate any historical `.work/` layout to the new tiers (propose then wait for sign-off; never leave a repo with both the old and new working-state homes), merge into - `AGENTS.md`, offer the commit gates. -5. **Attribution (opt-in)** — install the AI-disclosure hook only if the user - says the repo requires it. + `AGENTS.md`, offer the commit gates, and — only where the user says the repo + requires AI disclosure — install the attribution hook (opt-in). When abcd's own record has conflicting sources, the command trusts a fixed authority order: `AGENTS.md`, then `work/CONTEXT.md`'s live-constraints section, diff --git a/.abcd/development/brief/04-surfaces/16-audit.md b/.abcd/development/brief/04-surfaces/16-audit.md index eda26c0..f50f808 100644 --- a/.abcd/development/brief/04-surfaces/16-audit.md +++ b/.abcd/development/brief/04-surfaces/16-audit.md @@ -16,12 +16,14 @@ abcd audit --json ``` emits `{ "findings": [ … ], "skipped": [ … ] }`. Each finding carries a stable -`ruleId`, a `severity` (`error` or `warn`), a `file` and `line`, a `message`, and -a `fix`. `skipped` names rules whose enablement condition was not met (e.g. -`docs-currency` where there is no `docs/`), so a not-applicable rule reads as -skipped, not failed. Without `--json`, `abcd audit` prints a grouped, -doctor-style human report (severity glyph, rule id, `file:line`, message, indented -fix) and a summary tail. +`ruleId`, a `severity` (`error` or `warn`), a `file`, a `message`, a `fix`, and a +`policyInfo` rationale; content-scanning rules (`docs-currency`, `privacy-hygiene`) +additionally carry a `line`, while path-presence findings omit it. `skipped` names +rules whose enablement condition was not met (e.g. `docs-currency` where there is +no `docs/`), so a not-applicable rule reads as skipped, not failed. Without +`--json`, `abcd audit` prints a grouped, doctor-style human report (severity glyph, +rule id, `file:line`, message, indented fix) and a summary tail. The `--root` flag +audits a repo other than the current working directory. The exit code is Conftest's tri-state: `0` clean, `1` warnings only, `2` any error — so `abcd audit` gates a repo's CI as well as backing onboarding. diff --git a/.abcd/development/brief/04-surfaces/README.md b/.abcd/development/brief/04-surfaces/README.md index 823b990..c10cc06 100644 --- a/.abcd/development/brief/04-surfaces/README.md +++ b/.abcd/development/brief/04-surfaces/README.md @@ -1,6 +1,6 @@ # Surfaces — User-Facing Commands -The brief's user-facing command surface is the set enumerated below (not all are shipped yet — see [`06-delivery/`](../06-delivery) for current delivery state). Each has its own file with the surface contract: purpose, flow, acceptance criteria. (Operator-internal commands are wiring, not part of this user-facing surface — e.g. `run`, the autonomous-run operator surface over the pluggable run seam (`status`/`pause`/`resume`/`preflight`): a design target (itd-29, `intents/planned/`); no `run` verb or `commands/abcd/run.md` is on any shipped surface. The shipped `spec` and `rules` binary verbs are likewise CLI-only — `rules` is operator-internal (hook-driven rule injection; its bare render is read-only diagnostics), and `spec` is internal spec-store tooling for the intent lifecycle (`spec close` reconciles the linked intent) — neither is a user-facing `/abcd:` command, so neither carries a `commands/abcd/*.md` surface.) +The brief's user-facing command surface is the set enumerated below (not all are shipped yet — see [`06-delivery/`](../06-delivery) for current delivery state). Each has its own file with the surface contract: purpose, flow, acceptance criteria. (Operator-internal commands are wiring, not part of this user-facing surface — e.g. `run`, the autonomous-run operator surface over the pluggable run seam (`status`/`pause`/`resume`/`preflight`): a design target (itd-29, `intents/planned/`); no `run` verb or `commands/abcd/run.md` is on any shipped surface. The shipped `spec` and `rules` binary verbs are likewise CLI-only — `rules` is operator-internal (hook-driven rule injection; its bare render is read-only diagnostics), and `spec` is internal spec-store tooling for the intent lifecycle (`spec close` reconciles the linked intent) — neither is a user-facing `/abcd:` command, so neither carries a `commands/abcd/*.md` surface. The hidden `hook` verb is the Claude Code hook entrypoint set — `prompt-router`, `prompt-router-reset`, `session-start`, and `session-end` — live-wired from `hooks/hooks.json` (the plugin's production hook manifest); `session-end` is the automatic redact-and-store write path into the transcript store. Cobra's generated `completion` verb (per-shell autocompletion scripts) is operator-internal on the same footing. Neither carries a `commands/abcd/*.md` surface.) | # | Command | Status | Purpose | File | |---|---|---|---|---| @@ -21,7 +21,7 @@ The brief's user-facing command surface is the set enumerated below (not all are | 15 | `/abcd:prepare-this-repo` | shipped | Bring an owned repo up to abcd's conventions (interim bridge) — host-delegated command, no Go verb | [`15-prepare-this-repo.md`](15-prepare-this-repo.md) | | 16 | `/abcd:audit` | shipped | Check whether a repo conforms to the working conventions (three-tier layout, AGENTS.md router, durable decisions, docs currency, privacy hygiene) — read-only, tri-state exit; backs `prepare-this-repo` and gates CI (itd-85) | [`16-audit.md`](16-audit.md) | -The **Status** column is machine-checked: the `surface_coverage` record-lint rule asserts every `shipped` row has a backing surface (`commands/abcd/.md` or `skills//`) and every `staged` row (a design target) has none — and, in reverse, that every real surface has a row here. The bare `/abcd` top-level is binary-backed (no command file) and is exempt from the file check. Keeping this column honest is how the brief's surface set stays reconciled with the shipped binary; the semantic half — whether each row's *prose* matches binary behaviour — stays a release-gate agent check. +The **Status** column is machine-checked: the `surface_coverage` record-lint rule asserts every `shipped` row has a backing surface (`commands/abcd/.md` or `skills//`) and every `staged` row (a design target) has none — and, in reverse, that every real surface has a row here. The bare `/abcd` top-level is binary-backed (its command file is `commands/abcd.md`, not a `commands/abcd/.md` entry) and is exempt from the file check. Keeping this column honest is how the brief's surface set stays reconciled with the shipped binary; the semantic half — whether each row's *prose* matches binary behaviour — stays a release-gate agent check. **Bare-command-as-help is a universal abcd convention** — every command shows read-only status when invoked without args (bare `abcd`, `abcd ahoy`, `abcd capture`, `abcd memory` all render status). The **suggested-next-actions** half of this convention is a design target: no shipped bare invocation yet emits next actions (bare `abcd launch` currently only hints to pass `--dry-run`). Provides discoverability without forcing the user to remember subcommand names. diff --git a/.abcd/development/brief/05-internals/01-agents.md b/.abcd/development/brief/05-internals/01-agents.md index 9235b40..83821bf 100644 --- a/.abcd/development/brief/05-internals/01-agents.md +++ b/.abcd/development/brief/05-internals/01-agents.md @@ -1,6 +1,6 @@ # Agent Catalog -The catalog below declares **16 agents** (the design-target roster — not all are shipped yet; the **Status** column marks which exist today, and [`06-delivery/`](../06-delivery) carries current delivery state), each with declared JSON inputs/outputs (schemas owned by the core, `internal/core/schema`). Agents are **markdown**, host-delegated reviewers the host dispatches (adr-25); markdown is rendered, not authored by agents. +The catalog below declares the **16-agent design roster** (not all are shipped yet; the **Status** column marks which of these exist today, and [`06-delivery/`](../06-delivery) carries current delivery state); the shipped agents that live outside this roster are listed under [§ Shipped agents outside the design roster](#shipped-agents-outside-the-design-roster). Ten agent prompt files ship in `agents/` today. Each catalog agent declares JSON inputs/outputs (schemas owned by the core, `internal/core/schema`). Agents are **markdown**, host-delegated reviewers the host dispatches (adr-25); markdown is rendered, not authored by agents. | Agent | Pass | Status | Inputs (JSON) | Outputs (JSON) | |---|---|---|---|---| @@ -9,18 +9,31 @@ The catalog below declares **16 agents** (the design-target roster — not all a | `review-collator` | A | design target — Phase 6 | `.abcd/work/reviews/oracle-review-*` | `reviews-consolidated.json` | | `code-rescuer` | A | design target — Phase 6 | codemap adapter (when wired) or spec-window file selection | `code-principles.json` | | `chat-distiller` | B | design target — Phase 6 | spine entry + time-windowed transcript subset (per call) | per-call: emits `rationale-fill`, `unrecorded-decision`, `pitfall` delta entries; aggregated across calls into `research/rationale-fills.json`, `research/unrecorded-decisions.json`, `research/pitfalls.json` (delta). Density measured on user-message denominator per Phase 0 Measurement Deviation; see `research/phase/0/transcript-sampling.md`. | -| `principle-distiller` | C | design target — Phase 6 | `.abcd/memory/`, ADRs, conventions, `spec-essence.json` (spine), `code-principles.json`, `candidate-pitfalls.json` (from review-collator), Pass B distiller deltas | `principles.json` (domain-grouped, with four-source pitfall dedup by topic-hash) | +| `principle-distiller` | C | **shipped** — `disembark principles` | `.abcd/memory/`, ADRs, conventions, `spec-essence.json` (spine), `code-principles.json`, `candidate-pitfalls.json` (from review-collator), Pass B distiller deltas | `principles.json` (domain-grouped, with four-source pitfall dedup by topic-hash) | | `artefact-curator` | C | design target — Phase 6 | user-docs (tutorials/guides/reference/explanation), assets (logos/charts/screenshots) | `assets/_manifest.json` with per-item classification: `keep` (copy verbatim), `adapt` (suggest adaptation, embark prompts user), `drop` (skip silently). Also writes `docs/` lifeboat copies. | | `brief-composer` | C | design target — Phase 6 | `spec-essence.json`, `decisions-timeline.json`, `principles.json`, `code-principles.json`, `reviews-consolidated.json`, `rationale-fills.json`, `unrecorded-decisions.json`, `pitfalls.json`, `assets/_manifest.json` | `README.json` (lifeboat brief synthesising all Pass A/B/C inputs) | -| `press-release-composer` | C | design target — Phase 6 | spec-essence, decisions, principles, source CLAUDE.md/README/metadata | `press-release.json` + invokes the oracle seam for a product-thinker audit (host-delegated by default) → `audit/press-release-oracle-.json` | +| `press-release-composer` | C | **shipped** — `disembark press-release` | spec-essence, decisions, principles, source CLAUDE.md/README/metadata | `press-release.json` + invokes the oracle seam for a product-thinker audit (host-delegated by default) → `audit/press-release-oracle-.json` | | `issue-scout` | C (opt-in) | design target — Phase 6 | `.abcd/work/issues/.md` entries | annotated entries with "Related upstream" sections; uses `gh` CLI. **Peer-preferred:** delegates to a peer github-scout over MCP when one is present; this native agent is the default. Default: disabled in `config.json`; ahoy asks. | -| `lifeboat-oracle` | C (audit) | design target — Phase 6 | rendered MD + JSON corpus | `audit/oracle-.json` | +| `lifeboat-oracle` | C (audit) | **shipped** — `disembark oracle` | rendered MD + JSON corpus | `audit/oracle-.json` | | `embark-scaffolder` | embark | design target — Phase 6 | lifeboat JSONs + target probe | `scaffold-plan.json` + extended responsibilities for doc-architecture at scaffold time (legacy harvest) | | `launch-gatekeeper` | launch | design target — itd-65 (gate suite, per [adr-33](../../decisions/adrs/0033-launch-phase-ownership-tiered.md)) | scan results + payload manifest | `preflight.json` + extended responsibilities for doc updates and OWASP/security auditing at promotion time (legacy harvest) | -| `intent-fidelity-reviewer` | intent — **three roles, three verbs** (`review` / `consistency` / `shape`) | design target — spc-12 (`review` discipline subset) + spc-29 (`consistency` / `shape`) | varies by role (see [`04-surfaces/05-intent.md § 7`](../04-surfaces/05-intent.md#7-the-intent-fidelity-reviewer-agent-three-roles-three-verbs)) | varies by role | +| `intent-fidelity-reviewer` | intent — **three roles, three verbs** (`review` / `consistency` / `shape`) | **shipped** (`review` discipline subset, spc-12); `consistency` / `shape` design targets (spc-29) | varies by role (see [`04-surfaces/05-intent.md § 7`](../04-surfaces/05-intent.md#7-the-intent-fidelity-reviewer-agent-three-roles-three-verbs)) | varies by role | | `documentation-auditor` | subagent (pre-pack, post-scaffold, pre-promotion) | design target — Phase 6 (disembark/embark) + itd-65 (launch pre-promotion) | source `docs/` directory or lifeboat `docs/` directory | `documentation-audit-.json` — invoked by disembark (pre-pack), embark (post-scaffold), and launch (pre-promotion). Subagent-only; not user-invoked. Lifted from legacy `~/ABCDevelopment/.claude/agents/documentation-auditor` | | `reflection-composer` | reflect | design target — spc-83 (thin V1) | selected spc-66 phase-audit receipt (`.abcd/logbook/audit/phase-/report.json`) — per-bullet acceptance verdicts + `member_specs` + `done_total` | single JSON object with the five retrospective section keys (`went_well` / `could_improve` / `lessons_learned` / `decisions_made` / `metrics`); the deterministic reflect writer (`internal/core/reflect`) renders `.abcd/retrospectives//README.md`. Dispatched by `/abcd:reflect ` (itd-24). Phase-only grain. | +## Shipped agents outside the design roster + +Six further agent prompts ship in `agents/` today, outside the 16-agent design roster above — lifeboat/launch synthesis helpers and repo-workflow reviewers, each a **markdown**, host-delegated agent the host dispatches: + +| Agent | Purpose | +|---|---| +| `graveyard-interpreter` | Interpret a packed lifeboat's graveyard into cited lessons (each citing the layer-1/2 finding ids it rests on) — feeds `abcd disembark graveyard --lessons-json`. | +| `release-changelog-composer` | Compose one release cut's changelog prose, every line citing the record id it reports so the binary can prove the cut — feeds `abcd launch ship --changelog-json`. | +| `docs-currency-reviewer` | Semantic docs-currency review — verifies every user-facing claim against the code that implements it (the release-gate docs check). | +| `ruthless-reviewer` | Demanding senior code review — correctness, resource handling, error paths, API misuse, dead code — run before presenting a non-trivial diff. | +| `security-reviewer` | Adversarial security review of a diff or design touching a trust boundary (auth, secrets, network, input parsing, subprocess). | +| `sota-researcher` | Deep state-of-the-art research — ranked recommendations with evidence tiers and source attributions. | + ## `intent-fidelity-reviewer`'s three roles, three verbs The catalog row above declares one agent with three roles, sharing the agent's prompt scaffolding, oracle backend resolution, and receipts. Each role has its own user-facing verb under `/abcd:intent` — no role-by-kind dispatch: @@ -69,7 +82,7 @@ Every agent's prompt file carries declared frontmatter. Current fields: | Field | Required | Source | Purpose | |---|---|---|---| | `prompt_version` | yes | itd-5 | Semver of the agent prompt; bumps on any prompt change | -| `capability_scope` | yes | itd-5 extension (idea-4 jagged-frontier) | Declared task classes the agent is designed for. Object: `{ task_classes: [, ...], designed_for: "" }`. **`task_classes` is authored as a YAML inline list** — `task_classes: [spec_planning, audit]` on one line, never a block list of `- token` items (the frontmatter parser does not support a block list nested under a nested key). Validated by the prompt-lint checks in `internal/core/lint` for set-membership against the `task_classes` closed enum (owned by `internal/core/schema`; prose counterpart in `02-constraints/04-naming.md`, PR-to-extend). Cites Dell'Acqua et al. 2023 ("Navigating the Jagged Technological Frontier") as the framing source. | +| `capability_scope` | synthesis/composer prompts | itd-5 extension (idea-4 jagged-frontier) | Declared task classes the agent is designed for. Object: `{ task_classes: [, ...], designed_for: "" }`. **`task_classes` is authored as a YAML inline list** — `task_classes: [spec_planning, audit]` on one line, never a block list of `- token` items (the frontmatter parser does not support a block list nested under a nested key). Carried today by the five synthesis/composer prompts (`graveyard-interpreter`, `release-changelog-composer`, `press-release-composer`, `principle-distiller`, `lifeboat-oracle`); the reviewer prompts omit it. Set-membership lint in `internal/core/lint` against the `task_classes` closed enum (owned by `internal/core/schema`; prose counterpart in `02-constraints/04-naming.md`, PR-to-extend) is a design target — no shipped check reads the field. Cites Dell'Acqua et al. 2023 ("Navigating the Jagged Technological Frontier") as the framing source. | | `reads_untrusted_input` | conditional (agents that read untrusted input) | itd-5 | Boolean. `true` declares the agent reads attacker-influenceable input (transcripts, lifeboats, GitHub issues, commit messages, model-emitted reviews). When `true`, the agent MUST carry at least one canary fixture under `agents//fixtures/`; the prompt-lint checks in `internal/core/lint` enforce fixture *presence*. | **Deliberately omitted from agent frontmatter** (boundary against scope creep, per idea-4's static/dynamic split): `known_failure_modes` (runtime-appended events), per-task-class `model_id` history, plan-time capability gating output. These belong to the later-phase Frontier Awareness intent — capture-stable, no ID reserved. diff --git a/.abcd/development/brief/05-internals/03-configuration.md b/.abcd/development/brief/05-internals/03-configuration.md index 700b3e4..61b027a 100644 --- a/.abcd/development/brief/05-internals/03-configuration.md +++ b/.abcd/development/brief/05-internals/03-configuration.md @@ -364,16 +364,14 @@ abcd/ │ ├── abcd-capture/{SKILL.md, workflow.md} │ ├── commit-attribution/SKILL.md │ └── secrets-and-pii/SKILL.md # consolidated pii-protection + secret-scan -├── agents/ # 16 agents — see 01-agents.md (markdown, host-delegated) -│ ├── flow-essence.md / decision-archaeologist.md / review-collator.md / chat-distiller.md -│ ├── principle-distiller.md / artefact-curator.md / brief-composer.md / press-release-composer.md -│ ├── lifeboat-oracle.md / code-rescuer.md / issue-scout.md / embark-scaffolder.md -│ ├── launch-gatekeeper.md / intent-fidelity-reviewer.md / documentation-auditor.md -│ └── reflection-composer.md # /abcd:reflect retrospective composer (itd-24) -└── hooks/ # Claude Code event hooks — thin shims that shell to the binary - ├── hooks.json # UserPromptSubmit → prompt-router; SessionStart / PreCompact → reset - ├── prompt_router_hook # CARL-style rule injector (per itd-3); reads .abcd/rules.json + plugin defaults - └── prompt_router_reset # per-session dedup-state reset (SessionStart / PreCompact) +├── agents/ # 10 agents — see 01-agents.md (markdown, host-delegated) +│ ├── docs-currency-reviewer.md / graveyard-interpreter.md / intent-fidelity-reviewer.md +│ ├── lifeboat-oracle.md / press-release-composer.md / principle-distiller.md +│ ├── release-changelog-composer.md / ruthless-reviewer.md / security-reviewer.md +│ └── sota-researcher.md # plus per-agent fixtures/ dirs, README.md, CHANGELOG.md +└── hooks/ # Claude Code event hooks — each command shells directly to the binary + └── hooks.json # UserPromptSubmit → hook prompt-router; SessionStart → prompt-router-reset + session-start; + # PreCompact → prompt-router-reset; SessionEnd → session-end ``` The core is organised one package per capability under `internal/core/`, and the diff --git a/.abcd/development/brief/05-internals/05-prompt-quality.md b/.abcd/development/brief/05-internals/05-prompt-quality.md index 8fe5534..0635928 100644 --- a/.abcd/development/brief/05-internals/05-prompt-quality.md +++ b/.abcd/development/brief/05-internals/05-prompt-quality.md @@ -1,6 +1,6 @@ # Prompt Quality Infrastructure -16 agents = 16 prompt `.md` files (the canonical roster is [`01-agents.md`](01-agents.md)). Static prompts rot as models evolve and prompting best practices shift. abcd ships three layers (B+C+D) plus the itd-5 additions: +The 16-agent design roster ([`01-agents.md`](01-agents.md)) plans one prompt `.md` file per agent; ten prompt `.md` files ship in `agents/` today (six of them outside that roster — see [`01-agents.md § Shipped agents outside the design roster`](01-agents.md#shipped-agents-outside-the-design-roster)). Static prompts rot as models evolve and prompting best practices shift. abcd ships three layers (B+C+D) plus the itd-5 additions: **B — Per-agent golden-test fixtures.** Each agent spec ships 2–3 fixture inputs with expected output structure (JSON schema validation + oracle-judged "is this output good enough"). Fixtures live in `agents//fixtures/`. The `intent-fidelity-reviewer` fixtures are spc-12's; the generic `internal/core/prompttest` harness that runs fixtures in CI lands with the first Pass-A agent spec ([Phase 6](../../roadmap/phases/phase-6-lifeboat.md), the lifeboat pipeline), the point at which a second agent exists to generalize the runner over. Catches regressions when models change. diff --git a/.abcd/development/brief/05-internals/08-skills.md b/.abcd/development/brief/05-internals/08-skills.md index 0b6c4e9..8fef54f 100644 --- a/.abcd/development/brief/05-internals/08-skills.md +++ b/.abcd/development/brief/05-internals/08-skills.md @@ -6,19 +6,19 @@ abcd's surface namespace (`/abcd:`) is **commands only**. The skill/command dist A **skill** is a procedural-workflow surface — a markdown-encoded interview, audit, or guided pass that runs against existing material in the repo. Skills are auto-registered by Claude Code's plugin system from the plugin's `skills//` directory; abcd does not maintain a separate skill registry. -**abcd ships zero skills.** The three workflows once classified as skills (`consult`, `ingest`, `prepare-this-repo`) were reclassified as commands — each mutates state, which the boundary rule below makes command-shaped. An earlier version of this brief also proposed `/abcd:grill` as a skill; its **design target** is promotion to a sub-verb of `/abcd:intent` (per the round-2 command-structure review, see `04-surfaces/05-intent.md § 2`) — but neither `grill` nor the `intent` parent is on any shipped surface (no binary verb, no `commands/abcd/` file). Grill's mid-session glossary writes and per-session logbook output are command-shaped responsibilities — exactly the trigger for promotion this file describes below. +**abcd ships zero skills.** The three workflows once classified as skills (`consult`, `ingest`, `prepare-this-repo`) were reclassified as commands — each mutates state, which the boundary rule below makes command-shaped. An earlier version of this brief also proposed `/abcd:grill` as a skill; its **design target** is promotion to a sub-verb of `/abcd:intent` (per the round-2 command-structure review, see `04-surfaces/05-intent.md § 2`) — the `intent` parent ships (a binary verb with `commands/abcd/intent.md`), but `grill` is not among its sub-verbs. Grill's mid-session glossary writes and per-session report output are command-shaped responsibilities — exactly the trigger for promotion this file describes below. ## What's a command? A **command** is a stateful operation that creates, modifies, or moves artefacts. Commands have: -- A **brief surface file** (`04-surfaces/NN-.md`) describing acceptance criteria, interaction flow, and side effects (or a sub-verb row in an existing parent's surface file). Every shipped verb has one — `docs`, `history`, and `version` are chapters `10`–`12`. -- A **logbook subdirectory** (`.abcd/logbook///`) for per-invocation reports. +- A **brief surface file** (`04-surfaces/NN-.md`) describing acceptance criteria, interaction flow, and side effects (or a sub-verb row in an existing parent's surface file) — for example `docs`, `history`, and `version` are chapters `10`–`12`. The CLI-only binary verbs `changelog`, `rules`, and `spec` have a Go verb but no surface chapter. +- A **per-invocation report subdirectory** (`.abcd/.work.local/logs///`) for commands that emit run reports. - A **status + help** mode when called bare — the bare-status-board convention holds for `ahoy`, `capture`, and `memory` (and bare `abcd`); it is not universal (`version` prints only the version string, and the `docs`/`history` cobra parents print help/usage with no status board). -abcd ships **seven top-level commands**: `/abcd:ahoy`, `/abcd:capture`, `/abcd:docs`, `/abcd:history`, `/abcd:launch`, `/abcd:memory`, `/abcd:version`. **Design targets — not on any shipped surface (no binary verb, no `commands/abcd/` file):** `/abcd:disembark`, `/abcd:embark`, and `/abcd:intent` (design record in `04-surfaces/02-disembark.md`, `03-embark.md`, `05-intent.md`). The `intent` parent's design gives it the largest sub-verb tree (`refine`, `grill`, `plan`, `ship`, `review`, `consistency`, `shape`, `reclassify`, `link`), plus the canonical bare quoted create `/abcd:intent ""` — all design target, none shipped. See [`04-surfaces/`](../04-surfaces) for per-command detail. +abcd ships **eleven binary-backed top-level commands**, each with both a Go verb and a `commands/abcd/` file: `/abcd:ahoy`, `/abcd:audit`, `/abcd:capture`, `/abcd:disembark`, `/abcd:docs`, `/abcd:embark`, `/abcd:history`, `/abcd:intent`, `/abcd:launch`, `/abcd:memory`, and `/abcd:version`. The `intent` parent carries the largest sub-verb tree: `link`, `plan`, `ready`, and `review` ship (alongside the deprecated `new` alias and the canonical bare quoted create `/abcd:intent ""`); `refine`, `grill`, `ship`, `consistency`, `shape`, and `reclassify` are design targets. Three further binary verbs — `changelog`, `rules`, and `spec` — expose a Go verb but no `commands/abcd/` file, so they are CLI-only. See [`04-surfaces/`](../04-surfaces) for per-command detail. -Alongside the seven binary-backed verbs, abcd ships **three host-delegated commands** — `/abcd:consult`, `/abcd:ingest`, and `/abcd:prepare-this-repo` (chapters `13`–`15`). They are commands (state-mutating) but have **no Go verb**: the workflow runs in the host agent, so they have no bare-status render and no binary sub-verbs. This is the shape a command takes when its work is host-delegated rather than owned by the transport-agnostic core. +Alongside the eleven binary-backed verbs, abcd ships **three host-delegated commands** — `/abcd:consult`, `/abcd:ingest`, and `/abcd:prepare-this-repo` (chapters `13`–`15`). They are commands (state-mutating) but have **no Go verb**: the workflow runs in the host agent, so they have no bare-status render and no binary sub-verbs. This is the shape a command takes when its work is host-delegated rather than owned by the transport-agnostic core. ## Skill vs command — decision criteria @@ -35,7 +35,7 @@ A surface is a **command** when ANY of: - Output includes *new or modified artefacts* (files, directory moves, frontmatter updates) — even alongside findings. - Re-running has different effects (idempotent or not, but state-mutating). - The surface needs an `acceptance` block, side-effect documentation, and (often) a checkpoint/resume protocol. -- The surface writes to a `.abcd/logbook//` subdirectory. +- The surface writes run reports to a `.abcd/.work.local/logs//` subdirectory. When in doubt, ship as a command. The earlier "ship as a skill first; promote on mutation" guidance was overturned by the round-2 review: by the time you discover a skill is mutating state, downstream contracts have hardened around the skill shape and rework is expensive. Better to recognise command-shape up front. diff --git a/.abcd/work/DECISIONS.md b/.abcd/work/DECISIONS.md index 8d5674e..3db87c5 100644 --- a/.abcd/work/DECISIONS.md +++ b/.abcd/work/DECISIONS.md @@ -674,3 +674,30 @@ parallel-agent merge contention bites. general rule: any new whole-tree traversal states which of {entries, depth, aggregate bytes} bounds it, because a per-item cap and a count cap multiply and their product is not a bound. + +- 2026-07-22 — A release is a release regardless of path: the clean-cutover + manual roll follows the SAME two-commit release-branch shape as a derived + ship (the content commit, then a receipts commit carrying the sha-keyed + PROMOTE receipts for docs-currency-reviewer and iss35-brief-surface-crosscheck). + The v0.4.0 roll landed as one commit with no receipts; the receipt gate + refused fail-closed — its first genuine firing, and correct. Recovery uses + the workflow's own escape hatch (tag on the receipts commit; content = tag^). + Rejected: weakening the required-gates list to unblock the tag — that edits + the release contract the whole programme was built to preserve. + +- 2026-07-22 — Detector findings are triaged adversarially BEFORE fixing, and + the numbers justify it: the full-depth iss35 crosscheck returned 102 + discrepancies; independent refuters confirmed 95 and killed 7 (two + cross-direction duplicates, two wrong-reality, three legitimately + staged/exempt). 93% precision is high enough to trust the detector and low + enough that unfiltered fixing would have written seven falsehoods into the + record. The refutation criteria are the detector's own exemptions plus + adr-5; the refuters re-probe the binary rather than trusting the finding. + +- 2026-07-22 — When a brief is agreed upfront, it is a TARGET document; it + becomes the state document claim-by-claim, at ship time, because shipping + includes the brief row edit (spec-moves-with-the-surface). Unbuilt design + lives in intents or under an explicit staged marking — never as unmarked + present-tense brief prose. The 95 confirmed discrepancies are that ratchet + skipped at scale; the crosscheck is the measuring instrument; promoting the + principle to a mechanical discipline is the open follow-up (iss-121, iss-122). diff --git a/.abcd/work/issues/open/iss-122-iss35-crosscheck-scope-and-depth-unpinned.md b/.abcd/work/issues/open/iss-122-iss35-crosscheck-scope-and-depth-unpinned.md new file mode 100644 index 0000000..6c35e82 --- /dev/null +++ b/.abcd/work/issues/open/iss-122-iss35-crosscheck-scope-and-depth-unpinned.md @@ -0,0 +1,12 @@ +--- +schema_version: 1 +id: "iss-122" +slug: "iss35-crosscheck-scope-and-depth-unpinned" +severity: "minor" +category: "tech-debt" +source: "agent-observation" +found_during: "v0.4.0 release gate" +found_at: ".abcd/development/release-gate/brief-surface-crosscheck.js" +--- + +The iss35 crosscheck's scope and depth are unpinned, so the gate is not reproducible: v0.3.0's receipt records zero findings four days ago while a full-depth run (17 brief docs, both directions, 22 checkers) returns 102 discrepancies, and the receipt's own promptHash field is the literal 'no-pinned-prompt' admission. The maintainer choosing briefDocs per run means two honest runs of the same gate can disagree by two orders of magnitude; the gate needs a pinned input manifest and depth so a PROMOTE means the same thing every release. \ No newline at end of file diff --git a/.abcd/work/issues/resolved/iss-121-brief-surface-chapters-fail-iss35-crosscheck-at-depth.md b/.abcd/work/issues/resolved/iss-121-brief-surface-chapters-fail-iss35-crosscheck-at-depth.md new file mode 100644 index 0000000..75a37a8 --- /dev/null +++ b/.abcd/work/issues/resolved/iss-121-brief-surface-chapters-fail-iss35-crosscheck-at-depth.md @@ -0,0 +1,14 @@ +--- +schema_version: 1 +id: "iss-121" +slug: "brief-surface-chapters-fail-iss35-crosscheck-at-depth" +severity: "major" +impact: internal +category: "observation" +source: "agent-finding" +found_during: "v0.4.0 release gate" +found_at: ".abcd/development/brief/04-surfaces/" +resolution: "The brief's 17 surface docs are reconciled with the shipped binary: all 95 adversarially confirmed discrepancies fixed (68 rewrites, 14 surface documentations, 8 deletions of abandoned-design prose, 5 staged markings), plus the orphaned cross-references and the stale superseded banner the deletions exposed. 7 of the original 102 findings were refuted in triage (duplicates, wrong-reality, legitimately staged)." +--- + +The brief's surface chapters fail the iss35 brief-surface crosscheck at full depth: 102 unique discrepancies across the 16 surface chapters plus the skills internals page (54 false-claim, 16 undocumented-surface, 13 fictional-layout, 13 stale-count, 6 criterion-violation). Examples: 02-disembark.md describes a dev-sync verb, an agent-dispatch pack flow, and a backgrounded 'disembark to' sub-verb, none of which the binary ships; 01-ahoy.md omits seven shipped install flags and the identity-pin write. The v0.4.0 release is blocked fail-closed at receipt_gate until the record is reconciled and the crosscheck PROMOTEs. Full findings preserved in the local scratch tier (2026-07-22-iss35-crosscheck files). \ No newline at end of file diff --git a/commands/README.md b/commands/README.md index e70db31..187f1fb 100644 --- a/commands/README.md +++ b/commands/README.md @@ -7,9 +7,9 @@ engine. - `abcd.md` → `/abcd` — the read-only where-am-i status board (`abcd --json`). - `abcd/.md` → `/abcd:` — one file per verb. The shipped set is the - 13 files in [`abcd/`](abcd/): `ahoy`, `audit`, `capture`, `consult`, - `disembark`, `docs`, `embark`, `history`, `ingest`, `launch`, `memory`, - `prepare-this-repo`, `version`. + 14 files in [`abcd/`](abcd/): `ahoy`, `audit`, `capture`, `consult`, + `disembark`, `docs`, `embark`, `history`, `ingest`, `intent`, `launch`, + `memory`, `prepare-this-repo`, `version`. Commands stay thin: they call `abcd --json` and format the result; they never reimplement behaviour that belongs in the core. From f0fa344a9acb88e47e3ce0b4439eb5f81dfffcec Mon Sep 17 00:00:00 2001 From: REPPL <77722411+REPPL@users.noreply.github.com> Date: Wed, 22 Jul 2026 16:35:46 +0100 Subject: [PATCH 2/4] docs: sweep the remaining brief-surface discrepancies (round 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A fresh full-depth crosscheck over the reconciled brief surfaced 52 further discrepancies — mostly new sites the first run's checkers never cited (the unpinned-depth effect iss-122 records) plus sibling claims the first round's site-scoped fixes left behind. Adversarial triage confirmed 44 (8 refuted, largely the spc-N predecessor- provenance exemption); fixes applied per document with an explicit fix-the-class sibling sweep this round. Assisted-by: Claude:claude-opus-4-8 Assisted-by: Claude:claude-fable-5 --- .../development/brief/04-surfaces/01-ahoy.md | 49 +++++---- .../brief/04-surfaces/02-disembark.md | 8 +- .../brief/04-surfaces/03-embark.md | 17 ++- .../brief/04-surfaces/04-launch.md | 12 ++- .../brief/04-surfaces/05-intent.md | 100 ++++++++---------- .../brief/04-surfaces/06-capture.md | 10 +- .../brief/04-surfaces/07-memory.md | 16 +-- .../development/brief/04-surfaces/08-abcd.md | 2 +- .abcd/development/brief/04-surfaces/README.md | 4 +- .../brief/05-internals/01-agents.md | 4 +- .../brief/05-internals/03-configuration.md | 14 --- .../development/brief/05-internals/06-lint.md | 6 +- .../brief/05-internals/08-skills.md | 6 +- .../development/brief/05-internals/README.md | 2 +- .abcd/development/brief/glossary/README.md | 2 +- 15 files changed, 115 insertions(+), 137 deletions(-) diff --git a/.abcd/development/brief/04-surfaces/01-ahoy.md b/.abcd/development/brief/04-surfaces/01-ahoy.md index 2aa65c9..0a8e9cf 100644 --- a/.abcd/development/brief/04-surfaces/01-ahoy.md +++ b/.abcd/development/brief/04-surfaces/01-ahoy.md @@ -8,7 +8,7 @@ cannot drift apart. ## Sub-verbs -Bare `/abcd:ahoy` shows status + help only — never mutates state. The +Bare `/abcd:ahoy` shows read-only status only — never mutates state. The `/abcd:ahoy` slash command dispatches only this bare read-only pass and names the CLI for anything that writes; the sub-verbs below ship on the CLI as `abcd ahoy `. Current sub-verbs: @@ -23,11 +23,13 @@ the CLI for anything that writes; the sub-verbs below ship on the CLI as - **`/abcd:ahoy dry-run`** — run the detection pass and render the `DetectionResult` envelope as JSON (per spc-16 T1 — JSON ONLY; no unified-diff renderer). Never mutates state. -- **`/abcd:ahoy doctor`** — run the **full** detection pass and report every - gap read-only, including user-scope state (history store, `index.json`, - symlink, hook) that `install` touches but never otherwise - re-validates. - Distinct from bare invocation, which only shows summary status + help. +- **`/abcd:ahoy doctor`** — run the **full** detection pass plus a read-only + audit pass, and report the gap counts for each. The text render shows the + detection-gap and audit-gap counts; the JSON envelope carries full per-gap + detail, including user-scope state (history store, `index.json`, symlink, + hook) that `install` touches but never otherwise re-validates. + Distinct from bare invocation, which shows the folder kind, plugin-root + status, root SHA, and gap count. - **`abcd ahoy identity-check`** — exit non-zero if the git commit identity does not match `.abcd/config/identity.json` (the commit-identity gate). Read-only; a CLI-only operator/CI check with no slash-command surface. @@ -88,13 +90,13 @@ detection pass** and differ only in what they do with its output: | Sub-verb | Detection | Then | |---|---|---| -| bare `/abcd:ahoy` | full | render summary + help, no gap detail | -| `doctor` | full | render every gap read-only | +| bare `/abcd:ahoy` | full | render folder kind, plugin-root status, root SHA, gap count — no gap detail | +| `doctor` | full | render detection- and audit-gap counts (full per-gap detail in the JSON envelope) | | `dry-run` | full | render the canonical `DetectionResult` JSON envelope (per spc-16 T1 — no unified-diff) | | `install` | full | run the apply pass over the gaps | This means **idempotency is a property of detection, not of a version stamp.** -A check compares *actual state* (`git check-ignore -v`, `readlink`, file +A check compares *actual state* (`.gitignore` block compare, `readlink`, file presence, hook-config grep, `index.json` lookup) — never `setup_version` alone. If a user hand-deletes the marker block while `setup_version` is current, detection still reports it `missing` and `install` repairs it. @@ -298,8 +300,8 @@ notes the orphaned-predecessor possibility in the summary. CLAUDE.md/AGENTS.md and the `/usr/local/bin/abcd` symlink **if it points at this plugin** (otherwise leave it alone). `hooks/hooks.json` is plugin-static per spc-14 T7 — uninstall NEVER mutates it (per spc-16 T1 brief amendment). -**Leaves the entire `.abcd/` namespace intact** (meta, config, corpus, rules, -development/, memory/, lifeboat/, logbook/, rp/) and the history store. Deeper +**Leaves the entire `.abcd/` namespace intact** (`config/`, `config.json`, +`rules.json`, `development/`, `work/`, `memory/`) and the history store. Deeper removal (`/abcd:ahoy destroy`) comes in a later phase as itd-10. **Uninstall ↔ install is a tested round-trip invariant:** after `uninstall` @@ -315,11 +317,13 @@ plugin_root_status, repo_identity, signals, gaps}` so the plugin command (`commands/abcd/ahoy.md`) summarises state off `folder_kind` + `gaps` and names `abcd ahoy install` for anything actionable. -**Doctor (`/abcd:ahoy doctor`):** runs the detection pass and reports every gap -read-only, grouped by category. Unlike bare invocation it shows full gap detail -including user-state checks — the history store exists and is writable, the -`index.json` entry matches this root SHA, the PATH symlink and hook manifest -are intact, and any stale/duplicate `index.json` entries. +**Doctor (`/abcd:ahoy doctor`):** runs the detection pass plus a read-only +audit pass. The text render shows two counts — detection gaps and audit gaps. +The JSON envelope (`{detection, audit_gaps}`) carries full per-gap detail: the +detection gaps cover user-state checks (the history store exists and is +writable, the `index.json` entry matches this root SHA, the PATH symlink and +hook manifest are intact), while the audit pass reconciles the registered +history-store `path` against `index.json` read-only (a stale registered path). The check users reach for after a repo rename, a machine migration, or "why aren't my transcripts showing up." Never mutates state, never auto-fixes user-scope app-state. @@ -327,9 +331,10 @@ user-scope app-state. ## Acceptance - **Given** any abcd-aware terminal, **when** the user runs bare `/abcd:ahoy`, - **then** the dispatcher runs the detection pass and shows summary install - status (installed?, plugin version, marker drift state, visibility) plus the - available sub-verbs with suggested next actions — and never mutates state. + **then** the dispatcher runs the detection pass and shows the folder kind, + plugin-root status, root SHA, and gap count, plus a next-step line for the + folder kind (naming `/abcd:ahoy install` on an unmanaged repo) — and never + mutates state. - **Given** a fresh repo with no `.abcd/` directory, **when** `/abcd:ahoy install` runs to completion, **then** the two-file repo carve-out (`.abcd/config.json` + `.abcd/rules.json` per spc-16 T1) is written, @@ -375,9 +380,9 @@ user-scope app-state. plugin_root_status, repo_identity, signals, gaps}` per spc-16 T1) is printed to stdout, and no files are modified. - **Given** the user runs `/abcd:ahoy doctor` on an installed repo whose - registered history-store `path` no longer matches `index.json`, **then** the - user-state gap is reported read-only with both paths cited, and no files are - modified. + registered history-store `path` no longer matches `index.json`, **then** an + audit gap citing both paths appears under `audit_gaps` in the JSON envelope, + reported read-only, and no files are modified. - **Given** a fresh machine with no `~/.abcd/`, **when** `/abcd:ahoy install` runs in a repo, **then** `~/.abcd/` is bootstrapped — the `history/` store (directory + `index.json` with `schema`/`description` header) — before the diff --git a/.abcd/development/brief/04-surfaces/02-disembark.md b/.abcd/development/brief/04-surfaces/02-disembark.md index dc162fc..af8cee7 100644 --- a/.abcd/development/brief/04-surfaces/02-disembark.md +++ b/.abcd/development/brief/04-surfaces/02-disembark.md @@ -61,7 +61,7 @@ When structure and content disagree (e.g., a later spec mentions an earlier deci ``` / # operator-chosen, outside the source repo (adr-35) -├── _provenance.json # the lifeboat marker and re-pack gate key (written last): sources probed, file manifest, manifest_sha256, schema_version +├── _provenance.json # the lifeboat marker and re-pack gate key (written last): schema_version, generator, source name and root SHA, tiers present, manifest_sha256 over every other file, omissions ├── coverage.json # per-section status (grounded|partial|blank), evidence, what was searched ├── coverage.md # rendered ├── brief/ # the brief, section by section, grounded from the source @@ -95,15 +95,15 @@ Acceptance is checked across the validation corpus (`.abcd/corpus.json` — a de ## 7. Acceptance -> **Open question (adr-35):** bare `/abcd:disembark` previously reported "where the lifeboat lives", which was answerable only because the home was fixed and in-tree. With an operator-chosen ``, the destination path must come from somewhere, and adr-35 pins the `history.jsonl` line to `manifest_sha256`, file list, oracle backend, and verdict — it does **not** say the line records the destination. **Decide:** does a `disembark/history.jsonl` entry carry the destination path (and does bare invocation resolve `` from the cwd, as `probe` and `plan` already default to it)? The first bullet below assumes yes on both counts. +> **Open question (adr-35):** bare `/abcd:disembark` previously reported "where the lifeboat lives", which was answerable only because the home was fixed and in-tree. With an operator-chosen ``, the destination path must come from somewhere, and adr-35 pins the `history.jsonl` line to `manifest_sha256`, file count, oracle backend, and verdict — it does **not** say the line records the destination. **Decide:** does a `disembark/history.jsonl` entry carry the destination path (and does bare invocation resolve `` from the cwd, as `probe` and `plan` already default to it)? The first bullet below assumes yes on both counts. - **Given** any abcd-aware terminal, **when** the user runs bare `/abcd:disembark`, **then** the dispatcher reads the voyage log at `~/.abcd/voyage//disembark/history.jsonl` and shows when the source last disembarked and where that snapshot was written, the available sub-verbs (`pack `, `probe`, `plan`, `coverage`, `oracle`, `graveyard`, `press-release`, `principles`; later phase: `to-spec-kit`), and suggested next actions — bare invocation never mutates state. - **Given** any source repo, **when** any sub-verb runs against it (`probe`, `plan`, or a full pack), **then** the source tree is byte-for-byte identical afterwards — a test hashes the tree before and after (adr-35: disembark is read-only). There is no `home`, and no path under `` is ever a destination. - **Given** a corpus repo with an intent corpus, ADRs, and a memory backend present, **when** `/abcd:disembark pack ` runs to completion, **then** `` contains all sections in [§ 5](#5-output-shape) and the oracle audit returns a registered verdict of `SHIP` with specific findings. - **Given** a corpus repo where one source is sparse (e.g., a repo with no intent corpus), **when** a full pack runs, **then** the run **succeeds**: the affected section is omitted from the brief rather than fabricated, and `coverage.{json,md}` records it with `status: blank`, what was searched, and the question a human must answer — a blank is a first-class result, not a failure or an exemption footnote (adr-35). -- **Given** the user runs `/abcd:disembark probe `, **when** the command completes, **then** all adapters' `probe()` runs in parallel, `coverage.{json,md}` is emitted with every brief section marked `grounded` / `partial` / `blank` plus what was searched, nothing is written into ``, and the run takes a small fraction of the time a full pack would take (no LLM dispatches). +- **Given** the user runs `/abcd:disembark probe `, **when** the command completes, **then** all adapters' `probe()` runs in parallel, the coverage report is rendered to stdout (text, or JSON with `--json`) with every brief section marked `grounded` / `partial` / `blank` plus what was searched, nothing is written into ``, and the run takes a small fraction of the time a full pack would take (no LLM dispatches). - **Given** `probe` is run across the validation corpus, **when** the per-repo coverage reports are aggregated, **then** the aggregate reports the section-coverage delta between a rich-record repo and a git-only repo — the experiment's readout, and the evidence the packer's section list is built to (itd-88, adr-35). - **Given** the user runs `/abcd:disembark plan`, **when** the command completes, **then** the source inventory runs end-to-end, the would-be writes are listed as file paths, and nothing is written to `` or ``. - **Given** a destination that is neither absent, nor an empty directory, nor one carrying a parseable `_provenance.json`, **when** a pack targets it, **then** the run **refuses** and writes nothing — abcd never overwrites a directory it did not produce (adr-35's destination safety gate; there is no `.bak`). -- **Given** any pack run completes, **when** the lifeboat is written, **then** a new line is appended to `~/.abcd/voyage//disembark/history.jsonl` with the run's `schema_version`, event, timestamp, `manifest_sha256`, source name, source root SHA, destination, file **count**, and bytes written (per [`03-embark.md § 7`](03-embark.md#7-voyage-layout-embarkdisembark-provenance-and-history)). The line carries no oracle backend or verdict — nothing produces them yet, and an empty field would be a lie (adr-35); the full file list lives in the lifeboat's `_provenance.json`, pinned by the hash, which matches this run's `_provenance.json`. +- **Given** any pack run completes, **when** the lifeboat is written, **then** a new line is appended to `~/.abcd/voyage//disembark/history.jsonl` with the run's `schema_version`, event, timestamp, `manifest_sha256`, source name, source root SHA, destination, file **count**, and bytes written (per [`03-embark.md § 7`](03-embark.md#7-voyage-layout-embarkdisembark-provenance-and-history)). The line carries no oracle backend or verdict — nothing produces them yet, and an empty field would be a lie (adr-35); its `manifest_sha256` matches the lifeboat's own `_provenance.json`, whose hash pins every other packed file. - **Given** a `` carrying a parseable `_provenance.json` from a previous run, **when** a new snapshot lands there, **then** the previous snapshot is replaced AND its manifest remains in the voyage log — there is never a `lifeboat-v1/` / `lifeboat-v2/` directory; history is preserved in the manifest log, not in stale snapshots. diff --git a/.abcd/development/brief/04-surfaces/03-embark.md b/.abcd/development/brief/04-surfaces/03-embark.md index 543596f..49917fb 100644 --- a/.abcd/development/brief/04-surfaces/03-embark.md +++ b/.abcd/development/brief/04-surfaces/03-embark.md @@ -27,7 +27,7 @@ ## Sub-verbs -Bare `/abcd:embark` shows status + help only — never mutates state. The two **shipped** sub-verbs: +Bare `/abcd:embark` prints dispatcher help only — never mutates state. The two **shipped** sub-verbs: - **`/abcd:embark from `** — unpack the lifeboat at `` into the current repo. Path is required, and it is always an explicit path to a destination a disembark wrote — **there is no `home` shorthand** (adr-35: there is no in-tree lifeboat home to expand it to). The round-trip / self-test case is `disembark pack ` followed by `embark from `. *(Design target, not yet shipped: the flag-shaped modifiers `--force` — override conflict refusal, `--archive` — copy input lifeboat verbatim to `~/.abcd/voyage//embark/from//` before unpacking, and `--refresh-audit` — re-run oracle product audit instead of trusting cached. The shipped `from` takes no flags.)* - **`/abcd:embark probe `** — inspect a lifeboat at `` without unpacking: show what would land where, run schema/audit checks, write nothing. @@ -73,18 +73,15 @@ Embark is a deterministic Go run: it reads the lifeboat, plans, refuses on any c When the target already has files that the lifeboat would write, **core returns the conflict set and the surface renders it as a single bulk prompt** — never a per-file barrage, and never a file written by core (adr-35): ``` -embark detected N conflicts across: - • 3 native spec store files - • 1 CLAUDE.md (will inject markers if 'merge') - • 2 .abcd/memory/ files +embark detected N conflicts across the record families: + • 3 .abcd/development/specs/ + • 2 .abcd/work/issues/ • 1 .abcd/development/decisions/adrs/ - • 1 .abcd/development/brief/README.md (existing brief vs incoming press release) How to resolve all conflicts? → keep target (skip everything in lifeboat that conflicts) → replace target (lifeboat wins everywhere) - → merge where possible, prompt otherwise (CLAUDE.md gets marker injection; - ADRs/specs/memory get per-file prompt) + → merge where possible, prompt otherwise (a per-file prompt for each conflict) → abort (surface prints the conflict list; nothing is written) ``` @@ -106,8 +103,8 @@ Single decision, transparent (shows scope before asking). The conflict list is a - **Given** the user runs `/abcd:embark scan`, **when** the command completes, **then** the directories found carrying a parseable `_provenance.json` are listed ranked by mtime with their detected source repo, no unpacking occurs, and the user is shown candidates ready to pass to `embark from `. *(What `scan` searches is settled; **where** it searches is the open question above, and this criterion cannot be made checkable until that is decided.)* - **Given** the user runs `/abcd:embark scan --deep`, **when** the command completes, **then** the search widens — the exact widening depends on the same open question. - **Given** the user runs `/abcd:embark probe `, **when** the command completes, **then** the lifeboat at `` is inspected (file tree, schema validation, would-be writes), no target mutation occurs, and the user sees a report ready to inform the decision to run `embark from `. -- **Given** a lifeboat containing `.abcd/rp/workspace.json` and RP installed on the embarker, **when** `embark from ` runs, **then** the user is asked whether to register the workspace with RP and the choice is applied. -- **Given** a lifeboat containing `.abcd/rp/workspace.json` and RP *not* installed, **when** `embark from ` runs, **then** the command warns gracefully and continues without failing. +- *(Design target, not yet shipped.)* **Given** a lifeboat containing `.abcd/rp/workspace.json` and RP installed on the embarker, **when** `embark from ` runs, **then** the user is asked whether to register the workspace with RP and the choice is applied. +- *(Design target, not yet shipped.)* **Given** a lifeboat containing `.abcd/rp/workspace.json` and RP *not* installed, **when** `embark from ` runs, **then** the command warns gracefully and continues without failing. - **Given** the user passes `--refresh-audit`, **when** `embark from --refresh-audit` runs, **then** the oracle product audit re-runs against the current lifeboat content and the drift vs the disembark-time audit is reported. - *(Design target, not yet shipped.)* **Given** an `embark from ` run completes, **then** `~/.abcd/voyage//embark/provenance.json` exists with `source_path`, `source_manifest_sha256`, `timestamp`, and `files_written` populated (per [§ 7](#7-voyage-layout-embarkdisembark-provenance-and-history)); `embark/from//` is absent unless `--archive` was passed. - **Given** an `embark from --archive` run completes, **then** the input lifeboat is copied verbatim to `~/.abcd/voyage//embark/from//` and the path is referenced from `provenance.json`. diff --git a/.abcd/development/brief/04-surfaces/04-launch.md b/.abcd/development/brief/04-surfaces/04-launch.md index 7248b50..d612962 100644 --- a/.abcd/development/brief/04-surfaces/04-launch.md +++ b/.abcd/development/brief/04-surfaces/04-launch.md @@ -7,7 +7,7 @@ The shipped verb surface is the `--dry-run` flag on `abcd launch` — a read-only preview of the bundle and gates. Bare `abcd launch` never mutates state: it refuses (exit 1) with a hint to pass `--dry-run`; a bare-as-status render is a design target, unshipped. The sub-verb design: - **`/abcd:launch ship`** — **partly shipped: the RELEASE CUT only** (itd-73 derived versioning + itd-67's changelog slice). `abcd launch ship` derives the version and the record set from what shipped since the newest tag, runs the surface guardrail, and — with `--changelog-json` — validates the host-composed prose against the record set (the completeness bijection) and writes the dated `CHANGELOG.md` heading `.github/workflows/auto-release.yml` turns into a tag; `commands/abcd/launch.md` carries the emit → compose → ingest orchestration and the `release-changelog-composer` agent it dispatches. The `Ship` engine is wired: `abcd launch ship` is a live subcommand, and `--payload-dir ` stages the versioned release payload — the derived version stamped into the payload's `plugin.json`/`marketplace.json` and lockstep-proved before return. Commit, tag, and publish stay a design target (itd-65 gate suite + itd-72 publishing): the verb neither commits, tags, nor publishes. The full-cut design: cut a curated release artefact from the one repo: run pre-flight gates, filter the artefact (default-deny, `.abcd/**` excluded by packaging), stamp the version, and on a `v*` tag publish a GitHub Release ([adr-28](../../decisions/adrs/0028-single-repo-curated-release.md)). The flow described in §§ 1–6 below is this sub-verb's behaviour. Flag-shaped modifiers `--allow-dirty` and `--allow-doc-warnings` belong to this sub-verb's design; the shipped `ship` accepts `--changelog-json` and `--payload-dir` (plus the global `--json`), and bare `abcd launch` accepts only `--dry-run` and the global `--json`. There is no version flag — the version is derived, never authored ([adr-31](../../decisions/adrs/0031-derived-versioning-from-intents.md), see [§ 3](#3-versioning--marketplace)). -- **`/abcd:launch dry-run`** — shipped as the `--dry-run` flag (the plugin command `commands/abcd/launch.md` maps the `dry-run` of its `[dry-run | ship]` argument hint onto `abcd launch --dry-run`; the binary has no `dry-run` subcommand). **Report-only preview, always exit-0** (a preview never blocks). It runs the parts of the pre-flight suite that exist today: as of spc-64 the **secret + PII scan gate** (the native scanners, see [§ 1](#1-pre-flight-gates)) runs for real in report-only mode and prints what it *would* refuse on (a finding, or a fail-closed reason such as "scanner unavailable"); the **installability smoke** runs for real at its light tier (see [§ 1](#1-pre-flight-gates)); the remaining gates (marker-block, documentation-auditor) are the gate-suite intent's (itd-65) and render as "(not yet implemented)". It also produces the would-be artefact manifest, without writing the release artefact. dry-run is **not** "ship minus publish": running the *full* gate suite and **hard-failing** on a finding (exit non-zero) is the full `ship` verb's behaviour (itd-65 + itd-72), not dry-run's. +- **`/abcd:launch dry-run`** — shipped as the `--dry-run` flag (the plugin command `commands/abcd/launch.md` maps the `dry-run` of its `[dry-run | ship]` argument hint onto `abcd launch --dry-run`; the binary has no `dry-run` subcommand). **Report-only preview, always exit-0** (a preview never blocks). It runs the parts of the pre-flight suite that exist today: as of spc-64 the **secret + PII scan gate** (the native scanners, see [§ 1](#1-pre-flight-gates)) runs for real in report-only mode and prints what it *would* refuse on (a finding, or a fail-closed reason such as "scanner unavailable"); the **installability smoke** runs for real at its light tier (see [§ 1](#1-pre-flight-gates)); the **manifest lockstep check** runs for real at its `dev` polarity over the working tree — the polarity adr-19 requires the committed manifests to satisfy (no version key), see [§ 3](#3-versioning--marketplace) — reports its result and folds any drift or an unreadable version-location contract into what it *would* refuse on; the remaining gates (marker-block, documentation-auditor) are the gate-suite intent's (itd-65) and render as "(not yet implemented)". It also produces the would-be artefact manifest, without writing the release artefact. dry-run is **not** "ship minus publish": running the *full* gate suite and **hard-failing** on a finding (exit non-zero) is the full `ship` verb's behaviour (itd-65 + itd-72), not dry-run's. ## 1. Pre-flight gates @@ -110,9 +110,13 @@ location and the marketplace entry's version + changelog must agree. A read-only lockstep checker proves this over the pinned path list [adr-20](../../decisions/adrs/0020-manifest-version-lockstep.md) records; a half-state (a version in one manifest and not the other) is drift. `ship`'s bump -step runs it against the staged release artefact and refuses to publish on drift. -The checker has no bypass flag, and adr-20 records that `--allow-dirty` must not -bypass manifest consistency (wiring policy, enforced by the pre-flight suite). +step runs it against the staged release artefact at the **public** polarity +(primary present, every secondary agreeing) and refuses to publish on drift; the +**dev** polarity runs in `dry-run` over the working tree, asserting the committed +manifests carry no version key (adr-19), and folds any drift into the preview's +would-refuse-on set. The checker has no bypass flag, and adr-20 records that +`--allow-dirty` must not bypass manifest consistency (wiring policy, enforced by +the pre-flight suite). Commit message: `chore(release): launch abcd v (: ) from ` — e.g. `chore(release): launch abcd v0.3.0 (minor: additive itd-40 shipped) from a1b2c3d`. diff --git a/.abcd/development/brief/04-surfaces/05-intent.md b/.abcd/development/brief/04-surfaces/05-intent.md index b99235f..b01ad9b 100644 --- a/.abcd/development/brief/04-surfaces/05-intent.md +++ b/.abcd/development/brief/04-surfaces/05-intent.md @@ -38,9 +38,9 @@ Every intent has a `kind` declared in frontmatter, set at `/abcd:intent plan` ti **Two distinct history fields, two distinct concerns:** `reclassification_history` records *kind* transitions (standalone ↔ bundle-member ↔ discipline ↔ superseded). `surface_history` records *surface-shape* transitions where the kind is unchanged but the user-facing surface form changes (e.g., skill → sub-verb, top-level command → sub-verb of another command, command → flag). Both are append-only; both have the same `{ date, from, to, reason }` shape. Worked example: itd-27 was always `kind: standalone`, but its surface shifted from a top-level skill (`/abcd:grill`) to a sub-verb of `/abcd:intent` on 2026-05-07 — that's a `surface_history` entry, not a `reclassification_history` entry, because the kind is unchanged. The two fields together preserve a complete audit trail of an intent's evolution without overloading either. -**`suggested_kind` is advisory.** At capture time, the LLM classifier writes a soft hint (`suggested_kind: `) to frontmatter. This is informational only — the binding decision is at plan time. +**`suggested_kind` is advisory.** The quoted-text create seeds `suggested_kind: null`; the field is an optional soft hint a human may set later, informational only — the binding decision is at plan time. -**A fourth capture verdict, `decision`, routes to the ADR store (itd-44).** The capture-time classifier (`intent classify-capture-kind`) can also emit `decision` — a *standing infrastructure choice* (no user moment, not a per-artefact rule, e.g. "we use Postgres"). `decision` is a **capture verdict only, never a persisted `kind`**: the `kind` / `kind_at_supersession` enums stay three-valued (`standalone` / `bundle-member` / `discipline`). A confirmed `decision` DIVERTS capture to the existing ADR store (`.abcd/development/decisions/adrs/`, `NNNN-.md`, zero-padded) instead of writing an intent draft — no spec, no lifecycle directory, no `intents/decisions/`. The verdict is advisory: capture confirms "capture as an ADR?" or overrides to a normal draft carrying a *plannable* `suggested_kind` (`null`/`standalone`, never `decision`). `suggested_kind: decision` validates in the schema but is REFUSED by `plan_single`/`reclassify` ("decisions are not plannable"). See [itd-44](../../intents/drafts/itd-44-fourth-intent-kind-decision.md) and the [ADR store README](../../decisions/adrs/README.md). +**A fourth capture verdict, `decision`, routes to the ADR store (itd-44 — a later phase).** A later-phase capture-time classifier can also emit `decision` — a *standing infrastructure choice* (no user moment, not a per-artefact rule, e.g. "we use Postgres"). `decision` is a **capture verdict only, never a persisted `kind`**: the `kind` / `kind_at_supersession` enums stay three-valued (`standalone` / `bundle-member` / `discipline`). A confirmed `decision` DIVERTS capture to the existing ADR store (`.abcd/development/decisions/adrs/`, `NNNN-.md`, zero-padded) instead of writing an intent draft — no spec, no lifecycle directory, no `intents/decisions/`. The verdict is advisory: capture confirms "capture as an ADR?" or overrides to a normal draft carrying a *plannable* `suggested_kind` (`null`/`standalone`, never `decision`). `suggested_kind: decision` validates in the schema but is REFUSED by `plan_single`/`reclassify` ("decisions are not plannable"). See [itd-44](../../intents/drafts/itd-44-fourth-intent-kind-decision.md) and the [ADR store README](../../decisions/adrs/README.md). **Bundle invariant: all members of a bundle MUST belong to the same phase.** A bundle ships as one shared spec; one spec belongs to one phase. Cross-phase bundles are structurally impossible — the shared spec cannot live in two phases at once. `/abcd:intent plan ...` (multi-arg, kind=bundle-member) hard-blocks promotion when the proposed members are scoped to different phases. The only resolutions are: (a) re-scope all members into the same phase before re-running plan, or (b) downgrade one or more members to `kind: standalone` so they ship independently. Worked example: the `intent-capture-discipline` bundle (itd-27 + itd-30) was retired on 2026-05-07 precisely because the two members were scoped to different phases — both intents reclassified to standalone (see their `reclassification_history` entries for the full reasoning). Lint code: `IL011` per [`05-internals/06-lint.md`](../05-internals/06-lint.md) (plan-time tooling, a later phase — the shipped record lint has no bundle check). @@ -118,33 +118,19 @@ Until then, `kind_notes` is the free-text descriptor. │ fill before planning └─ Writes intents/drafts/itd-N-.md (no spec created yet) -2. /abcd:intent plan [...] (when ready to commit to work) - ├─ Reads prd_path (produced by the grill → PRD flow); refuses if null — no PRD yet, grill first - │ (GR002 blocker; suppressed-as-info for prd_grandfathered intents). See § 5 for the full freeze sequence. +2. /abcd:intent plan (when ready to commit to work; single intent ID) ├─ Runs internal/core/lint: refuses to promote if `## Acceptance Criteria` is missing/malformed - ├─ Reads suggested_kind + cross-references; proposes a kind (standalone / bundle-member / discipline) - ├─ User confirms or overrides the kind (binding); written to `kind:` frontmatter - │ - ├─ IF kind = standalone (single intent ID passed): - │ ├─ Creates a stub spec in the native spec store for the intent - │ ├─ Runs plan-review (Carmack-style review of the stub) - │ ├─ Injects bidirectional link (spec.intent: itd-N; intent.spec_id: spc-N) - │ └─ Moves intents/drafts/itd-N-*.md → intents/planned/itd-N-*.md - │ - ├─ IF kind = bundle-member (multiple intent IDs passed): - │ ├─ Creates one shared spec with all intents as joint input - │ ├─ Runs plan-review for the combined plan - │ ├─ Injects bidirectional link to shared spc-N (spec.intent: [itd-A, itd-B]; - │ │ each intent.spec_id: spc-N; each intent.bundle: ) - │ └─ Moves all members from drafts/ → planned/ - │ - └─ IF kind = discipline (single intent ID, kind chosen explicitly): - ├─ NO spec created — disciplines don't get their own spec - ├─ Registers the rule in .abcd/disciplines/.json (acceptance gates that - │ every subsequent spec inherits in its plan-review check) - ├─ Runs plan-review on the discipline's `## Rule` itself for sanity - └─ Moves intents/drafts/itd-N-*.md → intents/disciplines/itd-N-*.md - (no `status` field — the disciplines/ directory IS the active state) + ├─ Mints (or reuses) the intent's native spec; kind defaults to standalone + ├─ Injects bidirectional link (spec.intent: itd-N; intent.spec_id: spc-N) + └─ Moves intents/drafts/itd-N-*.md → intents/planned/itd-N-*.md + + Later phase — plan grows a PRD-freeze front end and multi-kind dispatch: + a prd_path read + provenance freeze sequence (§ 5); a suggested_kind-driven + kind proposal the user confirms or overrides (binding); a plan-review of the + stub; a multi-arg bundle-member branch (plan …, one shared + spec across members); and a discipline branch (no spec — registers the rule + in a disciplines gate store, moves drafts/ → disciplines/). None of these + ship today; plan schedules a single standalone intent. 3. Spec marked done in the native spec store (standalone + bundle: work complete) ├─ native spec-store `spec close` close-hook (spc-36) → intent lifecycle reconcile (spc-28) @@ -175,35 +161,33 @@ Until then, `kind_notes` is the free-text descriptor. │ change the meaning of "superseded") └─ Triggers intent-fidelity-reviewer (shape-classification role) to verify the new kind fits -On demand: intent-fidelity-reviewer (shape-classification role) scans the corpus when - the user runs /abcd:intent shape (spc-29 ships only the on-demand surface). - Bare /abcd:intent (status+help) surfaces the latest cached shape suggestions - in its summary — bare invocation never runs a fresh scan. The user accepts - via /abcd:intent reclassify; declined suggestions become entries in the - intent's Audit Notes for future review. (Deferred follow-up per - .abcd/work/issues/ [spc-29 follow-up]: scheduled / pre-commit shape scanning — - the shape(...) function's mode="pre_commit" parameter is preserved as a seam - but no hook invokes it.) +Later phase — intent-fidelity-reviewer (shape-classification role) scans the corpus + when the user runs /abcd:intent shape (spc-29; an on-demand surface, not + yet a binary sub-verb). The user accepts a suggestion via + /abcd:intent reclassify; declined suggestions become entries in the + intent's Audit Notes for future review. (Deferred follow-up: scheduled / + pre-commit shape scanning — the shape(...) function's mode="pre_commit" + parameter is a preserved seam that no hook invokes.) ``` ## 2. Subcommands | Subcommand | Purpose | File movement | |---|---|---| -| `/abcd:intent` (no args) | Help + status: lists intents grouped by directory (drafts / planned / shipped / disciplines / superseded), shows commands, surfaces shape suggestions from `intent-fidelity-reviewer`, suggests next actions for any intent in flight | — | -| `/abcd:intent ""` | **Canonical create** (spc-30/itd-46): a leading quoted seed is the canonical create entry. Interview-driven capture (press release with persona quote + acceptance criteria); assigns `itd-N`; LLM classifier writes advisory `suggested_kind`. A leading quote always creates — never falls through to bare render | writes to `drafts/itd-N-.md` (no spec created) | +| `/abcd:intent` (no args) | Read-only status: bucket counts (drafts / planned / shipped / disciplines / superseded), open/closed spec counts, the itd↔spc links, and a ledger-routing hint (`abcd capture "…"` for an observation, `abcd intent "…"` for a user-facing change) | — | +| `/abcd:intent ""` | **Canonical create** (spc-30/itd-46): a leading quoted seed is the canonical create entry. Seeds a draft skeleton from the text — placeholder Press Release and Acceptance Criteria sections for the human to expand — and assigns `itd-N`; writes `suggested_kind: null`. A leading quote always creates — never falls through to bare render | writes to `drafts/itd-N-.md` (no spec created) | | `/abcd:intent new ` | Deprecated alias for the quoted-text create (`abcd intent ""`); files a draft from the text | writes to `drafts/itd-N-.md` (no spec created) | | `/abcd:intent grill ` | Socratic adversarial interview that stress-tests an intent for vagueness, missing acceptance, hidden assumptions before planning. Glossary-aware once `terminology/` exists. `--brief-section ` flag for stress-testing a brief section instead. (per itd-27) | (stays in current state) | | `/abcd:intent plan ` | Plans a draft: mints its native spec, injects the bidirectional link (intent `spec_id` ↔ spec `intent`), and moves the file `drafts/` → `planned/`. Single intent ID. | `drafts/` → `planned/` | | `/abcd:intent ready ` | **Implement-readiness gate** (read-only): reports whether an intent is ready to implement — planned, with acceptance criteria and a written spec body. Exit 0 ready / 1 not ready / 2 fault. | (no move) | | `/abcd:intent review ` | **Role 1 — single-document fidelity.** Compares the intent's press release + acceptance criteria against delivered reality (code, configs, docs, tests). Per-criterion verdicts (`MET` / `MET_WITH_CONCERNS` / `NOT_MET` / `INCONCLUSIVE`) appended to the intent's `## Audit Notes`. Aligns with the spec store's `plan-review` / `impl-review` / `completion-review` vocabulary — same operation shape (adversarial second opinion), different opponent (press release vs engineering spec). spc-12 ships this **manual** verb; spc-28 ships the on-close hook (move `planned → shipped` + queue a review), but auto-running the reviewer off that queue is still deferred (no spec currently owns it; spc-6 disowned auto-firing). | (stays) | | `/abcd:intent review ingest --verdict-json ` | Ingests a host-delegated intent-fidelity verdict JSON, validated fail-closed against the schema and the parked review request, and writes its per-criterion verdict into the shipped intent's `## Audit Notes` (or quarantines a bad payload). | (no move; updates `## Audit Notes`) | -| `/abcd:intent consistency []` | **Role 2 — cross-document fidelity.** Surfaces five judgement categories (terminology drift, premise contradictions, scope leakage, sequencing impossibilities, naming conflicts) across briefs + intents. **Bare** scans the whole corpus; **with ``** narrows to one intent's relationship with the rest. Findings land in `.abcd/logbook/audit/consistency-/report.{json,md}`. The judgement half + on-demand verb are spc-29's; mechanical-half categories and pre-commit hook are deferred follow-ups (see `.abcd/work/issues/` `[spc-29 follow-up]` entries). | (stays) | -| `/abcd:intent shape []` | **Role 3 — kind classification.** Examines whether an intent's declared `kind` (the noun) still fits the corpus. Surfaces *suggested* reclassifications across three live types: `kind_change`, `bundle`, `supersession`. **Bare** scans the corpus; **with ``** checks one intent. Pairs with `reclassify` (action verb that commits a `shape` finding). On-demand only per spc-29; findings land in `.abcd/logbook/audit/shape-/report.{json,md}`. Concurrency via `flock(2)` on `.abcd/coordination/shape.lock` (see § 7). Scheduled / continuous invocation is a deferred follow-up (see `.abcd/work/issues/` `[spc-29 follow-up]`). | (stays) | +| `/abcd:intent consistency []` | **Role 2 — cross-document fidelity.** Surfaces five judgement categories (terminology drift, premise contradictions, scope leakage, sequencing impossibilities, naming conflicts) across briefs + intents. **Bare** scans the whole corpus; **with ``** narrows to one intent's relationship with the rest. Findings land in `.abcd/.work.local/logs/audit/consistency-/report.{json,md}`. The judgement half + on-demand verb are spc-29's (a later phase); mechanical-half categories and pre-commit hook are deferred follow-ups. | (stays) | +| `/abcd:intent shape []` | **Role 3 — kind classification.** Examines whether an intent's declared `kind` (the noun) still fits the corpus. Surfaces *suggested* reclassifications across three live types: `kind_change`, `bundle`, `supersession`. **Bare** scans the corpus; **with ``** checks one intent. Pairs with `reclassify` (action verb that commits a `shape` finding). On-demand only per spc-29 (a later phase); findings land in `.abcd/.work.local/logs/audit/shape-/report.{json,md}`. Concurrency via `flock(2)` on `.abcd/coordination/shape.lock` (see § 7). Scheduled / continuous invocation is a deferred follow-up. | (stays) | | `/abcd:intent reclassify --kind [--reason ]` | Late reclassification (e.g., a standalone intent realised to be a bundle-member; a draft realised to be a discipline; a shipped intent superseded by a later one). Records `reclassification_history` entry; moves the file between directories as the new kind dictates. `--kind superseded --by ` is the supersession path: the file moves to `superseded/`, frontmatter records `superseded_by: itd-M` AND `kind_at_supersession: ` so future readers know what shape the intent had when retired. | varies by destination kind | | `/abcd:intent link ` | Manual bidirectional link — used if the auto-link missed (rare) or for retroactive linking of pre-existing specs | (no move; updates frontmatter) | -**No aggregator verb.** A `check` subverb that runs `review` + `consistency` + `shape` together is *not* provided — the three primitives have very different runtime costs (review is code+oracle expensive; consistency is corpus-wide expensive; shape is cheap on demand). Bundling them produces a slow verb users avoid. Release-readiness is `/abcd:launch`'s pre-flight job. (Note: a future scheduled / pre-commit shape leg is recorded as a **deferred follow-up** under `.abcd/work/issues/` `[spc-29 follow-up]`; spc-29 ships only the on-demand surface.) +**No aggregator verb.** A `check` subverb that runs `review` + `consistency` + `shape` together is *not* provided — the three primitives have very different runtime costs (review is code+oracle expensive; consistency is corpus-wide expensive; shape is cheap on demand). Bundling them produces a slow verb users avoid. Release-readiness is `/abcd:launch`'s pre-flight job. (Note: a scheduled / pre-commit shape leg is a **deferred follow-up**; spc-29's shape surface is on demand only.) **Bare-command-as-help is a universal abcd convention** — every command in the surface set (the enumeration lives in the [surfaces README](README.md)) shows status + suggested next actions when invoked without args. Provides discoverability without forcing the user to remember subcommand names. @@ -253,7 +237,7 @@ Discipline-kind intents use a different template — see § 1 "Discipline format ## 4. Persona registry -See [`01-product/05-personas.md`](../01-product/05-personas.md) for the canonical persona registry (SSOT). The intent-create flow (`/abcd:intent ""`) calls the native persona picker to pick a persona for the customer quote in each press release; the codified abcd principle (no real names, no "hypothetical user") lives in the canonical persona file. +See [`01-product/05-personas.md`](../01-product/05-personas.md) for the canonical persona registry (SSOT). A press release attributes its customer quote to a persona from that roster, and the `persona_registry` lint rule checks that every quote attribution resolves to it; the codified abcd principle (no real names, no "hypothetical user") lives in the canonical persona file. ## 5. Frontmatter fields (spc-3 additions) @@ -285,14 +269,14 @@ spc-3 adds the following optional frontmatter fields to intent files. All are ad `/abcd:intent grill ` runs two phases over a single session context: -1. **Phase 1 (interactive)**: Socratic adversarial interview. Produces a grill-report at `.abcd/logbook/grill/-/grill-report.json`. Writes `grill_session_id`, `grilled_at`, `grilled_intent_hash`, `glossary_terms_used` back to intent frontmatter. +1. **Phase 1 (interactive)**: Socratic adversarial interview. Produces a grill-report at `.abcd/.work.local/logs/grill/-/grill-report.json`. Writes `grill_session_id`, `grilled_at`, `grilled_intent_hash`, `glossary_terms_used` back to intent frontmatter. 2. **Phase 2 (silent synthesis)**: Consumes the sharpened intent + glossary citations + grill findings. Produces the PRD at `.abcd/intents//prd.md` with all required frontmatter including `source_intent_hash`, `grill_report_path`, `grill_report_hash`. Sets `prd_path` on the intent. -The PRD is a **frozen contract** artefact (not a session log). It lives at a per-intent path, not under logbook/. +The PRD is a **frozen contract** artefact (not a session log). It lives at a per-intent path, not under the ephemeral logs tier. -### `/abcd:intent plan` as the PRD-validating + freezing sub-verb +### `/abcd:intent plan`: the later-phase PRD-validating + freezing front end -`/abcd:intent plan ` runs the ordered freeze sequence: +The later-phase PRD-freeze front end for `/abcd:intent plan ` runs this ordered sequence (the shipped `plan` mints the spec, links both sides, and moves drafts → planned — see § 1): 1. Reads `prd_path` from intent frontmatter; refuses if null (no PRD yet). 2. Validates PRD file exists, non-empty, passes section and frontmatter validators. @@ -325,7 +309,7 @@ Both the press-release intent and the frozen PRD are immutable input artefacts p - **Every intent in `superseded/` has both `superseded_by: ` AND `kind_at_supersession: `.** The first names the successor; the second preserves what shape the intent had when it was retired (standalone vs bundle-member vs discipline change the meaning of "superseded"). Both are required. If `kind_at_supersession: bundle-member`, the intent ALSO carries `bundle_at_supersession: ` — preserving the bundle membership at retirement time even though the active `bundle:` field is `null`. - No intent ID collisions; no spec referencing a non-existent intent ID. - File location matches `kind` frontmatter (drift between dir and field flagged). -- For intents promoted from issues (per itd-4): bidirectional `related_issues` ↔ `related_intents` linkage holds. Per spc-23 (intent-fidelity-reviewer `--issue-drift` mode). +- For intents promoted from issues (per itd-4): bidirectional `related_issues` ↔ `related_intents` linkage holds. Per spc-23 (intent-fidelity-reviewer's issue-drift role — a later phase). Drift triggers a warning, not a block (since spec-store state may legitimately lag intent state during work in progress). Acceptance-criteria absence and kind/directory mismatch are hard blocks (the whole point of the itd-1 discipline is to force the AC discipline; the kind/directory contract makes the lifecycle navigable). @@ -333,7 +317,7 @@ Drift triggers a warning, not a block (since spec-store state may legitimately l `intent-fidelity-reviewer` is a single agent in the catalog (per `05-internals/01-agents.md`) that owns three roles. Roles share the agent's prompt scaffolding, oracle backend resolution, and receipts; they differ in what they review, when they run, where findings land, and **which subverb users invoke them through**. Each role has its own dedicated verb — no role-by-kind dispatch, no hidden-state forking. -The verb `review` is chosen for Role 1 to align with the spec store's review vocabulary (`plan-review`, `impl-review`, `completion-review`); `audit` is reserved for the top-level `/abcd:audit` (compliance / hash-chain integrity). Each verb means one thing. +The verb `review` is chosen for Role 1 to align with the spec store's review vocabulary (`plan-review`, `impl-review`, `completion-review`); `audit` names the top-level `/abcd:audit`, which runs the read-only working-conventions conformance check (its later-phase `chain` sub-verb covers hash-chain integrity). Each verb means one thing. ### Role 1 — single-document fidelity → `/abcd:intent review ` @@ -349,7 +333,7 @@ This is product-tier review. The opponent is the codebase. Distinct from the spe **What spc-12 ships.** spc-12 ships the **discipline-judgement subset** of `/abcd:intent review` — the itd-1 per-criterion acceptance verdicts and the itd-37 `MG004` boilerplate check, with their writers and receipts. The broader **press-release prose review** (the `honoured` / `diverged` / `missing` buckets below) and other prose/terminology/PRD-fidelity outputs are **deferred** to a later spec. spc-12 also ships the **manual** review surface; spc-28 ships the on-close hook (move `planned → shipped` + queue a review on that transition). Auto-running the reviewer off that queue is still deferred — no spec currently owns it; spc-6 disowned auto-firing. -**What spc-23 ships.** spc-23 ships the `--issue-drift` role — a corpus-wide bidirectional cross-reference walk between shipped intents and the `iss-N` ledger (per itd-4). Receipts land at `.abcd/logbook/audit/issue-drift-/`. Default exit 0 with warnings to stderr; `--strict` opts into exit 1 for CI gates. See the native spec `spc-23-intent-fidelity-reviewer-extension`. +**The spc-23 issue-drift role (a later phase).** spc-23 adds an `--issue-drift` role — a corpus-wide bidirectional cross-reference walk between shipped intents and the `iss-N` ledger (per itd-4), with receipts under `.abcd/.work.local/logs/audit/issue-drift-/`, a default exit 0 with warnings to stderr, and a `--strict` exit-1 mode for CI gates. See the native spec `spc-23-intent-fidelity-reviewer-extension`. Outputs findings with two layers: @@ -392,7 +376,7 @@ Role 1 records per-criterion verdicts; **itd-50 adds the POLICY that decides wha **The `UNACHIEVABLE` replan surface (no rollback).** A terminal `UNACHIEVABLE` writes a `why-unachievable` explanation + a **replan invitation** block (naming both the product thinker and the facilitator) into the intent's `## Audit Notes`. The intent **stays in `shipped/`** — its `spec_id` / `kind` / directory + delivered artifacts are byte-untouched (a `drafts/` move would break the lifecycle invariant + spc-48 lint and read as a partial un-ship). The invitation seeds `/abcd:intent grill`; no machine authors a replan, nothing is auto-rolled-back. -**Gated manual verification + verification receipt (R5).** The manual-verification invitation renders **only** when the machine rollup is acceptance-eligible (all `MET`, or `MET_WITH_CONCERNS` with no `NOT_MET`) — if any criterion is not `MET`/concerns, the loop or replan invitation runs first; the product thinker is never asked to hand-test something the audit already knows is broken. The sign-off is recorded as a **verification receipt distinct from the machine verdict of record** — a separate JSON artifact under `.abcd/logbook/audit/verify-/receipt.json`, never merged into `## Audit Notes`: +**Gated manual verification + verification receipt (R5).** The manual-verification invitation renders **only** when the machine rollup is acceptance-eligible (all `MET`, or `MET_WITH_CONCERNS` with no `NOT_MET`) — if any criterion is not `MET`/concerns, the loop or replan invitation runs first; the product thinker is never asked to hand-test something the audit already knows is broken. The sign-off is recorded as a **verification receipt distinct from the machine verdict of record** — a separate JSON artifact under `.abcd/.work.local/logs/audit/verify-/receipt.json`, never merged into `## Audit Notes`: ```json { "intent_id": "itd-N", "machine_rollup": "MET", "state": "offered", @@ -408,19 +392,19 @@ Receipt **states**: `offered` (the gate opened — the drainer stamps this on a Introduced by itd-48 (which superseded itd-31). The opponent is *other documents*: compares the brief and every intent against each other (and against the brief itself), surfacing the five live judgement categories — **terminology drift, premise contradictions, scope leakage, sequencing impossibilities, naming conflicts**. No spec-store analogue — the spec store reviews one artefact at a time; corpus-wide consistency is pure abcd ground. -spc-29 ships the judgement half on demand via `/abcd:intent consistency` (Carmack-level oracle review). +The judgement half runs on demand via `/abcd:intent consistency` (Carmack-level oracle review) — a later phase (spc-29), not yet a binary sub-verb. -**Deferred follow-up** (recorded in `.abcd/work/issues/` under `[spc-29 follow-up]`): the mechanical-half lint categories — schema/state contradictions, reference rot, acknowledgement gaps — were originally planned as `internal/core/lint --cross-doc` codes `XD002`/`XD006`/`XD007` per `05-internals/06-lint.md`; the lint-code half is deferred to a follow-up intent. Pre-commit hook wiring that would let `/abcd:intent consistency` findings block commits is also deferred. +**Deferred follow-up**: the mechanical-half lint categories — schema/state contradictions, reference rot, acknowledgement gaps — were originally planned as `internal/core/lint --cross-doc` codes `XD002`/`XD006`/`XD007` per `05-internals/06-lint.md`; the lint-code half is deferred to a follow-up intent. Pre-commit hook wiring that would let `/abcd:intent consistency` findings block commits is also deferred. **Polymorphic on arg presence (same operation, narrowed scope):** bare = scan the whole corpus; with `` = scan one intent's relationship with the rest. This is *not* the forbidden hidden-state dispatch — the operation is identical; the arg just narrows scope (like `git log` vs `git log `). -Findings land in `.abcd/logbook/audit/consistency-/report.{json,md}`. +Findings land in `.abcd/.work.local/logs/audit/consistency-/report.{json,md}`. ### Role 3 — kind classification → `/abcd:intent shape []` Introduced alongside the three intent kinds (per itd-34). The opponent is the *kind taxonomy*: examines whether each intent's declared `kind` (the noun in frontmatter) still fits the corpus. The verb `shape` matches the taxonomy noun and pairs cleanly with `reclassify` (the action verb that commits a `shape` finding). -spc-29 ships the on-demand surface only. **Bare** scans the corpus; **with ``** checks one intent. Findings appear in `/abcd:intent` status output (passive surface) and as a separate report at `.abcd/logbook/audit/shape-/report.{json,md}`. The user accepts a suggestion via `/abcd:intent reclassify`; declined suggestions are logged for future review (so the reviewer doesn't re-surface the same suggestion every run). +The on-demand surface is a later phase (spc-29). **Bare** scans the corpus; **with ``** checks one intent. Findings land in a report under `.abcd/.work.local/logs/audit/shape-/report.{json,md}`. The user accepts a suggestion via `/abcd:intent reclassify`; declined suggestions are logged for future review (so the reviewer doesn't re-surface the same suggestion every run). **Concurrency contract** (between any future scheduled invocation and on-demand `shape`): @@ -431,7 +415,7 @@ spc-29 ships the on-demand surface only. **Bare** scans the corpus; **with ` **Deferred follow-up** (recorded in `.abcd/work/issues/` under `[spc-29 follow-up]`): pre-commit hook wiring for continuous shape scanning, and the `shape(...)` function's `mode="pre_commit"` parameter is preserved as a seam but no hook invokes it. Discipline subtype clustering ("once enough disciplines exist, surfaces 'three disciplines have similar `kind_notes`; consider formalising a subtype'") was named in earlier itd-34 drafts and is *not* shipped by spc-29 — it is not a live suggestion type. +> **Deferred follow-up**: pre-commit hook wiring for continuous shape scanning, and the `shape(...)` function's `mode="pre_commit"` parameter is preserved as a seam but no hook invokes it. Discipline subtype clustering ("once enough disciplines exist, surfaces 'three disciplines have similar `kind_notes`; consider formalising a subtype'") was named in earlier itd-34 drafts and is *not* shipped by spc-29 — it is not a live suggestion type. ### Review and audit trail layout @@ -453,4 +437,4 @@ The later-phase review/audit verbs write their per-run receipts under the local - `/abcd:audit chain` → `audit/chain-/` (conversation/edit-history Merkle, default application per itd-16 — a later phase) - `/abcd:audit lifeboat ` → `audit/lifeboat-/` (lifeboat-artefact integrity per itd-35 — a later phase) -`chain` and `lifeboat` are sub-verbs of `/abcd:audit` (the umbrella verb — a later phase; its backing intents itd-16 and itd-35 sit in `intents/drafts/`); `review`, `consistency`, `shape` are sub-verbs of `/abcd:intent`. Bare `/abcd:audit` and bare `/abcd:intent` are status+help only per the universal bare-command-as-help convention. +`chain` and `lifeboat` are later-phase sub-verbs of `/abcd:audit` (their backing intents itd-16 and itd-35 sit in `intents/drafts/`); `abcd audit` itself runs the read-only working-conventions conformance check, and bare invocation executes that check. `review` is a shipped sub-verb of `/abcd:intent`; `consistency` and `shape` are later phases. Bare `/abcd:intent` is status+help per the universal bare-command-as-help convention. diff --git a/.abcd/development/brief/04-surfaces/06-capture.md b/.abcd/development/brief/04-surfaces/06-capture.md index 07badb1..a7123f7 100644 --- a/.abcd/development/brief/04-surfaces/06-capture.md +++ b/.abcd/development/brief/04-surfaces/06-capture.md @@ -14,7 +14,7 @@ See itd-4 for the full intent. Ledger schema lives in the Go binary (`internal/c | `/abcd:capture list --resolved` | Query the ledger for resolved issues | — | | `/abcd:capture list --wontfix` | Query the ledger for wontfix issues | — | | `/abcd:capture list --all` | Query the ledger across all three states | — | -| `/abcd:capture promote ` | Promote an issue to an intent draft: skill-orchestrated (never a `capture promote` CLI sub-verb), it hands the issue body to `abcd intent ""`, which files a new draft under `intents/drafts/` seeded from that text. The reciprocal four-field back-link (`source_issue`/`promoted_to` scalars + `related_issues` ↔ `related_intents` lists) onto the `iss-N` record is written by hand — no engine verb writes that edge. | (issue stays; intent created in `drafts/`) | +| `/abcd:capture promote ` | Promote an issue to an intent draft: command-orchestrated (never a `capture promote` CLI sub-verb), it hands the issue body to `abcd intent ""`, which files a new draft under `intents/drafts/` seeded from that text. The reciprocal back-link (`promoted_to` scalar + `related_issues` ↔ `related_intents` lists) onto the `iss-N` record is written by hand — no engine verb writes that edge. | (issue stays; intent created in `drafts/`) | | `/abcd:capture resolve ""` | Mark issue resolved | `open/` → `resolved/` | | `/abcd:capture wontfix ""` | Explicit non-action decision | `open/` → `wontfix/` | @@ -66,13 +66,13 @@ Body is free-form: details, suggested fix, links to context. ## 3. Legacy scratch migration -A later phase, not yet built — the migration rides the `abcd dev-sync work` surface ([`02-disembark.md`](02-disembark.md)); the shipped Go ledger engine reserves a migrator-only `ForceID` seam for it. On first run of `abcd dev-sync work` after install (or first `/abcd:ahoy` upgrade), the command parses a free-form scratch buffer under `.abcd/.work.local/` entry-by-entry and promotes each to a corresponding `.abcd/work/issues/open/iss-N-.md`. Idempotent. The original scratch buffer under `.abcd/.work.local/` is preserved as a staging buffer (still works for ad-hoc scribbles; subsequent entries promoted on the next `abcd dev-sync work`). +A later phase, not yet built — the migration rides the `abcd dev-sync work` surface ([`08-abcd.md`](08-abcd.md)); the shipped Go ledger engine reserves a migrator-only `ForceID` seam for it. On first run of `abcd dev-sync work` after install (or first `/abcd:ahoy` upgrade), the command parses a free-form scratch buffer under `.abcd/.work.local/` entry-by-entry and promotes each to a corresponding `.abcd/work/issues/open/iss-N-.md`. Idempotent. The original scratch buffer under `.abcd/.work.local/` is preserved as a staging buffer (still works for ad-hoc scribbles; subsequent entries promoted on the next `abcd dev-sync work`). ## 4. Acceptance - **Given** an abcd-installed repo, **when** the user runs `/abcd:capture "review nitpick: T7 cache_ttl_days dead-config alternative"`, **then** a new file `.abcd/work/issues/open/iss-N-.md` exists with frontmatter populated and the captured text in the body. - **Given** an existing issue at `.abcd/work/issues/open/iss-3-foo.md`, **when** the user runs `/abcd:capture resolve iss-3 "fixed in spc-7 task 4"`, **then** the file moves to `.abcd/work/issues/resolved/iss-3-foo.md` with the resolution recorded. -- **Given** an existing issue, **when** the user runs `/abcd:capture promote iss-N`, **then** the issue body is handed to `abcd intent ""`, which files a new draft intent under `intents/drafts/` seeded from that text; the reciprocal four-field back-link (`source_issue`/`promoted_to` scalars + `related_issues` ↔ `related_intents` lists) is recorded by hand, since no engine verb writes that edge. +- **Given** an existing issue, **when** the user runs `/abcd:capture promote iss-N`, **then** the issue body is handed to `abcd intent ""`, which files a new draft intent under `intents/drafts/` seeded from that text; the reciprocal back-link (`promoted_to` scalar + `related_issues` ↔ `related_intents` lists) is recorded by hand, since no engine verb writes that edge. - **Given** a fresh `/abcd:ahoy` upgrade with an existing scratch buffer under `.abcd/.work.local/`, **when** `dev-sync` runs (a later phase, not yet built — § 3), **then** every entry in that scratch buffer is promoted to the structured ledger with provenance noting "migrated from `.abcd/.work.local/` scratch". - **Given** a ledger containing 5 open issues, **when** the user runs `/abcd:capture list --open` (the flag is explicit — there is no implicit default), **then** the output lists all 5 with id, state, severity, and slug, in derived-priority order — unblocked issues first, then severity (`critical` → `nitpick`); rows blocked by an open dependency are demoted and annotated with their open blockers. - **Given** a ledger with a mix of open, resolved, and wontfix issues, **when** the user runs `/abcd:capture list --all`, **then** every issue across all three states is listed; the equivalent unfiltered CLI form `abcd capture list` (no flag) instead exits 2 with a "choose a filter" message. @@ -88,8 +88,8 @@ A later phase, not yet built — the migration rides the `abcd dev-sync work` su - **Command flow:** delivered by `spc-21-abcdcapture-command-flow-text-ingest`. - **Legacy `.abcd/.work.local/` scratch migration:** design target per `spc-22-workissuesmd-migration-promote-legacy` — a later phase, not yet built (rides the `dev-sync` surface, § 3). - **intent-fidelity-reviewer cross-check:** delivered by `spc-23-intent-fidelity-reviewer-extension`. -- **`promote ` bridge:** the skill-orchestrated flow leans on +- **`promote ` bridge:** the command-orchestrated flow leans on `abcd intent ""`, delivered by `spc-7-abcd-intent-quoted-text-create-symmetric` (itd-46). The issue body is handed to that create path, which files a new draft; - the reciprocal four-field back-link onto the `iss-N` record is written by hand — + the reciprocal back-link onto the `iss-N` record is written by hand — no engine verb writes that edge. diff --git a/.abcd/development/brief/04-surfaces/07-memory.md b/.abcd/development/brief/04-surfaces/07-memory.md index ffe1928..8402a72 100644 --- a/.abcd/development/brief/04-surfaces/07-memory.md +++ b/.abcd/development/brief/04-surfaces/07-memory.md @@ -9,7 +9,7 @@ For the **substrate spec** (page-class enum, source-class taxonomy, curator beha Bare `/abcd:memory` shows status + help + render of current memory state — never mutates state. Per the [bare-command-as-render discipline](../02-constraints/04-naming.md). Current sub-verbs (each does something bare cannot): - **Bare `/abcd:memory`** — render: page count by class (e.g., "23 session_memory + 8 external_pdf + 4 oracle_review + 2 spec_modification_grammar"), last-ingest timestamp, recent contradictions surface, suggested next actions. No mutation. Quotation-budget headroom per source renders READ-ONLY from the spc-39 `.coverage_index.json`: when the index is present AND fingerprint-fresh (a read-only crawl recomputes the current fingerprint and matches the stored one) it shows per-source warn/block headroom; fingerprint drift shows a "stale — run /abcd:memory lint" hint; an absent index an info line; a malformed index or crawl failure a non-fatal "headroom unavailable" line. The bare render never rebuilds or mutates the index. -- **`/abcd:memory ingest `** — register an external source (transcript / article / URL; PDF is a later-phase seam — the binary rejects PDF sources with a clear error until a text-extraction dependency is wired) as typed entity/topic pages with citation frontmatter, appending to the ingest log. The host agent is the distiller: it reads the source, produces the `DistilledPage` JSON array, and passes it via the load-bearing `--pages-json ` flag (required for a new source; an already-known source re-ingests from the registry without it); the binary computes provenance, licence, and content hash, validates every page, and writes atomically. **Default: do NOT store original.** Flag-shaped modifier: `--keep-original` (opt-in storage at `.abcd/memory/sources/.`; the later-phase lifeboat licence gate — `/abcd:disembark`, NOT launch, per adr-18 — is designed to refuse publish without an explicit allowlist entry; launch excludes `.abcd/**` wholesale per [`04-launch.md § 2`](04-launch.md#2-curated-release-artefact-default-deny)). +- **`/abcd:memory ingest `** — register an external source (transcript / article / URL; PDF is a later-phase seam — the binary rejects PDF sources with a clear error until a text-extraction dependency is wired) as typed entity/topic pages with citation frontmatter, appending to the ingest log. The host agent is the distiller: it reads the source, produces the `DistilledPage` JSON array, and passes it via the load-bearing `--pages-json ` flag (required for a new source; an already-known source re-ingests from the registry without it); the binary computes provenance, licence, and content hash, validates every page, and writes atomically. **Default: do NOT store original.** Flag-shaped modifier: `--keep-original` (opt-in storage at `.abcd/memory/sources/.`; the later-phase lifeboat licence gate — `/abcd:disembark`, NOT launch, per adr-28 — is designed to refuse publish without an explicit allowlist entry; launch excludes `.abcd/**` wholesale per [`04-launch.md § 2`](04-launch.md#2-curated-release-artefact-default-deny)). - **`/abcd:memory ask `** — query memory by domain + class; synthesise an answer with citations (every citation references `source.class` + `citation` + `source_hash`); optionally file the result back as a new memory page (flag-driven: `--file-back` with `--page-json `, the host-produced answer page; `--top-n ` sets retrieval depth, 0 uses the pinned default). - **`/abcd:memory lint` (spc-39)** — full-store curator health-check: per-page quotation budgets (`MQ001`), cumulative source coverage (`MQ002`), coverage-unavailable diagnostic (`MQ003`, info), source-class single-class advisory (`MS001`), cross-class without weighting note (`MS002`), missing licence on `external_*` (`ML001`). ALWAYS crawls the full repo store, rebuilds the regenerable `.coverage_index.json`, emits findings to `.abcd/.work.local/logs/memory/lint-/report.{json,md}`. Exit: blockers → nonzero; warn-only → 0 (curator advisory — see [`06-lint.md §2`](../05-internals/06-lint.md#2-severity-model)). Mutates no memory-store state (coverage index + logbook report are its only writes). Per ADR-13's write/lint split, the spc-38 write core ships ingest/ask/bare; spc-39 ships this lint family ONLY — contradictions are rendered by spc-38's reconciliation (surfaced by the bare render), orphan/stale-claim audits are deferred. @@ -67,7 +67,7 @@ DISCARD ORIGINAL (default behaviour) line only (`## [YYYY-MM-DD HH:MM] | `) ``` -`--keep-original` opts the user into storing the original at `.abcd/memory/sources/.`. Later phase: the spc-38 restrictive-licence gate refuses to publish anything under `.abcd/memory/sources/` unless `.abcd/launch-allowlist.json` explicitly names the file — the shipped ingest classifies restrictive licences but never gates. Per adr-18 this gate is the **lifeboat's** (`/abcd:disembark`), NOT launch's — launch excludes `.abcd/**` wholesale and never publishes `.abcd/memory/sources/`; the gate is future/inert at launch. +`--keep-original` opts the user into storing the original at `.abcd/memory/sources/.`. Later phase: the spc-38 restrictive-licence gate refuses to publish anything under `.abcd/memory/sources/` unless `.abcd/launch-allowlist.json` explicitly names the file — the shipped ingest classifies restrictive licences but never gates. Per adr-28 this gate is the **lifeboat's** (`/abcd:disembark`), NOT launch's — launch excludes `.abcd/**` wholesale and never publishes `.abcd/memory/sources/`; the gate is future/inert at launch. ## 2. Acceptance Criteria (Given-When-Then, per itd-1) @@ -75,7 +75,7 @@ See [the full acceptance criteria](../../intents/planned/itd-36-memory-unificati - **Bare**: bare `/abcd:memory` renders current state; never mutates. - **Ingest default-no-original**: original NOT stored unless `--keep-original`; citation + source_hash recorded; quotation budget applied per page (enforced at lint time by spc-39's `MQ001`, never at ingest). -- **Ingest with `--keep-original`**: original stored at `.abcd/memory/sources/.`; the later-phase lifeboat licence gate (`/abcd:disembark`, not launch — adr-18) is designed to refuse publish without allowlist. +- **Ingest with `--keep-original`**: original stored at `.abcd/memory/sources/.`; the later-phase lifeboat licence gate (`/abcd:disembark`, not launch — adr-28) is designed to refuse publish without allowlist. - **Ask**: synthesises answer with per-citation provenance (class + citation + source_hash); optionally files result back. - **Lint (spc-39, not spc-38 behaviour)**: emits `MQ001` / `MQ002` / `MQ003` / `MS001` / `MS002` / `ML001` codes; cumulative coverage uses span-level dedup. spc-38 writes `licence: unknown` explicitly; spc-39's `ML001` is what lints it. - **Schema extension on existing**: existing flat-named pages preserved; `index.md` generated over them; `source.class: session_memory` backfilled as default. @@ -91,16 +91,16 @@ See [the full acceptance criteria](../../intents/planned/itd-36-memory-unificati ├── ask-/ # later phase — comes with the principle-distiller curator (Phase 6) │ └── ask-report.{json,md} # question, retrieved page slugs, synthesised answer with citations └── lint-/ - └── report.{json,md} # lint findings: MQ001/MQ002/MS001/MS002/ML001 with locations + └── report.{json,md} # lint findings: MQ001/MQ002/MQ003/MS001/MS002/ML001 with locations ``` Only `lint-/report.{json,md}` is written by the shipped store (spc-39's lint); ingest and ask write no reports — the `ingest-` and `ask-` trees come in a later phase with the principle-distiller curator. Runtime artefacts (reports only) live in the gitignored `.abcd/.work.local/logs/` tier. ## 4. Composition with adjacent surfaces -- **`/abcd:disembark`** exports curated project memory/provenance into the lifeboat (designed behaviour; itd-36 doesn't change disembark's source-mapping). What the lifeboat is specified to carry is the curated provenance surface named in [`02-disembark.md §5`](02-disembark.md) — `research/pitfalls.{json,md}`, `assets/_manifest.json`'s provenance/classification, and root `_provenance.json` — **not** a verbatim `.abcd/memory/` payload; declaring an exact `.abcd/memory/`-verbatim payload is deferred to the disembark spec that wires the lifeboat packer (adr-18). The recovery-humility framing on disembark/embark applies: the lifeboat is the floor of recoverable theory, not theory itself. +- **`/abcd:disembark`** exports curated project memory/provenance into the lifeboat (designed behaviour; itd-36 doesn't change disembark's source-mapping). What the lifeboat is specified to carry is the curated provenance surface named in [`02-disembark.md §5`](02-disembark.md) — `research/pitfalls.{json,md}`, `assets/_manifest.json`'s provenance/classification, and root `_provenance.json` — **not** a verbatim `.abcd/memory/` payload; declaring an exact `.abcd/memory/`-verbatim payload is deferred to the disembark spec that wires the lifeboat packer (adr-28). The recovery-humility framing on disembark/embark applies: the lifeboat is the floor of recoverable theory, not theory itself. - **`/abcd:embark`** unpacks a lifeboat's record families into the receiving repo; carrying `.abcd/memory/` forward is designed behaviour. Source-class enum carries forward; receiver runs `/abcd:memory lint` (spc-39) post-unpack to verify quotation budgets and licences haven't drifted. -- **`/abcd:launch`** does **not** consume the provenance substrate's licence gate (adr-18): the public launch payload excludes `.abcd/**` — including `.abcd/memory/**` — wholesale as policy, so launch never publishes the files the gate checks. The restrictive-licence gate's real consumer is the **lifeboat** (`/abcd:disembark`, above), the surface that publishes curated project memory/provenance; the later-phase gate is designed to refuse publish on restrictive-licence files and warn on `licence: unknown`. At launch the gate is future/inert — launch excludes `.abcd/**` wholesale, so `/abcd:launch dry-run` surfaces no licence-gate verdict (its dry-run gates are the secret/PII scan, marker-block, installability smoke, and documentation-auditor; see [`04-launch.md § 2`](04-launch.md#2-curated-release-artefact-default-deny) and adr-18). +- **`/abcd:launch`** does **not** consume the provenance substrate's licence gate (adr-28): the public launch payload excludes `.abcd/**` — including `.abcd/memory/**` — wholesale as policy, so launch never publishes the files the gate checks. The restrictive-licence gate's real consumer is the **lifeboat** (`/abcd:disembark`, above), the surface that publishes curated project memory/provenance; the later-phase gate is designed to refuse publish on restrictive-licence files and warn on `licence: unknown`. At launch the gate is future/inert — launch excludes `.abcd/**` wholesale, so `/abcd:launch dry-run` surfaces no licence-gate verdict (its dry-run gates are the secret/PII scan, marker-block, installability smoke, and documentation-auditor; see [`04-launch.md § 2`](04-launch.md#2-curated-release-artefact-default-deny) and adr-28). - **`/abcd:dredge`** (a later phase, itd-25) writes synthesis output to `.abcd/memory/__.md` with `source.class: dredge_synthesis`. Distinct verb (storage vs operation per dredge-pushback in idea-1 R4); shared destination namespace. - **Native** specs inherit from itd-37 modification grammar — at spec completion, `principle-distiller` (design target — Phase 6, per [`05-internals/01-agents.md`](../05-internals/01-agents.md); nothing ships this yet) extracts the spec's `## Modification Grammar` section into `spec_modification_grammar_.md` (append-only) and updates curator-merged `modification_grammar_.md` (compounding-curated). User does not invoke `/abcd:memory` for this — the extraction is designed to be automatic on spec completion. @@ -111,8 +111,8 @@ Only `lint-/report.{json,md}` is written by the shipped store (spc-39's | Schema extension | 3 new sibling files (`index.md`, `log.md`, `contradictions.md`) + typed `source:` frontmatter; no migration of existing flat-named files | | Curator role on `principle-distiller` | Role extension (Phase 6 design-target agent per [`05-internals/01-agents.md`](../05-internals/01-agents.md), not yet shipped; itd-31 precedent — the design-target agent roster stays at 16) | | Provenance/licence substrate | Separable spec at [`05-internals/09-provenance-substrate.md`](../05-internals/09-provenance-substrate.md); shared with the later-phase itd-26 loot | -| Lifeboat licence-gate extension (adr-18) | `.abcd/memory/sources/` allowlist + restrictive-licence detection over the lifeboat's gated payload (`/abcd:disembark`); NOT a launch payload gate — launch excludes `.abcd/**` wholesale, so the gate is future/inert at launch | -| Lint codes | New family: `MQ001` (per-page quotation), `MQ002` (cumulative coverage), `MS001` (source-class single-class), `MS002` (mixed-class without weighting note), `ML001` (licence missing) | +| Lifeboat licence-gate extension (adr-28) | `.abcd/memory/sources/` allowlist + restrictive-licence detection over the lifeboat's gated payload (`/abcd:disembark`); NOT a launch payload gate — launch excludes `.abcd/**` wholesale, so the gate is future/inert at launch | +| Lint codes | New family: `MQ001` (per-page quotation), `MQ002` (cumulative coverage), `MQ003` (coverage-unavailable diagnostic, info), `MS001` (source-class single-class), `MS002` (mixed-class without weighting note), `ML001` (licence missing) | **Tight coupling** (logged as a principal risk): itd-36 is structurally non-decomposable — sub-verbs need the schema; schema needs the curator role; curator role needs the lint codes. Partial ship not meaningful. diff --git a/.abcd/development/brief/04-surfaces/08-abcd.md b/.abcd/development/brief/04-surfaces/08-abcd.md index 9ecce69..05b0d1f 100644 --- a/.abcd/development/brief/04-surfaces/08-abcd.md +++ b/.abcd/development/brief/04-surfaces/08-abcd.md @@ -193,5 +193,5 @@ no distinct status-render module yet. ## Related documentation - Naming / SD001 discipline: [`../02-constraints/04-naming.md`](../02-constraints/04-naming.md) -- Intent: `itd-20` (`../intents/…/itd-20-top-level-abcd-dispatcher.md`) +- Intent: [`itd-20`](../../intents/planned/itd-20-top-level-abcd-dispatcher.md) - The per-verb bare renders this board complements: [`05-intent.md`](05-intent.md), [`01-ahoy.md`](01-ahoy.md) diff --git a/.abcd/development/brief/04-surfaces/README.md b/.abcd/development/brief/04-surfaces/README.md index c10cc06..3f6bf8a 100644 --- a/.abcd/development/brief/04-surfaces/README.md +++ b/.abcd/development/brief/04-surfaces/README.md @@ -23,9 +23,9 @@ The brief's user-facing command surface is the set enumerated below (not all are The **Status** column is machine-checked: the `surface_coverage` record-lint rule asserts every `shipped` row has a backing surface (`commands/abcd/.md` or `skills//`) and every `staged` row (a design target) has none — and, in reverse, that every real surface has a row here. The bare `/abcd` top-level is binary-backed (its command file is `commands/abcd.md`, not a `commands/abcd/.md` entry) and is exempt from the file check. Keeping this column honest is how the brief's surface set stays reconciled with the shipped binary; the semantic half — whether each row's *prose* matches binary behaviour — stays a release-gate agent check. -**Bare-command-as-help is a universal abcd convention** — every command shows read-only status when invoked without args (bare `abcd`, `abcd ahoy`, `abcd capture`, `abcd memory` all render status). The **suggested-next-actions** half of this convention is a design target: no shipped bare invocation yet emits next actions (bare `abcd launch` currently only hints to pass `--dry-run`). Provides discoverability without forcing the user to remember subcommand names. +**Bare-command-as-status is a common abcd convention, not a universal one** — many commands render read-only status when invoked without args (bare `abcd`, `abcd ahoy`, `abcd capture`, `abcd memory`, `abcd intent`, and `abcd spec` all render a status board), but it does not hold everywhere: `version` prints only the version string, and the `docs`, `history`, and `disembark` cobra parents print help/usage with no status board. The **suggested-next-actions** half of this convention is a design target: no shipped bare invocation yet emits next actions (bare `abcd launch` currently only hints to pass `--dry-run`). Provides discoverability without forcing the user to remember subcommand names. -**abcd ships zero skills** — the `/abcd:` surface is commands only. `/abcd:consult`, `/abcd:ingest`, and `/abcd:prepare-this-repo` (rows 13–15) were once shipped as skills but are commands: each mutates state (the sources corpus, its ledger, the target repo), which the boundary rule makes command-shaped. They are **host-delegated** commands (`commands/abcd/.md`, no Go verb — the workflow runs in the host agent). The skill/command boundary is documented in [`05-internals/08-skills.md`](../05-internals/08-skills.md). (`/abcd:grill` was likewise proposed as a skill but is now a sub-verb of `/abcd:intent` — its mid-session glossary writes and per-session logbook output are command-shaped.) +**abcd ships zero skills** — the `/abcd:` surface is commands only. `/abcd:consult`, `/abcd:ingest`, and `/abcd:prepare-this-repo` (rows 13–15) were once shipped as skills but are commands: each mutates state (the sources corpus, its ledger, the target repo), which the boundary rule makes command-shaped. They are **host-delegated** commands (`commands/abcd/.md`, no Go verb — the workflow runs in the host agent). The skill/command boundary is documented in [`05-internals/08-skills.md`](../05-internals/08-skills.md). (`/abcd:grill` was likewise proposed as a skill; its **design target** is promotion to a sub-verb of `/abcd:intent` — its mid-session glossary writes and per-session logbook output are command-shaped — but the `intent` parent ships no `grill` sub-verb yet.) ## Where to find related design diff --git a/.abcd/development/brief/05-internals/01-agents.md b/.abcd/development/brief/05-internals/01-agents.md index 83821bf..c05bcbf 100644 --- a/.abcd/development/brief/05-internals/01-agents.md +++ b/.abcd/development/brief/05-internals/01-agents.md @@ -2,6 +2,8 @@ The catalog below declares the **16-agent design roster** (not all are shipped yet; the **Status** column marks which of these exist today, and [`06-delivery/`](../06-delivery) carries current delivery state); the shipped agents that live outside this roster are listed under [§ Shipped agents outside the design roster](#shipped-agents-outside-the-design-roster). Ten agent prompt files ship in `agents/` today. Each catalog agent declares JSON inputs/outputs (schemas owned by the core, `internal/core/schema`). Agents are **markdown**, host-delegated reviewers the host dispatches (adr-25); markdown is rendered, not authored by agents. +`agents/` also holds two plain docs — `agents/README.md` and `agents/CHANGELOG.md` — that carry no agent frontmatter. Because `.claude-plugin/plugin.json` declares no `agents` key, the plugin loader globs the flat `agents/*.md` set and registers both as harness agents (`abcd:README`, `abcd:CHANGELOG`) alongside the real prompt files; iss-110 tracks the mis-registration. + | Agent | Pass | Status | Inputs (JSON) | Outputs (JSON) | |---|---|---|---|---| | `flow-essence` | A | design target — Phase 6 | native spec store (newest-first) | `spec-essence.json` | @@ -83,7 +85,7 @@ Every agent's prompt file carries declared frontmatter. Current fields: |---|---|---|---| | `prompt_version` | yes | itd-5 | Semver of the agent prompt; bumps on any prompt change | | `capability_scope` | synthesis/composer prompts | itd-5 extension (idea-4 jagged-frontier) | Declared task classes the agent is designed for. Object: `{ task_classes: [, ...], designed_for: "" }`. **`task_classes` is authored as a YAML inline list** — `task_classes: [spec_planning, audit]` on one line, never a block list of `- token` items (the frontmatter parser does not support a block list nested under a nested key). Carried today by the five synthesis/composer prompts (`graveyard-interpreter`, `release-changelog-composer`, `press-release-composer`, `principle-distiller`, `lifeboat-oracle`); the reviewer prompts omit it. Set-membership lint in `internal/core/lint` against the `task_classes` closed enum (owned by `internal/core/schema`; prose counterpart in `02-constraints/04-naming.md`, PR-to-extend) is a design target — no shipped check reads the field. Cites Dell'Acqua et al. 2023 ("Navigating the Jagged Technological Frontier") as the framing source. | -| `reads_untrusted_input` | conditional (agents that read untrusted input) | itd-5 | Boolean. `true` declares the agent reads attacker-influenceable input (transcripts, lifeboats, GitHub issues, commit messages, model-emitted reviews). When `true`, the agent MUST carry at least one canary fixture under `agents//fixtures/`; the prompt-lint checks in `internal/core/lint` enforce fixture *presence*. | +| `reads_untrusted_input` | conditional (agents that read untrusted input) | itd-5 | Boolean. `true` declares the agent reads attacker-influenceable input (transcripts, lifeboats, GitHub issues, commit messages, model-emitted reviews). When `true`, the agent MUST carry at least one canary fixture under `agents//fixtures/`; a fixture-presence prompt-lint in `internal/core/lint` is a design target — no shipped check enforces it (the M6 agents ship conforming files, not the linter — see `agents/README.md`). | **Deliberately omitted from agent frontmatter** (boundary against scope creep, per idea-4's static/dynamic split): `known_failure_modes` (runtime-appended events), per-task-class `model_id` history, plan-time capability gating output. These belong to the later-phase Frontier Awareness intent — capture-stable, no ID reserved. diff --git a/.abcd/development/brief/05-internals/03-configuration.md b/.abcd/development/brief/05-internals/03-configuration.md index 61b027a..b118949 100644 --- a/.abcd/development/brief/05-internals/03-configuration.md +++ b/.abcd/development/brief/05-internals/03-configuration.md @@ -350,20 +350,6 @@ abcd/ │ └── … # plus operator-internal commands │ # NOTE: `uninstall` is a sub-verb of /abcd:ahoy (not a standalone command). The ahoy command │ # markdown handles the install/uninstall/dry-run/destroy sub-verb dispatch internally. -├── skills/ # see 08-skills.md — abcd ships ZERO user-facing skills. -│ # # The entries below are plugin-runtime workflow files that each -│ # # command points at internally; they are NOT user-facing skills -│ # # surfaced under /abcd:. The only would-be user-facing skill -│ # # (/abcd:grill) is a /abcd:intent grill sub-verb. A later phase -│ # # may introduce new user-facing skills here. -│ ├── abcd-ahoy/{SKILL.md, workflow.md} -│ ├── abcd-disembark/{SKILL.md, workflow.md} -│ ├── abcd-embark/{SKILL.md, workflow.md} -│ ├── abcd-launch/{SKILL.md, workflow.md} -│ ├── abcd-intent/{SKILL.md, workflow.md} -│ ├── abcd-capture/{SKILL.md, workflow.md} -│ ├── commit-attribution/SKILL.md -│ └── secrets-and-pii/SKILL.md # consolidated pii-protection + secret-scan ├── agents/ # 10 agents — see 01-agents.md (markdown, host-delegated) │ ├── docs-currency-reviewer.md / graveyard-interpreter.md / intent-fidelity-reviewer.md │ ├── lifeboat-oracle.md / press-release-composer.md / principle-distiller.md diff --git a/.abcd/development/brief/05-internals/06-lint.md b/.abcd/development/brief/05-internals/06-lint.md index ed1562b..e4fa5e5 100644 --- a/.abcd/development/brief/05-internals/06-lint.md +++ b/.abcd/development/brief/05-internals/06-lint.md @@ -15,7 +15,7 @@ Lint codes are 2-letter category prefix + 3-digit numeric suffix: `AAnnn`. Categ | `GR` | Grill | `internal/core/lint` | Intent has acceptance but never grilled / grill-state inconsistencies | | `RV` | Review (spec-tied) | future `internal/core/lint` (post-spc-2 task 3) | Review front-matter / verdict enum / SHA validation / size cap | | `IS` | Issue Ledger | `internal/core/lint` (capture extension) | Issue frontmatter / supersedes / state machine | -| `PQ` | Prompt Quality | `internal/core/lint` | Mechanical agent-prompt checks: `prompt_version` semver, `agents/CHANGELOG.md` heading, `capability_scope.task_classes` enum membership, inline-list syntax, canary-fixture presence (`PQ001`–`PQ006`). Broader structural lint — research footer, example-I/O, canary *execution* — is deferred (see §1 reserved codes). | +| `PQ` | Prompt Quality | `internal/core/lint` | Mechanical agent-prompt checks — a **design target**, not yet shipped (no `PromptLinter` exists in `internal/core/lint`): `prompt_version` semver, `agents/CHANGELOG.md` heading, `capability_scope.task_classes` enum membership, inline-list syntax, canary-fixture presence (`PQ001`–`PQ006`). Broader structural lint — research footer, example-I/O, canary *execution* — is deferred (see §1 reserved codes). | | `TM` | Terminology | `internal/core/lint` (new in spc-3 task 1) | Glossary file schema validation | | `XD` | Cross-Document (post-audit, itd-48 / superseded itd-31; mechanical-half codes **deferred** per spc-29 — see the `.abcd/work/issues/` ledger `[spc-29 follow-up]`) | `internal/core/lint --cross-doc` | R1–R7 meta-audit (terminology drift, premise drift, scope leakage, reference rot, naming reservation conflicts, schema/state contradictions, acknowledgement gaps) | | `MQ` | Memory Quotation (added 2026-05-08, itd-36) | `internal/core/lint` (memory extension) | Per-page bounded quotation budget + cumulative source-coverage ceiling; prevents copyright-laundering via N-page reconstruction | @@ -60,7 +60,7 @@ Lint codes are 2-letter category prefix + 3-digit numeric suffix: `AAnnn`. Categ - `GR005` provenance mismatch — PRD `source_intent_hash` does not match current `grilled_intent_hash`, OR PRD `grill_report_hash` does not match on-disk grill-report file. Severity: blocker. Delivered in spc-3 task .5. - `RV001`–`RV010` — reserved for review lint (front-matter required fields, verdict enum, SHA validation, size cap, etc.) — itd-28 / spc-2 task 3 - `IS001`–`IS010` — reserved for itd-4 issue capture -- `PQ001`–`PQ006` — **delivered** in spc-8 task .8 (`internal/core/lint` / `PromptLinter`). These are the *mechanical* prompt-quality checks spc-8 ships: `PQ001` agent prompt missing `prompt_version` or not valid semver; `PQ002` no matching `### ` heading in `agents/CHANGELOG.md`; `PQ003` `capability_scope` absent or malformed; `PQ004` a `capability_scope.task_classes` token not in `task_classes.json` (closed-enum set-membership); `PQ005` `capability_scope` authored with block-list syntax (must be an inline list); `PQ006` declares `reads_untrusted_input: true` but no canary fixture is *present*. Severity: all blocker. The broader prompt-structural checks of [`05-prompt-quality.md § C`](05-prompt-quality.md) — research-footer / example-I/O structural lint and canary-fixture *execution* (running the canary, not just checking it exists) — are **deferred to a later prompt-test spec** and are not part of the `PQ` codes shipped here. +- `PQ001`–`PQ006` — a **design target** (itd-5 prompt-quality discipline); no `PromptLinter` ships in `internal/core/lint` yet — the agent files carry conforming frontmatter, but the linter that will enforce it is not built (see `agents/README.md`). The intended *mechanical* prompt-quality checks are: `PQ001` agent prompt missing `prompt_version` or not valid semver; `PQ002` no matching `### ` heading in `agents/CHANGELOG.md`; `PQ003` `capability_scope` absent or malformed; `PQ004` a `capability_scope.task_classes` token not in `task_classes.json` (closed-enum set-membership); `PQ005` `capability_scope` authored with block-list syntax (must be an inline list); `PQ006` declares `reads_untrusted_input: true` but no canary fixture is *present*. Severity: all blocker. The broader prompt-structural checks of [`05-prompt-quality.md § C`](05-prompt-quality.md) — research-footer / example-I/O structural lint and canary-fixture *execution* (running the canary, not just checking it exists) — are **deferred to a later prompt-test spec** and are not part of this `PQ` code family. - `TM001` **RETIRED (tombstoned, never reused).** Was the single collapse code the pre-spc-82.2 `internal/core/lint` terminology adapter folded EVERY `internal/core/lint` schema-validation error into (the tool emitted plain text with no per-error sub-classification, so one code carrying the raw message was the honest registration). spc-82.2 gives `internal/core/lint` a native `--json`/`--codes` interface with per-class TM codes (`TM003`–`TM011` below), so the collapse code is no longer emitted. `TM001` is tombstoned: it is never emitted and never re-allocated to a new meaning. - **TM allocation table (spc-82.2 R2 — one code per distinguishable `internal/core/lint` error class; recorded BEFORE the native interface landed).** Each distinguishable error class the linter's `_validate_text` produces maps to exactly one NEW code (`TM003`+), OR is classified as a tool HARD-ERROR (exit 2, not a finding). `TM001` retired (above); `TM002` frozen — untouched. @@ -88,7 +88,7 @@ Lint codes are 2-letter category prefix + 3-digit numeric suffix: `AAnnn`. Categ - `TM009` terminology `bounded_context` ≠ directory name — the `bounded_context` value does not equal the parent directory name. Severity: blocker. **Delivered** in spc-82 task .2. - `TM010` terminology `forbidden_synonyms` ∩ `aliases` overlap — a value appears in both lists. Severity: blocker. **Delivered** in spc-82 task .2. - `TM011` terminology lifecycle-field asymmetry — under `status: stable`, exactly one of `starts_when`/`ends_when` is set while the other is missing (both required together). Severity: blocker. **Delivered** in spc-82 task .2. -- `TM002` abcd-owned prose body uses the `core/spec` forbidden synonym `epic` (use `spec`) — a SECOND terminology code (spc-65, itd-43), distinct from `TM001`. Unlike `TM001` (term-file schema validation via the `internal/core/lint` terminology checker), `TM002` is emitted IN-PROCESS by `internal/core/lint`'s `_lint_terminology_prose_files` helper: it scans abcd-owned prose BODY for the forbidden synonym `epic` (ONLY `epic` — not the other `core/spec` `forbidden_synonyms` `sprint`/`milestone`/`project`/`feature`, which are common English words; broad multi-synonym prose enforcement is future work) and flags each unexempt occurrence. Severity: blocker (config-resolvable). The CANONICAL in-scope path set is the single source of truth (`docs/**/*.md`, `commands/abcd/**/*.md`, `.abcd/development/brief/**/*.md`, root `README.md`, `agents/README.md` + nested `agents/**/README.md`, `intents/README.md`, the live-intent corpus `drafts/`+`disciplines/`+`planned/*.md`, and `skills/abcd-intent-grill/SKILL.md`); historical records (intents `shipped/`+`superseded/`, `decisions/adrs/**`, `research/**`) and terminology term files are EXCLUDED by path. FIVE machine-checkable exemptions: (i) a line containing the external token `flow-next:epic-review` / `epic-review` / `flowctl` passes; (ii) historical records excluded by path; (iii) terminology term files excluded by path (the `forbidden_synonyms` declaration stays owned by `TM001`); (iv) fenced (triple-backtick) and inline (single-backtick) code spans are masked — which is why naming the forbidden noun inside a code span here does NOT fire; (v) YAML frontmatter is not scanned (BODY only — `slug:` / `glossary_terms_used:` machine fields are not prose). Dispatched through BOTH `internal/core/lint` entrypoints — `dispatch` (working-tree bytes) and `dispatch_staged` (the pre-commit staged tier, staged-blob bytes; findings carry a 1-based `line > 0` so they survive the staged-line filter). **Delivered** in spc-65 task .1. +- `TM002` abcd-owned prose body uses the `core/spec` forbidden synonym `epic` (use `spec`) — a SECOND terminology code (spc-65, itd-43), distinct from `TM001`. Unlike `TM001` (term-file schema validation via the `internal/core/lint` terminology checker), `TM002` is emitted IN-PROCESS by `internal/core/lint`'s `_lint_terminology_prose_files` helper: it scans abcd-owned prose BODY for the forbidden synonym `epic` (ONLY `epic` — not the other `core/spec` `forbidden_synonyms` `sprint`/`milestone`/`project`/`feature`, which are common English words; broad multi-synonym prose enforcement is future work) and flags each unexempt occurrence. Severity: blocker (config-resolvable). The CANONICAL in-scope path set is the single source of truth (`docs/**/*.md`, `commands/abcd/**/*.md`, `.abcd/development/brief/**/*.md`, root `README.md`, `agents/README.md` + nested `agents/**/README.md`, `intents/README.md`, and the live-intent corpus `drafts/`+`disciplines/`+`planned/*.md`); historical records (intents `shipped/`+`superseded/`, `decisions/adrs/**`, `research/**`) and terminology term files are EXCLUDED by path. FIVE machine-checkable exemptions: (i) a line containing the external token `flow-next:epic-review` / `epic-review` / `flowctl` passes; (ii) historical records excluded by path; (iii) terminology term files excluded by path (the `forbidden_synonyms` declaration stays owned by `TM001`); (iv) fenced (triple-backtick) and inline (single-backtick) code spans are masked — which is why naming the forbidden noun inside a code span here does NOT fire; (v) YAML frontmatter is not scanned (BODY only — `slug:` / `glossary_terms_used:` machine fields are not prose). Dispatched through BOTH `internal/core/lint` entrypoints — `dispatch` (working-tree bytes) and `dispatch_staged` (the pre-commit staged tier, staged-blob bytes; findings carry a 1-based `line > 0` so they survive the staged-line filter). **Delivered** in spc-65 task .1. - `XD001` terminology drift, `XD002` schema/state contradiction, `XD003` reservation conflict, `XD004` premise drift, `XD005` scope leakage, `XD006` reference rot, `XD007` acknowledgement gap — reserved by itd-48 (which superseded itd-31); the mechanical-half codes (`XD002`, `XD006`, `XD007`) are **deferred** post-spc-29 (see the `.abcd/work/issues/` ledger `[spc-29 follow-up]`) - `MQ001` per-page quotation budget exceeded (>5% per page OR >150-word contiguous span); `MQ002` cumulative source coverage exceeded (>25% deduplicated coverage across all pages citing one `source_hash`, span-level dedup, configurable threshold). Severity: `MQ001` warn; `MQ002` per-finding at emission — warn in [15%, 25%), blocker ≥25% (thresholds configurable via `.abcd/memory/config.json` `quotation_budget`; a blocker fires only when UNAMBIGUOUS single-source attribution alone reaches the block threshold — ambiguous multi-source attribution caps at warn). Reserved by itd-36 (memory unification, 2026-05-08); **Delivered** in spc-39 .2 (`MemoryLinter._check_quotation` page-local MQ001; `run_memory_coverage_lint` full-corpus MQ002 + the regenerable `.abcd/memory/.coverage_index.json` rebuild). MQ002 runs ONLY on the full-corpus path (`/abcd:memory lint`, wired by spc-39 .3) — never the changed-file orchestrator. - `MQ003` coverage unavailable — a source's `source_token_count` is missing/malformed/zero in `.sources_index.json` (including an entirely absent registry entry): the MQ001 ratio check and MQ002 are SKIPPED for that source and this diagnostic is emitted instead — never a divide-by-zero, never a false block (the denominator-free contiguous-span half of MQ001 still applies). Severity: info (non-blocking). **Delivered** in spc-39 .2. diff --git a/.abcd/development/brief/05-internals/08-skills.md b/.abcd/development/brief/05-internals/08-skills.md index 8fef54f..6efb613 100644 --- a/.abcd/development/brief/05-internals/08-skills.md +++ b/.abcd/development/brief/05-internals/08-skills.md @@ -13,10 +13,10 @@ A **skill** is a procedural-workflow surface — a markdown-encoded interview, a A **command** is a stateful operation that creates, modifies, or moves artefacts. Commands have: - A **brief surface file** (`04-surfaces/NN-.md`) describing acceptance criteria, interaction flow, and side effects (or a sub-verb row in an existing parent's surface file) — for example `docs`, `history`, and `version` are chapters `10`–`12`. The CLI-only binary verbs `changelog`, `rules`, and `spec` have a Go verb but no surface chapter. -- A **per-invocation report subdirectory** (`.abcd/.work.local/logs///`) for commands that emit run reports. -- A **status + help** mode when called bare — the bare-status-board convention holds for `ahoy`, `capture`, and `memory` (and bare `abcd`); it is not universal (`version` prints only the version string, and the `docs`/`history` cobra parents print help/usage with no status board). +- A **per-invocation report subdirectory** under the gitignored `.abcd/.work.local/logs//` tier for commands that emit run reports — `memory lint` writes each run to `logs/memory/lint-/`. +- A **status + help** mode when called bare — the bare-status-board convention holds for `ahoy`, `capture`, `memory`, `intent`, and `spec` (and bare `abcd`); it is not universal (`version` prints only the version string, and the `docs`/`history`/`disembark` cobra parents print help/usage with no status board). -abcd ships **eleven binary-backed top-level commands**, each with both a Go verb and a `commands/abcd/` file: `/abcd:ahoy`, `/abcd:audit`, `/abcd:capture`, `/abcd:disembark`, `/abcd:docs`, `/abcd:embark`, `/abcd:history`, `/abcd:intent`, `/abcd:launch`, `/abcd:memory`, and `/abcd:version`. The `intent` parent carries the largest sub-verb tree: `link`, `plan`, `ready`, and `review` ship (alongside the deprecated `new` alias and the canonical bare quoted create `/abcd:intent ""`); `refine`, `grill`, `ship`, `consistency`, `shape`, and `reclassify` are design targets. Three further binary verbs — `changelog`, `rules`, and `spec` — expose a Go verb but no `commands/abcd/` file, so they are CLI-only. See [`04-surfaces/`](../04-surfaces) for per-command detail. +abcd ships **eleven binary-backed top-level commands**, each with both a Go verb and a `commands/abcd/` file: `/abcd:ahoy`, `/abcd:audit`, `/abcd:capture`, `/abcd:disembark`, `/abcd:docs`, `/abcd:embark`, `/abcd:history`, `/abcd:intent`, `/abcd:launch`, `/abcd:memory`, and `/abcd:version`. The `disembark` parent carries the largest sub-verb tree (`coverage`, `graveyard`, `oracle`, `pack`, `plan`, `press-release`, `principles`, `probe`); the `intent` parent's shipped sub-verbs are `link`, `plan`, `ready`, and `review` (alongside the deprecated `new` alias and the canonical bare quoted create `/abcd:intent ""`), with `refine`, `grill`, `ship`, `consistency`, `shape`, and `reclassify` as design targets. Three further binary verbs — `changelog`, `rules`, and `spec` — expose a Go verb but no `commands/abcd/` file, so they are CLI-only. See [`04-surfaces/`](../04-surfaces) for per-command detail. Alongside the eleven binary-backed verbs, abcd ships **three host-delegated commands** — `/abcd:consult`, `/abcd:ingest`, and `/abcd:prepare-this-repo` (chapters `13`–`15`). They are commands (state-mutating) but have **no Go verb**: the workflow runs in the host agent, so they have no bare-status render and no binary sub-verbs. This is the shape a command takes when its work is host-delegated rather than owned by the transport-agnostic core. diff --git a/.abcd/development/brief/05-internals/README.md b/.abcd/development/brief/05-internals/README.md index c361704..5eae317 100644 --- a/.abcd/development/brief/05-internals/README.md +++ b/.abcd/development/brief/05-internals/README.md @@ -11,7 +11,7 @@ This directory holds the *plumbing* design for abcd: the agents, adapters, confi | 5 | [`05-prompt-quality.md`](05-prompt-quality.md) | Prompt-quality infrastructure (B+C+D + itd-5 prompt-quality additions) | | 6 | [`06-lint.md`](06-lint.md) | Lint contract: the intent/prompt/terminology checkers in `internal/core/lint`, severity model, CI integration | | 7 | [`07-memory.md`](07-memory.md) | `.abcd/memory/` component — multi-upstream curated knowledge substrate per itd-36; page-class enum; curator role on `principle-distiller`; quotation/licence lints. Karpathy LLM Wiki pattern as prior art. | -| 8 | [`08-skills.md`](08-skills.md) | Skills-vs-commands boundary: codifies decision criteria for later skill additions. abcd ships zero skills — `/abcd:grill` was originally proposed as one but is now `/abcd:intent grill` (mid-session glossary writes are command-shaped). | +| 8 | [`08-skills.md`](08-skills.md) | Skills-vs-commands boundary: codifies decision criteria for later skill additions. abcd ships zero skills — `/abcd:grill` was originally proposed as one; its **design target** is promotion to `/abcd:intent grill` (mid-session glossary writes are command-shaped), not a shipped sub-verb. | | 9 | [`09-provenance-substrate.md`](09-provenance-substrate.md) | Provenance/licence subsystem (used by both itd-36 memory ingest and itd-26 loot OSS-vendor). Licence detection (SPDX), citation generation, source-hash registry, restrictive-licence publish gate (lifeboat consumer, future/inert at launch — adr-18). Separable spec; pulled forward alongside itd-36. | ## Policy: no skeleton enforcement (deferred) diff --git a/.abcd/development/brief/glossary/README.md b/.abcd/development/brief/glossary/README.md index 2bf9554..09e5086 100644 --- a/.abcd/development/brief/glossary/README.md +++ b/.abcd/development/brief/glossary/README.md @@ -126,7 +126,7 @@ Grill offers to draft an ADR only when all three Pocock clauses pass: hard-to-reverse + surprising-without-context + real-trade-off. If any clause fails, no offer is made. ADRs are written to `.abcd/development/decisions/adrs/` with atomic write. -See `skills/abcd-intent-grill/phase-1-glossary-mode.md` for the complete write-back protocol. +The complete write-back protocol is a **design target** of `/abcd:intent grill`'s glossary-aware mode; no shipped file documents it yet. --- From 0135d02f0f6c49934558f702bd4291d853efc88f Mon Sep 17 00:00:00 2001 From: REPPL <77722411+REPPL@users.noreply.github.com> Date: Wed, 22 Jul 2026 16:53:50 +0100 Subject: [PATCH 3/4] docs: disambiguate predecessor spec citations and finish the surface sweep (round 3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round 3 of the crosscheck surfaced 25 findings, dominated by a class independent panels kept ruling opposite ways: bare predecessor spc-N citations that now collide with this repo's native spc-2..spc-13 store. Every such citation carries an explicit predecessor-store qualifier per the brief README's provenance note, so it can no longer be read as naming a native spec. The genuinely false claims are rewritten to the shipped reality (no reclassify sub-verb, no embark resolution prompt, itd-73 derived versioning ships, embark's optional [target] argument), and the two panel flip-flop sites carry explicit design-target markers — the ambiguity itself was the defect. The disembark voyage open question narrows to what the shipped voyage line leaves open. Assisted-by: Claude:claude-opus-4-8 Assisted-by: Claude:claude-fable-5 --- .../brief/04-surfaces/02-disembark.md | 4 +- .../brief/04-surfaces/03-embark.md | 20 +++---- .../brief/04-surfaces/04-launch.md | 18 +++--- .../brief/04-surfaces/05-intent.md | 56 +++++++++---------- .../brief/04-surfaces/06-capture.md | 8 +-- .../brief/04-surfaces/07-memory.md | 7 ++- .../development/brief/04-surfaces/08-abcd.md | 24 ++++---- .../brief/04-surfaces/09-reflect.md | 17 +++--- .abcd/development/brief/04-surfaces/README.md | 4 +- 9 files changed, 82 insertions(+), 76 deletions(-) diff --git a/.abcd/development/brief/04-surfaces/02-disembark.md b/.abcd/development/brief/04-surfaces/02-disembark.md index af8cee7..21c82a8 100644 --- a/.abcd/development/brief/04-surfaces/02-disembark.md +++ b/.abcd/development/brief/04-surfaces/02-disembark.md @@ -95,9 +95,9 @@ Acceptance is checked across the validation corpus (`.abcd/corpus.json` — a de ## 7. Acceptance -> **Open question (adr-35):** bare `/abcd:disembark` previously reported "where the lifeboat lives", which was answerable only because the home was fixed and in-tree. With an operator-chosen ``, the destination path must come from somewhere, and adr-35 pins the `history.jsonl` line to `manifest_sha256`, file count, oracle backend, and verdict — it does **not** say the line records the destination. **Decide:** does a `disembark/history.jsonl` entry carry the destination path (and does bare invocation resolve `` from the cwd, as `probe` and `plan` already default to it)? The first bullet below assumes yes on both counts. +> **Open question (adr-35):** the shipped `history.jsonl` line records schema version, event, timestamp, `manifest_sha256`, source name, source root SHA, destination, file count, and bytes — so the destination a bare-invocation readout would need is already persisted. **Decide:** does bare invocation resolve `` from the cwd (as `probe` and `plan` already default to it) and read the voyage log? The design-target marker on the first bullet below is gated on that decision. -- **Given** any abcd-aware terminal, **when** the user runs bare `/abcd:disembark`, **then** the dispatcher reads the voyage log at `~/.abcd/voyage//disembark/history.jsonl` and shows when the source last disembarked and where that snapshot was written, the available sub-verbs (`pack `, `probe`, `plan`, `coverage`, `oracle`, `graveyard`, `press-release`, `principles`; later phase: `to-spec-kit`), and suggested next actions — bare invocation never mutates state. +- **Given** any abcd-aware terminal, **when** the user runs bare `/abcd:disembark`, **then** the dispatcher prints the available sub-verbs (`pack `, `probe`, `plan`, `coverage`, `oracle`, `graveyard`, `press-release`, `principles`; later phase: `to-spec-kit`) and flags only — bare invocation never mutates state. *(Design target, gated on the adr-35 open question above: once the `history.jsonl` line's destination field and the cwd `` resolution are decided, bare invocation also reads the voyage log at `~/.abcd/voyage//disembark/history.jsonl` and shows when the source last disembarked and where that snapshot was written, plus suggested next actions.)* - **Given** any source repo, **when** any sub-verb runs against it (`probe`, `plan`, or a full pack), **then** the source tree is byte-for-byte identical afterwards — a test hashes the tree before and after (adr-35: disembark is read-only). There is no `home`, and no path under `` is ever a destination. - **Given** a corpus repo with an intent corpus, ADRs, and a memory backend present, **when** `/abcd:disembark pack ` runs to completion, **then** `` contains all sections in [§ 5](#5-output-shape) and the oracle audit returns a registered verdict of `SHIP` with specific findings. - **Given** a corpus repo where one source is sparse (e.g., a repo with no intent corpus), **when** a full pack runs, **then** the run **succeeds**: the affected section is omitted from the brief rather than fabricated, and `coverage.{json,md}` records it with `status: blank`, what was searched, and the question a human must answer — a blank is a first-class result, not a failure or an exemption footnote (adr-35). diff --git a/.abcd/development/brief/04-surfaces/03-embark.md b/.abcd/development/brief/04-surfaces/03-embark.md index 49917fb..83f78d0 100644 --- a/.abcd/development/brief/04-surfaces/03-embark.md +++ b/.abcd/development/brief/04-surfaces/03-embark.md @@ -29,8 +29,8 @@ Bare `/abcd:embark` prints dispatcher help only — never mutates state. The two **shipped** sub-verbs: -- **`/abcd:embark from `** — unpack the lifeboat at `` into the current repo. Path is required, and it is always an explicit path to a destination a disembark wrote — **there is no `home` shorthand** (adr-35: there is no in-tree lifeboat home to expand it to). The round-trip / self-test case is `disembark pack ` followed by `embark from `. *(Design target, not yet shipped: the flag-shaped modifiers `--force` — override conflict refusal, `--archive` — copy input lifeboat verbatim to `~/.abcd/voyage//embark/from//` before unpacking, and `--refresh-audit` — re-run oracle product audit instead of trusting cached. The shipped `from` takes no flags.)* -- **`/abcd:embark probe `** — inspect a lifeboat at `` without unpacking: show what would land where, run schema/audit checks, write nothing. +- **`/abcd:embark from [target]`** — unpack the lifeboat at `` into `[target]`, which defaults to the working directory when omitted. `` is required, and it is always an explicit path to a destination a disembark wrote — **there is no `home` shorthand** (adr-35: there is no in-tree lifeboat home to expand it to). The round-trip / self-test case is `disembark pack ` followed by `embark from `. *(Design target, not yet shipped: the flag-shaped modifiers `--force` — override conflict refusal, `--archive` — copy input lifeboat verbatim to `~/.abcd/voyage//embark/from//` before unpacking, and `--refresh-audit` — re-run oracle product audit instead of trusting cached. The shipped `from` takes no flags.)* +- **`/abcd:embark probe [target]`** — inspect what the lifeboat at `` would write into `[target]` (defaulting to the working directory) without unpacking: show what would land where, run schema/audit checks, write nothing. Design-target sub-verb (not yet shipped): @@ -70,7 +70,7 @@ Embark is a deterministic Go run: it reads the lifeboat, plans, refuses on any c ## 4. Conflict UX -When the target already has files that the lifeboat would write, **core returns the conflict set and the surface renders it as a single bulk prompt** — never a per-file barrage, and never a file written by core (adr-35): +When the target already has files that the lifeboat would write, **core returns the conflict set and the surface renders it as a single bulk report** — never a per-file barrage, and never a file written by core (adr-35). The shipped `from` **writes nothing** on any conflict and exits non-zero; the surface relays the bulk report so the user resolves the conflicts and re-runs: ``` embark detected N conflicts across the record families: @@ -78,14 +78,12 @@ embark detected N conflicts across the record families: • 2 .abcd/work/issues/ • 1 .abcd/development/decisions/adrs/ -How to resolve all conflicts? - → keep target (skip everything in lifeboat that conflicts) - → replace target (lifeboat wins everywhere) - → merge where possible, prompt otherwise (a per-file prompt for each conflict) - → abort (surface prints the conflict list; nothing is written) +nothing was written — resolve the conflicts and re-run. ``` -Single decision, transparent (shows scope before asking). The conflict list is a value core hands back; if the operator wants it on disk, the surface writes it — core does not. +The conflict list is a value core hands back; if the operator wants it on disk, the surface writes it — core does not. + +*(Design target, not yet shipped: `embark from --force` turns that bulk report into a single resolution prompt — keep target (skip everything in lifeboat that conflicts) / replace target (lifeboat wins everywhere) / merge where possible, prompt otherwise / abort (surface prints the conflict list; nothing is written) — and applies the chosen resolution uniformly. Single decision, transparent (shows scope before asking). The shipped `from` takes no flags and offers no resolution choice.)* ## 6. Acceptance @@ -96,13 +94,13 @@ Single decision, transparent (shows scope before asking). The conflict list is a > criteria (`from`, `probe`, bare invocation, the conflict refusal) hold today. - **Given** any abcd-aware terminal, **when** the user runs bare `/abcd:embark`, **then** the dispatcher prints help listing the shipped sub-verbs (`from`, `probe`) and the global `--json` flag — bare invocation never mutates state. -- **Given** a lifeboat at `` and a conflict-free target, **when** `/abcd:embark from ` runs, **then** the four record families (ADRs, issues, intents, specs) land at their canonical locations, the current abcd marker block is re-injected into the target `CLAUDE.md`, and everything else in the lifeboat informs the report but is never written. +- **Given** a lifeboat at `` and a conflict-free target, **when** `/abcd:embark from [target]` runs (the target defaulting to the working directory when omitted), **then** the four record families (ADRs, issues, intents, specs) land at their canonical locations under the target, the current abcd marker block is re-injected into the target `CLAUDE.md`, and everything else in the lifeboat informs the report but is never written. - **Given** a repo disembarked to ``, **when** `embark from ` runs in an empty target, **then** the round-trip completes with no shorthand and no special case — `` is an ordinary explicit path. (There is no `home`, and no sub-verb resolves one; matches disembark's parallel rule for sub-verb-agnostic resolution.) - **Given** a target holding a file that conflicts with a planned write, **when** `/abcd:embark from ` runs, **then** the command refuses, core returns the conflict list **without writing any file**, and the surface renders it as one bulk report — a target that merely holds unrelated files is not a conflict. - **Given** a non-empty target with conflicts and `--force`, **when** `embark from --force` runs, **then** the bulk conflict prompt fires once with a summary of all conflicts, and the chosen resolution is applied uniformly. - **Given** the user runs `/abcd:embark scan`, **when** the command completes, **then** the directories found carrying a parseable `_provenance.json` are listed ranked by mtime with their detected source repo, no unpacking occurs, and the user is shown candidates ready to pass to `embark from `. *(What `scan` searches is settled; **where** it searches is the open question above, and this criterion cannot be made checkable until that is decided.)* - **Given** the user runs `/abcd:embark scan --deep`, **when** the command completes, **then** the search widens — the exact widening depends on the same open question. -- **Given** the user runs `/abcd:embark probe `, **when** the command completes, **then** the lifeboat at `` is inspected (file tree, schema validation, would-be writes), no target mutation occurs, and the user sees a report ready to inform the decision to run `embark from `. +- **Given** the user runs `/abcd:embark probe [target]` (the target defaulting to the working directory when omitted), **when** the command completes, **then** the lifeboat at `` is inspected against the target (file tree, schema validation, would-be writes), no target mutation occurs, and the user sees a report ready to inform the decision to run `embark from [target]`. - *(Design target, not yet shipped.)* **Given** a lifeboat containing `.abcd/rp/workspace.json` and RP installed on the embarker, **when** `embark from ` runs, **then** the user is asked whether to register the workspace with RP and the choice is applied. - *(Design target, not yet shipped.)* **Given** a lifeboat containing `.abcd/rp/workspace.json` and RP *not* installed, **when** `embark from ` runs, **then** the command warns gracefully and continues without failing. - **Given** the user passes `--refresh-audit`, **when** `embark from --refresh-audit` runs, **then** the oracle product audit re-runs against the current lifeboat content and the drift vs the disembark-time audit is reported. diff --git a/.abcd/development/brief/04-surfaces/04-launch.md b/.abcd/development/brief/04-surfaces/04-launch.md index d612962..fced01f 100644 --- a/.abcd/development/brief/04-surfaces/04-launch.md +++ b/.abcd/development/brief/04-surfaces/04-launch.md @@ -1,17 +1,17 @@ # `/abcd:launch` — Curated Release -> **Phase ownership** ([adr-33](../../decisions/adrs/0033-launch-phase-ownership-tiered.md)): the curated-release cut — packaging with `.abcd/**` excluded plus the secret/PII scan — ships in [Phase 1](../../roadmap/phases/phase-1-ahoy.md). The full pre-flight gate suite and release automation below are separately scheduled intents (itd-65 gate suite, itd-66 render parity, itd-70 retention, itd-72 tier-b publishing, itd-73 derived versioning). +> **Phase ownership** ([adr-33](../../decisions/adrs/0033-launch-phase-ownership-tiered.md)): the curated-release cut — packaging with `.abcd/**` excluded plus the secret/PII scan — ships in [Phase 1](../../roadmap/phases/phase-1-ahoy.md). The full pre-flight gate suite and remaining release automation below are separately scheduled intents (itd-65 gate suite, itd-66 render parity, itd-70 retention, itd-72 tier-b publishing); itd-73 derived versioning ships with the release cut (see [Sub-verbs](#sub-verbs)). ## Sub-verbs The shipped verb surface is the `--dry-run` flag on `abcd launch` — a read-only preview of the bundle and gates. Bare `abcd launch` never mutates state: it refuses (exit 1) with a hint to pass `--dry-run`; a bare-as-status render is a design target, unshipped. The sub-verb design: - **`/abcd:launch ship`** — **partly shipped: the RELEASE CUT only** (itd-73 derived versioning + itd-67's changelog slice). `abcd launch ship` derives the version and the record set from what shipped since the newest tag, runs the surface guardrail, and — with `--changelog-json` — validates the host-composed prose against the record set (the completeness bijection) and writes the dated `CHANGELOG.md` heading `.github/workflows/auto-release.yml` turns into a tag; `commands/abcd/launch.md` carries the emit → compose → ingest orchestration and the `release-changelog-composer` agent it dispatches. The `Ship` engine is wired: `abcd launch ship` is a live subcommand, and `--payload-dir ` stages the versioned release payload — the derived version stamped into the payload's `plugin.json`/`marketplace.json` and lockstep-proved before return. Commit, tag, and publish stay a design target (itd-65 gate suite + itd-72 publishing): the verb neither commits, tags, nor publishes. The full-cut design: cut a curated release artefact from the one repo: run pre-flight gates, filter the artefact (default-deny, `.abcd/**` excluded by packaging), stamp the version, and on a `v*` tag publish a GitHub Release ([adr-28](../../decisions/adrs/0028-single-repo-curated-release.md)). The flow described in §§ 1–6 below is this sub-verb's behaviour. Flag-shaped modifiers `--allow-dirty` and `--allow-doc-warnings` belong to this sub-verb's design; the shipped `ship` accepts `--changelog-json` and `--payload-dir` (plus the global `--json`), and bare `abcd launch` accepts only `--dry-run` and the global `--json`. There is no version flag — the version is derived, never authored ([adr-31](../../decisions/adrs/0031-derived-versioning-from-intents.md), see [§ 3](#3-versioning--marketplace)). -- **`/abcd:launch dry-run`** — shipped as the `--dry-run` flag (the plugin command `commands/abcd/launch.md` maps the `dry-run` of its `[dry-run | ship]` argument hint onto `abcd launch --dry-run`; the binary has no `dry-run` subcommand). **Report-only preview, always exit-0** (a preview never blocks). It runs the parts of the pre-flight suite that exist today: as of spc-64 the **secret + PII scan gate** (the native scanners, see [§ 1](#1-pre-flight-gates)) runs for real in report-only mode and prints what it *would* refuse on (a finding, or a fail-closed reason such as "scanner unavailable"); the **installability smoke** runs for real at its light tier (see [§ 1](#1-pre-flight-gates)); the **manifest lockstep check** runs for real at its `dev` polarity over the working tree — the polarity adr-19 requires the committed manifests to satisfy (no version key), see [§ 3](#3-versioning--marketplace) — reports its result and folds any drift or an unreadable version-location contract into what it *would* refuse on; the remaining gates (marker-block, documentation-auditor) are the gate-suite intent's (itd-65) and render as "(not yet implemented)". It also produces the would-be artefact manifest, without writing the release artefact. dry-run is **not** "ship minus publish": running the *full* gate suite and **hard-failing** on a finding (exit non-zero) is the full `ship` verb's behaviour (itd-65 + itd-72), not dry-run's. +- **`/abcd:launch dry-run`** — shipped as the `--dry-run` flag (the plugin command `commands/abcd/launch.md` maps the `dry-run` of its `[dry-run | ship]` argument hint onto `abcd launch --dry-run`; the binary has no `dry-run` subcommand). **Report-only preview, always exit-0** (a preview never blocks). It runs the parts of the pre-flight suite that exist today: as of spc-64 (predecessor store) the **secret + PII scan gate** (the native scanners, see [§ 1](#1-pre-flight-gates)) runs for real in report-only mode and prints what it *would* refuse on (a finding, or a fail-closed reason such as "scanner unavailable"); the **installability smoke** runs for real at its light tier (see [§ 1](#1-pre-flight-gates)); the **manifest lockstep check** runs for real at its `dev` polarity over the working tree — the polarity adr-19 requires the committed manifests to satisfy (no version key), see [§ 3](#3-versioning--marketplace) — reports its result and folds any drift or an unreadable version-location contract into what it *would* refuse on; the remaining gates (marker-block, documentation-auditor) are the gate-suite intent's (itd-65) and render as "(not yet implemented)". It also produces the would-be artefact manifest, without writing the release artefact. dry-run is **not** "ship minus publish": running the *full* gate suite and **hard-failing** on a finding (exit non-zero) is the full `ship` verb's behaviour (itd-65 + itd-72), not dry-run's. ## 1. Pre-flight gates -- **Secret scan** — a **native Go scanner** is the default, hard-fail (absent/fail-closed, never a silent skip). **gitleaks** is an opt-in deeper scanner (the spc-64 ship gate pins `gitleaks >= 8.18.0` when wired; absent/older = fail-closed, never a regex fallback). +- **Secret scan** — a **native Go scanner** is the default, hard-fail (absent/fail-closed, never a silent skip). **gitleaks** is an opt-in deeper scanner (the spc-64 (predecessor store) ship gate pins `gitleaks >= 8.18.0` when wired; absent/older = fail-closed, never a regex fallback). - **PII** scan (real names, emails) via the **native Go PII engine** (`scan_text` + the merged Config + the non-overridable secret/identity severity floor) — hard-fail. - **Custom regex** layer — home dirs (`~/...`) and local usernames — hard-fail; GitHub usernames from git config — warn (legitimate in repo-URL contexts). The per-kind severity floor is config-raisable, never lowerable (`internal/adapter/scanner/identity.go`). - **TruffleHog** — opt-in deep scan when `scan.deep=true` — hard-fail (live credential verification) @@ -25,13 +25,13 @@ The shipped verb surface is the `--dry-run` flag on `abcd launch` — a read-onl - **OWASP / vulnerability check** (folded into the pre-flight suite) — warn-fail - **Documentation auditor** (subagent) — runs over `docs/` to verify user-facing documentation is well-formed before release — warn-fail -Pre-flight report written to `.abcd/logbook/launch//preflight.{json,md}` — **full-`ship` behaviour (itd-65)**. The spc-64 secret/PII gate is itself side-effect-free w.r.t. the repo (its only writes are to a private temp tree it removes), and `dry-run` renders the gate result inline rather than writing a report file. +Pre-flight report written to `.abcd/logbook/launch//preflight.{json,md}` — **full-`ship` behaviour (itd-65)**. The spc-64 (predecessor store) secret/PII gate is itself side-effect-free w.r.t. the repo (its only writes are to a private temp tree it removes), and `dry-run` renders the gate result inline rather than writing a report file. ## 2. Curated release artefact (default-deny) - **Include:** the shipped include list, pinned in `.abcd/config/launch-payload.json`: `.claude-plugin/` (holds both `plugin.json` and the ONE canonical `marketplace.json` — there is no root-level `marketplace.json`), `commands/`, `agents/` (the reviewer/synthesis prompt catalog), `hooks/` (`hooks.json` is load-bearing), `scripts/`, `docs/` (user-facing only), `README.md`, `LICENSE`, `.gitignore`. `skills/` is absent from the include list and does not exist in the tree at all. -- **Exclude:** `.abcd/` (entire namespace — `development/` (brief, decisions, intents, plans, principles, research, roadmap), plus the design-target tiers `memory/` and `logbook/` once they exist), and patterns from `.gitignore`. Per [adr-28](../../decisions/adrs/0028-single-repo-curated-release.md) the wholesale `.abcd/` exclusion (incl. `.abcd/memory/**`) is a **packaging filter over the one tree**, not a copy between two repos: the release artefact carries plugin code, never the project's design record or knowledge store. The lifeboat is **not** among the excluded tiers because it is not an in-tree tier at all: `/abcd:disembark` is read-only over the source repo and lands the lifeboat at an operator-chosen destination outside it, with its voyage log at the operator level under `~/.abcd/voyage/` ([adr-35](../../decisions/adrs/0035-lifeboat-as-coverage-experiment.md)) — so there is nothing for the payload filter to exclude. The spc-38 restrictive-licence gate is NOT this artefact's gate — its real consumer is the lifeboat (`/abcd:disembark`), the surface that publishes curated project memory/provenance ([adr-35](../../decisions/adrs/0035-lifeboat-as-coverage-experiment.md)). At launch the gate is future/inert; the shipped `dry-run` renders no licence verdicts (a diagnostic preview of the gate's verdicts in `dry-run` is part of the gate's own design, unshipped). -- **Override:** the include list in `.abcd/config/launch-payload.json` is the packaging override (the only mechanism that can put a path *into* the release artefact). The deny is **structural** (`internal/core/launch/bundle.go`, per adr-28): no include entry can promote a denied namespace — a `.abcd/**` line is never promoted — so nothing can re-include `.abcd/memory/**`. The spc-38 gate's own evaluation-input allowlist belongs to that gate's design (future/inert, see above) and is documented-distinct from packaging: it re-includes files into the gate's *own evaluation input*, never into the release artefact — two mechanisms, never one name. +- **Exclude:** `.abcd/` (entire namespace — `development/` (brief, decisions, intents, plans, principles, research, roadmap), plus the design-target tiers `memory/` and `logbook/` once they exist), and patterns from `.gitignore`. Per [adr-28](../../decisions/adrs/0028-single-repo-curated-release.md) the wholesale `.abcd/` exclusion (incl. `.abcd/memory/**`) is a **packaging filter over the one tree**, not a copy between two repos: the release artefact carries plugin code, never the project's design record or knowledge store. The lifeboat is **not** among the excluded tiers because it is not an in-tree tier at all: `/abcd:disembark` is read-only over the source repo and lands the lifeboat at an operator-chosen destination outside it, with its voyage log at the operator level under `~/.abcd/voyage/` ([adr-35](../../decisions/adrs/0035-lifeboat-as-coverage-experiment.md)) — so there is nothing for the payload filter to exclude. The spc-38 (predecessor store) restrictive-licence gate is NOT this artefact's gate — its real consumer is the lifeboat (`/abcd:disembark`), the surface that publishes curated project memory/provenance ([adr-35](../../decisions/adrs/0035-lifeboat-as-coverage-experiment.md)). At launch the gate is future/inert; the shipped `dry-run` renders no licence verdicts (a diagnostic preview of the gate's verdicts in `dry-run` is part of the gate's own design, unshipped). +- **Override:** the include list in `.abcd/config/launch-payload.json` is the packaging override (the only mechanism that can put a path *into* the release artefact). The deny is **structural** (`internal/core/launch/bundle.go`, per adr-28): no include entry can promote a denied namespace — a `.abcd/**` line is never promoted — so nothing can re-include `.abcd/memory/**`. The spc-38 (predecessor store) gate's own evaluation-input allowlist belongs to that gate's design (future/inert, see above) and is documented-distinct from packaging: it re-includes files into the gate's *own evaluation input*, never into the release artefact — two mechanisms, never one name. ## 3. Versioning + marketplace @@ -78,11 +78,11 @@ a mislabelled impact cannot ship a compatibility lie. `launch ship` is responsible for writing the version into **the selected version location**, never a hard-coded `plugin.json`. That location is read from -the spc-77.1 decision artifact +the spc-77.1 (predecessor store) decision artifact (`.abcd/config/version-location.json`) as `manifest_path` + `json_pointer` (see [adr-19](../../decisions/adrs/0019-plugin-json-version-carve-out.md)). The -artifact records spc-77.1's ACCEPT outcome — `.claude-plugin/plugin.json` at +artifact records the predecessor's spc-77.1 ACCEPT outcome — `.claude-plugin/plugin.json` at `/version` — and the shipped lockstep checker fails closed when it is missing or malformed; a `blocked: true` decision has no schema-valid location, so version-writing refuses and the escalation stands. Concretely, `ship`: @@ -160,7 +160,7 @@ The first release cut of abcd itself is a manual `v*` tag + GitHub Release. Docu ## 6. Acceptance - **Given** any abcd-aware terminal, **when** the user runs bare `/abcd:launch`, **then** the dispatcher shows current launch readiness (pre-flight gate state, last launch attempt timestamp), the available sub-verbs (`ship`, `dry-run`), and suggested next actions — bare invocation never mutates state. **Design target:** the shipped bare `abcd launch` refuses (exit 1) with a hint to pass `--dry-run`, and the shipped plugin command runs the dry-run preview directly rather than a status+help render. -- **Given** a clean tree with a deliberate PII fixture (e.g., a real email in a comment) inside the resolved artefact, **when** `/abcd:launch dry-run` runs, **then** the report-only gate (spc-64) PRINTS that it *would* refuse on that finding (the offending file/line in the gate result), still **exits 0**, and writes no artefact. (The **hard-fail** on that finding — exit non-zero plus a `preflight.{json,md}` report under `.abcd/logbook/launch//` — is the full `ship` verb's behaviour (itd-65), not dry-run's.) +- **Given** a clean tree with a deliberate PII fixture (e.g., a real email in a comment) inside the resolved artefact, **when** `/abcd:launch dry-run` runs, **then** the report-only gate (spc-64, predecessor store) PRINTS that it *would* refuse on that finding (the offending file/line in the gate result), still **exits 0**, and writes no artefact. (The **hard-fail** on that finding — exit non-zero plus a `preflight.{json,md}` report under `.abcd/logbook/launch//` — is the full `ship` verb's behaviour (itd-65), not dry-run's.) - **Given** a clean tree, **when** `/abcd:launch dry-run` runs, **then** the report lists exactly the include/exclude artefact manifest in [§ 2](#2-curated-release-artefact-default-deny) with no surprises and no artefact is written. - **Given** only `impact: fix` intents (or no intent-tied change) shipped since the last release, **when** `launch ship` runs, **then** the bump tier is **patch** (`v0.0.x`) and the next patch version is written into the **selected version location** (from `.abcd/config/version-location.json`, per [§ 3](#3-versioning--marketplace)) in the **release artefact** only — the working-tree manifests stay unversioned (adr-19, adr-28) — plus the canonical `.claude-plugin/marketplace.json`, never a hard-coded `plugin.json`. - **Given** at least one `impact: additive` intent and no `breaking` intent shipped since the last release, **when** `launch ship` runs, **then** the bump tier is **minor** (`v0.x.0`) and the launch report names the intents that drove it. diff --git a/.abcd/development/brief/04-surfaces/05-intent.md b/.abcd/development/brief/04-surfaces/05-intent.md index b01ad9b..9d1b8ef 100644 --- a/.abcd/development/brief/04-surfaces/05-intent.md +++ b/.abcd/development/brief/04-surfaces/05-intent.md @@ -34,7 +34,7 @@ Every intent has a `kind` declared in frontmatter, set at `/abcd:intent plan` ti | `bundle-member` | Yes | Same as standalone, with `bundle: ` linking members | Shared spec with bundle-mates (N:1) | (no live bundles in any phase — see [`intents/README.md`](../../intents/README.md#bundles) for retired bundle history) | | `discipline` | **No** — uses `## Rule` instead | `disciplines/` | No spec; imposes acceptance gates on every other spec | itd-1 (AC gate), itd-5 (prompt-quality) | -**`kind` is binding once set at plan time.** Late changes go through `/abcd:intent reclassify --kind `, which records the change in the intent's frontmatter `reclassification_history` and surfaces it for reviewer review. +**`kind` is binding once set at plan time.** Late changes go through `/abcd:intent reclassify --kind ` — a later phase (no `reclassify` sub-verb ships; the binary exposes `link`, `new`, `plan`, `ready`, `review`) — which records the change in the intent's frontmatter `reclassification_history` and surfaces it for reviewer review. **Two distinct history fields, two distinct concerns:** `reclassification_history` records *kind* transitions (standalone ↔ bundle-member ↔ discipline ↔ superseded). `surface_history` records *surface-shape* transitions where the kind is unchanged but the user-facing surface form changes (e.g., skill → sub-verb, top-level command → sub-verb of another command, command → flag). Both are append-only; both have the same `{ date, from, to, reason }` shape. Worked example: itd-27 was always `kind: standalone`, but its surface shifted from a top-level skill (`/abcd:grill`) to a sub-verb of `/abcd:intent` on 2026-05-07 — that's a `surface_history` entry, not a `reclassification_history` entry, because the kind is unchanged. The two fields together preserve a complete audit trail of an intent's evolution without overloading either. @@ -119,7 +119,7 @@ Until then, `kind_notes` is the free-text descriptor. └─ Writes intents/drafts/itd-N-.md (no spec created yet) 2. /abcd:intent plan (when ready to commit to work; single intent ID) - ├─ Runs internal/core/lint: refuses to promote if `## Acceptance Criteria` is missing/malformed + ├─ Refuses to promote if `## Acceptance Criteria` is missing/malformed (the intent package's own hasAcceptanceCriteria check, not internal/core/lint) ├─ Mints (or reuses) the intent's native spec; kind defaults to standalone ├─ Injects bidirectional link (spec.intent: itd-N; intent.spec_id: spc-N) └─ Moves intents/drafts/itd-N-*.md → intents/planned/itd-N-*.md @@ -133,7 +133,7 @@ Until then, `kind_notes` is the free-text descriptor. ship today; plan schedules a single standalone intent. 3. Spec marked done in the native spec store (standalone + bundle: work complete) - ├─ native spec-store `spec close` close-hook (spc-36) → intent lifecycle reconcile (spc-28) + ├─ native spec-store `spec close` close-hook (spc-36, predecessor store) → intent lifecycle reconcile (spc-28, predecessor store) └─ Moves intents/planned/itd-N-*.md → intents/shipped/itd-N-*.md (+ enqueues a review) (For bundles, all member intents move together when the shared spec closes.) @@ -145,13 +145,13 @@ Until then, `kind_notes` is the free-text descriptor. with the review request staged under .abcd/.work.local/reviews/. For bundles, review runs per-intent (each member's acceptance criteria checked separately against the same delivered reality). - (spc-12 ships only this MANUAL review surface. spc-28 ships the on-close hook + (spc-12 (predecessor store) ships only this MANUAL review surface. spc-28 (predecessor store) ships the on-close hook that moves the intent planned → shipped and QUEUES a review on that transition. Auto-running the reviewer off that queue is still deferred (no epic currently owns - it; spc-6 disowned auto-firing). Until then, the `## Audit Notes` of a freshly + it; spc-6 (predecessor store) disowned auto-firing). Until then, the `## Audit Notes` of a freshly shipped intent stays empty until /abcd:intent review is run by hand.) -4. /abcd:intent reclassify --kind (late reclassification) +4. /abcd:intent reclassify --kind (late reclassification — a later phase; no reclassify sub-verb ships yet) ├─ Records the change in intent.reclassification_history (date + from-kind + to-kind + reason) ├─ Moves the file between directories (e.g., drafts/ → disciplines/) as needed ├─ For supersession: --kind superseded --by moves the file to superseded/, @@ -162,7 +162,7 @@ Until then, `kind_notes` is the free-text descriptor. └─ Triggers intent-fidelity-reviewer (shape-classification role) to verify the new kind fits Later phase — intent-fidelity-reviewer (shape-classification role) scans the corpus - when the user runs /abcd:intent shape (spc-29; an on-demand surface, not + when the user runs /abcd:intent shape (spc-29, predecessor store; an on-demand surface, not yet a binary sub-verb). The user accepts a suggestion via /abcd:intent reclassify; declined suggestions become entries in the intent's Audit Notes for future review. (Deferred follow-up: scheduled / @@ -175,19 +175,19 @@ Later phase — intent-fidelity-reviewer (shape-classification role) scans the c | Subcommand | Purpose | File movement | |---|---|---| | `/abcd:intent` (no args) | Read-only status: bucket counts (drafts / planned / shipped / disciplines / superseded), open/closed spec counts, the itd↔spc links, and a ledger-routing hint (`abcd capture "…"` for an observation, `abcd intent "…"` for a user-facing change) | — | -| `/abcd:intent ""` | **Canonical create** (spc-30/itd-46): a leading quoted seed is the canonical create entry. Seeds a draft skeleton from the text — placeholder Press Release and Acceptance Criteria sections for the human to expand — and assigns `itd-N`; writes `suggested_kind: null`. A leading quote always creates — never falls through to bare render | writes to `drafts/itd-N-.md` (no spec created) | +| `/abcd:intent ""` | **Canonical create** (spc-30 (predecessor store)/itd-46): a leading quoted seed is the canonical create entry. Seeds a draft skeleton from the text — placeholder Press Release and Acceptance Criteria sections for the human to expand — and assigns `itd-N`; writes `suggested_kind: null`. A leading quote always creates — never falls through to bare render | writes to `drafts/itd-N-.md` (no spec created) | | `/abcd:intent new ` | Deprecated alias for the quoted-text create (`abcd intent ""`); files a draft from the text | writes to `drafts/itd-N-.md` (no spec created) | -| `/abcd:intent grill ` | Socratic adversarial interview that stress-tests an intent for vagueness, missing acceptance, hidden assumptions before planning. Glossary-aware once `terminology/` exists. `--brief-section ` flag for stress-testing a brief section instead. (per itd-27) | (stays in current state) | +| `/abcd:intent grill ` | Socratic adversarial interview that stress-tests an intent for vagueness, missing acceptance, hidden assumptions before planning. Glossary-aware once `terminology/` exists. `--brief-section ` flag for stress-testing a brief section instead. (per itd-27, `intents/planned/` — a later phase; no `grill` sub-verb ships yet) | (stays in current state) | | `/abcd:intent plan ` | Plans a draft: mints its native spec, injects the bidirectional link (intent `spec_id` ↔ spec `intent`), and moves the file `drafts/` → `planned/`. Single intent ID. | `drafts/` → `planned/` | | `/abcd:intent ready ` | **Implement-readiness gate** (read-only): reports whether an intent is ready to implement — planned, with acceptance criteria and a written spec body. Exit 0 ready / 1 not ready / 2 fault. | (no move) | -| `/abcd:intent review ` | **Role 1 — single-document fidelity.** Compares the intent's press release + acceptance criteria against delivered reality (code, configs, docs, tests). Per-criterion verdicts (`MET` / `MET_WITH_CONCERNS` / `NOT_MET` / `INCONCLUSIVE`) appended to the intent's `## Audit Notes`. Aligns with the spec store's `plan-review` / `impl-review` / `completion-review` vocabulary — same operation shape (adversarial second opinion), different opponent (press release vs engineering spec). spc-12 ships this **manual** verb; spc-28 ships the on-close hook (move `planned → shipped` + queue a review), but auto-running the reviewer off that queue is still deferred (no spec currently owns it; spc-6 disowned auto-firing). | (stays) | +| `/abcd:intent review ` | **Role 1 — single-document fidelity.** Compares the intent's press release + acceptance criteria against delivered reality (code, configs, docs, tests). Per-criterion verdicts (`MET` / `MET_WITH_CONCERNS` / `NOT_MET` / `INCONCLUSIVE`) appended to the intent's `## Audit Notes`. Aligns with the spec store's `plan-review` / `impl-review` / `completion-review` vocabulary — same operation shape (adversarial second opinion), different opponent (press release vs engineering spec). spc-12 (predecessor store) ships this **manual** verb; spc-28 (predecessor store) ships the on-close hook (move `planned → shipped` + queue a review), but auto-running the reviewer off that queue is still deferred (no spec currently owns it; spc-6 (predecessor store) disowned auto-firing). | (stays) | | `/abcd:intent review ingest --verdict-json ` | Ingests a host-delegated intent-fidelity verdict JSON, validated fail-closed against the schema and the parked review request, and writes its per-criterion verdict into the shipped intent's `## Audit Notes` (or quarantines a bad payload). | (no move; updates `## Audit Notes`) | -| `/abcd:intent consistency []` | **Role 2 — cross-document fidelity.** Surfaces five judgement categories (terminology drift, premise contradictions, scope leakage, sequencing impossibilities, naming conflicts) across briefs + intents. **Bare** scans the whole corpus; **with ``** narrows to one intent's relationship with the rest. Findings land in `.abcd/.work.local/logs/audit/consistency-/report.{json,md}`. The judgement half + on-demand verb are spc-29's (a later phase); mechanical-half categories and pre-commit hook are deferred follow-ups. | (stays) | -| `/abcd:intent shape []` | **Role 3 — kind classification.** Examines whether an intent's declared `kind` (the noun) still fits the corpus. Surfaces *suggested* reclassifications across three live types: `kind_change`, `bundle`, `supersession`. **Bare** scans the corpus; **with ``** checks one intent. Pairs with `reclassify` (action verb that commits a `shape` finding). On-demand only per spc-29 (a later phase); findings land in `.abcd/.work.local/logs/audit/shape-/report.{json,md}`. Concurrency via `flock(2)` on `.abcd/coordination/shape.lock` (see § 7). Scheduled / continuous invocation is a deferred follow-up. | (stays) | -| `/abcd:intent reclassify --kind [--reason ]` | Late reclassification (e.g., a standalone intent realised to be a bundle-member; a draft realised to be a discipline; a shipped intent superseded by a later one). Records `reclassification_history` entry; moves the file between directories as the new kind dictates. `--kind superseded --by ` is the supersession path: the file moves to `superseded/`, frontmatter records `superseded_by: itd-M` AND `kind_at_supersession: ` so future readers know what shape the intent had when retired. | varies by destination kind | +| `/abcd:intent consistency []` | **Role 2 — cross-document fidelity.** Surfaces five judgement categories (terminology drift, premise contradictions, scope leakage, sequencing impossibilities, naming conflicts) across briefs + intents. **Bare** scans the whole corpus; **with ``** narrows to one intent's relationship with the rest. Findings land in `.abcd/.work.local/logs/audit/consistency-/report.{json,md}`. The judgement half + on-demand verb are the predecessor's spc-29 (a later phase); mechanical-half categories and pre-commit hook are deferred follow-ups. | (stays) | +| `/abcd:intent shape []` | **Role 3 — kind classification.** Examines whether an intent's declared `kind` (the noun) still fits the corpus. Surfaces *suggested* reclassifications across three live types: `kind_change`, `bundle`, `supersession`. **Bare** scans the corpus; **with ``** checks one intent. Pairs with `reclassify` (action verb that commits a `shape` finding). On-demand only per spc-29 (predecessor store; a later phase); findings land in `.abcd/.work.local/logs/audit/shape-/report.{json,md}`. Concurrency via `flock(2)` on `.abcd/coordination/shape.lock` (see § 7). Scheduled / continuous invocation is a deferred follow-up. | (stays) | +| `/abcd:intent reclassify --kind [--reason ]` | **A later phase — no `reclassify` sub-verb ships yet.** Late reclassification (e.g., a standalone intent realised to be a bundle-member; a draft realised to be a discipline; a shipped intent superseded by a later one). Records `reclassification_history` entry; moves the file between directories as the new kind dictates. `--kind superseded --by ` is the supersession path: the file moves to `superseded/`, frontmatter records `superseded_by: itd-M` AND `kind_at_supersession: ` so future readers know what shape the intent had when retired. | varies by destination kind | | `/abcd:intent link ` | Manual bidirectional link — used if the auto-link missed (rare) or for retroactive linking of pre-existing specs | (no move; updates frontmatter) | -**No aggregator verb.** A `check` subverb that runs `review` + `consistency` + `shape` together is *not* provided — the three primitives have very different runtime costs (review is code+oracle expensive; consistency is corpus-wide expensive; shape is cheap on demand). Bundling them produces a slow verb users avoid. Release-readiness is `/abcd:launch`'s pre-flight job. (Note: a scheduled / pre-commit shape leg is a **deferred follow-up**; spc-29's shape surface is on demand only.) +**No aggregator verb.** A `check` subverb that runs `review` + `consistency` + `shape` together is *not* provided — the three primitives have very different runtime costs (review is code+oracle expensive; consistency is corpus-wide expensive; shape is cheap on demand). Bundling them produces a slow verb users avoid. Release-readiness is `/abcd:launch`'s pre-flight job. (Note: a scheduled / pre-commit shape leg is a **deferred follow-up**; the predecessor's spc-29 shape surface is on demand only.) **Bare-command-as-help is a universal abcd convention** — every command in the surface set (the enumeration lives in the [surfaces README](README.md)) shows status + suggested next actions when invoked without args. Provides discoverability without forcing the user to remember subcommand names. @@ -239,9 +239,9 @@ Discipline-kind intents use a different template — see § 1 "Discipline format See [`01-product/05-personas.md`](../01-product/05-personas.md) for the canonical persona registry (SSOT). A press release attributes its customer quote to a persona from that roster, and the `persona_registry` lint rule checks that every quote attribution resolves to it; the codified abcd principle (no real names, no "hypothetical user") lives in the canonical persona file. -## 5. Frontmatter fields (spc-3 additions) +## 5. Frontmatter fields (spc-3 (predecessor store) additions — a later phase) -spc-3 adds the following optional frontmatter fields to intent files. All are additive — pre-existing intents without them remain valid (schema: `intent.schema.json`, part of the plan-time intent-lint design per [`05-internals/06-lint.md`](../05-internals/06-lint.md) — a later phase). +spc-3 (predecessor store) adds the following optional frontmatter fields to intent files. All are additive — pre-existing intents without them remain valid (schema: `intent.schema.json`, part of the plan-time intent-lint design per [`05-internals/06-lint.md`](../05-internals/06-lint.md) — a later phase). **Not shipped:** the `grill` sub-verb that writes these fields and the PRD at `.abcd/intents//prd.md` are a design target (backing intent itd-27, `intents/planned/`); the shipped quoted-text create seeds none of them (see § 1's seed skeleton). | Field | Type | When set | Purpose | |-------|------|----------|---------| @@ -252,7 +252,7 @@ spc-3 adds the following optional frontmatter fields to intent files. All are ad | `grill_session_id` | UUID | set by grill sub-verb | UUIDv4 of the Phase 1 grill session that produced the latest grill report | | `grilled_intent_hash` | SHA-256 | set by grill sub-verb | Hash of the intent at grill time (intent_source_hash recipe). Copied to PRD as `source_intent_hash`. Used by `/abcd:intent plan` to detect intent-edited-after-grill | | `prd_path` | string or null | set by grill sub-verb Phase 2 | Relative path to the PRD at `.abcd/intents//prd.md`. Null until grilled | -| `prd_grandfathered` | bool or null | set by one-shot migration | True for pre-spc-3 planned intents. Suppresses GR002 and GL005 as info-only (not blocker). Cleared when intent is regrilled | +| `prd_grandfathered` | bool or null | set by one-shot migration | True for pre-spc-3 (predecessor store) planned intents. Suppresses GR002 and GL005 as info-only (not blocker). Cleared when intent is regrilled | ### Term ID semantics — machine vs body prose @@ -293,7 +293,7 @@ Both the press-release intent and the frozen PRD are immutable input artefacts p ## 6. Acceptance gates and bidirectional link verification -`internal/core/lint` (cross-cutting; its shipped wiring is `abcd docs lint` and the `cmd/record-lint` gate) gates `/abcd:intent plan` promotion. The shipped subset is the `intent_lifecycle` rule (the directory/kind/`spec_id` invariants and the `status:`-key ban below), the `intent_impact_valid` rule (the `impact:` field's legal value set), and `persona_registry` (press-release quote attributions resolve to the persona roster); the remaining gates (the `IL0xx` codes per [`05-internals/06-lint.md`](../05-internals/06-lint.md)) are plan-time design, a later phase. It verifies: +`internal/core/lint` (cross-cutting; its shipped wiring is `abcd docs lint` and the `cmd/record-lint` gate) is the record-lint over the committed intent tree — it does not run inside `/abcd:intent plan`; the acceptance-criteria refusal at plan time is the intent package's own `hasAcceptanceCriteria` check (`internal/core/intent`). The shipped record-lint subset is the `intent_lifecycle` rule (the directory/kind/`spec_id` invariants and the `status:`-key ban below), the `intent_impact_valid` rule (the `impact:` field's legal value set), and `persona_registry` (press-release quote attributions resolve to the persona roster); the remaining gates (the `IL0xx` codes per [`05-internals/06-lint.md`](../05-internals/06-lint.md)) are plan-time design, a later phase. It verifies: - **Acceptance criteria present and well-formed** (per the itd-1 discipline): every intent in `drafts/`, `planned/`, and `disciplines/` has a `## Acceptance Criteria` section with at least one Given-When-Then bullet. Intents cannot be promoted from `drafts/` → `planned/` (or `drafts/` → `disciplines/`) without this. Hard block. - **`kind` is set on intents in `planned/`, `shipped/`, `disciplines/`, and `superseded/`.** Intents in `drafts/` may have `kind: null` (binding decision is at plan time). Lint blocks promotion from `drafts/` if `kind` cannot be inferred + confirmed. @@ -304,12 +304,12 @@ Both the press-release intent and the frozen PRD are immutable input artefacts p - **No intent has a `status` field — across any kind.** Lifecycle state is encoded by directory location only (`drafts/` / `planned/` / `shipped/` / `disciplines/` / `superseded/`). The 2026-05-08 directive removed the cached-mirror option: directory IS the state, no exceptions. Lint hard-blocks any frontmatter containing a `status:` key (shipped lint rule: `intent_lifecycle`, severity: blocker; templates and existing files were stripped in the 2026-05-08 sweep). The historical `status: draft | planned | shipped` field on standalone/bundle-member intents has been retired; uniform "directory is canonical" applies to all kinds. - Every intent in `drafts/` has `spec_id: null` (drafts have no plan yet). - Every intent in `planned/` has `spec_id: null` (unscheduled) or a `spc-N` id; a non-null `spec_id` points to an existing native-spec-store `-*.md` whose frontmatter `intent` field matches the intent's `id` (or contains the intent's `id` as one of a list, for bundle-member intents). -- Every intent in `shipped/` has a closed-status linked spec (or `spec_id: null` + a `manual_ship_reason` field for the no-spec case). +- Every intent in `shipped/` has `kind` set (`standalone` or `bundle-member`) and a non-null `spec_id`. (The stronger invariant — the linked spec exists and is closed, or `spec_id: null` + a `manual_ship_reason` for the no-spec case — is a later-phase gate; the shipped rule checks only that `spec_id` is non-null.) - Discipline-kind intents have `spec_id: null` always (disciplines never get a spec; this is structurally enforced). - **Every intent in `superseded/` has both `superseded_by: ` AND `kind_at_supersession: `.** The first names the successor; the second preserves what shape the intent had when it was retired (standalone vs bundle-member vs discipline change the meaning of "superseded"). Both are required. If `kind_at_supersession: bundle-member`, the intent ALSO carries `bundle_at_supersession: ` — preserving the bundle membership at retirement time even though the active `bundle:` field is `null`. - No intent ID collisions; no spec referencing a non-existent intent ID. - File location matches `kind` frontmatter (drift between dir and field flagged). -- For intents promoted from issues (per itd-4): bidirectional `related_issues` ↔ `related_intents` linkage holds. Per spc-23 (intent-fidelity-reviewer's issue-drift role — a later phase). +- For intents promoted from issues (per itd-4): bidirectional `related_issues` ↔ `related_intents` linkage holds. Per spc-23 (predecessor store; intent-fidelity-reviewer's issue-drift role — a later phase). Drift triggers a warning, not a block (since spec-store state may legitimately lag intent state during work in progress). Acceptance-criteria absence and kind/directory mismatch are hard blocks (the whole point of the itd-1 discipline is to force the AC discipline; the kind/directory contract makes the lifecycle navigable). @@ -331,9 +331,9 @@ This is product-tier review. The opponent is the codebase. Distinct from the spe - the **itd-1 acceptance pass** (a shipped intent) writes per-criterion verdicts into that intent's `## Audit Notes` (the verdict of record), with the review request staged under `.abcd/.work.local/reviews/`; - the **itd-37 `MG004` pass** (a native spec's `## Modification Grammar`) writes its `PASS` / `FAIL` verdict to an `audit/spec-mg-/` receipt under the local ephemeral logs tier — native specs have no `## Audit Notes` section, so the verdict cannot land in-file (a later phase). -**What spc-12 ships.** spc-12 ships the **discipline-judgement subset** of `/abcd:intent review` — the itd-1 per-criterion acceptance verdicts and the itd-37 `MG004` boilerplate check, with their writers and receipts. The broader **press-release prose review** (the `honoured` / `diverged` / `missing` buckets below) and other prose/terminology/PRD-fidelity outputs are **deferred** to a later spec. spc-12 also ships the **manual** review surface; spc-28 ships the on-close hook (move `planned → shipped` + queue a review on that transition). Auto-running the reviewer off that queue is still deferred — no spec currently owns it; spc-6 disowned auto-firing. +**What the predecessor's spc-12 ships.** The predecessor's spc-12 ships the **discipline-judgement subset** of `/abcd:intent review` — the itd-1 per-criterion acceptance verdicts and the itd-37 `MG004` boilerplate check, with their writers and receipts. The broader **press-release prose review** (the `honoured` / `diverged` / `missing` buckets below) and other prose/terminology/PRD-fidelity outputs are **deferred** to a later spec. It also ships the **manual** review surface; spc-28 (predecessor store) ships the on-close hook (move `planned → shipped` + queue a review on that transition). Auto-running the reviewer off that queue is still deferred — no spec currently owns it; spc-6 (predecessor store) disowned auto-firing. -**The spc-23 issue-drift role (a later phase).** spc-23 adds an `--issue-drift` role — a corpus-wide bidirectional cross-reference walk between shipped intents and the `iss-N` ledger (per itd-4), with receipts under `.abcd/.work.local/logs/audit/issue-drift-/`, a default exit 0 with warnings to stderr, and a `--strict` exit-1 mode for CI gates. See the native spec `spc-23-intent-fidelity-reviewer-extension`. +**The spc-23 (predecessor store) issue-drift role (a later phase).** The predecessor's spc-23 adds an `--issue-drift` role — a corpus-wide bidirectional cross-reference walk between shipped intents and the `iss-N` ledger (per itd-4), with receipts under `.abcd/.work.local/logs/audit/issue-drift-/`, a default exit 0 with warnings to stderr, and a `--strict` exit-1 mode for CI gates. See the predecessor's `spc-23-intent-fidelity-reviewer-extension`. Outputs findings with two layers: @@ -352,13 +352,13 @@ Findings appended to the intent's `## Audit Notes` section. Manual re-run via `/ For bundle-member intents, this role runs *per intent* against the same delivered reality (each member's acceptance criteria checked separately). -#### The audit loop — record-only vs loop-to-acceptance (itd-50 / spc-52) +#### The audit loop — record-only vs loop-to-acceptance (itd-50 / spc-52, predecessor store) Role 1 records per-criterion verdicts; **itd-50 adds the POLICY that decides what happens to a recorded verdict.** The policy rides the review-queue drainer on the run seam (adr-27) — it is NEVER in the pure on-close lifecycle hook (the lifecycle close stays a pure data function; the mode logic lives in the drainer/policy layer). **Three audit-loop modes, facilitator-elected per intent** via the `audit_mode` frontmatter key: -- **`record-only`** (the default, and today's behaviour) — a `NOT_MET` is written to `## Audit Notes`; no re-work is triggered. An **absent** `audit_mode` key resolves to `record-only` (additive, spc-28/spc-43-compatible). +- **`record-only`** (the default, and today's behaviour) — a `NOT_MET` is written to `## Audit Notes`; no re-work is triggered. An **absent** `audit_mode` key resolves to `record-only` (additive, spc-28/spc-43-compatible — predecessor store). - **`loop-to-acceptance`** — a `NOT_MET` re-opens the linked work and iterates against the same acceptance criteria until they read `MET`, bounded by `audit_budget` (the spec-grain SHIP/NEEDS_WORK fix-loop lifted to the intent grain). See `05-internals/03-configuration.md` for the `audit_mode` / `audit_budget` keys, the default budget (`3`), and the fail-closed rule for a malformed/zero/negative budget. **Full state-coverage table** (every Family-2 rollup maps to a defined action — no dead-ends; the loop trigger is `NOT_MET` only): @@ -374,7 +374,7 @@ Role 1 records per-criterion verdicts; **itd-50 adds the POLICY that decides wha `UNACHIEVABLE` is an intent-level **rollup** terminal the policy layer writes — it is **NOT** a per-criterion verdict (`ACCEPTANCE_VERDICTS = {MET, MET_WITH_CONCERNS, NOT_MET, INCONCLUSIVE}` is unchanged) and is recognised at the `Overall:` / rollup parse layer only. -**The `UNACHIEVABLE` replan surface (no rollback).** A terminal `UNACHIEVABLE` writes a `why-unachievable` explanation + a **replan invitation** block (naming both the product thinker and the facilitator) into the intent's `## Audit Notes`. The intent **stays in `shipped/`** — its `spec_id` / `kind` / directory + delivered artifacts are byte-untouched (a `drafts/` move would break the lifecycle invariant + spc-48 lint and read as a partial un-ship). The invitation seeds `/abcd:intent grill`; no machine authors a replan, nothing is auto-rolled-back. +**The `UNACHIEVABLE` replan surface (no rollback).** A terminal `UNACHIEVABLE` writes a `why-unachievable` explanation + a **replan invitation** block (naming both the product thinker and the facilitator) into the intent's `## Audit Notes`. The intent **stays in `shipped/`** — its `spec_id` / `kind` / directory + delivered artifacts are byte-untouched (a `drafts/` move would break the lifecycle invariant + spc-48 (predecessor store) lint and read as a partial un-ship). The invitation seeds `/abcd:intent grill`; no machine authors a replan, nothing is auto-rolled-back. **Gated manual verification + verification receipt (R5).** The manual-verification invitation renders **only** when the machine rollup is acceptance-eligible (all `MET`, or `MET_WITH_CONCERNS` with no `NOT_MET`) — if any criterion is not `MET`/concerns, the loop or replan invitation runs first; the product thinker is never asked to hand-test something the audit already knows is broken. The sign-off is recorded as a **verification receipt distinct from the machine verdict of record** — a separate JSON artifact under `.abcd/.work.local/logs/audit/verify-/receipt.json`, never merged into `## Audit Notes`: @@ -392,7 +392,7 @@ Receipt **states**: `offered` (the gate opened — the drainer stamps this on a Introduced by itd-48 (which superseded itd-31). The opponent is *other documents*: compares the brief and every intent against each other (and against the brief itself), surfacing the five live judgement categories — **terminology drift, premise contradictions, scope leakage, sequencing impossibilities, naming conflicts**. No spec-store analogue — the spec store reviews one artefact at a time; corpus-wide consistency is pure abcd ground. -The judgement half runs on demand via `/abcd:intent consistency` (Carmack-level oracle review) — a later phase (spc-29), not yet a binary sub-verb. +The judgement half runs on demand via `/abcd:intent consistency` (Carmack-level oracle review) — a later phase (spc-29, predecessor store), not yet a binary sub-verb. **Deferred follow-up**: the mechanical-half lint categories — schema/state contradictions, reference rot, acknowledgement gaps — were originally planned as `internal/core/lint --cross-doc` codes `XD002`/`XD006`/`XD007` per `05-internals/06-lint.md`; the lint-code half is deferred to a follow-up intent. Pre-commit hook wiring that would let `/abcd:intent consistency` findings block commits is also deferred. @@ -404,7 +404,7 @@ Findings land in `.abcd/.work.local/logs/audit/consistency-/report.{json,md} Introduced alongside the three intent kinds (per itd-34). The opponent is the *kind taxonomy*: examines whether each intent's declared `kind` (the noun in frontmatter) still fits the corpus. The verb `shape` matches the taxonomy noun and pairs cleanly with `reclassify` (the action verb that commits a `shape` finding). -The on-demand surface is a later phase (spc-29). **Bare** scans the corpus; **with ``** checks one intent. Findings land in a report under `.abcd/.work.local/logs/audit/shape-/report.{json,md}`. The user accepts a suggestion via `/abcd:intent reclassify`; declined suggestions are logged for future review (so the reviewer doesn't re-surface the same suggestion every run). +The on-demand surface is a later phase (spc-29, predecessor store). **Bare** scans the corpus; **with ``** checks one intent. Findings land in a report under `.abcd/.work.local/logs/audit/shape-/report.{json,md}`. The user accepts a suggestion via `/abcd:intent reclassify`; declined suggestions are logged for future review (so the reviewer doesn't re-surface the same suggestion every run). **Concurrency contract** (between any future scheduled invocation and on-demand `shape`): @@ -423,7 +423,7 @@ The three live `suggestion_type` values this role produces: - **`bundle`** — "intents X and Y reference each other in scope/references and target the same release; consider `kind: bundle-member` with shared bundle ID." - **`supersession`** — "intent X's scope is fully covered by intent Y; consider `kind: superseded --by Y`." -> **Deferred follow-up**: pre-commit hook wiring for continuous shape scanning, and the `shape(...)` function's `mode="pre_commit"` parameter is preserved as a seam but no hook invokes it. Discipline subtype clustering ("once enough disciplines exist, surfaces 'three disciplines have similar `kind_notes`; consider formalising a subtype'") was named in earlier itd-34 drafts and is *not* shipped by spc-29 — it is not a live suggestion type. +> **Deferred follow-up**: pre-commit hook wiring for continuous shape scanning, and the `shape(...)` function's `mode="pre_commit"` parameter is preserved as a seam but no hook invokes it. Discipline subtype clustering ("once enough disciplines exist, surfaces 'three disciplines have similar `kind_notes`; consider formalising a subtype'") was named in earlier itd-34 drafts and is *not* shipped by spc-29 (predecessor store) — it is not a live suggestion type. ### Review and audit trail layout diff --git a/.abcd/development/brief/04-surfaces/06-capture.md b/.abcd/development/brief/04-surfaces/06-capture.md index a7123f7..77a4d28 100644 --- a/.abcd/development/brief/04-surfaces/06-capture.md +++ b/.abcd/development/brief/04-surfaces/06-capture.md @@ -80,14 +80,14 @@ A later phase, not yet built — the migration rides the `abcd dev-sync work` su ## 5. Implementation status -- **Library primitives:** delivered by `spc-20-issue-ledger-primitives-iss-n-allocator`. +- **Library primitives:** delivered by the predecessor's `spc-20-issue-ledger-primitives-iss-n-allocator` (predecessor store). The API the command surface consumes is the Go package `internal/core/capture` (allocator, find, read, build, mutate; capture, resolve, wontfix, list, status) — a port of the predecessor's `_issue_lib` and `issue_workflow` primitives. -- **Command flow:** delivered by `spc-21-abcdcapture-command-flow-text-ingest`. -- **Legacy `.abcd/.work.local/` scratch migration:** design target per `spc-22-workissuesmd-migration-promote-legacy` — a later phase, not yet built (rides the `dev-sync` surface, § 3). -- **intent-fidelity-reviewer cross-check:** delivered by `spc-23-intent-fidelity-reviewer-extension`. +- **Command flow:** delivered by the predecessor's `spc-21-abcdcapture-command-flow-text-ingest` (predecessor store). +- **Legacy `.abcd/.work.local/` scratch migration:** design target per the predecessor's `spc-22-workissuesmd-migration-promote-legacy` (predecessor store) — a later phase, not yet built (rides the `dev-sync` surface, § 3). +- **intent-fidelity-reviewer cross-check:** delivered by the predecessor's `spc-23-intent-fidelity-reviewer-extension` (predecessor store); the reviewer surface ships as `agents/intent-fidelity-reviewer.md`. - **`promote ` bridge:** the command-orchestrated flow leans on `abcd intent ""`, delivered by `spc-7-abcd-intent-quoted-text-create-symmetric` (itd-46). The issue body is handed to that create path, which files a new draft; diff --git a/.abcd/development/brief/04-surfaces/07-memory.md b/.abcd/development/brief/04-surfaces/07-memory.md index 8402a72..098fe5c 100644 --- a/.abcd/development/brief/04-surfaces/07-memory.md +++ b/.abcd/development/brief/04-surfaces/07-memory.md @@ -1,6 +1,6 @@ # `/abcd:memory` — Multi-Upstream Curated Knowledge Substrate -User-facing command for the per-project compounding-curated knowledge substrate at `.abcd/memory/`. Design target per itd-36 (idea-1 final shape after 5-round adversarial oracle review); the write core (ingest/ask/bare) is spc-38's (the memory write core) and the lint family spc-39's (the memory-coverage lints + the `MQ`/`MS`/`ML` codes). itd-36 sits in `intents/planned/` — delivery state is the intent lifecycle's, not this page's (see the [brief README's provenance note](../README.md)). +User-facing command for the per-project compounding-curated knowledge substrate at `.abcd/memory/`. Design target per itd-36 (idea-1 final shape after 5-round adversarial oracle review); the write core (ingest/ask/bare) traces to the predecessor store's spc-38 (the memory write core) and the lint family to the predecessor store's spc-39 (the memory-coverage lints + the `MQ`/`MS`/`ML` codes). Every bare `spc-38`/`spc-39` on this page is a predecessor-store id cited as provenance — this repo's spec store holds only spc-2..spc-13, and none of these ids names a spec in it. itd-36 sits in `intents/planned/` — delivery state is the intent lifecycle's, not this page's (see the [brief README's provenance note](../README.md)). For the **substrate spec** (page-class enum, source-class taxonomy, curator behaviour, lifecycle class, integration with itd-26 loot), see [`05-internals/07-memory.md`](../05-internals/07-memory.md). This file is the surface contract: what the user types and what happens. @@ -54,6 +54,7 @@ DISTIL (host-delegated: the host agent is the distiller, supplying the ▼ WRITE - .abcd/memory/__.md (new pages or updates) + - .abcd/memory/README.md (store skeleton, scaffolded on first write) - .abcd/memory/index.md (regenerated catalog) - .abcd/memory/log.md (append: ## [YYYY-MM-DD HH:MM] external_pdf | ) - .abcd/memory/contradictions.md (if curator surfaces conflict with existing pages) @@ -98,7 +99,7 @@ Only `lint-/report.{json,md}` is written by the shipped store (spc-39's ## 4. Composition with adjacent surfaces -- **`/abcd:disembark`** exports curated project memory/provenance into the lifeboat (designed behaviour; itd-36 doesn't change disembark's source-mapping). What the lifeboat is specified to carry is the curated provenance surface named in [`02-disembark.md §5`](02-disembark.md) — `research/pitfalls.{json,md}`, `assets/_manifest.json`'s provenance/classification, and root `_provenance.json` — **not** a verbatim `.abcd/memory/` payload; declaring an exact `.abcd/memory/`-verbatim payload is deferred to the disembark spec that wires the lifeboat packer (adr-28). The recovery-humility framing on disembark/embark applies: the lifeboat is the floor of recoverable theory, not theory itself. +- **`/abcd:disembark`** exports curated project memory/provenance into the lifeboat (designed behaviour; itd-36 doesn't change disembark's source-mapping). What the lifeboat is specified to carry is the curated provenance surface named in [`02-disembark.md §5`](02-disembark.md) — root `_provenance.json` (the lifeboat marker and manifest) and `coverage.{json,md}` (per-section grounded/partial/blank status) — **not** a verbatim `.abcd/memory/` payload; declaring an exact `.abcd/memory/`-verbatim payload is deferred to the disembark spec that wires the lifeboat packer (adr-28). The recovery-humility framing on disembark/embark applies: the lifeboat is the floor of recoverable theory, not theory itself. - **`/abcd:embark`** unpacks a lifeboat's record families into the receiving repo; carrying `.abcd/memory/` forward is designed behaviour. Source-class enum carries forward; receiver runs `/abcd:memory lint` (spc-39) post-unpack to verify quotation budgets and licences haven't drifted. - **`/abcd:launch`** does **not** consume the provenance substrate's licence gate (adr-28): the public launch payload excludes `.abcd/**` — including `.abcd/memory/**` — wholesale as policy, so launch never publishes the files the gate checks. The restrictive-licence gate's real consumer is the **lifeboat** (`/abcd:disembark`, above), the surface that publishes curated project memory/provenance; the later-phase gate is designed to refuse publish on restrictive-licence files and warn on `licence: unknown`. At launch the gate is future/inert — launch excludes `.abcd/**` wholesale, so `/abcd:launch dry-run` surfaces no licence-gate verdict (its dry-run gates are the secret/PII scan, marker-block, installability smoke, and documentation-auditor; see [`04-launch.md § 2`](04-launch.md#2-curated-release-artefact-default-deny) and adr-28). - **`/abcd:dredge`** (a later phase, itd-25) writes synthesis output to `.abcd/memory/__.md` with `source.class: dredge_synthesis`. Distinct verb (storage vs operation per dredge-pushback in idea-1 R4); shared destination namespace. @@ -108,7 +109,7 @@ Only `lint-/report.{json,md}` is written by the shipped store (spc-39's | Item | Cost | |---|---| -| Schema extension | 3 new sibling files (`index.md`, `log.md`, `contradictions.md`) + typed `source:` frontmatter; no migration of existing flat-named files | +| Schema extension | 4 new sibling files (`README.md`, `index.md`, `log.md`, `contradictions.md`) + typed `source:` frontmatter; no migration of existing flat-named files | | Curator role on `principle-distiller` | Role extension (Phase 6 design-target agent per [`05-internals/01-agents.md`](../05-internals/01-agents.md), not yet shipped; itd-31 precedent — the design-target agent roster stays at 16) | | Provenance/licence substrate | Separable spec at [`05-internals/09-provenance-substrate.md`](../05-internals/09-provenance-substrate.md); shared with the later-phase itd-26 loot | | Lifeboat licence-gate extension (adr-28) | `.abcd/memory/sources/` allowlist + restrictive-licence detection over the lifeboat's gated payload (`/abcd:disembark`); NOT a launch payload gate — launch excludes `.abcd/**` wholesale, so the gate is future/inert at launch | diff --git a/.abcd/development/brief/04-surfaces/08-abcd.md b/.abcd/development/brief/04-surfaces/08-abcd.md index 05b0d1f..f81d678 100644 --- a/.abcd/development/brief/04-surfaces/08-abcd.md +++ b/.abcd/development/brief/04-surfaces/08-abcd.md @@ -19,10 +19,13 @@ per-verb bare renders (`/abcd:ahoy`, `/abcd:capture`, …). Each per-verb bare render is scoped to its own command's surface; this board is the *cross-verb* answer to "what's the state of my abcd project right now?". -The `/abcd` surface routes every verb through the transport-agnostic core (the +Binary-backed `/abcd:` verbs route through the transport-agnostic core (the Cobra CLI is the front door today; an MCP server follows later, per -[adr-23](../../decisions/adrs/0023-transport-agnostic-core.md)) — it no longer -hides any bundled dependency behind the wrapper. +[adr-23](../../decisions/adrs/0023-transport-agnostic-core.md)). Not every verb +does: `consult` and `ingest` run entirely as host-side markdown over the +operator-level sources corpus (`~/.abcd/sources/`) and never invoke the binary, +and `prepare-this-repo` is an interim bridge that shells out to `abcd audit` +for part of its work and handles the rest host-side. ## Sub-verbs and aliases @@ -62,16 +65,17 @@ therefore satisfy SD001 rather than tripping it. (Had they instead introduced divergent behaviour, they would be forbidden sub-verbs and the design would be reconsidered — the gate is real, not a rubber stamp.) -## spc-17 stub replacement (investigation-gated) +## Predecessor spc-17 stub replacement (investigation-gated) -itd-20 was planned assuming a spc-17 probe stub for the bare top-level command -would be *replaced*. Investigation found NO such stub: the spc-17 work is -credited with probe/bare renders for the *sub-verb* surfaces. The `disembark`, +itd-20 was planned assuming a predecessor-store spc-17 probe stub for the bare +top-level command would be *replaced*. Investigation found NO such stub: the +predecessor's spc-17 work is credited with probe/bare renders for the *sub-verb* +surfaces. The `disembark`, `embark`, `launch`, and `intent` CLI verbs all ship today, and each has its plugin command file present (`commands/abcd/disembark.md`, `embark.md`, `launch.md`, `intent.md`). `commands/abcd/` ships `ahoy`, `audit`, `capture`, `consult`, `disembark`, `docs`, `embark`, `history`, `ingest`, `intent`, -`launch`, `memory`, `prepare-this-repo`, and `version` (14 files). The investigation found no spc-17 probe STUB behind the +`launch`, `memory`, `prepare-this-repo`, and `version` (14 files). The investigation found no predecessor spc-17 probe STUB behind the top-level command to replace — the "stub replacement" premise is therefore recorded here as not-applicable. The top-level command file itself is present today (`commands/abcd.md`, referenced above and scaffolded with the repo), so a @@ -161,8 +165,8 @@ Therefore section (3) is PERMANENTLY the `no dev-sync record` known-state line in v1 — the dev-sync-staleness signal does **not** function until a state substrate exists. This is an ALLOWED terminal state, recorded here and in the itd-20 intent so it is not read as a shipped capability. Adding a dev-sync -state substrate is explicitly OUT of scope for spc-83.2 (and named in the .5 -out-of-scope update). +state substrate is explicitly OUT of scope for the predecessor's spc-83.2 (and +named in that predecessor spec's .5 out-of-scope update). ## Performance bounds (NFR) diff --git a/.abcd/development/brief/04-surfaces/09-reflect.md b/.abcd/development/brief/04-surfaces/09-reflect.md index 41916bf..6b4bb81 100644 --- a/.abcd/development/brief/04-surfaces/09-reflect.md +++ b/.abcd/development/brief/04-surfaces/09-reflect.md @@ -22,7 +22,8 @@ deterministic reflect-writer capability, which renders This surface doc records the design contract; the runtime behaviour (contract verification, README write, consumed-receipt-path + phase/audit/member-spec -links) is owned by task `spc-83-operator-surfaces-manifest-lockstep.3` and the +links) is owned by the predecessor store's task +`spc-83-operator-surfaces-manifest-lockstep.3` and the command file `commands/abcd/reflect.md`. ## Argument @@ -36,7 +37,7 @@ Bare `/abcd:reflect` (no argument) renders help/state and writes nothing. ## What it does -1. Selects the **latest** spc-66 phase-audit receipt whose `phase_id` matches the +1. Selects the **latest** spc-66 (predecessor store) phase-audit receipt whose `phase_id` matches the argument (newest `timestamp` wins) at `.abcd/logbook/audit/phase-/report.json`. 2. Runs the `reflection-composer` agent as a seeded single-pass interview: five @@ -71,7 +72,7 @@ The writer refuses (each with a message naming the phase-audit prerequisite): |---------|-----------| | Non-phase argument | `itd-N` or free text — phase-only grain | | No reflection answers | A bare `{}` on stdin (hollow all-"none recorded") — refused unless `--allow-empty` | -| No spc-66 audit receipt | No phase-audit receipt exists for the named phase | +| No spc-66 (predecessor store) audit receipt | No phase-audit receipt exists for the named phase | | Empty-audited latest receipt | `member_specs` empty OR `done_total.total == 0` — nothing shipped to reflect on | | Re-run without `--overwrite` | A retrospective already exists for the phase | @@ -92,10 +93,11 @@ SINGLE dispatch target and the ONLY writer. ## Output path and single-source-of-truth Output is fixed at `.abcd/retrospectives//README.md` (a peer of -`.abcd/intents/` and `.abcd/logbook/`), committed as part of the phase's +`.abcd/development/intents/` and the design-target `.abcd/logbook/` tree), +committed as part of the phase's permanent record. The README LINKS to the phase doc, the audit report (its receipt path recorded in the README), and each member spec — it never copies -their bodies. v1 links are limited to those three: the spc-66 receipt carries no +their bodies. v1 links are limited to those three: the spc-66 (predecessor store) receipt carries no intent ids, so intent links are a recorded future extension. Canonical glossary terms (`voyage`, `persona`) are used in body prose; the README @@ -125,6 +127,7 @@ shipped capability. - Agent: `agents/reflection-composer.md` (the 16th catalog agent — see [`../05-internals/01-agents.md`](../05-internals/01-agents.md)) - Intent: `itd-24` (`../../intents/planned/itd-24-reflect-command.md`) -- The spc-66 phase-audit contract reflect consumes: the phase-audit report - schema owned by the Go binary (`internal/core/...`) +- The predecessor store's spc-66 phase-audit contract reflect is designed to + consume: its phase-audit report schema is a design target for the Go binary + (`internal/core/...`), not yet shipped - Naming / VR001 registration: [`../02-constraints/04-naming.md`](../02-constraints/04-naming.md) diff --git a/.abcd/development/brief/04-surfaces/README.md b/.abcd/development/brief/04-surfaces/README.md index 3f6bf8a..4d6f353 100644 --- a/.abcd/development/brief/04-surfaces/README.md +++ b/.abcd/development/brief/04-surfaces/README.md @@ -9,10 +9,10 @@ The brief's user-facing command surface is the set enumerated below (not all are | 3 | `/abcd:embark` | shipped | Unpack a packed lifeboat's record families back into a target repository (`embark from [target]`), the inverse of `disembark`: the read-only `probe` verb inspects first, the lifeboat is verified against its `manifest_sha256` before any read (a symlink or oversize file anywhere inside is refused), and only the four record families (ADRs, issues, intents, specs) are written — verbatim, into their canonical locations — through two-layer containment (`os.Root` plus lexical validation). The write is **conflict-safe**: any per-file conflict (a differing target, a non-regular target, a non-directory parent) refuses the WHOLE write and exits non-zero with one bulk report (identical bytes are an idempotent skip; a re-run is a clean no-op). The current abcd marker block is re-injected into the target `CLAUDE.md` (never foreign prose), and the coverage blanks a human must answer are surfaced first (itd-88, M5, adr-35). The full embark chapter — the press-release interview, asset curation, and provenance ledger — remains a design target (M6). | [`03-embark.md`](03-embark.md) | | 4 | `/abcd:launch` | shipped | Preview the curated release bundle and gates (read-only), plus the **release cut**: `launch ship` derives the version and the record set from what shipped since the newest tag, runs the surface guardrail, and with `--changelog-json` validates the host-composed prose against that record set (the completeness bijection) before writing the dated `CHANGELOG.md` heading (itd-73, itd-67's changelog slice). `commands/abcd/launch.md` carries the emit → compose → ingest orchestration over the `release-changelog-composer` agent; the deterministic emit alone is `abcd changelog`. Packaging/publishing the artefact remains a design target (itd-72) — see [`04-launch.md`](04-launch.md) | [`04-launch.md`](04-launch.md) | | 5 | `/abcd:intent` | shipped | Intent lifecycle over the press-release record store: bare status, quoted-text create, the `ready ` implement-readiness gate (exit 0/1/2; an unready intent is refused with remedies and routed to the host-run planning interview, whose human-confirmed `plan` is the acceptance-criteria sign-off — itd-94), plus `plan` / `link` / `review` (itd-80, itd-46). `commands/abcd/intent.md` carries the surface and the interview script. The richer press-release verbs (refine / grill / ship / consistency / shape / reclassify; three review roles per [`05-intent.md § 7`](05-intent.md#7-the-intent-fidelity-reviewer-agent-three-roles-three-verbs)) remain design targets (backing intents in `intents/planned/`) | [`05-intent.md`](05-intent.md) | -| 6 | `/abcd:capture` | shipped | Issue ledger (capture / list / resolve / wontfix; `promote` is a design target per spc-30/itd-46, skill-orchestrated — see [`06-capture.md`](06-capture.md)) | [`06-capture.md`](06-capture.md) | +| 6 | `/abcd:capture` | shipped | Issue ledger (capture / list / resolve / wontfix; `promote` is a design target per spc-30 (predecessor store)/itd-46, skill-orchestrated — see [`06-capture.md`](06-capture.md)) | [`06-capture.md`](06-capture.md) | | 7 | `/abcd:memory` | shipped | Multi-upstream curated knowledge substrate (per itd-36) — `ingest` external sources / `ask` queries / `lint` health-checks. Component spec: [`05-internals/07-memory.md`](../05-internals/07-memory.md). | [`07-memory.md`](07-memory.md) | | 8 | `/abcd` | shipped | Top-level where-am-i status board (per itd-20, `intents/planned/`) — cross-command re-orientation. The shipped bare render is four read-only lines (directory, git repo, record present, `.abcd/` work tiers); the richer board (visibility, lifeboat, dev-sync, recent logbook, active intents, next actions) is a design target. `status` is a positional plugin alias for the bare render; `help` is not an alias (the binary has no `status` verb, and `abcd help` prints command usage). Read-only. | [`08-abcd.md`](08-abcd.md) | -| 9 | `/abcd:reflect` | staged | Phase retrospective (per itd-24) — `/abcd:reflect ` composes a five-section retrospective (went well / could improve / lessons / decisions / metrics) seeded by the spc-66 phase-audit receipt. Phase-only grain — design target, not shipped: no `reflect` binary verb or `commands/abcd/reflect.md` (itd-24) | [`09-reflect.md`](09-reflect.md) | +| 9 | `/abcd:reflect` | staged | Phase retrospective (per itd-24) — `/abcd:reflect ` composes a five-section retrospective (went well / could improve / lessons / decisions / metrics) seeded by the spc-66 (predecessor store) phase-audit receipt. Phase-only grain — design target, not shipped: no `reflect` binary verb or `commands/abcd/reflect.md` (itd-24) | [`09-reflect.md`](09-reflect.md) | | 10 | `/abcd:docs` | shipped | Documentation-currency lint (`lint`) — change-narration, broken links, stray root markdown; read-only, the deterministic half of the docs release gate | [`10-docs.md`](10-docs.md) | | 11 | `/abcd:history` | shipped | Session-transcript store (`capture` / `list` / `show`) — per-repo, redact-on-write, keyed on the root-commit SHA | [`11-history.md`](11-history.md) | | 12 | `/abcd:version` | shipped | Print the installed abcd version — read-only | [`12-version.md`](12-version.md) | From 45fb46828f1429e62a160a5a3c86ed33a76ac3ff Mon Sep 17 00:00:00 2001 From: REPPL <77722411+REPPL@users.noreply.github.com> Date: Wed, 22 Jul 2026 20:24:29 +0100 Subject: [PATCH 4/4] docs: fix the nine round-4 residue findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The final targeted pass over the four-round crosscheck's residue: bare ahoy's next-step line scoped to unmanaged kinds; doctor's distinct contribution stated as the audit pass, not sole user-scope validation; embark's conflict render depicted as the shipped per-file list and its manifest seal qualified with the excluded synthesis layer; launch's opening sentence and gate-render claims aligned with the binary's text and JSON outputs; the live spc-83-operator-surfaces bundle (itd-20/24/63/69, verified in frontmatter — the finding undercounted at two) recorded in both the kinds table and the intents/README.md bundles table; history capture idempotency stated as the (content hash, session id, kind) triple. Resolves iss-123. Assisted-by: Claude:claude-opus-4-8 Assisted-by: Claude:claude-fable-5 --- .abcd/development/brief/04-surfaces/01-ahoy.md | 12 +++++++----- .abcd/development/brief/04-surfaces/03-embark.md | 14 ++++++++------ .abcd/development/brief/04-surfaces/04-launch.md | 4 ++-- .abcd/development/brief/04-surfaces/05-intent.md | 4 ++-- .abcd/development/brief/04-surfaces/11-history.md | 7 +++++-- .abcd/development/intents/README.md | 3 ++- ...reconciliation-round-4-residue-nine-findings.md | 14 ++++++++++++++ 7 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 .abcd/work/issues/resolved/iss-123-brief-reconciliation-round-4-residue-nine-findings.md diff --git a/.abcd/development/brief/04-surfaces/01-ahoy.md b/.abcd/development/brief/04-surfaces/01-ahoy.md index 0a8e9cf..a3df5aa 100644 --- a/.abcd/development/brief/04-surfaces/01-ahoy.md +++ b/.abcd/development/brief/04-surfaces/01-ahoy.md @@ -26,8 +26,9 @@ the CLI for anything that writes; the sub-verbs below ship on the CLI as - **`/abcd:ahoy doctor`** — run the **full** detection pass plus a read-only audit pass, and report the gap counts for each. The text render shows the detection-gap and audit-gap counts; the JSON envelope carries full per-gap - detail, including user-scope state (history store, `index.json`, symlink, - hook) that `install` touches but never otherwise re-validates. + detail, including the user-scope state (history store, `index.json`, symlink, + hook) that the shared detection pass validates on every sub-verb — doctor's + distinct contribution is the read-only audit pass layered on top. Distinct from bare invocation, which shows the folder kind, plugin-root status, root SHA, and gap count. - **`abcd ahoy identity-check`** — exit non-zero if the git commit identity @@ -332,9 +333,10 @@ user-scope app-state. - **Given** any abcd-aware terminal, **when** the user runs bare `/abcd:ahoy`, **then** the dispatcher runs the detection pass and shows the folder kind, - plugin-root status, root SHA, and gap count, plus a next-step line for the - folder kind (naming `/abcd:ahoy install` on an unmanaged repo) — and never - mutates state. + plugin-root status, root SHA, and gap count, plus a next-step line on the + unmanaged kinds (naming `/abcd:ahoy install` on an `unmanaged-repo`, "nothing + to act on" on an `unmanaged-folder`); a `managed-repo` prints the four status + lines with no next-step line — and never mutates state. - **Given** a fresh repo with no `.abcd/` directory, **when** `/abcd:ahoy install` runs to completion, **then** the two-file repo carve-out (`.abcd/config.json` + `.abcd/rules.json` per spc-16 T1) is written, diff --git a/.abcd/development/brief/04-surfaces/03-embark.md b/.abcd/development/brief/04-surfaces/03-embark.md index 83f78d0..0b34fcc 100644 --- a/.abcd/development/brief/04-surfaces/03-embark.md +++ b/.abcd/development/brief/04-surfaces/03-embark.md @@ -60,7 +60,7 @@ There is no emptiness gate. A conflict is per-file: a target that merely carries Embark is a deterministic Go run: it reads the lifeboat, plans, refuses on any conflict, then writes the record families plus the `CLAUDE.md` marker. No interactive scaffolder, no Python, and no LLM agent sit in the write path. -0. **Read lifeboat:** the record families — ADRs (`docs/adrs/`), issues (`activity/issues/`), intents (`rescue/intents/`), specs (`rescue/specs/`) — plus the report-only files `_provenance.json`, `press-release.{json,md}`, `principles.{json,md}`, `coverage.{json,md}`, `brief/**`, `rescue/spine.md`, `graveyard/**`, and the post-pack `audit/**` oracle audit. The lifeboat is untrusted input: embark verifies its `manifest_sha256` against the on-disk tree and refuses a symlink or oversize file anywhere inside. +0. **Read lifeboat:** the record families — ADRs (`docs/adrs/`), issues (`activity/issues/`), intents (`rescue/intents/`), specs (`rescue/specs/`) — plus the report-only files `_provenance.json`, `press-release.{json,md}`, `principles.{json,md}`, `coverage.{json,md}`, `brief/**`, `rescue/spine.md`, `graveyard/**`, and the post-pack `audit/**` oracle audit. The lifeboat is untrusted input: embark verifies its `manifest_sha256` against the on-disk tree — every hashed file, i.e. the tree minus the post-pack synthesis layer the manifest deliberately excludes (`_provenance.json`, `press-release.{json,md}`, `principles.{json,md}`, `audit/**`, `graveyard/lessons.json`, `graveyard/low-confidence/**`) — and refuses a symlink or oversize file anywhere inside. A tampered hashed record or an added stray file is refused; the excluded report-only files sit outside the manifest seal and carry their own per-entry integrity (cite-or-be-dropped, registered-verdict gate), not the hash. 1. **Plan.** Map each record file to its target path and classify it `create`, `unchanged` (byte-identical), or a conflict. On **any** conflict embark writes nothing and refuses ([§ 4](#4-conflict-ux)). 2. **Write the record families verbatim** to their canonical target locations — ADRs to `.abcd/development/decisions/adrs/`, issues to `.abcd/work/issues/`, intents to `.abcd/development/intents/`, specs to `.abcd/development/specs/` — through two-layer containment (an `os.Root` boundary plus independent lexical path validation), skipping `unchanged` files. Bucketed families keep their source bucket (issues by state; intents into `drafts`/`planned`/`shipped`/`disciplines`/`superseded`; specs into `open`/`closed`). Terminology, docs, and `.abcd/memory/` are **not** embark families — they do not travel. 3. **Re-inject the current abcd marker block** into the target `CLAUDE.md` between BEGIN/END markers (idempotent) — never AGENTS.md, and never a verbatim copy of lifeboat prose. The block is the modular-rules-loader block (per itd-3); principles surface through the rules loader's domain rules on demand by prompt-keyword recall. @@ -73,14 +73,16 @@ Embark is a deterministic Go run: it reads the lifeboat, plans, refuses on any c When the target already has files that the lifeboat would write, **core returns the conflict set and the surface renders it as a single bulk report** — never a per-file barrage, and never a file written by core (adr-35). The shipped `from` **writes nothing** on any conflict and exits non-zero; the surface relays the bulk report so the user resolves the conflicts and re-runs: ``` -embark detected N conflicts across the record families: - • 3 .abcd/development/specs/ - • 2 .abcd/work/issues/ - • 1 .abcd/development/decisions/adrs/ +2 conflict(s) (nothing was written): + • .abcd/development/specs/open/spc-12.md (exists-differs) + • .abcd/work/issues/open/iss-7.md (exists-differs) -nothing was written — resolve the conflicts and re-run. +nothing was written — resolve the conflicts and re-run ``` +The report is a per-file list — one line per conflicting target path with its +conflict kind (`exists-differs`) — not a per-family count roll-up. + The conflict list is a value core hands back; if the operator wants it on disk, the surface writes it — core does not. *(Design target, not yet shipped: `embark from --force` turns that bulk report into a single resolution prompt — keep target (skip everything in lifeboat that conflicts) / replace target (lifeboat wins everywhere) / merge where possible, prompt otherwise / abort (surface prints the conflict list; nothing is written) — and applies the chosen resolution uniformly. Single decision, transparent (shows scope before asking). The shipped `from` takes no flags and offers no resolution choice.)* diff --git a/.abcd/development/brief/04-surfaces/04-launch.md b/.abcd/development/brief/04-surfaces/04-launch.md index fced01f..e169c9e 100644 --- a/.abcd/development/brief/04-surfaces/04-launch.md +++ b/.abcd/development/brief/04-surfaces/04-launch.md @@ -4,10 +4,10 @@ ## Sub-verbs -The shipped verb surface is the `--dry-run` flag on `abcd launch` — a read-only preview of the bundle and gates. Bare `abcd launch` never mutates state: it refuses (exit 1) with a hint to pass `--dry-run`; a bare-as-status render is a design target, unshipped. The sub-verb design: +The shipped verb surface is the `--dry-run` flag on `abcd launch` — a read-only preview of the bundle and gates — plus the `abcd launch ship` subcommand, which cuts the release (the RELEASE CUT slice only; see the ship bullet below). Bare `abcd launch` never mutates state: it refuses (exit 1) with a hint to pass `--dry-run`; a bare-as-status render is a design target, unshipped. The sub-verb design: - **`/abcd:launch ship`** — **partly shipped: the RELEASE CUT only** (itd-73 derived versioning + itd-67's changelog slice). `abcd launch ship` derives the version and the record set from what shipped since the newest tag, runs the surface guardrail, and — with `--changelog-json` — validates the host-composed prose against the record set (the completeness bijection) and writes the dated `CHANGELOG.md` heading `.github/workflows/auto-release.yml` turns into a tag; `commands/abcd/launch.md` carries the emit → compose → ingest orchestration and the `release-changelog-composer` agent it dispatches. The `Ship` engine is wired: `abcd launch ship` is a live subcommand, and `--payload-dir ` stages the versioned release payload — the derived version stamped into the payload's `plugin.json`/`marketplace.json` and lockstep-proved before return. Commit, tag, and publish stay a design target (itd-65 gate suite + itd-72 publishing): the verb neither commits, tags, nor publishes. The full-cut design: cut a curated release artefact from the one repo: run pre-flight gates, filter the artefact (default-deny, `.abcd/**` excluded by packaging), stamp the version, and on a `v*` tag publish a GitHub Release ([adr-28](../../decisions/adrs/0028-single-repo-curated-release.md)). The flow described in §§ 1–6 below is this sub-verb's behaviour. Flag-shaped modifiers `--allow-dirty` and `--allow-doc-warnings` belong to this sub-verb's design; the shipped `ship` accepts `--changelog-json` and `--payload-dir` (plus the global `--json`), and bare `abcd launch` accepts only `--dry-run` and the global `--json`. There is no version flag — the version is derived, never authored ([adr-31](../../decisions/adrs/0031-derived-versioning-from-intents.md), see [§ 3](#3-versioning--marketplace)). -- **`/abcd:launch dry-run`** — shipped as the `--dry-run` flag (the plugin command `commands/abcd/launch.md` maps the `dry-run` of its `[dry-run | ship]` argument hint onto `abcd launch --dry-run`; the binary has no `dry-run` subcommand). **Report-only preview, always exit-0** (a preview never blocks). It runs the parts of the pre-flight suite that exist today: as of spc-64 (predecessor store) the **secret + PII scan gate** (the native scanners, see [§ 1](#1-pre-flight-gates)) runs for real in report-only mode and prints what it *would* refuse on (a finding, or a fail-closed reason such as "scanner unavailable"); the **installability smoke** runs for real at its light tier (see [§ 1](#1-pre-flight-gates)); the **manifest lockstep check** runs for real at its `dev` polarity over the working tree — the polarity adr-19 requires the committed manifests to satisfy (no version key), see [§ 3](#3-versioning--marketplace) — reports its result and folds any drift or an unreadable version-location contract into what it *would* refuse on; the remaining gates (marker-block, documentation-auditor) are the gate-suite intent's (itd-65) and render as "(not yet implemented)". It also produces the would-be artefact manifest, without writing the release artefact. dry-run is **not** "ship minus publish": running the *full* gate suite and **hard-failing** on a finding (exit non-zero) is the full `ship` verb's behaviour (itd-65 + itd-72), not dry-run's. +- **`/abcd:launch dry-run`** — shipped as the `--dry-run` flag (the plugin command `commands/abcd/launch.md` maps the `dry-run` of its `[dry-run | ship]` argument hint onto `abcd launch --dry-run`; the binary has no `dry-run` subcommand). **Report-only preview, always exit-0** (a preview never blocks). It runs the parts of the pre-flight suite that exist today: as of spc-64 (predecessor store) the **secret + PII scan gate** (the native scanners, see [§ 1](#1-pre-flight-gates)) runs for real in report-only mode and prints what it *would* refuse on (a finding, or a fail-closed reason such as "scanner unavailable"); the **installability smoke** runs for real at its light tier (see [§ 1](#1-pre-flight-gates)); the **manifest lockstep check** runs for real at its `dev` polarity over the working tree — the polarity adr-19 requires the committed manifests to satisfy (no version key), see [§ 3](#3-versioning--marketplace) — reports its result and folds any drift or an unreadable version-location contract into what it *would* refuse on; the remaining gates (marker-block, documentation-auditor) are the gate-suite intent's (itd-65): `--dry-run --json` reports each as `{"status": "not_implemented", "detail": "Phase-5 deferred"}`, while the plain-text `--dry-run` render omits the gate list entirely (it prints only version, files bundled, scan hardfails, and would-publish, plus a would-refuse-on line when there is a finding). It also produces the would-be artefact manifest, without writing the release artefact. dry-run is **not** "ship minus publish": running the *full* gate suite and **hard-failing** on a finding (exit non-zero) is the full `ship` verb's behaviour (itd-65 + itd-72), not dry-run's. ## 1. Pre-flight gates diff --git a/.abcd/development/brief/04-surfaces/05-intent.md b/.abcd/development/brief/04-surfaces/05-intent.md index 9d1b8ef..779b8a4 100644 --- a/.abcd/development/brief/04-surfaces/05-intent.md +++ b/.abcd/development/brief/04-surfaces/05-intent.md @@ -31,7 +31,7 @@ Every intent has a `kind` declared in frontmatter, set at `/abcd:intent plan` ti | `kind` | Has press release? | Lives in | Maps to | Examples | |---|---|---|---|---| | `standalone` | Yes | `drafts/` → `planned/` → `shipped/` | One spec (1:1) | itd-3, itd-4, itd-7, most of the corpus | -| `bundle-member` | Yes | Same as standalone, with `bundle: ` linking members | Shared spec with bundle-mates (N:1) | (no live bundles in any phase — see [`intents/README.md`](../../intents/README.md#bundles) for retired bundle history) | +| `bundle-member` | Yes | Same as standalone, with `bundle: ` linking members | Shared spec with bundle-mates (N:1) | itd-20, itd-24, itd-63, itd-69 (bundle `spc-83-operator-surfaces`, in `planned/` — committed but unscheduled, named in no phase doc); see [`intents/README.md`](../../intents/README.md#bundles) | | `discipline` | **No** — uses `## Rule` instead | `disciplines/` | No spec; imposes acceptance gates on every other spec | itd-1 (AC gate), itd-5 (prompt-quality) | **`kind` is binding once set at plan time.** Late changes go through `/abcd:intent reclassify --kind ` — a later phase (no `reclassify` sub-verb ships; the binary exposes `link`, `new`, `plan`, `ready`, `review`) — which records the change in the intent's frontmatter `reclassification_history` and surfaces it for reviewer review. @@ -237,7 +237,7 @@ Discipline-kind intents use a different template — see § 1 "Discipline format ## 4. Persona registry -See [`01-product/05-personas.md`](../01-product/05-personas.md) for the canonical persona registry (SSOT). A press release attributes its customer quote to a persona from that roster, and the `persona_registry` lint rule checks that every quote attribution resolves to it; the codified abcd principle (no real names, no "hypothetical user") lives in the canonical persona file. +`.abcd/development/personas.json` is the machine-checked persona roster (SSOT): the `persona_registry` lint rule resolves every press-release quote attribution against the names in that JSON file (its path is set by `.abcd/record-lint.json`). The prose page [`01-product/05-personas.md`](../01-product/05-personas.md) describes the roster and carries the codified abcd principle (no real names, no "hypothetical user"), but defers to `personas.json` as the roster's home — the lint checks the JSON, not the page. ## 5. Frontmatter fields (spc-3 (predecessor store) additions — a later phase) diff --git a/.abcd/development/brief/04-surfaces/11-history.md b/.abcd/development/brief/04-surfaces/11-history.md index 9d75852..184698b 100644 --- a/.abcd/development/brief/04-surfaces/11-history.md +++ b/.abcd/development/brief/04-surfaces/11-history.md @@ -21,8 +21,11 @@ secret or absolute home path survives capture. has several records) or by record filename. - **`/abcd:history capture [|-]`** — redact and store a raw transcript, read from a file or from stdin (`-`). Capture is **fail-closed on - redaction** and **idempotent on content hash** (re-capturing identical content - does not duplicate). Flags: `--kind` (`native` | `specstory-import`, default + redaction** and **idempotent on the (content hash, session id, kind) triple** + (re-capturing identical content under the same `--session` and `--kind` is a + no-op; the same content under a different session id or kind writes a new + record, so a second session is never mis-attributed to the first). Flags: + `--kind` (`native` | `specstory-import`, default `native`) and `--session` (the record's session id; defaults to the transcript filename, and is **required** when reading from stdin). diff --git a/.abcd/development/intents/README.md b/.abcd/development/intents/README.md index 48a2545..854dfdf 100644 --- a/.abcd/development/intents/README.md +++ b/.abcd/development/intents/README.md @@ -35,7 +35,7 @@ Every intent has a `kind` declared in frontmatter, set at `/abcd:intent plan` ti | `kind` | Has press release? | Lives in | Maps to | Examples | |---|---|---|---|---| | `standalone` | Yes | `drafts/` → `planned/` → `shipped/` | One spec (1:1) | itd-3, itd-4, itd-7, most of the corpus | -| `bundle-member` | Yes | Same as standalone, with `bundle: ` linking members | Shared spec with bundle-mates (N:1) | (no live bundles — `tier-0-audit-substrate` dissolved 2026-05-07; itd-31 promoted to standalone, itd-32 superseded) | +| `bundle-member` | Yes | Same as standalone, with `bundle: ` linking members | Shared spec with bundle-mates (N:1) | itd-20, itd-24, itd-63, itd-69 (bundle `spc-83-operator-surfaces`, in `planned/` — unscheduled); see [Bundles](#bundles) | | `discipline` | **No** — uses `## Rule` instead | `disciplines/` | No spec; imposes acceptance gates on every other spec | itd-1 (AC gate), itd-5 (prompt-quality) | Standalone is the default (~60% of the corpus). Bundle-members ship together as one spec. Disciplines are cross-cutting rules with no user moment of their own; they apply to every other spec as inherited acceptance gates. @@ -251,6 +251,7 @@ Active bundles (sets of intents that ship as one shared spec via multi-arg `/abc | Bundle ID | Members | Why a bundle | |---|---|---| +| `spc-83-operator-surfaces` | itd-20, itd-24, itd-63, itd-69 (all `planned/`, `spec_id: null`) | The operator-facing surface set that couples through the plugin manifest/metadata lockstep — top-level dispatcher (itd-20), reflect (itd-24), setup-wizard (itd-63), and plugin-metadata lockstep (itd-69) share one spec. Committed but unscheduled: named in no phase doc's `## Scope`, and `spec_id` is still `null` (the shared spec is not yet minted). | | ~~`tier-0-audit-substrate`~~ (dissolved 2026-05-07) | ~~itd-31 + itd-32~~ | The bundle premise (unified `/abcd:audit` surface bundling all review/audit roles into one verb's subverbs) was dissolved when the round-2 command-structure review split the three intent-fidelity-reviewer roles into three distinct verbs under `/abcd:intent` (review/consistency/shape). itd-31 promoted to standalone; itd-32 superseded by itd-31. | Bundles are declared in member intents' frontmatter (`bundle: `); membership is bidirectional (verified by the record lint). When a bundle's shared spec closes, all member intents move from `planned/` to `shipped/` together. diff --git a/.abcd/work/issues/resolved/iss-123-brief-reconciliation-round-4-residue-nine-findings.md b/.abcd/work/issues/resolved/iss-123-brief-reconciliation-round-4-residue-nine-findings.md new file mode 100644 index 0000000..1f76b80 --- /dev/null +++ b/.abcd/work/issues/resolved/iss-123-brief-reconciliation-round-4-residue-nine-findings.md @@ -0,0 +1,14 @@ +--- +schema_version: 1 +id: "iss-123" +slug: "brief-reconciliation-round-4-residue-nine-findings" +severity: "minor" +impact: internal +category: "observation" +source: "agent-finding" +found_during: "iss-121 reconciliation, round 4" +found_at: ".abcd/development/brief/04-surfaces/" +resolution: "All nine residue findings fixed and verified against binary and source in a final targeted pass; the live-bundle finding corrected upward during verification (four bundle-members, itd-20/24/63/69, not two) with the stale intents/README.md bundles table fixed alongside. Fifth-run crosscheck counts are iss-122 calibration data by maintainer decision, not a merge gate." +--- + +Round-4 residue of the brief reconciliation: nine precision-grade discrepancies remain after three fix rounds (102 -> 52 -> 25 -> 9), all empirically verified by the checkers and none of the judgement-flip-flop class. Examples: bare ahoy prints a next-step line only for unmanaged kinds but the criterion says any folder kind; embark's manifest verification excludes the post-pack synthesis layer while the chapter claims blanket tamper-evidence; a live bundle (spc-83-operator-surfaces, itd-20+itd-24) contradicts the no-live-bundles claim; the persona_registry lint checks personas.json, not the markdown page named as SSOT; history capture idempotency keys on (sha256, session, kind), not content hash alone. Full findings in the local scratch tier (2026-07-22-iss35-crosscheck-round4 file). The geometric tail also suggests the detector at 22-checker depth has a nonzero noise floor per fresh run — the pass threshold question belongs to iss-122. \ No newline at end of file