Skip to content

feat(a2a): a thread's home is its project - #482

Merged
Jacksondr5 merged 3 commits into
j5/mainfrom
fold/ledger-rekeys-to-projects
Oct 8, 2026
Merged

Jacksondr5 merged 3 commits into
j5/mainfrom
fold/ledger-rekeys-to-projects

Conversation

@Jacksondr5

@Jacksondr5 Jacksondr5 commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Problem

Squadrons are being retired and folded into projects (decided 2026-10-05, #412). The client PRs put upstream's project UI back, but the server still kept its agent-to-agent ledger per Squadron, still told agents about Squadrons, and still left some threads (mobile, system-started, imported) outside the ledger.

This PR and #457 merge together. Part of #412.

What changed

After this, a thread's home is its project.

Migration 031 (031_LedgerRekeysToProjects.ts)

  • Re-keys and renames in place. The Squadron table is renamed and its ids replaced; it is never dropped. With foreign keys on, dropping it would empty its nine cascading child tables, and defer_foreign_keys does not hold a cascade back. A test pins that SQLite behaviour.
  • Twelve tables move from squadron_id to project_id; the Squadron-to-project reference table is dropped; j5_a2a_retired_squadron keeps every old id, name and project.
  • No row is added, removed or renumbered. Two checks run inside the transaction and abort with the table and counts: every table has exactly the rows it had, and every row sits under a project a Squadron was moved to. That covers the shapes the product could not build (a Squadron with no project, or several).
  • A project several Squadrons share is refused by name, and nothing changes. The message points at a new runbook section with the repair.
  • A peer server's ids are left alone. Which ids are local is decided per column and per JSON path, by the environment id beside them, by the peer:receive command prefix, and by the drop cause's kind.
  • Placement fingerprints are rewritten as text, keeping property order, so a spawn or fork replayed after the upgrade still matches.
  • It is 031, not 030: the peer-poll stack took 030 while this was being built. This PR adds 31 to the snapshot list (emptied by fix(server): no database copy before the peer-poll migration #481), so the automatic copy is statev2.pre-j5-031.sqlite.

Registration

  • Every thread except a provider Subagent is a participant in its project. Registration left the launch path for the stored thread.created event, which every creation writes, including the two importers that bypass the thread.create command.
  • Existing threads are registered at start. The lifecycle service registers every non-Subagent, non-deleted thread that has no home. A thread already archived is registered as archived, whichever path registers it; a thread found registered but not yet recorded as archived is finished at the next start.
  • A caller with no home registers on its first agent-to-agent call. A Subagent is told it is not a participant.

Tools, routes and contracts

  • Removed: list_squadrons, join_squadron, the Squadron routes, the homes read, the squadron_id input on stop_agent, stop_crew and archive_crew, the same-Squadron checks on archive and merge-back (upstream's same-project rule is the only rule), and the scheduled-task guard.
  • Renamed: list_participants and spawn_agent return project_id and project_title. Fleet, Inbox, Crew proposals, runtime requests, pre-archive facts and machine senders carry project ids. j5 a2a participant create takes --project.
  • Text: envelope headers are version 22 and read in project <id> (<title>). The spawn identity block has project_id and project_title. Error and tool text says project.
  • The peer wire is unchanged in this PR. A peer on the previous version keeps working. See "Where the word still appears".

Clients

  • A client refuses a J5 server that has not been updated, with upstream's "This client requires a newer server" message. It recognizes one by the old ledger capability it still reports. A server with no J5 ledger, plain T3 Code included, connects exactly as before. An older client against this server still connects and shows its J5 views as unsupported. The server reports the two older capability keys, j5Squadrons and j5HumanInbox, as false, so that client's Fleet, Inbox, Inbox count, Crew proposals and Crew runtime requests never read their changed routes. New clients gate every J5 read on j5ProjectLedger. Two older-client reads have no capability key and are accepted gaps: a Crew roster proposal does not appear above the composer, so it cannot be approved from an older client; and archiving shows that client's "could not check" warning without the toast explaining why a Crew seat cannot be archived alone (the server still refuses that archive).
  • Known limit: a new web client whose own primary server is an old J5 server shows upstream's "Still connecting" screen, not the update message. Upstream's own protocol refusal lands on the same screen. It is reachable only through a dev proxy, since a client is served by its own server; adding an old J5 server in Settings → Connections shows the update message.
  • The web keeps reading old stored messages: the two envelope header readers and the spawn-brief reader accept both formats.
  • The sidebar's "spawned by an agent" fact now comes from the spawned-children read (spawnedByAgent), with the same meaning as before.
  • Deleted: the Squadron directory, the Squadron-to-project lookups, the homes store, and the playbook author picker on web and mobile (the author starts in the workspace's project).

Where the word still appears

  • The peer wire: originSquadronId on a delivery, squadronId in a drop cause, and squadronId / squadronName on a roster row (packages/contracts/src/j5.ts). The values are project ids and titles; only the names are old. Renaming them means bumping the peer protocol version, after which every peer pair has to update together, so it is left out of this PR. Whether it is built is Jackson's call.
  • j5Squadrons in packages/contracts/src/environment.ts: kept so a client can recognize a J5 server that has not been updated.
  • Internal names (squadronId, SquadronId, readSquadron, the Fleet page's types): the next PR renames them with no behaviour change.
  • Migrations 001 to 031, j5_a2a_retired_squadron, and the readers for old stored messages, as agreed in Check that Squadron is gone from code, docs and agent text (on or after 2026-10-28) #476.

UI changes

  • Settings → Playbooks (web and mobile): the "Authoring Squadron" picker is gone.
  • Fleet and Inbox: a row whose project the client cannot find shows the title the server sent, not a Squadron name.

Playbooks settings, web (the author picker is gone; same projects, data, viewport and light theme). Captured by the crew's tester.

State Before After
Empty project library Empty project library, before Empty project library, after
Library loading Library loading, before Library loading, after
Library read error Library read error, before Library read error, after
Populated project library Populated project library, before Populated project library, after

Playbooks settings, mobile (native iOS, empty library).

Before After
No home: Create is blocked The author starts in the project: Create is enabled

Fleet (same data, viewport and theme; the last row is a controlled fixture).

State Before After
Empty Empty, before Empty, after
Loading Loading, before Loading, after
Read error Read error, before Read error, after
A row whose project the client cannot find A row whose project the client cannot find, before A row whose project the client cannot find, after

Inbox (same data, viewport and theme; the last row is a controlled fixture).

State Before After
Empty Empty, before Empty, after
Loading Loading, before Loading, after
Read error Read error, before Read error, after
A row whose project the client cannot find A row whose project the client cannot find, before A row whose project the client cannot find, after

Sidebar, threads an agent spawned (same parent and seven children). The nesting is unchanged; its source moved to the spawned-children read.

Before After
Seven spawned children nested under their parent The same seven nested under their parent

Pinning a spawned child moves it to the top level, and unpinning nests it again:

Pin, top level, unpin, nested

Version skew.

An older client against this server This client adding an older J5 server
The Inbox shows the source as unsupported The update message in Add environment

Not captured: the loading, error and populated Playbooks states on mobile; those states are captured on web.

Upstream impact

File FORK.md case
orchestration-v2/ThreadLaunchService.ts, .test.ts, ws.ts, packages/contracts/src/orchestrationV2.ts 10 retired: the squadronId launch carrier and the registration block are removed
scheduledTasks/ScheduledTaskService.ts 12 retired: the guard is removed
packages/client-runtime/src/operations/commands.ts, .test.ts 10 retired; 11 down to its client half
apps/web/src/components/Sidebar.tsx 23: one hook swapped for another
packages/client-runtime/src/connection/compatibility.ts 56 (new): the client gate, one call
packages/contracts/src/environment.ts, environment/ServerEnvironment.ts, .test.ts 34: capability key j5ProjectLedger added and reported true; j5Squadrons and j5HumanInbox kept and reported false
mcp/toolkits/worktree/registration.test.ts, provider/T3OrchestrationInstructions.test.ts 4, 8: two tool names and one assertion removed
packages/client-runtime/package.json 34: two ./j5/* exports removed

Register of divergences: D8, D9 and D11 retired; D29 added for the client gate. The gate changes nothing for upstream's own servers; it refuses only a J5 server from before this change. The decision behind it is the plan's strict version gate for clients in #412.

How it was checked

  • vp run typecheck in contracts, client-runtime, web, mobile, desktop and server: 0 errors each.
  • vp test run in contracts (564), client-runtime (1,953), web (5,842) and mobile (1,861): all passed.
  • CI on the final commit: the required job "Format, lint, typecheck, and unit tests" passed, with the whole server suite in three shards (Test Server 1, 2 and 3), the client tests, and format, lint and typecheck.
  • A placement command recorded before the upgrade is replayed through the real placement service against the migrated row and is recognized as the same command.
  • The real startup path migrated a copy of a live database from 29 to 31 in about 6 seconds: 2,596 events, 2,249 receipts, 1,032 deliveries, 161 Exchanges and 143 memberships before and after, foreign_key_check and integrity_check clean, the snapshot written beside it.

Checklist

  • One concern: the description has no "also" — one concern (a thread's home is its project), but it is large; the server and its clients share packages/contracts, so it could not be cut further without a throwaway translation layer
  • Tests cover the changed behavior (backend changes ship with focused tests)
  • UI changes: before/after screenshots above, and a video for motion or interaction — captured by the crew's tester; mobile Playbooks has the empty state only
  • Upstream-owned files: each one is recorded in FORK.md (case text and file-table row) in this PR
  • Upstream product: any change to what upstream's product does has a human decision linked above and a register entry in docs/j5/product/upstream.md
  • Surfaces: entry points, clients, providers, contracts, reverse states, connection modes (see AGENTS.md) — nothing here is provider-shaped; the mobile app and a second machine were not run
  • Docs: definitions under docs/j5/product/ and user docs rewritten where this changes them — left for the docs PR by instruction; this PR edits only FORK.md, the register, one runbook section, and one sentence of docs/j5/product/a2a/agent-tools.md that a test holds byte-equal to the send_message tool description

Claude Opus 5.5 (1M context), Claude Code in J5 Code.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
j5-code Ready Ready Preview Oct 8, 2026 6:22am UTC

Request Review

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). labels Oct 8, 2026
The welcome wizard loses its Squadron stage and imports into projects as
upstream does. Branding and the Providers label stay.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Jacksondr5
Jacksondr5 force-pushed the fold/onboarding-back-to-upstream branch from 1912866 to 197e79c Compare October 8, 2026 04:27
@Jacksondr5
Jacksondr5 force-pushed the fold/ledger-rekeys-to-projects branch from 8c2966e to 850fe82 Compare October 8, 2026 04:27
@Jacksondr5 Jacksondr5 closed this Oct 8, 2026
@Jacksondr5
Jacksondr5 deleted the fold/ledger-rekeys-to-projects branch October 8, 2026 04:28
@Jacksondr5
Jacksondr5 restored the fold/ledger-rekeys-to-projects branch October 8, 2026 04:29
@Jacksondr5 Jacksondr5 reopened this Oct 8, 2026
@Jacksondr5
Jacksondr5 force-pushed the fold/ledger-rekeys-to-projects branch from 850fe82 to 377247f Compare October 8, 2026 04:40
@Jacksondr5
Jacksondr5 force-pushed the fold/ledger-rekeys-to-projects branch from 377247f to eb3cba9 Compare October 8, 2026 04:46
@Jacksondr5
Jacksondr5 force-pushed the fold/ledger-rekeys-to-projects branch from eb3cba9 to b8a3915 Compare October 8, 2026 04:54
Squadrons are retired (#412). The agent-to-agent ledger is keyed by
project id, every thread except a provider Subagent is a participant in
its own project, and everything an agent or a person reads says project.

Migration 031 re-keys and renames the ledger in place: the Squadron table
is never dropped, so nothing cascades, and no row is added, removed or
renumbered. It refuses by name a project that several Squadrons share,
keeps a record of the old ids and names, leaves a peer server's ids alone,
and aborts if any table's row count changes or any row is left without a
project.

Registration moves off the launch path to the stored thread.created
event, which every creation writes, including the two importers that
bypass the thread.create command. A server that upgrades registers the
threads it already has, and a caller without a home registers on its
first agent-to-agent call.

list_squadrons, join_squadron and the Squadron routes are removed. Tools,
routes and contracts carry project ids and titles. A client refuses a
server whose ledger is not keyed by project.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Jacksondr5
Jacksondr5 force-pushed the fold/ledger-rekeys-to-projects branch from b8a3915 to 0cca778 Compare October 8, 2026 04:56
@Jacksondr5 Jacksondr5 mentioned this pull request Oct 8, 2026
5 of 7 tasks
@Jacksondr5
Jacksondr5 marked this pull request as ready for review October 8, 2026 05:29
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Jacksondr5
Jacksondr5 changed the base branch from j5/fold-onboarding-back-to-upstream to j5/main October 8, 2026 06:22
@Jacksondr5
Jacksondr5 merged commit c98a6d9 into j5/main Oct 8, 2026
46 of 59 checks passed
@Jacksondr5
Jacksondr5 deleted the fold/ledger-rekeys-to-projects branch October 8, 2026 06:40

This branch was successfully deployed

1 active deployment
Preview — ce5ddc4f Deployed Oct 8, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant