Skip to content

Land app/v3 on dev: one trunk (draft until the hand gates pass) - #2561

Draft
AbirAbbas wants to merge 898 commits into
devfrom
land/app-v3
Draft

AbirAbbas wants to merge 898 commits into
devfrom
land/app-v3

Conversation

@AbirAbbas

@AbirAbbas AbirAbbas commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

What this is

This lands app/v3 (the desktop engine line) on dev, so dev becomes the one trunk. app/v3 retires afterwards. The plan's G0 gate is this branch: app/v3 (a687c92) with dev's twelve newer commits merged in (0fa1b5d).

  • Size: 876 commits ahead of dev, 0 behind.
  • Merge method: a merge commit, not a squash. The 876 reviewed commits stay bisectable, and one revert undoes all of it.

Draft until the hand gates pass. The plan's G1 (this PR's checks), G2 (ci-full.yml on this sha), G3–G4 (race and end-to-end), G5 (data both ways, including rollback), G6 (the desk on the merged engine) and G7 (the WorkAF loop live) all run before this leaves draft. It lands after Friday 13 Nov's staging cutoff, so it gets a full week on dev before reaching staging.

How the 28 conflicts were resolved

Every behaviour from both sides is kept, and both sides' tests pass unchanged except where noted below. Decisions a reviewer should check:

Later dev merges, and the rules they settled

