Skip to content

Merge dev (through #2002) into app/v3: relay, two-computer fixes, quit saving - #2009

Merged
AbirAbbas merged 34 commits into
app/v3from
merge/dev-into-app-v3
Oct 28, 2026
Merged

AbirAbbas merged 34 commits into
app/v3from
merge/dev-into-app-v3

Conversation

@AbirAbbas

@AbirAbbas AbirAbbas commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Brings engine dev through #2002 into app/v3, so the desktop's engine (codeaf app) carries the relay work: the Raft relay (#1950), the two-computer fixes (#1967, #1990), quit saving (#2001), the older-chat copy rule (#2002), private task copies behind their opt-in (#1944), the knowledge vault (#1943) and the add-device card (#1949, #1968). The desktop's relay setup, pairing and cross-computer chats build on this.

#1942 squash-merged dev up to 7f5da935c into app/v3, so a plain merge replays those commits as conflicts. The first commit records 7f5da935c as merged (-s ours, no tree change); the second is the real merge of 3bf6072fd (26 conflicts, resolved keeping both sides); the third brings in app/v3 #2004/#2005.

App-mode changes made while resolving

  • Quit saving in app mode (Quitting right after work saves quietly and never branches bookkeeping #2001): runApp now runs the same bounded final save and drive close on shutdown as the terminal does, so quitting the desktop right after a turn no longer leaves "could not save this chat", a stranded hold, or an empty-turn branch chat.
  • Admission + relay renewal: ConfirmWork retires a stale send hold and wakes the renewal readers, so continuing a chat another computer held admits work at once (real-relay takeover test, red before, green after).
  • Directory records: kept dev's canonical Raft aliases and strict decoder; the app's devices.*, sync.get and location routes keep their wire shape (no fields lost).
  • Session: unions app/v3's factory/process/task-source work with dev's knowledge, workspace ownership and saving errors; historical plan rows keep their recorded source.
  • go.mod: union of both sides, no version changes; furrow pin stays at dev's v0.3.5.

Checks

  • go build ./..., go vet on the touched packages, gofmt: clean. Cross builds (linux, darwin, windows; amd64 and arm64): pass.
  • CI was dispatched on this branch and compared with the parents, since both parents are already red:
    • Full check (run 40615478974, base app/v3 run 40555137316): no new failure from the merge. The one test not failing on app/v3, TestR25ArchivedPreconditionalClient, fails the same way on dev's own Full run (40528811891; offline module lookup for the archived client). Fixed by the merge: TestAStoppedTaskResultCannotAutoStartItsRequestAgain, TestFactoryMergeQuestionsReachTheMergeSection, TestPlainQuestionsReachThePageThatAnswersThem, TestTheChatManualAnswersTheQuestionsPeopleAsk.
    • PR gate (run 40615476131, base 40582883520): the lock-defer and taxonomy laws and the two runner-slot tests also fail on app/v3's tip df717e212. TestTheSourceHoldsEveryRowTheAppLists is fixed here. TestANewDirectionArrivesWithTheClaimUnspent failed once: it is an inherited fixture race (its second checkpoint reader can finish after the script ends; reproduced on both df717e212 and this branch by delaying that reader), and passes 20/20 here. The fix is test-only (watchReadings + watchedContext, as checkpoint_test.go documents) and belongs in its own PR.

Also in this PR now

CI before merging

🤖 Generated with Claude Code

santoshkumarradha and others added 30 commits October 25, 2026 09:35
…1935)

Preserve explicit worker model selection for joined tasks and descendants. Add model-selected independent and teammate chats, atomic launch claims, updated tool guidance, and regression coverage.

Validated all five live OpenRouter launch scenarios with DeepSeek v4.1 Flash and Kimi K3. All PR checks passed.

Refs #1839.
Add direct skill definition, capability and troubleshooting answers, including memory-independent behavior. Pin the four original queries and verify tool-result delivery through real hosted terminal chats with memory saving on and off.

All eight live cases passed using exact deepseek/deepseek-v4.1-flash. All PR checks passed.

Closes #1937.
* Scope All to the current conversation tab strip

* Keep screenshot launches guarded and use rendered count grammar

* Guard the screenshot terminal option command

* Expect current-tab scope in the All navigation hint

* Scope the All wall's copy and empty state to open tabs

The in-wall All chip now says the grid holds the tabs in this view
rather than saved conversations, and an empty scoped grid says no
conversations are open here instead of promising saved ones will
appear. wallEmptyText is shared by the live grid and the currently
unused wallEmptyRow, so the two stay in step. The dock test now proves
the label and the mark each open the wall scoped to open tabs, and the
render test pins the scoped empty line.

Assisted-by: CodeAF (deepseek-v4.1-flash-1017)
Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com>

* Make open tabs a property of the open wall

closeWall now clears wall.openTabs with wall.on, so no reader after a
close can mistake a closed scoped wall for an open one. wallOpen still
reads the scope before it closes the wall, which is where the team is
kept.

Assisted-by: CodeAF (deepseek-v4.1-flash-1017)
Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com>

* Open an overview unpicked

A pick outlived a close that was not esc, so a card picked in the wall left
the next overview in selection mode - and in the All scope it could be a card
nobody had picked there. Every overview now opens with nothing picked.

Assisted-by: CodeAF (deepseek-v4.1-flash-1017)
Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com>

* Name the All door as the tabs in view in the manual

places.md still promised all saved conversations from ▦ All, and keys.md
listed alt+v, /wall and the strip door as one thing. They are two views
since this change.

Assisted-by: CodeAF (deepseek-v4.1-flash-1017)
Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com>

---------

Co-authored-by: CodeAF <267109073+agentfield-bot@users.noreply.github.com>
* tui3, config: the gate row lands on the conversation you are in, and /status names the gate (#1063)

The three Safety rows — `ask before running`, the tool exceptions and the shell
command rules — wrote the profile and left the running gate alone, while the
YOLO badge read that profile live. So the badge and the gate disagreed in both
directions: `allow` lit the badge over a gate that kept asking, and `prompt` put
it out over a gate that was still wide open, which is the false-safety claim
#322 and #325 exist to prevent.

`applySetting` now pushes those three rows through `ApplyApprovals` — the seam
/permissions already drops a banked rule through — and refreshes the badge on
the same keystroke, on the branch where the push landed. A rebuild that could
not be done is said out loud on the panel's foot line in /permissions' own
words, `saved · from the next session`, and the badge stays with the gate in
force rather than following the file.

And /status carries the posture it never carried: `approvals prompt`, `allow` or
`deny`, in the `ask before running` row's own words, in the note, in --json and
on the phone's status sheet, from the one posture reading the badge uses. The
status row keeps its negative-space badge and the segment is no longer written
into the page twice.

The note's label column is measured from the widest label it carries, so three
crew assertions that spelled `"\ncrew"` plus exactly five spaces were
assertions about every other row on the page. They read the crew line by its
label now.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

* attribution: the footer names CodeAF, and still links to agentfield.ai (#1075)

* attribution: the footer names CodeAF, and still links to agentfield.ai

The pull request, issue and comment footers read "Drafted with agentfield ai".
They now read "Drafted with CodeAF"; the link, its utm parameters and the
"reviewed and owned by the author" tail are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GNBScbyNAbbEQ5jn9G6auy

* docs: the change entry for the CodeAF footer

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GNBScbyNAbbEQ5jn9G6auy

---------

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

* session, prompt, tui3: a wrong completion check is answered with [no change], and the answer stands (#1065 round 1) (#1067)

* session, prompt: a wrong completion check is answered with [no change], and the answer stands (#1065 round 1)

The end-of-turn reader saw 600 bytes of the last message, so a long table read
as cut off; the carry-on told the model to explain itself, and the explanation
became the only answer left in view under the fold.

The reader now sees up to 8 KB of the last words, and a cut it still has to
make ends with `[clipped by codeaf: N of M bytes]`, which its ask says is never
evidence of a cut-off answer. The carry-on lead says who wrote the note and
offers one exact reply, `[no change]`: it ends carrying on for the ask without
another reading, the transcript keeps it for the model, and neither replay nor
the live feed draws it — so the settled answer before it is what the fold finds.

system.md trades `# Tone` and `# Delivery` for `# The answer`, `# Words or
work`, `# When corrected` and `# Messages from codeaf`, and the two lines that
pushed action on questions are qualified. The lean page keeps all four; the
prefix waivers rise by the 1,705 bytes that cost, dated and named.

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

* docs: the change entry takes the pull request's own number

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

* session: a quiet landing is asked about the notes it was handed, not the page that names them

The worker's system page now names [silent] and [stuck] under
`# Messages from codeaf`, so a search of the whole request found the tag on
the page. childWasHanded reads the conversation and leaves the page out.

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

* session, prompt: [no change] answers the note it follows, and a long ask leaves the last words cut by name (#1065 round 1)

Self-review of #1067 found the token honoured on the wrong replies. The gate
read the turn's carry-on count, so a model that made calls in answer to the
note and then said [no change] ended the turn unread on an answer those calls
may have made stale, and a token after a load or ask nudge counted too. It now
reads the transcript: the token must be the very next reply to the reader's
own lead (answeredNoteUnchanged), and the dead declined flag is gone.

An ask up to the whole digest rides in its head, so eight kilobytes of last
words behind a long ask reached the backstop clip and ended in a bare `…`.
checkpointClipSaid takes the room that is left. A turn that ends on the token
no longer starts the route judge or hands the token to the memory reflex, and
a later reading's last words skip it.

`# Messages from codeaf` named `[still asked]`, which only the reader ever
receives, and left out `[stuck]` and `[held]`, which the conversation does; it
now names the tags that arrive and says any other opening, such as
`[image #1]`, is the person's. The waivers sit on the measured 1,779 bytes, and
the manual and the change entry say what the gate actually honours.

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

* prompt: the new sections keep the rules the old two carried, and say what stays in view (#1065 round 1)

Owner review of #1067. Replacing `# Tone` and `# Delivery` had dropped five
rules; each is back inside the new sections: match the person's task and
vocabulary, choose the safe option under uncertainty, evidence and blocking
details complete, never a compiling scaffold or a narrowed test, never
half-solved work.

`# The answer` said only the last message reaches the person, which `# Planning`
contradicts: the plan and the per-batch sentence are shown while a turn runs. It
now says that once a turn ends only its last message stays in view.

`# Words or work` sat beside `## Work or words`, which spends the same two words
on hand-off sizing; it is `# Answer or change`, and it says an answer asked for
in chat is written in the reply, not a file, unless the person asks for one.

The waivers sit on the new measurement, 2,035 bytes over dev on both arms.

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

* prompt: the messages-from-codeaf list names every note codeaf writes

The system prompt now distinguishes conversation user-role notes from private reader and tool-result markers. A structural test follows the production constants, and the byte waivers record the exact fixed and lean measurements.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* session: no change skips judgment only when it answers the reader

The completion loop now uses the transcript context before suppressing the route judge or substituting the prior answer for memory. Tests cover the withdrawn and ordinary-token memory paths, and the manual separates counting from display.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test: the model stub replays the no-change fold

The OpenAI-compatible stub can now drive the real binary through eleven read rounds, a long table, a rejecting reader, and the exact token reply. The recipe records the tmux setup and the expected log and screen evidence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* prompt: the standing-news tag stays with the event, not on the page

The messages-from-codeaf list had named [something you set up fired], and
TestTheSteeringLineReadsAsNewsAndNotAsARequest refused it: that line says
under the news itself what it is and what not to do about it, so the page's
copy is one law bought on every request of every turn for a turn most
sessions never have. The list keeps the compaction tags, the structural test
now files the standing frame with the private voices the page must not name,
and the waivers fall to the new measurement (fixed 55,280, lean 47,055).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Abir Abbas <abirabbas1998@gmail.com>

* Clean internal experiment records and revive test/ux for codeaf (#1068)

* chore: clean internal experiment records and revive test/ux for codeaf

Public-repository cleanup ahead of opening the docs to outside readers.

- Remove four internal DOE/experiment record directories that reference
  private issue and pull-request numbers and are not current-product
  documentation: docs/design/pin-doe, plan-gate-doe, turn-wall-share-doe,
  and workspace-foundation.
- Neutralize internal machine references in benchmark and design docs:
  the bench host ("spark"), its secrets.env path, and a personal working
  branch name, all now generic placeholders.
- Revive test/ux/, the user-facing journey suite, for the renamed product:
  collapse the removed v1/v2 dual-surface logic into the single v3 surface,
  repoint the binary to cmd/codeaf and state to ~/.codeaf/CODEAF_*, and
  delete gate.sh whose premise was the v1-vs-v2 parity table.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* chore: drop internal conversation-runtime wave records, keep the measurement contract

The conversation-runtime directory was a local development wave: day-by-day
status, /private/tmp paths, "owner authorized" process notes, draft PR numbers,
and local change logs. None of it is current-product documentation a public
reader needs. Keep PARETO.md, which is the benchmark's cited measurement
contract, trimmed of its dangling calibration link and owner language.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* chore: neutralize the remaining bench-host and work-machine references

A wider sweep found the internal bench host ("Spark"), its secrets path
(fleet/secrets.env), and a scratch ops directory still named in live docs,
bench READMEs and bench scripts that the earlier redaction had not touched.
Rename the host to "benchhost"/"the bench host", drop the secrets path, and
replace "the laptop" as the owner's own machine with a neutral "work machine"
where it meant the internal one. The manual's first-person "the laptop you are
sitting at" is public-facing and left alone.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* chore: fix review findings in the public-cleanup diff

- PERF.md: drop a private tracker issue reference (#846).
- PARETO.md: restore a complete sentence dropped when the calibration link was
  removed, and align tense with the remaining present-tense claim.
- home-mission-control/DESIGN.md: drop the internal "fleet" system name and
  an owner-framing phrase, consistent with the secrets-path scrub elsewhere.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs/design: the two records live code still cites stay in the tree

plan-gate-doe/REPORT.md is the measured evidence behind the plan-gate default
that twelve comments in cmd/codeaf, internal/config, internal/session and
internal/splitgate point at, and turn-wall-share-doe is the run
internal/session/turnwall.go says it was added from. CLAUDE.md keeps the
record on purpose for exactly this reason, and neither folder names a machine,
a secret or a private address.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test/ux: the revived journey suite goes back out

#991 removed it because it drove the v1 and v2 surfaces, and the revival did
not change that: its journal verb reads graph.db tables (messages, nodes,
usage, commands) that the v3 chat never writes, lib.sh still waits for the
resident, nothing runs it from the Makefile or a workflow, and its only
evidence was bash -n. docs/JOURNEY.md says internal/e2e is the one suite that
drives the binary, and that stays true. A suite that has been run green
against bin/codeaf can come back on its own pull request with a target that
runs it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs/changes: entry for #1068

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs/changes: the #1068 entry says why the journey suite is out, correctly

The first draft said the v3 chat never writes the graph.db tables the suite
reads. Running the suite showed both message rows landing, so that sentence
was wrong; what is true is that nothing runs the suite, it had never been run,
and its first journey fails on its first screen check (#1072).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: codeaf <agentfield-bot@users.noreply.github.com>
Co-authored-by: Abir Abbas <abirabbas1998@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

* session: task commits are authored by the bot account, not codeaf@localhost

The author codeaf stamped on the commits it lands was `codeaf <codeaf@localhost>`, which no GitHub account can link, so every branch codeaf landed failed the CLA check. It now writes `codeaf <agentfield-bot@users.noreply.github.com>` - the identity the Co-Authored-By trailer already used - and keeps `codeaf@localhost` and `aforge@localhost` recognised as legacy by taskCommitIdentity.

GLM 5.3's review caught an unmarked retired name in permissions.md that reddened the namelaw gate, and seven stale test fixtures; both fixed. Laws gate, session, exec and cmd are green.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* README: show the pinned-version install line the release test requires (#1103)

* README: show the pinned-version install line the release test requires

The Install rewrite dropped the VERSION=<tag> pin example, and
TestPublishedPinExamplesGiveVersionToBash reads every published install
surface for `| VERSION=` because `VERSION=<tag> curl ...` is an easy
mistake to copy — it sets the variable for curl instead of the install
script. The pin goes back inline on the same host the section already
uses, with the releases page still linked for tags.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs: the change entry for the README pin example

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

---------

Co-authored-by: codeaf <agentfield-bot@users.noreply.github.com>

* telemetry: anonymous usage counts, off with CODEAF_TELEMETRY=off (#1095)

* telemetry: the package behind anonymous usage counting, per the wire contract

Four typed events (first_run, session_started, session_ended, fault) with the
contract's allowlisted, bucketed props; the opt-out ladder with its off
reasons; a hashed install identity at 0700/0600; a fault fingerprint over
codeaf function names only; a JSON-lines spool with a deadline-bounded flush
of up to 50 events per POST, 7-day age drops and a 1000-line cap; the notice
constant with NoticeShown/MarkNoticeShown gating the flush; and Show() for a
future codeaf telemetry show.

A library only: nothing in cmd/codeaf, internal/session or internal/config
reads it yet.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* telemetry: tests, doc, and contract fixes for the usage-count package

Adds the package's tests — bucketing edges, the opt-out ladder, install
identity, the notice, fingerprinting, the spool against an httptest relay
(cap, age drop, 50-per-POST splitting, a hard deadline against a hanging
server, silence on failure) and the privacy law that no sentinel string
reaches the wire. docs/TELEMETRY.md documents the notice, what is sent and
how to turn it off, with a test that fails if the doc's property table
drifts from the Go allowlist.

Fixes four drifts from the wire contract found running those tests: the
<1m duration bucket was missing, the fingerprint's argument stripping cut
too much, the rc channel suffix must be a single integer, and under go
test a config-off rung outranks the test rung in the reason reported.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* telemetry: fix review defects 1-8 with tests

Fingerprint stability (last-( cut, .go:/main./created-by frame rules),
counted-batch flush, guarded and multi-process-safe spool, BucketDuration
edges, Configure-gated write paths, O_EXCL install id, install.json
preservation, buildinfo.Dirty accessor.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* installer: record the install locally and print the telemetry notice

Following the telemetry wire contract: a successful install now writes
telemetry/install.json under the state root (dir 0700, file 0600) with
the install method, channel and UTC timestamp, leaves any existing
install_id alone, and makes no request the download steps did not
already make. At the very end the installer prints the contract's
notice to stderr after one blank line, or one line saying telemetry is
off when CODEAF_TELEMETRY/DO_NOT_TRACK opts out. README gains a plain
Telemetry section pointing at docs/TELEMETRY.md, and a new
test/installer-telemetry.sh proves the marker, the notice and the
opt-out without a network.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* installer: four review corrections to the telemetry commit

The review of 71ff95a6b found four things. Revert the one line that
changed `if [[ -z "${TAG:-}" ]]; then` back to `${TAG:-}`: under
set -u an unset TAG must reach api_problem, not crash. In
write_install_marker the channel lands in JSON verbatim and CHANNEL
can come from the environment, so only stable, rc, staging or dev is
recorded and anything else becomes "unknown" — with test cases for a
valid channel and for we"ird, both asserting the marker parses
(python3 -m json.tool, or jq, else a strict channel check). Fence the
README Telemetry notice in a ```text block so Markdown keeps its
alignment, wording byte for byte. Clear the two shellcheck findings in
test/installer-telemetry.sh with one block disable for SC2016/SC2034 —
ok() evals those strings, so the quoting is deliberate — and export
CODEAF_TELEMETRY/DO_NOT_TRACK where a subshell needs them, without
weakening any assertion.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* release-downloads: daily PostHog report of public GitHub release download counts

A scheduled workflow reads the repository's releases from the public GitHub
API and sends one codeaf:release_downloads event per binary asset
(codeaf-<os>-<arch>[.exe]) — sidecars like checksums.txt are not reported.
Each event carries release_tag, channel (stable or the prerelease suffix),
prerelease, os, arch, the cumulative download_count and the UTC
snapshot_date; the event id is derived from tag, asset and date, so a
same-day rerun deduplicates instead of counting twice. No code in the
binary; counts come only from public release data.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* release-downloads: tests cover the workflow, docs and pagination

The dry-run unittests now also assert the identifiers the wire spec spells
out where the script and the workflow spell them: the schedule, dispatch,
contents-read permissions, built-in token, project-key secret and host
variable in .github/workflows/release-downloads.yml, the public-data
paragraph in docs/rules/promotion.md, stdlib-only imports, pagination
across API pages, the host override and the /batch/ endpoint, sidecar
skipping, draft skipping, and the channel words stable, rc, dev and
staging.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* release-downloads: four review corrections to the script

Channel detection now follows the tag grammar in
cmd/codeaf-release/version.go: stable, rc, dev and staging tags match
their four patterns and anything else reports "unknown", so
dev-20260917-09299019bcdc is no longer mislabelled "stable".

--dry-run works without CODEAF_POSTHOG_PROJECT_KEY, the local and test
path: the batch prints with api_key "<unset>" and exits 0, and only the
sending path stops with the notice.

Every event carries "$geoip_disable": true next to
"$process_person_profile": false, since the sender is a CI runner whose
location is meaningless. The module docstring now names
release-downloads.yml, the file that exists.

The fixture holds one tag of each shape plus an unrecognised one, and
the tests assert the channel for each, including the two real dev and
staging tags.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* release-downloads: test runs on Python 3.9 too

ScriptSourceTest read sys.stdlib_module_names unguarded; the attribute
arrived in Python 3.10, so the suite errored on macOS stock python3
(3.9.6). Guarded with getattr so the pinned import-set check stands on
its own where the attribute is absent; 32 tests pass on 3.9, 3.10 and
3.14.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* installer telemetry: the notice is read from docs/TELEMETRY.md

The test read .telemetry-contract.md, a working note that is not in the
repository and never will be, so it aborted with "telemetry contract is
missing" in a clean checkout. It now extracts the fenced block under
"## The notice" in docs/TELEMETRY.md — the same bytes as the Notice
constant in internal/telemetry/notice.go — and holds the installer's
TELEMETRY_NOTICE to it as before; a new assertion holds the README's
telemetry fence to it too, byte for byte, so the invariant the suite
existed to guard now has its third leg.

Comments in scripts/install.sh, internal/telemetry/telemetry.go and this
test now name docs/TELEMETRY.md as the source of the notice.

No Go code changed and no Go tests were run. shellcheck is clean; the
suite's 33 passing assertions include the two new notice checks, and the
same two stat(1)-related assertions that already failed on this GNU
machine before the change still fail with identical output.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* release-downloads: the download-counts paragraph lives in docs/TELEMETRY.md

docs/rules/promotion.md was the only home the paragraph had, and it came
with a fallback pinning the branch to the state before the telemetry
doc existed. The paragraph moves, byte for byte, into docs/TELEMETRY.md
as a final "## Download counts" section beside the rest of the telemetry
story, and promotion.md returns to its origin/dev content, so this
branch now touches no release docs.

test_no_telemetry_doc_on_this_branch asserted docs/TELEMETRY.md did not
exist and is deleted. The two tests that read the paragraph read it
where it now lives: both require a "## Download counts" section in
docs/TELEMETRY.md naming .github/workflows/release-downloads.yml,
scripts/release_downloads.py and the checksums.txt sidecar, and the
promotion.md fallback is gone.

No Go code changed and no Go tests were run; the property table in
docs/TELEMETRY.md is untouched. The suite passes: 31 tests, OK.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* installer telemetry: the mode check asks GNU stat first

On Linux `stat -f` succeeds and reports the filesystem, so the BSD-first
order never reached the GNU form and both permission checks failed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CqfhxLrrBNQxwPNvEgoBGR

* telemetry: refuse to send to the production relay from a test binary

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* telemetry: testHome points the endpoint at a dead loopback

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* telemetry: the orphan-adoption test names the dead relay it now gets

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* telemetry: a test pins that no test binary reaches the production relay

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* telemetry: three tests pin the test-binary relay refusal

Each asserts through real calls, not a re-implemented effect: the
newRelay override lands after testHome and a flush reaches that
recorder; a default-endpoint flush is refused like a dead relay
(zero round trips, event still spooled); forcing the ladder on
cannot reopen the path.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* telemetry: allocation-free session counters

Snapshot returns the SessionStats fields the SessionEnded constructor
takes, so the wiring hands the struct straight over. Cost is stored as
integer micro-dollars and divided back once, in Snapshot. Count* never
ask whether telemetry is enabled and never allocate.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* telemetry: gofmt the counters test

One alignment fix in the many-goroutine test; no behaviour change.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* config: telemetry setting row (CODEAF_TELEMETRY)

Key "telemetry", on|off, default on, env pin CODEAF_TELEMETRY, in the
interface group beside the history row. TelemetryAtIn walks the ladder —
pin, project file, profile config, default — and TelemetryOffReason
answers whether config turned the pipe off and names which rung did.
The row sits in ProjectKeys so a repository may answer for itself.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* cmd/codeaf: the telemetry command, and the setting it writes

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* cmd/codeaf: session lifecycle events at execute (work in progress)

Configure, notice, first_run and session_started at the start of every session
command, session_ended and a one-second flush at its exit. The end event does
not yet carry the session counters.

* provider: count answered model calls and their spend at the log door

* session: count sealed turns and executed tool calls

* cmd/codeaf: the session-ended event carries the run's counters

telemetryEnd builds the end event from telemetry.Snapshot so turns, model
calls, tool calls and cost reach the spool; the stop-reason switch becomes
a map over the exit ladder; and both lifecycle constructors are gated on
the ladder being on so a disabled run touches no state.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* cmd/codeaf: the counters test says the tally is process-wide

The exact turn, call and cost assertions hold for one run of the package; a
repeated run counts into the same atomic tally again.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* guard: a fault hook Recover calls, for a reporter above this package

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* cmd/codeaf: the fault event at both crash sites, off the current session

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* cmd/codeaf: a test that the crash site spools a fault and not the panic text

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs: finish the anonymous usage counts contract and add the 1095 changelog entry

* guard: the fault hook sits behind an atomic pointer

A goroutine recovering a panic read the hook while a test on another
goroutine wrote it, which the race detector reported. SetOnFault stores it
once; Recover loads it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* cmd/codeaf: a task's interrupt word comes from the signal, not the cancelled context

stopSignals cancels the signal context at the end of every errand, so the
flag read from its Err said interrupted for a run that finished cleanly.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* tui3, installer: the telemetry row has a tab, and a custom install writes no marker

TestEverySettingRowHasATab wants every registry row on the settings panel.
TestCustomInstallOutsideTheStateRootDoesNotRunAdoption wants an install
outside the state root to leave the login's state folders alone, so the
install marker is written only when the root exists or the install is in it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* cmd/codeaf: the counters test zeroes the process tally first

Other tests in the package drive model and tool calls through the doors
that count, so the exact bands held only when the test ran alone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* cmd/codeaf: the version test drives the telemetry lifecycle, not execute

execute starts writers under the state root that outlive the test, and the
temp dir's cleanup raced them. The lifecycle alone is what the test pins.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* cmd/codeaf: gofmt the lifecycle test

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: codeaf <agentfield-bot@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

* session: the carry-ladder fixtures wait for the mark's drawing, not race the scheduler (#1104)

* session: the carry-ladder fixtures wait for the mark's drawing, not race the scheduler

TestASuccessfulHandoffJournalsTheRungThatSuppliedTheBrief failed once in a full
make check at line 118: the worker's brief was the running model's draft, not
the mastermind's written handoff.

THE CAUSE IS NOT A STOLEN STEP. Reproduced under load (60 CPU burners, the
fixture run 1500 times), every request the fixture makes is either the turn's
own or one the aside answers: the queue carries only `round`, `draft` and
`writer`, and the errands beside the turn (title, namer, the mark's drawing)
never touch it. What moves is the ROUND COUNT — 41 to 54 across the runs —
because the mark's drawing is a sidecar (sidecar.go) whose landing the scheduler
places, and the turn is never held for it. On a busy machine the turn runs its
rounds off faster than the drawing's goroutine is scheduled onto a core, the gap
between the net firing and the drawing landing outruns the slack the script
carries ([checkpointSlack] says so in its own words), the writer's ask lands
PAST the end of the 53-step script, the scripted completer answers it with its
past-the-end line `(unscripted)` — which is not prose — the handoff rung reads
degenerate, and the carry ladder falls onto the draft. That is #392 exactly, one
mechanism along.

WHAT FIXES IT IS WAITING FOR THE READING. checkpointAgent now puts a watch on
the turn's own context ([watchReadings]) and the handover fixtures submit
through [watchedContext], so [answerWhenQuiet] holds each request that carries
the belt until every reading beside it has landed — the order a real turn has,
where a real model spends seconds on a step and the small readings land inside
it. The gap is then one boundary whatever else the machine is doing, and a
script long enough to cross the mark is long enough.

THE REGRESSION TEST holds the drawing back by a fixed wall time — standing in
for a scheduler that does not reach it in time — and insists on the behaviour:
the turn does not spend its script racing the drawing, and the writer's rung
still supplies the brief. It fails without the watch (the turn makes 54 rounds)
and passes with it.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs: the change entry for the carry-ladder drawing race

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

---------

Co-authored-by: codeaf <agentfield-bot@users.noreply.github.com>

* docs: the install proxy is live, and the manual says so (#1076)

* docs: the install proxy is live, and the manual says so

`https://agentfield.ai/get/codeaf` has served `scripts/install.sh` since
2026-09-16, the day the repository went public, so the compiled-in manual
and docs/GUIDE.md stop saying it is "not serving yet" and stop calling the
repository private. The proxied line goes first, as the README already has
it; the raw GitHub line is described as the script under it. Both pages say
to pipe into `bash`, not `sh`, because the script uses pipefail and `[[ ]]`
and dash rejects them, and both state the channel path (`/dev`, `/staging`,
`/rc`) beside the flags. The manual also says what the proxy refuses: a
script without its channel line exactly once, or a body that is not a shell
script, is a 502 rather than the wrong thing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs/changes: entry for #1076

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

* A launch says a newer codeaf is out, and /update installs it and restarts (#1060)

* The release-tag grammar has one home, and it is internal/update

Stable, rc, dev and staging tags are parsed, compared and bumped in exactly one
place now. cmd/codeaf-release keeps its next, kind and prune behaviour and its
tests unchanged; it reads the grammar from the new package rather than owning a
second copy of it.

A second parser is the thing that drifts: the updater about to land needs the
same answer to "is this tag newer than that one" that the release tool gives,
and one source of truth is the only way they cannot disagree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* internal/update: look up a release, check it, and replace this binary with it

The launch check answers only for a build that could sensibly be behind: a
stable tag, or a release candidate whose stable line has since been published.
A dev or staging build, a make build sha and an unstamped binary make no request
at all, because a channel build is deliberately newer than stable and a source
build belongs to whoever built it. The answer is cached for a day beside the
profile, and every failure is silence.

The install downloads into the target's own directory, matches the sha256 from
checksums.txt, and only then renames over the running file, so a refused
download, a wrong checksum or an unwritable directory all leave the original
binary exactly where it was and no temp file behind. The API token never leaves
the API host: the redirect policy strips it the moment a redirect changes host,
which Go does not do on its own for another port or a subdomain. Windows has no
atomic rename over a running image, so it goes through ReplaceFileW and puts the
original back when Windows reports the one failure that moves it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* tui3: a launch says a newer codeaf is out, and /update installs it

The launch line is one dim transcript note carrying both versions, /update, and
the curl line, because the command is what the owner asked to be given. It
arrives as a command rather than an opening read, so the first frame is drawn
before the network is asked anything and a slow or refused answer draws nothing.

/update — /upgrade is the same road — names the tag, then the checksum and where
it landed, then quits with a restart plan for the door to carry out. Already
newest says so and stays. It refuses while a turn or a task is running, and once
it has started, one shared guard keeps a second /update and a new turn from
beginning underneath it: the restart must never quit on top of live work, and
the home page's Enter roads go through the same guard chat's does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* codeaf update, and the door that restarts the same conversation

codeaf update --check prints one line and exits 0 when this release is newest,
3 when a newer one exists, 1 when it could not ask. Without --check it installs
stable, or whatever --rc, --dev, --staging or --version names, choosing a
channel build by its own publish time the way the installer does. A binary built
from source refuses in place and names its path rather than replacing something
a developer is holding.

The restart happens here and not inside Bubble Tea: the surface fills a slot,
the door reads it once the frame is down and the terminal is back, and then
execs the new binary with this launch's own arguments plus --session pointing at
this transcript, so the conversation reopens where it was. A linked-local engine
is asked to retire only after the surface has detached, and it still refuses
while another window or any live work is there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* remote: a window that says it is leaving needs no torn-link grace

The twenty-second grace exists for a terminal that was killed without saying so,
and a deliberate detach is the opposite of that — the window has just announced
its departure. Carrying the grace anyway left a cleanly closed window looking
attached for two more call deadlines, which kept a freshly installed codeaf
waiting behind the engine the old one had already left.

It clears only once the last view is gone. Turns, tasks, unanswered questions,
other views and live work all still refuse retirement, and a refusal still never
stops the work it protects.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* The installer and the updater agree on what a channel tag looks like

Two roads now install codeaf, and they were reading the same release list by
different rules: the shell accepted any dev-* or staging-* tag and semver parts
with leading zeroes, while internal/update accepts only the shapes the release
workflow actually publishes. A malformed tag newer than a real one would send
the two roads to different builds.

One helper carries the grammar to both channel selection and the line that names
the channel, and an unrecognised tag for the asked-for channel now says so
instead of installing whatever came back. The publish-time ordering #1056 added
is untouched: only which candidates are eligible changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* The manual stops saying that nothing self-updates

That sentence was true until this change and is now the opposite of true, so it
goes, and a section written in the words people bring takes its place: is there
a newer version, how do I update codeaf, why does it say this every time I
start. It states who gets the launch line and who never does, the opt-out
variable, the day the cached answer lasts, the shell exit codes, and both
refusals — a build made from source and an executable nobody may write.

The command table names /update and /upgrade with the argument they take. Four
probes reach the new section, and the remote page keeps its own asker's words in
its heading so the new section cannot take that question from it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs/changes: the update check and /update, for #1060

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* update: --check answers for the channel it was asked about, and a newer build is never downgraded

Two things a hand verification caught. --check parsed whatever tag it had
selected with the stable grammar alone, so --dev, --staging and --rc could
never be "newer" and every one of them answered "you are on the newest codeaf"
and exited 0 while a newer build sat in the list. The check now keeps its
channel all the way through the decision and always names the tag it is talking
about: stable and rc compare as semver, a dev or staging tag is the same tag or
a different one, and --version is equal or it is not.

And a build AHEAD of the newest stable was quietly installed backwards — the
check said newest, the install downgraded it. Both roads refuse now, naming both
versions and the deliberate way past:

  this codeaf is v0.3.0, ahead of the newest stable v0.2.0 — pass --version
  v0.2.0 to install it anyway

An explicit tag IS that deliberate road, so --version and /update <tag> still
install exactly what was named.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* update: a failure names the asset and the status, and ends with the curl line

A download that failed printed the whole fallback URL, which put the retired
product name on somebody's screen. The line names the asset it asked for and the
status it got back, and nothing about which repository answered — the
compatibility attempts still happen, they are just not the person's business.
Every terminal failure now ends with the curl line, which D9 asked for and the
download path had been missing.

The / list declared no argument for /update while the command page documented
one, so the list gains the second row the way /cache has one. The manual section
now says what the code does about a build ahead of the newest stable, where it
had said such a build "gets nothing" — true of the launch line, and false of the
install until this wave.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* update: the curl line is agentfield.ai/get/codeaf, the supported road

The launch line, the refusals and the terminal door offered the raw
GitHub address of scripts/install.sh. The repository is public and
agentfield.ai/get/codeaf has served that script from main since
2026-09-16, so the constant now spells the supported line, the manual's
update section names it in place of "the curl line below", and the
change entry says so. Asked for on the pull request by Santosh.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* a run's own scratch note no longer fails the delivery gate (#1096)

* codeaf: a run's own note is offered outside the tree it is judged in

leafOutputHint invited a job root to write its overflow document at a
workspace-relative name (exec.SuggestPathFor's task-2-x1-toolchain-checks.md
shape), so a run working in a person's directory left that note untracked at
the root of their working tree. The delivery gate holds the run's record —
the artifact registry, joined by jobArtifacts, plus the delivered tree —
against the request, read the note as part of what was handed over, and
refused the delivery; the run then ended incomplete over complete work.
There is no separate mechanical clean-tree check to exempt, so the fix is
the writer's: where the workspace belongs to the person, the root's file
invitation points at the run's scratch beside its recorders and job logs,
and the message keeps carrying the deliverable.

* codeaf: a person's own file still joins the record the gate reads

TestARootLeafInAPersonsDirectoryPutsItsNotesInScratch baselined the tree
after the note was already written, so its landing read could never have
observed anything: the record stayed empty because of the ordering, not
because of the address. The baseline now happens first, in the executor's
own order, and a hint that ever points into the tree again fails on the
observation, not only on the spelling.

And the other half of the law is pinned down: a file that turns up in the
person's tree while the leaf runs — even at a name wearing the run's own
task-<n>-<slug>.md shape — is still observed and still lands in the record
the delivery gate is held to. Only the run's own note moved; nothing else
about what the tree says was loosened.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs: the change note for the root's overflow file moving out of the tree

---------

Co-authored-by: codeaf <agentfield-bot@users.noreply.github.com>

* chat: the wordmark is drawn CodeAF, four rows, capitals on the d's cap line (#1106)

`┌─┐ / ├─┤ / └─┘` is not an `a`, it is two closed bowls of equal size
stacked on each other, and two equal closed bowls are an `8`. That was
the second-to-last glyph of the product's own name on the first screen
of every fresh install. The owner's answer is to have the two letters
at the end stand up out of the word as capitals: `A` is an apex, a
crossbar and two legs; `F` is the one letter whose case is told by its
arms alone, so its top arm is the longer one.

A CAPITAL'S TOP IS THE ASCENDER'S TOP, and three rows of box-drawing
could not give it that. A bar is drawn at the middle of its cell and a
stem reaches the cell's edge, so the `d` stood half a row over any
capital however it was formed — and a stem in the bottom row hung half
a row under the bowls' baseline the same way. The fourth row is that
half a row made whole: the x-height letters live in the lower three and
leave the top row blank; the capitals and the `d`'s ascender start in
the top row, the ascender as the half-stroke `╷` so its top is the
capitals' bar; and every stem that ends on the baseline ends as `╵`,
which stops where `└─┘` does. A `│` in the bottom row is a descender
and nothing else.

THE WORD IS SPELLED `codeaf` AND DRAWN `CodeAF`. `product` is unchanged,
the glyph table is still keyed by its lowercase letters, and a terminal
that cannot draw boxes still gets the word `codeaf`.

The greeting's centring test read the unit's left edge off the
wordmark's first row, which is now the cap line the x-height letters
leave blank; it reads the second row. The one manual page that draws
the wordmark had been drawing the retired `aforge` letterforms under
the label "the codeaf wordmark" — the name law walks text and cannot
see a name spelled in box-drawing — and draws the shipped forms now.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

* A real release download finishes: the 3-second check clock no longer covers the asset (#1110)

* update: a release download gets its own clock, not the check's

The release client was built as http.Client{Timeout: 3s} because three seconds
is right for the launch check, and Go's Client.Timeout covers the WHOLE
exchange including every body byte. The same client then downloaded the
release asset, so a 57 MB download on a healthy link — about eight seconds
here — was cut off three seconds in and a person read `read the release
response: context deadline exceeded`.

Client.Timeout is gone. The transport bounds only what has made no progress: a
5 s dial, a 5 s TLS handshake, a 10 s wait for response headers. Every call now
takes its deadline from its context. The launch notice and `--check` share the
one CheckTimeout constant, install metadata gets 10 s per request, and the
asset gets a 30-second no-byte stall window plus a 15-minute ceiling — a dead
link fails with a sentence, a slow one finishes.

No Go deadline text reaches a person. A stall says `downloading
codeaf-linux-amd64 stalled — no bytes for 30 s`, the ceiling says `took longer
than 15 minutes`, and late headers say `did not start arriving within 10 s`.
The stall window, the ceiling and the metadata window are injectable, so the
tests prove the clocks with real slow, silent and trickling servers rather than
waiting out the shipped ones.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* update: both doors read the one check constant

`codeaf update` and the chat surface each spelled `3*time.Second` when they
built the release client, which is how a figure meant for one small request
came to govern an install. Both now pass update.CheckTimeout, and the three
seconds live in exactly one place.

The terminal's `--check` takes the client's Check, which is the whole-exchange
check deadline; a plain `codeaf update` keeps Select, so the metadata requests
an install makes get the ten seconds they need.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs/changes: the update download's own clock (#1110)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

* docs/changes: v0.2.1 rolled up into CHANGELOG.md (#1113)

The 33 entries that landed on dev since the v0.2.0 roll-up are the ## v0.2.1 section of CHANGELOG.md now; docs/changes/unreleased/ keeps only this pull request's own entry. Stable v0.2.1 takes its notes from that section.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* README: telemetry folds under Docs (#1115)

The 23-line Telemetry section between Install and the product tour becomes one collapsed block at the end of Docs: a plain summary line, the notice verbatim, one sentence and the link to docs/TELEMETRY.md. The notice block is unchanged, so it still matches the binary, the installer and docs/TELEMETRY.md. Replaces #1112.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoeSVN8nqmwNVRXExgoUNN

* santos/dev lands on dev: crew family, Pareto crewing, the Model Pool, custom connections (#1194)

Lands the integration branch santos/dev (#1108, head 6db5125b2, 152 commits over dev) on dev as one squash, plus two fixes that kept its touched-packages job red: the pool start-up test empties the CI word it is not about (649bb4bfd), and a zombie leader no longer keeps a job's process group alive on Linux, which had delayed every finished background job by the two-second termination grace since #1155 (053e6a90f). #1108 carries the full account; the 94 change entries under docs/changes/unreleased/ are the per-change record.

The commits folded in, oldest first:

5c413ef7b Keep archived evidence readable while tasks finish
3705a0cfe Document recovery evidence retrieval change
7834cc7d0 tui3, manual: a question's own answer box answers to the whole caret vocabulary
99d66e98c task: Add open/all source toggle to crew config
c1e4cb57f docs: the change entry for the crew model family toggle
e63b43927 crew: round-1 review fixes
304a5a223 crew: round-2 review fix
65477c0c5 task: Add open/all selector to /crew TUI picker
9189ef764 Merge branch 'task/add-open-all-selector-to-crew-tu-9e8cc0' into feat/crew-open-all-toggle
ba9c12d61 crew: reconcile the picker onto config's source helpers
ea05bc0f5 crew: a chosen family counts as an answered crew
e1a037e0a Merge remote-tracking branch 'origin/dev' into feat/crew-open-all-toggle
f6c2924a7 crew: the open table takes the picks we landed on
eff44b542 docs: the change entry carries the open-table move and the family gate
71ce9bec0 crew: the family reaches the fallback, the settings skin, and the row's line
b8ca5f513 crew: round-1 review fixes on the new table
8db5708ef crew: round-2 review fixes
ce9759ef4 crew: round-3 review fixes
956e1f514 crew: round-4 review fixes
37ad27f4e crew: fixes from the first Opus round
6ffb95813 crew: the fixes from the Opus confirmation round
72b027436 crew: the comments the atomic write left behind
70d0e205b crew: round-four fixes from the Opus review
234300e37 Merge branch 'dev' into feat/crew-open-all-toggle
894292f28 crew: round-five fixes from the Opus review
fba77f92d An answered landing carries its answer's fate when it is raised again, and an in-flight settle holds the question down (#1077)
e69445058 Changelog entry for the landing-answer fate (#1086)
64ab7eae0 crew: round-six fixes from the Opus review
070016419 crew: round-seven fixes from the Opus review
af09aa852 The review round's four findings: claim ownership, decider changes, recordable answers, plain words
c24af0248 crew: round-eight fixes from the Opus review
289d372b9 The tests the landing's answer carries: fate, hold-down, redraw, claim doors
7e38346a9 The take-down comes before every mint, and the tui3 walk reads the newest card
dbe1be03e The review rounds' last word: the release owns its claim, and the docs say what the code does
4cc4945a7 The Opus review: a release owns its generation, and every failed round still says its word
a5cce0dde The done card says who is deciding, so the clause is a duplication, not the fact
6bd276e6c Merge fix/1077-landing-reraise into santos/dev
271016938 Merge feat/crew-open-all-toggle into santos/dev
b459399d3 Merge fix/question-answer-box-caret-keys into santos/dev
a38492026 Merge codex/recovery-evidence into santos/dev
dc22e6461 cmd/codeaf: the role-door tests read the shipped mastermind instead of a copy of it
39f528f70 release: the pin-example check reads the guide the examples moved to
6439d3cb8 session, tui3: gofmt the three files santos/dev merged unformatted
854037967 tui3: a question's answer box kills to the end of the line on ctrl+k
d93ec61ea session: the late-attach proposal test holds the work until the replay is read
a81ead4de session: the landing-fate tests read today's clock instead of a fixed date
8fe4fff38 provider: the retry-avoid list rides the call's context into the knobs
a266da3e2 provider: the retry-avoid veto narrows the wire through one field law
197d89334 exec: a node retry asks the router to avoid the lanes that failed it
2d02cc0b9 provider: the set-servable law's doc comment keeps its paragraph break
e1d0e7524 exec: the retry test names its helper and spells its wall in time units
e7d65a517 docs: the change note for the retry that avoids the failed upstream
11decbd92 Merge fix/provider-failover into santos/dev
c099a51c2 Merge dev into santos/dev
e47d92bde provider, exec: the lane a failure names is a wire fact provider reads (#1105)
396be97be Merge dev into santos/dev
3b97f50c3 Merge dev into santos/dev
ea3b5c9a0 telemetry: the send path drops events that cannot name their version
573b288e4 cmd/codeaf: keep smoke runs from sending usage counts
e8d627c6b docs: the change note for the usage-count audit fixes
11bec8d80 chat: a hosted session's usage counts are read off the events it receives
b9d75b39e chat: the hosted-count tee is tested against the tally it must produce
39fa08d3d telemetry: stamp a version in the lifecycle tests
681e2adf1 chat: count the hosted boot agent once, and conversations opened beside
825ef2c41 connect, settings: named, multiple, switchable custom connections (#1092)
f4d3f9a1a Merge branch 'dev' into fix/telemetry-audit
76fec37e3 ci: the Model Pool mirror workflow and the signature check it runs
2fb296bf1 crew: Pareto crewing, the learned picker and the Model Pool (#1093)
a065ebb1b Merge remote-tracking branch 'origin/dev' into santos/dev
80ac250a0 Merge remote-tracking branch 'origin/feat/pool-mirror-workflow' into santos/dev
6092393ab Merge remote-tracking branch 'origin/fix/telemetry-audit' into santos/dev
ef808dda4 session: a decision card under --yolo takes its default (#1117)
70a9e1f6e session: a task's thread stops its parts before it closes, and a machinery cut says so (#1118)
c4df8f364 crew: an auto seat's rung names the pick that ran, once, for both doors (#1119)
a9fcf9bc8 crew: a bare auto row under picked from = catalog reads the catalog's figures alone (#1120)
13be0dce4 relay: accept the exec and run doors, not only task and do (#1121)
90f29aec1 changes: name the #1120 note by its PR and stamp the pr field
0b77926ca do: seat from the settings the run will use (#1122)
32148d87d changes: stamp pr: 1120 on the #1120 note
113ed628e pool: a launch's start-up errands stop when the process closes (#1124)
6c98ddca3 verify, codeaf: a launch compiles fewer patterns and a surface holds fewer Ps (#1125)
50fe6a3e4 pool: the min_installs floor counts installs on the client (#1127)
076cde8ae relay: fold a resubmitted row once by its nonce (#1128)
8339ae71b relay: carry the acceptable metric beside role_quality (#1129)
0367c441b outbox: a refused row leaves the outbox and the rest of its batch is sent (#1130)
09aecd2f1 relay: the index holds only the cells that meet the min_installs floor (#1131)
ae7c05dcd outbox: a Send compacts the file to the rows it still holds (#1133)
9e24f72d8 pool: the errand tracker's critical sections unlock from a defer (#1134)
358a569aa relay: fold a submit batch with one read and one write per distinct cell and day (#1136)
a1d2eb1f7 pool: judge landed-but-unjudged runs on the next chat start (#1132)
3c6722859 verify: take the finished-tree check at a project cloned into a subdirectory (#1135)
263625b63 exec: a headless run leaves a pending judge record at its tail (#1137)
8be373d39 run: the headless task door leaves a pending judge record (#1138)
a336b246e do: leave a pending landing for the Model Pool's judge (#1139)
8820a5659 cmd/codeaf: one readPendingRows test helper, not two (#1140)
49760fb45 pool: show and verify print both metrics the index carries (#1141)
6eaeb25c9 pool: status shows the pending judge records and the last sweep (#1142)
7c00b5f2c codeaf: a test's crash fixture stays in a profile the test owns (#1145)
fc2eb357a manual: the status row's ledger is the tree's, not the conversation's (#1143)
a06b53a91 test: pin the delivery gate over a grown job root (#1147)
7231347eb resident: the wall refuses a first replan round it cannot hold (#1144)
5e257557f cmd/codeaf: the package's test floor clears a profile the environment named
4dd1c562f Revert "cmd/codeaf: the package's test floor clears a profile the environment named"
9075f863c pool: the seed index is regenerated from the relay by seedgen (#1148)
a5c44f3d2 pool: show --cells lists the held index's cells with installs and dims (#1149)
825996739 cmd/codeaf: the package test floor clears a profile the environment named (#1150)
81568aa82 session: report gitignored build products a run leaves, without gating on them (#1152)
da4910593 internal/tui3: the opening hint test owns its profile and pins both doors
72de3bca7 docs/changes: #1151 — the opening hint test owns its profile
499083d1c Revert "docs/changes: #1151 — the opening hint test owns its profile"
beb31ecf7 Revert "internal/tui3: the opening hint test owns its profile and pins both doors"
25b8b7dea version: an unstamped binary names the condition, not a Makefile target (#1151)
c833b423a pool: a dropped row keeps the relay's reason and status shows it (#1154)
cbb7d6d9e processgroup: verify a job group's identity before signalling it (#1155)
efe498ed0 internal/tui3: the opening hint test owns its profile and pins both doors (#1153)
17b3b0e40 do: a continuation continues the plan it has instead of re-planning (#1156)
22cf3ce17 changes: correct the #1155 and #1156 notes' pr fields (#1157)
8e85b0fe7 cmd/codeaf: a surface runs under a soft memory limit drawn from the machine (#1158)
ef1e92756 task: status row accounting (#1159)
44f572e26 pool: status's index line says what this run's fetch did to the cache (#1161)
3600fc9a5 pool: the pool's copy of a seat's id is the bare vendor/id (#1163)
a2ebd1397 cmd/codeaf: no test can push pool rows to the default relay (#1164)
4fb7f0546 docs: the road a chat door bash push took under --yolo (#1160)
69219a1e1 cmd/codeaf: the soft memory limit is the smallest real bound, not the host s memory (#1162)
2b155b4b4 relay: bound judge severity, clamp adjusted scores, publish mean and sd on one scale (#1165)
2fb55324a relay: purge fixture vendors and refuse them at the door (#1167)
57504187a exec: a job's shell cannot reach the tmux server hosting codeaf (#1166)
eb7b46559 internal/session: an unattended run may not move a branch it did not create (#1168)
80f770639 pool: status says the identity, the judged total and that nothing has landed (#1169)
1cb7333fa exec: an exec run's worker spend reaches the usage ledger (#1170)
96bb9402e changes: the #1169 note's title parses and reads as one line (#1172)
627236d95 config: the task door honors a stored crew word, and a pin survives the learned pick (#1171)
593092be6 internal/tui3: a test may not read a profile it did not create (#1173)
ad8be1ed7 session: a landing card under --yolo takes its default instead of parking the run (#1174)
03d49a678 gitignore: the .furrow ids the engine writes into a workspace (#1175)
46edbdd32 internal/tui3: gofmt the profile-law helpers from #1173 (#1176)
3179e3611 relay: ALLOWED_VENDORS names the catalog's vendors and codeaf (#1177)
397a145a6 docs: why a settled /task leaves commits on task/<slug>, not the worktree branch (#1178)
915ff3a76 pool: regenerate the seed index from the relay's first clean publish (#1180)
d9b42430f cmd/codeaf: the model warm stops when the process closes (#1179)
bd2dcf334 session: the auditor's calls say they are the auditor's and which node they check (#1181)
c6ed47c5a task: json unverified branch (#1182)
360352ad4 session: a settle turn runs under the checker's own bound, not the run's wall (#1183)
814e1af0e session: the tasks text and record line name a failed/unverified node's kept branch and verdict (#1184)
7c25a26d9 internal/session: the identical-effects test stops riding the turn guard (#1185)
6e7d0ed44 cmd/codeaf: exec and run envelopes name a non-verified node's kept branch and verdict (#1186)
f4079d716 session: bound a worker's park on a promoted command (#1187)
994689fe7 docs: the model pool runbook names the Workers Paid plan and what it costs (#1188)
c315be13e docs: the runbook's plan line says metered, not capped, and nothing more (#1189)
2e80c60d8 internal/tui3: the status word says working while a task subtree turns (#1191)
99363ca8f session: bound a job subtree's CPU and process count (#1190)
6db5125b2 docs: what the audit check seat can execute, and where that boundary is set (#1192)
649bb4bfd cmd/codeaf: the pool start-up test empties the CI word it is not about
053e6a90f processgroup: a zombie leader does not keep a job's group alive
b4e66f515 docs/changes: the entry for #1194

Co-Authored-By: Santosh kumar <29346072+santoshkumarradha@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* ci: Model Pool mirror workflow (signed index onto the model-pool branch) (#1116)

* ci: the Model Pool mirror workflow and the signature check it runs

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: the change entry for the Model Pool mirror workflow

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: santoshkumarradha <eng@agentfield.ai>

* chat: the unconnected custom row on /connect keeps its vendored name (#1195)

modelConnectionStatus renamed every custom-id row to the connection's Written word, and the catalog template's Written is the bare id, so a person who had connected nothing yet saw `custom` where the manual, the README and #1107 say `Custom OpenAI-compatible API`. Only a connected instance is called what the person called it now; the contract test draws both rows and is red without the fix. Found verifying the staging build of #1194.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs/changes: v0.3.0 rolled up into CHANGELOG.md (#1196)

95 loose entries — the crew family and Pareto crewing, the Model Pool and its relay, custom connections, the telemetry audit, the update door's download clock, and the fixes that followed — become the `## v0.3.0` section the stable release reads its notes from.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* ci: a changed directory in a nested module is not a package of the root module (#1332)

The touched-packages job and the Makefile's test-touched target both derived the test set as every directory holding a changed .go file, and handed a directory under its own go.mod to go test, which answers "main module does not contain package" and fails the job as a setup error. Both walks now stop at the first go.mod above the directory and skip nested modules, so the bench fixtures under bench/bashloop/fixtures are left alone and make pr-ready derives the set the gate will.

Verified on the 621-file diff that surfaced it: 43 directories to 36, the seven dropped are exactly the fixtures, and the two halves print the same list.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* santos/dev lands on dev: the worker harness, stopping and limits, the task page and rail, goroutine ownership at close (#1108)

Integration branch santos/dev, held as a closed slice at d39a4e308 plus the empty-prompt guard for generate_image (3a1f80f9c). Everything through #1192 had already reached dev as #1194; this carries #1193 and #1203 through #1331.

The worker harness: one loop under /task and codeaf do with a plan store per run, a plan seat, a work seat, a check seat and a review round after each piece of work, off unless CODEAF_TASK_BELT=bash is set, so the default road does not move. Stopping and limits: a person's stop reaches a run, a time or dollar limit ends a run and names itself, a run is handed what is left of the conversation's limits, a worker that stops making progress is told once and then ended. The task page and the rail: the plan as a tree, a run's row with its parts under it, step rows cut from the commands as they ran, a press on a run's row opens its page in under 0.2 seconds. The engine host and sign-in, the profile's composed writes across two processes, unread config keys said once. Nothing keeps writing after the process closes: the place sweep, the standing ticker, the usage writers, the pool judge sweep and the catalog warm are each owned, stopped and joined, and a law lists every goroutine a conversation process starts. The heavy-suite lock is a kernel file lock that follows the suite. The lazy lexer registry, one fuzzy matcher for every picker, the drafted-with footer on the CodeAF repository.

Reviewed 2026-09-21: gates green locally and in CI once #1332 taught the touched-packages walk about nested modules; the default road verified live on both roads against a dev baseline; the one regression the slice introduced (generate_image had lost its empty-prompt guard) fixed on the slice with a contract test.

Co-authored-by: Santosh kumar <29346072+santoshkumarradha@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* a dev build installs beside codeaf as devaf and follows the dev channel (#1333)

* install: --name installs the binary under another file name beside codeaf

The installer takes --name WORD and CODEAF_INS…
* experiment: add sourced knowledge articles and local wiki lab

* experiment: learn emergent subjects and audit memory costs

* experiment: compose sourced guides with bounded knowledge learners

* experiment: tighten knowledge support and reference usefulness

* experiment: add incremental knowledge workflow and validation

* experiment: add canonical inline knowledge links and backlinks

* experiment: connect project knowledge to Memory and local chats

* Polish knowledge reader links, sources and large-vault navigation

* Complete knowledge reader hover feedback and shared selection

* Fix knowledge sidebar scrolling and add folder controls

* Organize knowledge sidebar, format sources and configure its model

* Add durable knowledge page editing and deletion

* Document project knowledge in release notes

* Align shared UI assertions with Memory navigation and switch

* Explain automatic project knowledge capture in internal manual
* docs: organize product and developer knowledge by audience

* docs: record documentation organization in change history
* Fix worker first-turn wake after delayed team membership

* Document delayed worker startup fix

* Keep new chats and returning tab history visible during setup
* fix(tui): keep navigation usable while opening a chat

* docs: record opening chat navigation fix

* docs: clarify corrected opening status
…1948)

* Keep chat startup and navigation responsive during background work

* Tighten latency audit synchronization and cancellation evidence

* Document responsive startup and resume behavior
…1951)

* fix(tui): keep cold team tab opens off the input loop

* docs: record responsive cold team tab opening

* fix(tui): settle team selection after a cold tab opens
* Make add-device card clickable with an editable pairing field

* Exercise pairing card and input clicks in terminal journey

* Document clickable add-device pairing field
…1950)

* Share pure directory records and canonical coordination schemas.

Keep legacy lease decisions and JSON behavior intact while making the shared records usable without HTTP or SQLite. Define a strict typed v2 boundary with exact uint64 and sealed-byte round trips across Go and JavaScript before consensus code depends on it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Start an independent sequential coordination oracle.

Give the fault simulator an ownership, vault and revocation model that cannot inherit defects from production apply functions. Keep candidate history states independent and pin observed-take and displaced-write behavior before the history checker is added.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Pin the shared Raft source with per-node election randomness.

Native and WASI members must use the same reviewed election seam and protobuf wire implementation. Retain upstream source and notices, record the narrow patch and source hashes, and expose only these dependency directories through the existing ignore rule.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Make consensus effects wait for explicit durable completions.

Wrap RawNode in a seeded single-threaded input and effect contract so hosts cannot release dependent messages or authority reads before persistence and application. Bound read contexts, reject peer-injected control messages, stop on storage or schema failures, and pin election replay with focused quorum and transfer smoke tests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Define a shared reactor ABI so native and hosted members use the same Go core.

Keep consensus synchronous behind owned binary buffers and explicit durability completions. Bound frames and handles, audit the WASI runtime imports, and exercise real lease rules over immutable paged rows without adding a production writer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Prove the Worker boundary locally before building the hosted cluster on it.

Compare native and wasm effects through real commit and ReadIndex exchanges, restart disk-backed workerd storage, and measure the complete package and a grandfathered-size fixture. Run consensus and bulk memory phases serially so the proof fits the shared laptop budget without changing the Worker workload.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Persist native Raft members before releasing dependent effects.

Keep each member in its own FULL-synchronous SQLite WAL store so votes, conflicting suffixes and application results survive restart. Refuse corrupt authoritative state and commit rows, dedup results, configuration and the applied index together.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Prove native recovery across persistence and application failures.

Cut subprocesses at every transaction boundary and observe actual WAL flushes before acknowledgment. Exercise production cores with three real stores so leader loss, joint configuration and dropped application completions recover without losing committed results.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep durable coordination checkpoints without retaining learner history.

Stream complete application snapshots, retain two checked generations and atomically install their state before acknowledging Raft persistence. Compact only covered applied history so a disconnected learner catches up without pinning the log.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Carry portable snapshot maintenance across the reactor boundary.

Preserve ABI-1 effects while adding checkpoint completion and maintenance queries. Pin the snapshot schema and source hash in the Worker bundle so native and hosted adapters share the same format.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Add an independent bounded checker for coordination histories.

Keep timed-out operations as unknown outcomes and retain session results and tombstones so the oracle can detect stale reads, double takes and duplicate execution without sharing production rules.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Exercise the production Raft core with deterministic durability and network faults.

Drive explicit write, flush, application and reply boundaries through seeded virtual-time schedules, preserve portable snapshots across recovery and compare the disk model with native SQLite. Keep replayable counterexamples and require broken fence, read and flush controls to fail.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Wait for durable results and repeated election attempts in fault campaigns.

A lost proposal can still be uncommitted after draining messages, and randomized elections can split votes beyond a fixed 25-tick wait. Preserve exact replay regressions, check each election timeout separately, and run 500 seeds by default with an explicit larger-soak count.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Authenticate and bound native peer delivery so learner backlog cannot displace voter traffic.

Bind relay exchanges to committed identity and member generations, preserve signed opaque bytes across reconnects, and reserve bounded inbox capacity for voters and snapshots. Publish a shared native and JavaScript wire vector.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Prove native quorum progress through outbound relay exchanges before production host assembly.

Mount the peer handler through the relay host boundary and drive three durable native members through election, committed application, ReadIndex, restart and absent-learner progress without inbound member listeners.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep hosted storage framing on the shared Go core.

Expose bounded protobuf inspection, streaming hashes and portable snapshot pointers so hosted storage can persist consensus without a second application writer. Exercise the same protocol through native SQLite proof peers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Persist hosted members before their coordination effects escape.

Serialize each actor across durability waits, authenticate bounded peer inboxes and stream retained snapshots through the shared reactor. Add the local compiled-module entry and independent object binding while leaving the directory cutover to a later change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Prove hosted recovery beside real native peers.

Drive signed mixed-runtime commits, quorum reads, both snapshot directions and joint-state restarts in local workerd. Bound admission, retained state and co-resident memory before the directory adapter uses these actors.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Decide directory mutations and retry results in the shared Go state machine.

Verify committed enrollment and signed requests at application, retain exact sequence results, and expose bounded dependency evaluation through the same native and WASI ABI.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Commit native directory responses only after quorum and durable application.

Adapt every Client verb to the consensus boundary, assemble immutable native read-sets and atomic write-sets, and prove the public directory and HTTP doors with real stores and the independent ownership model.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Route hosted member application through the shared Go evaluator.

Keep JavaScript limited to dependency bytes and durable writes, expose the migration-ready facade, and prove signed hosted decisions with native ABI parity while preserving the earlier workerd mechanism fixture.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Require committed upload evidence and install pairing authority before delivering grants.

Keep effect signing separate from personal authority, bind approvals to authenticated request keys, and retain exact outbox outcomes across crashes. Preserve original v1 upload ordering while requiring evidence for v2 publication.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Make hosted blob and pairing effects durable and safe to replay.

Reserve each public request for one cluster, commit its device authorization before exposing the sealed grant, and repair interrupted uploads and delivery acknowledgements without deleting content.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Freeze legacy namespaces before importing one durable coordination history.

Keep the source irrevocably gated, preserve its complete rows and watch evidence, and resume the same three-voter genesis after interruption so old and new writers cannot overlap.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Resume hosted imports through bounded durable row staging.

Drain admitted legacy effects before freezing, fence incomplete member activation, and compare the hosted genesis with native storage so eviction and retries retain the original authority and content.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Record durable learner intents so pairing cannot enlarge the voting quorum.

Keep admission separate from applied configuration, retain every retired node ID, and complete authorized joins after the requester sleeps. Replay configuration at each log position so follower batching cannot change policy. Prove bounded churn, snapshot catch-up, exact retries and restart with the production core and native stores.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Derive hosted peer authority from committed membership so stale sockets cannot restore retired nodes.

Page registry policy through the shared Go reactor and use the applied join barrier before reporting readiness. Preserve the source-provisioned three-anchor bootstrap, isolate the old mechanism seam in its test fixture, and exercise real workerd restarts with a dead learner. Document enrollment limits and refusals with the implementation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep leadership policy and ranked election timing durable across fallback.

Use committed owner intents and explicit joint consensus for personal leadership, require a fresh quorum read for completion and retain it to prevent automatic regain. Rank randomized election windows from the immutable provisioned anchor manifest while preserving quorum and vote rules and exact versioned replay.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Carry leadership and anchor rank through native and hosted control doors.

Recover the committed anchor order through registry and import snapshots in both runtimes. Serialize readiness and control effects, recheck current authorization, persist original request retries and document the actual commands, refusals and fallback limits.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Prove ranked fallback through conditional writes and both runtimes.

Retain every original split-vote seed and all joint crash cases, measure every seeded conditional acquire and preserve old exact replay. Exercise native process death, WASI promotion, snapshot restoration and minority refusal under the existing laptop resource ceilings.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Commit fresh hold evidence and durable idle barriers.

Keep renewal authority in the shared state machine because a socket cannot extend ownership outside quorum. Require every voter to apply a generation before timer suppression, and invalidate it across wake and recovery. Prove the policy with native persistence and seeded partial-delivery histories.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Require committed lease receipts before suppressing heartbeats.

Keep presence advisory because pongs cannot prove durable renewal. Recheck quiet mode for retained clients and watch streams so a later sync-off decision closes every sending path. Document the ownership and idle limits beside the continuation journey.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Drive hosted renewal and idle scheduling through the shared Go policy.

Serialize timer decisions with durable dispatch because a concurrent wake must retain its alarm. Bound evidence pages and transport retention, and require exact batch acknowledgments across restart. Exercise the production actor, alarms and native replay locally.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Delegate selected chats without exporting personal identity authority.

Bind expiring worker certificates to exact chat scopes and retain sealed grants. Commit registration, availability, drain and revocation rules with incarnation fencing so replacement processes cannot reuse old authority.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep native and hosted worker access behind committed policy.

Return only selected chat grants after the host quorum barrier and derive learner routing from current worker scope, expiry and incarnation. Exercise real native recovery and byte-identical WASI policy to preserve the same authorization boundary in both runtimes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Explain delegated worker trust and its current limits.

Describe selected-chat access, explicit learner metadata disclosure and draining in the embedded manual while keeping worker setup and assignment execution absent until their client lifecycle is implemented.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Commit explicit worker assignments with conditional chat ownership.

Keep scheduling decisions and chat fences in one durable application transaction so stale continuations, revoked workers and replaced processes cannot claim the same observed work.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep worker execution behind staged handoff and durable local intent.

Reuse selected content, ordinary upload and handoff dependencies while retaining original signed retries and refusing automatic tool replay after interruption or reassignment.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Describe delegated claims and interrupted worker recovery.

Keep the manual and adapter contracts accurate about conditional claims, child results, recovery limits and the remaining public setup work.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Retain original device proofs when adapting v1 requests to committed commands.

Check the original HTTP certificate, route, body and signature at application so forwarding cannot manufacture personal authority or a new observed holder.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Pin the production compatibility door and explain older-client refusals.

Retain a failing real-relay contract gate until namespace migration and member assembly replace the legacy writer, and keep the manual accurate about safe continuation and incomplete saves.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Preserve certified coordination and portable snapshots at durable boundaries.

Keep returning holders usable at their imported capacity without enrolling extra rows. Bind operator policy and anchor keys to the source manifest, retain retryable authority failures, and preserve the exact portable snapshot encoding after the library fills default fields. These shared boundaries let the production relay use the same deterministic policy without weakening its storage checks. Give committed read callbacks the actor context so losing a client response cannot cancel durability.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Replace the persistent relay writer with three durable local anchors.

Freeze and preserve the original namespace before one resumable three-store import, then require quorum commit and fresh reads for directory, rotation and blob authority. Keep pairing and watches working through committed effects, recover returning stores through checked snapshot chunks, and refuse to recreate lost voter identities or re-import a published voter without its own receipt. Keep disconnected clients from poisoning shared actors. Document the native topology and limits, include the modified Raft notices, and record the measured runtime cost in the size budget.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep original v1 proofs stable across typed hosted boundaries.

Normalize empty frame hints and accept the original safe rotation schema marker so canonical forwarding retains the signed operation. Prepare restricted hosted effects, typed legacy projections and committed authority checks in the shared Go ABI.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Route hosted identities through the same durable three-anchor namespace.

Freeze and resume first-use imports with one manifest before publishing quorum-backed ownership. Keep blob I/O outside the coordination queue, recheck authority after it, exclude unclaimed replacement stores and retain resumable all-replica retirement.

Distinguish the local platform name table from unknown retained application data, and finish KV transport cleanup after an interrupted retirement.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Prove the public hosted compatibility and recovery doors.

Exercise migration cuts, majority loss, original v1 takes and pairing, retained wide numbers, stale snapshots, result provenance and rotation through the exported Worker. Reuse the real two-home Go journey and pin the actual lost-race screen sentence while measuring local runtime budgets.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Document the irreversible hosted directory upgrade.

Describe the exact v3-raft-members migration, preserved source backups, quorum refusals, older-client behavior and all-replica retirement in the hosted relay README, the reactor notes and the shipped manual.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Bind scoped cluster ingress to original credentials and committed authority.

Authenticate selected requests before activation and repeat scope/time checks at the quorum-backed view. Retain original personal certificates without overriding committed roles or revocation, and initialize imported anchor authority through the original manifest instead of rewriting frozen rows.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Connect native scoped requests and watch receipts to durable coordination.

Serialize current-time commit, fresh ReadIndex and selected reads. Carry only committed hold receipts, replay bounded outboxes and stop production timers through the shared all-voter sleep protocol. Exercise actual HTTP workers, delayed renewals and ten minutes of virtual idle.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Connect hosted scoped ingress and idle maintenance to the shared Go policy.

Use additive ABI doors rather than JavaScript authorization or lease rules. Preserve hibernating certificate evidence, original request bytes and durable results, and prove production worker claims and idle scheduling through the local Worker.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep the missing personal-member journey visible before release.

The production personal learner transport and leadership door remain incomplete. Retain an end-to-end failing acceptance test and describe the actual limit in the manual so adapter proofs cannot be mistaken for a finished journey.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Wait for owned journey engines to exit before removing their homes.

The keyless journey can finish its assertions while an engine daemon still writes the temporary store. Wait for the same owned processes after killing them so cleanup records real process termination before TempDir removal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Expose committed native cluster status while refusing unfinished leadership.

A signed personal status request must cross a fresh majority barrier and current revocation checks. Report only committed membership and refuse leadership before saving or proposing an intent that the production host cannot complete. Keep the missing learner lifecycle visible in its unchanged acceptance test.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Reuse hosted fixture bundles so runtime restarts preserve their program.

Cache each normal or injected-crash bundle once per fixture. Restart Miniflare over the same persisted state without rebundling, retaining every scoped-flow assertion and its original timeout. Pin the absent hosted control route across restart.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Serve durable personal learners through the native relay's committed membership.

Preserve each paired device's original signed admission, bind node authority permanently to its store, and reconcile separate learner configuration after a restart. Route authenticated exchange and socket traffic through the native member queue without waiting on learners, stream bounded snapshots independently of compaction, and require signed durable application evidence for readiness. Keep the provision immutable while fencing restart cursors, and preserve legal maximum command writes through snapshot chunks. Keep three anchor voters and all-voter sleep with fresh quorum wake, and pin the hosted member doors absent.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep a completed device removal visible when a later list read fails.

Retain the action result independently of refresh errors so a delayed devices-list failure cannot erase removal confirmation. Preserve initial-load error behavior and test both callback orders without altering the retained assertion or its predicate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep passive learner transport authenticated without waking an idle quorum.

Learner replies that arrive after the all-voter sleep barrier must not restart periodic coordination. Keep the existing response deadline while deliveries await acknowledgment, and let empty subscriptions wake passively. Device signing callbacks retain the canonical wire without exporting private seeds.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Run durable personal learners inside the existing home sync lifetime.

Persist store identity before authenticated admission and finish real snapshot and log application before reporting readiness. Reuse authority on reopen, replace it after store loss, and check current quiet policy at every HTTP, socket and delayed callback. Share local applied display and follower hints across Home handles so idle native homes stop recurring requests while ownership actions retain fresh majority reads and hosted sync keeps its existing polling. Expose committed learner identity and readiness in cluster status.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep device guidance readable while documenting native learner sync.

Move coordination and worker machinery into its own manual page while preserving device sentences and every retained retrieval probe. Explain native durable learners, the local Home follower, the actual healthy backstop, hosted polling and the continuing leadership refusal. Keep delegate and remote-session answers reachable through headings in the askers' words.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Check that the public relay config declares the Raft entry, build, binding and migration.

The private deployment configs and the test that checked them are no longer in the repository. The base config still has to send fresh installs and upgrades to the Raft reactor and the three anchor stores, and it must only append the anchor migration so that existing namespace IDs stay in place. This test keeps those assertions for wrangler.toml alone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Describe the coordination code by its behaviour rather than how it was built.

Comments and package notes cited internal planning ids, development steps and private documents that mean nothing to a reader of the public tree. State what each component does, what it leaves to its host and what is not available yet, keeping every limit, invariant and refusal intact. Test comments keep the contract item they prove. The hosted relay notes now say plainly that personal-member learners and leadership requests are not offered there yet, and source backups are described as lasting until the identity is retired.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Let joined personal computers coordinate the native cluster safely.

Forward original application requests over the enrolled member connection while peer persistence stays live. Complete explicit joint leadership transfer with fresh receipts, retain saved requests across retries, and restore the anchors after personal departure. Preserve quorum safety, bounded snapshot flow and quiet idle homes, and document the native capability with production lifecycle proofs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Move the size cap by the coordination core's measured weight on the current dev base.

On linux/amd64 the stripped binary built from this branch weighs 78,332,169 bytes and the dev base it sits on weighs 75,071,753, so the coordination core adds 3,260,416 bytes. The earlier cap was set from a smaller measurement on an older base and later coordination work used up its headroom, which left the branch 1,281 bytes over. The cap now moves from dev's 75,811,848 by the measured amount, keeping the 740,095 bytes of headroom dev has, and PERF.md states the new measurement.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* State the personal-member lifecycle for the default hosted relay.

The real codeaf client, pointed at the hosted relay running in the local
Worker runtime, must join its identity's cluster as a durable learner beside
the three hosted anchors and then lead it with an elected, committed and
freshly read receipt. The Go test skips outside the local hosted proof runner;
its launcher compiles the client tests before starting the Worker so linking
cannot spend the journey's request deadlines. It fails at the status door
until the hosted relay offers personal members.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Share committed personal membership policy with the hosted core.

Use the same admission stages and status projection for native and hosted relays so enrollment retries retain their node and signed proof. Authenticate native peer frames and encode client responses inside the Go core while preserving the WASI network boundary.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Serve durable personal learners through the hosted relay.

Expose enrollment, peer sockets, snapshot catch-up and authorized status while keeping learners outside quorum and hosted leadership unavailable. Fence recovered routing and outbound sequences durably, and cover real home lifetimes, compaction and bounded delivery through the production Worker.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep hosted presence independent of sleeping coordination.

Persist outstanding maintenance separately from advisory presence so idle connections survive coordinator reconstruction without waking voters. Preserve racing peer wakes and require fresh committed authority for retirement. Document quiet home traffic and the presence sweep cost.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Exercise quiet hosted homes and retain useful local failure diagnostics.

Cover real home lifetimes, observers, compatibility pings, sleeping alarms, cold wake and prompt state delivery. Retain bounded redacted runtime output and exact failing proxy routes. Fresh upstream connections on the counting hop prevent a local idle close from masquerading as a directory refusal, while real clients keep their ordinary transport.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Allow joined computers to lead the hosted relay safely.

Keep public coordination available through signed forwarding and fresh quorum reads while a personal computer leads. Reuse the native membership planner and application wire, and preserve quiet learner homes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Complete hosted fallback and idle personal leadership.

Advance surviving voters before authority reads and use the shared leadership planner to restore the standby after election. Preserve quiet scheduling with pending work accounted for, durable sleep cursors and a continuous monotonic clock across coordinator activation.

Cover process death, sync-off, cold wake, minority refusal and returning learners through the real client and local Worker. Document the fallback windows and quiet behavior on both relays. Keep background-job and self-hosting questions reaching their explanatory pages.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep large signed metadata within the existing command limits.

Older clients retain their original body limit through bounded authenticated body pages and one atomic directory change. Lossless physical records and bounded projection preserve opaque fields without increasing the command or reactor envelopes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Commit older requests through native and personal leaders.

The native ingress waits for durable staged completion and resolves large forwarded views from the exact applied result. Archived clients retain their signed bytes, safe refusals and complete saved metadata after recovery.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Preserve large requests across hosted member recovery.

Hosted members apply the shared paged representation in their existing atomic SQL transaction. Exclusive durable-object replacement fences stale actors, while bounded temporary accounting and portable snapshots preserve retained capacity and complete metadata. Earlier automatic collection keeps repeated large views within the reactor budget.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Document complete metadata saves and retained capacity.

Older builds can save metadata up to the existing request limit. An interrupted save exposes no partial metadata, and users check the latest save before retrying. Storage capacity continues to apply to retained records.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Document current cluster leadership and worker limits.

Joined computers can coordinate through either relay. Manual searches explain phone availability, saved leadership requests, fallback, scoped workers and retained capacity with the product's own status and refusal text.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Explain relay storage, recovery and compatibility.

Three persistent voters coordinate directory changes while encrypted chat content keeps its ordinary storage. Relay guidance explains the one-way upgrade, personal leadership, idle traffic and atomic older requests through the original body limit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Describe the relay coordination change.

The relay uses a majority per identity, joined computers can lead, and fallback preserves saved work. Older requests retain their original size limit while scoped workers and irreversible upgrades keep their stated boundaries.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Reuse durable directory authority without repeating unchanged work.

Unchanged peer acknowledgements reuse validated routing while every applied change refreshes authority. Reads authenticate original authority at a fresh majority barrier and append only changes to durable time or records. Preserve committed revocation and bound unchanged transport work without relaxing durability.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Preserve directory isolation and exact relay lease expiry.

Recovered personal leaders keep independent directory state and obtain a fresh majority receipt before serving. Lease expiry and maintenance follow one relay timeline, including the instant before expiry and expiry itself.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Check the hosted directory's paged Go policy boundary.

The hosted directory now evaluates and projects through the reactor's paged Go operations, so the routing law names those operations instead of the earlier single-call frames. It still requires the original signed commit, the barrier read and no JavaScript lease writer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep deferred engine durability on its captured home.

Capture the same socket and engine for primary and fallback routes so a later flush cannot follow a changed home. Await captured startup work before reaping its engine.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep reopened chats on the correct ownership record.

Bind each window to the record it opened and check that lease through fresh directory reads. Settle a displaced writer before opening its replacement, and retain a failed closing result for every caller.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Await native namespace readiness before watch handshakes.

Complete durable namespace import and election before starting the socket handshake clock, preserving the shared watch cases and their existing deadline.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Reduce retained memory in the local hosted member harness.

Use complete local HTTP replies and a smaller inspector client, omit unused services, and bound background compilation caches. Reap completed native hosts while keeping every hosted actor and the whole-run combined memory measurement.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep the update question reaching its answer beside the coordination pages.

The coordination pages add enough text to the chat manual that "is there a
newer version" fell out of its answer's top results, behind a short Windows
handoff section that mentions a newer engine. The update section now says the
asker's words in its body as well as in its heading, so the question reaches
the section that answers it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Map the relay's coordination packages in the architecture guide.

The relay now keeps its directory in a replicated log that the native relay,
the hosted relay and a joined computer share. The architecture guide names
the packages a contributor starts from for that work, beside the remote
session row, and points at the relay guide and the manual page that explain
the behaviour people see.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep hosted maintenance working while a cut-short large request's pages remain.

A large request from an older client is staged as raw body pages beside the
join requests in the same namespace. When such a request is cut short, its
pages wait for the next request to replace them, and the maintenance turn that
looks for undelivered join decisions decoded every row it scanned, failed on
the raw pages and skipped the rest of its idle work. It now reads only join
requests, as the delivery replay already does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Describe the proof helpers' snapshot statements by what they check.

The row probe's summary and the hosted smoke test's assertion message now say
what they observe about snapshots instead of naming internal planning steps.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep relay directories usable at their content ceiling.

Preserve complete older records with lossless encoding and retain their original sources. Reserve bounded initialization and retry bookkeeping so full directories can finish upgrading and persist capacity refusals without stopping committed work. Share the storage accounting between native and hosted relays while keeping content and protocol limits unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Release sleeping hosted members after saving their state.

Sleeping groups free their reservations so other households can activate. Busy groups keep their reservations and admission pressure returns a retryable refusal. The next operation recovers durable followers and establishes a fresh majority.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Prevent older relays from changing upgraded directories.

Freeze the retained tables and install durable write guards in one transaction so older relay code cannot acknowledge divergent changes. Keep the frozen source readable for import and recovery, and document the older relay refusal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep signed relay paths and empty frame plans usable.

Hosted requests retain the configured prefix with the original signed URI so every member interprets the same operation when a joined computer leads. Directory changes, enrollment and large rotation requests use that exact prefix. Native creates and publishes treat an empty frame array like an omitted or null plan while retaining the original actor and operation binding.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Release worker retry sessions when assignments end.

Finished, cancelled and reassigned work frees claim retry records so later assignments and personal leadership requests retain session capacity. Retained assignment receipts keep retries idempotent and prevent old requests from acquiring work again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Batch native hold renewals on the shared thirty-second cycle.

Sharing the existing cycle avoids one replicated renewal per presence ping. Each bounded batch still requires fresh evidence for the exact holder and fence, and receipts remain available only after durable commitment.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Measure the coordination core's binary weight on the current dev base.

The coordination core now adds 3,379,200 bytes to the stripped linux/amd64
binary: 75,755,785 bytes on the current dev base and 79,134,985 with the core
linked. The size cap moves by the same amount, from dev's 75,811,848 to
79,191,048, so the build keeps the 56,063 bytes of headroom the base has.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep the coordination change entry to what changed against dev.

The entry lists what a reader of dev now believes wrongly. Problems that
existed only while this feature was being built never reached dev, so their
lines go, and the upgrade line now says what an operator sees when an older
relay is started on an upgraded store.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Number the coordination change entry.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Give the coordination change entry its pull request number.

The entry was renamed for this pull request, but its pr field still said 0,
which the change-entry check rejects.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Measure the hosted learner's command boundary on the relay's own command.

A hosted relay under a prefix commits that prefix beside the signed URI, and
members now route the proof by it. The maximum-command proof captured its
request on the client without the prefix, so its size probe could no longer
translate the request at all. It now carries the request's prefix, measuring
exactly the command the relay commits; the boundary and delivery assertions
are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Show a joined computer as online only while its relay connection is open.

A joined home's Home took presence from its local learner, which counted any
computer holding an unexpired chat lease as online. A computer that stopped
without goodbye therefore stayed online until its 90-second lease lapsed, and
the other computer could not offer to continue its chat for over a minute.

Home now takes online and offline only from the relay watch, with the usual
fifteen-second offline debounce, while versions, device news and directory
reads still come from the local learner. Lease expiry stays the authority for
ownership decisions and never for the display.

New tests stop the holding process without goodbye and require the
offer within 30 seconds while the lease is still valid, on the native relay
and the hosted Worker. The open-home idle tests now allow Home's watch socket
its platform ping and pong, bounded per ten minutes from the ping interval,
and still require zero HTTP requests, client attempts, peer and forward
frames, recurring alarms and presence-driven coordination wakes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Say that Home keeps a relay watch for presence after joining.

The manual and the change entry described a joined Home with no remote
presence viewer. Home keeps its relay watch with ten-second pings, so a
stopped computer appears offline within 30 seconds and its chat can be
continued elsewhere.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Prepare the take-back proof's holder home like every other test home.

The holder runs in a re-executed test process, which did not mark the cell
engine available the way the test's other homes are prepared. On a computer
without an installed engine binary the holder could not open its home and
exited at once, so the proof depended on the machine it ran on.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Link the hosted take-back proof's client before its time limit starts.

Linking the client tests can take minutes on a busy disk. Doing it inside
the timed test let the link consume the proof's own limit, as the other
hosted client launchers already avoid by linking first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Prove a store written by furrow 0.2.0 keeps working under a newer engine

* Add the change entry for the furrow store compatibility test

* Bump embedded furrow to v0.3.3

---------

Co-authored-by: agentfield-bot <agentfield-bot@users.noreply.github.com>
* test(session): building a session must not wait on furrow

* perf(engine): keep furrow detection off the session build path

* docs: number the change entry for #1954

* test: furrow answers must reach the model and waiters must be able to leave

* perf(engine): arm furrow verbs late, ctx-aware detection, settle on the first request

* fix(session): arm furrow verbs under the session lock and only for the folder asked about

* fix(session): read the session config from a build-time copy in the furrow arming goroutine

* fix(session): decide there is nothing to arm before touching the session config; test writes take the lock

* test(session): anchoring must ask furrow about the new folder

* fix(session): anchoring asks furrow about the new folder from the background

* docs: state the first-frame gain against the furrow that was embedded then
* feat(knowledge): build profile knowledge asynchronously in native Go

* fix(knowledge): verify model routing, recovery and evidence support

* fix(knowledge): use shared account configuration and memory call tags

* fix(knowledge): guard optional intake against active compilation
* Make the add-device card clearer and clickable

* Align pairing hierarchy with the shared palette rules

* Keep command emphasis across wrapped instruction lines

* Describe pairing focus and heading-only hover against current dev

* Use keyboard selection ground for the focused pairing field
* test(session): a swept landed fork stays retired

The second sweep of a kept conversation asks furrow about a fork it retired
on the first sweep, and logs "retired" again.

* fix(session): a swept fork stays retired

The checkpoint keeps naming a landed fork forever, and furrow answers a
fork it no longer holds with success, so every launch asked furrow about
the same forks and logged "retired" again. The sweep now remembers what it
retired in forks-retired.json beside the checkpoint and skips those forks.

* docs(changes): a swept fork stays retired
…ce wording (#1967)

* Keep a chat continued at startup owned by the window that took it

A bare start that continued another computer's released chat dropped the
command batch Home returned, which carried the directory ask whose
single-flight guard it had already set. The guard then stayed set, so the
window kept the listing it read before the take: the first message opened
"Continue this chat here?" for a chat this computer held, and choosing
continue here retired this computer's own session and released its own
hold before the conditional take was refused.

Home's commands are now returned on every automatic continuation outcome.
A completed take marks the chat as held here at once and replaces a listing
that was asked before it. A send decides the continuation card from a fresh
ownership read rather than the cached listing, and that read no longer ends
an outstanding listing request. Continuing checks the fresh holder before
retiring anything: a chat this computer already holds stays open as it is,
and a different holder is refused before local work is touched.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Let a coordinating computer's clean quit release its chats, then hand coordination back

A personal computer that led coordination ran its member inside the open
terminal and stopped it when the terminal exited. The engine's closing
release for the chat it held then reached a coordinator that was already
leaving, got 503, and was never tried again, so the chat read as held
offline until its lease ran out and coordination paused until the anchors
elected a new leader.

A coordinating terminal now waits, as it leaves its chats, for each idle
chat's closing release to finish, then transfers leadership to an anchor
and waits for an anchor to answer a fresh majority read before its member
stops. That hand-back is bounded by LeaderQuitWithin (5 seconds); if the
anchors cannot take over, the ordinary fallback applies. The anchors then
restore their own voters as fallback already does, and the computer that
quit does not get leadership back. A terminal that is not coordinating
leaves exactly as before. A closing release that meets a temporary
coordination refusal, including a bare 503, is retried within its existing
close deadline; a definitive refusal is never retried.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Say a removed computer was removed when it asks for cluster status or lead

The cluster control client read every answer other than 200, 404 and 409 as
a coordination outage, including the 401 a removed device receives. So on a
removed computer `codeaf cluster status` said the cluster could not
coordinate and told the person to reconnect voting computers, while
`codeaf devices` correctly said the device was removed.

A 401 carrying the directory's revoked code is now the removed-device error,
and `codeaf cluster status` and `codeaf cluster lead` explain it with the
same sentence `codeaf devices` uses, from one shared definition. Every other
refusal keeps its words.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep continued words, count only real local work, hide empty remote chats, and keep version checks side-effect free

Four small findings from a two-computer run:

- Choosing continue here on the card a send raised replaced the displaced
  session with a fresh one and lost the words held behind the card. The
  arriving chat's box now receives the same draft, pasted text and
  attachments, unsent until the person presses enter again.
- Taking a chat back into a displaced window counted the retired
  session's closing save of unchanged bookkeeping as unsent turns, so the
  move said edits were kept and Home grew a branch row for turns the relay
  already had. A sealed turn is now kept as a branch only when the engine
  finds real local work in it, the same rule continuing already uses.
- Every window registers its empty new chat with the directory, and the
  other computer listed it as an untitled running chat. A record with no
  saved turn is no longer drawn on another computer's Home; it appears
  once its first turn is saved. Registration and ownership are unchanged.
- codeaf --version, help and manual requests ran the former-folder
  adoption and removed old scheduler timers before parsing anything, so a
  version check could move a state folder and recreate the compatibility
  link. Those informational requests now skip both; every real start keeps
  them.

The footer's fit-by-priority, the engine outliving its window, and /quit
closing the conversation in front are the documented behaviour and are
unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Add the change entry for the two-computer fixes

The entry is numbered when the pull request exists.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep the manual's moving and task questions reaching the pages that answer them

The new paragraphs about a send's ownership check and the words kept after
continue here lengthened the page's first section until "continue on
another computer" no longer reached the section that says where moving
works, and the clean-quit heading competed for the same words. They now
have a section of their own and the clean-quit heading says what it is
about. The version-request note on the help page drew "old version of the
file" away from the task pages; the starting page already says that
version, help and manual requests change nothing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Number the two-computer change entry.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Name the pull request in the two-computer change entry.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep a displaced window's reply bookkeeping out of kept local work

Taking a chat back into a window another computer had displaced reported
"your unsaved edits here were kept as 1 turn" and left a branch row on both
Homes, in both directions. A real reply appends pace, usage and call
accounting after its shared save, and retiring the displaced session seals
that tail. Both the take's unsaved-work check and the old drive's closing
publish counted it as new work.

Local work is now decided from the journal beyond the shared boundary:
accounting records are not work, while conversation turns, tool results,
unreadable records and workspace changes are still kept. The same decision
guards the unsaved-work check and the closing publish, so neither creates a
branch for what the relay already has.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Carry the words typed in a displaced window into the chat it continues

A send in a displaced window raises Home with the continuation card. The
typed words then live in the conversation's stashed composer, not in the
input Home shows, and retiring the displaced session can clear them before
the moved chat opens. The moved chat opened with an empty box.

The conversation's composer is now taken when the confirmed take starts,
carried with the take's answer and restored into the arrived chat, even
when its transcript or session differs from the displaced one. Nothing is
sent until the person presses enter.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Publish a chat to other computers only after its first conversation turn

A window's launch chat saves a bookkeeping head at start, and that head
was published, so the other computer listed the never-used chat as
"untitled running on <device>". A directory record always has a head, so
the earlier filter on an empty head could never match.

The drive keeps saving startup bookkeeping locally but notes a head for
publication only once its saved journal holds a conversation message,
bounded to that save, so text typed later cannot publish an earlier empty
snapshot. A never-used chat therefore never reaches another computer's
Home; it appears with its first turn and keeps ownership, quit and continue
as before. A keyless end-to-end run covers the displaced take-back in both
directions and both Homes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Say a removed computer was removed when its cluster commands are refused

`codeaf cluster status` and `codeaf cluster lead` start with a control
request whose handler answers an authentication refusal as a plain 403, so
the client still reported the generic coordination failure on a removed
computer.

When a cluster request fails without a typed cause, the command now checks
through the signed directory read whether this device was removed and, if
so, explains it with the `codeaf devices` sentence. An unexplained 403 is
never guessed to be a removal, and other failures keep their words.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Stop a removed computer's sync traffic until it is paired again

After a removal, the open window kept listing chats, sending presence and
heartbeats and running state and member rounds every few seconds, each
answered 401.

The sync HTTP gate now remembers the removed refusal, and so do directory
watches and a committed local removal. A marker bound to this device,
identity and relay shares that fact across windows and restarts, and
blocked requests are answered locally with the same removed refusal. State
and member rounds stand down and open members close. Pairing installs a new
device key, which lifts the marker, and an open Home resumes with the new
device. Local chats keep working throughout.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Read repositories without taking git's optional index lock

Home's repository band ran `git status` without `--no-optional-locks`, so
each refresh could create `.git/index.lock` and rewrite `.git/index`. When
that landed during a move's in-place restore, the restore saw `.git` change
and cancelled, and the take gave the chat back.

Every git command codeaf runs on its own to read a repository now passes
`--no-optional-locks`, so a background read never takes the index lock or
rewrites the index. Commands that deliberately write are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Send only from a chat this window holds, and leave a failed move safe

After a failed take the chat was free, so the window's send door, which
only blocked a chat another computer held, let a turn run on a stale copy
that could never publish.

A send now goes ahead only when this window holds the chat at the fence it
recorded, or the chat was never shared. A free chat whose local head equals
the shared head is taken back quietly with a conditional take, then sent;
otherwise the continuation card or the view-only state appears and the
words stay in the box. A failed take marks the window's copy as one it may
not send on and says why in plain words, and a stale-fence refusal makes
the window view-only. When the ownership check cannot reach the other
computers, a chat this window holds keeps working and its turns wait to be
shared, and anything else stays unsent.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep the whole draft when continuing a chat fails

The draft captured when a take started was restored only on success, so a
lost race, a refused or unreachable take, or a failed in-place restore lost
the words typed behind the card. Every failure path now puts the draft back
in the box, unsent, and the manual says what a failed move leaves behind.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Offer nothing about continuing on a computer that was removed

A removed computer's Home still offered "Continue <device>'s latest chat
here?" with its chord. Once this computer knows it was removed, Home drops
the left-off offer, the continue chord and the continue action on rows,
and keeps the removal sentence.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Explain a removed computer's stopped syncing without the word relay

The device pages keep to the words people use for their computers; the
removal paragraph now says the computer stops asking the other computers
for chats, and that pairing can still reach the service.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Pace the removal traffic test's heartbeats until the removal is known

The test drove the drive's heartbeat every 10 ms from the start, which
queued relay commits ahead of the member's first catch-up. On a slow disk
the member round waited minutes, and the request signed before that wait
was refused as clock skew. The heartbeat now keeps an ordinary pace until
the removal is learned and only then runs fast, so the observation still
spans many heartbeat intervals.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Announce a displaced chat only when another computer took it

After every take the window that took the chat said "<its own device>
continued this chat; this window now only shows it". The take's acquire is
followed by the arrived drive's own acquire, which raises the fence, and the
window compared the listing's fence with the hold it recorded at the take.

The window's recorded hold now follows its own drive's publishing receipt,
so a newer fence held by this computer for this window is still this
window's hold. The notice and the card come only from a hold by another
computer; a stale local receipt still cannot send. The second acquire stays:
it is the boundary that keeps two local drives from publishing under one
fence.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep each chat's draft with that chat when continuing from Home

A take captured whatever the window's main box held and restored it into
the chat that arrived, so an unsent draft typed for another chat appeared
in the moved chat's box and was saved as its draft.

The arrived chat's box now holds only the words typed for it: the draft
held behind the card its own send raised, otherwise its own saved draft,
otherwise nothing. A draft typed for another chat stays with that chat,
including when a take fails.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep the directory's orphaned draft adoption, and limit draft ownership to takes

Keeping each chat's draft with that chat had also stopped an ordinary
opening from adopting the draft a dead window left in its directory, which
is how the words follow the person and the directory. Adoption is restored.
Only a chat arriving through a take now brings just its own saved words and
never adopts another chat's draft.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Keep "how do I add another computer" reaching the pairing steps

The removal and empty-chat sections spoke of "another computer" and
"adding the computer again", which drew the question away from the section
with the pairing steps once that section's card text changed. They now say
"machine" and "your other devices", and the question reaches the steps
again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ves (#1972)

* test(enginehost): a retiring host's save must not hold the next launch

Two failing tests. A host on its way out keeps the workspace lock while its
conversations save, so no new host can start; and a launch that finds the
lock busy with nothing listening waits out the whole birth wait without
starting a host when the holder lets go.

* perf(enginehost): hand the workspace over before a retiring host saves

A host on its way out closed its conversations, which waits for each one's
last save, and only then removed its socket and released the workspace lock.
In a large folder that save runs for its whole 30 s bound. A launch arriving
in that window found the lock busy and no socket, waited the full 10 s birth
wait for a host nobody was starting, and then opened its conversation in its
own process: about 15 s to the first frame.

The host now removes its socket and releases the lock first, then saves.
Each conversation keeps its own journal lock until its save is done. And a
launch that finds the lock busy now asks again on every turn of its wait, and
starts the host itself the moment the holder lets go, which covers hosts of
older builds that still save while holding the lock.

* fix(enginehost): let a retire wait a moment for the saves it hands over

The full suite caught it: an environment restart retires the idle host and
the new host reopens its journals, which the old process was still closing.
A retiring host now holds host.saving from before it gives up the workspace
until its last conversation is closed, and waitForGone waits up to one second
for it once the slot is free. Launches that only need the slot never wait on
it, and a slow save past that second no longer holds anybody.

Also the change entry.
* Bump embedded furrow to v0.3.4 (prune abandoned capture plans)

* Add change entry for #1974

* Name the #1974 change entry by slug
)

* test(cellstore): a folder holding home is never planned at startup

Red: a seat opened in the home folder (or its parent, or a filesystem
root) asks the engine for a full capture plan of it. The root case walks
the whole disk in the guard before the engine is asked.

* fix(cellstore): never capture a folder that holds the home folder

A chat opened in the home folder, a folder above it or a filesystem root
seals its own folder instead: the conversation is kept, the folder's files
are not. Engine.workspace is the one place every seal, rewind and take
reads the tree from, so all of them agree. The seat says so once.

* docs(changes): a home-folder chat saves the conversation only

* test(cellstore): a home chat never captures the sender's home path elsewhere

Red: the same chat, on a machine whose home is not the sender's, plans
the sender's home path as its project. The syncsetup guard covers the
take: B keeps the chat in a folder of its own and A's path is untouched.

* fix(cellstore): a home chat never lands in the sender's home path elsewhere

A chat that sealed only its own folder because its folder held a home
folder records that in .cell/own-folder, which travels with every
snapshot. Wherever the chat is continued, a workspace outside the chat's
own folder that holds no home folder there is foreign: the engine seals
and restores the chat's own folder instead, and the take side never
takes the chat in place there.

* docs(changes): the own-folder mark travels with a home chat

* test(cellstore): the home notice test declares the engine available

It seals over a fake transport, so it needs no engine binary. A build
with no engine inside (CI's touched rest leg) gave it a plain seat and
no notice.
…ailed chat copies (#1980)

* test(vaultsync): a restore opens the vault a fixed number of times

Fails on dev: a restore of N shared keys opens the whole sealed vault
N+1 times, once per key the ledger knows.

* perf(vaultsync): read a scope's tombstones once per restore

A restore asked the vault whether each key the ledger knew had been
removed, and every question opened the whole sealed vault. A profile with
963 shared memories and a 1.5 MB vault decoded about 1.4 GB of JSON per
state round, every few seconds, in an idle terminal. Removed lists a
scope's tombstones in one read.

* refactor(olderchats): one visit per chat behind a copy seam

No behaviour change: the eligibility checks and the copy move into
olderChats.visit so a test can count copy attempts.

* test(olderchats): a failed copy waits for the chat to change

Fails on dev: the background copier tries a chat whose copy failed again
every round, and each attempt seals the chat's whole folder.

* perf(olderchats): a failed copy waits for its chat to change

The background copier visits every older chat each pass, and a copy seals
the chat's whole folder. A copy that failed was attempted again on every
pass for as long as the terminal stayed open. It is now remembered against
the journal's size and time, and tried again when the chat changes, on the
next launch, or when the person opens it.

* test(olderchats): the background copier leaves a folder that holds codeaf's home

Fails on dev: a chat started in a home directory is sealed whole in the
background, and that seal never settles while codeaf writes its own state
inside the folder.

* perf(olderchats): leave a folder that holds codeaf's home to be copied on open

A chat started in a home directory has codeaf's own state inside its
folder. Codeaf writes there while it runs, so the whole-folder seal a copy
begins with never settles: in a live terminal each attempt planned the
folder eight times and failed with "files kept changing". The background
copier now skips these chats; opening one still copies it.

* revert(olderchats): leave home-folder chats to the cellstore rule

#1976 makes a folder that holds the home folder seal only the chat's own
folder, which is the case this skip covered. One rule for it is enough;
the failed-copy memo stays.

* docs(changes): an idle terminal stops reopening the vault per key
Separate saved evidence, concept organization, reviewed guides, and human changes. Publish maintained pages atomically for Memory and agent recall. Document module contracts for contributors.
… two-computer fixes (#1944)

Delegated tasks can work in private copies that span several folders and come home through /task review and
/task apply. This sits behind CODEAF_PROTOTYPE_WORKSPACE_OWNERSHIP with an absolute CODEAF_HOME, on Linux and
macOS only. Without the switch, ordinary chats land delegated work as before and keep the checkpoint and carrier
formats older builds read. Apply refuses before writing when the profile and the project's Git index are on
different disks.

Warm-transfer frame hints are carried by the Raft relay's state machine. They are bounded, dropped from listings,
and off by default.

Two-computer fixes:
- a removed computer keeps working on chats it held or never shared;
- a move that fails because the other machines are unreachable says the chat stayed and the words were kept,
  in the chat and on Home;
- activity lists a chat once and no longer marks a finished task incomplete;
- a moved task's worker transcript reopens.

Co-authored-by: Santosh kumar <29346072+santoshkumarradha@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…and six rough edges (#1990)

Fixes everything the real two-computer relay tests found after #1944, and what three further real PC + Mac rounds found on the way.

- A take-back after a kill reaches its first acquire in about a second; ordinary chats restore once, after the lease. A copied chat can move back and forth any number of times, and a killed computer takes its chat back on the first try.
- /quit waits for the final publish and release. Only a live hold sends without asking the relay; a displaced window raises the continue card on the next Enter and can never fork the chat. A removed computer keeps working in its own chats.
- Words typed while /new opens, sent through a card, or left by a failed take land exactly once in the right box; drafts are keyed per chat.
- Sealing recovery is claimed only when this chat's turns are saved, a stuck store gets a plain sentence, a failed final save names where the turns are kept, and closing never freezes the window. Embedded furrow 0.3.5 removes only its own socket.
- A moved finished task opens read-only with its steps and worker transcript.
- Plain sentences for cluster status, devices and pairing when the relay is down or slow; the holder says when it is not syncing; Home never offers this computer's own chat.

Closes #1982, #1984, #1985, #1986.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…1992)

* Reuse unchanged knowledge checks without fragmenting fresh work

* Preserve separate occurrences of identically worded questions

* Exercise question reuse through the large-catalog path

* Assign knowledge reuse change entry to PR 1992

* Integrate reviewed knowledge into existing guides with coverage checks

* Preserve valid published knowledge through bounded section maintenance

* Weave additions into section prose with support and coverage review

* Reuse supported maintained core when its editorial context is unchanged

* Reconcile every guide update with concept-specific admission review

* Pack reviewed paragraphs and bound whole-guide budget recovery

* Update provider fixtures for maintained guide publication
* Reuse unchanged character-bounded knowledge groups after corrections

* Describe evidence-group reuse at its existing validation boundary

* Record knowledge correction reuse fix
#2001)

* Quitting right after work saves quietly and never branches bookkeeping

A quit straight after a finished task could report "could not save", leave its
hold behind and make the other computer see this one as offline. The drive's
closing step cancelled a publish the relay had already accepted, then retried
it with the old head and got a conflict from itself. Close now joins the
publish in flight, publishes the newest head, and releases.

The saving wait also stopped at the ordinary ten-second call deadline while
saying "up to 30s", so a save that went on to succeed was reported as failed.
Close and detach now use the shared saving bound, and a reply that never came
says the save is still finishing rather than that it failed.

Each launch sealed an empty turn onto the chat, even one held elsewhere, and a
later take turned those turns plus late cost records into a branch chat nobody
typed. A launch no longer seals an empty turn, a viewer never seals, and
accounting-only history is adopted on a take instead of kept as edits. A
removed computer's quit saves locally and prints nothing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Change entry for the quiet quit and bookkeeping takes

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Unlock the final-saving state from a defer at both new sites

The quit's finishing flag was set and read under a lock released by hand, which
turns an absorbed panic into a deadlock. Both now go through small helpers that
unlock from a defer, as the rest of this file does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

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

* fix(sync): never copy an older chat that works in the home folder

The background older-chat copier sealed each recent chat's folder. A chat
started in the home folder has the home folder as its workspace, and a copy's
first seal has no change hints, so furrow planned over every file under it
while holding the shared store's one writer. Every open chat's tool calls
queued behind that plan, hit the 30 s client wait, and failed as
"engine capture-plan answer interrupted: ... use of closed network connection".

Reuse the existing rule that the home folder is not a project
(session.HoldsHomeFolder, exported from delegate_door.go): the background
round and the open-time copy both skip a non-owned chat whose folder is or
holds the home folder. It keeps opening in place, as before Wormhole.

* docs: change entry for #2002

---------

Co-authored-by: agentfield-bot <agentfield-bot@users.noreply.github.com>
AbirAbbas and others added 2 commits October 27, 2026 21:29
Brings the Raft relay (#1950), the two-computer fixes (#1967, #1990),
quit saving (#2001), the older-chat copy rule (#2002), private task
copies (#1944), the knowledge vault and the add-device card into the
desktop's engine line.

App mode keeps its own engine lifetime and now runs the same final save
on quit: runApp flushes the drive and closes it within the saving bound,
so quitting right after a turn no longer strands a hold or leaves the
turn unsaved. ConfirmWork retires a stale send hold and wakes the
renewal readers, so taking a chat back after another computer held it
admits work at once. Directory records stay canonical Raft aliases; the
app's devices, sync and location routes keep their wire shape.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AbirAbbas and others added 2 commits October 28, 2026 15:05
…e two-computer fixes (#2011)

* codeaf: open a chat, pair route and Continue here for a given home

The CLI's chat opening, pairing mailbox/grant and Continuer construction
read the process home. Each now has a variant that takes the home
folder, and the CLI calls it with home.Dir(), so the app backend can
build the same objects for its own home instead of copying them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: pairing, chats on other computers and Continue here for the desktop

New app-wire doors behind three hello features:

- devices.pair: pair.start/get/answer/cancel and pair.changed. One
  pairing at a time in all four modes the terminal has (offer a code,
  use a code, make a link, approve a link), with the terminal's own
  sentences. A computer that already has chats is asked before its own
  chats are replaced; a join while an agent works is refused as busy;
  a join that replaces this computer's identity ends with restart.
- chats.take: chats.list plus chat.changed/chat.removed from one shared
  Sync.Follow follower (no polling), each row naming the computer it
  lives on, presence when known, and the local agent when this computer
  has it. Older home-folder chats stay local-only (#2002). agents.take
  continues a chat here through the CLI's Continuer, refuses a stale
  seen with conflict/holder_changed, and returns the local agent; the
  other computer's open agent gets agent.changed with here:false.
  Location gains cell, seen, can_take and takeover_line.
- sync.relay: Sync.relay reports the relay address and where it came
  from (environment, paired, default, none) or off.

Tests cover the desktop contract (E-P1..E-P4, E-C1, E-C2, E-T1, E-T2,
E-Q1, E-R1) with two homes and a loopback relay, including the real app
entry point in two processes taking a chat, refusing the old holder,
and saving on quit so it can be taken straight back.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: the desktop's relay, pairing and Continue here doors

PROTOCOL.md lists the new methods, events, types and hello features;
the chat manual's desktop-sync page says how Relay, pairing, chats on
other computers and Continue here work in the desktop; and a change
entry records the additions.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: the desktop's own lines for an unpaired home and for sync off

The app reused the terminal's sentences: an unpaired home said "pair
from a terminal with `codeaf pair`" and sync off said "set
CODEAF_SYNC_URL". The desktop now pairs and sets its relay itself, so
app mode says "Only this computer is here so far." and "Sync is off on
this computer." (devices.list, sync.get, devices.remove, agents.take).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: publish chats made before pairing, desktop words, quieter chat events

From the first live two-computer run (Windows desktop and a Mac engine
through one relay):

- Chats saved before pairing now reach the other computer: at app start
  and after a pairing, each local cell chat not yet in the relay's
  directory is published once through the terminal's publish door
  (PublishCopy), without opening it or running a turn. Older home-folder
  chats keep #2002's rule and stay local.
- chat.changed is sent only for rows that changed. Rows carried a
  relative save age, so every directory version looked like a change;
  they now carry the saved date and time.
- Pairing speaks to a desktop person: sentence case, "computer", no
  command lines, flags or y/n buttons (Pair / Don't pair, Replace this
  computer's chats / Not now). Option ids are unchanged; the terminal's
  copy in internal/pair is untouched.
- A chat open on another computer reads "open on <name>", not
  "running": chatlist's running means a live lease, not a turn.
- A failed Continue here says "This chat could not be moved to this
  computer." with the cause in the error detail.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* handoff: a real seal, fetch and swap test for the take path

Exercises the take's fetch into a staging folder and its swap into
place with the native store, so the Windows rename failure seen in the
live run can be reproduced with a Windows test binary.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: chats from before pairing, open on <name>, failed takes

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: synced settings reach the desktop; devices.changed only on change

A preference restore from a paired computer now emits one settings.changed
with the full settings, as a local settings.set does. Identical computer
lists no longer repeat devices.changed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* provider: never send reasoning to a model that refuses it

A model the catalog marks as non-reasoning gets no reasoning parameter. An
unlisted model's specific thinking refusal is repaired once without
reasoning and remembered for the process; that 400 is permanent, so the app
no longer keeps the failed turn to run again later.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: a failed take leaves the chat free, and its source can write again

After an install fails and the taker releases its lease, a computer
without the chat in place no longer reads it as here; it offers Continue
here and names the last holder. The source's next send re-acquires the
free lease at a higher fence when its saved head still matches, instead of
refusing held_elsewhere. Fences never roll back.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: keep takeover staging folders out of agent admission

Listing, recovery and lazy admission could open a copied chat under
copies/<id>.taking and watch it before the taker renamed it into place,
which on Windows fails the rename with Access is denied.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: failed takes, synced settings and reasoning refusals

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* relayserve: forward quiet releases through the presence wrapper

The native relay client's wrapper hid ReleaseQuiet, so the fallback sent an
ordinary release that no longer matched the signed quiet mutation. The relay
refused it with 401 and the startup publish left a stray lease behind.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* directory: carry a chat's saved-turn time beside its publish time

A published cell now records when its sealed head was saved, so a chat
published later still says when its last turn ended. Relays advertise the
optional field with Codeaf-Saved-Turn-Times: 1; an older relay still accepts
the publish without it, and an unknown time is omitted, never replaced by
the publish time.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: chat rows say offline only when it is, and change only when they do

A closed chat no longer reads "<name> offline" while its computer is online;
offline needs presence to say so, and unknown presence is omitted rather
than sent as false. saved_at is the saved turn's time and the takeover line
carries no absolute UTC time. chats.list never emits, and a row is sent
again only when its content changed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: chat-row presence, saved-turn times and quiet releases

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* sync: a model choice travels only to computers that can use its provider

An incoming model slot is applied only when this computer's resolved
provider connections can call it (a key, a sign-in or a local endpoint).
A skipped choice keeps the receiver's own value, is remembered so it is
not echoed back as a newer edit, and does not advance the slot's clock; a
later choice made after the provider is set up still travels. Effort and
spending limits keep syncing as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* sync: a holder's fence moves only when ownership changes

Startup heartbeats a hold it still owns instead of acquiring it again, a
take ends at the fence it returns, and a watch reconnect renews an
unchanged idle hold rather than releasing and re-acquiring it. A take
whose seen fence was bumped by the same holder with the same saved head is
no longer a holder_changed conflict; a different holder or a new head
still is. Relays advertise this with Codeaf-Head-Takes: 1, and older relays
keep exact-fence checks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* presence: a reconnect does not make a computer look offline

Last-seen times only move forward, fresh presence survives a watch
reconnect, and presence queries use the same reconnect grace as watch
events. A computer reads offline only when its presence actually lapsed;
lost observation becomes unknown after the existing 25-second window.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: one complete devices.changed per change, no row events on reconnect

The computer list is emitted once complete and then only when its wire
value changes, from every emitter. A watch reconnect that changes no chat
row sends no chat.changed, and a sync round whose only incoming model
choice was skipped sends no settings.changed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: model sync, stable fences, presence and head-guarded takes

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cellstore: materialize a take's tree through a one-shot engine

The shared store daemon keeps a directory watch on a tree it materialized.
A take renames its staging tree into place next, which Windows refuses
while the tree is watched (Access is denied). Guarded materialization now
runs a one-shot engine and waits for it to exit; Follow binds the final
root after the swap.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cellstore: run follow and plans from the store, naming the workspace

Follow and the dirty/local-work plans started the engine inside the chat's
workspace. A taken chat filed under its origin folder can pass Windows'
260-character limit for a working directory, and a recorded workspace can
be the other computer's path. The engine now starts in the local store
and receives the workspace with --repo.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cellstore: pass the composed cell folder as an extended path on Windows

Restoring a chat writes receipts under its .cell folder, and those paths can
pass 260 characters. Furrow does not canonicalize this argument itself, so
Windows now receives it in \\?\ form. Other platforms are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* handoff: take tests with the shared daemon, long paths and composed take-back

Real encrypted fetch, staging-watch check, swap, Follow, Dirty/Seal and a
take-back that restores receipts past 260 characters. The app round-trip
test can keep its state on failure (CODEAF_E2_KEEP_FAILED=1) for native runs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: Continue here onto Windows

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* sync: a computer keeps its own chat through a relay outage

If this computer's lease lapsed only because the relay was unreachable,
it renews it when the relay answers again (same fence when nobody else
acquired and the relay's head is an ancestor of the local one) and
publishes the work saved meanwhile. An unreachable renewal keeps the chat
writable and its publisher alive; startup publishing retries every 5–60
seconds instead of giving up. A computer never names itself as the one
that continued a chat elsewhere.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: a taken chat whose model this computer can't use continues on the local default

Before the first turn on this computer, a chat whose model has no usable
provider here switches to this computer's default model and says so once.
Going back to the original computer keeps working. With no usable model at
all, the turn fails with a plain setup reason instead of a provider 400.
The provider check is the one model sync already uses, moved to config.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: outage and model-fallback acceptance tests

Two homes on a loopback relay that stops past the lease TTL and restarts on
the same address; a competing take during the outage; startup recovery; and
disjoint providers for the model fallback.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: relay outages and chats whose model is not set up here

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: an open chat keeps its hold instead of churning its fence

The ten-minute idle sweep retired an open chat's conversation, and the next
read (skills.list, agents.open) reopened it and acquired again, moving the
fence every ten and a half minutes. A chat the person has open now keeps
its hold with heartbeats until it is closed; background-only conversations
still retire.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* syncsetup: refuse a send-lease path without a home

A test built Sync without Home, so its refusal wrote a hold lock relative to
the package folder. The fixture now uses a temp home, and an empty home is
an error before any read, mkdir or lock.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: open chats keep their hold

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* presence: a restarted relay does not call a computer it has not heard from offline

Presence lives in relay memory. Right after a relay (re)starts, every paired
computer that has not reconnected yet looked offline, so a peer that never
stopped read "<name> offline" until its first heartbeat reached the new relay.

For one default presence window after presence memory starts, both the native
and hosted relays omit a computer they have not observed (unknown). An
observed connection that actually lapses still reads offline, and a computer
that stays away past the window reads offline as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: Continue here completes when this computer has no usable model

A take whose chat used a model this computer can't run, with no usable local
default either, acquired the lease and installed the chat, then returned an
error. The chat had moved, but the desktop showed a failure and a stale row.

Now the take completes and returns the local agent, and the row's local id is
reconciled in the same change. The first send fails locally with a plain
setup sentence and no provider call (naming waits too). Once a usable default
is set, the next send uses it and explains the switch once.

Take errors with a plain, actionable reason carry it in the visible message;
the detail keeps the technical cause without repeated "engine:" labels.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: taking a chat without a usable model, and presence after a relay restart

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cellstore: a take's one-shot materialize keeps a transport the caller chose

The guarded materialize always replaced the engine's transport with a fresh
Spawn, so a caller's own transport (a test's fake, an isolated runner) was
dropped and a carried furrow became mandatory. Only the shared daemon is now
swapped for a one-shot process, keeping its socket, so the daemon still never
holds a watch on a staging tree; any other transport is used as given.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* syncsetup: the idle-wake test's homes can use the model they sync

A model choice now travels only to a computer that can use its provider, so
the receiving home needs the same fake provider key as the sender for the
C30 preference to arrive.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: guard the relay goroutines and release every new lock from a defer

The repository laws require each goroutine in the guarded tree to run under
guard.Go and each Lock to be released by a defer directly beneath it, so an
absorbed panic can neither take the surface down nor leave a lock held. The
relay work added two bare goroutines (take and existing-chat cancellation)
and critical sections in the chat inventory, pairing, take, devices, sync
and live-drive code that unlocked by hand.

Each section now sits in a small helper that defers its unlock. Lock order,
what each section covers, and what stays outside it (waits, network calls,
emission) are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* session: a job whose ending is already known does not reopen its chat

A job that ended instantly could leave its process record saying "running"
while the chat's task checkpoint recorded it as finished. Every engine start,
and every idle sweep that closed the chat, found that record, opened the chat
to recover it and so took its relay lease, then released it again 10.5 minutes
later: the fence moved every cycle with nobody using the chat.

The recovery check now treats a running record as finished when the checkpoint
holds the ending of the same run (same start and call), and it only reads, so
a chat held by another computer is never written to. When the chat is next
opened for real work, recovery also writes that ending into the process record.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* session: a chat's model choice travels with it

The conversation's model and its main/fallback pair lived only in the local
summary, outside the sealed tree, so taking a chat back onto a computer that
still had its own record of it restored the model from before the other
computer changed it. The choice now rides in the sealed session truth, and a
completed take refreshes the local record from what was installed; the usual
rule then applies (keep it if usable here, else the local default with one line).
Older cells keep reading their summary until their next save.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* session: a continued chat explains its model change in one line

Switching a continued chat to this computer's model recorded the generic
"Main model changed from X to Y." and then the specific reason, and the first
request could add "Using main model Y." too. The switch now records only the
specific sentence through the same conversation model rules; a separate
fallback change keeps its own line.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: background chats stay released, and a chat's model comes back with it

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* raftstore: open the relay's raft store on Windows

The store's SQLite URI was built as file://C:%5C…, which SQLite reads as an
invalid authority, so a native relay on Windows never opened its group's
store and every directory request answered 503. Drive paths now become
file:///C:/…. Directory fsync goes through fsreplace.SyncDir, which skips the
flush Windows cannot do on a read-only directory handle; regular files still
sync before rename.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* relayserve: say why a directory is unavailable

Every 503 path now logs its underlying cause through the relay's log, once
per cause per minute; responses are unchanged. Native provision and transport
persistence use the Windows-safe directory sync.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: a paired computer shows up on its other computers at once

A computer wrote its own device record only when pairing finished and when a
chat's sync drive started, so one failed write left it invisible to its other
computers until a chat synced. The engine now publishes its record whenever
it starts paired, after pairing, after a rename and when the relay answers
again, retrying with backoff until it lands and never rewriting an identical
record. devices.list keeps a readable roster when the advisory presence read
fails, instead of calling the relay unreachable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* pair: words that match the desktop, and computers told apart by platform

The offer tells the other computer to choose Pair a computer, then Enter a
code from the other computer. A successful pairing no longer warns that the
name could not be shared yet (the engine retries it). The approval names the
other computer with its platform, e.g. "Abir (Windows)", and older peers that
send no platform keep working.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: computers appear at once, pairing words, the relay on Windows

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* pair: a pending rename says when the name arrives without naming the relay

The device pages keep their vocabulary law (no relay, lease or node), and the
rename line quoted on naming-a-device said "when they can reach the relay".
It now says "once this computer is back online".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* app: chat rows go out even when device publication connected the watch first

Device publication opens the shared directory watch at startup, so the chat
inventory could join a watch that had already heard its version and
presence. A new subscriber only hears later frames, so the first online rows
could stay cached and never reach the desktop. The inventory now reconciles
an already connected watch once when it subscribes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AbirAbbas
AbirAbbas merged commit d902739 into app/v3 Oct 28, 2026
14 of 22 checks passed
@AbirAbbas
AbirAbbas deleted the merge/dev-into-app-v3 branch October 28, 2026 19:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants