Skip to content

feat: reactor acts as a service account (owned, reviewable autonomous proposals) - #86

Merged
mroops0111 merged 4 commits into
masterfrom
fix/reactor-system-owner
Aug 5, 2026
Merged

feat: reactor acts as a service account (owned, reviewable autonomous proposals)#86
mroops0111 merged 4 commits into
masterfrom
fix/reactor-system-owner

Conversation

@mroops0111

Copy link
Copy Markdown
Owner

Summary

An autonomous reactor run has no human caller, so the AI agent's API calls carried no identity: under local trust they fell back to local-user, under authentication they were rejected (401). Neither produced a reviewable, correctly-attributed proposal owner.

This gives the reactor a real, non-human identity so its proposals are owned and reviewable, in both auth modes, without hardcoding a mode branch.

Design: service accounts

@braidhq/schema gains a small, extensible registry of built-in service accounts (reactor, plus a generic system). Adding a future autonomous component is one entry here, seeding, auth, workspace access, and pending-visibility all read the set via isServiceAccount, so none of them need to change.

  • Identity: the reactor runs as the reactor service account. It mints a short-lived session and threads it as the run's callerToken, which propagates through the existing subprocess plumbing (BRAID_TOKEN env, MCP gateway Bearer) to the agent's API calls. So its proposals are owned by reactor.
  • Auth: the middleware now honours a valid Bearer session even under local trust, so the reactor's token identifies it in both modes. Studio under local trust sends no Bearer, so nothing is shadowed.
  • Access: service accounts are seeded as admins, so they clear the workspace-access gate through the existing admin-to-owner rule, no service-account special-case in the middleware.
  • Visibility: pending proposals and clarifications owned by any service account are always visible to reviewers (isServiceAccount(owner)), generalising the prior owner === 'system' special-case. A sole workspace owner can now review autonomous proposals.

Why this over the alternatives

Re-owning proposals after the fact does not work in authenticated mode, the agent's submit is rejected before anything exists to re-own. A trusted internal credential is the only way the round-tripping agent can authenticate as a known non-human principal while an anonymous caller stays rejected.

Verification

  • pnpm typecheck green (15/15); pnpm lint clean.
  • New tests: service-account registry, reactor threads its caller token (and runs tokenless without it), auth middleware honours a Bearer session under local trust.
  • Chrome e2e against a server on this branch: with three pending proposals in one workspace (owned by reactor, local-user, and another user), a logged-in workspace owner sees only the reactor-owned one in the personal Pending view, the other-user and non-viewer ones stay hidden. Confirms service-account visibility without breaking the personal filter. reactor and system are seeded into the user registry on boot.

Supersedes the closed #85 (which removed a Studio toggle gate, the wrong layer). Relates to #83.

🤖 Generated with Claude Code

…owned and visible

An autonomous reactor run has no human caller, so its agent's API calls had
no identity: under local trust they fell back to local-user, under
authentication they were rejected (401). Neither yielded a reviewable owner.

Introduce built-in service accounts (reactor, system) in @braidhq/schema.
The reactor runs as the reactor service account by minting a short-lived
session and threading it as the run's caller token, so its proposals are
owned by 'reactor'. The auth middleware honours a valid Bearer session even
under local trust (Studio sends none, so nothing is shadowed), and the
accounts are seeded as admins so they clear the workspace-access gate via the
existing admin-to-owner rule. Pending visibility now treats any service
account owner as always-visible (isServiceAccount), generalising the prior
owner === 'system' special-case, so a sole workspace owner can review
autonomous proposals.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mroops0111 and others added 3 commits August 5, 2026 02:17
…s only

Autonomous (service-account) proposals and clarifications can only be applied
by an owner, so restrict their pending visibility to owners too: the list
routes pass includeServiceAccounts only when the viewer is an owner (or when
there is no viewer context, an open in-memory composition). Also prune
already-expired sessions on issue, so the reactor's per-cycle short-lived
tokens do not accumulate in the session file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nomy

Service-ness is now registry data (User.kind='service') snapshotted onto
each proposal and ticket as ownerKind at submit time, via the user
directory. The HITL filter checks ownerKind==='service' instead of the
hardcoded SERVICE_ACCOUNTS set, so a new autonomous component needs no
schema edit, it just seeds its own account with ensureServiceAccount.

Drops schema/identity.ts and the read-time service-account id plumbing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mroops0111
mroops0111 merged commit 457f0e3 into master Aug 5, 2026
6 checks passed
@mroops0111
mroops0111 deleted the fix/reactor-system-owner branch August 5, 2026 02:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant