diff --git a/.abcd/development/brief/06-delivery/03-out-of-scope.md b/.abcd/development/brief/06-delivery/03-out-of-scope.md index 9f2550df7..c76820c0b 100644 --- a/.abcd/development/brief/06-delivery/03-out-of-scope.md +++ b/.abcd/development/brief/06-delivery/03-out-of-scope.md @@ -36,7 +36,6 @@ gate. That is what keeps "not hand-counted" true after the day it was written. - `itd-14` — Prompt registry + versioning (heavier successor to itd-5) - `itd-15` — Self-dogfooded SOTA audit (recurring per-disembark sibling to itd-5) - `itd-16` — `/abcd:audit` umbrella + chain substrate (default application: hash-chain over conversation/edit history; reframed as umbrella on 2026-05-08, lifeboat-integrity application extracted to itd-35) -- `itd-17` — Per-backend per-agent oracle effectiveness tracking - `itd-18` — `.claude/settings.local.json` permission templates - `itd-19` — ABCDevelopment stage-aware defaults - `itd-21` — `/abcd:init-project` empty-repo scaffolding @@ -117,11 +116,9 @@ gate. That is what keeps "not hand-counted" true after the day it was written. - `itd-2609150819440345` — a claim record beside the machine-scoped worktree store says which session holds which worktree, branch or record, replacing the per-session handshake (promoted from iss-2609100519122086) - `itd-2609151138388536` — the decisions log becomes a folder of individually minted decision records with an assembled index, `DECISIONS.md` a symlink to it, in abcd and in every managed repository; the shape retires the decisions-append gate (the rule is adr-2609151138420062; promoted from iss-2609100507439414) - `itd-2609151516525843` — a committed declaration lifts the public visibility fence so a fresh public repository can create its committed banned-names layer on day one, and a machine-global private banned-names list in the user-level home bans a name in every repository on the machine; CI never reads the home list and no pattern from it reaches a committed file (`builds_on` itd-74, `refines` adr-56; promoted from iss-2609100506269348) -- `itd-2609081951381895` — abcd ships an OpenAI-compatible api oracle adapter, the first wired provider a local MLX server with a cloud aggregator as config-only (refines adr-25; filed 2026-09-08) - `itd-2609090746410233` — A lifeboat packs from a worktree, a branch, or an abandoned feature test, experiment or implementation, not only from a whole repository (refines itd-88 and adr-35; realises the press release's not-yet-real widening, git-source half) - `itd-2609090746414083` — A lifeboat packs from a lab session home, the throwaway experiment's intention, harvest and bundle, with the same coverage honesty as a repository (refines itd-88 and adr-35; the non-git half, sequenced after the lab verb family) - `itd-2609180517121254` — every payload a host hands back from a delegated step names the model that produced it and the number of agents that ran, and the ingesting verb refuses one that does not -- `itd-2609201916056194` — a delegated agent runs through a command-line model runner the operator chose (claude CLI, opencode/openrouter); the opt-in cli oracle rung **Later-phase items with no intent id.** These four were written into the brief diff --git a/.abcd/development/decisions/adrs/2609221009491186-a-provider-adapter-serves-only-the-models-it-lists-under-a.md b/.abcd/development/decisions/adrs/2609221009491186-a-provider-adapter-serves-only-the-models-it-lists-under-a.md new file mode 100644 index 000000000..bd8b48ee8 --- /dev/null +++ b/.abcd/development/decisions/adrs/2609221009491186-a-provider-adapter-serves-only-the-models-it-lists-under-a.md @@ -0,0 +1,73 @@ +--- +id: adr-2609221009491186 +slug: a-provider-adapter-serves-only-the-models-it-lists-under-a +status: accepted +date: 2026-09-22 +supersedes: null +superseded_by: null +related_intents: [itd-2609081951381895, itd-2609221009495079, itd-2609170822093401, itd-6] +related_rfcs: [] +related_adrs: [adr-25] +--- + +# ADR-2609221009491186: A provider adapter serves only the models it lists, under a vendor denylist no listing overrides; everything else runs on the host + +## Context + +adr-25 makes the model host-delegated by default: the agent host owns model +choice, credentials and execution, and abcd's adapters (an OpenAI-compatible +API, a command-line runner, a reviewer route over MCP) are opt-in. The first +aggregator to reach abcd through the API adapter is OpenRouter, brought by a +new decision model (Jev, TypeSafe AI, September 2026) that is reachable +only that way. An aggregator serves every vendor's models under one key, +including the frontier models the person already pays for through the host +and its subscription. The adapter draft as written would route any model +name it was handed. The product thinker asked, on 2026-09-21, how to +integrate OpenRouter and how to ensure only certain models are reached +through it, so that a frontier model is never billed through the +aggregator or its data routed through a third party unasked. + +## Decision + +We will make every provider adapter default-deny by model. + +1. **An allowlist per provider.** Each provider block in the configuration + lists the model identifiers it may serve. A role or a judgement + configured for a model not on its provider's list is refused when the + configuration is read, before any call is made, naming the list. +2. **A vendor denylist above the allowlist.** A bundled denylist of vendor + prefixes (`anthropic/*` at minimum) refuses a listed model whose prefix + it matches, and no allowlist entry overrides it; the repository or the + machine may extend the denylist and never shorten it below the bundled + set. +3. **Everything else runs on the host.** A model that is neither listed nor + the host's own is not a route; the host and the person's subscription are + where frontier models run. +4. **The key is named, never stored.** The adapter reads its credential from + the machine's configuration or the environment by name; nothing about a + key enters the repository. +5. **The run record names the route.** Every call through an adapter records + the provider, the model identifier asked for and the model the provider + reports, so a substitution by the aggregator is visible. + +## Alternatives Considered + +- **Any model the key can reach, controlled at the provider.** Rejected: the + aggregator's own controls are outside the record, invisible to the run, + and a listing mistake on the provider's side would route a frontier model + through it with nothing in abcd to say so. +- **An allowlist only, no denylist.** Rejected: a listing mistake in the + repository's configuration would reach a frontier model; the denylist is + the rule that a mistake cannot override. +- **A denylist only.** Rejected: default-allow leaves every newly listed + aggregator model routable the day it appears. + +## Consequences + +- The API adapter intent (itd-2609081951381895) carries the list, the + denylist, the refusal and the record as criteria; the model tier + (itd-2609170822093401) reads the lists when it proposes a route; the + decision adapter (itd-2609221009495079) can name only a listed model. +- A person who wants a frontier model through an aggregator edits the + denylist on their machine, deliberately, and the run record shows it. +- The brief's adapters chapter gains the invariant. diff --git a/.abcd/development/decisions/adrs/2609221017021499-every-external-credential-abcd-holds-is-named-in.md b/.abcd/development/decisions/adrs/2609221017021499-every-external-credential-abcd-holds-is-named-in.md new file mode 100644 index 000000000..5e6d32add --- /dev/null +++ b/.abcd/development/decisions/adrs/2609221017021499-every-external-credential-abcd-holds-is-named-in.md @@ -0,0 +1,67 @@ +--- +id: adr-2609221017021499 +slug: every-external-credential-abcd-holds-is-named-in +status: accepted +date: 2026-09-22 +supersedes: null +superseded_by: null +related_intents: [itd-2609221017023290, itd-2609081951381895, itd-2609061543533170, itd-63] +related_rfcs: [] +related_adrs: [adr-25, adr-2609221009491186] +--- + +# ADR-2609221017021499: Every external credential abcd holds is named in configuration and kept in one store the person chose; never the harness, never the repository + +## Context + +The first adapter to need a secret (the OpenAI-compatible API adapter, for +OpenRouter) was given a one-time walkthrough at `ahoy` with three homes for +the key: a setup outside abcd, abcd-only on the machine, or the platform +keychain. The site setup needs a hosting credential; a transcript cloud hook +and a forge token are foreseeable. The product thinker observed on +2026-09-22 that the walkthrough is a pattern for every credential, and the +record's rule is one canonical primitive: a generic-smelling thing is built +once in its home and extended, never copied. + +## Decision + +We will hold every external credential through one credential store. + +1. **A name in configuration, never a value.** Configuration names a + credential; the value lives in the store. Nothing under the repository + and nothing in the harness's settings ever carries a value. +2. **Three homes, the person's choice**, made once per credential at an + `ahoy` walkthrough that first explains what the credential unlocks and + what works without it (itd-63's mode): a setup accessible outside abcd + (an existing tool's configuration or a named environment variable; the + store holds the pointer), abcd-only on the machine (the user-level + `~/.abcd/` store, owner-only permissions), or the platform keychain + (macOS Keychain; the secret service on Linux). The keychain is named as + the recommendation in the walkthrough's prose and never as a marked + option. +3. **One reader.** Every adapter resolves a credential through the store's + one function by name; an adapter that reads a secret any other way is a + defect. A name that resolves to nothing is a refusal naming the + walkthrough, never a silent unauthenticated call. +4. **Scanned before it can be written.** The store's write path runs the + secret scanner on any file it touches, and a value that would land in a + tracked path is refused. + +## Alternatives Considered + +- **Each adapter its own walkthrough and file.** Rejected: three copies of one + thing within a month, each a place to audit and each a place to leak. +- **The harness's own secret handling.** Rejected: abcd is host-agnostic, the + harness's settings are the one place the product thinker asked to keep + secrets out of, and a harness file is often synced or committed. +- **Keychain only.** Rejected: some people will not want it, and Linux has no + single equivalent everywhere; the choice is theirs. + +## Consequences + +- itd-2609221017023290 builds the store and the walkthrough; the API adapter + (itd-2609081951381895) and the site setup (itd-2609061543533170) read + through it, and their own walkthrough criteria are met by calling it. +- A future hook to an external service adds a name and an explanation, not a + store. +- The brief's adapters chapter gains the invariant beside adr-2609221009491186. diff --git a/.abcd/development/intents/drafts/itd-22-harness-portability.md b/.abcd/development/intents/drafts/itd-22-harness-portability.md index 8510984f7..9c2b00db1 100644 --- a/.abcd/development/intents/drafts/itd-22-harness-portability.md +++ b/.abcd/development/intents/drafts/itd-22-harness-portability.md @@ -11,6 +11,9 @@ severity: major # abcd Reaches Any Harness Through an Adaptor +> **Widened on 2026-09-22** by the product thinker: "any harness" includes **no harness**. abcd run as the binary alone is a first-class way to manage a repository: it calls whichever harnesses are available for the roles that need one, and where a role's runner is unavailable the fallback is a host the operator configured rather than a host session that happens to be there (itd-2609201916056194). The adaptor ladder below gains that rung; the console that would drive it from outside a harness is reframed as an operator console served by the binary (research note, 2026-09-22) and is not this record. + + ## Press Release > **abcd runs wherever the user's harness runs.** abcd's core is a transport-agnostic engine, and each harness reaches it through a thin adaptor built on one shared seam. An adaptor climbs a fixed ladder — the host's own plugin format first, any other native seam second, the MCP floor always — and one parity suite proves every host gets the same conventions, intents, and lifeboats. Adding a harness is one adaptor over an unchanged core, never a second copy of abcd. diff --git a/.abcd/development/intents/drafts/itd-2609081951381895-abcd-ships-an-openai-compatible-api-oracle-adapter-the-first.md b/.abcd/development/intents/drafts/itd-2609081951381895-abcd-ships-an-openai-compatible-api-oracle-adapter-the-first.md deleted file mode 100644 index a881cb4f4..000000000 --- a/.abcd/development/intents/drafts/itd-2609081951381895-abcd-ships-an-openai-compatible-api-oracle-adapter-the-first.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -id: itd-2609081951381895 -slug: abcd-ships-an-openai-compatible-api-oracle-adapter-the-first -spec_id: null -kind: null -suggested_kind: null -reclassification_history: [] -builds_on: [] -severity: minor -impact: additive -origin: researcher-authored -production_mode: hand-written ---- - -# abcd ships an OpenAI-compatible api oracle adapter — the first concrete implementation of adr-25's api oracle-backend shape. An operator configures a baseURL (and optional key) in .abcd/config.json and abcd routes oracle calls — reviews, audits — to that model over plain HTTP. The first wired provider is a local OpenAI-compatible server (Gropius MLX on localhost, no key needed); cloud aggregators such as OpenRouter ride the same adapter as pure configuration, not code. The host-delegated default is untouched: with no adapter configured nothing changes and no network call is attempted. - -## Press Release - -> _Seeded from a quoted-text intent capture. Expand into the full press-release narrative before planning._ - -## Why This Matters - -abcd ships an OpenAI-compatible api oracle adapter — the first concrete implementation of adr-25's api oracle-backend shape. An operator configures a baseURL (and optional key) in .abcd/config.json and abcd routes oracle calls — reviews, audits — to that model over plain HTTP. The first wired provider is a local OpenAI-compatible server (Gropius MLX on localhost, no key needed); cloud aggregators such as OpenRouter ride the same adapter as pure configuration, not code. The host-delegated default is untouched: with no adapter configured nothing changes and no network call is attempted. - -## Mechanism - -> _Prompted (the claim-recording gradient): why the authors expect this to work, as a falsifiable "we expect X because Y" — not the outcome restated. Replace this line with the claim, or with the exact token `None stated.` alone on its line to record the claim as considered and declined._ - -## Scope Conditions - -> _Required (the claim-recording gradient): the population, platform, scale, or assumptions this claim holds under, one per top-level bullet — `abcd intent plan` stamps each with a persistent identity. Replace this line with those bullets, or with the exact token `None stated.` alone on its line._ - -## Acceptance Criteria - -> _Required (the itd-1 discipline): add at least one Given-When-Then bullet describing the verifiable bar for "shipped" before this draft can be planned._ - -## Open Questions - -_None recorded yet._ - -## Audit Notes - -_Empty. Populated by intent-auditor when intent moves to shipped/._ diff --git a/.abcd/development/intents/drafts/itd-2609201916056194-abcd-runs-a-delegated-agent-through-a-command-line-model-run.md b/.abcd/development/intents/drafts/itd-2609201916056194-abcd-runs-a-delegated-agent-through-a-command-line-model-run.md deleted file mode 100644 index 23c6de210..000000000 --- a/.abcd/development/intents/drafts/itd-2609201916056194-abcd-runs-a-delegated-agent-through-a-command-line-model-run.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -id: itd-2609201916056194 -slug: abcd-runs-a-delegated-agent-through-a-command-line-model-run -spec_id: null -kind: null -suggested_kind: null -reclassification_history: [] -builds_on: [] -related_intents: [itd-2609170822093401, itd-2] -supersedes: [itd-2] -severity: minor -impact: additive -origin: researcher-authored -production_mode: hand-written ---- - -# abcd runs a delegated agent through a command-line model runner the operator chose, not only through the host session. A managed repository declares, per agent role, whether the role is launched in the host's own sub-agent (the default, unchanged) or through an opt-in command-line adapter: the claude CLI, or an opencode runner reaching an openrouter model. The adapter is the cli rung the oracle backend already names and nothing reads. It launches the role with the same prompt, the same inputs and the same output contract the host sub-agent gets, captures the transcript into the same store, and reports which runner and model answered. A role whose adapter is configured but unreachable falls back to the host and says so, never silently. This is the runner the implement verb builds on, and it refines the per-agent model tier intent by giving the tier a second way to be satisfied. - -## Press Release - -> _Seeded from a quoted-text intent capture. Expand into the full press-release narrative before planning._ - -## Typed Links - -Refines itd-2609170822093401 (the per-agent model tier gains a runner that can -satisfy it outside the host) and itd-2 (the host sub-agent stays the default; -this is the opt-in cli rung the boundary rules already name). Built on by -itd-2609201916151817. - -## Why This Matters - -abcd runs a delegated agent through a command-line model runner the operator chose, not only through the host session. A managed repository declares, per agent role, whether the role is launched in the host's own sub-agent (the default, unchanged) or through an opt-in command-line adapter: the claude CLI, or an opencode runner reaching an openrouter model. The adapter is the cli rung the oracle backend already names and nothing reads. It launches the role with the same prompt, the same inputs and the same output contract the host sub-agent gets, captures the transcript into the same store, and reports which runner and model answered. A role whose adapter is configured but unreachable falls back to the host and says so, never silently. This is the runner the implement verb builds on, and it refines the per-agent model tier intent by giving the tier a second way to be satisfied. - -## Mechanism - -> _Prompted (the claim-recording gradient): why the authors expect this to work, as a falsifiable "we expect X because Y" — not the outcome restated. Replace this line with the claim, or with the exact token `None stated.` alone on its line to record the claim as considered and declined._ - -## Scope Conditions - -> _Required (the claim-recording gradient): the population, platform, scale, or assumptions this claim holds under, one per top-level bullet — `abcd intent plan` stamps each with a persistent identity. Replace this line with those bullets, or with the exact token `None stated.` alone on its line._ - -## Acceptance Criteria - -> _Required (the itd-1 discipline): add at least one Given-When-Then bullet describing the verifiable bar for "shipped" before this draft can be planned._ - -## Open Questions - -_None recorded yet._ - -## Audit Notes - -_Empty. Populated by intent-auditor when intent moves to shipped/._ diff --git a/.abcd/development/intents/planned/itd-2609061543533170-abcd-sets-up-a-managed-repository-s-release-rendered-site-en.md b/.abcd/development/intents/planned/itd-2609061543533170-abcd-sets-up-a-managed-repository-s-release-rendered-site-en.md index 238162b46..4faf3b7c1 100644 --- a/.abcd/development/intents/planned/itd-2609061543533170-abcd-sets-up-a-managed-repository-s-release-rendered-site-en.md +++ b/.abcd/development/intents/planned/itd-2609061543533170-abcd-sets-up-a-managed-repository-s-release-rendered-site-en.md @@ -41,7 +41,7 @@ We expect a managed repository whose site is live to be read by people who never - **The credentialled path**: with a hosting credential configured, the same verb creates and routes the host through the provider adapter and reports the live address; without one it stops at the step above and says so. - **The provider seam**: one adapter ships (the provider abcd's own site uses); a second is a later intent, not a change to the verb. - **The pages**: landing, explorer, record pages, graph, timeline, glossary and status render for every managed repository from its own text; the site configuration switches pages off. -- **Re-runnable and credential-clean**: a second run changes nothing current and says so; no credential is ever written into the repository. +- **Re-runnable and credential-clean**: a second run changes nothing current and says so; the hosting credential is resolved by name through the credential store (itd-2609221017023290) and never written into the repository. - **Security review** before the lane ships. ## What's Out of Scope diff --git a/.abcd/development/intents/planned/itd-2609081951381895-abcd-ships-an-openai-compatible-api-oracle-adapter-the-first.md b/.abcd/development/intents/planned/itd-2609081951381895-abcd-ships-an-openai-compatible-api-oracle-adapter-the-first.md new file mode 100644 index 000000000..99cdf2f3a --- /dev/null +++ b/.abcd/development/intents/planned/itd-2609081951381895-abcd-ships-an-openai-compatible-api-oracle-adapter-the-first.md @@ -0,0 +1,84 @@ +--- +id: itd-2609081951381895 +slug: abcd-ships-an-openai-compatible-api-oracle-adapter-the-first +spec_id: spc-2609221011153746 +kind: standalone +suggested_kind: null +reclassification_history: [] +builds_on: [itd-2609170822093401] +severity: minor +impact: additive +origin: researcher-authored +production_mode: hand-written +related_intents: [itd-2609201916056194, itd-6, itd-2609221009495079] +related_adrs: [adr-2609221009491186] +--- + +# abcd ships an OpenAI-compatible API adapter, and a provider serves only the models it lists + +## Press Release + +> **An OpenAI-compatible API adapter reaches a configured provider for the roles and judgements pointed at it, and serves only the models that provider's list allows.** +> +> "I wanted one cheap decision model through OpenRouter, and I wanted to be certain nothing else of mine would ever go through it," said a product thinker configuring the first aggregator. "The adapter takes a base URL and a key name, the provider block lists the models it may serve, a bundled denylist keeps the frontier vendors out whatever I list, and the run record shows the model that actually answered." + +## Why This Matters + +adr-25 names an API oracle backend and nothing implemented it. The first need arrived with Jev (TypeSafe AI, September 2026), a decision model reachable through OpenRouter, which speaks the OpenAI-compatible protocol; the same adapter serves a local OpenAI-compatible server with no key. The product thinker's condition, ruled on 2026-09-22 (adr-2609221009491186), is that an aggregator serves only listed models under a vendor denylist, so a frontier model the person pays for through the host is never billed or routed through a third party unasked. + +## Mechanism + +We expect a default-deny list per provider to make the aggregator serve only what the person meant, because a route that must be listed is a route someone wrote down; shown wrong if a frontier model is ever billed through the adapter. + +## Scope Conditions + +- Holds for providers that speak the OpenAI-compatible chat protocol; a provider with its own protocol is its own adapter. +- Holds where the credential can be named in the machine's configuration or the environment; a provider that needs an interactive login is out of reach. + +## What's In Scope + +- **Configuration**: `oracle.api.` with `base_url`, `key` (a name, resolved from the machine's configuration or the environment), and `models` (the allowlist); roles and judgement types are pointed at `/`. +- **The refusal**: a role or judgement configured for a model not on its provider's list is refused when the configuration is read, before any call, naming the list; a listed model matching the bundled vendor denylist (`anthropic/*` at minimum) is refused the same way, and no allowlist entry overrides the denylist. +- **The call**: the same prompt, inputs and output contract the host sub-agent gets, over the protocol; the transcript captured into the same store. +- **The record**: provider, model asked for and model reported, per call, in the run record. +- **Unconfigured**: nothing changes and no network call is attempted (adr-25's default). +- **The one-time setup** through the credential store (itd-2609221017023290, adr-2609221017021499), in itd-63's explain-then-install mode: when `ahoy` takes a repository over, or is run bare, and no provider is configured, it explains what an aggregator is, what abcd would use it for (decision models and cheap judgements), what works without it (everything, on the host), and offers the walkthrough; on yes it asks where the key should live, the person's choice of three: a setup accessible separately from abcd (an existing OpenRouter or opencode configuration or a named environment variable; abcd stores only the name), abcd-only (the user-level `~/.abcd/` store, owner-only permissions), or the platform keychain (macOS Keychain; the secret service on Linux), with the keychain named as the recommendation in the prose above the choice, never as a marked option; then it writes the provider block with the first allowlist and verifies with one call. The key never enters the harness's settings or the repository. +- **Security review** before it ships: it sends prompts to a network endpoint under a credential. + +## What's Out of Scope + +- Provider routing (`:nitro`, `:floor`) and any per-request learned router; the adapter asks for the model it is given. +- A model's quality judgement (the tier's, itd-2609170822093401). +- Anything generative through a decision model (itd-2609221009495079). + +## Decisions + +Ruled by the product thinker on 2026-09-21, in the interview that filed and planned this intent (adr-2609221009491186 records the vocabulary rulings it rests on): + +1. Default-deny per provider with a vendor denylist above it (adr-2609221009491186). +2. OpenRouter is configuration of this adapter, not code. +3. The first models listed are decision models; frontier models stay on the host. +4. **The setup is a one-time walkthrough at `ahoy`, and the key's home is the person's choice of three** (ruled 2026-09-22): a setup outside abcd, abcd-only on the machine, or the platform keychain, recommended in prose. Basic by default, the adapter as the optional upgrade. + +## Open Questions + +_None open._ + +## Acceptance Criteria + +- **Given** `oracle.api.openrouter` configured with a base URL, a key name and a model list, **when** a role or judgement pointed at a listed model runs, **then** the call goes over the OpenAI-compatible protocol with the host sub-agent's prompt, inputs and output contract; unconfigured, nothing changes and no call is attempted. +- **Given** a role or judgement configured for a model not on its provider's list, **when** the configuration is read, **then** it is refused before any call, naming the list. +- **Given** a listed model whose prefix matches the vendor denylist, **when** the configuration is read, **then** it is refused the same way, and an allowlist entry does not override it. +- **Given** the key, **when** the adapter reads it, **then** it comes from the machine's configuration or the environment by name, and no credential is written into the repository. +- **Given** a call through the adapter, **when** the run record is read, **then** it names the provider, the model asked for and the model the provider reported. +- **Given** a repository abcd takes over with no provider configured, **when** `ahoy` runs, **then** it explains the aggregator, its use and what works without it, and offers the walkthrough; declining leaves the host as the only route and says so. +- **Given** the walkthrough accepted, **when** it asks where the key lives, **then** it offers the three homes (outside abcd; abcd-only, owner-only; the platform keychain) with the keychain recommended in the prose above the choice, writes the provider block with the first allowlist, verifies with one call, and writes nothing into the harness's settings or the repository. +- **Given** the lane, **when** it ships, **then** a security review of the network path and the credential handling is on its record. + +## Audit Notes + +_Empty. Populated by intent-auditor when intent moves to shipped/._ + +## Grounds + +- pursued: Jev is reachable only this way and the person's subscription must stay where Opus runs; we expect the listed route to serve only what was meant; shown wrong if a frontier model is ever billed through the adapter diff --git a/.abcd/development/intents/planned/itd-2609170822093401-the-oracle-choice-is-one-repo-wide-value-in-abcd-config-json.md b/.abcd/development/intents/planned/itd-2609170822093401-the-oracle-choice-is-one-repo-wide-value-in-abcd-config-json.md index 22e2c3a71..265e9e6e1 100644 --- a/.abcd/development/intents/planned/itd-2609170822093401-the-oracle-choice-is-one-repo-wide-value-in-abcd-config-json.md +++ b/.abcd/development/intents/planned/itd-2609170822093401-the-oracle-choice-is-one-repo-wide-value-in-abcd-config-json.md @@ -155,6 +155,14 @@ several routes, each receipt naming the route that governed it. the only evidence, and a host that cannot name its model cannot be checked, so rows for it are advice with a receipt that says so. +## Decisions + +Ruled by the product thinker on 2026-09-22, after the research pass on model routing: + +1. **Escalation inside a lane is a rule, not a router.** A lane starts at its role's tier; on a failed fix round the next round runs one tier up, and the switch is recorded as a fact in the state file with the round and the gate output that caused it. +2. **A route through a provider adapter must be on that provider's allowlist** (adr-2609221009491186); the tier proposes only routes the resolver admits. +3. **No learned per-request router** (itd-17 superseded by itd-2609221009495079). + ## Acceptance Criteria - **Given** a machine with no accepted table and a repository with no @@ -200,6 +208,8 @@ several routes, each receipt naming the route that governed it. - **Given** a table carrying a row for a name that is not an agent in the roster, **when** the table is read, **then** the orphan row is reported by name on stderr and the remaining rows still apply. +- **Given** a lane whose fix round failed, **when** the next round starts, **then** it runs one tier up from the role's tier and the state file records the round, the gate output and the switch. +- **Given** a proposed route through a provider adapter, **when** the tier proposes it, **then** it is one the provider's allowlist admits, or the proposal names the refusal instead. ## Open Questions diff --git a/.abcd/development/intents/planned/itd-2609201916056194-abcd-runs-a-delegated-agent-through-a-command-line-model-run.md b/.abcd/development/intents/planned/itd-2609201916056194-abcd-runs-a-delegated-agent-through-a-command-line-model-run.md new file mode 100644 index 000000000..f298a5d9b --- /dev/null +++ b/.abcd/development/intents/planned/itd-2609201916056194-abcd-runs-a-delegated-agent-through-a-command-line-model-run.md @@ -0,0 +1,87 @@ +--- +id: itd-2609201916056194 +slug: abcd-runs-a-delegated-agent-through-a-command-line-model-run +spec_id: spc-2609221533057881 +kind: standalone +suggested_kind: null +reclassification_history: [] +builds_on: [itd-2609201916151817, itd-2609170822093401] +related_intents: [itd-22, itd-2609081951381895, itd-6, itd-51] +supersedes: [itd-2] +severity: minor +impact: additive +origin: researcher-authored +production_mode: hand-written +related_adrs: [adr-2609221009491186] +--- + +# A role runs through the command-line harness the operator chose, and every fall back to the host is recorded + +## Press Release + +> **Any role can run through a command-line harness the operator names, with the same brief, contract and transcript store as the host's own sub-agent; where it cannot, the host takes it and the record says so.** +> +> "My reviews were the scarcest thing in a run, and a second harness was sitting on the machine doing nothing," said a technical facilitator. "Now a role names its runner, the review goes out through it, and the receipt reads the same as one the host ran. When the runner is not there the host picks it up, and the run tells me how often that happened, which is how I learned which roles to move." + +## Why This Matters + +The loop's process driver names a command-line rung that nothing reads. Every harness checked on 2026-09-22 can be driven headlessly with a prompt in, structured events out, an exit code and a non-interactive permission switch: Claude Code's print mode, opencode's run mode and its server, Codex's exec and app-server, Gemini's headless mode, Cursor's CLI, Aider's message mode. What is missing is abcd's side: one runner interface, a route per role, and the discipline that a fallback is evidence rather than a silent recovery. Two hazards are recorded: a print-mode run without the bare flag executes the target repository's hooks and configured servers with no trust dialog, and a runner's model route must obey the provider allowlist. + +## Mechanism + +We expect a role routed through another harness's command line to land in the record indistinguishably from a host-run one, because the runner is handed the same brief and held to the same output contract; shown wrong if a reader cannot tell which route ran from the receipt alone, or if no role is routed off the host in a release of it shipping. + +## Scope Conditions + +- Holds for a harness with a documented non-interactive mode that takes a prompt, returns structured output and an exit code, and grants permissions without a prompt. +- Holds where the runner's binary is on the machine and its own credential is already configured; abcd never logs a harness in. +- Holds under adr-2609221009491186: a runner's model route is one its provider's allowlist admits. + +## What's In Scope + +- **The route per role**: `roles..runner` names `host` (the default) or a configured runner; nothing changes for a role left unset. +- **The runner interface**: the same brief, inputs and output contract the host sub-agent gets; the answer validated the same way; the transcript captured into abcd's own store, whatever the harness keeps of its own. +- **The shipped runners**: the claude CLI in print mode with the bare flag (so a target repository's hooks and servers do not run untrusted) and opencode, through its run mode or its server; a third is configuration of the same interface where the harness's shape allows. +- **The fallback, and its record**: an unavailable or failing runner hands the role to the host, which is the default route, and where abcd runs as the binary with no host session the fallback is a host the operator configured (their main connector, the claude CLI for example) so there is always a landing. Every fallback writes a receipt naming the role, the runner asked for, the reason, and the route that ran. +- **The intel**: the run's summary reports the fallback count per runner and per role, so a route that never works is visible without reading transcripts. +- **The allowlist**: a runner's model route is resolved against its provider's list before the lane starts. +- **The first proof**: a ruthless review of a real lane sent through the opencode runner from a lane the host drives, its receipt indistinguishable from a host-run review but for the route it names. +- **Security review** before it ships: it starts processes with a prompt and a repository path. + +## What's Out of Scope + +- Choosing the runner for a role (the model tier's, itd-2609170822093401). +- Installing or authenticating a harness (itd-63 explains; the person installs). +- A served console over the loop (reframed as the operator console, research note of 2026-09-22). + +## Decisions + +Ruled by the product thinker on 2026-09-21, in the interview that filed and planned this intent (adr-2609221009491186 records the vocabulary rulings it rests on): + +1. The host is the default route; a runner is opt-in per role (adr-25). +2. Where abcd runs with no host session, the fallback is a host the operator configured, never nothing (ruled 2026-09-22). +3. Every fallback is recorded as intel: a receipt per event and a count per runner and per role in the run record (ruled 2026-09-22). +4. The claude CLI runner passes the bare flag, so a target repository's hooks and configured servers do not run untrusted. + +## Open Questions + +_None open._ + +## Acceptance Criteria + +- **Given** a role whose runner is set to a configured command-line harness, **when** the lane reaches that role, **then** the runner gets the same brief, inputs and output contract the host sub-agent would get, its answer is validated the same way, and its transcript lands in abcd's own store. +- **Given** a role left unset, **when** the lane reaches it, **then** the host runs it and nothing differs from today. +- **Given** a runner that is absent, refuses or fails, **when** the lane reaches that role, **then** the host runs it instead; with no host session the configured fallback host runs it; and a receipt names the role, the runner asked for, the reason and the route that ran. +- **Given** a completed run, **when** its summary is read, **then** it reports the fallback count per runner and per role. +- **Given** a runner whose model route is not on its provider's allowlist, **when** the lane is about to start, **then** it is refused before the runner is launched. +- **Given** the claude CLI runner, **when** it launches, **then** it passes the bare flag, so the target repository's hooks and configured servers do not run untrusted. +- **Given** a review run through the opencode runner, **when** its receipt is read beside a host-run review's, **then** the two differ only in the route named. +- **Given** the lane, **when** it ships, **then** a security review of the process launch, the repository path and the permission flags is on its record. + +## Audit Notes + +_Empty. Populated by intent-auditor when intent moves to shipped/._ + +## Grounds + +- pursued: the goal is abcd in the preferred harness with the others reachable, and the run's reviews are its scarcest step; we expect a review sent through a second harness to be indistinguishable in the record; shown wrong if a reader cannot tell the route from the receipt alone, or if no role is routed off the host in a release diff --git a/.abcd/development/intents/planned/itd-2609221009495079-abcd-s-typed-judgements-can-run-through-a-decision-adapter.md b/.abcd/development/intents/planned/itd-2609221009495079-abcd-s-typed-judgements-can-run-through-a-decision-adapter.md new file mode 100644 index 000000000..cd00e61b1 --- /dev/null +++ b/.abcd/development/intents/planned/itd-2609221009495079-abcd-s-typed-judgements-can-run-through-a-decision-adapter.md @@ -0,0 +1,82 @@ +--- +id: itd-2609221009495079 +supersedes: [itd-17] +slug: abcd-s-typed-judgements-can-run-through-a-decision-adapter +spec_id: spc-2609221011151661 +kind: standalone +suggested_kind: null +reclassification_history: [] +builds_on: [itd-2609212137128014, itd-2609081951381895, itd-2609170822093401] +severity: minor +impact: additive +origin: researcher-authored +production_mode: hand-written +related_intents: [itd-17, itd-2609212137116617, itd-82] +related_adrs: [adr-2609221009491186] +--- + +# abcd's typed judgements can run through a decision adapter, measured in a lab before it decides anything + +## Press Release + +> **A decision adapter answers abcd's closed-option judgements with a value and a calibrated probability; it runs in shadow as a lab first, and a research note turns it on.** +> +> "Half of what my agents ask a frontier model is a yes-or-no about one record," said a product thinker reading a run's cost. "A decision model answers those in half a second for a fraction of a cent. I do not trust it yet, so it runs beside the host, the lab records every pair, and when the note shows it agrees, I turn that judgement over." + +## Why This Matters + +The research pass of 2026-09-22 found Jev (TypeSafe AI) to be a typed-decision model, not a text model and not a router: choose one of N, score against a rubric, yes or no, each with a calibrated probability, in under a second at $0.042 per million tokens, 32K context, and unable to abstain. Independent evidence is one forty-case test; the vendor's numbers are vendor-graded. abcd's judgement-shaped steps are many: the remedy read, duplicate matching, unmeetable criteria, the eligibility residual, source classification and selection in the library, memory checks, ledger category and severity, the pre-pass's overlap, the docs lint's present-tense advisory, embark's lesson ranking. Ruled: an adapter behind one interface, the host as default, shadow mode as an abcd lab, a research note as the evidence a ruling turns a judgement type on with. + +## Mechanism + +We expect calibrated typed answers to agree with the host on closed-option judgements at a rate a lab can measure, because those judgements have a small option set and a state that fits the model's window; shown wrong if the shadow run's agreement is below what the note's threshold demands. + +## Scope Conditions + +- Holds for judgements whose option set is closed and whose state fits 32K tokens after retrieval; a judgement needing the whole corpus in view is out. +- Holds where a refusal is not the right answer: the model cannot abstain, so a judgement type whose right answer is often "unknown" is not turned over. +- Holds under adr-2609221009491186: the adapter's model is on its provider's allowlist. + +## What's In Scope + +- **One interface**: `(state, typed question) → (value, probability)` with three question kinds (choose, score, yes/no); the host's own judgement implements it too and stays the default. +- **The judgement types** it may be configured for: the remedy read, duplicate matching, unmeetable criteria, the eligibility residual, source classification and selection over a shortlist, the memory checks, ledger category and severity, the pre-pass's overlap, the docs lint's present-tense advisory, embark's lesson ranking. +- **Shadow mode as a lab**: `abcd lab mint` opens a lab home; the adapter runs beside the host on the configured types, decides nothing, and each pair (host's answer, adapter's answer, probability, the state's hash) is recorded under the lab home in the machine-scoped store, never the repository. +- **The evidence**: `abcd lab harvest` produces agreement rates per type with counts and probability bands; the research note filed from it (dated, under the record's research notes) is the evidence a ruling turns a type on with. +- **Turning on**: per judgement type in configuration; unreachable falls back to the host with a receipt; the run record names which route judged. +- **The first candidate**: `typesafe/jev-1.13` through the API adapter, on OpenRouter's allowlist; a small text model scoring options by log-probability is the second, behind the same interface. + +## What's Out of Scope + +- Any generative step. +- A learned per-request router for lane models (itd-17, superseded). +- What to build next (a computed score, itd-2609211116005482; a rubric score may become one component later). + +## Decisions + +Ruled by the product thinker on 2026-09-21, in the interview that filed and planned this intent (adr-2609221009491186 records the vocabulary rulings it rests on): + +1. Shadow mode only, as an abcd lab; a research note is the evidence; the host stays the default (ruled 2026-09-22). +2. The interface is the host's too, so the two are compared like for like. +3. Turned on per judgement type, never all at once. + +## Open Questions + +_None open._ + +## Acceptance Criteria + +- **Given** a judgement type configured for shadow and a lab home minted, **when** the judgement runs, **then** the host decides, the adapter answers beside it, and the pair with its probability and the state's hash is written under the lab home; nothing is written into the repository. +- **Given** a lab home with recorded pairs, **when** `abcd lab harvest` runs, **then** it reports agreement per judgement type with counts and probability bands, and the note filed from it names the lab home and the count. +- **Given** a judgement type turned on in configuration, **when** it runs, **then** the adapter decides, the run record names the route, and an unreachable adapter falls back to the host with a receipt saying so. +- **Given** a configured model not on its provider's allowlist, **when** the configuration is read, **then** it is refused before any call (adr-2609221009491186). +- **Given** a judgement whose state exceeds the model's window, **when** it runs in shadow, **then** the pair is recorded as skipped with the size, and the host decides alone. +- **Given** the host judgement and the adapter, **when** either is called, **then** both go through the one interface, so a type can be switched without a caller changing. + +## Audit Notes + +_Empty. Populated by intent-auditor when intent moves to shipped/._ + +## Grounds + +- pursued: cost per judgement is a hundredfold lower and the evidence is vendor-graded; we expect the shadow lab to show agreement the note can put a number on; shown wrong if the agreement is below the note's threshold diff --git a/.abcd/development/intents/planned/itd-2609221017023290-abcd-keeps-every-external-credential-the-same-way-one.md b/.abcd/development/intents/planned/itd-2609221017023290-abcd-keeps-every-external-credential-the-same-way-one.md new file mode 100644 index 000000000..29d2eb45e --- /dev/null +++ b/.abcd/development/intents/planned/itd-2609221017023290-abcd-keeps-every-external-credential-the-same-way-one.md @@ -0,0 +1,76 @@ +--- +id: itd-2609221017023290 +slug: abcd-keeps-every-external-credential-the-same-way-one +spec_id: spc-2609221017544877 +kind: standalone +suggested_kind: null +reclassification_history: [] +builds_on: [itd-63] +severity: minor +impact: additive +origin: researcher-authored +production_mode: hand-written +related_intents: [itd-2609081951381895, itd-2609061543533170, itd-6] +related_adrs: [adr-2609221017021499] +--- + +# abcd keeps every external credential the same way + +## Press Release + +> **One credential store, three homes the person chooses once, and every adapter reads through it; no secret ever enters the harness or the repository.** +> +> "OpenRouter today, a hosting provider next month, my transcript cloud after that, and each one wanted a key somewhere," said a product thinker. "Now abcd asks me once per service where the secret should live, explains what it unlocks first, and every part of abcd reads it the same way. Nothing lands in the harness's settings. Nothing lands in the repo." + +## Why This Matters + +The first adapter to need a secret was given its own walkthrough and its own file; the second was about to be. The record's rule is one canonical primitive, and a secret is the last thing to have three copies of. Ruled 2026-09-22 (adr-2609221017021499): one store, three homes, one reader, scanned before written. + +## Mechanism + +We expect one store with one reader to make every secret abcd holds auditable in one place and impossible to commit by accident, because the only write path runs the scanner and the only read path is named; shown wrong if a secret is found in a harness file or a tracked path after this ships, or if an adapter is found reading one another way. + +## Scope Conditions + +- Holds on macOS with the Keychain and on Linux with a secret service; a platform with neither offers the two other homes and says why. + +## What's In Scope + +- **The store** (`internal/core/credential`): `Resolve(name)` for every adapter; `Set(name, home, value)` used only by the walkthrough; homes `external` (a pointer to an existing tool's configuration or an environment variable name), `abcd` (`~/.abcd/credentials.json`, mode 0600), `keychain` (the platform keychain under abcd's service name). +- **The walkthrough** at `ahoy`, through itd-63's explain-then-install mode: what the credential unlocks, what works without it, then the three homes with the keychain recommended in prose, then a verification call the calling adapter supplies. +- **Refusals**: a name that resolves to nothing refuses naming the walkthrough; a write that would land in a tracked path is refused; the write path runs the secret scanner. +- **The readers**: the API adapter, the site setup and any later hook resolve by name; a review finding of any other read is a defect. +- **The record**: the run record names which credential names a run used, never a value. + +## What's Out of Scope + +- Rotating or expiring credentials. +- Sharing a credential between machines. +- Any credential the host itself holds for its own model. + +## Decisions + +Ruled by the product thinker on 2026-09-21, in the interview that filed and planned this intent (adr-2609221017021499 records the vocabulary rulings it rests on): + +1. One store, three homes, one reader, scanned before written (adr-2609221017021499). +2. The keychain is recommended in prose; the choice is the person's. + +## Open Questions + +_None open._ + +## Acceptance Criteria + +- **Given** an adapter needing a credential that is not set, **when** it resolves the name, **then** it refuses naming the walkthrough, and no unauthenticated call is made. +- **Given** the walkthrough accepted for a service, **when** it runs, **then** it explains what the credential unlocks and what works without it, offers the three homes with the keychain recommended in the prose above the choice, stores the value in the chosen home, and verifies with the adapter's own call. +- **Given** any home, **when** the tree and the harness's settings are inspected afterwards, **then** neither carries the value; a write that would land in a tracked path is refused and the write path runs the scanner. +- **Given** the API adapter and the site setup, **when** they read their credentials, **then** both call the store by name, and a test walks the adapters for any other read. +- **Given** a run, **when** its record is read, **then** it names the credential names used and no value. + +## Audit Notes + +_Empty. Populated by intent-auditor when intent moves to shipped/._ + +## Grounds + +- pursued: three adapters are about to be built and each would otherwise invent its own store; we expect one store to be the one place a secret is audited; shown wrong if a secret is found in a harness file or a tracked path, or an adapter reads one another way diff --git a/.abcd/development/intents/drafts/itd-17-model-effectiveness-tracking.md b/.abcd/development/intents/superseded/itd-17-model-effectiveness-tracking.md similarity index 93% rename from .abcd/development/intents/drafts/itd-17-model-effectiveness-tracking.md rename to .abcd/development/intents/superseded/itd-17-model-effectiveness-tracking.md index 79026d777..0e2c2a24a 100644 --- a/.abcd/development/intents/drafts/itd-17-model-effectiveness-tracking.md +++ b/.abcd/development/intents/superseded/itd-17-model-effectiveness-tracking.md @@ -1,5 +1,6 @@ --- id: itd-17 +superseded_by: itd-2609221009495079 slug: model-effectiveness-tracking spec_id: null kind: standalone @@ -12,6 +13,9 @@ severity: minor # Pick the Right Oracle for the Job, Automatically +> **Superseded by itd-2609221009495079** on 2026-09-22, on the product thinker's ruling after an independent research pass: a learned per-request router for lane models is not adopted (independent leaderboards show commercial routers over-selecting expensive models and drifting toward them; the choice is opaque and attackable, which the record's rule of computed facts with a falsifier cannot carry). Model per role is the tier (itd-2609170822093401); escalation is a rule on a failed fix round; the closed-option judgements go to a decision adapter measured in a lab first. + + > **This intent is abcd's frontier mapping** — capability-aware dispatch across the configured oracle adapters and the host-delegated default, framed per Dell'Acqua et al. 2023 ("Navigating the Jagged Technological Frontier"). It observes the jagged frontier (which adapter or the host is strong for which task), dispatches capability-aware, and renders the frontier map on demand: > > - The dispatch signal is a `{task_class, agent, adapter, model_id, outcome, failure_mode_tag}` schema, with closed-enum failure-mode tags (`hallucination` / `scope_drift` / `stale_context` / `under_specification_blindness` / `format_violation`). diff --git a/.abcd/development/research/legacy-harvest.md b/.abcd/development/research/legacy-harvest.md index 16e23ff4d..37e57a865 100644 --- a/.abcd/development/research/legacy-harvest.md +++ b/.abcd/development/research/legacy-harvest.md @@ -139,7 +139,7 @@ Source: `~/.abcd/` (~1.6 GB). Disposition: drop runtime data; harvest a few patt | `audit-export.py`, `audit-query.py`, `hash-chain.py`, `sign.py`, `keygen.py`, `uuid7.py`, `jcs.py` | [`itd-16-hash-chain-merkle-audit`](../intents/drafts/itd-16-hash-chain-merkle-audit.md) | | `audit.schema.json`, `vap-record-types.json` | Same | | `spec-export.py`, `spec-import.py` | [`itd-23-spec-kit-interop`](../intents/drafts/itd-23-spec-kit-interop.md) | -| `model-effectiveness.json`, `model-scorecard.jsonl` | [`itd-17-model-effectiveness-tracking`](../intents/drafts/itd-17-model-effectiveness-tracking.md) — referenced as v0 empirical seed; the tracker resets and rebuilds | +| `model-effectiveness.json`, `model-scorecard.jsonl` | [`itd-17-model-effectiveness-tracking`](../intents/superseded/itd-17-model-effectiveness-tracking.md) — referenced as v0 empirical seed; the tracker resets and rebuilds | ### Archive (move outside active tree, don't delete, don't harvest into plugin) @@ -279,7 +279,7 @@ If the first phase starts feeling too heavy, the candidates for moving to a late [itd-1]: ../intents/disciplines/itd-1-acceptance-gates.md "itd-1 — Acceptance gates" [itd-3]: ../intents/shipped/itd-3-modular-rules-loader.md "itd-3 — Modular rules loader" [itd-7]: ../intents/drafts/itd-7-rp-workspace-portability.md "itd-7 — RP workspace portability" -[itd-17]: ../intents/drafts/itd-17-model-effectiveness-tracking.md "itd-17 — Model effectiveness tracking (a later phase)" +[itd-17]: ../intents/superseded/itd-17-model-effectiveness-tracking.md "itd-17 — Model effectiveness tracking (a later phase)" [itd-23]: ../intents/drafts/itd-23-spec-kit-interop.md "itd-23 — Spec Kit interop (a later phase)" [itd-24]: ../intents/planned/itd-24-reflect-command.md "itd-24 — /abcd:reflect command (a later phase)" [carl]: https://github.com/ChristopherKahler/carl "CARL — Context Augmentation & Reinforcement Layer" diff --git a/.abcd/development/research/notes/2026-08-15-decomposition-calibration.md b/.abcd/development/research/notes/2026-08-15-decomposition-calibration.md index 0b37fd4fd..37e601f73 100644 --- a/.abcd/development/research/notes/2026-08-15-decomposition-calibration.md +++ b/.abcd/development/research/notes/2026-08-15-decomposition-calibration.md @@ -1965,3 +1965,34 @@ Per hand-run, append: clarification was taken as the answer and read back. The maintainer sweep is the first record of the day captured explicitly *not* to be fixed in the session that ruled it. + +### Run: Jev, OpenRouter and the allowlist (2026-09-22, product thinker's interview after a research pass) + +- **Proposal (as received):** "consider Jev as a new routing model (e.g. via + openrouter); SOTA on which tasks Jev could help automate"; then "how to + integrate openrouter and ensure only certain models are accessed via it"; + then classification and library selection as use cases; then the one-time + setup at `ahoy` with the key kept out of the harness. +- **Table (as run):** + + | Part | Type | Home | Link | + | --- | --- | --- | --- | + | Provider adapters serve listed models only, under a vendor denylist | trust rule | adr-2609221009491186 + brief invariant | refines adr-25 | + | OpenRouter integration | capability | itd-2609081951381895 written and planned | builds_on the tier | + | The decision adapter, shadow as a lab, note as evidence | capability | new itd-2609221009495079 | builds_on the lab, the adapter, the tier; supersedes itd-17 | + | Escalation on a failed fix round | rule | a decision on itd-2609170822093401 | | + | The one-time setup with three key homes | capability | criteria on itd-2609081951381895 through itd-63's mode | builds_on itd-63 | + | Model per role, the pick, consistency | not routing | no record | | + | itd-17 (a learned router) | RETIRE | superseded by the decision adapter | | + +- **Verdict:** SPLIT; the research pass changed the proposal's own framing + (Jev is a typed-decision model, not a router) before any question was + asked, and the human's questions widened the adapter's judgement list to + the library and the memory store. +- **Routing survived?** Yes, with one move: the shadow run's evidence was + first offered as a run-record rate and the human routed it through the lab + verb filed the night before, which made the research note the evidence + path rather than a new one. +- **Notes:** the human corrected the vendor mid-pass ("from typescript"), and + the pass resolved it as a near-homophone with a primary source rather than + guessing; a wrong vendor would have routed the whole proposal wrongly. diff --git a/.abcd/development/research/notes/2026-09-22-ideate-abcd-operator-console.md b/.abcd/development/research/notes/2026-09-22-ideate-abcd-operator-console.md new file mode 100644 index 000000000..a47d77f44 --- /dev/null +++ b/.abcd/development/research/notes/2026-09-22-ideate-abcd-operator-console.md @@ -0,0 +1,79 @@ +# Ideate verdict — abcd-operator-console + +**Verdict: reframed.** Recorded on 2026-09-22 by abcd's idea-admission protocol — +primary-source research, a grill against the existing record, and an +independent adversarial review. This record exists so the idea is not +re-litigated: it stands whether the idea lived or died. + +## The idea + +A control programme and monitor for abcd: a GUI application (desktop or local web) through which a product thinker configures abcd (providers, credentials, the model tier per role, the pace, which judgements run where) and monitors it (the Now / Next / Later status, lanes in flight, the run record, the badge state, owed reviews, the ledger), for one or many abcd-managed repositories, with abcd itself running as the binary alone and calling whatever harnesses are available (claude -p, opencode, others) — the ultimate goal being that a person can manage their repositories outside any harness, from this programme, while still working inside their preferred harness when they choose. The working name 'abcd CP/M' is a joke on Digital Research's mark and cannot be used; the idea needs a name of its own. + +## Leg 1 — Primary-source research + +Every load-bearing claim checked against its primary source, never a +secondary citation. + +| Claim | Primary source | Finding | +|---|---|---| +| Claude Code can be driven headlessly by another program with structured output and non-interactive permissions | https://code.claude.com/docs/en/headless | verified | +| Without --bare, a claude -p run executes the project's hooks and MCP servers with no trust dialog | https://code.claude.com/docs/en/headless | verified | +| opencode can be driven headlessly, and offers a server with HTTP, SSE and an explicit permission endpoint | https://opencode.ai/docs/server/ | verified | +| OpenAI Codex can be driven headlessly, and its app-server speaks JSON-RPC with approvals as server-initiated requests | https://learn.chatgpt.com/docs/app-server | verified | +| At least one further harness (Gemini CLI, Cursor CLI, Aider) offers a headless mode a Go program could call | https://geminicli.com/docs/cli/headless/ | verified | +| A local-web GUI over the Go binary needs no new dependency (net/http plus embed), while every desktop shell costs cgo or a second toolchain | https://pkg.go.dev/embed | verified | +| Existing agent control planes manage sessions and diffs; none configures a policy layer above the harness (model tier per role, pace, where judgements run) | https://github.com/andyrewlee/awesome-agent-orchestrators | verified | +| A GUI control plane improves outcomes over a CLI plus a status page | https://arxiv.org/abs/2607.01418 | unverifiable | +| The CP/M trademark registration is live | https://tsdr.uspto.gov/statusview/sn73149955 | falsified | + +## Leg 2 — Record grill + +Does the brief, an intent, an ADR, or a principle already cover, +contradict, or supersede this idea? Every hit is cited by record id, and +every id resolved in this repository when the verdict was recorded. + +| Record | Relation | Note | +|---|---|---| +| itd-2609201916151817 | contradicted | Decision 5 records the process driver as an opt-in reversal of the host-delegated boundary, confirmed for the opt-in path only; the idea made harness-less operation the destination. The loop is also non-resident (every invocation does one step and exits), so nothing is in flight between steps for a monitor to show. | +| itd-2609201916056194 | covered | The command-line runner the programme would drive; a draft whose unreachable path falls back to the host, which the harness-less goal removes. | +| itd-22 | covered | Any harness through an adaptor over one seam; the programme is a consumer of that, not a second copy. | +| itd-2609170822093401 | contradicted | Names the operator, not the product thinker, as the one who accepts the model tier, and keeps model choice and credentials with the host on the harness leg. | +| itd-167 | covered | Records the product thinker's decision of 2026-08-29 that their first stop surface is a web page they open when they have time, which supports a served pane rather than contradicting it. | +| itd-2609212103568351 | covered | The computed Now / Next / Later status the programme would render, already planned for the board and the site. | +| itd-2609061543533170 | covered | The rendered site already presents the record read-only for a managed repository. | +| itd-2609221017023290 | covered | The credential store with three homes is the configuration half; the programme edits through it rather than holding secrets of its own. | +| itd-113 | covered | The MCP front door is the third thin door and is still a draft; a fourth door is not prescribed by the architecture, only permitted. | +| itd-2609212130146198 | covered | The badge the programme would show is a status-line surface already planned. | + +## Leg 3 — Adversarial review + +Conducted fresh-context and off-policy by an evaluator that did not carry +out the research and received the idea as an artefact of unknown +authorship — the evaluator-outside-the-loop principle applied to ideas. + +- **fatal** — The named user is wrong for most of the surface: an accepted decision record bars addressing the product thinker in the facilitator's register, and the ledger, run record and owed reviews are facilitator artefacts, as is the model tier's acceptance +- **fatal** — Making harness-less operation the ultimate goal inverts the host-delegated boundary whose reversal was confirmed only as an opt-in, and removes the host fallback two planned records depend on +- **partial** — No resident process exists to show lanes in flight; a monitor needs a first long-running verb or a second driver beside the binary's +- **partial** — One or many repositories presumes an enumeration abcd cannot do: the machine stores are keyed by root commit with no map back to a checkout +- **partial** — Script-first is skipped on top of three unshipped contracts (the runner, the routing table, the credential store) +- **partial** — A desktop shell breaks the single-binary and no-new-dependency rules; local web over the standard library does not +- **partial** — The credential pane can configure only the provider leg, since the host owns its own login, and a localhost UI that accepts secrets is reachable by any local process without a per-launch token +- **partial** — The idea states no falsifier, and no evidence exists that a GUI control plane beats a CLI plus a status page +- **survived** — The record rejects a web surface for the product thinker +- **partial** — Fanning claude -p across many repositories runs each repository's hooks and MCP servers with no trust dialog +- **partial** — A fourth front door is added before the third (MCP) exists + +## Rejected alternatives + +- **A desktop application (Wails, Fyne, Tauri or Electron)** — Every shell costs cgo or a second toolchain against the single-binary boundary, for no measured outcome gain; local web over the standard library costs nothing. +- **A product-thinker control programme that configures providers, credentials, tiers and judgement placement** — An accepted decision record bars addressing the product thinker in the facilitator's register; those items are the operator's, and the product thinker's pane carries stops, the plain-language verdict and the pace. +- **Harness-less operation as the destination, with no host in the picture** — The host-delegated boundary's reversal was confirmed as an opt-in only, and the runner's fallback lands on the host; ruled instead that when abcd runs as the binary with no host session, the fallback is a configured host of the person's choosing, and every fallback is recorded. +- **Many repositories from the start** — The machine stores are keyed by the repository's root commit with no map back to a checkout; multi-repository waits for a registered-checkout store under the user-level home. +- **Building the console before the runner and routing intents ship** — Script-first: the console's contract is exactly what those seams are still settling, and the rendered site plus the JSON the CLI emits are the script rung that discovers it. +- **Keeping the working name CP/M** — The trademark registration is dead, so the objection is not legal, but the name reads as a 1970s operating system to the audience and names nothing about what the thing does. + +## What follows + +The idea as posed does not survive, but the reframing recorded above +does. Any graduation to a draft intent carries the reframing, not the +original wording. diff --git a/.abcd/development/research/notes/2026-09-22-jev-and-model-routing-sota.md b/.abcd/development/research/notes/2026-09-22-jev-and-model-routing-sota.md new file mode 100644 index 000000000..365981df0 --- /dev/null +++ b/.abcd/development/research/notes/2026-09-22-jev-and-model-routing-sota.md @@ -0,0 +1,72 @@ +# Jev and model routing: the research pass behind adr-2609221009491186 and itd-2609221009495079 + +Recorded 2026-09-22 from an independent research pass (web only, primary +sources where they exist), run before the product thinker's rulings of the +same night. It is the SOTA record for the API adapter (itd-2609081951381895), +the decision adapter (itd-2609221009495079), the model tier's escalation +rule (itd-2609170822093401) and the retirement of itd-17. + +## What Jev is + +Jev is TypeSafe AI's "System One" model (early access 2026-09-15; on +OpenRouter as `typesafe/jev-1.13`, alias `typesafe/jev-latest`, at $0.042 per +million input tokens, 32K context, text only). It is neither a text model nor +a router: it takes a state and a typed question and returns a value with a +calibrated probability through three primitives, choose one of N, score +against a rubric, yes or no, in under a second. It cannot generate text and it +cannot abstain; its founder conceded publicly that it "can still emit a +completely wrong valid value". Weights are closed, hosting is single-vendor +and waitlisted. Independent evidence is thin: one forty-case routing test (all +correct, middle-tier confidence 0.57 to 0.67); the vendor's speed and cost +figures are measured by the vendor on its own workflows. The name resolves to +this one thing; the product thinker's "from TypeScript" was a near-homophone +of TypeSafe. + +## Routing, 2024 to 2026, in one table + +| System | Decides | Signal | Evidence | Failure mode | +| --- | --- | --- | --- | --- | +| RouteLLM (Berkeley, 2024) | strong or weak model, before inference | preference data, learned classifiers | paper: over 2x cost cut at held quality | one pair of models | +| Arch-Router (2025) | which user-defined policy a query matches | a 1.5B classifier | vendor-authored paper | intent, not difficulty | +| OpenRouter Auto (2026) | model per request in five cost tiers | task classifier ranked by market spend | vendor-run | popularity is not correctness; not reproducible | +| Provider routing, LiteLLM | which provider or deployment | price, throughput, uptime | docs | not a quality decision | +| Not Diamond, Martian, Unify | model per request | learned on eval data | vendor-quoted; RouterArena ranks Not Diamond twelfth for over-selecting expensive models | opacity | +| Degenerate convergence (2026) | analysis | | routers collapse to the priciest model as budget rises | objective mismatch | +| Rerouting LLM routers (2025) | attack | | query-independent gadgets force upgrades | an attackable control plane | +| Cascades (TMLR 2026; RouteNLP) | escalate after a cheap attempt | confidence or a judge | 40 to 85 per cent cost cut at 96 to 100 per cent quality on structured tasks | needs a calibrated signal | + +Vendor guidance converges: classify then dispatch, "build the right system, +not the most sophisticated" (Anthropic); the host's own mechanism is static +per role (Claude Code's `model:` frontmatter and the plan-with-opus, +execute-with-sonnet split); prototype on the most capable model and downgrade +where evaluations hold (OpenAI). + +## The mapping the rulings rest on + +- **Model per role**: not routing; a configuration table. The role name is + the classifier. Learned routers lose on opacity and on the measured drift + toward expensive models. Kept as the tier. +- **Escalation inside a lane**: a cascade whose signal abcd already owns, the + failed fix round; a rule, recorded as a fact. No predicted difficulty beats + ground truth. +- **The closed-option judgements**: the one place a decision model fits; an + adapter behind the host's own interface, measured in shadow as a lab before + it decides anything. Constraints: 32K (retrieve first), no abstention (never + for a judgement whose right answer is often unknown), single vendor. +- **What to build next**: nowhere in the literature a routing problem; a + scoring problem; the pick stays computed. A rubric score may become one + component later. +- **Corpus consistency**: batch classification; lint first, typed verdicts on + the residue, the strong model on what is flagged. + +## Not adopted + +A learned per-request router for lane models; OpenRouter Auto as a role's +model; provider routing as a quality lever; Jev as a default judge. + +## Where no evidence was found + +An independent, methodology-disclosed benchmark of Jev's calibration; third- +party measurements of the commercial routers beyond RouterArena; any framing +of backlog selection as routing; a study of cost-escalation cascades inside +multi-round coding-agent fix loops. diff --git a/.abcd/development/specs/open/spc-2609221011151661-abcd-s-typed-judgements-can-run-through-a-decision-adapter.md b/.abcd/development/specs/open/spc-2609221011151661-abcd-s-typed-judgements-can-run-through-a-decision-adapter.md new file mode 100644 index 000000000..bb2f2ec85 --- /dev/null +++ b/.abcd/development/specs/open/spc-2609221011151661-abcd-s-typed-judgements-can-run-through-a-decision-adapter.md @@ -0,0 +1,45 @@ +--- +id: spc-2609221011151661 +slug: abcd-s-typed-judgements-can-run-through-a-decision-adapter +intent: itd-2609221009495079 +origin: researcher-authored +production_mode: hand-written +--- +# abcd-s-typed-judgements-can-run-through-a-decision-adapter + +## Summary + +The design record for itd-2609221009495079: the decision interface, the shadow lab, the harvest and the per-type switch. + +## Scope + +1. **The interface** (`internal/core/decide/judge.go`, distinct from the ADR verb's package): `Judge(state, question) (value, prob)`, three question kinds; the host implementation renders the question as a prompt and parses the answer with probability 1 (criteria 1, 6). +2. **The callers**: each judgement-shaped step names its type and calls the interface; the registry of types is the closed list in scope (criterion 1). +3. **Shadow**: the router reads the configuration per type (`host` | `shadow:` | ``); shadow runs both and appends the pair to `~/.abcd/lab//pairs.jsonl` (criteria 1, 5). +4. **Harvest**: `abcd lab harvest` gains the agreement report for a pairs file (criterion 2). +5. **Turned on**: the router calls the adapter and records the route; fallback with a receipt (criterion 3). +6. **Allowlist**: the resolver of adr-2609221009491186 runs before any call (criterion 4). + +## Out of scope + +- Generative steps; learned routers; the pick. + +## Approach + +One interface, one router with three modes, one adapter over the API adapter's client; the lab verb records and harvests, so the evidence path is the lab's, not a new one. + +## Footprint + +- packages: internal/core/decide, internal/core/lab, internal/adapter/openaiapi, callers across capture, intent, memory, lint +- tests: the router's three modes with a fake adapter; the pairs file; the harvest's rates; the window skip; the fallback receipt + +## How the criteria are satisfied + +| Criterion | Where | +| --- | --- | +| 1 shadow records pairs | scope 3 | +| 2 harvest and the note | scope 4 | +| 3 turned on, fallback | scope 5 | +| 4 allowlist | scope 6 | +| 5 window skip | scope 3 | +| 6 one interface | scope 1, 2 | diff --git a/.abcd/development/specs/open/spc-2609221011153746-abcd-ships-an-openai-compatible-api-oracle-adapter-the-first.md b/.abcd/development/specs/open/spc-2609221011153746-abcd-ships-an-openai-compatible-api-oracle-adapter-the-first.md new file mode 100644 index 000000000..27469c662 --- /dev/null +++ b/.abcd/development/specs/open/spc-2609221011153746-abcd-ships-an-openai-compatible-api-oracle-adapter-the-first.md @@ -0,0 +1,47 @@ +--- +id: spc-2609221011153746 +slug: abcd-ships-an-openai-compatible-api-oracle-adapter-the-first +intent: itd-2609081951381895 +origin: researcher-authored +production_mode: hand-written +--- +# abcd-ships-an-openai-compatible-api-oracle-adapter-the-first + +## Summary + +The design record for itd-2609081951381895: the OpenAI-compatible API adapter with per-provider allowlists and the vendor denylist (adr-2609221009491186). + +## Scope + +1. **Configuration** (`internal/core/oracle/config.go`): the provider blocks, the bundled denylist, the resolver that validates every role and judgement route at read time and returns the refusal (criteria 1 to 3). +2. **The adapter** (`internal/adapter/openaiapi`): one client over the chat completions protocol, the request rendered from the same brief the host gets, the response validated against the same output contract, the transcript captured (criterion 1). +3. **The credential**: by name from the machine configuration or the environment; a name that resolves to nothing is a refusal (criterion 4). +4. **The record**: provider, requested and reported model per call in the run record (criterion 5). +5. **The setup** (`ahoy` gap → itd-63's mode): detect no provider block; explain; on yes ask the home (three choices, keychain recommended in prose); write the key by the chosen method (`external`: store the variable or config name only; `abcd`: `~/.abcd/credentials.json` mode 0600; `keychain`: the platform keychain under abcd's service name via the `security` command on macOS and the secret-service API on Linux, the configuration holding the name); write the provider block with the first allowlist; one verification call (criteria 7, 8). +6. **Review**: security reviewer on the lane (criterion 9). + +## Out of scope + +- Provider routing; learned routers; the tier's judgement. + +## Approach + +The adapter implements the same validator/runner interface the host path and the RepoPrompt route (itd-6) implement, so the loop does not know which ran; OpenRouter and a local server differ only in configuration. + +## Footprint + +- packages: internal/core/oracle, internal/adapter/openaiapi, internal/surface/cli +- tests: the resolver's refusals (unlisted, denylisted, no key); the call against a fake server; the record fields + +## How the criteria are satisfied + +| Criterion | Where | +| --- | --- | +| 1 the call; unconfigured unchanged | scope 2 | +| 2 unlisted refused at read | scope 1 | +| 3 denylist wins | scope 1 | +| 4 key by name | scope 3 | +| 5 the record | scope 4 | +| 7 ahoy explains and offers | scope 5 | +| 8 three homes, keychain in prose, nothing in the harness | scope 5 | +| 9 security review | scope 6 | diff --git a/.abcd/development/specs/open/spc-2609221017544877-abcd-keeps-every-external-credential-the-same-way-one.md b/.abcd/development/specs/open/spc-2609221017544877-abcd-keeps-every-external-credential-the-same-way-one.md new file mode 100644 index 000000000..7fd2089ac --- /dev/null +++ b/.abcd/development/specs/open/spc-2609221017544877-abcd-keeps-every-external-credential-the-same-way-one.md @@ -0,0 +1,43 @@ +--- +id: spc-2609221017544877 +slug: abcd-keeps-every-external-credential-the-same-way-one +intent: itd-2609221017023290 +origin: researcher-authored +production_mode: hand-written +--- +# abcd-keeps-every-external-credential-the-same-way-one + +## Summary + +The design record for itd-2609221017023290: the credential store, its three homes, the walkthrough and the single reader (adr-2609221017021499). + +## Scope + +1. **The package** `internal/core/credential`: `Resolve`, `Set`, the three home implementations (`external` resolves an environment variable or reads a named field of a named tool's configuration file; `abcd` reads and writes `~/.abcd/credentials.json` at 0600 through the atomic writer; `keychain` shells to the platform's keychain command on macOS and the secret-service tool on Linux, the configuration holding the name) (criteria 1, 3). +2. **The walkthrough** as a function itd-63's mode calls with the service's explanation and verification call; the CLI asks on the terminal, the plugin page through the host's question tool (criterion 2). +3. **The scanner** on the write path; a tracked-path write refused (criterion 3). +4. **The readers** switched to `Resolve`; a test greps adapters for direct environment or file reads of secret-shaped names (criterion 4). +5. **The record** names only names (criterion 5). + +## Out of scope + +- Rotation; sharing; the host's own credentials. + +## Approach + +The API adapter and the site setup land after this or in the same cut, reading through it from the first commit; the walkthrough is itd-63's mode with a credential step, not a second wizard. + +## Footprint + +- packages: internal/core/credential, internal/core/ahoy, internal/adapter/* +- tests: each home over fixtures; the refusal on an unset name; the tracked-path refusal; the reader grep; the record + +## How the criteria are satisfied + +| Criterion | Where | +| --- | --- | +| 1 unset refuses, no call | scope 1, 3 | +| 2 walkthrough with three homes | scope 2 | +| 3 no value in tree or harness; scanned | scope 3 | +| 4 one reader | scope 4 | +| 5 names only in the record | scope 5 | diff --git a/.abcd/development/specs/open/spc-2609221533057881-abcd-runs-a-delegated-agent-through-a-command-line-model-run.md b/.abcd/development/specs/open/spc-2609221533057881-abcd-runs-a-delegated-agent-through-a-command-line-model-run.md new file mode 100644 index 000000000..10aab799e --- /dev/null +++ b/.abcd/development/specs/open/spc-2609221533057881-abcd-runs-a-delegated-agent-through-a-command-line-model-run.md @@ -0,0 +1,47 @@ +--- +id: spc-2609221533057881 +slug: abcd-runs-a-delegated-agent-through-a-command-line-model-run +intent: itd-2609201916056194 +origin: researcher-authored +production_mode: hand-written +--- +# abcd-runs-a-delegated-agent-through-a-command-line-model-run + +## Summary + +The design record for itd-2609201916056194: one runner interface, a route per role, a configured fallback and the fallback as recorded intel. + +## Scope + +1. **The interface** (`internal/core/runner`): `Run(role, brief, contract) (answer, transcript, err)`; the host implementation is the existing sub-agent dispatch, so the loop calls one thing (criteria 1, 2). +2. **The adapters**: `claude` (print mode, bare, structured output, allowed tools from the role's contract) and `opencode` (run mode, or the server's session and prompt endpoints where a server is already up); each parses the harness's structured events into the one answer shape and writes the transcript to abcd's store (criteria 1, 6, 7). +3. **The route**: `roles..runner` through the layered resolver; unset is host (criterion 2). +4. **The fallback**: one place decides it (runner error, absent binary, non-zero exit, unparsable answer); it calls the host implementation, or `runner.fallback_host` when no host session is present, and appends a receipt to the run's state; the summary counts them (criteria 3, 4). +5. **The allowlist** resolved before launch (adr-2609221009491186) (criterion 5). +6. **Review**: security reviewer on the lane (criterion 8). + +## Out of scope + +- The tier's choice; installing a harness; a served console. + +## Approach + +The runner interface is the same shape the validator stage already defines for reviewers, so a role's route is one lookup; the fallback is one branch with one receipt writer, which is what makes the count trustworthy. + +## Footprint + +- packages: internal/core/runner, internal/core/implement, internal/surface/cli +- tests: each adapter against a fake harness binary; the fallback receipt on each failure kind; the count in the summary; the allowlist refusal; the bare flag asserted in the launch + +## How the criteria are satisfied + +| Criterion | Where | +| --- | --- | +| 1 same brief, contract, store | scope 1, 2 | +| 2 unset unchanged | scope 3 | +| 3 fallback and receipt | scope 4 | +| 4 counts in the summary | scope 4 | +| 5 allowlist | scope 5 | +| 6 bare flag | scope 2 | +| 7 receipts differ only in route | scope 1, 2 | +| 8 security review | scope 6 | diff --git a/.abcd/work/DECISIONS.md b/.abcd/work/DECISIONS.md index da90881ed..01eeb98ea 100644 --- a/.abcd/work/DECISIONS.md +++ b/.abcd/work/DECISIONS.md @@ -2500,3 +2500,6 @@ together (the script's header says why there is no escape hatch). - 2026-09-15 — GHSA-4q78-ccfv-f374 (iss-2609012039102770) is closed by OPTION B, ruled by the maintainer at an interactive question: bind the cache to a record the environment does not choose alone. The owned PATH-copy promotion re-verified the cache only against the `binary-meta` beside it, and `CLAUDE_PLUGIN_DATA` is taken from the environment as given, so whoever chose the directory wrote both the bytes and the record that "verified" them — reproduced at v0.7.0 as a one-byte file installed 0755 as `~/.local/bin/abcd` with provenance recorded. Now the bootstrap, the one process holding the harness's real data dir that has just established manifest trust for the cache (an authenticated cache hit, or a fresh download verified against the same-origin manifest), writes `~/.abcd/cache-attestation` — `data_dir`, the manifest-authenticated `binary_sha256`, `cache_trust=manifest`, `attested_at`; 0600, temp-and-rename, beside `path-entry` — and `ahoy install` promotes a cache only when that record names the directory, the co-located record carries the attested hash, and the artefact hashes to it, whichever route (environment or the root's `.data-dir` stamp) named the directory; detection offers the heal on the same predicate. An offline run neither writes nor rewrites the attestation, so a cache provisioned offline waits for a networked session before it reaches PATH, said out loud. Why B: the trust floor moves from a value the environment supplies to a write into the caller's own home, which adr-46 decision 4 already treats as the ownership root, so the attestation grants nothing that authority did not hold and costs `ahoy install` no network (adr-38 stands). Rejected: A (a manifest GET on a disk-only verb, re-fetching what the session already proved); C (a documented residual — weaker than it reads, since a harness honouring a committed settings file's environment block lets a hostile checkout set the variable, and the owned-copy claim is what the hook shims trust); the mechanical partial of cross-checking against the plugin-root binary (breaks dogfood installs whose root binary is a local build); and a terminal rung through the attestation alone (it would heal a dogfood checkout's stable symlink into a release copy). adr-46 is superseded by adr-2609151706587280 (never amended, always superseded; retained because its numbered decisions are cited), spc-35 Design 2 step 1 and Design 3 are revised in place and dated, and brief invariant 12 gains the clause. - 2026-09-09 — The headline product is settled (maintainer, closing the press release's "Product framing after adr-35" open question, both halves). abcd helps a product thinker realise an intent as a high-fidelity prototype or demonstrator, carrying the why from idea to shipped reality: the identity block's story, the one the README strapline and the roles page already tell and the one the roles and product-thinker-surface decisions on the design branch (numbered 0055 and 0056 there, re-minted at merge) are built on. The lifeboat is a key capability of that product rather than its headline, and its widening from whole repositories to a single feature, a lab session or an abandoned worktree enters the press release under the not-yet-real marker, as intention rather than commitment. `disembark probe` is a user-facing command and keeps its place in the press release's scope list. Rejected: the rescue story as headline (it would re-pin the identity block and every surface held to it); adr-35's "read any repository for its theory" as headline (it is the probe's own promise, now one capability among the surfaces); deferring (the audit of the press release against delivered reality had been blocked on this question since adr-35). - 2026-09-21 — Phases and milestones are retired, and the word roadmap with them (product thinker, at an interactive interview after an independent research pass; adr-2609212115255771 supersedes adr-9). Sequencing is dependencies plus the lifecycle shelves, rendered as a Now / Next / Later status block on the `abcd` board and the site (itd-2609212103568351), never stored; the checkpoint is the derived release plus each intent's acceptance criteria, with an optional `target_release` the cut reports and moves forward (itd-2609212103572513); the unit below a spec is the step, a section not a family (itd-2609212103565953); an issue carries no spec by design; the batch is the run's internal order; one page maps the families (itd-2609211913453478). itd-24 becomes release retrospectives; itd-34 drops its phase rule. Same interview: `build` is the verb a person types and `implement` the loop (itd-2609201916151817 decision 8), the loop takes an issue key (decision 10), only the loop writes a verdict (decision 9); the person's command list is grouped with an agents block behind `--agent` (itd-146), every verb opens with a does/writes/refuses sentence (itd-2609212113220149), modes become flags and five checks one lint (itd-2609212130136102, breaking); the status-line badge is guarded and reset (itd-2609212130146198); a new capture or draft is matched and linked at filing, never refused (itd-2609212137116617); abcd's text names the product thinker or the technical facilitator and never the maintainer (itd-2609212137129937, captured for the run, no sweep today); the lab conventions become `abcd lab` (itd-2609212137128014); a managed repository's site goes live by one verb behind a provider seam (itd-2609061543533170). +- 2026-09-22 — Model routing, after an independent research pass (product thinker, at an interactive interview). Jev (TypeSafe AI, on OpenRouter) is a typed-decision model, not a router: it fits abcd's closed-option judgements and nothing generative. Rulings: a provider adapter serves only the models it lists under a vendor denylist no listing overrides, everything else on the host (adr-2609221009491186); the OpenAI-compatible API adapter is planned with OpenRouter as configuration, a one-time walkthrough at ahoy and the key kept out of the harness (itd-2609081951381895); a decision adapter for the typed judgements runs in shadow as an abcd lab and a research note is the evidence a ruling turns a judgement type on with (itd-2609221009495079); escalation inside a lane is a rule on a failed fix round, recorded on the model tier (itd-2609170822093401); model per role, the pick and consistency are not routing and stand as ruled; itd-17's learned router is superseded. Every external credential abcd holds goes through one store with three homes the person chooses (adr-2609221017021499, itd-2609221017023290); the API adapter and the site setup read through it. The research is filed as research/notes/2026-09-22-jev-and-model-routing-sota.md. +- 2026-09-22 — ideate: abcd-operator-console — verdict reframed. The idea, the three legs, and the rejected alternatives: .abcd/development/research/notes/2026-09-22-ideate-abcd-operator-console.md +- 2026-09-22 — The control-programme idea is REFRAMED, not adopted as put (product thinker, after the ideate gauntlet; research/notes/2026-09-22-ideate-abcd-operator-console.md). Two kill attempts were fatal to the idea as stated: it named the product thinker as the configurer of operator-register material, which adr-2609151528057260 bars, and it made harness-less operation the destination, inverting the host-delegated boundary whose reversal itd-2609201916151817 records as opt-in only. The reframing, adopted: an operator console SERVED BY THE BINARY over local web (no new dependency), which is also the resident process that hosts the opt-in process driver, with a separate product-thinker pane in their own register (open stops, the plain-language verdict, the pace) — the web page itd-167 already decided on; configuration stays operator-side; many repositories waits for a registered-checkout store; a localhost surface that accepts secrets needs a per-launch token; the whole sequenced after the runner and routing intents; and it must carry a falsifier, since no evidence exists that a GUI control plane beats a CLI plus a status page. Ruled with it: where abcd runs as the binary with no host session, the fallback is a HOST THE OPERATOR CONFIGURED (their main connector), never nothing; and EVERY fall back to the host is recorded — a receipt per event and a count per runner and per role in the run record — as intel on what to improve (itd-2609201916056194, planned; itd-22 widened so that "any harness" includes none).