Captures design for an action-provenance DAG layer downstream of final_label - five new stages (action_plan -> tool_call -> tool_output -> postcondition_check -> action_label) - and why this stays a research doc rather than an open ticket today. Three options analyzed: Option A in-tree action DAG (identity drift), Option B sidecar package (recommended; preserves the verified-answer-cache identity by chaining a separate action_root that cross-links into run_dag_root), Option C out-of-scope. Promotion criteria spelled out so the doc graduates to a ticket when the first agent use case shows up. Cross-links #000001 (upstream provenance gap), #000022 (LossReport, same axiom one stage upstream), #000012 (v8 selection could later score action histories), and the 2026-05-07 arborist-vs-donto comparison. TICKETS.md gains a pointer in "Distinction from other docs" so future shifts find the doc.
10 KiB
Tool/action DAG — research path
Status: research path · not a ticket · awaiting first agent use case Opened: 2026-05-07 Promotion trigger: when an actual agent (an LLM that takes real-world actions through arborist's verified answers) is in flight or about to land. Until then this stays a doc. Audience: fox + future blackops shifts evaluating whether arborist should host an action-provenance layer.
Why this is a doc, not a ticket
Per docs/TICKETS.md: a doc proposes change AND awaits a decision
AND has scoped implementation cost → it's a ticket. Otherwise it's
reference. This proposal has the first two but not the third — the
implementation cost is conditional on a use case that doesn't exist
in tree today (grep for tool_call, action_plan, etc. returns
zero hits across arborist/ and docs/tickets/ as of 2026-05-07).
Five-step algorithm step 2 says: don't optimize a process that
shouldn't exist yet. So we capture the design now, ticket it later
when an agent shows up.
Problem statement
Today arborist's run-DAG ends at final_label. Everything
downstream of "the LLM gave us a STRICT pointer-linked answer" is
invisible to the audit chain. When arborist serves a question-
answerer LLM upstream of human decision-making, that's fine — the
human is the side-effect surface and lives outside arborist's
audit scope.
When arborist serves an agent — an LLM that calls tools, makes API requests, modifies state on the strength of a verified answer — the audit chain has a downstream gap analogous to the one #000001 closed for retrieval keywords:
cache_key(8-dim): no action footprintrun_dag_root: no action footprintaudit_events: no action footprintprovidence_cache: no action footprint
The map (verified answer) is committed; the territory reached (tools dispatched, side effects produced) is not.
Concrete failure shapes
Case A — silent action. An agent reads STRICT from arborist,
calls delete_user(id=42), and the deletion is permanent. Audit
replay can recover the question, the sources, the verified answer —
but no record that anything was done with it.
Case B — wrong tool. LLM answers correctly, then dispatches the wrong tool (typo'd argument, schema drift). The verified answer was right; the action was wrong. Today there's no separation between "the answer was right" and "the action that the answer authorized was right."
Case C — partial rollback. Action sequence partially completes, host process crashes, restart finds inconsistent world state. No committed rollback plan to replay.
Proposed DAG extension
Five new stages chained after final_label:
final_label
→ action_plan # LLM-proposed sequence, committed BEFORE any execution
→ tool_call # one node per dispatch; committed at moment of dispatch
→ tool_output # one node per response; committed when host returns it
→ postcondition_check # did the declared postcondition hold?
→ action_label # EXECUTED / FAILED / ROLLED_BACK / QUARANTINED
action_label mirrors audit_mode — a binary verdict; soft signals
stay sidecars. Same verifier-stays-binary discipline as the existing
trichotomy.
Three design options
Option A — in-tree action DAG
Schema lives in arborist's SQLite. Host process registers a
tool-dispatch callback; arborist commits the call before the
callback runs and commits the output when it returns. Failures
(timeout, exception) commit a node with tool_output.status='failed'
so audit replay sees the attempt.
Pros: one audit chain, one Merkle root, one verifier path.
Cons: arborist's surface area expands to include a tool-dispatch contract. Pass 1 of the Donto comparison framed arborist as "upstream of an LLM" — Option A makes it both upstream and downstream. Identity drift.
Option B — sidecar package (recommended)
Separate arborist-action package consumes arborist's run_dag_root
and chains its own action_root. Two roots, audit chain spans both
via a typed cross-link (e.g., action_root.parent_run_dag_root is
a foreign key into the answer's run-DAG).
Pros:
- Preserves arborist's Pass-1 identity. The verified-answer cache stays a verified-answer cache; agent-side audit lives next door.
- Two-root model is the same shape the existing mesh code uses to chain across peers.
- Operators who don't want action-DAG don't install it. Operators who do install it get the cross-link "for free."
- Schema migrations live in the action package, not in arborist
core —
chain-check-shardsand the existing audit chain stay unchanged.
Cons:
- Two roots to verify, two packages to keep in sync.
- Cross-link contract has its own drift surface (the foreign-key shape, what the action package commits to, what arborist commits to).
Option C — out of scope
Document the boundary. Operators wanting action provenance build it in their host process and emit their own audit chain. Arborist stays a pure question→answer cache.
Pros: smallest blast radius. No new code, no new identity question.
Cons: every operator that hits this need re-derives the same shape. Provenance discipline becomes per-operator self-discipline, which is exactly the gap arborist refuses to accept on the question→answer side (cf. #000022 LossReport on the adapter side).
Recommended landing shape (when promoted to ticket)
Option B, scoped to:
- New package
arborist-action(separate pyproject, separate import root) with its own SQLite schema and Merkle conventions matchingarborist/merkle.py. - Cross-link contract:
action_rootpayload includesparent_run_dag_rootandparent_cache_key. Verifier is "those values resolve to a live providence-cache row whoseaudit_modeis in the policy-allowed set." - Tool dispatch happens in the host process. Action package
commits
tool_callbefore dispatch andtool_outputwhen the host returns. Failures (timeout, exception) commit atool_outputrow withstatus='failed'. - Rollback plans are committed at
action_plantime, replay-able from the audit chain. Actually running rollback is host-process work. action_labelmirrorsaudit_mode—EXECUTED/FAILED/ROLLED_BACK/QUARANTINED. Binary verdict. Soft signals (e.g., "did the action have unintended side effects?") stay sidecars in the action package, never enter the proof path.
Out of scope (whenever this ships)
- Tool dispatch. Arborist (core or action package) never calls a tool. It commits intent, call, and response. Host process executes.
- Semantic validation of tool outputs. Verifier stays binary.
postcondition_checkis structural ("did this declared invariant hold?"), not semantic ("was this the right action?"). - Multi-step planning logic. Whatever plan the LLM produced gets committed; the action package doesn't re-plan, doesn't optimize, doesn't constrain.
- Rollback execution. Plan commit only. Running it is host work.
- The arborist 8-dim cache_key, the verifier, retrieval, schema / canonicalization / chunking versions. Untouched.
The identity question
The Donto comparison (Pass 1) was explicit:
Arborist is upstream of an LLM. Donto sits next to humans curating contested knowledge.
Adding an action-DAG layer makes arborist (or its sidecar) also downstream of an LLM — committing what the LLM did, not just what it said. Three readings:
- Identity drift. Arborist becomes a hybrid: cache for verified answers + ledger for agentic actions. Operators who wanted "pure verified-answer cache" now share a tree with action provenance even if they don't install the sidecar.
- Natural extension. The Merkle-AGI principle that says "every cognition-affecting input must be committed" also says "every action taken under a verified answer must be committed." Same axiom, downstream side. Option B (sidecar) honors the axiom without forcing the identity shift on operators who don't want it.
- Premature. No agent is hitting arborist today. Build action-DAG when the first real agent runs through, not before. Reading 3 is consistent with five-step algorithm step 2.
Reading 3 is the current default. Readings 1 and 2 reactivate when the first real agent surfaces.
Promotion criteria
This doc graduates to a ticket when any of:
- An operator-in-tree (or a clearly named upstream consumer) has an LLM in flight that takes real-world actions on the strength of arborist verified answers.
- A second LossReport-shaped audit gap surfaces (e.g., a Donto-style comparison naming a different downstream missing-provenance axis) and the cumulative case for "arborist needs a downstream provenance story" outweighs the identity-drift cost.
- A verified-answer cache gets used for an irreversible action in a real production incident, and post-mortem can't recover what happened. (Don't wait for this. The first two are cheaper signals.)
Until then: the doc captures the design so the work is ready when the use case is.
References
/home/fox/Downloads/arborist_vs_donto.pdf§6.1 ("Pass 1 — Purpose and audience"). Names arborist's audience as "developer who wantsask(question) → (answer, proof)." This doc explicitly lives downstream of that contract.docs/tickets/ticket-000001-retrieval-keywords-audit-gap.md— the same provenance-gap shape on the upstream (retrieval) side. Closing the analogous gap on the action side is the symmetric move.docs/tickets/ticket-000022-adapter-loss-report.md— same axiom ("every causally relevant transformation must surface what it dropped or did") applied to adapter normalization. Action-DAG would extend the axiom one stage further downstream.docs/tickets/ticket-000012-selection-consensus-protocol.md— v8 selection protocol. If action-DAG ships, accepted mutations in v8 fork-choice should be able to reference action histories as evidence (e.g., "this policy patch reduced rollback rate by X%"). Cross-link surface, not implementation dependency.arborist/qa/dag.py— the existing run-DAG construction; the action package would chain into the resultingrun_dag_rootvia Option B's foreign key, not modify this module.