Skip to content

Agent-runtime audit fixes (Phase 1 complete; 2 & 3 in progress)#16

Merged
Codename-11 merged 21 commits into
mainfrom
worktree-audit-fixes
Jul 7, 2026
Merged

Agent-runtime audit fixes (Phase 1 complete; 2 & 3 in progress)#16
Codename-11 merged 21 commits into
mainfrom
worktree-audit-fixes

Conversation

@Codename-11

Copy link
Copy Markdown
Owner

Implements the roadmap from the agent-runtime audit (73 findings; report artifact). Landing in phases on this branch; Phase 1 is complete, Phase 2 + 3 in progress.

Phase 1 — safety / lifecycle / false-terminal / dead-ends (done)

Orchestration loop

  • Readiness cascade now gates on plan.status===RUNNING — a BLOCKED (budget) or CANCELED (abandon/replan) plan stops dispatching finishing steps into fresh spend.
  • recordVerdict rejects a verdict on a settled step (DONE/BLOCKED/CANCELED) → a stale/duplicate reviewer webhook can't reopen completed work.
  • ExecutionPlan.startedAt (migration 0091) → wall-time budget measured from execution start, not decompose.

Runtime backend

  • getStatus 'unknown' is now non-terminal (stale watchdog arbitrates) → fixes the Codex worker-restart / deploy false-STALL; unrecognized Hermes statuses map to running.
  • Always keep-alive a live running run so a quiet-but-alive run isn't watchdog-STALLED.
  • Hermes approve/stop inspect res.ok + throw; the approve/reject mutation surfaces failure instead of a false success.
  • runtime.register enforces the TLS transport guard like create/update.
  • AgentRun indexes for the cross-tenant 5s/60s sweeps.

CLI daemon

  • O_EXCL pid lock closes the double-daemon TOCTOU.
  • Heartbeat keeps last-known agent linkage on a transient agents.me blip (was: dropped all dispatch ~60s).

UI

  • Goals/Plans list+detail distinguish a load error ("couldn't load … Retry") from an empty state.
  • Fresh LOCAL_DAEMON runtime settings reachable from the global Runtimes page (no more dead-end).

Verification: pnpm typecheck clean · pnpm lint 0 errors · orchestration suite 24 passed (4 new regression tests) · pnpm build:cli clean.

Phase 2 (in progress)

Budget integrity (poll-cost → plan budget), abandon/replan run teardown, dep-cycle detection + stuck-plan watchdog, maxConcurrent on the RUNS path, the two security holes (admin→owner self-promotion; ADMIN_EMAIL bootstrap), Codex restart reconciliation, daemon reconcile-on-reconnect + fatal-auth.

Phase 3 (in progress)

Cross-workspace move (instance-admin, dry-run remap), native-<select> migration + token colors, Hermes usage frames / ACP / multi-agent-per-runtime, view-hierarchy legibility, housekeeping.

🤖 Generated with Claude Code

Codename-11 and others added 21 commits July 6, 2026 18:28
…-time from startedAt

Phase-1 safety fixes for the goal/execution-plan loop (from the agent-runtime audit):

- cascadeReadiness / transitionStepToReady now early-return unless the plan is
  RUNNING. A BLOCKED (budget/wall-time) or CANCELED (abandon/replan) plan no
  longer cascades finishing steps into fresh dispatches — closing the hole where
  a blocked plan kept spending the exact budget the operator was asked to approve.
- recordVerdict rejects a verdict on a settled step (DONE/BLOCKED/CANCELED) with
  CONFLICT. A stale/duplicate reviewer webhook can no longer move a DONE step back
  to TODO and re-dispatch, un-completing finished work.
- ExecutionPlan.startedAt (migration 0091) stamped in activatePlan; wall-time
  budget measured from it, not createdAt, so planning + approval-wait isn't
  charged against the execution clock.
- Add @@index([status,lastEventAt]) + @@index([assignmentEventId]) on AgentRun so
  the cross-tenant 5s/60s worker sweeps stop sequential-scanning.

4 new regression tests; all 24 orchestration tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pagate approve/stop failures

Phase-1 runtime-backend safety fixes (agent-runtime audit):

