Skip to content

feat(flow): compile-agent — compile an agent's first verified run into a zero-token program - #1277

Open
abrichr wants to merge 9 commits into
mainfrom
claude/crystallize-from-prompt-89950e
Open

abrichr wants to merge 9 commits into
mainfrom
claude/crystallize-from-prompt-89950e

Conversation

@abrichr

@abrichr abrichr commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

openadapt-flow compile-agent "<task>" compiles an agent's first verified run: a computer-use agent does the task once against --url (it writes there, so point it at a test copy), OpenAdapt checks the record it wrote in the system of record, and compiles the run into a program. Later runs make no model calls. openadapt-flow compile-agent run <dir> runs it.

What it does

  1. Run 1 in a harness (openadapt_flow/compile_agent/harness.py). Claude (claude-opus-5-5, the customer's own key) gets the page's structure as short text lines with element refs, not a screenshot every turn. It can ask for a screenshot, and gets one after a save. Every action goes through the JIT CaptureComputer, so the run is recorded and compiled like any JIT capture.
  2. Save gate. Every action that may commit a write (a commit-labelled control of any role, an unreadable element, Enter, a typed line break, Space off a text field, any shortcut) is checked first: the record's identity (--on-screen "MRN P1") must appear as whole words in the control's own row, dialog, form or section. The harness keeps a screenshot as evidence and asks the hold when there is one. A save on the wrong record is never performed, and nothing is compiled.
  3. Run-1 record check. The system of record is read when run 1 starts and after it ends. It passes only when exactly one record appeared, it has every --expect field, and every input landed in it.
  4. Checks a person confirms. The change becomes record --verify proposals. A person confirms them (and the parameters); they become the program's verify.yaml.
  5. Admission through the JIT lifecycle: canaries (values the operator supplies, since a canary is a real save), then an admitter key, a registered agent under a signed policy (self-admission when the driving agent is that registered agent and the policy allows it, feat(jit): let the recording agent admit its own draft when the policy allows it #1249), or a person at a terminal who is shown the steps and the canary results. Each canary also gets a verify-run receipt.
  6. Emit an Agent Skill, an MCP server with one tool, and a one-line CLI command, all calling run_compiled.
  7. Later runs: no model calls, every save read back, and a verify-run receipt against the confirmed checks.

Python API (openadapt_flow.compile_agent): compile_first_run(...), run_compiled(dir, **params), and FirstRunCompiler for the steps one at a time. ClaudeAgent(vision="selective" | "every_turn"), ScriptedAgent for no-model runs. Docs: packages/flow/docs/COMPILE_AGENT.md.

Measured (packages/flow/benchmark/compile_agent/)

Ten live trials per arm on MockMed, alternating order, a distinct note per trial, prompt caching on in both arms. Raw rows are committed. (Measured at 2d5e4c1, before the rename.)

Per run 1 (median) Structure first Screenshot every turn
Passed the record check 10 / 10 10 / 10
Screenshots sent to the model 1 15
Prompt tokens 10,332 76,053 (7.4x)
Cost $0.028 $0.148 (5.3x)
Model time 8.9 s 11.6 s

Wall time mostly tracked the shared host's load (1-minute load average 60 to 480 during the run). At low load (trials 1 to 3), run 1 took 15 to 18 s structure first and 36 to 59 s every turn. Ten replays of the admitted programs: 10/10 VERIFIED, 0 model calls. Total spend under $2.30 of the $20 cap.

Tests

  • tests/test_compile_agent.py (no browser): record check, commit labels, listing, scrubbing that keeps the task's own values, the Claude loop's token accounting, cost cap and refusal, every gated way to commit and the plain actions that are not, whole-word scoped identity, stale refs by row, the hold, the emitted MCP server.
  • tests/e2e/test_compile_agent_e2e.py (MockMed, no network): the real ClaudeAgent loop with a fake client compiles run 1, the registered agent self-admits under its policy after two canaries, the replay makes 0 model calls with a VERIFIED receipt, compiling again does not call the agent and run 1 refuses to run on an admitted task, a replay on an app that files the note on another patient halts, an agent that opens the wrong patient is refused at the save with nothing written or compiled, and the CLI path (no terminal stops at draft, a person confirms at a terminal, an admitter key admits, compile-agent run). Added to the flow-critical CI job, which is the job that runs tests/e2e files.

Other changes

  • CaptureComputer gains page, pointer and screenshot() (read-only).
  • docs/JIT.md: the live-loop sketch now points at the shipped loop.
  • docs/CAPABILITIES.md (flow): a "From a prompt to a program" section. The root CAPABILITIES.md does not exist on main yet; docs: one map of what OpenAdapt can do #1260 adds it (with a test that every verb is listed); compile-agent goes into its command list once it is on main.

Web only, and says so

compile-agent runs web apps only in this PR. --backend windows|macos|linux|rdp|citrix, a --config whose backend.kind is not web, or a non-http(s) URL is refused with exit 2 before anything runs (UnsupportedTarget in Python). There is no silent browser fallback. Desktop, RDP and Citrix need the JIT to drive the record --backend backends and a non-DOM observer; that is left for the backend-parity work.

Limits

Listed in COMPILE_AGENT.md: web apps only; the gate recognizes commits by label and Enter in a form; on a wrong-record replay the verify-run receipt says HALTED_BEFORE_EFFECT rather than RECONCILIATION_REQUIRED until the guarded save (#1258) lands; canaries write test records; MockMed's demo idempotency key needs a reset between runs in tests.

Review

An independent full-diff review found gaps in the save gate (newline, Space, shortcuts, role=button, unreadable elements, whole-page substring identity), person admission filing the review for the person, the admitter stamped as recorder, and default canary values. All are fixed in 98a3223 with tests; the remaining items are listed as limits.

🤖 Generated with Claude Code

abrichr and others added 6 commits October 18, 2026 13:24
…ecomes a zero-token program

Live agent loop with selective vision (structural listing first, screenshots
on request or at the save gate), right-record check and hold before every
save, run-1 record check against the system of record, person-confirmed
record checks, JIT admission (policy self-admission), emitted skill, MCP
server and CLI line, and replays with a verify-run receipt.

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

- Gate a click at the current pointer (no coordinate) like any other click.
- docs/CRYSTALLIZE.md; CAPABILITIES.md and JIT.md point at it.
- benchmark/crystallize/measure.py: live selective vs every-turn runs.
- Run tests/e2e/test_crystallize_e2e.py in the flow-critical CI job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ten live claude-opus-5-5 trials per arm on MockMed: structure first used
10,332 prompt tokens and $0.028 per run against 76,053 and $0.148 with a
screenshot every turn; 10/10 passed in each arm; 10/10 replays VERIFIED with
0 model calls. A new run 1 clears checks confirmed for an earlier draft.

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

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

Rename: openadapt-flow compile-agent / compile-agent run, package
openadapt_flow.compile_agent (compile_first_run, run_compiled,
FirstRunCompiler), COMPILE_AGENT.md, benchmark/compile_agent, cache dir
.openadapt-compile-agent. The help says the agent writes to --url.

Review fixes:
- Gate every action that may commit: commit-labelled controls of any role,
  unreadable elements, Enter, typed line breaks, Space off text fields,
  shortcuts. More commit words (delete, update, done, ...).
- Identity must appear as whole words in the control's row, dialog, form or
  section, not anywhere on the page; refs are re-checked by row.
- A refused run's draft is set aside even if the agent carried on; stale
  proposals are cleared; run 1 refuses on a task with a draft or program.
- The recording is signed with the driving agent's principal, not the admitter.
- Person admission shows steps and canary results and asks at each stage.
- Canary values are required (a canary is a real save); no defaults.
- Unknown models need explicit prices for the cost cap.
- confirm_params runs before verify.yaml is written; receipt baseline is reset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@abrichr abrichr changed the title feat(flow): crystallize — an agent's first verified run becomes a zero-token program feat(flow): compile-agent — compile an agent's first verified run into a zero-token program Oct 18, 2026
--backend (record's choices), a --config whose backend.kind is not web, or a
non-http(s) URL stop with exit 2 and a message before anything runs. The
Python API raises UnsupportedTarget. No silent browser fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
abrichr added a commit that referenced this pull request Oct 18, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
abrichr and others added 2 commits October 18, 2026 15:12
…e not saves

- A click over an iframe is read inside the frame through the backend's
  hit-tested frame chain; a frame it can't map counts as unreadable (gated).
- Enter or Space with focus inside a frame reads the frame that has focus.
- A click on an editable field ("Postal code") only focuses it; not a commit.

Found by the code-as-action session (claude/code-as-action d3b8efc4, 8bbcad02);
ported here so #1277 does not merge with the gaps.

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

This branch has not been deployed

No deployments
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