Skip to content

feat(cli): add native supervisor and Herdr integration - #440

Open
shuv1337 wants to merge 10 commits into
integration-v2from
native-supervisor
Open

shuv1337 wants to merge 10 commits into
integration-v2from
native-supervisor

Conversation

@shuv1337

@shuv1337 shuv1337 commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Issue for this PR

No linked issue.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Adds an experimental shuvcode supervisor launcher: run it from any directory to open the same firstmate Session in ~/fleet-home. Firstmate registers, clones, and creates projects conversationally, then coordinates ship/scout work, decisions, and delivery through durable Shuvcode Sessions. A separate supervisor process owns backlog, authority, receipts and recovery; a bundled plugin supplies native lead/worker tools. The CLI and board expose the same fleet state. Herdr views are optional: the background supervisor discovers a running server, publishes firstmate and worker views without an attached client, and preserves work across terminal/SSH disconnects. Inside Herdr, the launcher focuses firstmate; outside, it opens a direct TUI. Existing ShuvBro-owned homes retain their separate adapter.

Supports isolated worktrees, local/PR/no-mistakes delivery policies, persistent SSH delegates, away mode, bounded private/shared knowledge and optional Relay/voice adapters. Managed restart waits for the tool bridge and applies durable cancellations before resuming claimed Sessions, including background descendants. Startup knowledge and budget warnings enter model system context without being copied into user messages; later messages, curation, and restart retain that separation.

Adds the Shuvcode side of native ShuvBro/Herdr integration: immutable orchestration profiles for new managed homes, a read-only presentation projection, and exact home/Session/location attachment. Opening or restoring a Herdr pane cannot initialize a home, admit work or fall back to a different Session. Projection includes descendant permission/form blocking, explicit unknown observations and settlement facts for guarded display cleanup. Bundled plugin inputs use portable Standard Schema validation so the compiled plugin's Effect runtime validates its own constraints. Status delivery refresh preserves the serialized observer’s event cursor, preventing an overlapping status read from failing reconciliation.

See the operator guide, workflow qualification, and three-repository contract and local evidence. Managed panes require companion Herdr #16. The explicit ShuvBro policy/placement adapter is in ShuvBro #62; the bare launcher supplies its own background publisher. Existing-home migration, live forge/Relay/speech integrations, two-host networking and production load remain outside the qualified local path.

How did you verify your code works?

  • Supervisor suite: 181 tests, 1,298 assertions across 30 files, including actual bundled-plugin validation.
  • Core Session/process-death recovery: 54 tests passed after merging current integration-v2, preserving prepared-subagent recovery and native cancellation.
  • bun run check: all 41 tasks passed; scoped formatting and whitespace checks passed.
  • Compiled Linux CLI: five real project/attachment/presentation tests with 57 assertions cover profile activation, exact identity rejection without admission, permissions, cancellation, descendant forms, adopted lead locations and manager/native restart.
  • Launcher checks: projectless startup from unrelated directories; conversational existing/clone/new project registration followed by a real native worker; incompatible Herdr identity/capabilities; uncertain creation across manager restart; lost launch acknowledgement without resubmission; explicit view reopening; stale-lead focus refusal.
  • Current exe.dev entry: ssh -t shuvcode-test.exe.xyz shuvcode supervisor, with ssh -t shuvcode-test.exe.xyz herdr for all worker views. A real model registered the project and completed a scout without Herdr. A later default headless Herdr server added both views. Native/Herdr restart retained exact identities with step counts 16→16. A second real worker stayed running through direct SSH TUI closure and two Herdr attach/detach cycles, then completed with its verified receipt. Three Bun tests passed independently. The requested reset then removed 21 old Sessions, test worktrees, four earlier homes, retired wrappers, the watcher and the private test SSH route. The real 2password checkout and registration remain; the fresh firstmate has no messages or tasks, and all runtimes are stopped.
  • Private three-repository lab: real Herdr PTYs, one lead and two workers; close a display while native work continues; preserve identities and model-call counts across runtime/manager/Herdr restart; clean up an exact pane while preserving its neighbor, renamed workspace and parent focus; reopen the same Session; complete a fresh worker with a Git commit and verified artifact receipts.
  • Startup-knowledge regression: the second prompt reproduced two copies before the fix. Source and compiled integration checks now verify system-only knowledge, byte-identical user messages, stable Session identity across restart, knowledge edits, and budget warnings. The compiled check passed 28 assertions. The installed VM build completed two real-model requests across restart with unchanged user text and knowledge-derived replies; its temporary verification home was removed.
  • Initial ShuvBro adapter at 089d2b7: cleanup and reopen while continuous watch stays active; exact native Session retained, new binding reports advance, both generations' neighboring panes and parent focus survive, and model requests remain 4→4.
  • Final Herdr 63835aa2: real client Done survives acknowledgement and Herdr restart with unchanged identities and model requests. Closed highest workspace identity stays reserved across restart (w5:p1 closes; the next created pane is w6:p1). All seven Herdr checks pass, including native Windows and ConPTY.
  • ShuvBro follow-up 76d68406 passes real missing/mismatched peer refusal, exact foreground-argv readback, and pane-absence confirmation while watch stays active. Candidate ca4dc617 also passes a real delayed-launch check: the first up returns success in launch_pending with sequence zero; watch marks it ready only after one exact foreground attach appears. Repeated cleanup/reopen preserves the Session, neighboring panes, focus and settlement with model requests unchanged at 4. All 20 executable CLI tests pass. The required Test evidence-review gate remains pending before this candidate becomes the published ShuvBro PR head.
  • Historical exe.dev Ubuntu 24.04 adapter evaluation: a real eval/gpt-6-sol worker committed its implementation and result, passed three independently rerun tests, and was locally landed at 99218396. Herdr restart preserved exact home/Session/pane/binding identities; the lead was idle and worker Done. These old trial homes were retired during the requested reset.
  • Additional core recovery, installed-package and real-model evaluation evidence is recorded in the qualification document.

