Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1fd70c6
docs: name the decision adapter spec's footprint as real paths
REPPL Oct 29, 2026
fc68014
feat: read a judgement type's route as host, shadow or on
REPPL Oct 29, 2026
ade43b0
feat: record decision adapter shadow pairs in a lab and harvest their…
REPPL Oct 29, 2026
6b8f11b
feat: route typed judgements through one interface, host or decision …
REPPL Oct 29, 2026
966013a
docs: drop embark's lesson ranking from the decision adapter's judgem…
REPPL Oct 29, 2026
7b30d0e
docs: describe the decision adapter's routes, shadow pairs and harves…
REPPL Oct 29, 2026
443f0f3
docs: say the drain's decision routing is built but not yet called
REPPL Oct 29, 2026
26f0e61
feat: route the drain's judgement through the decision adapter
REPPL Oct 29, 2026
3909542
fix: keep the judge package's surface to what production code reaches
REPPL Oct 29, 2026
f29b0ae
Revert "docs: drop embark's lesson ranking from the decision adapter'…
REPPL Oct 29, 2026
8f13330
feat: register embark's lesson ranking as a decision adapter judgemen…
REPPL Oct 29, 2026
5177c29
feat: turn a judgement type on only from the person's own configuration
REPPL Oct 29, 2026
6c7d2e5
fix: spell a shadow judgement route with its prefix on the providers …
REPPL Oct 29, 2026
f5a656b
fix: print n/a agreement for a type with nothing compared in lab harvest
REPPL Oct 29, 2026
89b42f4
docs: say the lab pages' shadow pairs wait on a router call
REPPL Oct 29, 2026
613437e
fix: offer a judgement type the shadow spelling after ahoy connect
REPPL Oct 29, 2026
3ef1924
docs: list a repository's turned-on judgement route among skipped routes
REPPL Oct 29, 2026
5f2e03d
fix: read a lab's shadow pairs a line at a time in lab harvest
REPPL Oct 29, 2026
9ef569a
feat: route the drain's judgement through the decision adapter
REPPL Oct 30, 2026
6d1e887
docs: say the drain's judgement goes through the decision adapter
REPPL Oct 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .abcd/development/brief/04-surfaces/31-lab.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,31 @@ Coverage grades each section grounded, partial or blank, the lifeboat's
vocabulary. The harvest files nothing, and claims no cost the lab did not
measure.