- pollActiveRuns treats getStatus 'unknown' as NON-terminal (leaves the run
  ACTIVE, lets the stale watchdog arbitrate) instead of false-STALLing. This is
  the worker-restart / deploy false-STALL for Codex, and any momentary blip.
- hermes-runs mapStatus defaults an unrecognized non-empty lifecycle word
  ('initializing', 'preparing', …) to 'running', not 'unknown'.
- On a live 'running' poll always bump lastEventAt, even when the step label is
  unchanged, so a quiet-but-alive run (mid long tool call) isn't watchdog-STALLED.
- hermes-runs approve/stop now inspect res.ok and throw on failure; the agent-run
  approve/reject mutation surfaces it as an error instead of clearing the block
  and returning ok — no more false 'approved' while the provider run stays blocked.
- runtime.register now calls assertEndpointTransport like create/update, so a
  plaintext public endpoint can't slip in via the register path.

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

Phase-1 daemon reliability fixes (agent-runtime audit):

- acquirePidLock() writes the daemon pid via an atomic O_CREAT|O_EXCL open BEFORE
  the SSE stream opens, closing the TOCTOU where two racing 'forge daemon start'
  invocations both passed the liveness check and both dispatched every event. A
  loser that finds a live holder refuses; a stale holder is reclaimed.
- refreshLinkedAgent distinguishes a transient failure (undefined) from a genuine
  unlink (null); the heartbeat loop only overwrites linkedAgent on a definitive
  answer, so one soft agents.me blip no longer nulls the linkage and silently
  drops all chat + assignment dispatch for up to 60s.

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

Phase-1 runtime/orchestration UX fixes (agent-runtime audit):

- Goals list/detail and Plans list/detail now render a themed 'couldn't load …
  Retry' state on a fetch error, instead of masking it as 'No goals yet' / 'Goal
  not found — may have been abandoned', which read as though work was deleted.
- global.runtimes returns each runtime's home workspace; the global Runtimes
  settings page routes the settings gear via workspacesInUse[0] ?? homeWorkspace,
  so a freshly registered LOCAL_DAEMON (no workspacesInUse yet) is no longer a
  read-only dead-end you can't self-test or add secrets to.

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

Phase-2 lifecycle + budget integrity (agent-runtime audit):

- reapPlanRuns(): abandonGoal + re-decompose/re-generate now cancel non-terminal
  steps and stop/abandon their in-flight AgentRuns (best-effort connector.stop),
  so an abandoned goal or a superseded plan stops spending immediately instead of
  burning tokens until the stale watchdog reaps it. A superseded prior RUNNING
  plan is CANCELED, not just demoted, so two plans can't drive one goal.
- assertNoStepCycles(): plan creation (insertStepsTx + createExecutionPlan) rejects
  a dependency cycle with BAD_REQUEST via Kahn's algorithm — a mutually-dependent
  step pair no longer leaves the plan RUNNING forever with nothing ready.
- sweepOrchestrationBudget(): new 60s worker watchdog runs checkAndBlockBudget on
  every RUNNING plan with a wall-time cap (enforcing time independent of cost
  reporting — was only ever checked on a cost record) and logs wedged plans.

Tests: cycle rejection + abandon-cancels-steps; 26 orchestration tests green.

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

Phase-2 dispatch fixes (agent-runtime audit):

- Poll-mirrored RUNS cost now flows into applyRunCostToPlan (delta vs the run's
  last-known cost, persisted each tick), so a Hermes/Codex RUNS agent that reports
  cost only via poll actually trips the plan's maxTotalCostUsd instead of burning
  past it.
- Enforce the agent's maxConcurrent on the RUNS dispatch path (startNewRuns), not
  just in auto-dispatch selection — a maxConcurrent=1 agent can no longer hold N
  concurrent provider runs from manual/mention/watcher/rule assignments.
- A disabled runtime's in-flight runs are left alone (disable blocks NEW dispatch
  only) instead of being false-STALLed by the sentinel connector.
- An unresolvable connector annotates the run ("runtime unresolvable — check
  config") instead of silently spinning, and doesn't bump lastEventAt so the
  watchdog can still reap it.

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

Phase-2 security fixes (agent-runtime audit):

- setMemberRole: only an OWNER may grant the OWNER role or change an existing
  owner's role. Closes the privilege-escalation where any workspace ADMIN could
  set its own row to OWNER (the server accepted any Role) and then delete the
  founder's tenant, making the OWNER-only archive/delete gate meaningless.
- ADMIN_EMAIL env bootstrap (instanceAdminProcedure + requireInstanceAdmin) is now
  honored ONLY while no INSTANCE_ADMIN exists yet. auth.ts no longer re-stamps
  INSTANCE_ADMIN on every credentials login (only bootstraps when none exists).
  Together: a demoted operator stays demoted, and a shared / recycled / SSO-
  asserted ADMIN_EMAIL can't silently regain full instance control.

Test: non-owner admin can't self-promote to OWNER; owner can. 18 members tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase-2 daemon reliability (agent-runtime audit):

- On every SSE (re)connect the daemon now pulls its agent's UNACKED inbox
  (agent.inbox.list) and drives each pending assignment through the same handler
  as a live event, deduped against seenEvents by the triggering event id. This
  recovers AGENT_ASSIGNED events dropped while the daemon was stopped or mid-
  reconnect — the live-only SSE has no replay, so they were previously lost.
- Fatal-auth backstop: consecutive 401/403 SSE errors past a threshold log
  'run forge login' and exit non-zero, so a revoked/expired token surfaces to a
  supervisor instead of looping forever while 'daemon status' reads healthy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase-2 budget integrity (agent-runtime audit): recordUsage read the run's prior
cost outside any lock, so two concurrent calls (or a call racing the poll sweep)
could both compute a delta from the same base and double-count against the plan
budget. The prev-read + update now run in one tx behind a row lock on the
AgentRun, so the prev→new delta applied to the plan/goal totals is correct.

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

Phase-2 (agent-runtime audit): runtimes.register always CREATEd, so a daemon that
lost its cached runtime id (fresh clone / config wipe) stacked a duplicate row per
re-register. It now reuses a non-archived match on (workspaceId, name, kind),
refreshing endpoint/providers/heartbeat, and only creates when none exists.

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

Implements the operator's explicit ask #2: move issues to another workspace as an
instance admin. Issue is the hub of ~14 workspace-scoped child tables with
per-workspace unique constraints, so a naive workspaceId re-stamp corrupts data —
this does it safely:

- planIssueMove() (read-only dry-run): renumber map (AXI-42 → WRK-108), status
  remap by category, label remap by name (drops unmatched), FKs nulled across the
  tenant boundary (project/cycle/agents), parent kept only if also moving, dropped
  cross-workspace relations, attachment-quota delta, and per-issue BLOCK reasons.
- executeIssueMove(): runs the plan in one transaction — re-tenants the issue +
  its workspaceId-bearing content children (comments/attachments/time/watchers/
  external links), renumbers, remaps status + labels, drops cross-ws relations, and
  writes MOVE audit events in BOTH workspaces.
- Safety: REFUSES to move an entangled issue (agent runs, orchestration links,
  action requests, artifacts) rather than risk a cross-tenant reference; the
  preview reports which and why. instanceAdmin-gated (references two tenants).
- MCP-exposed as instanceAdmin.previewIssueMove / moveIssues.

Tests: preview+block, execute (re-tenant/renumber/remap/audit), refuse-all-blocked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds /admin/move-issues (instance-admin): pick source + target workspace + paste
issue ids, Preview shows the full remap table (new keys, status/label remap,
blocked-issue reasons, quota warning), then a type-confirmed Move executes it.
Uses themed primitives (Combobox, useConfirm, admin panels) — no native controls.
New 'Move issues' nav entry in the admin shell.

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

Phase-3 governance + housekeeping (agent-runtime audit):

- Governance: MAX_WORKSPACES_PER_USER env caps self-service tenant creation for
  non-instance-admins (default 0 = unlimited; no behavior change until set).
- Docs: rewrote CLAUDE.md's stale CLI 'v1 gaps' (all closed — chat.getThread,
  full issue-loop, runtimes.list/agents.list shipped; reconcile + fatal-auth added;
  real remaining gaps noted). Corrected run-dispatcher's 'restart-safe' claim to
  Hermes-only (Codex 'unknown'-after-restart now non-terminal).
- Copy: stripped internal 'Stream D' jargon from the runtime detail settings page.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase-3 slices (agent-runtime audit):

- P3.3: cached-token usage now flows end-to-end for RUNS agents — RunStatus.usage
  + the 'usage' RunEvent carry tokensCached, the Hermes connector extracts it from
  the provider response (cached_tokens / cache_read_input_tokens), and the poll
  loop's terminal write persists it onto AgentRun.tokensCached (was always null).
- P3.2: the workspace members role picker is now the themed Combobox instead of a
  native <select> (pairs with the P2.5 owner-only guard, which toasts a clear error
  if a non-owner tries to grant OWNER).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Finish the P3.2 token sweep across the orchestration + members surfaces —
replace ad-hoc emerald-*/amber-* tailwind with the semantic --success /
--warning tokens (which already flip light/dark, so the redundant dark:
variants collapse). No visual change intended; brings these files in line
with the run-row/step-node retry chips that already use the tokens.

- plans + goals pages (in-flight): judge verdicts, progress bar, plan
  status dots → bg-success / bg-warning
- orchestration-ui/status.ts: goal PLANNING/ACHIEVED tones
- orchestration/crew-roster-panel.tsx: REVIEWER/OPERATOR_PROXY role tones
- orchestration/step-node.tsx: awaiting-approval ring + approval chip
- settings/runtimes: serves-chat badge, disabled badge, tier-1 explainer

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
P3.2 observability items — make the orchestration surfaces update in
place and let the operator act without a detour to Command Center.

- Plan detail: wire the P1.7 `startedAt` into the budget meter's
  wall-time bar (elapsed / cap), mirroring the goal cockpit. `hasBudget`
  now also trips on a wall-time cap so a time-only budget still renders.
- Plans index: subscribe to realtime (execution-plan/step/agent-run) so
  background dispatch progress no longer looks frozen until manual
  Refresh. Mirrors the goals index.
- Review inbox: realtime on review-gate events (a crew member requesting
  approval, or another operator resolving) + tokenize the status tones
  (amber/emerald/destructive → warning/success/danger — REJECTED now
  actually reads red instead of the undefined `destructive` class).
- RunRow: replace the "open Command Center to approve" dead-end notice
  with the shared RunApprovalCard (Approve session/once + Reject inline),
  which its own doc comment already claimed it hosted.

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

P3.2 native-select sweep for the orchestration + runtime-settings
surfaces — replace every raw <select> in these files with the themed
Combobox primitive (portaled popover, keyboard nav, no native control):

- settings/runtimes: Adapter, Codex Sandbox mode, Approval policy
- goals/[goalId]: crew picker (allowNone → "No crew")
- plans/[planId]: plan-status + both step-status transition pills

These three files are now 100% native-select-free (the app-wide backlog
guard stays a warn; other surfaces untouched).

e2e: runtime-management.spec.ts drove the sandbox-mode + adapter selects
via Playwright selectOption, which only works on native <select>. Rewire
both to the Combobox contract — click the role="combobox" trigger by its
aria-label, then click the role="option" row; assert the selected label
via toContainText instead of toHaveValue.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
P3.2 modal item — replace the three hand-rolled orchestration modals with
the shared QuickForm primitive: one graphite scrim + backdrop-blur (was a
mix of bg-black/40 and bg-foreground/20), consistent header/footer, Enter
-to-submit + Esc-to-close, draft-safe fields, and an inline error banner.

- plans: New-plan modal (title + template) — onSubmit reuses submit();
  empty title now banners instead of a disabled button.
- goals: New-goal modal (objective + planner context + CrewSelector).
- goals/[goalId]: GoalEditModal (title/description/crew/cost+wall caps) —
  onSave is now awaited so a failing update keeps the modal open with the
  server message in-banner rather than a fire-and-forget toast.

The goal-router shim (use-goal-trpc) exposes `mutateAsync` too — it already
wraps the real tRPC mutation, so this only surfaces what exists at runtime,
letting the create/edit forms await the call.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Codename-11 Codename-11 merged commit 11e6e07 into main Jul 7, 2026
3 checks passed
@Codename-11 Codename-11 deleted the worktree-audit-fixes branch July 7, 2026 20:35
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