dev moved four times after the first merge. Each move came in as its own merge commit.

  • 8511037 merges dev b0dfe97 (Fix what the pre-release hand test of dev found #2567).

    • Knowledge compilation keeps app/v3's retry policy: backoff from 5 to 120 s, and a stored cap of 3 attempts that survives reopening. A new build or a new source resets the cap. Only the owner retries owned work.
    • dev's stored 30-second retry pause becomes an in-memory, per-vault repeat guard of 2 s.
    • Each paid answer is billed once, through one path, and a usable answer updates its receipt.
    • The build's cap key is each ordinary source's id and content hash. The old key reset whenever a source was revisited.
    • The proofs are TestKnowledgeOrdinaryBuildCapsEveryBilledAnswer, TestKnowledgeOrdinaryBuildRetryCapSurvivesRepeatedHandovers, TestKnowledgeDailyLimitBlocksEveryRetry and TestKnowledgeMixedPaidAnswersMatchCallLogLedgerAndReceipts.
  • 6ed333e merges dev ace7888 (Fix what the second pre-release hand test of dev found #2577), and leaves one rule for recognising a paid call already in the spending ledger. session.UsageCallID decides everywhere, and ChargeID delegates to it:

    1. An explicit id wins: CallID, remote:+RemoteEvent, or the row ID. Two different explicit ids are two calls, even when everything else about them matches (A take no longer loses or double-counts cost #2332's take law).
    2. Otherwise, the eight-field fingerprint decides: At (UTC, RFC3339Nano), Session, Model, Role, Calls, Input, Output and USD.
    3. A line with neither is still counted against its session's uncovered rows. It is never booked again on every reopen; an unparseable journal timestamp, for example, must not re-bill. "No identity, no dedupe" applies to rows: a row with no identity is never another call's duplicate.
    • A chat with no receipts: its transcript's usage lines are per-turn aggregates, stamped when the turn is sealed, so they can never fingerprint-match the per-call ledger rows. Those lines are covered by the session's row count, which is dev's rule. A chat with receipts keeps A take no longer loses or double-counts cost #2332's strict matching.
    • This fixes the double bill on reopening a chat with no receipts. That bug was on app/v3 alone and on this branch's earlier merge. The proofs are TestReopeningFoldedTaskSpendNeverRebillsWorkerCalls (all four variants) and the seven contract tests in internal/cellindex/usage_remerge_contract_test.go.
  • 3cb458b: the take road files a moved chat's copy through dev's capped bucket name. The first merge had dropped the cap, so a chat moved back and forth failed with file name too long. The proof is TestThreeRoundTripsBetweenTwoHomesStillOpen.

  • 528122a merges dev 15d40fd (The model pool's writers stop with their owner, and the tmux suite runs on the engine road #2583). It has one conflict, in cmd/codeaf/poolrecord.go. app/v3 stands the pool judge down while the pool is being handed over (poolJudgeStandsDown). dev stops the judge once its owner's context is cancelled. Both checks are kept, in the per-landing judge and in the sweep.

  • 6e4d317 merges dev b8c5f26 (A settled task keeps its row in the conversation, and the pool's index pull stops at close #2588), with no conflicts.

Fixes #2579
Fixes #2585

Closing the desktop engine: what it waits for

On the landing head, the full suite went red in scattered, order-dependent ways. The cause was one family: work a test's backend, engine or factory had started kept running after that test returned. Through http.DefaultTransport it then reached the next test's fake. The fix makes each owner's Close stop what it started.

  • 5499dbf: the factory's GitHub people, CODEOWNERS and pipeline readers get a bounded Close. A factory with no GitHub token starts no profile reads.

  • 679544c: the app backend's Close joins:

    • the solo factory's poll and readers;
    • a brief write already past its debounce;
    • the provider-model warm-up;
    • the catalog warm-ups.

    A second Close waits for the first.

  • 6992c46: tests only. Owners close before their fakes, and every pipe fleet and profile road a test opens is closed. No assertion is loosened.

  • fd949df: every new wait has a named bound of a second or less. Tests show that a reader or writer that ignores cancellation cannot hold its Close past that bound.

  • 392b2b0: every new critical section unlocks from a defer (the guard laws).

Worst case these changes add to a desktop quit: 2 s (briefStopWait + closeWait). That is reached only when a brief write or a GitHub read ignores cancellation; otherwise they add about nothing. The engine-stop and window-quit times, measured on app/v3 a687c92 and on this head, will be added here from the landing hand checks.

Wait on the quit path Bound Origin
briefDebounce.stop: cancels the write in flight, then waits briefStopWait, 1 s this branch
stopFactoryLoaders: people and pipeline loaders close concurrently closeWait, 1 s in total this branch
A second Close of the backend, the local factory or the factory host ends when the first does; only a concurrent caller reaches it this branch
Local runner and the local factory server's shutdown appLocalStopWait, 10 s app/v3
Factory host <-h.done and the read-loop join (THE READ LOOPS END BEFORE THE FLOOR IS LET GO) unbounded join; cancels first; worst case is the forge client's 30 s timeout if a transport ignores cancellation app/v3, unchanged
Pairing, chat list, devices, close watch, orders sweep, crews unbounded joins, each after cancelling app/v3, unchanged
Conversation release appReleaseWait, 5 s, shared app/v3
closeAll: knowledge, credits (10 s HTTP), catalogs (15 s fetch), place sweep unbounded joins, each after cancelling unchanged
closeAll: receipts appQuitReceiptWait, 3 s unchanged
closeAll: sync drives driveCloseWithin, 2 s unchanged
closeAll: usage ledger and the SQLite closes ledger drain; 10 s busy bound unchanged

The engine process still closes where RunApp has always closed it: after the wire has finished its calls in flight, and after CloseUsage.

  • 679544c had moved that close, and the engine's pool-errand stop, into the backend's Close. The wire calls Close before it waits for calls in flight, so the engine's receipt wait ran ahead of a receipt that lands after the quit.
  • 0343fde backs both out. The same commit also fixes a second Close that waited on a flag tests set without starting a teardown (a 15-minute hang in the PR gate), and gives solo() a refusal that does not mistake a serve-only host's cancelled context for a closing one.

TestUsageMillionRowLedgerFlush (#1749) is the other number this landing owes. Its first write took 3.1 s on one CI runner. It will be timed on this machine, three alternating runs each on a687c92 and on this head, before the merge.

Who it reaches

  • The moment it lands: every dev-channel build and the dev relay.
  • The desk switches its engine build from app/v3 to dev on landing day.
  • Open PRs: the 23 against app/v3 are re-pointed to dev. Their diffs do not change. Superseded ones are closed.

🤖 Generated with Claude Code

santoshkumarradha and others added 30 commits November 2, 2026 19:18
…count (#2168)

* factory: each repository says its open issues apart from pull requests

factory.repos available[] and the factory's /v1/repos rows gain
open_issues: open issues WITHOUT pull requests. open stays GitHub's
open_issues_count, which counts both. The count comes from one batched
GraphQL request per 100 listed repositories (aliases over
repository(owner,name){issues(states:OPEN){totalCount}}), so discovery
costs one request per REST page it already made. A batch GitHub refuses,
or a repository it does not answer, leaves open_issues absent: never 0
and never a guess.

* changes: open issues apart from pull requests (#2154)

* factory: a live read-only lane for the repository list's open issues

Opt-in (CODEAF_LIVE_REPOS_OPEN_ISSUES=1, GH_TOKEN): the picker's own
lister reads the real account's repositories, every request counted and
anything but the REST list's GET or the GraphQL count refused.

* factory: the setup's open issues count issues alone

The first-run setup's repository rows sent open_issues as GitHub's
open_issues_count, issues and pull requests together, though the read
the setup shows progress against counts issues alone. Discovery now asks
the same batched GraphQL count the repository list uses (one request per
page of 100, forge.OpenIssueCounts shared by both) and sends issues alone
with open_issues_exact true. A repository GraphQL does not count keeps
the combined number with open_issues_exact false: open_issues stays a
required number, so no reader loses it. The read's own count is marked
exact.

The test guards that refuse GitHub writes treat a GraphQL query as the
read it is; a mutation is still refused.

* factory: the test write guards pass one GraphQL query and nothing else

The guards that refuse GitHub writes took any POST /graphql whose query
began with "query", so a second operation chosen by operationName, or a
case-variant duplicate key read last by encoding/json, could carry a
mutation past them. graphQLRead now passes a body only when its real keys,
read token by token, are exactly "query" and optionally "variables" (no
operationName, no duplicates, no other key), and a small lexer that skips
strings, block strings and comments finds exactly one top-level operation
opened by the keyword query and no mutation or subscription anywhere. The
product's own open-issue count request, generated by its own code, passes.

* factory: setup's open issues test pins 18 issues beside 12 pull requests and every failed count

A setup repository row with 18 open issues and 12 open pull requests
(GitHub's open_issues_count 30) shows 18 with open_issues_exact when the
batched GraphQL count answers, and keeps 30 without the flag when the
request is refused, answers GraphQL errors with no data, leaves the
repository's alias null, or answers something that is not JSON.

* changes: setup's open issues tests (#2168)

* changes: shorter title for #2168
)

* manual: a cost range writes both dollar signs and a look's price reads ≈ $0.02, as the app shows them

* changes: factory cost words entry (#2194)
* factory: an opt-in per-endpoint log of GitHub requests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changes: entry for the GitHub request log

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* config: register CODEAF_GITHUB_LOG as operator plumbing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…th who) (#2125)

* factory: the pipeline editor adds an approval (add_step kind gate, with who)

add_step takes kind gate: a person put in one pipeline, with an optional
prompt and who it waits on (@handle, operator, or the job's person). A gate
takes no until, rounds, effort or fanout, and an existing gate may be listed
in another pipeline when its words match. Behind the new hello word
factory.pipeline_edit_gates; who on the add_step also needs
factory.pipeline_edit_approvers. Both kind and who are runtime keys, so the
preview takes them only for a file its factory attested.

* factory: tidy the gate edit and number its change entry
…2126)

* factory: the pipeline editor adds an approval (add_step kind gate, with who)

add_step takes kind gate: a person put in one pipeline, with an optional
prompt and who it waits on (@handle, operator, or the job's person). A gate
takes no until, rounds, effort or fanout, and an existing gate may be listed
in another pipeline when its words match. Behind the new hello word
factory.pipeline_edit_gates; who on the add_step also needs
factory.pipeline_edit_approvers. Both kind and who are runtime keys, so the
preview takes them only for a file its factory attested.

* factory: tidy the gate edit and number its change entry

* factory: the pipeline editor renames a step (rename_step {name, to})

One step under a new name in every pipeline that lists it, in the required
list and in its definition, whose words and who move with it. The new name is
spelled as a step is and runs as a stage the runner can run; approve and
proof are never renamed from or into, and no other edit in the preview names
either name. The preview's structure carries renamed. Behind the new hello
word factory.pipeline_edit_rename.

* changes: number the rename_step entry

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* factory: start and create take the pipeline a job runs (a kind of the repository's file)

factory.job_start and factory.job_create take pipeline, a kind the repository's
file lists, behind the new hello word factory.start_pipeline. The app sends it
to the factory, which already took it on create; start_now now takes it too and
decides it in the start's one write, from the file the start decides on the
job. pr and ci stay refused, as at creation; a job that ran or waits in the
queue keeps its steps; every refusal comes before any write. An older factory
refuses the unknown field, so a pick is never dropped.

* changes: number the start pipeline entry

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…word (#2128)

* factory: the pipeline editor adds an approval (add_step kind gate, with who)

add_step takes kind gate: a person put in one pipeline, with an optional
prompt and who it waits on (@handle, operator, or the job's person). A gate
takes no until, rounds, effort or fanout, and an existing gate may be listed
in another pipeline when its words match. Behind the new hello word
factory.pipeline_edit_gates; who on the add_step also needs
factory.pipeline_edit_approvers. Both kind and who are runtime keys, so the
preview takes them only for a file its factory attested.

* factory: tidy the gate edit and number its change entry

* factory: the pipeline editor renames a step (rename_step {name, to})

One step under a new name in every pipeline that lists it, in the required
list and in its definition, whose words and who move with it. The new name is
spelled as a step is and runs as a stage the runner can run; approve and
proof are never renamed from or into, and no other edit in the preview names
either name. The preview's structure carries renamed. Behind the new hello
word factory.pipeline_edit_rename.

* changes: number the rename_step entry

* factory: adapt per pipeline; the pipeline editor sets one pipeline's word

adapt: in .codeaf/factory.yaml may now be a map of pipeline to word
(adapt: {issue: ask, docs: fixed}); one word is still read as before.
set_adapt takes an optional pipeline (feature
factory.pipeline_edit_adapt_kind) and the source carries adapt_kinds.
A map is a runtime key: an older factory would drop it, so it is read
and previewed only for a file its factory attested.

* changes: entry for #2128

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… kind (#2254)

* test: reproduce duplicated tool names and home paths in run logs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: make factory tool log captions readable and identifiable

Tool event hints already include the tool name. Normalize tool captions before clipping and retain identity through the existing report tone so strict older factories accept reports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…mote's stamp answers (#2047)

* tests: the last test-only Mac assumptions (T3-T7)

Twelve tests assumed Linux and failed on a Mac with nothing wrong in the
product:
- BSD mktemp ignores TMPDIR without a template, so the five scratch-routing
  probes now name "${TMPDIR:-/tmp}/probe.XXXXXX" (session, executor);
- the three cellstore socket fixtures resolve /tmp to /private/tmp, clean
  the temp prefix and keep a short base, as the product's socket rules do;
- the release-surface probe guards an empty array, which bash 3.2 calls
  unset under nounset (the workflow itself runs on Linux bash);
- the teams doors' unchanged answer is asserted to carry exactly the stamp
  and the flag, since a Mac's device and inode numbers make the stamp longer;
- tui3's Memory switch is expected in the app's own chord spelling, which
  is opt+u on a Mac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: the change entry for #2047

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…d the job bound work there (#2039)

* processgroup: read macOS process groups and job subtrees without cgo

Members lists a group through kern.proc.pgrp with a bounded retry, and
Group.Usage walks the recorded leader's descendants through the kernel's
proc_info interface, converting task time with hw.tbfrequency. The new
darwinproc package owns the raw trap, the SDK layouts and the argv and
listening-socket readers the executor uses next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* executor: name and port left-running services on macOS

cmdline reads kern.procargs2 and the ports come from each member's TCP
listeners. describe reads each member's sockets once for both the naming
member and the port set. The two service tests run on Linux and macOS, and
the listening-service test polls the record until the backgrounded shell
has exec'd its server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: the job subtree bound, and that macOS now applies it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* processgroup, darwinproc: bound the macOS walk and keep strangers out

Review fixes:
- A queued parent is re-read after its children are listed and its start
  time compared, so a pid handed to another process cannot bring that
  process's children into the job's reading.
- A child listing that fills the largest buffer, and a walk that reaches
  1024 processes, count as lower bounds; any unexpected listing failure makes
  the reading unknown instead of partial.
- kern.procargs2 is parsed at the kernel's eight-byte boundary after the
  executable path, so an empty argv[0] can never shift an environment string
  into argv (checked for path lengths 1-40 on arm64 and under Rosetta).
- The listening-service test's shell now waits for the server's port before
  it exits, so the record the call returns is asserted directly; the
  membersOf helpers it no longer needs are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* processgroup, session: a partial CPU sum yields no rate

Review fix: a reading that left part of the tree out (the walk limit, a
truncated child listing, a process that could not be read) still counts
processes as a lower bound, but its CPU sum is marked partial. The job bound
takes no rate from it and does not use it as the next baseline, so a process
missing from one reading and present in the next cannot read as a burst of
its whole lifetime's CPU and invent a strike. Linux readings are always
complete, so nothing changes there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* processgroup: a parent that cannot be re-read makes the sum partial

Review fix: when a queued parent read once but its re-read after the child
listing fails for a reason other than having exited, its children are left
out of this reading only, so the CPU sum is marked partial and no rate is
taken from it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: number the change entry for #2039

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* processgroup: a child counts only while its parent is still the same

Review fix: the macOS walk now reads a parent's listed children first and
re-reads the parent after them. A pid is never handed on while its process
lives, so a parent with the same start time after its children were read was
their parent while they were read; a parent and child whose pids both changed
hands just after the parent's own re-read can no longer bring a stranger's busy
child into the job's reading.

The partial-sum regression now starts from one complete, quiet reading, so it
also fails if a partial reading ever keeps that earlier baseline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test: specify marking a job pull request ready

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: mark job pull requests ready through the app

Reuse the GitHub ready mutation with the caller credential and persist its confirmed draft state before returning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover GitHub ready conflicts and permission refusals

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: preserve definite ready-for-review refusals

Keep automatic publication idempotent while explicit ready requests conflict on an already ready PR and preserve GitHub permission refusals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: pr-ready is a server-owned verb in the client contract

Its own serve tests cover it against the real store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: a draft's merge block now names what readiness waits for

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Take the gated copy of the union merged docs

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ls (factory.effort_models) (#2148)

* factory: a cheap step runs on the small work model, a strong one on the checks model

A step's effort word now goes with its conversation, and with no model
of the step's or the job's the step opens on the model its effort runs
on: models.tiers.low for cheap, models.tiers.high for strong. One model
for every task keeps its one model. The app announces
factory.effort_models.

* changes: name PR 2148

* factory: effort tiers keyed by the one list of effort words

* factory: live check of a cheap step on the small work model

* factory: a step's effort word comes before the factory's default model

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…b_protected) (#2145)

* factory: a Backlog job says which protected paths it names

The queue judges every Backlog job against the rules it would start
under, read once per repository and show-only, and carries the
protected paths its issue, comments, plan or files name. Absent is not
judged. The app announces factory.job_protected.

* changes: name PR 2145

* factory: live check of the protected key against a real repository

* changes: name PR 2145 in the entry

* factory: a cheap step runs on the small work model, a strong one on the checks model

A step's effort word now goes with its conversation, and with no model
of the step's or the job's the step opens on the model its effort runs
on: models.tiers.low for cheap, models.tiers.high for strong. One model
for every task keeps its one model. The app announces
factory.effort_models.

* changes: name PR 2148

* factory: judge protected paths only for jobs the canvas shows in Backlog

* factory: effort tiers keyed by the one list of effort words

* factory: live check of a cheap step on the small work model

* factory: a step's effort word comes before the factory's default model

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… can't succeed doesn't spend (#2256)

* factory: a raised budget resumes an Auto job its runner parked at the cap

A worker's runner parks at the budget through its ask, and its report
puts that question on the desk copy as Asking. raise_budget resumed only
when Asking was nil, so for a held job it never resumed. Its Yes on the
budget signal was a second answer path that the gate never reached.

The raise now treats the open ask as the cap question when QKind is cap
and any Asking is that same question. When the new cap clears the spend
on an Auto job, the raise answers "continue" through the answer door a
person's answer uses (continueCap, taken out of the answer handler). The
ask comes down, the ledger keeps budget_changed then answered, and the
timeline says the budget was raised. The budget signal now carries only
the cap and goes first, so the runner never doubles the old cap.

An unheld cap continue had the same gap: KeepAnswer cleared Asking but
left the job needing you. AnswerUnheld now queues it through
run.ReleaseCap, which the answer handler's own fallback uses as well.

Manual jobs, any other open ask, and a raise to the spend or lower stay
parked. The new worker tests run against the rig's worker-backed desk.
The old test ran on a Manual job through a second handler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* factory: say the raise resumed only once the continue is answered

Review of the raise found two problems. First, the timeline said "carrying
on" in the same write as the cap, before the answer door ran. A refused
answer then left the job parked with a timeline that said otherwise. The
note is now written after the continue is accepted.

Second, the answer door's fallback for a cap no runner waits on queued
the job whatever it waited on at that moment. A job that moved on to
another question between the read and the release lost that question.
The release now takes down only a cap question.

This commit also adds the change entry and removes a stray test comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changes: the raised budget entry carries its PR number

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* factory: a person's continue at the cap queues an unheld job again (test)

Pins the second half of the fix: on an unheld Auto job whose copy still
carries the cap question as an open ask, a person's continue takes the ask
down and queues the job. On the base (39cec14) the job was left needing
you with its question; on this branch it is queued.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(factory): preserve ordered budget answers and spending stops

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(factory): a Manual budget raise reaches the next approval

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(factory): a budget raise answers Manual's cap question

The Auto-only resume predicate stranded Manual jobs after a raise above spend. Keep Take over and other decisions parked while allowing Manual to reach its next step approval.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce no-change feedback report escaping correction

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: correct no-change feedback reports in the same conversation

Tracked feedback requires a structured current-head assessment. Return omissions at the reporting door while the model can correct them, retaining the clean published-head verifier.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: use the required change entry schema

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: refuse unchanged no-change feedback retries before spending

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: refuse unchanged feedback verification retries before spending

Bind the no-change verification refusal to its PR observations in the saved result. Answer and rerun paths retain the question and budget until those facts change; Stop and fresh feedback remain available.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: exercise no-change feedback with a live factory model

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: preserve unpaid retry when a refused checkout is restored

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: recheck refused checkout state before another model call

A PR snapshot alone cannot tell whether a rejected checkout was restored. Preserve its content receipt and validate it on the worker without spending; changed contents permit a fresh assessment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain current-head feedback review fences

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: preserve the running state in the live feedback fixture

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test: require explicit Model Pool opt-in

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep Model Pool off until explicitly enabled

Default the shared resolver to off, including CI, while preserving saved choices and environment overrides. Align settings and manual copy and opt pool fixtures in explicitly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: exercise pool default through production app boot

Use the production app opener so the provider and record assertions cover the real pool startup sweep without one-model mode masking it. Explicitly enable the two pool push fixtures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: explicitly enable pool for exec pending records

The restart-sweep fixture intentionally tests pool records; opt it in now that untouched profiles stay off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* config: Model Pool off says only what the switch does

Off stops pool judging and sharing. It does not hold task roles to the chat
model: in Auto the router still picks from the allowed models.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…at 1 s ticker (#2255)

* knowledge: capture wakes on the chat's own journal writes

After #2042 an idle chat's capture tick opened no database, but it still
woke every second and opened, read and closed the chat's journal. It was
the largest per-chat timer left in an idle engine.

The journal writer's hook now wakes capture after each line reaches the
file (an in-process signal; no file watch). Each write is followed by one
trailing look a second later, a look that moves the cursor is followed by
another, so a backlog still moves one batch a second, and a five-second
safety tick covers lines another process writes to the same journal and
consent changed elsewhere. A look a write woke uses a consent answer at
most a second old (CaptureConsentWithin), so a turn written just after
consent changed elsewhere is captured on the trailing look.

An idle chat now runs one five-second tick and nothing else.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: change entry for #2048

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* knowledge: a new capture scanner always takes one trailing look

Review found that a rebind could leave a write without its trailing look:
the retired scanner may consume the wake and answer from a consent view
under a second old, and the replacement's first look moved nothing, so it
armed no trailing look and capture waited for the safety tick.

Every scanner now looks once when it starts and once more a second later
with fresh consent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…for branch publication (#2032)

Rebased onto app/v3, where #2178 and #2183 already cover most of this
branch: the engine-backed fixtures and the hosted take-back's resumed-hold
proof. What is left:

- the hosted move rig and the drive rig's default daemon keep their homes
  under /tmp on Unix, because macOS's temporary folder leaves a workspace
  socket only a few bytes under the address limit;
- the L12 death-branches test waits for A's branch row to hold its three
  orphan turns before it reads them.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
)

* remote: an app chat's streamed text is folded before it is framed

Every streamed delta of an in-process app chat crossed the remote framing,
the in-memory pipe and about eight goroutine handoffs on its own. At 40
tok/s the deltas are 25 ms apart, so the app outbox's 16 ms window never
folded anything. Engine.DeltaWindow (opt-in, set by the app's in-process
and lent conversations to DefaultDeltaWindow, 33 ms) lets each stream's
pump send the first chunk at once and fold the text that follows until
the window ends or 4 KiB. Every non-text event sends the held text first
and goes out at once; the channel closing flushes before "closed".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* remote: the delta window's tests cover swap, a real reconnect and a steady stream

A swap while text is held lets none of the old conversation through; a
real client cut mid-burst and resumed sees each character once; with the
production window a quiet tail arrives and a steady producer is never
starved. The 4 KiB value is documented as the flush threshold it is, and
the latency check keeps its median bound without a per-sample maximum
that scheduler pauses could break.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: change entry for #2043

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* remote: the delta window's swap and steady-stream tests stand on their own

The swap test drives its own pump and joins only that one, so it no longer
depends on the fake agent opening no other lanes. The steady-producer test
bounds frames by the time the producer actually took and checks that text
reaches the surface before the producer finishes, keeping the largest gap
as a diagnostic. The change entry names the conversations it applies to:
the ones the desktop's engine holds in process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* factory: a write step that changed nothing is not done

A write round that reported done while the job's checkout had no change
from the run's base counted as done, so test and review ran on an
untouched tree and only publishing found nothing to commit. The runner
now reads the checkout after a write round; with no change, the round
does not pass, says "the checkout has no change from where the run
began", and goes round again or asks. Other steps may still finish with
no change, and an unreadable checkout never holds a round back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: changelog entry for #2158

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover factory write retries against the job base

Exercise committed earlier work on rerun, retry after failure and resume
after Stop, and retain the refusal for an empty first attempt and rerun.
Include the waiting question when the retry fixture misses its failed check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep write retry checks at the Test approval gate

Include the following test step so the manual approval is reached,
and stop the current step so its recorded attempt can be resumed.
Check the retained commit and change through the job reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: distinguish stopped writes before and after reporting

Compare immediate Stop with Stop after the first running receipt,
using the same committed change and wire actions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: retain the factory write base across stopped attempts

Stop can revoke a lease before its first report carries ChangeBase.
Keep the original pin on the job branch before edits so its replacement
counts earlier committed work instead of comparing against the edited HEAD.
Make the three retry fixtures perform their promised write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: synchronize stopped write reports and refuse an unpinned base

Hold periodic reporting on the real lease worker until Stop, then run
the replacement with normal reporting. Verify a locked repository config
prevents Write from editing before its comparison base is preserved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: stop a factory round when its base cannot be preserved

A repository config lock must not let edits start without their durable
comparison pin. Surface the save error before calling the executor.
Keep the deterministic replacement worker on the same fixture machine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: assert the factory preparation failure receipt

A failure to preserve the base is an execution preparation error,
so read its failed-step detail rather than an out-of-rounds result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep read-only factory checks independent of Git metadata

Exercise the exported runner with a real command in a directory without
Git metadata, matching the leased-worker fixtures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: discard failed Git reads before preserving a factory base

Git errors include stderr as output. Do not mistake that text for a
branch or commit and try to persist it. Keep the existing behavior when
Git metadata is unavailable, while refusing an actual base save failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Take the gated copy of the union merged docs

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test: pin one next budget for cap questions and answers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: give cap questions and answers one allowed next budget

Select the smallest allowed cap above recorded spend instead of doubling the old cap. Share budget choices with the runner, job reads and budget edits, and refuse a continue when no higher budget remains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep budget continuation available above thirty dollars

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: offer matching budget continuation beyond thirty dollars

The fixed budget ladder returned zero after thirty dollars, trapping jobs. Derive the next cap from the job and accept that exact amount at the factory budget door.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: align cap metadata expectations with the next budget rule

The whole-package run found two expectations left on the former doubling and zero-limit rules. Expect the next standard five dollar cap at both public seams.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test: force task notes to arrive before the final answer ends

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: hold the worker request rather than its helper

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep task directions queued until a worker request reads them

Turn cleanup recorded late directions and emptied the queue before the runner checked it. The task could finish with saved notes that no worker request had read. Keep those directions queued for the runner to take another turn and stamp the keyed reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: clarify the final-answer note regression fixture

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: distinguish a missing note receipt from lost words

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test: expose later person requests to the job brief

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: judge job briefs against later person requests

Carry gate answers and saved notes into the writer input so requested work is judged against the person’s current request. Preserve bounded requests through input cuts and include them in freshness checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: prepare paired live brief request probe

Compare the same scripted work with and without person requests using the pinned live model. Print checked verdicts and blocks, and reject the false-fault vocabulary seen in the report. The probe is opt-in and left for the caller to run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: log why a live brief reply ended

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: a live brief names the request in its text

The verdict is at most fourteen words and may refer to the request by name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* manual: say Brief, not the writer's word for its headline

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#2264)

* test: force action reads while a light crew owner opens

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: wait for crew readiness before requiring live process actions

Reads deliberately answer saved history while a light owner is opening. Check that nonblocking answer during a forced open, then require live actions after agent.changed instead of depending on scheduler timing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce preview refusal digits in a factory identity

Pin the fixture identity to include 820 so the whole-error wording check fails consistently despite a clean displayed message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: prove the preview wording guard catches a displayed numeric leak

Check only the displayed message and deliberately inject 820 there for the negative control. The following commit removes this test-only mutation after the remote guard verdict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep preview refusal wording checks on the displayed message

Remove the negative-control message injection after confirming that the guard rejects 820 in displayed words. Factory identity digits remain deterministic and typed refusal details are still checked separately.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* factory: a write step that changed nothing is not done

A write round that reported done while the job's checkout had no change
from the run's base counted as done, so test and review ran on an
untouched tree and only publishing found nothing to commit. The runner
now reads the checkout after a write round; with no change, the round
does not pass, says "the checkout has no change from where the run
began", and goes round again or asks. Other steps may still finish with
no change, and an unreadable checkout never holds a round back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: changelog entry for #2158

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce missing memory spend through the vault interface

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: bank memory receipts and bind job compilation to finished turns

The compiler used the provider transport without a ledger consumer, while the process worker owned its lifetime independently of jobs. Bank decoded receipts before validation and keep job sources out of independent compilation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: isolate memory snapshots and verify cancellation during compilation

Keep compilation boundaries per owner, drain finished-turn journals, and classify older factory conversations from persisted memberships. Preserve projection upgrades and reject a structural bypass that discards the billed context.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover retained job sources and large memory snapshots

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: refuse memory admission on paused and published jobs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: drain owned memory snapshots and fence unchanged failed input

Reclassify retained job events independently of journal cursors, check stored pause and publication state at admission, and finish every extraction batch in the turn snapshot. New source input or an explicit Build is required to retry failed background compilation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reject repeated refinement of unchanged memory input

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: require new evidence or explicit build for further memory refinement

Record the Build request consumed by each publication so forced flushes on unchanged sources stay idle. Preserve recovery when extraction produced new unpublished evidence, and let an explicit Build advance retained guide work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: old job ownership excludes background memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: retain old factory ownership in memory inventory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: compile delegated reports with their owning turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: use the persisted place layout for delegated memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: compile delegated evidence on the owning job turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure live job memory cost and stage tail delays

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory cannot turn successful work into a cap stop

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: preserve successful stage results after deferred spending

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: bound stage memory waits and retain cancelled charges

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: enforce memory cap only at the next unfinished step

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: record completed phases before checking the next budget

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: distinguish memory delay and stop timed-out live jobs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: bound stage memory waits to the owning job lifetime

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain skipped stage evidence across job memory handoff

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: recover skipped stage evidence in the next owned memory pass

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure late memory receipts after job completion

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep an empty owned source selection empty

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count overlapping memory waits as wall time

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: attribute memory tail wait to its initiating stage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: verify live memory starts stay inside the job lifetime

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: stop live measurements with a fresh job version

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count measured memory calls independently of late prices

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: read measured job spending through the app wire

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: settle cancelled live calls before checking measured spend

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: open live factory brief chats through the in-process app

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: deliver late memory receipts through the real factory worker

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: observe the public ready flag and bound worker fixture cleanup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep job reporting alive for late memory receipts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test late memory pricing after stopping a factory job

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Observe finished steps before settling final prices and pin live helpers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reconcile paid memory after a stopped job loses its lease

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep one accounting anchor per run and retry final settlement

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test final memory settlement preserves previously counted job spend

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Measure stage answers separately from automatic brief calls

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Report per-job memory dollars and bound delays by the next step start

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test paid memory survives reopening a finished stage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Preserve paid usage in a closed stage journal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test leased memory receipts during actual factory quit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Allow bounded accounting settlement after worker cancellation

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reproduce the memory tail fixture accounting observation race

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Wait for both memory ledger and job accounting in lifecycle fixture

The ledger is published before the independent job meter update. Poll both observable totals and name standalone session closure accurately.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test quitting while final worker accounting is already settling

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep bounded accounting alive when quit interrupts settlement

Detach the existing settlement context from execution immediately, then cancel it five seconds after quit. This also covers cancellation arriving during the existing receipt wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Recognize late journal usage in the append funnel guard

The journal is a separate store from artifact indexes. Account for both journal append owners while retaining the guard against unknown append paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reproduce stage budget exemptions and unowned batch draining

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Distinguish memory cap crossings and restore bounded ordinary Flush

Keep memory in total job spend while excluding only newly charged memory from completed-round cap checks. Preserve model and tool stops, and retain finite draining only for owned turns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce duplicate factory brief charges through the app wire

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 8f8015c20a5e74cd8c73ec68d8fe22b4feb5118e)

* fix: charge factory brief calls only at the session billing boundary

The session already books each provider receipt to the job and ledger. Keeping a brief charged the job again, including rejected drafts. Preserve session booking so failed saves retain their cost and worker spending remains additive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit c2938deaf4b3e6ec8eb9c865a58c229663def4ac)

* test: audit manager and title charges beside factory briefs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit bb52801b802fd29a1b6889d55461eeeb69e66f87)

* test: exercise title billing on an unnamed job-owned conversation

Factory managers have a saved title and correctly skip model naming. Use an unnamed stage for the separate title receipt audit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit eee242d5899dd62c6d89e3186a1f83d853a67cda)

* test: align the factory keep contract with receipt-based billing

Keeping a brief preserves its cost metadata without charging the already billed call again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 3af433d90bf6809c42152de9b480be3ab3b9e99a)

* test: reserve the leased brief fixture for its simulated worker

The local factory starts a worker too. Pin the fixture to the remote machine so a local runner cannot win its queued lease when the surrounding suite supplies a runnable configuration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 4ff25c49ffc450b770045ee8a861156447001cd4)

* Declare local memory provenance at the reader boundary

Check late memory receipts retain their classified share as well as their total charge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Use the existing lane import alias for memory accounting

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check live brief charges against the job header

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: one entry for the brief charge fix

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the call log's served field names the provider, not the model

The recorder already checks the model that answered each call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory crossing the cap does not hide a daily refusal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* factory: a daily refusal is never read as a memory charge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover factory write retries against the job base

Exercise committed earlier work on rerun, retry after failure and resume
after Stop, and retain the refusal for an empty first attempt and rerun.
Include the waiting question when the retry fixture misses its failed check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep write retry checks at the Test approval gate

Include the following test step so the manual approval is reached,
and stop the current step so its recorded attempt can be resumed.
Check the retained commit and change through the job reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: distinguish stopped writes before and after reporting

Compare immediate Stop with Stop after the first running receipt,
using the same committed change and wire actions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: retain the factory write base across stopped attempts

Stop can revoke a lease before its first report carries ChangeBase.
Keep the original pin on the job branch before edits so its replacement
counts earlier committed work instead of comparing against the edited HEAD.
Make the three retry fixtures perform their promised write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: synchronize stopped write reports and refuse an unpinned base

Hold periodic reporting on the real lease worker until Stop, then run
the replacement with normal reporting. Verify a locked repository config
prevents Write from editing before its comparison base is preserved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: stop a factory round when its base cannot be preserved

A repository config lock must not let edits start without their durable
comparison pin. Surface the save error before calling the executor.
Keep the deterministic replacement worker on the same fixture machine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: assert the factory preparation failure receipt

A failure to preserve the base is an execution preparation error,
so read its failed-step detail rather than an out-of-rounds result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep read-only factory checks independent of Git metadata

Exercise the exported runner with a real command in a directory without
Git metadata, matching the leased-worker fixtures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: discard failed Git reads before preserving a factory base

Git errors include stderr as output. Do not mistake that text for a
branch or commit and try to persist it. Keep the existing behavior when
Git metadata is unavailable, while refusing an actual base save failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce pause after a memory stage tail detaches

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: pin one next budget for cap questions and answers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check memory admission when Pause returns

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: complete the paid memory response with valid JSON

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: give cap questions and answers one allowed next budget

Select the smallest allowed cap above recorded spend instead of doubling the old cap. Share budget choices with the runner, job reads and budget edits, and refuse a continue when no higher budget remains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain paused memory for a later resumed turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure live Pause memory and provider receipt totals

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep budget continuation available above thirty dollars

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check settled provider sums and detached memory lifecycle

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: pause live memory after a provider generation starts

Document paused memory admission, normal resume, and process-owned shutdown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: admit local job memory against the owning factory state

A worker mirror can still say running after Pause returns. Read the owning local factory for memory admission and cancellation while retaining mirror-based receipt settlement. The job conversation checks that owner before worker mirrors too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cap resumed memory at one normal scripted pass

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: offer matching budget continuation beyond thirty dollars

The fixed budget ladder returned zero after thirty dollars, trapping jobs. Derive the next cap from the job and accept that exact amount at the factory budget door.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure only stages that started before Pause

A paused job correctly has no conversation identity for its unstarted stages. Skip those stages when measuring memory rather than requiring identities from future work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: align cap metadata expectations with the next budget rule

The whole-package run found two expectations left on the former doubling and zero-limit rules. Expect the next standard five dollar cap at both public seams.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count a finished memory generation and its receipt once

A response can finish at the same time cancellation queues its receipt. Both observations describe the same provider charge, so share the existing once-only charge counter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain unfinished memory after a completed extraction

Pending counts unextracted source events. When the already-paid extraction finishes at Pause, those events can be consumed while NeedsUpdate retains the remaining compilation work. Keep the stricter source assertion for interrupted calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: name its call tag for the replay law and pass the new scan argument

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory fixtures use a step that isn't held for an unchanged checkout

A job's write step with no change now waits for a person. These tests are about memory, so their step is plain work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Take the gated copy of the union merged docs

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* factory: a write step that changed nothing is not done

A write round that reported done while the job's checkout had no change
from the run's base counted as done, so test and review ran on an
untouched tree and only publishing found nothing to commit. The runner
now reads the checkout after a write round; with no change, the round
does not pass, says "the checkout has no change from where the run
began", and goes round again or asks. Other steps may still finish with
no change, and an unreadable checkout never holds a round back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: changelog entry for #2158

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce missing memory spend through the vault interface

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: bank memory receipts and bind job compilation to finished turns

The compiler used the provider transport without a ledger consumer, while the process worker owned its lifetime independently of jobs. Bank decoded receipts before validation and keep job sources out of independent compilation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: isolate memory snapshots and verify cancellation during compilation

Keep compilation boundaries per owner, drain finished-turn journals, and classify older factory conversations from persisted memberships. Preserve projection upgrades and reject a structural bypass that discards the billed context.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover retained job sources and large memory snapshots

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: refuse memory admission on paused and published jobs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: drain owned memory snapshots and fence unchanged failed input

Reclassify retained job events independently of journal cursors, check stored pause and publication state at admission, and finish every extraction batch in the turn snapshot. New source input or an explicit Build is required to retry failed background compilation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reject repeated refinement of unchanged memory input

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: require new evidence or explicit build for further memory refinement

Record the Build request consumed by each publication so forced flushes on unchanged sources stay idle. Preserve recovery when extraction produced new unpublished evidence, and let an explicit Build advance retained guide work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: old job ownership excludes background memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: retain old factory ownership in memory inventory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: compile delegated reports with their owning turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: use the persisted place layout for delegated memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: compile delegated evidence on the owning job turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure live job memory cost and stage tail delays

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory cannot turn successful work into a cap stop

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: preserve successful stage results after deferred spending

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: bound stage memory waits and retain cancelled charges

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: enforce memory cap only at the next unfinished step

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: record completed phases before checking the next budget

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: distinguish memory delay and stop timed-out live jobs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: bound stage memory waits to the owning job lifetime

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain skipped stage evidence across job memory handoff

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: recover skipped stage evidence in the next owned memory pass

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure late memory receipts after job completion

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep an empty owned source selection empty

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count overlapping memory waits as wall time

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: attribute memory tail wait to its initiating stage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: verify live memory starts stay inside the job lifetime

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: stop live measurements with a fresh job version

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count measured memory calls independently of late prices

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: read measured job spending through the app wire

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: settle cancelled live calls before checking measured spend

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: open live factory brief chats through the in-process app

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: deliver late memory receipts through the real factory worker

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: observe the public ready flag and bound worker fixture cleanup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep job reporting alive for late memory receipts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test late memory pricing after stopping a factory job

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Observe finished steps before settling final prices and pin live helpers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reconcile paid memory after a stopped job loses its lease

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep one accounting anchor per run and retry final settlement

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test final memory settlement preserves previously counted job spend

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Measure stage answers separately from automatic brief calls

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Report per-job memory dollars and bound delays by the next step start

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test paid memory survives reopening a finished stage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Preserve paid usage in a closed stage journal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test leased memory receipts during actual factory quit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Allow bounded accounting settlement after worker cancellation

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reproduce the memory tail fixture accounting observation race

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Wait for both memory ledger and job accounting in lifecycle fixture

The ledger is published before the independent job meter update. Poll both observable totals and name standalone session closure accurately.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test quitting while final worker accounting is already settling

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep bounded accounting alive when quit interrupts settlement

Detach the existing settlement context from execution immediately, then cancel it five seconds after quit. This also covers cancellation arriving during the existing receipt wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Recognize late journal usage in the append funnel guard

The journal is a separate store from artifact indexes. Account for both journal append owners while retaining the guard against unknown append paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reproduce stage budget exemptions and unowned batch draining

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Distinguish memory cap crossings and restore bounded ordinary Flush

Keep memory in total job spend while excluding only newly charged memory from completed-round cap checks. Preserve model and tool stops, and retain finite draining only for owned turns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce duplicate factory brief charges through the app wire

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 8f8015c20a5e74cd8c73ec68d8fe22b4feb5118e)

* fix: charge factory brief calls only at the session billing boundary

The session already books each provider receipt to the job and ledger. Keeping a brief charged the job again, including rejected drafts. Preserve session booking so failed saves retain their cost and worker spending remains additive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit c2938deaf4b3e6ec8eb9c865a58c229663def4ac)

* test: audit manager and title charges beside factory briefs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit bb52801b802fd29a1b6889d55461eeeb69e66f87)

* test: exercise title billing on an unnamed job-owned conversation

Factory managers have a saved title and correctly skip model naming. Use an unnamed stage for the separate title receipt audit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit eee242d5899dd62c6d89e3186a1f83d853a67cda)

* test: align the factory keep contract with receipt-based billing

Keeping a brief preserves its cost metadata without charging the already billed call again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 3af433d90bf6809c42152de9b480be3ab3b9e99a)

* test: reserve the leased brief fixture for its simulated worker

The local factory starts a worker too. Pin the fixture to the remote machine so a local runner cannot win its queued lease when the surrounding suite supplies a runnable configuration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 4ff25c49ffc450b770045ee8a861156447001cd4)

* Declare local memory provenance at the reader boundary

Check late memory receipts retain their classified share as well as their total charge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Use the existing lane import alias for memory accounting

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check live brief charges against the job header

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: one entry for the brief charge fix

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the call log's served field names the provider, not the model

The recorder already checks the model that answered each call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory crossing the cap does not hide a daily refusal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* factory: a daily refusal is never read as a memory charge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover factory write retries against the job base

Exercise committed earlier work on rerun, retry after failure and resume
after Stop, and retain the refusal for an empty first attempt and rerun.
Include the waiting question when the retry fixture misses its failed check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep write retry checks at the Test approval gate

Include the following test step so the manual approval is reached,
and stop the current step so its recorded attempt can be resumed.
Check the retained commit and change through the job reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: distinguish stopped writes before and after reporting

Compare immediate Stop with Stop after the first running receipt,
using the same committed change and wire actions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: retain the factory write base across stopped attempts

Stop can revoke a lease before its first report carries ChangeBase.
Keep the original pin on the job branch before edits so its replacement
counts earlier committed work instead of comparing against the edited HEAD.
Make the three retry fixtures perform their promised write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: synchronize stopped write reports and refuse an unpinned base

Hold periodic reporting on the real lease worker until Stop, then run
the replacement with normal reporting. Verify a locked repository config
prevents Write from editing before its comparison base is preserved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: stop a factory round when its base cannot be preserved

A repository config lock must not let edits start without their durable
comparison pin. Surface the save error before calling the executor.
Keep the deterministic replacement worker on the same fixture machine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: assert the factory preparation failure receipt

A failure to preserve the base is an execution preparation error,
so read its failed-step detail rather than an out-of-rounds result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep read-only factory checks independent of Git metadata

Exercise the exported runner with a real command in a directory without
Git metadata, matching the leased-worker fixtures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: discard failed Git reads before preserving a factory base

Git errors include stderr as output. Do not mistake that text for a
branch or commit and try to persist it. Keep the existing behavior when
Git metadata is unavailable, while refusing an actual base save failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce pause after a memory stage tail detaches

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: pin one next budget for cap questions and answers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check memory admission when Pause returns

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: complete the paid memory response with valid JSON

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: give cap questions and answers one allowed next budget

Select the smallest allowed cap above recorded spend instead of doubling the old cap. Share budget choices with the runner, job reads and budget edits, and refuse a continue when no higher budget remains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain paused memory for a later resumed turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure live Pause memory and provider receipt totals

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep budget continuation available above thirty dollars

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check settled provider sums and detached memory lifecycle

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: pause live memory after a provider generation starts

Document paused memory admission, normal resume, and process-owned shutdown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: admit local job memory against the owning factory state

A worker mirror can still say running after Pause returns. Read the owning local factory for memory admission and cancellation while retaining mirror-based receipt settlement. The job conversation checks that owner before worker mirrors too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cap resumed memory at one normal scripted pass

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: offer matching budget continuation beyond thirty dollars

The fixed budget ladder returned zero after thirty dollars, trapping jobs. Derive the next cap from the job and accept that exact amount at the factory budget door.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure only stages that started before Pause

A paused job correctly has no conversation identity for its unstarted stages. Skip those stages when measuring memory rather than requiring identities from future work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: align cap metadata expectations with the next budget rule

The whole-package run found two expectations left on the former doubling and zero-limit rules. Expect the next standard five dollar cap at both public seams.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count a finished memory generation and its receipt once

A response can finish at the same time cancellation queues its receipt. Both observations describe the same provider charge, so share the existing once-only charge counter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain unfinished memory after a completed extraction

Pending counts unextracted source events. When the already-paid extraction finishes at Pause, those events can be consumed while NeedsUpdate retains the remaining compilation work. Keep the stricter source assertion for interrupted calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: require explicit memory consent through settings

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: require explicit consent for automatic memory

Use memory.enabled for both noticing and knowledge compilation. Empty history no longer grants consent; saved choices remain live across open chats and jobs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover fresh app memory calls through job and chat

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: stop memory requests while keeping saved knowledge available

Gate compiler requests and noticing retries on the live setting. Keep saved pages readable while off and retain pending builds across a switch. Prepare explicit-on billing fixtures and a capped live off test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: require zero new memory requests after switching off

Align remaining manual references with the shared learning switch and count request starts exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: verify fresh app leaves knowledge consent unset

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: cost words say it varies, and the manual names the job's switch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: name its call tag for the replay law and pass the new scan argument

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory fixtures use a step that isn't held for an unchanged checkout

A job's write step with no change now waits for a person. These tests are about memory, so their step is plain work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the live memory measure waits for the last brief before reading the header

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: fixtures that exercise automatic memory turn it on

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the switch test reads the one cost note, and its job step is plain work

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: a store that can't open says why, not that memory is off

The switch makes off a real state, so this refusal must not read as the person's choice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: a first start with memory off must still be able to join

Red: the off pass records its job as unbuilt, and the join check only knows waiting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* identity: a memory-off first start counts as a first start

A person who starts fresh with memory off could not join an organization, because the home read as holding work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* settings: memory's detail says again that nothing saved is deleted

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test: reproduce tombstone lookup panic on vault read failure

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: return vault read errors before checking tombstones

Deleted dereferenced a nil document when a valid vault handle failed to read its backing file. Return the read error so sync can retry instead of terminating its worker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: clarify previous and current vault read failure behavior

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* manual: name the vault read failure section by what it covers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test: two engines on one home must not both start

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* app: one engine on a home; a second waits for the first to end

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: a waiting engine starts nothing, and a child doesn't hold the home

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* factory: a write step that changed nothing is not done

A write round that reported done while the job's checkout had no change
from the run's base counted as done, so test and review ran on an
untouched tree and only publishing found nothing to commit. The runner
now reads the checkout after a write round; with no change, the round
does not pass, says "the checkout has no change from where the run
began", and goes round again or asks. Other steps may still finish with
no change, and an unreadable checkout never holds a round back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: changelog entry for #2158

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce missing memory spend through the vault interface

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: bank memory receipts and bind job compilation to finished turns

The compiler used the provider transport without a ledger consumer, while the process worker owned its lifetime independently of jobs. Bank decoded receipts before validation and keep job sources out of independent compilation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: isolate memory snapshots and verify cancellation during compilation

Keep compilation boundaries per owner, drain finished-turn journals, and classify older factory conversations from persisted memberships. Preserve projection upgrades and reject a structural bypass that discards the billed context.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover retained job sources and large memory snapshots

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: refuse memory admission on paused and published jobs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: drain owned memory snapshots and fence unchanged failed input

Reclassify retained job events independently of journal cursors, check stored pause and publication state at admission, and finish every extraction batch in the turn snapshot. New source input or an explicit Build is required to retry failed background compilation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reject repeated refinement of unchanged memory input

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: require new evidence or explicit build for further memory refinement

Record the Build request consumed by each publication so forced flushes on unchanged sources stay idle. Preserve recovery when extraction produced new unpublished evidence, and let an explicit Build advance retained guide work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: old job ownership excludes background memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: retain old factory ownership in memory inventory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: compile delegated reports with their owning turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: use the persisted place layout for delegated memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: compile delegated evidence on the owning job turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure live job memory cost and stage tail delays

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory cannot turn successful work into a cap stop

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: preserve successful stage results after deferred spending

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: bound stage memory waits and retain cancelled charges

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: enforce memory cap only at the next unfinished step

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: record completed phases before checking the next budget

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: distinguish memory delay and stop timed-out live jobs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: bound stage memory waits to the owning job lifetime

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain skipped stage evidence across job memory handoff

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: recover skipped stage evidence in the next owned memory pass

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure late memory receipts after job completion

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep an empty owned source selection empty

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count overlapping memory waits as wall time

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: attribute memory tail wait to its initiating stage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: verify live memory starts stay inside the job lifetime

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: stop live measurements with a fresh job version

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count measured memory calls independently of late prices

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: read measured job spending through the app wire

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: settle cancelled live calls before checking measured spend

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: open live factory brief chats through the in-process app

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: deliver late memory receipts through the real factory worker

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: observe the public ready flag and bound worker fixture cleanup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep job reporting alive for late memory receipts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test late memory pricing after stopping a factory job

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Observe finished steps before settling final prices and pin live helpers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reconcile paid memory after a stopped job loses its lease

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep one accounting anchor per run and retry final settlement

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test final memory settlement preserves previously counted job spend

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Measure stage answers separately from automatic brief calls

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Report per-job memory dollars and bound delays by the next step start

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test paid memory survives reopening a finished stage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Preserve paid usage in a closed stage journal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test leased memory receipts during actual factory quit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Allow bounded accounting settlement after worker cancellation

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reproduce the memory tail fixture accounting observation race

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Wait for both memory ledger and job accounting in lifecycle fixture

The ledger is published before the independent job meter update. Poll both observable totals and name standalone session closure accurately.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test quitting while final worker accounting is already settling

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep bounded accounting alive when quit interrupts settlement

Detach the existing settlement context from execution immediately, then cancel it five seconds after quit. This also covers cancellation arriving during the existing receipt wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Recognize late journal usage in the append funnel guard

The journal is a separate store from artifact indexes. Account for both journal append owners while retaining the guard against unknown append paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reproduce stage budget exemptions and unowned batch draining

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Distinguish memory cap crossings and restore bounded ordinary Flush

Keep memory in total job spend while excluding only newly charged memory from completed-round cap checks. Preserve model and tool stops, and retain finite draining only for owned turns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce duplicate factory brief charges through the app wire

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 8f8015c20a5e74cd8c73ec68d8fe22b4feb5118e)

* fix: charge factory brief calls only at the session billing boundary

The session already books each provider receipt to the job and ledger. Keeping a brief charged the job again, including rejected drafts. Preserve session booking so failed saves retain their cost and worker spending remains additive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit c2938deaf4b3e6ec8eb9c865a58c229663def4ac)

* test: audit manager and title charges beside factory briefs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit bb52801b802fd29a1b6889d55461eeeb69e66f87)

* test: exercise title billing on an unnamed job-owned conversation

Factory managers have a saved title and correctly skip model naming. Use an unnamed stage for the separate title receipt audit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit eee242d5899dd62c6d89e3186a1f83d853a67cda)

* test: align the factory keep contract with receipt-based billing

Keeping a brief preserves its cost metadata without charging the already billed call again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 3af433d90bf6809c42152de9b480be3ab3b9e99a)

* test: reserve the leased brief fixture for its simulated worker

The local factory starts a worker too. Pin the fixture to the remote machine so a local runner cannot win its queued lease when the surrounding suite supplies a runnable configuration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 4ff25c49ffc450b770045ee8a861156447001cd4)