A lab that collected the decision adapter's shadow pairs
(itd-2609221009495079) gets one more section. When a judgement type is
configured for shadow (`oracle.judgements.<type>` set to
`shadow:<provider>/<model>`, [`03-configuration.md`](../05-internals/03-configuration.md)),
the host decides and the adapter answers beside it. Each pair is appended to
`pairs.jsonl` in the newest lab home minted for the repository: the host's
answer, the adapter's answer, the adapter's probability, the state's SHA-256 and
size, and the model that answered, or why it did not. That is the only file the
adapter writes, and it is never written into the repository. The drain's
judgement before a lane opens (`eligibility-residual`) is today the one step
that goes through the router, so it is the one type whose pairs are kept;
every other type stays on the host whatever its route says
([`03-configuration.md`](../05-internals/03-configuration.md)).
Here the harvest
computes something rather than only assembling prose: from `pairs.jsonl` it
counts, for each judgement type, the pairs, the pairs compared (the adapter
answered), the pairs that agreed and the pairs skipped (the state was larger
than the model's window, or the call failed). It reports the agreement rate,
and the compared and agreed counts in four probability bands (below 0.50,
0.50–0.70, 0.70–0.90, 0.90 and above). Lines that do not parse are counted
apart and never guessed at. The section ends with the line a research note
filed from it carries, naming the lab home and the count. That note is the
evidence a ruling turns a type on with. A lab with no pairs has no such
section.

## Halt and record

A gate refusal halts the lab and is recorded, never adapted around. When the
Expand Down
39 changes: 39 additions & 0 deletions .abcd/development/brief/05-internals/02-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,45 @@ is sent to a provider. No test reaches a real provider: the client and every
dispatching verb are exercised end to end against a fake on the loopback
address, and the key is built at run time.

### The decision adapter — typed judgements, measured in a lab first

The decision adapter (itd-2609221009495079, `core/decide/judge`) answers abcd's
closed-option judgements with a value and a probability. It has no generative
step. Every judgement goes through one interface, `(state, typed question) →
(value, probability)`. A question is one of three kinds: choose one of N,
score on a scale, or yes/no. Its type is one of a closed registry
([`03-configuration.md`](03-configuration.md) lists it). The host's own
judgement implements the interface at probability 1, and so does the decision
model reached through the API adapter above, so the two are compared like for
like. The adapter renders the same prompt the host is shown, sends it through
`APIConfig.Call` (which admits the target against the allowlist and the
denylist again and resolves the key by name), and reads back one JSON object
with a `value` among the question's choices and a `probability` from 0 to 1.
The model cannot abstain, so any other answer is not an answer.

A router switches each type by configuration alone, so no caller changes when
a type moves. With `host`, or nothing set, the host decides. With
`shadow:<provider>/<model>`, the host decides and the adapter's answer is
recorded beside it, as a pair in the newest lab home's `pairs.jsonl`, never in
the repository. A state larger than the model's 32K window is recorded as
skipped with its size. With a bare `<provider>/<model>`, the adapter decides,
and the run record names the route and the model the provider reported; only
the person's own `~/.abcd.noindex/config.json` turns a type on, and the same
route in a repository's file is skipped so the machine's route, or the host,
applies ([`03-configuration.md`](03-configuration.md)). An
adapter that cannot answer leaves the decision to the host, with a fallback
receipt saying why. The allowlist refusal of adr-2609221009491186 happens when
the configuration is read, before any call. `abcd lab harvest` turns the pairs
into agreement per type, with counts and probability bands, and the research
note filed from it is the evidence a ruling turns a type on with. The first
candidate is `typesafe/jev-1.13` on OpenRouter's list. The live paid check
against it is owed to a person (iss-2609291925398978). The drain's judgement
(`eligibility-residual`) is the one type whose step calls the router: the
drain's move asks it in each mode. No other type's step calls the router yet,
so every other judgement stays on the host whatever its route says. The tests
drive the router, the pairs file, the harvest and the drain's moves in each
mode with a fake adapter and a temporary home.

### Reviewer routes — the command-line runner, and RepoPrompt over MCP

A build lane's ruthless and security reviews, which the validate stage hands
Expand Down
58 changes: 56 additions & 2 deletions .abcd/development/brief/05-internals/03-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ rather than skipped:
},
"denylist": ["openai/*"], // optional, none bundled; repo or machine
"roles": { "scribe": "openrouter/typesafe/jev-1.13" }, // an agent in the roster; a keyed
"judgements": { "duplicate-match": "openrouter/typesafe/jev-1.13" } // provider's routes: machine only
"judgements": { "duplicate-match": "shadow:openrouter/typesafe/jev-1.13" } // provider's routes: machine only;
// host | shadow:<provider>/<model> | <provider>/<model>
}
}
```
Expand Down Expand Up @@ -335,6 +336,57 @@ rather than skipped:
one, applies in its place (ruling CD2 of 2026-09-29). The same fault in
`~/.abcd.noindex/config.json` is refused, because that file is the person's own and a
route they set is never dropped silently.
- **A judgement type's route has a mode** (itd-2609221009495079). An
`oracle.judgements.<type>` value is `host`, `shadow:<provider>/<model>` or
`<provider>/<model>`. `host`, or no route, keeps the type on the host. A
repository's `host` holds against the machine's route to the same name,
since it only keeps work on the host. Only the person's own configuration
turns a type on: a bare `<provider>/<model>` is honoured from
`~/.abcd.noindex/config.json`, the rule a role handed to a runner follows
(`runner.personal`). The same route
in a repository's `.abcd/config.json` is skipped with one diagnostic naming
the file and the route, and the machine's own route to the type applies in
its place, in its own mode, or the host when the machine sets none; so a
repository never turns a machine's shadow into on. A repository may still
set `host`, or `shadow:<provider>/<model>`, which decides nothing. In
**shadow** the host still decides.
The decision adapter is asked the same typed question over the same state
beside it and decides nothing. Each pair (the host's answer, the adapter's
answer and probability, the state's SHA-256 and size) is appended to
`pairs.jsonl` in the newest lab home the repository's lane of
`~/.abcd.noindex/lab/` holds, so `abcd lab mint` comes first. A state larger
than the model's window (32K tokens, counted high at three bytes a token) is
never sent: its pair is recorded as skipped with the size. With no lab
minted, the host still decides and no pair is kept. A bare
`<provider>/<model>` **turns the type on**: the adapter decides, and the run
record names the route and the model that answered. If the adapter cannot
answer (unreachable, failed, an answer outside the question's choices, a
state over its window), the host decides instead, with a receipt saying
why. A shadow route passes every rule a route turned on does (the
allowlist, the denylist, the keyed-provider skip, the malformed-route skip)
when the configuration is read and before any call. A role takes no mode.
The types are a closed registry: `remedy-read`, `duplicate-match`,
`unmeetable-criteria`, `eligibility-residual`, `source-classification`,
`source-selection`, `memory-check`, `ledger-category`, `ledger-severity`,
`prepass-overlap`, `present-tense-advisory`, `embark-lesson-ranking`. A
route naming anything else is
a diagnostic and is skipped. The drain's judgement before a lane opens runs
as `eligibility-residual`, a choice of `no`, `user-visible` or
`trust-rule`, and the drain's move calls the router for it
(`loop/drain_decide.go`). On the host, the default, the drain moves as it
always did. In shadow, the move records a pair once the host's or the
routed judge's answer is taken, and changes nothing else. Turned on, the
move asks the adapter once the host's request is written, before any runner
the drain's judge is routed to, and takes the answer the way a routed
judge's is taken, with the route named `decision-adapter <provider>/<model>`
in the drain's record. If the adapter cannot answer, the host's request
stands, with a fallback receipt in the drain's state beside the routed
judge's, and the adapter is not asked again for that request. No other
type's step calls the router yet. Those judgements are made in the host's
command pages, which hand nothing back to the binary, so they stay on the
host whatever their route says until their step calls the router. `abcd lab harvest` reports the agreement per type, and
the research note filed from it is the evidence a ruling turns a type on
with.
- **A route to a provider that holds a key sits on the machine alone.** Only a
route the person set up on their own machine may spend their paid key (the
product thinker's ruling AA(b) of 2026-09-29), so a repository's
Expand All @@ -352,7 +404,9 @@ rather than skipped:
provider holds a key when its block names `key`, judged from the block and
never by reading the credential store. A repository's route to a provider
whose block names no key (a local server) is admitted and wins over the
machine's per name, and a `--route` the person types is unaffected.
machine's per name, except a judgement type's route that turns the type on,
which only the machine's file may set (above), and a `--route` the person
types is unaffected.
- **A call that spends a key takes no repository settings unless the person typed its route.** A
provider leg sends the connection's defaults, then the winning routing row's
settings, then the `--route`'s. On a leg to a provider that holds a key and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ One interface, one router with three modes, one adapter over the API adapter's c

## Footprint

- packages: internal/core/decide, internal/core/lab, the OpenAI-compatible client adapter, and callers across capture, intent, memory and lint
- packages: internal/core/decide, internal/core/lab, internal/adapter/openaiapi, internal/core/oracle/config.go, internal/core/oracle/judgement_mode_test.go, internal/core/oracle/connect_guide.go, internal/core/implement/loop/drain_decide.go, internal/core/implement/loop/drain_decide_test.go, internal/surface/cli/lab.go, internal/surface/cli/lab_surface_test.go, internal/README.md, commands/lab.md, .abcd/development/brief/04-surfaces/31-lab.md, .abcd/development/brief/05-internals/02-adapters.md, .abcd/development/brief/05-internals/03-configuration.md, .abcd/development/intents/planned/itd-2609221009495079-abcd-s-typed-judgements-can-run-through.md, .abcd/work/issues/open/iss-2610262033055327-the-decision-adapter-intent-treats.md, .abcd/work/issues/resolved/iss-2610262033055327-the-decision-adapter-intent-treats.md, .abcd/work/issues/open/iss-2610262037348735-the-decision-adapter-intent-needs-lab.md, .abcd/work/issues/resolved/iss-2610262037348735-the-decision-adapter-intent-needs-lab.md
- 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ found_at: ".abcd/development/intents/planned/itd-2609221009495079-abcd-s-typed-j
remedy: "In the decision-adapter intent state that lab harvest gains a per-type agreement section computed from the adapter's recorded pairs (new scope on the shipped lab verb), or name a new sub-verb for it; the lab chapter describes a harvest that only assembles prose sections."
related_intents: [itd-2609221009495079]
finding_ref: cfk-98716f3a82450774
resolution: "lab harvest gains a per-type agreement section computed from the decision adapter's pairs.jsonl (counts, compared, agreed, skipped, rate, four probability bands, and the research note's line naming the lab home and the count); the lab chapter now describes it"
impact: internal
resolved_by:
intent: "itd-2609221009495079"
spec: "spc-2609221011151661"
---

The decision-adapter intent needs lab harvest to report agreement rates with probability bands; the lab brief's harvest only assembles prose sections from findings and probe records
Expand All @@ -24,3 +29,7 @@ A sequencing impossibility (severity minor) found by the cross-document consiste
The intent's evidence for turning a judgement type on is the agreement rates per type, with counts and probability bands, that `abcd lab harvest` produces from pairs the adapter recorded. In the lab chapter, the harvest assembles the lifeboat's sections from the INTENTION, the findings log and the probe records, and computes no statistics. The lab verbs run nothing either, because whoever runs the lab runs each probe. As the lab is written, its harvest cannot produce the figures this intent depends on.

Evidence: `.abcd/work/reviews/2026-10-26-consistency/00-summary.md`, finding 733.

## Grounds

- pursued: the harvest's agreement section is the evidence path the intent names; shown wrong if a lab holding pairs harvests without per-type counts and bands
5 changes: 3 additions & 2 deletions commands/ahoy.md
Original file line number Diff line number Diff line change
Expand Up @@ -1372,8 +1372,9 @@ adapter refuses as `oracle_api.config_refused`, naming the file and the key.
A route the configuration read skips (a repository's route to a provider that
holds a key, a repository's route that is not `<provider>/<model>` or whose
name is not a plain lower-case name, a repository's route to a model a provider
holding no key does not list, a route to a provider this machine has not
configured, or a role outside the roster) is the optional gap `oracle_api.route_skipped`, its
holding no key does not list, a repository's judgement route that turns a type
on, which only `~/.abcd.noindex/config.json` may set, a route to a provider this
machine has not configured, or a role outside the roster) is the optional gap `oracle_api.route_skipped`, its
`detail` one line per skipped route; relay each line. A skipped
repository route never displaces the machine's: where `~/.abcd.noindex/config.json`
routes the same name, that route applies.
Expand Down
17 changes: 17 additions & 0 deletions commands/lab.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,23 @@ yourself**: show the candidates and let the user decide, and note any without a
`refutation`. When a finding cites no probe record or evidence, or one that is
missing or incomplete, `gaps` names it, the exit is 1 and nothing is written.

When the lab collected the decision adapter's shadow pairs, the harvest also
carries `agreement`. A judgement type is put in shadow by setting
`oracle.judgements.<type>` to `shadow:<provider>/<model>` in
`~/.abcd.noindex/config.json`. The host keeps deciding, and each answer the
adapter gives beside it is appended to `pairs.jsonl` in the newest lab home.
Today the drain's judgement before a lane opens (`eligibility-residual`) is
the one step that goes through the router, so it is the one type whose pairs
are kept; every other type stays on the host whatever its route says, and a
lab with no pairs has no `agreement`.
For each type, `agreement` gives `pairs`, `compared` (the adapter answered),
`agreed`, `skipped` (the state was over the model's window, or the call
failed), `rate`, and the compared and agreed counts in four probability
`bands`. It ends with the `note` line, naming the lab home and the count. Show
the user the rates and the note: a research note filed from it is the evidence
a ruling turns a type on with (`<provider>/<model>`, no `shadow:`), and turning
a type on is the user's decision, never yours.

**Plan a model matrix run** in a lab whose preflight passed:

```bash
Expand Down
14 changes: 14 additions & 0 deletions internal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,20 @@ plugin surface, and a future MCP server share one engine.
already resolves. It owns the id, the date, the filename and the four sections,
and states nothing: the decision is a human's to write, so the record lands
`proposed` and the skeleton carries questions rather than answers.
- **`core/decide/judge/`** — the decision adapter's one interface
(itd-2609221009495079), apart from the ADR verb in spite of the shared name: a
typed judgement, (state, question) → (value, probability), whose question is
one of three kinds (choose, score, yes/no) and whose type is one of a closed
registry. The host's judgement (`Host`, probability 1) and the decision model
reached through `core/oracle`'s API call (`Adapter`) both implement it. A
`Router` reads `oracle.judgements.<type>` as `host`, `shadow:<provider>/<model>`
or `<provider>/<model>`, which the oracle reader has already held to the
provider's allowlist. In shadow the host decides and the pair is recorded in
the newest lab home's `pairs.jsonl` (`core/lab`). Turned on, the adapter
decides, and an adapter that cannot answer leaves the decision to the host
with a receipt. The drain's judgement before a lane opens
(`eligibility-residual`, `core/implement/loop`) is the one step that calls
it; every other type stays on the host.
- **`core/decisions/`** — the decision records' folder, `.abcd/work/decisions/`:
one `dcn-<id>-<slug>.md` file per decision (adr-2610110925465593). `Record` is
its one writer, behind `abcd decide "<text>"`: it mints through
Expand Down
Loading
Loading