Screenshots / recordings

Board after the isolated evaluation's local delivery:

Native supervisor board

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

shuv1337 commented Oct 6, 2026

Copy link
Copy Markdown
Owner Author

🔍 Automated Cursor review is underway. Please hold off on merging until the review comment lands here (usually 10–20 minutes).

@cursor

cursor Bot commented Oct 6, 2026

Copy link
Copy Markdown

Review complete: I did not re-run the supervisor suite, the core Session execution tests, or bun run check. This is a static review of 5eb9abbf.

Existing non-supervisor behavior looks preserved. resumeSuspendedSessions is resumeSuspendedSessionsWith({ interrupted: [] }), and that empty set skips the new cancellation preamble. The normal service lifecycle still does not pass beforeRecovery. I did not find a behavior change for ordinary Session restart, the managed service, or shuvcode serve.

Medium

Managed native recovery fails open if the owner line never arrives

packages/cli/src/commands/handlers/supervisor/native.ts (about 12–27) treats stdin end as Effect.interrupt. packages/server/src/process.ts (about 107–114 and 240–248) forks that effect and then sets status.ready without waiting for it. packages/cli/src/supervisor/api.ts (about 99–113) starts the 1s reconcile timer inside serve(), and packages/cli/src/supervisor/managed.ts (about 198–210) writes interruptedSessionIDs only after serve() returns.

If the daemon is killed after native is listening and before that line is read, stdin closes, the recovery fiber is interrupted, and the HTTP server stays ready. Claimed Sessions are neither user-interrupted nor resumed. The next supervisor start can still terminate the recorded pid, but until then the pilot looks up while restart continuity has not run. A decode failure is Effect.orDie on that same forked fiber, with the same stuck-ready result. The cancellation integration test still passes if observe() settles cancelling tasks and nothing resumes them; it does not show that the bridge line was applied.

Direction: fail closed. Do not report ready until the line has been decoded and the interrupt pass has started, and treat stdin EOF or a malformed line as a fatal boot error so the daemon replaces the process. Keep reconcile from running until that pass has finished, instead of relying on the 1s timer.

Received handoffs install the source policy on the destination

packages/cli/src/supervisor/delegates-runtime.ts receiveHandoff (about 415–433) enqueues source deliveryMode, mergePolicy, and overrides.permissions directly. That path does not go through requireInheritedPermissions or the lead/operator delivery-mode checks in packages/cli/src/supervisor/runtime.ts (about 836–856). Dispatch then prefers those overrides over the destination project (runtime.ts about 371–374), and prepare/land uses the copied mode and merge policy (packages/cli/src/supervisor/delivery-runtime.ts about 114–117). assess skips the operator approval gate when mergePolicy is auto.