* Declare local memory provenance at the reader boundary

Check late memory receipts retain their classified share as well as their total charge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Use the existing lane import alias for memory accounting

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check live brief charges against the job header

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: one entry for the brief charge fix

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the call log's served field names the provider, not the model

The recorder already checks the model that answered each call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory crossing the cap does not hide a daily refusal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* factory: a daily refusal is never read as a memory charge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover factory write retries against the job base

Exercise committed earlier work on rerun, retry after failure and resume
after Stop, and retain the refusal for an empty first attempt and rerun.
Include the waiting question when the retry fixture misses its failed check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep write retry checks at the Test approval gate

Include the following test step so the manual approval is reached,
and stop the current step so its recorded attempt can be resumed.
Check the retained commit and change through the job reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: distinguish stopped writes before and after reporting

Compare immediate Stop with Stop after the first running receipt,
using the same committed change and wire actions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: retain the factory write base across stopped attempts

Stop can revoke a lease before its first report carries ChangeBase.
Keep the original pin on the job branch before edits so its replacement
counts earlier committed work instead of comparing against the edited HEAD.
Make the three retry fixtures perform their promised write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: synchronize stopped write reports and refuse an unpinned base

Hold periodic reporting on the real lease worker until Stop, then run
the replacement with normal reporting. Verify a locked repository config
prevents Write from editing before its comparison base is preserved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: stop a factory round when its base cannot be preserved

