Skip to content

Generic collections (factory_job refs) and client tools (integration branch; draft, do not merge) - #2306

Draft
AbirAbbas wants to merge 23 commits into
app/v3from
workstreams/main
Draft

AbirAbbas wants to merge 23 commits into
app/v3from
workstreams/main

Conversation

@AbirAbbas

@AbirAbbas AbirAbbas commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

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:

Known reds are the base's own (the same tests fail on app/v3's runs).

🤖 Generated with Claude Code

AbirAbbas and others added 21 commits November 2, 2026 19:29
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>
@AbirAbbas AbirAbbas changed the title Workstreams (integration branch; draft, do not merge) Generic collections (factory_job refs) and client tools (integration branch; draft, do not merge) Nov 3, 2026
AbirAbbas and others added 2 commits November 3, 2026 21:30
…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>
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.

1 participant