Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7c4cb55
feat: record both sides of a call through the provider adapter
REPPL Oct 17, 2026
7a305e0
feat: record both sides of a run through the command-line runners
REPPL Oct 17, 2026
35c25b5
feat: carry both sides of a run in the run state at schema 13
REPPL Oct 17, 2026
871291e
feat: name the asked model and the cost on the route lines
REPPL Oct 17, 2026
51eb7fb
docs: describe both sides of a call in the receipts and run state
REPPL Oct 17, 2026
0404bf5
chore: capture the setup call that a usage refusal would fail
REPPL Oct 17, 2026
a4fc6a9
fix: hint at the usage field only when a refusal names it
REPPL Oct 17, 2026
710c26f
fix: let ahoy connect settle a server that refuses the usage request
REPPL Oct 17, 2026
b39675c
fix: list a missing or unreadable token count as not reported
REPPL Oct 17, 2026
588fb9b
fix: hold the runners' token counts and costs to one readability rule
REPPL Oct 17, 2026
0e42070
docs: describe how a call reads each count and cost it records
REPPL Oct 17, 2026
0136b71
test: name the connect test for the third call it rules out
REPPL Oct 17, 2026
4f2f94d
docs: say the connect verification may be asked twice
REPPL Oct 17, 2026
32e9b23
docs: ship itd-166, recording both sides of every model call
REPPL Oct 17, 2026
4593a04
docs: record itd-166's audit and the local-field gap it found
REPPL Oct 17, 2026
3986172
fix: read a null usage count or cost as not reported, never as 0
REPPL Oct 17, 2026
703273d
Merge origin/main into feat/run-records-both-sides
REPPL Oct 18, 2026
6d1db10
docs: resolve the record itd-166 was promoted from, and unlink the pins
REPPL Oct 18, 2026
f4aeee7
chore: capture that a declined duplicate cannot follow its intent to …
REPPL Oct 18, 2026
7d425f0
refactor: keep the harness leg's unpinned list inside the oracle
REPPL Oct 18, 2026
aeb50c9
Merge origin/main into feat/run-records-both-sides
REPPL Oct 18, 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
1 change: 1 addition & 0 deletions .abcd/development/brief/04-surfaces/01-ahoy.md
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,7 @@ Sub-verbs: none.
| `--key` | string |
| `--model` | stringArray |
| `--resume` | string |
| `--stream-usage` | bool |

### `abcd ahoy credential`

