Skip to content

feat(agent): task ledger and ask-the-user over MCP - #1332

Merged
abrichr merged 4 commits into
mainfrom
claude/task-ledger-aa8cf0
Oct 19, 2026
Merged

abrichr merged 4 commits into
mainfrom
claude/task-ledger-aa8cf0

Conversation

@abrichr

@abrichr abrichr commented Oct 19, 2026

Copy link
Copy Markdown
Member

What this does

Agents that use OpenAdapt over MCP can open a job and keep its state on the server instead of rebuilding it from their own history. OSWorld 2.0 (arXiv:2606.29537) found that on long workflows agents lose track of constraints, miss information that arrives mid-task, guess instead of asking, and skip verification. This PR addresses each of those.

  • Eight MCP tools: open_job, get_job, add_items, record_evidence, ask_user, get_answer, change_constraint, close_job.
  • Rules are locked when the job opens. A rule can name a run_workflow input (equals, one_of, not_one_of). The server refuses a run for the job that breaks one ("Nothing ran."). Only a person changes a rule after that.
  • Item status comes only from OpenAdapt's own results: run_workflow, get_run, and the guarded-save tools. No tool lets the agent set a status. A verified item never goes back.
  • Evidence has provenance: user is trusted; app and document are never trusted, and evidence can't change a rule or a status.
  • Ask the user. A question is an item-approval request under <runs>/task-ledger/, so it shows on the paired phone and in Desktop's Run it. Only an answer from a person on a person's route counts; the ledger checks it with Flow's own answer_refusal. Waits are bounded (0 to 600 s).
  • A stop now reaches someone. When a run for a job item stops before saving, the ledger asks whether to try again. When a save can't be confirmed, it asks a person to check the record. This closes the documented "nobody is notified" gap for work in a job.
  • Surface-neutral. The ledger only reads results, so it works the same for every backend and for guarded saves.
  • On by default whenever runs or guarded saves are on; serve --no-ledger turns it off.

Measurement (synthetic, no model calls)

python -m openadapt_flow.agent.ledger_demo: a scripted agent works a 30-item synthetic MockMed worklist through the real MCP server. The scenario has a rule change at item 13, a referral that tells the reader to mark everything urgent, two patients who share a name, and two unconfirmed saves. A simulated person answers through the console's own waiting list. The agent has the same fixed flaws in every arm.

Agent alone Ledger, enforcement only Agent follows the ledger
Items saved against a rule 12 0 0
Items guessed instead of asked 2 2 0
Unverified items reported as done 2 1 0
Correct records (of 30) 16 27 30

Enforcement alone stops every rule break. It can't stop an agent from guessing a value or misreporting its own tally, but close_job lists what was verified, so a caller can check. The scripted agent stands for a policy, not a model, so these numbers test the mechanism. They aren't a model benchmark. Results: packages/agent/docs/task-ledger-measurement.json. No paid run: the machine's 1-minute load was far above 12 during this work.

Files

  • New: packages/flow/openadapt_flow/agent/ledger.py, ledger_demo.py, packages/agent/tests/test_ledger.py (22 tests).
  • Small edits: mcp.py (the ledger wraps dispatch), bridge.py and guard_tools.py (a ledger attribute), cli.py (--no-ledger), console/save_approvals.py (a question shows its own text instead of "Save this?").
  • Docs: packages/agent/README.md ("Track a long job"), and a CAPABILITIES.md row. No new openadapt-flow command, so "Every command, by capability" is unchanged.

Not done

  • The Desktop phone card still titles a question "Approve a save" with Save / Don't save buttons. Flow now sends presentation.kind = "question", so Desktop can relabel it in a follow-up.
  • Questions are yes or no only.
  • A job belongs to one server process.

Tests

  • packages/agent: 532 passed, 9 skipped, plus the 3 new regression tests; the README checks pass.
  • packages/flow: console save-approval, save-fields, item-approval, guarded-save and capabilities-map tests: 63 passed. Ruff check and format are clean.

🤖 Generated with Claude Code

abrichr and others added 4 commits October 19, 2026 09:24
Agents that use OpenAdapt over MCP can open a job and keep its state on
the server: the user's rules (locked when the job opens), items with a
status set only from OpenAdapt's own run and guarded-save results,
evidence labelled trusted or untrusted, and yes-or-no questions for a
person.

- Eight tools: open_job, get_job, add_items, record_evidence, ask_user,
  get_answer, change_constraint, close_job.
- run_workflow for a job item is refused when it breaks a typed rule,
  while a question about the item waits, or while the item needs a person.
- Only a person changes a locked rule.
- Questions are item-approval requests under <runs>/task-ledger, so they
  show on the paired phone and in Desktop's Run it; the console shows the
  question text. An answer must come from a person on a person's route.
- A run that stops before saving, or a save that can't be confirmed, asks
  a person by itself, so a stop in a job reaches someone.
- On by default when runs or guarded saves are; serve --no-ledger turns
  it off.

Measured with a scripted agent on a 30-item synthetic MockMed worklist
(python -m openadapt_flow.agent.ledger_demo, no model calls): rule
breaks 12 -> 0, guesses 2 -> 0, unverified reported as done 2 -> 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@abrichr
abrichr merged commit 4f6618d into main Oct 19, 2026
41 checks passed
@abrichr
abrichr deleted the claude/task-ledger-aa8cf0 branch October 19, 2026 22:22
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