Skip to content

feat(orchestration-v2): add subagent observability data model (1/5) - #4779

Draft
shivamhwp wants to merge 3 commits into
t3code/codex-turn-mappingfrom
subagent-obs/01-contracts
Draft

feat(orchestration-v2): add subagent observability data model (1/5)#4779
shivamhwp wants to merge 3 commits into
t3code/codex-turn-mappingfrom
subagent-obs/01-contracts

Conversation

@shivamhwp

@shivamhwp shivamhwp commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

First of five stacked PRs replacing #4551, which bundled the whole subagent-observability feature into one ~3,100-line change. Splitting it out so each piece can actually be reviewed; the seams between these pieces are where the bugs were hiding.

Scope: data model, storage, and migration only. Nothing produces the new data yet. Adapters are given neutral defaults so every subagent row stays valid. Populating them for real is 2/5, and reuse semantics are 3/5.

What's here

Contracts gain a reusable subagent identity — per-activation records (SubagentActivationId, OrchestrationV2SubagentActivation), cumulative usage, a role, recent activity, and workflow grouping. A subagent can also now rest at idle between activations.

Migration 044 creates the activation projection table. Legacy rows stay readable through schema defaults and projection replay, so no JSON backfill is required.

The status mapping is the important part

A subagent status is not a turn item status — idle has no timeline equivalent. The two are bridged by an explicit Record<subagent status, turn item status> rather than a copy, which makes the mapping total: adding a subagent status without giving it a timeline meaning is a compile error.

This is not hypothetical. An earlier iteration copied the raw status through, which wrote a turn-item.updated event that its own schema could not decode. Nothing failed at write time — but the projection rebuild runs at startup and died on that stored event, so the server became permanently unbootable with no in-app recovery. Adding idle in this PR immediately trips the new compile error at CodexAdapterV2.ts, which is the guard doing its job.

Notes for review

  • Envelope attribution change: ProviderEventIngestor no longer replaces an explicit null runId/nodeId with the ambient run. Activations legitimately have no run id. The envelope is persistence metadata only (routing reads payload fields), but this does change stored metadata for existing event types that already pass explicit nulls.
  • Migration behavior: migration 044 adds activation storage only. Legacy subagent JSON is decoded through schema defaults and projections are rebuilt from events, avoiding a one-off JSON backfill.
  • A database bricked by the earlier broken build is not repaired by this PR — turn items still correctly reject idle. Only relevant if that build ran anywhere beyond local dev.

Testing

Typecheck clean and lint clean across server, contracts, client-runtime, and web.

  • server 1,676 passing
  • contracts 223 passing
  • client-runtime 481 passing
  • web 1,498 passing

Also booted a server on a fresh state directory and confirmed migration 044 applies and the app starts.

Reviewed by an independent agent pass before opening; its findings on helper scope and the missing mixed-row migration test are folded in.


Note

Medium Risk
Touches event-sourced projections, a new activation-storage migration, and envelope attribution semantics; mistakes could corrupt stored events or startup rebuilds, though tests target the risky migration and status-mapping paths.

Overview
Introduces the orchestration v2 subagent observability contracts and persistence layer ahead of adapter/UI work. Subagents gain reusable identity fields (kind, role, usage, activationCount, workflow, recentActivity, etc.), a new resting idle status, and separate OrchestrationV2SubagentActivation records with a subagent-activation.updated domain event and SQLite projection table (schema version 3).

Migration 044 creates activation storage; schema defaults and projection replay keep legacy rows readable without a JSON backfill, and tests cover projection rebuild cleanup of stale activation rows.

Provider adapters and the orchestrator only wire neutral defaults and minimal lifecycle hooks (e.g. delegated-task spawn/terminal activation events); real population is deferred. Codex now maps subagent status to turn-item status via orchestrationV2SubagentStatusAsTurnItemStatus so idle cannot be written raw onto timeline rows.

ProviderEventIngestor stops substituting ambient runId/nodeId when the payload explicitly passes null, so detached activations and messages keep correct envelope metadata.

Reviewed by Cursor Bugbot for commit 877ef38. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add subagent activation observability data model to orchestration-v2

  • Introduces OrchestrationV2SubagentActivation as a new entity tracking per-activation lifecycle, usage, and timing separately from subagent identity, with a new SubagentActivationId branded type.
  • Extends OrchestrationV2Subagent with observability fields: kind, role, usage, currentActivationId, activationCount, workflow, workflowMembership, and recentActivity (all with safe defaults for legacy rows).
  • Adds database migration 044 (044_OrchestrationV2SubagentObservability.ts) creating the orchestration_v2_projection_subagent_activations table with indexes; existing subagent rows are not backfilled.
  • Emits subagent-activation.updated domain events on subagent dispatch start and terminal completion in the orchestrator and all provider adapters (ACP, Claude, Codex, Cursor, OpenCode).
  • Projection stores (server and client-runtime) now upsert activation records on subagent-activation.updated events; projection rebuild purges stale activation rows before replay.
  • Behavioral Change: projection schema version bumped from 2 to 3; ProviderEventIngestor no longer overwrites explicit null runId/nodeId with ambient values.

Macroscope summarized 313ae4d. (Automatic summaries will resume when PR exits draft mode or review begins).

shivamhwp and others added 2 commits July 28, 2026 20:53
Introduces the schema, storage, and migration for richer subagent
tracking. Nothing produces the new data yet — adapters populate neutral
defaults so every subagent row remains valid — which keeps this change
reviewable as a pure data-model step.

Contracts gain a reusable subagent identity: per-activation records
(SubagentActivationId, OrchestrationV2SubagentActivation), cumulative
usage, a role, recent activity, and workflow grouping. A subagent may
now also rest at "idle" between activations.

Because a subagent status is not a turn item status, the two are bridged
by an explicit mapping rather than a copy. The Record type makes that
total: adding a subagent status without giving it a timeline meaning is
a compile error. Without it, a producer can emit a turn item whose own
schema cannot decode it, which is unrecoverable — the projection rebuild
then fails on every subsequent startup and the server cannot boot.

Migration 043 backfills existing rows so old events stay readable. Its
riskiest path is a partially-migrated row, where already-present JSON is
round-tripped through COALESCE; a test pins that structured values keep
their JSON subtype rather than being rewritten as quoted strings.

Also narrows event-envelope attribution in ProviderEventIngestor: a
payload carrying an explicit null run or node id now keeps that null
instead of inheriting the ambient run. Activations legitimately have no
run id, and the envelope is persistence metadata only — routing reads
payload fields — but this does change stored metadata for existing
event types that already pass explicit nulls.
… backfill

Every observability field the migration backfilled carries a decoding
default equal to the value it wrote, so pre-upgrade rows already read
back correctly without it. The projection schema version bump also
fails startup verification, which deletes and replays those rows from
the event log before anything reads them.

Pin both halves: a contracts test decodes a stored payload missing all
eight fields through the schema the projection actually uses, and the
migration test asserts an existing row survives byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0e7a5067-d3e9-4b1e-a035-bd5351b46527

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch subagent-obs/01-contracts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Jul 28, 2026
…cope

The lint rule the CI Check job enforces flags a compiled decoder rebuilt
on every call. The activation payload decoder and the subagent decoders
this PR added were inline, which took the branch from the base's 67 lint
warnings to 73 and failed the job.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). 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