A repository config lock must not let edits start without their durable
comparison pin. Surface the save error before calling the executor.
Keep the deterministic replacement worker on the same fixture machine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: assert the factory preparation failure receipt

A failure to preserve the base is an execution preparation error,
so read its failed-step detail rather than an out-of-rounds result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep read-only factory checks independent of Git metadata

Exercise the exported runner with a real command in a directory without
Git metadata, matching the leased-worker fixtures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: discard failed Git reads before preserving a factory base

Git errors include stderr as output. Do not mistake that text for a
branch or commit and try to persist it. Keep the existing behavior when
Git metadata is unavailable, while refusing an actual base save failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce pause after a memory stage tail detaches

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: pin one next budget for cap questions and answers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check memory admission when Pause returns

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: complete the paid memory response with valid JSON

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: give cap questions and answers one allowed next budget

Select the smallest allowed cap above recorded spend instead of doubling the old cap. Share budget choices with the runner, job reads and budget edits, and refuse a continue when no higher budget remains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain paused memory for a later resumed turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure live Pause memory and provider receipt totals

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep budget continuation available above thirty dollars

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check settled provider sums and detached memory lifecycle

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: pause live memory after a provider generation starts

Document paused memory admission, normal resume, and process-owned shutdown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: admit local job memory against the owning factory state

