Skip to content

Add Heimdall watchers: PR sitter and objective runner - #97

Merged
zpyoung merged 114 commits into
mainfrom
zpyoung/heimdall-jev
Sep 28, 2026
Merged

zpyoung merged 114 commits into
mainfrom
zpyoung/heimdall-jev

Conversation

@zpyoung

@zpyoung zpyoung commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner
Files Added Deleted Net
Test 220 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​51712 $\color{#cf222e}{\Huge{\mathbf{−}}}$​31 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​51681
Prod 481 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​69274 $\color{#cf222e}{\Huge{\mathbf{−}}}$​2620 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​66654

ELI5

Heimdall is a supervisor that lives inside Orca and keeps working on something after you look away. You give it a job — "babysit this pull request until it's mergeable" or "get this goal done to this quality bar" — and it wakes up on its own schedule, checks what changed, decides the next step, and sends coding agents to do the work. Plain code makes every safety decision; the AI agents only do the parts that need judgment.

What Changed

Before: Orca could run agents, but a person had to keep checking on them, decide what came next, and restart work when something failed or a review came back.

After: a new Heimdall page in the sidebar lists every watcher, what it is doing, and what it is waiting on. There are two kinds of watcher:

  • PR Sitter (hosted-review) — armed from a worktree's Checks panel. It watches a GitHub or GitLab pull request, fixes failing checks and review comments, resolves merge conflicts, and stops to ask when a decision needs a human.
  • Objective — created from the Heimdall page. A planner agent breaks a goal into tasks and a reviewer checks the plan before anything runs. Workers then run in isolated git worktrees, in parallel where the plan allows. Each result is checked against the acceptance criteria before it is merged and landed.

Mechanism. One kernel in the main process owns everything that has to be right:

  • Scheduling and leases: when each watcher wakes, and which Orca instance currently owns it.
  • Budgets and stop rules: how much time and how many turns each watcher may spend.
  • Capability gates: every action is checked against what the watcher is allowed to do before it runs.
  • Ledger: an append-only record of every event, kept in its own SQLite database.

Agents are dispatched through Orca's existing orchestration and terminal machinery. They report back through files that the kernel reads defensively and validates before trusting.

Remote hosts are supported: work runs where the workspace lives, git commands on SSH hosts go through a strict relay allowlist, and losing contact is never treated as "the process died". The remote wire protocol changes are additive and capability-negotiated, so older clients keep working.

All code sits in fork-heimdall/, fork-heimdall-objective/ and fork-hosted-review-sitter/ directories and is declared in config/fork-ownership.json. Upstream files carry only one-line registration seams. The operating manual is docs/reference/heimdall-watcher.md.

The last two commits remove duplication found by an audit of the branch. That covers unused exports, store methods used only by tests, and swaps to upstream helpers (mapWithConcurrency, getErrorCode, settleWithinMs, isFullGitObjectId). It also adds one shared SQLite lifecycle class and one transaction helper, a single hardened report-file reader, and shared test fixtures. They cut about 1,000 lines with no intended behavior change. The one deliberate fix: landing-git-state.ts used to accept 41–63-character strings as git object IDs.

Why

Long-running agent work fails in the gaps between runs: nobody notices a red check, a review sits unanswered, a half-finished task is never retried. The alternative was to let an agent loop on its own. We rejected it because an agent that decides its own next step can also decide to skip a safety check.

Splitting the work keeps safety auditable and testable without a model in the loop. Deterministic code owns scheduling, gating and stopping, and agents only supply judgment. Keeping everything in fork directories with declared seams lets upstream syncs merge cleanly.

Linked Issue

N/A — issues are disabled on this repository.

Visual Proof

Not attached yet. This adds UI: the Heimdall sidebar page, the objective enrollment sheet and detail view, and the PR Sitter panel in the Checks sidebar. Screenshots need to be added before merge.

Testing

  • pnpm typecheck:node, typecheck:web and typecheck:cli pass.

  • All fork test suites pass in the remote sandbox: 407 files and 3,964 tests across main, shared, relay, renderer, CLI and the RPC methods.

  • The full unit lane has failures outside this feature's code (terminal pane, browser, sidebar, timeouts). One known failure is from this branch: fork-localization-catalogs.test.ts still expects 16 English catalogs, but the branch registers 19. It is filed in the ledger and not fixed here.

  • Tested locally on macOS; objective runs are logged under docs/heimdall-runs/. Linux and Windows are not tested manually.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

AI Disclosure

Written with Claude Code (Claude models). The final cleanup pass used Claude Opus with Sonnet subagents. It included an audit, a verification pass that tried to disprove each finding, and a behavior review of the refactor.

Review

Known items to resolve before merge:

  • 1cc57e0efb adds temporary [DBG-*] console.warn logging to the terminal-wait probe (orca-runtime-start-tui-idle-visible-read-probe.ts, runtime-terminal-wait.ts). Remove it once the readiness investigation ends.
  • The branch history includes a wip: commit.
  • The fork-localization catalog-count test described under Testing.

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Ensure no issues in: Security, Cross-platoform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

- Add durable watcher orchestration, leases, budgets, ledgers, and RPC APIs
- Add hosted review monitoring with gated fixes, branch updates, and merges
- Integrate watcher status, enrollment, and emergency controls into the UI
- Support structured plans, reports, checks, revisions, and landing evidence
- Expose durable lifecycle controls through remote transport and the Heimdall UI
…command

Tracks objective progress through files-on-disk -> committed -> pushed ->
hosted-review -> merged rungs, adds recovery/territory checks for the
landing action executor, consolidates approval-scope resolution and debug
reporting, and wires `orca heimdall debug` into the CLI.
- Preserve confirmed detail results across equivalent snapshots and refreshes
- Add regression coverage for pending and StrictMode requests
- Display watcher IDs and extract objective plan formatting logic
- Reset budgets safely and handle stale dispatches, questions, and worker terminals
- Improve objective enrollment and plan UI behavior
- Preserve OMP atomic submission through minification
- Add operational documentation, ownership metadata, and regression coverage
readyToResume was reachable only through parkedForWorkerQuestion, so a
park-worker-escalation never self-healed: resolveWorkerEscalations closed the
worker-escalation entry when the dispatch settled, but the park stayed open and
the enrollment stayed disabled until a human resumed.

A worker-escalation park now retires itself when the escalation is no longer
unresolved and the dispatch that raised it settled as landed. A dispatch that
failed, or was confirmed exited without a report, keeps the watcher parked so an
operator still reads what went wrong, and readyToResume takes the park kind it is
folding rather than assuming a question.

These paths carry the branch's already-staged in-flight state for the same files;
only the auto-recovery asymmetry is new here.
- Classify failures and bound infrastructure redispatch retries
- Fence leases and make shutdown, handoff, and retention atomic
- Preserve compatibility while exposing richer diagnostics and dispatch results
- Record durable, provider-aware shadow observations with bounded state
- Protect credentials and exclude remote execution hosts
- Support calibrated judgment for routing, failures, reviews, and handoffs
Concurrent implementer dispatch with per-dispatch worktrees, a merge
train onto the enrolled branch, derived lanes, owner revision amendments
and a live concurrency control. Also bumps the pnpm pin to 12.5.1 and
scopes the Claude runtime-auth clear to its target runtime.
Territory glob within/overlap checks, required task territory and
assumptions on the planner write path, repair and plan-review report
schemas, and the pure amendment helpers moved into shared.
Adds the optional gates list to the objective enrollment payload, the
renderer gates editor, and strips gates for peers without parallel
execution alongside lanesEnabled.
The gates editor now sits beside the parallel fields, is disabled for
hosts without parallel execution, and a present gate blocks submission
instead of being silently stripped.
Flags missing or out-of-territory task territory, test-only nodes,
whole-tree or unscoped checks, gate duplicates and conflict pairs, and
reports critical-path length and width. Never rejects a plan.
Prompts print the live concurrency cap; the planner gets a plan shaping
policy, territory and assumptions requirements, a repair report
contract, and budgeted open/frozen task context. Implementers are told
to run only task-scoped checks.
Schema v5 tables with replay-safe store APIs and world projection, and
a frozen-task guard on the amend path that refuses repairs touching
succeeded or running tasks.
Gate-attempt completion replays compare timedOut, patch and review
projections keep the newest 1,024 rows, and a patch may touch up to 256
task keys.
Concrete test filenames count as test-only territory, and unscoped
checks are judged per shell segment.
zpyoung and others added 18 commits September 24, 2026 16:43
A resync that changed enabled/paused only refreshed the published status on
the disabled path, so a re-enable, an owner-deviation early return, or an
error exit after the resync kept publishing the stale cached status.
…e before judging it

A Claude structured send settles on admission, so a fresh preamble is still
pending when the send returns. Judging it then marked every Claude structured
worker start and every Heimdall owner turn as unknown, although the agent ran.
Wait on the host's existing send-settlement observer; an unsettled wait is
still unknown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude Code paints no idle OSC title and draws with cursor moves, so since
the tui-idle evidence ranking a Claude pane had nothing to settle on: the
newline tail stays empty and the visible-screen probe looked once, before
Claude's first frame. Every Claude terminal worker start timed out at
agent_readiness.

Recognize Claude's cold-start input box (the last prompt carries its Try
placeholder) as a ready prompt, and re-arm the visible-screen probe each
second while the waiter's tail is still empty. Pinned to transcripts
captured from Claude Code 2.1.282.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… exists

A worker pane's preview holds only the status rows below Claude's prompt
box, so the visible-screen probe never started and the ready rule never saw
the prompt. Hook output also pushes Claude's banner off the visible screen,
so the ready rule no longer requires it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sing

A fresh pane's first provider snapshot can time out or arrive before the
provider holds a screen; treating that as final left Claude worker starts
waiting out their whole readiness timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Centralize shared validation, parsing, concurrency, and error handling
- Make objective store mutations transaction-scoped and prune unused exports
- Share scoped SQLite transactions and hardened report-file reads
- Consolidate worker evidence validation and test fixtures
@zpyoung
zpyoung marked this pull request as ready for review September 27, 2026 11:23
@greptile-apps

greptile-apps Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 3/5

[High risk] Adds new Heimdall watcher system with CLI, runtime, and test infrastructure.

The PR is not ready to merge while post-turn Claude waits time out and the localization test fails; the repository comment requirement also needs to be met.

Findings

  1. P1 Claude post-turn idle times out ▶
  2. P1 Catalog count test fails ▶
  3. P2 Lease writes expose partial records ▶
  4. P2 Test selection silently ignored ▶
  5. P2 Temporary terminal logging remains ▶
  6. P2 Comment is overly verbose ▶

Summary

The PR adds a Heimdall kernel and UI for durable PR and objective watchers, with remote fleet routing, lease-backed scheduling, guarded agent work, and landing evidence.

  • The Claude idle-after-turn path and the localization test need correction before merge.
  • Lease publication, sandbox selection behavior, and temporary terminal diagnostics need follow-up.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  UI[Heimdall UI or CLI] --> K[Watcher kernel]
  K --> L[Lease and ledger]
  K --> S[PR sitter]
  K --> O[Objective planner and workers]
  S --> F[GitHub or GitLab]
  O --> G[Checked git landing]
  K --> R[Remote host routing]
Loading

Reviews (1) · Last reviewed commit: "Centralize Heimdall persistence and repo..."

Comment thread src/main/fork-heimdall/lease-store.ts Resolved
@zpyoung

zpyoung commented Sep 27, 2026

Copy link
Copy Markdown
Owner Author

@greptile-apps Fixing. Will be in the next commit

@greptile-apps

greptile-apps Bot commented Sep 27, 2026

Copy link
Copy Markdown

Thanks — I’ll re-check the affected paths in the next commit, including the idle timeout, catalog-count test, lease publication, test selection, and temporary logging findings.

zpyoung and others added 5 commits September 28, 2026 05:23
…nd sandbox guards

Carries the terminal-wait readiness change, lease-store edits, the sandbox runner's
unit-lane-only --select guard, and the fork localization catalog count (16 to 19).
…test seams

- Replace type assertions in Heimdall, objective, and sitter code with checked
  types; remaining casts carry line-specific SAFETY rationales
- Rename the planner action's `shape` field to `plannerMode` (and other
  structure-named symbols) for the anti-slop rule; no legacy read support
- Keep the runtime graph Electron-free: remote objective detail reads go
  through the bound fleet transport
- Break import cycles in relay allowlists and the shared objective decider
- Move per-card Heimdall watcher lookups off the app store into one indexed
  store, restoring the sidebar listener budget
- Tolerate partial PTY records in the worker exit-tail seam
- Declare Heimdall seams in upstream capability, census, and proxy-audit tests
- Replace primitive restyles with variants, wrappers, or a fork tone pill
Node and web each peak near 10 GB; started together with cli and mobile-web
they exceed the 16 GB ubuntu-latest runner, which surfaces as a runner
shutdown signal with no type errors. Declared as a pending-upstream exception.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@zpyoung
zpyoung merged commit 9710c6b into main Sep 28, 2026
41 checks passed
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