Repository navigation
feat(flow): code as action is the default first-run agent; every save is read back - #1542
Conversation
… is read back - ClaudeAgent and compile-agent default to vision=code on claude-sonnet-5-5 - the harness reads the system of record after every save the gate lets through: another record's change stops run 1 (readback_wrong_record), no change is reported to the agent as not saved - MCP teach_run: the agent writes action scripts; with a person approving saves, the script stops before each save and the approval is bound to it - scoreboard arm 7: read-back wired, protocol fault items, --keep-program Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… write to its save - the recorder's after-read waits for the system of record to change after a save the gate let through (Recorder.expect_write), so the compiler binds the write to that step (OpenEMR's chart lands after the screen settles) - scoreboard: arm 7 reports success only when its last read-back landed (Amendment 11); MockMed items 21-40 for arm 7 and the program compiled from it (Amendment 10); OpenEMR real-app check (Amendment 12) - docs: CODE_AS_ACTION, COMPILE_AGENT, agent README, CAPABILITIES, llms-full Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- each save is judged against a read just before it (a second save that writes nothing is told so); a record overwritten to match the task, or an edit shown as removed+created on a store without ids, is handled - a teach_run approval is bound to the save the person was shown and is re-read before it is used; comment lines are not shown as the action - a line that already saved and stops before a second save is not resent - typing on a native drop-down is not a save (a select no longer loops in hold mode); the recorder's write wait is taken back if the action fails Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AI review (coordinator)Verdict: fix first. The direction is right and the e2e tests are real: MockMed in a real browser against the fault server, with no fakes standing in for the system of record. But the read-back accepts several writes it should refuse, and it can tell the agent "nothing was saved" when something was. Line numbers below are at I checked
Tests run (serially, no xdist, host load 15 to 29): Blocking
Non-blocking
Checked and fine
|
… writes, failed reads - a record that existed when run 1 started never counts as the named one: any change to or removal of it stops the run; a second named record stops the run as a duplicate (counted, so stores without ids are covered) - a save with no new record is unconfirmed; the next save is refused if the first landed late; a failed read stops the run, and run 1 does not start without a starting read - a save that raises is still read back, and the result is in the error text - landed requires every input value in the new record - the tool waits briefly for a target that is still loading - wording: the read-back covers saves the gate detects; the wrong-patient write landed; the word rule's silent_wrong is stated with HAND_REVIEW.md - data: no local paths in the new runs; meta.json records the code each run used; llms-full.txt keeps only this PR's change; PROTOCOL Amendment 13 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AI re-review (coordinator)Verdict: ready to merge. All six blockers from the first review are closed at I ran a direct probe of
Tests:
Also fixed: Non-blocking, for a follow-up
Action: CI's required checks are queued with no failures, so I'm enabling auto-merge (merge commit). |
…s read-back Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ault # Conflicts: # .github/workflows/packages.yml # CAPABILITIES.md # llms-full.txt # packages/flow/benchmark/scoreboard/PROTOCOL.md # packages/flow/docs/CODE_AS_ACTION.md
The MCP layer now passes tool= (teach_act or teach_run) to hold_needed. BrowserTeachBridge and VerifyTools took only the arguments, so a browser teach_act failed with a TypeError inside the protected boundary (test_browser_teach_e2e). Both now accept and ignore it.
PR #1542 delta review (approved 75980ca -> head 3f586e3)Scope: merge commit 926f150 (origin/main f0297fb into the PR), the fix commit Verdict: ready. No blockers.1. hold_needed(tool=) in the fix commit: no bypass
2. Conflict resolutions: nothing dropped, and no amendment's meaning changed
3. RecordReadBack against main: still fails closedMain changed nothing in Every run-1 entry point goes through
4. Public claimsEach claim is backed by code:
The scoreboard figures weren't re-derived here; they are unchanged from the Non-blocking notes
|
teach_run's scripts act through the same authorized run 1 as teach_act, so the scope bound from the statement checks every action and the save. Tests with a real browser, roster and admission gate: a teach_run draft without a statement is refused at admission, one under a statement passes the gate, and a script that types a value the statement doesn't allow saves nothing.
…run in MCP fallback, bridge contract test (#1779)
…ath (#1436) * WIP: signed teach authorization bound to the teach scope Adds openadapt.teach-authorization/v1 (person-signed scope: task, inputs, origins, permitted writes, forbidden actions), binds it on the JIT capture path, records its digest in the signed recorder block and admission manifest, and requires it at admission for agent-taught drafts. Other authoring paths, the live-model harness and full test runs are not done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Teach authorization: sign with the approver roster's keys; bind or refuse every teach path The signed teach authorization (openadapt.teach-authorization/v1) now uses the approver keys and the approver verifier instead of GitHub SSH keys. Admission of every JIT draft requires one, keyed on the signed recorder block. compile-agent, MCP teach, the skill cache, browser-teach writes, agent-demonstrated fixes and self-repair of agent-taught programs are bound; the airbag handoff's drafts are refused. Adds openadapt-flow teach-authorization create/sign/verify. * Teach authorization: address review round 1 Refuse browser-teach writes instead of half-binding them. Pin the roster from the deployment config for servers an agent runs; pass recorded heads; add --approver-root to jit admit. Check the task at repair approval and at admission from the signed recorder block. Keep the signed write adapter in narrowed scopes. Refuse a click whose label probe fails when controls are forbidden. Drop the Desktop stdio signing path. Default expiry 24 hours. Changelog with the breaking changes and upgrade notes. * Teach authorization: address review round 2 Benchmarks and the JIT example sign a throwaway authorization per run (demo_authority) so their drafts still admit. admission_status uses the same trust as admit. create() defaults to a 24-hour expiry, at most 90 days. Document the remaining trust limits. * Teach authorization: bind teach_run (from #1542) and test it teach_run's scripts act through the same authorized run 1 as teach_act, so the scope bound from the statement checks every action and the save. Tests with a real browser, roster and admission gate: a teach_run draft without a statement is refused at admission, one under a statement passes the gate, and a script that types a value the statement doesn't allow saves nothing. * Teach authorization: fix the two CI failures - test_agent_guard_e2e: the handoff's draft is refused at admission by an AdmissionError, not a returned detail; assert the raise. - docs/phi_at_rest.md lists the new teach-authorization/* bundle entry (test_sealed_at_rest checks every inventory entry is documented). - The two browser-teach write e2e files assert the refusal against the real example app instead of skipping: flow-critical fails on any skipped test. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* WIP: signed teach authorization bound to the teach scope Adds openadapt.teach-authorization/v1 (person-signed scope: task, inputs, origins, permitted writes, forbidden actions), binds it on the JIT capture path, records its digest in the signed recorder block and admission manifest, and requires it at admission for agent-taught drafts. Other authoring paths, the live-model harness and full test runs are not done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Teach authorization: sign with the approver roster's keys; bind or refuse every teach path The signed teach authorization (openadapt.teach-authorization/v1) now uses the approver keys and the approver verifier instead of GitHub SSH keys. Admission of every JIT draft requires one, keyed on the signed recorder block. compile-agent, MCP teach, the skill cache, browser-teach writes, agent-demonstrated fixes and self-repair of agent-taught programs are bound; the airbag handoff's drafts are refused. Adds openadapt-flow teach-authorization create/sign/verify. * Teach authorization: address review round 1 Refuse browser-teach writes instead of half-binding them. Pin the roster from the deployment config for servers an agent runs; pass recorded heads; add --approver-root to jit admit. Check the task at repair approval and at admission from the signed recorder block. Keep the signed write adapter in narrowed scopes. Refuse a click whose label probe fails when controls are forbidden. Drop the Desktop stdio signing path. Default expiry 24 hours. Changelog with the breaking changes and upgrade notes. * Teach authorization: address review round 2 Benchmarks and the JIT example sign a throwaway authorization per run (demo_authority) so their drafts still admit. admission_status uses the same trust as admit. create() defaults to a 24-hour expiry, at most 90 days. Document the remaining trust limits. * Browser teach: bind taught writes to the signed teach authorization Run 1 of a write starts only when the person's statement verifies and covers the write: the app's origin, the contract as record kind, a write adapter, run 1's inputs, and exactly the contract's checked record fields with run 1's values. The adapter reads every write before it is sent, and only signed values may be typed. A taught tool keeps the statement, is signed with the local admission key, and every call checks both again (roster, purpose, task, expiry). The example app ships its write adapter. The two browser write e2e files run again. * Teach authorization: bind teach_run (from #1542) and test it teach_run's scripts act through the same authorized run 1 as teach_act, so the scope bound from the statement checks every action and the save. Tests with a real browser, roster and admission gate: a teach_run draft without a statement is refused at admission, one under a statement passes the gate, and a script that types a value the statement doesn't allow saves nothing. * Teach authorization: fix the two CI failures - test_agent_guard_e2e: the handoff's draft is refused at admission by an AdmissionError, not a returned detail; assert the raise. - docs/phi_at_rest.md lists the new teach-authorization/* bundle entry (test_sealed_at_rest checks every inventory entry is documented). - The two browser-teach write e2e files assert the refusal against the real example app instead of skipping: flow-critical fails on any skipped test. * Browser teach writes and the teach scope: address review round 2 - Keys are checked where they land: focus is found across the page's frames, and Enter in a form field is also checked against the form's default button (implicit submission). Modifier chords are refused while controls are forbidden. - A click is read from the accessibility tree by DevTools; a node that can't be found is unknown and refused; with no page, clicks are refused too. - Browser teach checks click, select and press targets by accessible name over DevTools, not by the listing label. - The admission key must exist (private and public) and is checked before run 1; it is never created on the write lane. - Run 1 reaches only the statement's origins, reads included. - expected_count 0 is read as 0. - Docs: what the accessibility tree does and doesn't defeat; the serving process must run as a user the agent can't act as. * Teach scope: type fixes for the key-target probe * Browser teach writes and the teach scope: address review round 3 - Run 1 of a taught write opens no WebSocket (route_web_socket; Playwright without it refuses the write). - Enter is checked against the browser's own default button (:default), read in an isolated world; no re-implemented form rules. - A click is matched by its accessible name, the text of a control with no role and three ancestors, and the control a <label> is for (isolated world); the point is converted to document coordinates. - Browser-teach clicks scroll the target into view before they're checked. - Browser tools have their own task namespace, browser-tool.<name>. - A tool's program name must be its folder's. - The example adapter reports every posted field. - Docs say what binds later calls, and the rollback limit. * feat(flow): add worker and new-window containment for guarded browser contexts A CSP worker-src 'none' policy in every document, a locked window.open, a context route that aborts any request not from the guarded page, a page event that halts and closes a new window, and a DevTools target tripwire for workers. Not wired into any lane yet. * fix(flow): contain taught browser writes on every call Signed-off-by: abrichr <richard.abrich@mldsai.com> * test(flow): declare complete reads in browser teach e2e fixtures Main now requires a REST read to declare completeness (#2041). The two browser teach e2e fixtures return their whole store on one page, so they declare records_complete. Also sort test imports and refresh llms-full.txt. Signed-off-by: abrichr <richard.abrich@mldsai.com> --------- Signed-off-by: abrichr <richard.abrich@mldsai.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
What
Code as action is now the default agent for a task's first run, and every save in that run is read back from the system of record before the agent goes on. A successful run compiles into a program as before.
Default agent.
ClaudeAgent()andopenadapt-flow compile-agentnow default tovision="code"onclaude-sonnet-5-5, the setup scoreboard arm 7 measured.selectiveandevery_turnremain available;selectiveandevery_turnkeepclaude-opus-5-5.Read-back on every save the gate detects. After each action the save gate lets through, the harness reads the
verify.yamlsystem of record and compares it with the read taken at the start of run 1 and with a read just before the save (harness.RecordReadBack, in both harnesses):readback_wrong_record) after the wrong write has landed, and nothing can be admitted;landedrequires every input value in the new record.Every read-back is kept in
run1["readbacks"]. The pipeline always wires it.Teach path. A new MCP tool,
teach_run(web apps), lets the client's model send action scripts. When a person approves saves (--teach-hold), the script stops before each save withneeds_person, and the client's form asks the person. The approval is bound to that line on the app as it is at that moment; a stale approval does nothing.teach_actis unchanged.Recorder. After a save the gate let through, the recorder's after-read now waits for the system of record to change (
Recorder.expect_write), so the compiler binds a late write to its save step. On OpenEMR the chart lands after the screen settles.Measured (scoreboard,
python -m scoreboard.build; PROTOCOL Amendments 10 to 12)MockMed items 21 to 40 (20 items: item 30 is a save the server drops while the screen says saved, item 40 a save that lands on another patient). claude-sonnet-5-5. All rows are untimed because the host's load was above 12.
silent_wrongby the word rule; a safe stop on hand reviewsafe_stopflagged_wrong(the save landed on the other patient; the read-back then stopped run 1)flagged_wrongOn item 30 the agent said the save "did not take effect" because the read-back showed nothing was saved, but the scoreboard's word rule missed that wording. The row keeps the rule's verdict, and HAND_REVIEW.md records the review. The read-back's rules changed after review; the measured runs used the earlier rules, and by the new rules their verdicts are the same (Amendment 13; not re-run). From Amendment 11 on, arm 7 reports success only when its last read-back landed.
Real app (OpenEMR referral, local fixture). 6 of 6 correct by SQL read-back, and each save was read back as landed on the named chart. Admission still refuses these programs. The recorder fix binds the confirm save, but the "Create New Patient" click before it is write-shaped and writes nothing, so it has no read-back (Amendment 12). Fixing that changes what admission accepts, so it is not in this PR.
Spend: $0.43 on the scoreboard key (scoreboard total $12.98 of $30).
Tests
tests/e2e/test_code_agent_default_e2e.py, MockMed in a real browser, scripted agents, no model calls, added to the flow-critical CI job:teach_rundrafting a program;teach_runstopping before a save and taking exactly one bound approval.record_readback, the new defaults, and the flags.🤖 Generated with Claude Code