A worker mirror can still say running after Pause returns. Read the owning local factory for memory admission and cancellation while retaining mirror-based receipt settlement. The job conversation checks that owner before worker mirrors too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cap resumed memory at one normal scripted pass

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: offer matching budget continuation beyond thirty dollars

The fixed budget ladder returned zero after thirty dollars, trapping jobs. Derive the next cap from the job and accept that exact amount at the factory budget door.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure only stages that started before Pause

A paused job correctly has no conversation identity for its unstarted stages. Skip those stages when measuring memory rather than requiring identities from future work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: align cap metadata expectations with the next budget rule

The whole-package run found two expectations left on the former doubling and zero-limit rules. Expect the next standard five dollar cap at both public seams.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count a finished memory generation and its receipt once

A response can finish at the same time cancellation queues its receipt. Both observations describe the same provider charge, so share the existing once-only charge counter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain unfinished memory after a completed extraction

Pending counts unextracted source events. When the already-paid extraction finishes at Pause, those events can be consumed while NeedsUpdate retains the remaining compilation work. Keep the stricter source assertion for interrupted calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce missing factory charge settlement

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: settle factory charges from final provider receipts

Interim stream usage suppressed reconciliation even when no session billing sink could consume it. Factory calls now recover the final receipt and retain unknown-price ownership. Write through ledger overflow without holding a turn indefinitely. Memory keeps its existing billing path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: snapshot pending overflow writes across ledger flushes

Capture completion barriers without racing WaitGroup reuse. A full queue cannot certify a successful flush; it reports false under the existing deadline instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep unpriced factory receipts visibly unknown

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep factory token-only receipts unbilled

A receipt with token counts but no provider price cannot establish a dollar charge. Final-price consumers now retain the existing unknown-price marker instead of treating those counts as a settled bill. Other receipt consumers remain unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: name its call tag for the replay law and pass the new scan argument

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory fixtures use a step that isn't held for an unchanged checkout

A job's write step with no change now waits for a person. These tests are about memory, so their step is plain work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: a full usage queue never makes a turn wait

The 100 ms per overflow row added up on a stalled disk and broke the ledger's turn law. One background writer takes overflow rows instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: the ledger's one writer takes overflow rows, in order

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: format the ledger writer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: a full queue's flush waits for the kept rows instead of reporting false

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: wake the ledger writer when a row is kept after it emptied the queue

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: an answer with no usage doesn't hold the job's next step for its receipt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Take the gated copy of the union merged docs

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
#2275)

* factory: a write step that changed nothing is not done

A write round that reported done while the job's checkout had no change
from the run's base counted as done, so test and review ran on an
untouched tree and only publishing found nothing to commit. The runner
now reads the checkout after a write round; with no change, the round
does not pass, says "the checkout has no change from where the run
began", and goes round again or asks. Other steps may still finish with
no change, and an unreadable checkout never holds a round back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: changelog entry for #2158

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce missing memory spend through the vault interface

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: bank memory receipts and bind job compilation to finished turns

The compiler used the provider transport without a ledger consumer, while the process worker owned its lifetime independently of jobs. Bank decoded receipts before validation and keep job sources out of independent compilation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: isolate memory snapshots and verify cancellation during compilation

Keep compilation boundaries per owner, drain finished-turn journals, and classify older factory conversations from persisted memberships. Preserve projection upgrades and reject a structural bypass that discards the billed context.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover retained job sources and large memory snapshots

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: refuse memory admission on paused and published jobs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: drain owned memory snapshots and fence unchanged failed input

Reclassify retained job events independently of journal cursors, check stored pause and publication state at admission, and finish every extraction batch in the turn snapshot. New source input or an explicit Build is required to retry failed background compilation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reject repeated refinement of unchanged memory input

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: require new evidence or explicit build for further memory refinement

Record the Build request consumed by each publication so forced flushes on unchanged sources stay idle. Preserve recovery when extraction produced new unpublished evidence, and let an explicit Build advance retained guide work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: old job ownership excludes background memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: retain old factory ownership in memory inventory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: compile delegated reports with their owning turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: use the persisted place layout for delegated memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: compile delegated evidence on the owning job turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure live job memory cost and stage tail delays

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory cannot turn successful work into a cap stop

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: preserve successful stage results after deferred spending

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: bound stage memory waits and retain cancelled charges

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: enforce memory cap only at the next unfinished step

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: record completed phases before checking the next budget

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: distinguish memory delay and stop timed-out live jobs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: bound stage memory waits to the owning job lifetime

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain skipped stage evidence across job memory handoff

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: recover skipped stage evidence in the next owned memory pass

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure late memory receipts after job completion

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep an empty owned source selection empty

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count overlapping memory waits as wall time

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: attribute memory tail wait to its initiating stage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: verify live memory starts stay inside the job lifetime

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: stop live measurements with a fresh job version

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count measured memory calls independently of late prices

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: read measured job spending through the app wire

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: settle cancelled live calls before checking measured spend

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: open live factory brief chats through the in-process app

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: deliver late memory receipts through the real factory worker

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: observe the public ready flag and bound worker fixture cleanup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep job reporting alive for late memory receipts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test late memory pricing after stopping a factory job

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Observe finished steps before settling final prices and pin live helpers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reconcile paid memory after a stopped job loses its lease

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep one accounting anchor per run and retry final settlement

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test final memory settlement preserves previously counted job spend

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Measure stage answers separately from automatic brief calls

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Report per-job memory dollars and bound delays by the next step start

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test paid memory survives reopening a finished stage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Preserve paid usage in a closed stage journal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test leased memory receipts during actual factory quit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Allow bounded accounting settlement after worker cancellation

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reproduce the memory tail fixture accounting observation race

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Wait for both memory ledger and job accounting in lifecycle fixture

The ledger is published before the independent job meter update. Poll both observable totals and name standalone session closure accurately.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test quitting while final worker accounting is already settling

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep bounded accounting alive when quit interrupts settlement

Detach the existing settlement context from execution immediately, then cancel it five seconds after quit. This also covers cancellation arriving during the existing receipt wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Recognize late journal usage in the append funnel guard

