Repository navigation
Generic collections (factory_job refs) and client tools (integration branch; draft, do not merge) - #2306
Draft
AbirAbbas wants to merge 23 commits into
Draft
Generic collections (factory_job refs) and client tools (integration branch; draft, do not merge)#2306AbirAbbas wants to merge 23 commits into
AbirAbbas wants to merge 23 commits into
Conversation
The Workstreams feature lands on workstreams/main in work-package pull requests before it goes to app/v3. Listing the line here gives each of those pull requests the light gate and its touched packages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
) The Workstreams plan, WP-0: Go types for the 25 workstream, page, record and goahead methods and the workstream.changed event, registered through typed() and delegating to an optional Workstreamer; appBackend answers every one unknown_method "workstreams: not implemented yet" and does not announce the workstreams feature until the store lands (WP-1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…2277) * app: workstream pages render from editor JSON to markdown The Workstreams plan, WP-6c: internal/workstream/pagedoc.Markdown renders a page's editor JSON by the page document format rules, byte for byte the same as the desktop app's renderer, so a job that reads a page sees the markdown a person sees when they export it. Pure: no I/O and no store; invalid JSON is an error, an absent document is empty, unknown nodes keep their text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tests: page markdown matches the desktop app's fixtures and the contract example Every assertion of the desktop app's markdown tests as a Go table row with the identical expected string, the contract's worked example as testdata, invalid and absent documents, and edge cases that pin JavaScript's line terminators, number formatting and coercions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: change entry for the workstream page renderer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ved) (#2270) * collections: schema 3 with workstream tables and the factory_job ref kind The collections database moves to schema 3. The first write rebuilds a schema 1 or 2 file by copying its tables (SQLite cannot change a CHECK in place), keeping every collection and membership under the same sequence numbers and times, and adds the workstream tables: rows keyed by the collection id, pages and their versions, records, go-aheads, the plan, and a job claim table whose unique index keeps a Factory job in at most one workstream. factory_job joins the ref kinds in the SQLite CHECK, the workspace model and the wire validator, with its factory id on the ref. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstream: domain package for the workstream store Stage, numbers, plan, pages with versions, records and go-aheads, written over the collections store's transactions. Every rule the Workstreams plan (WP-1) gives the store lives here behind plain Go types, so the wire never reaches into the tables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: serve the workstream methods from the store Every workstream.*, page.*, record.* and goahead.* method now answers from the store, behind bounds checks at the wire's one door, and hello announces workstreams. Create makes the collection, the row, an Overview page and one bare agent (no first turn), and a repeated client id returns the first. Every write sends workstream.changed. Stage and numbers come from bounded Factory queue reads and the agent's recorded spend; anything unknown is left out rather than sent as zero. Collections mark workstreams, refuse to delete one, and refuse a job another workstream holds with in_other_workstream. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: change entry for the workstream store Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: workstream locks unlock from a defer, factory reads are guarded The guard laws want every lock released by a defer and every goroutine guarded; the workstream Factory cache and batch reads now are. Update, mark done and reopen answer with the same live numbers as get, and workstream text refuses control characters (prose keeps newline and tab). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstream: page.get renders markdown through pagedoc The engine keeps one page renderer: page.get's markdown now comes from pagedoc.Markdown, which matches the desktop app's renderer byte for byte, and the store's own renderer is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: a panicking Factory read still finishes its flight; writes keep the first start A queue read that panics inside its guard now closes its flight and caches the failure, so waiters are released and a later read tries again. Update, mark done and reopen persist the first observed start like get does and announce one workstream.changed for the whole change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…e) (#2288) * tests: the wire contract's workstream examples as engine testdata The examples, field tables, enums and error codes the Workstreams wire contract carries, one file per method and event, with the extractor that regenerates them when the contract changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tests: workstream answers through the wire keep the contract's shapes Each method's example params go through the real wire handler against a seeded store; each answer must have its example's keys at every level, the field tables' JSON kinds and enum words, no nulls and integral integers. Every engine-sent error code has a call that checks its code and detail keys. A method, event or error code the contract gains without a case fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: change entry for the workstream wire conformance test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: the conformance change entry names its pull request Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tests: conformance replies carry every illustrated array element A reply array shorter than its example no longer skips the elements the example illustrates (a plan's draft item, an unattributed page version); page.delete must return exactly the seeded parent and descendant. Request writes to the wire are bounded like replies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Brings the integration branch up to app/v3 (28 commits: Factory spend and budget rows, one engine per home, memory off by default, the app delta window, process-group reads on macOS, and their tests). No conflicts; the three files both sides touched (cmd/codeaf/app.go, internal/appwire/server.go, docs/design/app-v3/PROTOCOL.md) merged line-disjoint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ons read by jobs (#2290) * workstream: deleted pages are kept and restorable; pages read by version Schema 3 (unreleased) gains deleted_at and the delete that removed each page. Delete marks a subtree; restore brings back what that delete removed, placed last under a live parent or at the root. A page reads any saved version. Read-by-jobs defaults on for pages titled Spec or Decisions at creation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: page.get by version, page.list include_deleted, page.restore of a deleted page The wire handlers serve the wire contract's v0.1 page additions and apply the Spec/Decisions read-by-jobs default when page.create leaves it unset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tests: conformance covers the v0.1 page params and example pairs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: change entry for workstream page versions and restorable delete Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ges) (#2289) * workstream: agent pages stay the agent's, lookup by agent, markdown import The store now refuses, inside the save transaction, an agent write to a page the person owns, and an agent create of a page it would not own, so a page made the person's between a read and a save is still refused. ByAgent finds the workstream whose agent is a conversation. pagedoc.FromMarkdown turns markdown into the editor JSON the page renderer reads, live-block markers included, and the contract's example round-trips byte for byte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * session: a workstream agent's prompt section and six tools behind a door A conversation handed a WorkstreamDoor carries workstream_read, propose_decision, propose_question, propose_jobs, propose_goahead and write_agent_page, and a short prompt section naming its workstream and the rules: it proposes and the person keeps, it writes only its own pages, it never invents numbers and it starts nothing. Arguments outside each schema are refused before the door is touched, propose_jobs only appends drafts and retries a version conflict once, and a go-ahead proposal is a proposed decision that grants nothing. Without a door the belt and the page are unchanged, and task nodes, errands and clarifications never carry it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: give a workstream's agent its door, and re-read spend after its turns The app sets the workstream door on the conversation whose created-by is workstream and on no other; the door finds its workstream at each call, since the agent is opened a moment before the workstream row exists. Its writes are attributed to the agent and send workstream.changed, and a workstream agent's settled turn sends one too so its spend is read again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * manual: what a workstream's agent can do Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: change entry for the workstream agent's tools Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: gofmt the workstream agent door test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstream: agent writes check done in their transaction; markdown import is linear An agent's record or page write now refuses a done workstream inside the same write transaction, so marking done between the agent's read and its write leaves nothing behind. Imported list items always begin with a paragraph, and unmatched backticks or brackets are consumed in one pass instead of rescanning the rest of the line for each one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * session: workstream tools follow the argument and retry laws The prompt's workstream brief is a snapshot that never touches the store, so a re-render under the session lock cannot wait on it. Arguments decode only through decodeToolArguments, a repeated key is refused at any depth, every argument refusal is marked, and the plan's one retry is written without a counting loop. A go-ahead's proposed cap is dollars, excused from the whole-number law in its own file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: keep each workstream agent's brief current without reading the store The app remembers each workstream's name and why by its agent when it creates, reads, updates, marks done or reopens one, and when the agent's door resolves it, so the prompt reads that snapshot. A done refusal from the store reaches the agent as the workstream being done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: workstream agent tools refuse repeated keys; done is checked in the write Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * session: import the workstream types in the validation test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstream: only an explicitly attributed agent proposal is refused when done The done check in ProposeRecord read the defaulted actor, so the wire's unattributed record.propose on a done workstream, which has always landed as a proposal, was refused. It now reads the attribution the caller gave, and the agent's door keeps passing its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * session: the workstream agent keeps its "Where it stands" page current The app shows the agent's summary on the Overview from the agent page titled exactly "Where it stands". The prompt and write_agent_page now name that page and ask the agent to keep it current with what it was updated from. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * session: a shorter Where it stands rule, and room for it in the prompt budget The rule now fits in one short line, and the workstream section's own budget test allows 1000 bytes for the worst-case name and why. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tests: the conformance seed lends the agent the Overview for its one save The store now refuses an agent write to a page the person owns, so the seed that reproduces the contract's agent-saved Overview hands the page to the agent for that save and back again, keeping the agent's attribution and version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tests: page version and restore fixtures write as the agent only on its pages The store now refuses an agent write to a page the person owns, so the version-read and deleted-tree fixtures that save or create as the agent make those pages the agent's first; what they assert is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…eaf leg 25m) (#2296) * tests: the conformance busy case waits 200 ms for the lock, not 10 s The busy error case held a real writer on a second handle and let the backend's store wait out the full 10-second SQLite busy timeout. That one subtest was 10.0 of the workstream tests' 11.2 seconds in cmd/codeaf. It now sets PRAGMA busy_timeout to 200 ms on the backend's own single connection, reads it back and fails unless it took, then holds the same real writer. The refusal still travels SQLITE_BUSY -> ErrBusy -> wire busy through the real store and dispatch. Production keeps its 10 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci: the codeaf touched leg gets a 25-minute package timeout cmd/codeaf runs unsharded (sharding it failed in #1734). Its serial suite measured 1065 s (17.8 min) on the 4-vCPU runner on 2026-11-03, 1053 s of it in tests that predate the workstream work, so the leg hit the Makefile's 15-minute timeout and every test after the cut went unchecked. The base branch's own runs time out the same way. Only the codeaf leg passes --timeout 25m; tui3, session and rest keep the Makefile's 15m. A timeout is still red. docs/rules/ci.md records the exception and that it goes once the suite is back under 15 minutes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: change entry for the codeaf leg's timeout Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ow agent ids, goahead proposals that grant when kept (#2297) * workstream: draft size/budget/source, and go-ahead proposals that grant when kept Draft plan items keep an optional size, budget and source. A goahead record carries its proposed scope, cap and end; keeping it grants exactly one go-ahead in the same write, dropping a kept one revokes it. Schema 3 gains the record columns in place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: workstreams wire v0.2 (row agent ids, page sources, goahead records) and conformance Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * session: the workstream agent proposes go-aheads as goahead records and sources its draft jobs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * changes: number the workstreams wire v0.2 entry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… chat resume skips the per-chat database scan (#2303) * workspace: SQLite refuses deleting a workstream's collection, for any writer An engine that opened collections.db at schema 2 before another engine migrated it keeps its cached version and its old Delete, which has no workstream check: its DELETE FROM collections would cascade away the workstream, its pages, saved versions, records, go-aheads and plan. A BEFORE DELETE trigger now refuses that delete in the database itself. New and migrated files get it with the schema; schema-3 files from earlier integration builds gain it (IF NOT EXISTS) inside their next write. Plain collections delete and cascade as before; Store.Delete keeps its message. The deleted-pages purge test deletes the workstream row before its collection, since the collection alone can no longer be deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chat: resume asks collections.db about workstream agents only for empty chats v3EmptyUnassignedSession opened collections.db before its cheap local checks, so resuming in a project scanned the database once per saved conversation, including every conversation somebody spoke in. The ownership question now comes last, for folders the local checks already found empty, and still keeps a folder whose owner cannot be read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * changes: entry for the workstreams stability fixes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…sions pages read by jobs (#2310) * workstreams: size limits on plans, pages and records (wire v0.2.1) Draft text was bounded only by its own length, plans had no count or encoded-size bound, and a workstream could accumulate pages and records without limit, so every list read materialized all of them. The store now refuses, inside its write transaction and before anything is written: draft text over 80,000 bytes; a plan over 500 milestones, 500 waves, 5,000 items or 4 MiB of encoded milestones and waves; a 5,001st page (deleted pages included) or record. The new ErrLimit names the bound and answers too_large on the wire; the plan_set check refuses the counts and draft text early. The conformance testdata is regenerated from the contract (too_large on plan_set, record.propose, record.supersede). The agent manual states the limits and that new agent pages titled Spec or Decisions are read by jobs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstreams: agent pages titled Spec or Decisions are read by jobs, as page.create makes them Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstreams: control characters stay bad_params past the draft text bound; the manual separates the tool's draft limit Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstreams: tests for the size limits and the agent's Read-by-jobs default TestStabEm1AgentSpecAndDecisionsPagesAreReadByJobs: agent pages titled Spec or Decisions (any case, surrounding space) are read by jobs, others not; updates keep the flag. TestStabEM4*: plan bounds through the real wire and the store, exact bounds accepted and one past refused with too_large and no event; page and record counts (deleted pages included) refuse wire and agent writes; plan_set and record.propose too_large conformance cases; early wire refusals keep bad_params for control characters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: change entry for the workstream size limits Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Brings the integration branch up to app/v3 (29 commits: job step labels and conditions, step cost as the sum of its rounds, spending kept across reruns, closed issues leave Backlog, one way to build a crew watch, run workers act as their task, one model per role, live lines per call, and their tests). No conflicts; the three files both sides touched (cmd/codeaf/app.go, internal/appwire/server.go, internal/session/session.go) merged line-disjoint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since app/v3's "A job keeps its earlier spending when it runs again", a job's Cost is only its current run and LifetimeCost holds every run. The workstream's spend summed Cost, so it fell when a member job ran again. It now takes the larger of the two, which is LifetimeCost from a current factory and Cost from an older one that does not send it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…loat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e small changes, wake.* methods) (#2319) * workstreams: schema 4 adds wake tables by an additive migration Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstreams: wake records, settle under autonomy, apply/undo/not now Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: wire wake.list, wake.apply, wake.undo, wake.not_now and the one-shot wake turn Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: the workstream agent wakes each morning and on member job events; wake costs count toward spend Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: the watching agent's change entry names its pull request Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstreams: undo refuses drafts and pages changed since; morning wakes only for today's slot; wake calls carry their purpose Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: the protocol document lists the wake methods Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstreams: wake undo keeps a later rename and tolerates sibling reorder; replay knows the wake's role Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tests: wake.list fixture follows the contract's money form Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstreams: the wake call has a ledger owner; the wake queue unlocks from a defer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ma 5 (#2320) * workstreams: schema 4 adds workstream_sources by an additive migration Schema 3 stays readable without writing; the first write creates the table and moves to 4 in one transaction, keeping every existing row. Schemas 1 and 2 migrate straight to 4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstreams: sources store (file, repo, issue, link, kept) with strict path and URL checks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: workstreams wire v0.3: source.list/add/update/remove, create takes sources Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: the workstream agent reads sources and proposes new ones for the person to keep Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: change entry for workstream sources names its pull request Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: the app protocol lists the source methods Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…, suggest_page_edit, schema 6) (#2318) * workstreams: page text blocks, edit anchoring and keeping by code point Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstreams: suggestions store: list, keep one or all as one version, drop Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: workstreams wire v0.3: suggestion.list, suggestion.keep, suggestion.drop Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: the workstream agent suggests edits on the person's pages (suggest_page_edit) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: change entry for workstream suggestions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: number the workstream suggestions change entry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstreams: keeping an edit leaves other text nodes' fields intact; empty marks equal no marks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: the protocol lists the suggestion methods Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstreams: schema 5 adds the suggestion tables after the watching agent's schema 4 Files at 1-4 stay readable as they are (suggestion reads answer empty); the first write applies every missing step in order inside its transaction. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: the suggestions change entry names schema 5 and wire v0.4 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * workstreams: schema 6 adds the suggestion tables after Sources' schema 5 Files at 1-5 stay readable as they are (suggestion reads answer empty); the first write applies every missing step in order inside its transaction. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: the Suggestions addition counts 36 methods and eight agent tools; change entry names schema 6 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: the suggestion prompt rule fits the workstream section's 1000-byte budget beside the Sources rule Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Brings the integration branch up to app/v3 (6 commits: the stored repository list served with a background refresh, step conditions read the change's files, step and My pipeline AI count as Factory AI, notes and the completion guard share one command outcome, opening a job doesn't count as a person's touch, a budget raise isn't refused by spend progress). No conflicts; the three files both sides touched (cmd/codeaf/app.go, internal/appwire/server.go, docs/design/app-v3/PROTOCOL.md) merged line-disjoint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eld conversations are kept (#2334) * A client can attach tools to a conversation over the app wire agents.tools_set attaches, replaces or removes one named set of tools on a conversation; agents.tool_call carries each model call to the client and agents.tool_result answers it. The hello says agents.client_tools. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Client tools ride at the tail of a conversation's belt A conversation with a client tool door offers the client's tools after every built-in, with each set's instructions on its own page and nothing added when no set is attached. Tools follow their declared category, a built-in name wins, and child tasks and discussions get neither tools nor instructions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * A conversation any collection holds is not blank-chat litter Launch cleanup, the blank-chat sweep and the background places sweep keep a never-spoken conversation that a collection references, reading the collections database read-only and keeping the folder when it cannot be read. Opening such a conversation by id after a restart restores its owned, model, archive and spend facts from its metadata. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * codeaf app sends client tool calls to the connected app The app backend keeps each conversation's client tool sets for as long as the connection lasts, emits one agents.tool_call per model call and waits for the answer, its deadline, the turn's end or the connection closing. Call ids are stable for a replayed call, and a late or repeated answer is ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Change entry for client tools and collection-held conversations Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * The client tools manual page is found by its own name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Client tool registry follows the guard and name laws The registry's sender runs under guard.Go, every registry lock is released from a defer, and a test name no longer spells the old product name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… (schema 7) (#2336) * collections: schema 7 keeps every collection and Factory job ref, drops development tables The collections store now writes schema 7: collections and memberships in the generic shape (factory_job refs with their factory, five-column UNIQUE), and nothing else. Versions 1-7 are read as they stand; the first write migrates inside its own transaction. Schemas 1 and 2 are copied into the generic tables; schemas 3-6, written only by development builds, lose their extra tables, indexes and triggers while every collection, membership and ref stays. A failed first write leaves the file as it was; 8+ is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: the engine keeps only the generic pieces of the Workstreams plan Workstreams move into the app, so the engine drops everything that existed only for them: the workstream store and page renderer, the wire methods and hello features, the agent's built-in tools and prompt section, the morning wake scheduler and its replay purpose, the conformance fixtures, and the old launch-cleanup hook. Removing the agent door also removes the copy of it that Fresh and Open carried. What stays is generic: collections hold Factory jobs (the resolver moves to appcollections_factory.go under generic names, with its own tests), client tools, and collection-held conversations that survive cleanup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: protocol and manual keep Factory refs and client tools; workstream entries go The protocol and the collections manual lose the workstream methods, events, features and schemas, and keep the factory_job and client-tools sections. The unreleased workstream entries described features that will not ship from the engine; one entry for the generic pieces replaces them and the client-tools entry. The CI-timeout entry no longer mentions the deleted conformance case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: one change entry for the generic collections, client tools and kept conversations Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: the generic-pieces entry is written fresh, so the gate sees it as added The first version reused the client-tools entry's wording, so git paired the two files as a rename and the change-entry gate found no added entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…hown as live (#2337) * A blank conversation a collection holds is kept but never resumed Launch resumed a never-spoken conversation that a collection references, ahead of the last spoken one, because "kept" and "spoken" shared one predicate. The emptiness check now reads only content; references keep a blank out of both the resume and the reuse piles, and every removal path still refuses it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Collections show a held blank conversation as live after a restart The resolver built its view from agents.list, which leaves named blanks out, so collections.open and collections.expand called them deleted. A missing id is now looked up once on disk before it is called deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: held blanks are neither resumed nor called deleted Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Change entry for #2337 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * A permanently deleted conversation stays deleted in its collection The on-disk fallback found the metadata a permanent deletion keeps, so the resolver would have shown a deleted conversation as live and allowed a relink to it. A row pending deletion, or a folder with the deletion marker, now stays deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Only a conversation member uses the saved-folder fallback A blank found on disk shared the listing cache that task and process members use to find their owner, so a collection read could open a crew member's conversation to look for a child that does not exist. The fallback now serves conversation members only, from its own cache. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Brings the integration branch up to app/v3 (4 commits: pairing QA fixes for failures, approvals, removed computers and the devices loop; a person's pause survives a lost heartbeat reply; pairing follow-ups where app and pair approve settle a lost reply and chat rows say a take is running; the plugins desktop protocol over the connect layer). One conflict, in cmd/codeaf/app.go: the appBackend struct keeps the client-tools field beside app/v3's plugin pause, Ollama and auth fields. internal/appwire/server.go (both optional hello features), internal/session/connect.go and docs/design/app-v3/PROTOCOL.md merged line-disjoint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Integration branch for engine changes the desktop app's Workstreams feature needs. Draft: do not merge.
The Workstreams feature itself lives in the desktop app; the engine stays the AI harness. After an architecture
correction on 2026-11-03, every workstream-specific piece that had been built here was removed (#2336). What remains is
generic and useful to any client:
(
agents.tools_set); each call reaches that client asagents.tool_calland is answered withagents.tool_result.Tools exist only while the client is connected; disconnect, timeout and turn end answer the model with an error.
Hello feature
agents.client_tools.factory_jobcollection ref kind (with an optionalfactory), resolved incmd/codeaf/appcollections_factory.go.collections.dbschema 7: drops tables left by earlier builds of this branch and keeps every collection,membership and ref. app/v3 refuses a version-7 file (as it refuses 3-6) until this branch merges.
cmd/codeaftouched leg's timeout is 25 minutes (the base alone runs about 17.5); needs the CI owner'sagreement.
Known reds are the base's own (the same tests fail on app/v3's runs).
🤖 Generated with Claude Code