Expand Down
15 changes: 9 additions & 6 deletions .abcd/development/brief/04-surfaces/05-intent.md
Original file line number Diff line number Diff line change
Expand Up @@ -682,12 +682,15 @@ agent and refuses the override. A step no configured provider can serve at its
tier goes to the harness with the tier named in its request, and one stderr line
says so. The receipt is a `route` member in the JSON and a `route:` line in the
text, carrying `tier_asked`, `connection_tried`, `connection_used`,
`fallback_reason`, `override`, `settings_sent`, `model_reported` and
`provider_call`. `model_reported` is read from the payload's own `model` field (a
reading's `instrument.model`) and empty when the payload names none;
`provider_call` names the provider, the model asked for and the model it
reported when a provider adapter answered the step, and is null on the harness
leg. A routing table that cannot be read, an
`fallback_reason`, `override`, `settings_sent`, `model_reported`,
`provider_call` and `unpinned`. `model_reported` is read from the payload's own
`model` field (a reading's `instrument.model`) and empty when the payload names
none; `provider_call` names the provider, the model asked for and the model it
reported when a provider adapter answered the step, with both sides of the call
(the internals' adapters chapter lists them), and is null on the harness leg;
`unpinned` is the harness leg's list of the fields it could not pin, the
payload's model among them as self-reported, and null on a provider leg, whose
call record carries its own. A routing table that cannot be read, an
override naming an agent this invocation does not dispatch, a tier outside
`local`, `economy`, `frontier` and `host-decides`, or a connection this machine
has not configured exits 2 before anything is written. With no table accepted
Expand Down
22 changes: 17 additions & 5 deletions .abcd/development/brief/04-surfaces/34-build.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,17 @@ repository abcd manages has one, so a run is managed-only by construction. Each
run directory is created one level at a time and proved real, the state file is
replaced atomically inside an `os.Root`, and the reader decodes strictly,
refusing an unknown field, a schema version it does not know, or a file stored
under a run id it does not name. The state is schema version 13. Version 13
under a run id it does not name. The state is schema version 14. Version 14
added both sides of a run (itd-166): a runner's `route` on a verified receipt or
a validator's return carries, beside the route asked, the route that ran and the
served model, the model the runner asked for (`model_asked`), the SHA-256 of
the prompt it handed the harness, the served models, provider, finish reason
and harness version the harness's events name, the token counts and the cost,
and `unpinned`, each field of the fixed asked and served lists the run could
not pin with its reason. Version 13 is its strict subset, read as a run whose
routes recorded neither side; a file of version 13 or lower carrying either is
refused, and an abcd that writes version 13 refuses a version-14 file as a
newer abcd's. Version 13
added a lane's `refused` (spc-2610161044461448 step 5): an issue run's lane
logs its run of refused steps, the stage, the check, the reason, a count of the
same refusal in a row and since when it has refused at that stage, which a
Expand Down Expand Up @@ -326,7 +336,7 @@ added the fix-round cap (ruling DR1): the pace's `fix_rounds` and a lane's
`validation`). Each earlier version is the next one's strict subset, read as a
run that predates the addition (a version-5 run runs on the bundled cap, a
version-7 run is one the host ran every agent of) and
written back at version 13 by its next mutation; an earlier version carrying what
written back at version 14 by its next mutation; an earlier version carrying what
only a later one writes is refused. Version 4
renamed the lane's stage (BU1, iss-2609291313276243): a lane's and a record
line's `step` became `stage`, so "step" names only the spec's steps (`spec_step`,
Expand Down Expand Up @@ -480,9 +490,11 @@ transcript lands in abcd's history store, keyed on the repository's root
commit, and its receipt is handed back through the same receipt verb and
verified by the stage's own verifier, so a verified one completes the stage in
the same call. The verified receipt, or the validator's recorded return, names
the route that ran it (asked, ran, the model the runner reported), which is the
only field a runner-run review's record differs in from a host-run one's; the
record's receipt or verdict line names the runner that ran it. A runner that is absent, refuses, fails, runs past
the route that ran it (asked, ran, the model the runner reported, and both
sides of the run: the model it asked for, the token counts, the cost and the
fields it could not pin), which is the only field a runner-run review's record differs in from a host-run one's; the
record's receipt or verdict line names the runner that ran it, with the model
it asked for and the cost when the route carries them. A runner that is absent, refuses, fails, runs past
its time, answers unparsably or writes a receipt the verifier refuses leaves the
lane awaiting: the call records one fallback receipt (the role, the runner asked
for, the reason and the route that runs it) in the state and the record, and
Expand Down
40 changes: 38 additions & 2 deletions .abcd/development/brief/05-internals/02-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,28 @@ prompt, and `reading ingest --dispatch <rdg-N>` sends a parked run's bundle with
the position's definition. The verb runs the answer through its own ingest, and
the receipt names the provider as `connection_used` with its call record
(`provider_call`: provider, model asked, model reported, credential name), so a
host reading such a receipt knows the step already ran. DR5 is checked before a
host reading such a receipt knows the step already ran. The call record carries
both sides of the call (itd-166), built by the dispatch from the request it sent
and the response it read, never by asking the model about itself: the asked side
beside the receipt's `settings_sent` (the SHA-256 of the rendered messages,
never their text, and `adapter_added`, the request field the adapter sent on
its own, `stream_options.include_usage`), and the served side as the response
names it (`served_provider`, the upstream provider an aggregator names;
`generation_id`; `finish_reason`; `usage`, with prompt, completion, reasoning,
cache-read and cache-write tokens; and `cost`, in the provider's credits). Every
string the provider supplied is scrubbed of the key, cleaned and bounded as the
reported model is, and a usage member of an unexpected shape is a detail not
reported, never a refused answer: each count it does not carry readably is
listed as not reported on its own, and a later usage chunk that reads fewer
fields than an earlier one does not replace it. `unpinned` lists each field of the fixed asked
and served lists the call could not pin, with its reason: `not_reported` (the
response does not carry it), `self_reported`, `harness_computed` or
`not_configured` (no setting or pin names it; a route to this machine lists the
weight file's fingerprint, its quantisation and the runtime's version so until a
local route can be pinned). Recording adds no request to the ones the call
makes. On the harness leg the receipt's own `unpinned` lists the payload's
`model_reported` as self-reported and every other field as not configured or not
reported, since the host reports none of it. DR5 is checked before a
verb writes anything (`APIConfig.Admitted`), and again by the dispatch. An
ingest handed a payload the host produced while its agent is routed to a
provider refuses it, since the receipt would name work the provider never did;
Expand All @@ -181,7 +202,22 @@ review is skipped.
contract grants the shell carries the guard as the harness's permission
prompt tool, `abcd guard approve` over MCP on stdio, set on that launch's
command line ([`17-guard.md`](../04-surfaces/17-guard.md#answering-a-headless-agents-prompts)).
Besides the reviewers and the implementer, the drain's judge, `drain-judge`, takes the same route
A run records both sides
(itd-166) in the same vocabulary as the provider adapter's call record: the
model the runner passed and the prompt's SHA-256, beside what the harness's
events report. The claude CLI's result event gives the token counts, the
stop reason, the cost and the models its per-model usage names, and its init
event the served model and the harness's version; opencode's run mode names
no model, so its served model is empty and listed as not reported, never the
asked one, and its step-finish events' tokens and cost are summed, a floor,
since a sub-agent's steps are not among them. Either harness computes the
cost from its own price table, so it is listed as `harness_computed`. A
count is read only when present and not negative, and a cost only when it
is zero or more and finite, the rule the provider adapter holds its usage
member to; a summed count that would overflow, or a summed cost that stops
being finite, is listed as not reported rather than recorded.
Besides the reviewers and the
implementer, the drain's judge, `drain-judge`, takes the same route
([`35-drain.md`](../04-surfaces/35-drain.md#the-judgement-before-a-lane-opens)),
to the claude runner alone, in a private directory of its own outside every
working tree, granted reading that directory and writing its answer file
Expand Down
22 changes: 19 additions & 3 deletions .abcd/development/brief/05-internals/03-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ rather than skipped:
"base_url": "https://openrouter.ai/api/v1", // https, or http to this machine
"key": "openrouter", // a credential NAME, resolved through the credential
// source; omitted for a server that takes no key
"models": ["typesafe/jev-1.13"] // the allowlist: the only models it may serve
"models": ["typesafe/jev-1.13"], // the allowlist: the only models it may serve
"stream_usage": false // optional, default true: false stops asking for
// usage in the stream, for a server that refuses it
}
},
"denylist": ["openai/*"], // optional, none bundled; repo or machine
Expand All @@ -192,8 +194,22 @@ rather than skipped:
- **A provider block sits on the machine alone.** It names the address a key is
sent to, so a repository's `.abcd/config.json` declaring `oracle.api` is
refused: a checkout must never be able to aim the person's key at a server of
its choosing. `abcd ahoy connect` writes the block, after one verification
call, and it is the one write abcd makes to `~/.abcd.noindex/config.json`.
its choosing. `abcd ahoy connect` writes the block once its verification
succeeds (asked at most twice, below), and it is the one write abcd makes to `~/.abcd.noindex/config.json`.
- **A call asks for its usage unless the block says not to.** The adapter sends
`stream_options.include_usage` with every call, so the streamed answer ends
with the token counts and the cost the call record carries (itd-166). A
server that refuses the field fails the call with HTTP 400 or 422; when the
refusal's body names `stream_options` or `include_usage`, it names
`oracle.api.<provider>.stream_usage`, and set to `false`, calls go without
the field and a call whose answer carries no usage records it as not
reported. No ordinary call is retried without the field, since a retry is a
second request. The setup is the one exception: `abcd ahoy connect` asks
its verification call once more without the field when the first is
refused with 400 or 422, and when that succeeds writes `stream_usage: false`
into the block it creates and says so (`stream_usage_note` in its JSON).
`--stream-usage=false` (or `=true`) sets the block's value outright, and
the verification is then asked once, as given.
- **The allowlist alone decides; the denylist is optional and a union.** abcd
bundles no vendor denylist (adr-2609300107513982), so a model a provider lists
is served whichever vendor made it. `oracle.denylist` is the configuration's
Expand Down
Loading
Loading