The journal is a separate store from artifact indexes. Account for both journal append owners while retaining the guard against unknown append paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reproduce stage budget exemptions and unowned batch draining

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Distinguish memory cap crossings and restore bounded ordinary Flush

Keep memory in total job spend while excluding only newly charged memory from completed-round cap checks. Preserve model and tool stops, and retain finite draining only for owned turns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce duplicate factory brief charges through the app wire

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 8f8015c20a5e74cd8c73ec68d8fe22b4feb5118e)

* fix: charge factory brief calls only at the session billing boundary

The session already books each provider receipt to the job and ledger. Keeping a brief charged the job again, including rejected drafts. Preserve session booking so failed saves retain their cost and worker spending remains additive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit c2938deaf4b3e6ec8eb9c865a58c229663def4ac)

* test: audit manager and title charges beside factory briefs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit bb52801b802fd29a1b6889d55461eeeb69e66f87)

* test: exercise title billing on an unnamed job-owned conversation

Factory managers have a saved title and correctly skip model naming. Use an unnamed stage for the separate title receipt audit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit eee242d5899dd62c6d89e3186a1f83d853a67cda)

* test: align the factory keep contract with receipt-based billing

Keeping a brief preserves its cost metadata without charging the already billed call again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 3af433d90bf6809c42152de9b480be3ab3b9e99a)

* test: reserve the leased brief fixture for its simulated worker

The local factory starts a worker too. Pin the fixture to the remote machine so a local runner cannot win its queued lease when the surrounding suite supplies a runnable configuration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 4ff25c49ffc450b770045ee8a861156447001cd4)

* Declare local memory provenance at the reader boundary

Check late memory receipts retain their classified share as well as their total charge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Use the existing lane import alias for memory accounting

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check live brief charges against the job header

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: one entry for the brief charge fix

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the call log's served field names the provider, not the model

The recorder already checks the model that answered each call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory crossing the cap does not hide a daily refusal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* factory: a daily refusal is never read as a memory charge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover factory write retries against the job base

Exercise committed earlier work on rerun, retry after failure and resume
after Stop, and retain the refusal for an empty first attempt and rerun.
Include the waiting question when the retry fixture misses its failed check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep write retry checks at the Test approval gate

Include the following test step so the manual approval is reached,
and stop the current step so its recorded attempt can be resumed.
Check the retained commit and change through the job reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: distinguish stopped writes before and after reporting

Compare immediate Stop with Stop after the first running receipt,
using the same committed change and wire actions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: retain the factory write base across stopped attempts

Stop can revoke a lease before its first report carries ChangeBase.
Keep the original pin on the job branch before edits so its replacement
counts earlier committed work instead of comparing against the edited HEAD.
Make the three retry fixtures perform their promised write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: synchronize stopped write reports and refuse an unpinned base

Hold periodic reporting on the real lease worker until Stop, then run
the replacement with normal reporting. Verify a locked repository config
prevents Write from editing before its comparison base is preserved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: stop a factory round when its base cannot be preserved

A repository config lock must not let edits start without their durable
comparison pin. Surface the save error before calling the executor.
Keep the deterministic replacement worker on the same fixture machine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: assert the factory preparation failure receipt

A failure to preserve the base is an execution preparation error,
so read its failed-step detail rather than an out-of-rounds result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep read-only factory checks independent of Git metadata

Exercise the exported runner with a real command in a directory without
Git metadata, matching the leased-worker fixtures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: discard failed Git reads before preserving a factory base

Git errors include stderr as output. Do not mistake that text for a
branch or commit and try to persist it. Keep the existing behavior when
Git metadata is unavailable, while refusing an actual base save failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce pause after a memory stage tail detaches

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: pin one next budget for cap questions and answers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check memory admission when Pause returns

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: complete the paid memory response with valid JSON

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: give cap questions and answers one allowed next budget

Select the smallest allowed cap above recorded spend instead of doubling the old cap. Share budget choices with the runner, job reads and budget edits, and refuse a continue when no higher budget remains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain paused memory for a later resumed turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure live Pause memory and provider receipt totals

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep budget continuation available above thirty dollars

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check settled provider sums and detached memory lifecycle

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: pause live memory after a provider generation starts

Document paused memory admission, normal resume, and process-owned shutdown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: admit local job memory against the owning factory state

A worker mirror can still say running after Pause returns. Read the owning local factory for memory admission and cancellation while retaining mirror-based receipt settlement. The job conversation checks that owner before worker mirrors too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cap resumed memory at one normal scripted pass

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: offer matching budget continuation beyond thirty dollars

The fixed budget ladder returned zero after thirty dollars, trapping jobs. Derive the next cap from the job and accept that exact amount at the factory budget door.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure only stages that started before Pause

A paused job correctly has no conversation identity for its unstarted stages. Skip those stages when measuring memory rather than requiring identities from future work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: align cap metadata expectations with the next budget rule

The whole-package run found two expectations left on the former doubling and zero-limit rules. Expect the next standard five dollar cap at both public seams.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count a finished memory generation and its receipt once

A response can finish at the same time cancellation queues its receipt. Both observations describe the same provider charge, so share the existing once-only charge counter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain unfinished memory after a completed extraction

Pending counts unextracted source events. When the already-paid extraction finishes at Pause, those events can be consumed while NeedsUpdate retains the remaining compilation work. Keep the stricter source assertion for interrupted calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce missing factory charge settlement

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: settle factory charges from final provider receipts

Interim stream usage suppressed reconciliation even when no session billing sink could consume it. Factory calls now recover the final receipt and retain unknown-price ownership. Write through ledger overflow without holding a turn indefinitely. Memory keeps its existing billing path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: snapshot pending overflow writes across ledger flushes

Capture completion barriers without racing WaitGroup reuse. A full queue cannot certify a successful flush; it reports false under the existing deadline instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep unpriced factory receipts visibly unknown

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep factory token-only receipts unbilled

A receipt with token counts but no provider price cannot establish a dollar charge. Final-price consumers now retain the existing unknown-price marker instead of treating those counts as a settled bill. Other receipt consumers remain unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: name its call tag for the replay law and pass the new scan argument

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory fixtures use a step that isn't held for an unchanged checkout

A job's write step with no change now waits for a person. These tests are about memory, so their step is plain work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: a ledger row with no turn must move today's figure

Red: spend.changed fires only when a chat turn ends, so Factory triage, scouts and page AI never reach the page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: today's figure moves when any ledger row lands, not only when a turn ends

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: a full usage queue never makes a turn wait

The 100 ms per overflow row added up on a stalled disk and broke the ledger's turn law. One background writer takes overflow rows instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: overflow rows move today's figure too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: the ledger's one writer takes overflow rows, in order

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: finish the merge of the ledger writer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: format the ledger writer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: format the ledger writer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: drop the merge's duplicated writer loop

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: a full queue's flush waits for the kept rows instead of reporting false

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: spend.today names Factory AI, so triage, scouts and page AI aren't called chats

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name what changed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: wake the ledger writer when a row is kept after it emptied the queue

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* spend: an answer with no usage doesn't hold the job's next step for its receipt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* factory: a write step that changed nothing is not done

A write round that reported done while the job's checkout had no change
from the run's base counted as done, so test and review ran on an
untouched tree and only publishing found nothing to commit. The runner
now reads the checkout after a write round; with no change, the round
does not pass, says "the checkout has no change from where the run
began", and goes round again or asks. Other steps may still finish with
no change, and an unreadable checkout never holds a round back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: changelog entry for #2158

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce missing memory spend through the vault interface

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: bank memory receipts and bind job compilation to finished turns

The compiler used the provider transport without a ledger consumer, while the process worker owned its lifetime independently of jobs. Bank decoded receipts before validation and keep job sources out of independent compilation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: isolate memory snapshots and verify cancellation during compilation

Keep compilation boundaries per owner, drain finished-turn journals, and classify older factory conversations from persisted memberships. Preserve projection upgrades and reject a structural bypass that discards the billed context.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover retained job sources and large memory snapshots

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: refuse memory admission on paused and published jobs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: drain owned memory snapshots and fence unchanged failed input

Reclassify retained job events independently of journal cursors, check stored pause and publication state at admission, and finish every extraction batch in the turn snapshot. New source input or an explicit Build is required to retry failed background compilation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reject repeated refinement of unchanged memory input

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: require new evidence or explicit build for further memory refinement

Record the Build request consumed by each publication so forced flushes on unchanged sources stay idle. Preserve recovery when extraction produced new unpublished evidence, and let an explicit Build advance retained guide work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: old job ownership excludes background memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: retain old factory ownership in memory inventory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: compile delegated reports with their owning turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: use the persisted place layout for delegated memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: compile delegated evidence on the owning job turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure live job memory cost and stage tail delays

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory cannot turn successful work into a cap stop

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: preserve successful stage results after deferred spending

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: bound stage memory waits and retain cancelled charges

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: enforce memory cap only at the next unfinished step

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: record completed phases before checking the next budget

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: distinguish memory delay and stop timed-out live jobs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: bound stage memory waits to the owning job lifetime

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain skipped stage evidence across job memory handoff

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: recover skipped stage evidence in the next owned memory pass

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure late memory receipts after job completion

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep an empty owned source selection empty

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count overlapping memory waits as wall time

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: attribute memory tail wait to its initiating stage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: verify live memory starts stay inside the job lifetime

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: stop live measurements with a fresh job version

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count measured memory calls independently of late prices

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: read measured job spending through the app wire

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: settle cancelled live calls before checking measured spend

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: open live factory brief chats through the in-process app

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: deliver late memory receipts through the real factory worker

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: observe the public ready flag and bound worker fixture cleanup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep job reporting alive for late memory receipts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test late memory pricing after stopping a factory job

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Observe finished steps before settling final prices and pin live helpers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reconcile paid memory after a stopped job loses its lease

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep one accounting anchor per run and retry final settlement

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test final memory settlement preserves previously counted job spend

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Measure stage answers separately from automatic brief calls

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Report per-job memory dollars and bound delays by the next step start

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test paid memory survives reopening a finished stage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Preserve paid usage in a closed stage journal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test leased memory receipts during actual factory quit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Allow bounded accounting settlement after worker cancellation

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reproduce the memory tail fixture accounting observation race

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Wait for both memory ledger and job accounting in lifecycle fixture

The ledger is published before the independent job meter update. Poll both observable totals and name standalone session closure accurately.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test quitting while final worker accounting is already settling

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep bounded accounting alive when quit interrupts settlement

Detach the existing settlement context from execution immediately, then cancel it five seconds after quit. This also covers cancellation arriving during the existing receipt wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Recognize late journal usage in the append funnel guard

The journal is a separate store from artifact indexes. Account for both journal append owners while retaining the guard against unknown append paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reproduce stage budget exemptions and unowned batch draining

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Distinguish memory cap crossings and restore bounded ordinary Flush

Keep memory in total job spend while excluding only newly charged memory from completed-round cap checks. Preserve model and tool stops, and retain finite draining only for owned turns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce duplicate factory brief charges through the app wire

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 8f8015c20a5e74cd8c73ec68d8fe22b4feb5118e)

* fix: charge factory brief calls only at the session billing boundary

The session already books each provider receipt to the job and ledger. Keeping a brief charged the job again, including rejected drafts. Preserve session booking so failed saves retain their cost and worker spending remains additive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit c2938deaf4b3e6ec8eb9c865a58c229663def4ac)

* test: audit manager and title charges beside factory briefs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit bb52801b802fd29a1b6889d55461eeeb69e66f87)

* test: exercise title billing on an unnamed job-owned conversation

Factory managers have a saved title and correctly skip model naming. Use an unnamed stage for the separate title receipt audit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit eee242d5899dd62c6d89e3186a1f83d853a67cda)

* test: align the factory keep contract with receipt-based billing

Keeping a brief preserves its cost metadata without charging the already billed call again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 3af433d90bf6809c42152de9b480be3ab3b9e99a)

* test: reserve the leased brief fixture for its simulated worker

The local factory starts a worker too. Pin the fixture to the remote machine so a local runner cannot win its queued lease when the surrounding suite supplies a runnable configuration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 4ff25c49ffc450b770045ee8a861156447001cd4)

* Declare local memory provenance at the reader boundary

Check late memory receipts retain their classified share as well as their total charge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Use the existing lane import alias for memory accounting

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check live brief charges against the job header

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: one entry for the brief charge fix

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the call log's served field names the provider, not the model

The recorder already checks the model that answered each call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory crossing the cap does not hide a daily refusal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* factory: a daily refusal is never read as a memory charge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover factory write retries against the job base

Exercise committed earlier work on rerun, retry after failure and resume
after Stop, and retain the refusal for an empty first attempt and rerun.
Include the waiting question when the retry fixture misses its failed check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep write retry checks at the Test approval gate

Include the following test step so the manual approval is reached,
and stop the current step so its recorded attempt can be resumed.
Check the retained commit and change through the job reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: distinguish stopped writes before and after reporting

Compare immediate Stop with Stop after the first running receipt,
using the same committed change and wire actions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: retain the factory write base across stopped attempts

Stop can revoke a lease before its first report carries ChangeBase.
Keep the original pin on the job branch before edits so its replacement
counts earlier committed work instead of comparing against the edited HEAD.
Make the three retry fixtures perform their promised write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: synchronize stopped write reports and refuse an unpinned base

Hold periodic reporting on the real lease worker until Stop, then run
the replacement with normal reporting. Verify a locked repository config
prevents Write from editing before its comparison base is preserved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: stop a factory round when its base cannot be preserved

A repository config lock must not let edits start without their durable
comparison pin. Surface the save error before calling the executor.
Keep the deterministic replacement worker on the same fixture machine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: assert the factory preparation failure receipt

A failure to preserve the base is an execution preparation error,
so read its failed-step detail rather than an out-of-rounds result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep read-only factory checks independent of Git metadata

Exercise the exported runner with a real command in a directory without
Git metadata, matching the leased-worker fixtures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: discard failed Git reads before preserving a factory base

Git errors include stderr as output. Do not mistake that text for a
branch or commit and try to persist it. Keep the existing behavior when
Git metadata is unavailable, while refusing an actual base save failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: reproduce pause after a memory stage tail detaches

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: pin one next budget for cap questions and answers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check memory admission when Pause returns

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: complete the paid memory response with valid JSON

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: give cap questions and answers one allowed next budget

Select the smallest allowed cap above recorded spend instead of doubling the old cap. Share budget choices with the runner, job reads and budget edits, and refuse a continue when no higher budget remains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain paused memory for a later resumed turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure live Pause memory and provider receipt totals

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: keep budget continuation available above thirty dollars

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: check settled provider sums and detached memory lifecycle

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: pause live memory after a provider generation starts

Document paused memory admission, normal resume, and process-owned shutdown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: admit local job memory against the owning factory state

A worker mirror can still say running after Pause returns. Read the owning local factory for memory admission and cancellation while retaining mirror-based receipt settlement. The job conversation checks that owner before worker mirrors too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cap resumed memory at one normal scripted pass

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: offer matching budget continuation beyond thirty dollars

The fixed budget ladder returned zero after thirty dollars, trapping jobs. Derive the next cap from the job and accept that exact amount at the factory budget door.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: measure only stages that started before Pause

A paused job correctly has no conversation identity for its unstarted stages. Skip those stages when measuring memory rather than requiring identities from future work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: align cap metadata expectations with the next budget rule

The whole-package run found two expectations left on the former doubling and zero-limit rules. Expect the next standard five dollar cap at both public seams.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: count a finished memory generation and its receipt once

A response can finish at the same time cancellation queues its receipt. Both observations describe the same provider charge, so share the existing once-only charge counter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: retain unfinished memory after a completed extraction

Pending counts unextracted source events. When the already-paid extraction finishes at Pause, those events can be consumed while NeedsUpdate retains the remaining compilation work. Keep the stricter source assertion for interrupted calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: expose lost Factory page charges on failed generations

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: give Factory scout a real checkout in spending fixture

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: bank Factory page charges before handling failed generations

Record provider billing events because failed generations may return no response. Use the returned response only when no event was banked, so success counts once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: require an exact ledger-owner inventory for production model sends

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: inventory current ledger owners and explicit model-spend audit debt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: preserve returned usage and include model sends in initializers

Keep the returned response price-presence fact instead of synthesizing an explicit zero. Enumerate declaration initializers as well as named functions and prove package-scope mutations are rejected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: a failed scout reply counts in its limit, and charges may arrive concurrently

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: name its call tag for the replay law and pass the new scan argument

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: memory fixtures use a step that isn't held for an unchanged checkout

A job's write step with no change now waits for a person. These tests are about memory, so their step is plain work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: name the pull request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
santoshkumarradha and others added 7 commits November 9, 2026 15:52
…#2545)

* engine: an idle job chat and an unchanged vault cost nothing per look

The person-Stop watch every job chat runs ten times a second listed and
decoded every job on the floor (twice) per look; it now answers from a
per-floor manager index rebuilt only when the floor folder changes and
re-reads the managed job only when its document is replaced. A vault whose
sealed bytes are unchanged is no longer decrypted again on every slot read.

100 idle looks over 60 jobs: 12,000 documents decoded -> 0.
400 reads of an unchanged vault: 400 decrypts -> 0.

