Skip to content

Split sessions.rs, and make the agent event contract a type - #132

Merged
ParallelEntrepreneur merged 1 commit into
mainfrom
colonizer/issue-73-84c379b1
Sep 18, 2026
Merged

ParallelEntrepreneur merged 1 commit into
mainfrom
colonizer/issue-73-84c379b1

Conversation

@ParallelEntrepreneur

@ParallelEntrepreneur ParallelEntrepreneur commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

Ported onto main

This branch was written before the publish stages (#122), the pull-request watcher, the spend budget and the host-disk quota landed, and it still carried eight commits that had already been squashed onto main — which is why it showed 73 changed files and could not be merged. The branch has been rewritten: the work is redone against main as it stands, in one commit. Its CI half is already on main as #138, so what is left here is the refactor and the typed contract.

The split

sessions.rs was 2,963 lines. What moved out, by what it does:

Module Holds
events.rs The live agent link: one websocket per colony, the event stream, memory proposals, findings, the autopilot decision
lifecycle.rs Everything that ends or restarts a microVM — stop, cleanup, delete, resume, recovery, the sandbox tick — and the host's own limits (spend budget, host-disk quota), because passing one ends a colony the way the max session length does
publish.rs Publishing the work, and watching the pull request afterwards
queue.rs The parallel limit: who gets a slot, who waits, who starts next

sessions.rs keeps the types and the state they hang off: Session, Runtime, App's session methods, creating and booting a colony, and the browser-facing reads.

Tests moved with the code they cover. The shared fixtures stay in sessions::tests, and the modules that moved out use them. Behaviour is unchanged: the same 234 tests pass, plus six new ones below.

The agent events are a type now

protocol.rs replaces string matching over serde_json::Value in the dispatch:

  • docs/agent-events.schema.json is the machine-readable contract for all thirteen event types; docs/protocol.md §2 stays the prose.
  • The harness acts on seven. The rest, and any type a newer runner adds, land on AgentEvent::Other, so a new event type can never make a line fail to parse. Every line still reaches the browser before dispatch.
  • A line whose type this build acts on but whose body it cannot read is now logged on the colony rather than silently doing nothing.
  • modules/agents/claude-code/test/fixtures/events.jsonl is the runner's real output, and both sides test against it: the runner's suite validates it against the schema, and the harness deserialises every line into the variant the dispatch expects. Drift fails a test on both sides.

Verification

  • cargo test --workspace: 240 passed (234 before the split, plus six contract tests).
  • cargo clippy --workspace --all-targets -- -D warnings: clean.
  • npm test in modules/agents/claude-code: 59 passed.
  • npx tsc --noEmit and npx vitest run in web: clean, 67 passed.

Refs #73

@ParallelEntrepreneur

Copy link
Copy Markdown
Collaborator Author

Split, not superseded — #138 takes the half of this that lands clean on current main, and this PR should keep the other half.

In #138: the CI workflow and PR template, agentd's no-KVM smoke test, the runner's event fixtures and extra fake-sdk tests, and the web UI's 65 tests. Plus the sixteen clippy findings on main that the new -D warnings gate would otherwise have gone red on immediately — thirteen nested ifs that are let-chains, an iter().any() that is contains, a while-let that is a for loop, and a function after mod tests. One of those, the ci.yml comment pointing at crates/colonizer/src/boot.rs:66, was repointed at where the credential gate lives without the refactor.

Left here: the sessions.rs split into boot.rs, events.rs, lifecycle.rs, protocol.rs, publish.rs and queue.rs, with docs/agent-events.schema.json.

That part needs a real rebase rather than a mechanical one. It moves about 1,400 lines that main has changed since this branch was cut: the update sweep and Session.app_slot (#112), publish_stage (#87), the accepts_commands fix so an answer cannot be dropped silently (#114), and mem0 (#97). A cherry-pick resolves those as "ours deleted / theirs modified" — the danger is that a moved copy silently reverts a fix instead of failing to compile, so it should be redone by whoever made the split, function by function, against main.

Once #138 lands, that rebase gets checked by CI on its own pull request, which is the point of doing it in this order.

Ported from #132, which was written before the publish stages, the
pull-request watcher, the spend budget and the host-disk quota landed and
could no longer be merged: its branch still carried eight commits that had
already been squashed onto main. The work itself is redone here against
main as it stands.

`sessions.rs` was 2,963 lines. What moved out, by what it does:

- `events.rs` — the live agent link: one websocket per colony, the event
  stream, memory proposals, findings, and the autopilot decision.
- `lifecycle.rs` — everything that ends or restarts a microVM: stop,
  cleanup, delete, resume, recovery, the sandbox tick, and the host's own
  limits (spend budget, host-disk quota), because passing one ends a colony
  the way the max session length does.
- `publish.rs` — publishing the work, and watching the pull request after.
- `queue.rs` — the parallel limit: who gets a slot, who waits, who starts
  next.

`sessions.rs` keeps the types and the state they hang off: `Session`,
`Runtime`, `App`'s session methods, creating and booting a colony, and the
browser-facing reads. Tests moved with the code they cover; the shared
fixtures stay in `sessions::tests` and the modules that moved out use them.
Nothing changed behaviour: the same 234 tests pass, plus six new ones.

The agent events themselves are now a type (`protocol.rs`), not string
matching over `serde_json::Value`:

- `docs/agent-events.schema.json` is the machine-readable contract for all
  thirteen event types, and `docs/protocol.md` §2 stays the prose.
- The harness acts on seven of them; the rest, and any type a newer runner
  adds, land on `AgentEvent::Other`, so a new event type can never make a
  line fail to parse. Every line still reaches the browser before dispatch.
- A line whose type this build acts on but whose body it cannot read is now
  logged on the colony instead of silently doing nothing.
- `modules/agents/claude-code/test/fixtures/events.jsonl` is the runner's
  real output, and both sides test against it: the runner's own suite
  validates it against the schema, and the harness deserialises every line
  into the variant the dispatch expects. Drift fails a test on both sides.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ParallelEntrepreneur ParallelEntrepreneur changed the title Run the tests on every pull request, split sessions.rs, and type the agent event contract Split sessions.rs, and make the agent event contract a type Sep 18, 2026
@ParallelEntrepreneur
ParallelEntrepreneur merged commit 8a4abd2 into main Sep 18, 2026
7 checks passed
@ParallelEntrepreneur
ParallelEntrepreneur deleted the colonizer/issue-73-84c379b1 branch September 18, 2026 09:12
ParallelEntrepreneur added a commit that referenced this pull request Sep 18, 2026
Rebuilt on main rather than merged: this branch predated the tree-wide
format (#142) and the sessions.rs split (#132), so its diff collided with
both — the agent-link code it edits now lives in events.rs, and every hunk
fought the reflow. The feature is unchanged; only where it attaches moved.

- `autonomy.rs` is taken as it was written.
- The open question carries its questions now (`Runtime::open_question`
  returns `(id, questions)`), so the judge can answer among the labels the
  agent offered — and the dispatch that fills it is the typed
  `AgentEvent::Question` arm in events.rs, which already had them.
- `Activity::judged`, the `autonomy` module kind and its settings, the
  config entry, the docs and the web bits are as the branch had them.

Verified on this tree: cargo test --workspace 246 passed (240 + this
branch's 6), clippy -D warnings clean, cargo fmt --all --check clean, and
the web suite 67 passed with tsc clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ParallelEntrepreneur added a commit that referenced this pull request Sep 23, 2026
Taken from #132, which also refactors sessions.rs into six modules. That
refactor conflicts with everything merged since it was cut and deserves its
own rebase; this is the half that makes any of it verifiable, and it lands
clean.

CI runs four jobs on every pull request, one per suite so a red run names
what broke: cargo test --workspace plus clippy, the Claude Code runner's
tests, the live map receiver's, and the web UI's build and tests. Actions
are SHA-pinned, permissions are contents: read, and superseded runs are
cancelled.

A fifth job boots a real colony and is skipped unless COLONIZER_KVM_RUNNER
names a self-hosted runner with /dev/kvm — GitHub's hosted runners have
none, so even the rust:1-alpine build VM cannot start there. It is
deliberately not a required check: a permanently skipped job is not
coverage, and pretending otherwise is worse than saying so.

The clippy gate could not have gone green as it stood: main had sixteen
findings. Thirteen were nested ifs that are let-chains in this edition, one
an iter().any() over a Vec that is contains(), one a while-let over an
iterator that is a for loop, and one a function sitting after the test
module. None change behaviour; the Host/Origin check in main.rs was
collapsed carefully, and its tests still pass.

Also brought over: agentd's no-KVM smoke test, which boots the real binary
on loopback against a stub agent runner; the runner's event fixtures; and
the web UI's first tests, 65 of them across sessionStream and settlers.

Locally: 176 Rust tests, 59 runner, 11 receiver, 65 web, clippy clean.
ParallelEntrepreneur added a commit that referenced this pull request Sep 23, 2026
Ported from #132, which was written before the publish stages, the
pull-request watcher, the spend budget and the host-disk quota landed and
could no longer be merged: its branch still carried eight commits that had
already been squashed onto main. The work itself is redone here against
main as it stands.

`sessions.rs` was 2,963 lines. What moved out, by what it does:

- `events.rs` — the live agent link: one websocket per colony, the event
  stream, memory proposals, findings, and the autopilot decision.
- `lifecycle.rs` — everything that ends or restarts a microVM: stop,
  cleanup, delete, resume, recovery, the sandbox tick, and the host's own
  limits (spend budget, host-disk quota), because passing one ends a colony
  the way the max session length does.
- `publish.rs` — publishing the work, and watching the pull request after.
- `queue.rs` — the parallel limit: who gets a slot, who waits, who starts
  next.

`sessions.rs` keeps the types and the state they hang off: `Session`,
`Runtime`, `App`'s session methods, creating and booting a colony, and the
browser-facing reads. Tests moved with the code they cover; the shared
fixtures stay in `sessions::tests` and the modules that moved out use them.
Nothing changed behaviour: the same 234 tests pass, plus six new ones.

The agent events themselves are now a type (`protocol.rs`), not string
matching over `serde_json::Value`:

- `docs/agent-events.schema.json` is the machine-readable contract for all
  thirteen event types, and `docs/protocol.md` §2 stays the prose.
- The harness acts on seven of them; the rest, and any type a newer runner
  adds, land on `AgentEvent::Other`, so a new event type can never make a
  line fail to parse. Every line still reaches the browser before dispatch.
- A line whose type this build acts on but whose body it cannot read is now
  logged on the colony instead of silently doing nothing.
- `modules/agents/claude-code/test/fixtures/events.jsonl` is the runner's
  real output, and both sides test against it: the runner's own suite
  validates it against the schema, and the harness deserialises every line
  into the variant the dispatch expects. Drift fails a test on both sides.
ParallelEntrepreneur added a commit that referenced this pull request Sep 23, 2026
Split sessions.rs, and make the agent event contract a type
ParallelEntrepreneur added a commit that referenced this pull request Sep 23, 2026
Rebuilt on main rather than merged: this branch predated the tree-wide
format (#142) and the sessions.rs split (#132), so its diff collided with
both — the agent-link code it edits now lives in events.rs, and every hunk
fought the reflow. The feature is unchanged; only where it attaches moved.

- `autonomy.rs` is taken as it was written.
- The open question carries its questions now (`Runtime::open_question`
  returns `(id, questions)`), so the judge can answer among the labels the
  agent offered — and the dispatch that fills it is the typed
  `AgentEvent::Question` arm in events.rs, which already had them.
- `Activity::judged`, the `autonomy` module kind and its settings, the
  config entry, the docs and the web bits are as the branch had them.

Verified on this tree: cargo test --workspace 246 passed (240 + this
branch's 6), clippy -D warnings clean, cargo fmt --all --check clean, and
the web suite 67 passed with tsc clean.
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