Skip to content

app: workstream store (schema 3, factory_job refs, all 25 methods served) - #2270

Merged
AbirAbbas merged 7 commits into
workstreams/mainfrom
workstreams/wp1-store
Nov 3, 2026
Merged

AbirAbbas merged 7 commits into
workstreams/mainfrom
workstreams/wp1-store

Conversation

@AbirAbbas

@AbirAbbas AbirAbbas commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

The workstream store from the Workstreams plan, WP-1, built on the v0 wire contract that #2263 registered.

  • Storage (schema 3 of the collections database). The first write rebuilds a schema 1 or 2 file by copying its tables (SQLite cannot change a CHECK in place). Every collection and membership keeps its sequence number, id, name and times. New tables: workstreams, workstream_jobs (its unique index keeps a Factory job in one workstream), workstream_pages, workstream_page_versions, workstream_records, workstream_goaheads, workstream_plans.
  • factory_job ref kind in the SQLite CHECK, workspace.Ref.Validate and appwire.CheckRef, with factory on the ref. The CLI takes --factory.
  • Domain package internal/workstream/: stage, numbers, plan, pages with versions, records and go-aheads. page.get's markdown comes from internal/workstream/pagedoc (app: workstream pages render from editor JSON to markdown (pagedoc) #2277), the one page renderer in the engine.
  • Handlers: all 25 methods serve from the store, with bounds checks at the wire's one door. ServesWorkstreams() is true, so hello announces workstreams. Every write sends workstream.changed.
  • Create makes the collection, the row, an Overview page and one bare agent with no first turn, so no model runs. A repeated client_id returns the first workstream.
  • Numbers: stage, merged, needs_you and spend come from bounded Factory queue reads plus the agent's recorded spend. Unknown values are left out, never sent as 0.
  • Collections: workstream: true on listings; deleting a workstream is refused (mark a workstream done instead); a job another workstream holds is refused with in_other_workstream.

Validation contract

item test
(a) migration keeps collections and memberships, adds tables and kind internal/workspace/schema3_test.go
(b) job in another workstream → in_other_workstream, nothing changes internal/workspace/workstreams_test.go, cmd/codeaf/appworkstreams_test.go
(c) create makes one workstream, collection and agent in Shaping; repeat client_id returns the first internal/workstream/store_test.go, cmd/codeaf/appworkstreams_test.go
(d) Shaping → Building → Done only by mark_done; reopen → Building internal/workstream/store_test.go, cmd/codeaf/appworkstreams_test.go
(e) mark_done ends every go-ahead internal/workstream/records_goaheads_test.go, cmd/codeaf/appworkstreams_test.go
(f) stale page.save → version_conflict, nothing changes; history/restore add a version internal/workstream/pages_test.go, cmd/codeaf/appworkstreams_test.go
(g) records propose → keep/drop/supersede; superseded cannot change internal/workstream/records_goaheads_test.go, cmd/codeaf/appworkstreams_test.go
(h) go-ahead without scope, cap or end date refused internal/workstream/records_goaheads_test.go, internal/appwire/workstreams_check_test.go, cmd/codeaf/appworkstreams_test.go
(i) every write emits workstream.changed cmd/codeaf/appworkstreams_test.go
(j) unknown numbers omitted, measured zero kept internal/workstream/store_test.go, cmd/codeaf/appworkstreams_test.go

Stubbed or left

  • Go-ahead spent_usd stays absent, and go-aheads and wave auto-start are stored but not enforced. Both wait on the Factory side (plan §3.3 asks 4 and 3).
  • The workstream agent gets the plain prompt. WP-10 adds its tools and system prompt at the hook in workstreamAgentAsk.

CI

  • PR gate: the only reds are the known baselines: the laws ratchet in internal/session, TestAppChatRowsR2SavedTurnTime, TestAppChatStartupR3QuietRelease and TestRecipeDoorWritesTheWorktreeWhenOnlyTheCheckoutIsHeld. TestAppIdleReleaseI1I2* flaked once and passed on rerun.
  • Full check on the branch compared with the integration branch's full runs: no new failure repeats. The one-offs passed on rerun or on another run of the same commit, and none is in code this change touches: TestAProposedTaskCarriesTheMessageItWasMadeFromAndKeepsItAcrossAReopen, TestARequestOlderThanTheRunningTurnWaitsForIt, TestSilentRouterCanRecoverOnTheNextRoutingCompletion, and two temp-dir cleanup races in Factory tests (TestAppFactoryJobWithoutIssueHasAnHonestReference, TestFactoryStopAnyVersion).

Contract changes

None. The factory ref field, workstream: true and in_other_workstream are the additive collection behaviour the v0 contract already describes.

🤖 Generated with Claude Code

AbirAbbas and others added 6 commits November 2, 2026 22:14
…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>
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>
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>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
@AbirAbbas
AbirAbbas force-pushed the workstreams/wp1-store branch from 9245a68 to 5d2d994 Compare November 3, 2026 03:15
…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>
@AbirAbbas
AbirAbbas merged commit d7456f8 into workstreams/main Nov 3, 2026
25 of 41 checks passed
@AbirAbbas
AbirAbbas deleted the workstreams/wp1-store branch November 3, 2026 05:10
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