* changes: idle-cpu entry (#2545)

* factory: unlock the manager index from a defer
…an AgentField node (#2478)

General intake (inbox, Jira, the routing table with an AI filter and priority order), the factory as an AgentField node whose triggers are its intake, AgentField runs as factory steps, named pipelines and a pipeline library, the engine idle-cost rounds, codeaf --events/--steer for watched harness runs, and app/v3 (#2524 go-aheads, #2505 Claude Code, #2544, #2545) merged in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… per file (#2551)

On macOS the presence watch handed each held chat's session folder to fsnotify, whose kqueue backend opens a descriptor for every file in a watched folder. A darwin-only watcher now holds one handle per folder, plus one for answers.jsonl only while an answer waits to be drained (since #2478 a quiet session relies on the watch for answer appends). Other platforms keep fsnotify behind the same seam. Holds, viewOpen and the idle sweep are unchanged.

Mac proof (macOS 15.6): engine fds per held chat +6 -> +2; takeover reaction unchanged (1-3 ms); 20 darwin and presence tests pass 3/3. Fixes most of #2471.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…en by the opening stamp (#2556)

Fixes #2549. stampMeta now settles the owed user stamp before its snapshot, so an effort set right after a conversation opens is no longer overwritten by the opening stamp (a product bug, old since #876). A deterministic seam test fails 10/10 without the fix and passes 50/50 with it; the real-engine test ran 196 loaded iterations without a failure. The unknown-effort check sets up its own rung, so the classifier can retry one rung alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings dev's twelve commits that app/v3 lacked (#2426, #2491, #2509, #2536,
#2537, #2540, #2543, #2548, #2553, #2555, #2557, #2558) onto app/v3
(a687c92), so app/v3 can land on dev as one trunk. 28 files conflicted;
every behaviour from both sides is kept, and both sides' tests pass.

Decisions a reviewer should know:
- #2556 (app/v3) and #2557 (dev) are the same stamp-overwrite fix; one
  implementation is kept in internal/session/placemeta.go.
- Git auto-maintenance (#2400 vs #2548): dev's rule wins. Maintenance is
  suppressed only in owned copies, not on a person's own repository (#2548's
  TestTaskCommitSuppressesMaintenanceOnlyInOwnedCopies); #2400's finishing-
  commit tests still pass, since they run in owned copies. craft keeps
  #2400's attached maintenance. One flag list lives in internal/gitpolicy.
- A device's own directory record keeps app/v3's read-first write, so waiting
  messages (Outbox, Took) and an unchanged record survive. dev's
  TestLiveDriveRefusedStartupRetriesAndUploads asserted one relay call per
  refused startup, which a read-first write makes two; it now asserts no call
  beyond one startup attempt per wait, measured from the first attempt.
- dev's per-drive driveNotices keeps its name; app/v3's desktop callbacks
  become appDriveNotices.
- The desktop's local publication loop starts dev's storage refresh while
  hosted storage is off, as engine.go and syncsetup/state.go already do.
- Closing chats stop following ownership and checking their lease once
  displaced or stopped, so they never read /cells/ after /release.
- The two syncsetup TestMain functions are one; two manual headings gained
  the asker's words where merging both corpora moved their retrieval.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…low app/v3's engine law

Two findings from the landing candidate's first CI run (#2561):

- app/v3's law TestEngineCleanupUsesEngineDir rejects registering
  cellstore.StopSpawned directly. dev's storage-off tests (#2509, #2536) did
  it in nine files; they now take celltest.EngineDir, so LIFO cleanup stops
  spawned engines before their temporary folders go. Assertions unchanged.
- TestAServedFactoryStartsAGoAheadsJobWithNoAppAttached left a writer in its
  temporary home ("directory not empty"). The served factory's GitHub poll
  was cancelled but never joined, so a pass could still commit a live event
  or a go-ahead receipt after serveFactory returned. app/v3 had the same race
  (17 of 20 there). startFactoryServerPoll now returns a stop that cancels
  and joins the current pass, and serveFactory defers it (20 of 20).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AbirAbbas and others added 9 commits November 10, 2026 03:52
… a first run that works (#2546)

* processgroup: launch each Windows process tree inside its own Job Object

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* jobs, services, checks: stop whole launch trees and report stopped only once confirmed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* shell: tell the model the Windows facts for python, .cmd shims and stopping servers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fsreplace: open held state files with delete sharing and retry state removal on Windows

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automation: publish presence locks with shared handles so reminders fire on Windows

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* session: record spend through a lock-capable reader so Windows keeps every charge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* store: count charges made at the current clock tick in current spending windows

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* session: permanently delete conversations on Windows while keeping the deletion lock

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* storage: publish sync, service and task state through fsreplace

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* setup and doctor: shared default model, a key check at launch, and machine readiness checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* opener: open URLs and files on Windows so sign-in can reach the browser

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* install.sh: put codeaf on the Windows user PATH when run from Git Bash

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* exec: headless commands skip the login shell; edits keep UTF-16 BOMs and line endings; the model sees LF output

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* paths: the folder picker and report references accept native Windows paths

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* checkout: preview new files and offer Open/Reveal on Windows through the same contained open

The contained open behind checkout.diff's untracked preview and checkout.locate was Unix-only, so on Windows every new file read as 'could not be opened safely' and no changed file could be opened or revealed. os.Root works on Windows; only the leaf's open flags differ (no O_NOFOLLOW or O_NONBLOCK there), and the same-file check after the open still refuses a leaf swapped for a link.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fsreplace: replace a file another handle holds open on Windows with a POSIX-semantics rename

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* tests: give Windows a moment to close a stopped listener's socket before rebinding its port

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci: run the tests that pass natively on Windows on every pull request (advisory)

No engine test ran on Windows before; the engine only cross-built for it. The new job runs 306 named tests in 29 packages on windows-latest: process-tree stops, state replacement while held, reminders, spending, native paths, UTF-16 and line endings. It is not part of check yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changes: one entry for the Windows stability PR (#2546)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* setup: check the provider key when setup or health is first read, never at launch

A launch-time probe made a quiet engine send /key and /auth/key before anyone asked (TestC27NewChatByDefaultStaysQuiet), and the CLI started one on every command. Starting the engine now only enables key health; the first setup.get or health.check (or a key handover) starts one background check per key, and reads never wait for it. doctor still checks directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* keyhealth: unlock the cache from a defer, as the guard law requires

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* exec: macOS and Linux keep their login shell, output bytes, file-tool bytes and health checks

The non-login foreground shell, CRLF-to-LF output, line-ending coercion on
rewrites and pipefail health checks are Windows-only. On macOS and Linux a
headless command loads the login profile again (bash -lc), captured output
keeps its bytes, ordinary rewrites write what they are given, and a health
check's status is its last command's, as before this PR. The PR's text and
shell contract tests now state each OS's rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* session: macOS and Linux keep their job stop receipt and ledger lock order

Proof that the whole tree is gone before a kill reports stopped, and locking
the usage ledger through its read handle, are Windows-only leaves. Unix keeps
the original signal receipt and writer flock order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* head, tui3: macOS and Linux keep report pointers and folder parents as before

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* seniordev/patch: intercept only bash -lc, as before

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* verification: macOS and Linux keep the bounded snapshot diagnostics

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* setup: key check, fresh-setup default and doctor on every OS; desk readiness probes on Windows only

The lazy key check, the default model for a fresh setup and codeaf doctor's
new checks run on every OS; their wire fields are optional, so older payloads
still decode. The desk's setup and health reads run gh and git probes only on
Windows: on macOS gh reads its token from the login Keychain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: say what each OS does

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
PRODUCT BUG. Retire's departure check (waitForGone) and DialStartingHost's
probe (hostLockHeld) both took the workspace's host lock exclusively. Two
readers checking at once could each see the other's probe as a host still
holding the workspace, so Retire reported a host saving with the lock held
after it had given the workspace up, and a dial could wait on a host that
had already left. Probes now take a shared, non-blocking lock; only a host
takes it exclusively. The test now holds a second reader through the save.

Loaded reproduction (GOMAXPROCS=2, synchronous disk writer, CPU spinner,
-count=2000): 12 failures before, 0 after; app/v3 a687c92 failed 16.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TEST TIMING: an earlier queued writer could enter its run loop only after
the baseline stack count, making the process appear to own two writers.
Flush existing usage before measuring and retain every close assertion.
Seed an earlier writer so the original ordering fails under load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TEST TIMING: a background publish can display displacement before /quit
reaches the writer. Wait for real writer close before forcing the final
publish; preserve every restored-receipt assertion.

PRODUCT BUG: the last seal watch retires before drive close starts, and
news in that gap had no recipient. Retain it for the closing receipt.
Cover the gap separately so the fixture ordering cannot hide it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dev's pre-release hand-test fixes (#2567) meet app/v3 in 23 files. Every
behaviour from both sides is kept; where both sides fixed the same thing,
one implementation remains (provider retry admission after waits, the late
spending save, the shared daily-limit gate, compiler billing).

The knowledge compiler needed a ruling, because the two sides' retry
contracts disagreed:

- The retry policy is app/v3's: a five-second backoff doubling to 120
  seconds, and a persisted three-attempt cap per input that survives
  reopening the vault. Build or a new source resets the cap; a job-owned
  failure is retried only by its owner. A persisted total bound is a
  stronger money guard than a time pause.
- dev's persisted 30-second paid-failure pause becomes a two-second repeat
  guard held in memory by the open vault. It keeps the refusal dev's test
  asserts (an identical request right after an unusable paid answer is
  not bought again) without blocking app/v3's deliberate retries.
- The cap's key is now each ordinary source's id and content hash. The old
  key hashed max(arrived), which a revisit moves to now, so the cap could
  reset silently.
- The compiler asks with reasoning disabled (EffortOff, dev's fix for a
  provider that ignored effort "none" and billed 8000 reasoning tokens for
  nothing), with the request identity unchanged so cached answers stay
  cached.
- One billing path: the daily gate runs before every compiler call and
  provider attempt, and each billed answer writes one ledger line and one
  receipt, usable or not.

Proofs, by name:
  P1 TestKnowledgeOrdinaryBuildCapsEveryBilledAnswer
  P2 TestKnowledgeOrdinaryBuildRetryCapSurvivesRepeatedHandovers
  P3 TestKnowledgeDailyLimitBlocksEveryRetry, TestKnowledgeDailyLimitBlocksTransportRetry
  P4 TestKnowledgeMixedPaidAnswersMatchCallLogLedgerAndReceipts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…computer"

The section added with the final publish notice was headed "Quitting after
another computer continued the chat". Its words matched the general question
so closely that it ranked first and pushed the page that explains moving a
chat (availability, pairing, platforms) out of the results, which
TestMovingChatIntroductionsStateAvailabilityAndLegacyLimits caught in the
full suite. The heading now speaks the asker's words for this case: the line
printed after quitting when another device took the chat over.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One rule now recognises a paid call already in the spending ledger:
- session.UsageCallID decides it everywhere, and UsageLine.ChargeID delegates to it.
- A carried explicit id wins: CallID, "remote:"+RemoteEvent, or the row ID.
- Otherwise dev's eight-field fingerprint decides: At, Session, Model, Role, Calls, Input, Output and USD.
- A line with neither is still counted against its session's uncovered rows,
  as both parents did. A row with no identity is never another call's duplicate.
- Two different explicit ids are two calls, even when their fingerprints match.
  This is app/v3 #2332's take law.

PRODUCT BUG fixed (from the earlier landing merge, and also present on app/v3
alone): reopening a chat with no receipts booked its paid turns and folded task
spend again. All four TestReopeningFoldedTaskSpendNeverRebillsWorkerCalls
variants now pass. Seven contract tests in usage_remerge_contract_test.go pin
the rule.

Conflicts resolved, keeping both sides:
- cellindex/usage.go, cellstore/receipt.go, cellstore/types.go, executor/modelcall.go
- session/digest.go, loop.go, task_remote_money.go, usage_identity.go (add/add),
  usage_ledger.go, usage_receipt_test.go
- tui3/commands.go keeps app/v3's slashcat table, and dev's "Memory tab" wording
  moves into slashcat/catalog.go
- manual/chat/settings.md and what-i-remember.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PRODUCT BUG (a regression against dev, from the landing merge): the
first landing merge kept app/v3's take road in chatContinuer and dropped
dev's 255-byte component cap in takeCopyBucket, leaving an inline
"copy-"+encodeWorkspace(origin) BucketFor that fails with "file name too
long" once a chat's origin path exceeds the filesystem component limit.

Root dev's capped copy-bucket helper at an explicit home directory via
takeCopyBucketAt(dir, origin), keep takeCopyBucket(origin) delegating to
takeCopyBucketAt(home.Dir(), origin), and wire chatContinuer's BucketFor
to takeCopyBucketAt(s.Home, origin).

Contract:
- C1. Three round trips (six takes) of one chat between two homes all
  succeed and the chat opens on each side after each take, with every
  created path component at most 255 bytes and a stable bucket per
  origin.
- C2. A chat the taker already has keeps its existing local root
  (takeRootForAt), unchanged.
- C3. app/v3's take road is otherwise unchanged: its two-field takeover,
  its s.Home-relative paths, and every existing take, move and
  older-chats test on both sides, unedited.

Fixes #2579

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AbirAbbas and others added 2 commits November 10, 2026 12:59
PRODUCT BUG from the dev ace7888 merge. dev #2577 and app/v3 fixed the
same defect, a late charge missing from Home's figure, in different places:
- dev stamped at the end of every bank;
- app/v3 stamped after each late door (reconciled, memory billing, aux usage).

Keeping both stamped a turn's own calls as they were banked, and again when
the turn sealed. An interrupted turn therefore saved its spend twice
(TestLateRecoveredCancellationSavesCumulativeSpendOnce).

Now bank stamps every bill that is not a step of a live turn, and nothing
else does. The redundant stamps in reconciled and the memory billing are
gone. Both laws hold:
- TestPostSealChargesStampTheHomeTotal (dev);
- TestLateRecoveredCancellationSavesCumulativeSpendOnce (app/v3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…wice

PRODUCT BUG from the dev ace7888 merge. Rebuilding the usage index:
- flushes queued live rows before reading the ledger (dev);
- flushes again after importing (app/v3).

When the writer could not settle, the rebuild waited out both deadlines.
That spent a take's whole hook budget, and the take failed with
"handoff: i/o timeout" (TestTakeLedgerQueuedOrSettledLiveCall/queued).

The second wait now happens only if the first one settled. An unsettled
queue still refuses a row whose identity is already queued
(usageIdentityCursor), so the import cannot double-book while it waits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AbirAbbas and others added 9 commits November 10, 2026 14:26
One conflict, in cmd/codeaf/poolrecord.go: app/v3 stands the pool judge
down while the pool is being handed over (poolJudgeStandsDown), and dev's
#2583 stops it once its owner's context is cancelled. Both reasons to stay
quiet are kept, in both the per-landing judge and the sweep.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… their owner closes

The people loader read profiles on a detached goroutine under
context.Background(), and the CODEOWNERS and pipeline asks ran the same way,
so a read could still be calling GitHub, or caching an answer, after the
factory that wanted it had closed. In tests that call landed in whichever
test had swapped http.DefaultTransport next.

PeopleLoader and PipelineLoader now have a Close that cancels the read in
flight, keeps a cancelled read's answer out of the cache, and waits at most a
second for it to finish. factoryPeople returns no client when there is no
GitHub token, as factoryRules already did, so a tokenless factory starts no
read at all. stopFactoryLoaders detaches a profile's loaders and closes them,
and the factory service calls it when it closes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…se waits for the first

Several things the app backend starts could outlive its Close:
- the solo factory's poll and people readers;
- a brief write already past its debounce;
- the provider-model warm-up;
- a catalog warm-up opened by the clock process or the profile road.

A second concurrent Close also returned at once, while the first was still
tearing down.

Now:
- The brief debounce has a context. stop cancels the write in flight and
  waits for it within the pool's close budget.
- The local factory forbids its read loops before cancelling the host.
- The host's solo handler refuses to rebuild once the host is closing.
- The local factory's starts refuse once the backend is closing.
- The backend's Close waits for the provider-model warm-up and stops the pool
  errands. It also closes the factory loaders, and closes the engine process it
  assembled, the same close RunApp makes after the wire ends.
- closeEngineProcess clears the process slot before closing it.
- The clock process releases through closeAll.
- The profile road closes its catalogs.
- A second Close, of the backend, the local factory or the factory host, waits
  for the first to finish.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
These tests tore down their HTTP fakes or temp directories while something
they had started was still running, which is how one test's leftover calls
reached the next test's fake transport. The leftovers were:
- a backend;
- a pipe fleet's catalog;
- a profile road;
- the usage-ledger writer.

- The offline local factory fixture gets a poll source that polls nothing, so
  it no longer polls GitHub through http.DefaultTransport.
- The memory tests register their HTTP fakes before newLocalSetup, so under
  t.Cleanup's last-in-first-out order the backend closes first.
- wireOver waits for its server to return.
- Every onePipeFleet and profileRoad a test opens is closed in its cleanup.
- moneyVault and the session daily-limit tests stop the usage writer they
  started.
- The touched cmd/codeaf tests use testHome(t), so a late write names its
  file.

No assertion was removed or loosened.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r cannot hold it

The waits the leak fixes added to Close must not lengthen a quit without
limit.
- closeWait in internal/factory/github is a variable now (still one second).
- The brief debounce waits on briefStopWait (the pool's close budget).
- stopFactoryLoaders closes its loaders concurrently, so it spends one bound
  in total rather than one per loader.
- A read that ignores cancellation can no longer cache its answer or start
  another read after its loader closed.

Tests show that a profile read, a CODEOWNERS ask, a pipeline read and a brief
write that each ignore cancellation cannot hold their Close past its bound,
and that two stuck loaders still close within one.

The profile road keeps the per-service catalogs it opens and closes them with
the road. Closing them when the readiness wait returned cancelled a stale
custom catalog's refresh before it could finish.

testHome(t) now reports only for a failed test. When a test calls TempDir
before it, its walk runs before the shared removal and would list every file
the test legitimately left.

Worst case added to a desktop quit by these waits: briefStopWait + closeWait,
two seconds, reached only when a write or a GitHub read ignores cancellation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No conflicts. #2588 keeps a settled task's row in the conversation after an
answered landing, gives a just-settled node its result page, and stops the
pool's index pull at close; it touches internal/tui3, the pool pull code and
the chat manual, none of which the landing branch had changed since the
last merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The guard laws refuse a Lock whose Unlock is not deferred directly under it,
because an absorbed panic inside the section would leave the lock held and
turn into a deadlock, and they refuse a bare go statement for the same reason.
The leak fixes had added ten such sections and one bare goroutine.

Each section is now its own small function with its Unlock deferred: the
factory host's close claim, the brief debounce's stop and re-arm, the local
factory's teardown claim and its release, the profile road's catalog set, the
engine process slot, and the two loader caches. The brief stop's waiter starts
through guard.Go. Behaviour is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…down once

The PR gate on 392b2b0 found three faults in the Close changes.

- A second Close waited on the backend's closing flag. Tests that only
  simulate a quit set that flag themselves, so their cleanup's Close waited
  for a teardown nobody had started, and cmd/codeaf hung for 15 minutes in
  TestAppCrewProposalLaterWhileClosingRefuses. The teardown claim now has its
  own flag: every Close tears down once, and only a genuinely concurrent
  second Close waits for the first.
- solo() refused once the host's context was cancelled. A host built to serve
  only is given a context that is already cancelled, so
  TestAppFactoryMountTakesOverWhenTheSoloHostCloses and
  TestAppFactoryProxyKeepsIssuedRunnerCredentials failed. The refusal now
  reads a flag that close sets.
- The backend's Close closed the engine process it had assembled, and stopped
  the engine's pool errands. But the app wire calls Close before it waits for
  the calls still in flight, and before RunApp's CloseUsage. The engine's
  receipt wait therefore ran inside Close, ahead of a receipt that lands after
  the quit. Both are backed out: the engine closes after the wire has
  finished, where RunApp always closed it.

The quit-path receipt test, TestMemoryLateReceiptReachesFactoryHeaderAfterQuit,
went red here with provider=0.0400 memory=0.0000 ledger=0.0200 header=0.0200.
That is the same line #2566 records failing about one run in three on app/v3
itself. So that red is not shown to be caused by this branch, and #2566 stays
open; the back-out stands on the ordering argument above.

The worst case the remaining Close waits add to a quit is unchanged: two
seconds, briefStopWait + closeWait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TestUsageMillionRowLedgerFlush timed the writer's first write over a
1,000,000-row ledger on this machine, three alternating runs a side.
app/v3 a687c92 took 592.9, 575.4 and 582.1 ms. The landing head 392b2b0
took 595.2, 611.8 and 603.6 ms: every landing run slower than every app/v3
run, by about 3.4%.

The merge made ChargeID delegate to UsageCallID, and UsageCallID to
UsageExplicitID. So each row of the scan copied the whole UsageLine twice and
called strings.TrimSpace three times, where app/v3 read two fields.

ChargeID now reads CallID, RemoteEvent and ID in place, and copies the row
only when it has to fingerprint it. The trim answers first for an id whose
first and last bytes are visible ASCII, which has nothing to trim.
UsageExplicitID and UsageCallID give the same answers as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants