Development record
abcd keeps a per-session ledger of every agent it launches
Press Release
Every agent abcd starts is written down against the session that started it, and the session can add them up. A person leaves a build running and comes back to a status line that says how many agents are running of how many it started, how many failed, what the key-billed ones have spent against each key's budget, and how many tokens the lot have used; a line a person can ask about, and an agent can read, says where each figure came from. Behind the row is one ledger on the person's own machine: one entry per agent abcd launched, naming the session or terminal run that asked for it, the piece of work it was for, whether it ran on a named key or on the subscription, the model that answered, what it spent, its tokens, the time it spent waiting on the model, how it ended, and whether it is still running. Money is counted only for agents billed to a key; subscription work shows in the subscription's own gauges and is never added to the bill. A run left to work on its own gives the same account of itself when it is done: every agent it started, with its cost and outcome, in one listing. "I used to keep a spreadsheet of what each overnight run had cost," said Maya, an autonomous-development practitioner. "Now I ask abcd, and the answer covers the agents I never saw start."
The draft's seeded wording (2026-11-03, from the lab) cited the findings by number; it is revised above to the user moment, with the evidence moved to Why This Matters.
Why This Matters
Typed links: builds_on
itd-2609201916056194
(the runner, the one seam every launch abcd makes passes through),
itd-166 (both
sides of a run: the model asked for and served, the tokens and the cost, kept
on the run state today),
itd-2610111707085098
(each session's persona name, which the ledger names a session by) and
itd-2611011230226453
(the key a session names and the budget it keeps; this ledger is where each
launch's key and spend are written, so the two share one record of what was
spent). Related issues: the run-state schema trap
(iss-2611020719494343),
which the ledger's own schema is shaped to avoid; the unattended build drive
(iss-2611031146255825),
whose account of itself this ledger is; the stranded agent
(iss-2611031337115764),
which the ledger shows as two launches for one await; and the run-start
budget check that always skips
(iss-2610100913096346),
which a per-key sum of recorded launches answers. The status line's own
elements, words and dropping order are the sibling draft's,
itd-2611031342000215;
this intent supplies the figures that draft's agents and API elements show.
abcd starts agents on a person's behalf and keeps no account of them as a set. The lab of 2026-11-03 (lab-261103114820-bcba06f) read what the record holds today and what the host can see:
- The run state keeps each launch's spend and tokens on its route record but no API time, so the time the agents spent waiting on the model cannot be summed from it (finding F-2); and nothing in it names the session that drove the run, so a session cannot find the runs it started, let alone sum them (F-3). The planning interviewer's and the drain judge's launches go through the same runner and leave no per-launch figures anywhere.
- The host's own counters see none of it: the coordinating session of that day started 36 background shells that launched agents, and its transcript carries no trace of them, so the host's cost and token figures cover none of those agents; only a ledger abcd keeps can count them (F-4). The person kept one by hand, a spend log per key in a scratch file, which the prototype on their status line read; on the night of 2026-11-01 three sessions each tallied their own spend by script and had to ask each other what was left of the key.
- A subscription session's cost figure is an estimate of what the work would have cost, not money spent: adding it to the key-billed agents' spend overstated the bill (F-12). Which route a launch billed is recorded nowhere, and the harness's result does not say; the runner that chose the route has to write it down.
- Summed over that session, 70 agent results came to 48.2 million tokens, almost all cache reads, so a token figure needs a stated basis (F-5).
- On 2026-11-03 a routed implementer ran for an hour and a half, committed and wrote its receipt, and the step that would have collected it was refused; every later step asked for a fresh implementer, and an unattended run would have paid for the work twice (iss-2611031337115764). A ledger that shows two launches for one await is the first place that would have been visible.
The unattended runs abcd is moving toward (the drain from a plain terminal, itd-2610222215485265; a build driven to done, iss-2611031146255825; the autonomy grant's report, itd-2610240850336695) each need to say what they started and what it cost, and today each would have to reconstruct that from run states in several worktrees' local tiers, which a worktree's removal deletes.
Mechanism
- We expect a session, and a run left on its own, to account for every agent abcd started for it, because every launch passes through one dispatch seam (the runner's dispatcher, which the build, the drain, its judge and the planning interviewer all call) that writes the entry before the harness starts and completes it when the harness ends, keyed on the session the host exports to the command, so no launch can escape the record and no sum depends on a process pattern or on the host's own counters, which see none of them. Shown wrong if a launch abcd made is found with no entry, or if the number of running agents the ledger reports disagrees with the processes abcd holds handles for.
Scope Conditions
- Agents abcd launches itself through a runner (the claude and opencode runners, and a configured fallback host): a build or drain lane's implementer and reviewers, the drain's judge, the planning interviewer, a review abcd starts. A role handed to the host session is the host's own agent, counted by the host, and gets no figures of its own; an agent a person or a session starts by hand in a shell is not abcd's launch and gets no entry.
- The orchestrating session's own figures (its cost, API time and tokens) are the host's payload's and its transcript's, not the ledger's; the ledger is the agents' side, and a status line adds the session's own cost to the bill only when the session itself bills a key.
- One machine: the ledger lives under abcd's home on the machine that launched, and the same session or key on a second machine has a ledger of its own there. A hosted runner's home is as ephemeral as the runner, so there the run's own summary is the account that survives.
- Cost figures are the harness's own estimate at its price table; the provider's invoice is the final figure, and every render that shows spend says so. A harness that reports no API time leaves that field absent.
- Which route billed a launch is what abcd handed it or what the harness's event stream shows, never an account abcd looked up: a launch abcd handed a named key is key-billed; one whose stream reports rate-limit windows is the subscription's; one abcd can place in neither is recorded as unknown and counted in no sum of money.
Acceptance Criteria
Drafted from the lab's rulings of 2026-11-03 and planned as drafted under dcn-2611031615094092, with decisions 14 to 21 settling every open question.
- Given any agent abcd launches through a runner (a build or drain lane's implementer or reviewer, the drain's judge, the planning interviewer, a review abcd starts), when the harness starts, then one entry exists in the ledger naming the orchestrating session, the repository, the run, lane and role (or the interview and turn), the runner, the model asked for and the billing route, marked running; and when the harness ends, then the same entry carries the model that answered, the tokens by kind, the cost and its unit, the API time and wall time, the outcome (finished, failed with its reason, stopped at its bound or with its step) and when it ended.
- Given the command that launches runs under a host session that exports its session id, when the entry is written, then it names that session by id and by its persona name; given no session id is exported but the launching process descends from a host a session marker names, then it names that session; and given neither (a drain or build driven from a plain terminal), then it names the terminal run by its run id as the orchestrator, so every entry has one orchestrator and none is unattributed.
- Given the ledger, when any entry is written, then it is written under abcd's home in the machine-scoped store, one file per launch, and never in a worktree's local tier or the repository, so removing a worktree or a checkout deletes no entry and two launches never write one file.
- Given an entry, when a later abcd reads it, then a reader takes every field it knows and passes over those it does not, refuses nothing for a version above its own beyond naming it as newer in its explanation, and never writes an entry back; only the process that launched completes its own entry, and pruning removes whole files by age and count, so no reader on an older build is ever locked out as the run state's strict decode locks out a driver (iss-2611020719494343).
- Given a launch abcd handed a named key, when its entry is written, then the route reads key with the key's configured name, never its value, and its cost counts as spend; given a launch whose event stream reports the subscription's rate-limit windows, then the route reads subscription and its cost is kept as an estimate that counts in no sum of money; and given a launch abcd can place in neither, then the route reads unknown and its cost is likewise excluded, and the explanation says why.
- Given a launch killed at its time bound or with its step, when its entry is completed, then it carries what the transcript shows was spent, as the fallback receipt already keeps it, and the outcome stopped with the reason; given a launch whose harness process is gone with no end recorded, when the ledger is read, then the entry reads as lost, never as running.
- Given a session's entries, when the status line renders for that session, then its count of agents running and started, its count of failed or stopped agents, its spend per key against the key's budget, its burn rate since the session began and its agents' token total come from the ledger alone, read in one bounded pass that touches no network and finishes within the status command's deadline, with the result cached across refreshes for a short, configurable interval; and when a reader asks the explanation, then each of those figures names the ledger as its source, the token basis it counts, and the threshold it warns at.
- Given a session's entries, when the token total is shown, then the basis is the shipped default (every count the harness reports: prompt, completion, cache read and cache write) or the alternative the person configures (prompt and completion alone), and the explanation names which.
- Given the agents' API time, when the status line renders, then the time beside the session's name is the session's own, from the payload, and the agents' API time is summed in the ledger and shown by the verb and the explanation, not on the row.
- Given a person or an agent asks, when they run the ledger's verb
bare, then it lists this session's launches (orchestrator, work, route,
model, spend, tokens, API time, outcome, running or not) with totals, in
text and
--json; when they pass a run id, then it lists that run's launches and totals; when they pass a session id or ask for every session, then it lists those; and the verb reads only, writes nothing, and names an entry it could not read rather than dropping it silently. - Given a run driven to done unattended (a drain from a plain terminal, or the build drive iss-2611031146255825 asks for), when it ends or is stopped, then its summary carries the ledger's totals for that run (launches, failed or stopped, key spend, tokens, API time) and names the verb that lists each launch, so the run's account of itself is the ledger and not a tally the driver kept.
- Given two entries for one await of one lane (a second implementer launched while the first's entry still runs or finished unreadable), when the ledger is read, then both are listed against the same await, the verb names the pair, and the status line's explanation counts it as a warning.
- Given the ledger's bounds, when entries pass the retention the person configures (a number of days and a count, with shipped defaults), then the next writer prunes the oldest whole files past them and the ledger never grows without bound; whichever limit is set, days or count, pruning never removes an entry inside the longest cap period any key's budget counts (itd-2611011230226453), so a month's totals stay whole; a pruned launch is gone from the ledger and from nothing else.
- Given the run state, when this intent ships, then its schema version is unchanged: the ledger names the run, lane and await, and the run state names nothing of the ledger, so no driver on an older build refuses a step because of it.
Decisions
Ruled by the person on 2026-11-03, in the lab (lab-261103114820-bcba06f), restated here; the draft is revised to them:
- Spend is split by billing route. Spend and its budget sum the key-billed launches, and the session itself only when it bills a key; subscription work shows in the subscription's rate-limit gauges, which the host reports for the whole account and which therefore already include subscription agents; API time and tokens sum everything. A subscription session's cost figure is what the work would have cost and is never added to the bill (finding F-12).
- The row shows the session's own API time; the agents' time is recorded, not shown. The person still wants each agent's API time kept per session, which has nowhere to live until the runner stores it per launch (F-13). The ledger is that place.
- The ledger, not the host, counts background agents. The host's cost and token figures cover none of the agents a session starts in background shells, so only a ledger abcd keeps can count them (F-4). Agents a session starts by hand are outside it (scope conditions); the person's hand-kept spend log is the evidence for the per-key element, not a source the product reads.
- A token figure states its basis. Cache reads dominate (F-5); the figure shown names what it counts, and the explanation repeats it.
- Every warning has a deterministic, readable explanation, in the product a machine-readable form of the status line's state rather than a file, so a person, an agent and a test get the same answer (F-37). The ledger's figures carry their source, basis and threshold into it.
- The subscription's burn is a projection, the keys' is spend per hour (F-17): the ledger supplies the key-billed spend over the session's elapsed time; the subscription's projection reads the host's gauges, not the ledger.
Settled by the record, so no question was put (2026-11-03):
- The write sits in the dispatcher. Every launch abcd makes passes through the runner's dispatcher (the build's and drain's lanes, the drain judge and the planning interviewer each construct one), so the entry is written there, before the harness starts and again when it ends, and no launcher can forget it. The adapters read the harness's API time from the result event they already meter.
- The ledger is machine-scoped, under abcd's home, beside the session
markers, the transcript store and the worktree store, never in a
worktree's tier (which
git worktree removedeletes, and which a verifier's copy would read as the worktree's) and never in the repository. One file per launch, written by its launcher alone, so concurrent lanes and peers never contend for one file. - The ledger adds nothing to the run state. The run state's strict decode and its single version have locked a driver out three times (iss-2611020719494343); a per-launch record that bumped it would do so again, and its readers (the status line on the installed release, a peer on another build) are more often behind the writer than the run's driver is. The ledger has its own version, read leniently and never rewritten by a reader (the fourth criterion).
- The orchestrating session is the one the host exports. The command
that launches already reads the host's session id from its environment
for the checklist's
updated_by, and the session-start hook already keys a marker on the host process; the ledger uses the first, falls back to the second through the process walk the session register makes, and names the terminal run otherwise. - Billing route from what abcd handed and what the stream shows. abcd never reads a key's value and never asks an account; a named key it handed (itd-2611011230226453) is the key route, and a stream carrying the subscription's rate-limit events is the subscription's (the claude runner's stream shows them only on a subscription; a key-billed launch gets none). Neither is unknown, counted in no sum of money.
- Running is judged on the process, not a flag. Each entry carries the harness process's id and start time, as a session marker carries the host's, so a reader checks liveness itself and an entry whose process is gone with no end written reads as lost.
- Impact: additive. A new record and a new reader; nothing existing changes shape.
Settled at the planning interview of 2026-11-04 by the product thinker's standing stance that abcd ships a sensible default and a person configures it per repository (dcn-2611041550129278); each takes the draft's proposed default, and none was put as a question of its own:
- Retention: 30 days and 20,000 entries, pruned by the next writer; a machine or repository setting overrides both figures, with one floor: the retention is never shorter than the longest cap period of any key the budget intent keeps (itd-2611011230226453) plus one day, so a calendar month's totals are summed from entries still held, and a repository setting below that floor is read as the floor and named as such; the count limit keeps the same floor, never pruning inside that period. The store holds every repository's launches, so dcn-2611042248450089 narrows the repository's half: its figure stands only where it is longer, and binds that repository's own launches alone.
- The status line's cached read: 30 seconds, the interval the prototype used; configurable the same way.
- Token basis: every count the harness reports, as the prototype summed them; prompt and completion alone as the configurable alternative. The row shows one total naming its basis, and showing cache reads apart is a setting, off by default.
- The verb is
abcd agents, bare for this session,--run <id>,--session <id>,--all,--json;launchis the release verb andlauncheswould read as its plural. The naming rule is applied at the verb audit as for any verb. - The budget intent's per-key totals are summed from this ledger (itd-2611011230226453), not kept in a store of their own, so one record holds every launch's spend; decision 14's floor is what makes the sum whole for the longest period a cap counts.
- A session's by-hand launches (an agent started from a shell) are entered from their result file by a later intent, not this one.
- "Today" on the row is the machine's local day, configurable to UTC.
- A role handed to the host session gets a placeholder entry naming the host as the route, with no figures, so a run's listing is complete.
Open Questions
None open. The eight questions this draft carried are decisions 14 to 21, each settled on its proposed default by the standing stance recorded in dcn-2611041550129278.
Audit Notes
Empty. Populated by intent-auditor when intent moves to shipped/.
Grounds
Proposed for the product thinker to confirm at the planning interview and
record through abcd intent ready --grounds, not yet recorded: pursued
because abcd is about to leave runs working alone on a person's key, and we
expect one per-launch record written where every launch starts to make the
bill, the count and the account of an unattended run exact where the host's
counters and the run states cannot; we are wrong if sessions keep tallying by
hand, or if the ledger's running count and spend keep disagreeing with what
the person sees on their provider's console.
Frontmatter
| id | itd-2611031342011261 |
|---|---|
| type | intent |
| lifecycle | planned |
| kind | standalone |
| severity | minor |
.abcd/development/intents/planned/itd-2611031342011261-every-agent-abcd-launches-for-a-session.md
open on GitHub ↗ · commit history ↗
References4
- itd-166A run records what it was actually run withbuilds on
- itd-2609201916056194A role runs through the command-line harness the operator chose, and every fall back to the host is recordedbuilds on
- itd-2610111707085098Each session takes a persona name, shown in the status line and the session list, unique across the machinebuilds on
- itd-2611011230226453Agents abcd starts on your API key stay within the budget you setbuilds on
Referenced by10
- iss-2610100913096346The run-start budget check never compares anything today: no shipped runner implements QuotaReporter, so every real run skips the check, and the estimate leaves out fix rounds, so it is a lower bound.related
- iss-2611020719494343A lane whose change raises the run state's schema rewrites the live run's state at the new version: its own preflight's load gate (implement load --wait, run through the lane's freshly built binary) renews the run's reservation and saves the run state with the lane binary's schema, after which the driver, still on the default branch's older abcd, refuses every step and receipt ('schema version 18, which a newer abcd wrote'). On 2026-11-02 the lane for iss-2611012047534449, which moves the schema from 17 to 18, did this three times to its own run (about 04:56, 05:57 and 07:15 UTC, the last in its landing preflight), and the version was set back by hand each time, no version-18 field being in use.related
- iss-2611031146255825No abcd verb drives an intent build to done unattended: implement step takes one stage and exits, so a build of several steps needs something outside abcd to re-run it after each stage, wait out the merge queue, step again once the lane's pull request merges, and open the next lane. On 2026-11-03 the coordinator did this with a polling helper script (drive-run.sh) and timed wake-ups; drain --drive (itd-2610222215485265) covers drain lanes only, and itd-2610240933025076 starts a build but does not carry it through.related
- iss-2611031337115764A finished agent is stranded when the step that would collect it is refused: on run-2611031131030791 (2026-11-03) the routed implementer ran 1h37m, committed and wrote its receipt at 13:32, but the next implement step was refused on the run-state schema trap (iss-2611020719494343), and after the state was put right every further step asked the host to start a fresh implementer, though the receipt sat at the path the stage awaited. Only handing it in by hand with implement receipt moved the run on; an unattended run would have launched a second implementer and paid for the work twice.related
- itd-2611031342000215The status line shows each role what it needs, in a table a repository can tunebuilt on by
- itd-2611041549461012A session that takes over a run steps on from what abcd wrote downbuilt on by
- itd-2611041549471712A person stops the unattended runs of each account that chose a folder, with one commandbuilt on by
- itd-2611041549483696An unattended run writes one private daily digest pagebuilt on by
- spc-2611041701500441abcd keeps a per-session ledger of every agent it launchesimplemented by
- spc-2611050023402644The launch ledger reaches the status line and a run's own summaryimplemented by
mentions9
- dcn-26110316150940922026-11-03 — The product thinker's rulings for an experiment testing a long autonomous run (given 2026-11-03): during the experiment a release is cut whenever every gate passes, and any refusal waits for the product thinker; each API key has a hard monthly cap and no separate daily limit; sessions use the person's subscriptions first and move to a key when a subscription window reaches 95%, returning once it resets; the run may use the API keys the person has named to it; agents may plan a draft whose open questions each have one defensible answer on the record, and hold every other draft for the person; and the run keeps the person informed through one private daily digest page (what merged, what stalled, spend per key, what waits for them).
- dcn-26110415501292782026-11-04 — The product thinker's rulings on the profiles draft itd-2611040344170206, at its planning interview. Every agent abcd starts unattended carries abcd's guard whichever way its work is paid for: a stage on the key route runs on a profile with hooks on, and the bare launch adr-2610222222326104 names for the key route gives way for stages. Each way of paying, a subscription or a named key, is one profile with its own login and its own bare settings; a worktree is where the files are, a profile is who pays and what an agent can reach, and abcd pairs one of each per agent. A subscription's profile needs one login by the person; a key's profile needs none. A session the person starts that runs work unattended off a profile is warned in a line that cannot be missed, never refused. Whether an agent on a profile may reach the machine's other sessions is a setting, off by default. A profile's name is a readable one, never a random string: abcd ships a neutral list, a themed one such as a television series' characters is the person's own in their machine configuration, and a name is fixed at creation and retired once used; this replaces the random names of the draft's fourth decision, and the registry records the name, the tool and the date. abcd ships a sensible default allow-list of tools and a repository configures it within one bound: a repository file may narrow the list, only the person's own machine configuration may widen it, and an agent can never widen it. The same stance settles every open question of that kind, those of the ledger draft itd-2611031342011261 among them: abcd ships a sensible default and the person configures it per repository. The draft's title and scope widen to every way of paying, keys included.
- dcn-26110422484500892026-11-04 — A repository's ledger.retention_days and ledger.retention_count may lengthen what this machine keeps and may not shorten it. Decision 14 of itd-2611031342011261 let a machine or repository figure override the shipped retention, but the store is machine-scoped and holds every repository's launches, so a figure in a repository's own config, which is whatever its last committer wrote, pruned other repositories' entries past the floor against the person's own machine figure. The machine layer sets the retention, a repository's figure stands only where it is longer, and one below is named and ignored; a figure past 36500 days or 1000000 entries is refused, so the date arithmetic cannot overflow and the store stays bounded. Each launch prunes the whole store by the figures it read, so a repository's longer figure binds that repository's launches alone, and another repository's launch still prunes by its own; only the machine's figure binds every launch. The floor of 32 days is unchanged. The cost is that a person who wants one repository's entries kept for less cannot say so from that repository; a per-repository pruner would have to open every old entry at every launch to read its repository, and nobody has asked for it.
- itd-2610222215485265abcd drain --drive works through the ledger from a plain terminal
- itd-2610240850336695The person grants their sessions what they may do alone, until a time
- itd-2611040344170206Unattended work runs on a named profile, one per way of paying
- spc-2611041705580524A person stops the unattended runs of each account that chose a folder, with one command
- spc-2611041706004822A session that takes over a run steps on from what abcd wrote down
- spc-2611050505250705the-restart-reads-the-forge-and-the