A source home whose project is yolo / --auto can therefore hand off direct-PR work with automatic merge and allow-all tool rules into a destination project that is manual and ask-only. The destination operator registered the route, but did not accept that policy.

Direction: on receive, intersect permissions with the destination project and keep the destination's delivery mode and merge policy unless a destination operator record accepts the source policy before dispatch.

SSH delegation does not pin host keys and does not bound output

packages/cli/src/supervisor/delegates.ts remoteCommand / run (about 632–671) invokes ssh with only BatchMode=yes and ConnectTimeout=5. Host-key behavior is whatever is in the operator's ~/.ssh/config. The remote command is supervisor bridge, and packages/cli/src/commands/handlers/supervisor/actions.ts (about 345–354) runs the stdin JSON operation as the operator. A config that sets StrictHostKeyChecking no (or UserKnownHostsFile /dev/null) lets a network MITM present another host, read handoff payloads, and return forged status/results.

run also reads stdout and stderr to completion with no byte cap, then JSON.parses stdout. A hostile or buggy remote bridge can grow memory until the source process dies. On timeout, child.kill() is SIGTERM and the losing Promise.all keeps reading until ssh actually exits, so repeated timeouts can accumulate children and fds.

Direction: set an explicit StrictHostKeyChecking and a delegate known-hosts file on the argv. Cap stdout and stderr and fail closed with unknown. After timeout, SIGKILL the child and drop the pipes. Restrict bridge to delegate.receive, delegate.status, and delegate.cancel so that channel is not a general operator RPC.

Lead replies can attach any image the OS user can read

packages/cli/src/supervisor/channels-runtime.ts (about 159–161) lets the active lead call reply.send with imagePath. readImage in packages/cli/src/supervisor/channels.ts (about 1550–1588) opens that path with O_NOFOLLOW and checks size and image magic bytes, but it does not confine the path to the supervisor home or a worktree. The read happens in the supervisor process, so session tool permissions never see it. The bytes are stored on the reply and can be posted by an operator flush or by automatic replies. Public Relay text is untrusted input to the same lead.

Direction: resolve the file under an explicit media directory (or only accept inline base64) and reject paths that realpath outside that root.

Fenced recovery does not reset pre-migration attempt rows

packages/cli/src/supervisor/store.ts (about 208–226) adds attempt_epoch and uncertainty_epoch as nullable columns for databases created before managed recovery. recoverFencedEpoch (about 349–361) only updates rows where attempt_epoch < ? and uncertainty_epoch < ?. In SQL, NULL < epoch is not true, so an in-flight attempted = 1 row with a null epoch is not reset, and the uncertain task is not cleared. Those tasks stay admissionUncertain across the fence until the database is repaired by hand. Fresh rows written by beginDispatch in managed mode do set the epoch; this is the upgrade path the ALTER is there for.

Direction: treat null as the previous epoch, for example (attempt_epoch IS NULL OR attempt_epoch < ?), and the same for uncertainty_epoch, or backfill both columns in the migration before fencing.

Low

Project and task knowledge can grow without a count cap

putKnowledge in packages/cli/src/supervisor/channels.ts (about 1072–1074) limits each record to 256 KiB. Startup scopes have a token budget; project and task notes do not. A lead can keep inserting new ids and grow supervisor.sqlite without bound.

Direction: cap record count or total bytes for those scopes the same way startup knowledge is budgeted.

Not filed

Worktree creation, receipt realpath checks, and discard-before-remove look fail-closed for the cases the tests cover. Plugin tools stamp tool.sessionID rather than a model-supplied id. Both API tokens live in the same 0600 supervisor.json, so a same-user reader already has the operator token; I did not treat client-supplied sessionID as a separate escalation. no-mistakes-prod-only resolving classification: "internal" to direct-PR matches the documented lead policy.

@shuv1337 shuv1337 changed the title feat(cli): add native supervisor workflows feat(cli): add native supervisor and Herdr integration Oct 6, 2026
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