Chapter 19 of 30

Let the Machine Touch Something

Concepts

CHAPTER 19 — Let the Machine Touch Something

STATUS

Pinned Stage 19 bundle executed 2026-09-14 and independently verified. Written 2026-09-14. Baseline CodeAI 7a0d43b plus the Chapter 19 observation revision at a1b562a plus the explicitly identified, uncommitted repair that records any adapter Exception as FAILED. Historical bundles unchanged.

EDITORIAL PASS (2026-09-14)

  • Visual-mechanism pass 2026-09-14: added proposal to completed-action flowchart with separate adapter-report and runtime-observation paths and expected-effect dashed as outside the action contract.
  • Exception scope updated: at a1b562a only RuntimeError was recorded; a ConnectionError after a write let a same-key retry write again (found in the Ch22 pass). Uncommitted CodeAI repair records any adapter Exception as FAILED (regression test; suite 452). Remaining: raising resolver during failure recording; process death. Weakness #4 updated.
  • Added measured capstone-composition citation: adapter succeeded with no state, runtime resolver reading recorded, independent reader matches it to preserved after-bytes; resolver hashed one file so a crash branch’s effect on another file never reached the recorded observation (weakness #1 shown).
  • “working-tree revision” -> a1b562a; footer updated; default_repository_ state_hash docstring re-verified (HEAD + staged + unstaged + untracked path list, not contents).
  • Score ~900 -> ~950; evidence capped by owed Stage 19.

CENTRAL QUESTION

How does a proposed action become an observed effect?

THESIS

Decided != requested != performed != observed. A worker’s report and a runtime-controlled reading must retain distinct origins. Even an independent reading establishes only the state within its declared scope, not that the intended objective was met.

SECTION OUTLINE

  • Constructed opening: decision and proposed patch, unchanged file, worker says done.
  • Four stages; ReAct and PAL support bounded distinctions, mappings explicitly the book’s.
  • Actual ActionRequest example; requester, actor, adapter; conventional decision payload link.
  • ExecutionAdapter signature and exact execute_action order, including early reuse.
  • Named defect: adapter-first finalization obscures observation provenance.
  • Chapter 19 revision: separate observed_state_hash, old fields preserved for compatibility.
  • Repository identity policy versus target-file bytes; scope and missing observations.
  • Constructed real-API file fixture, independent after read and expected-byte comparison.
  • OpenCode’s session/transcript boundary; no live OpenCode execution.
  • Related capstone demo and measured Stage 29B authority rows, narrowly scoped.
  • Independent verification design and five owed joined cases; three editorial branches.
  • Limits, weaknesses, timed exercise, assistant prompt, failure modes, established, next.

LOAD-BEARING CLAIMS

  1. Request is recorded before new execution; reuse precedes policy/precondition checks; the normal new-action path reads state, invokes the adapter, reads state again, finalizes, and records completion. [source: src/codeai/runtime.py Runtime.execute_action]
  2. At 7a0d43b a nonempty adapter resulting_state_hash overrides the runtime reading in finalization. [source: src/codeai/runtime.py Runtime._finalize_action_result]
  3. The Chapter 19 revision separately retains the resolver reading in observed_state_hash; no resolver means None; adapter-supplied values in that field are overwritten. [source: src/codeai/runtime.py Runtime._finalize_action_result]
  4. Old records deserialize observed_state_hash as None; reused results carry the original reading, not a new observation. [source: src/codeai/runtime.py Runtime._action_result_from_payload, Runtime.execute_action]
  5. Default Git identity includes HEAD, staged/unstaged diff and untracked path presence, not untracked contents. [source: src/codeai/runtime.py default_repository_state_hash]
  6. No expected-after comparison or Stage 18 decision validation is built into execute_action. [source: src/codeai/runtime.py Runtime.execute_action]
  7. OpenCode returns a transcript, SUCCEEDED and a session ID as state_hash; this does not independently inspect the target. [source: src/codeai/opencode.py OpenCodeExecutionAdapter.execute]
  8. Stage 29B READ control: denied, 0 apply calls, file absent; WRITE: succeeded, 1 apply call, file contains [cache] and ttl_seconds = 300. [measured: experiments/applied-ai/evidence/execution-ladder/2026-09-14-7a0d43b]
  9. Joined decision/action/adverse-worker/changed-basis evidence is now pinned: honest shows intended bytes with origins retained; lying shows a succeeded report against an unchanged target; partial shows FAILED with an 8-byte prefix and a same-key FAILED replay (2 requests, 1 effect); wrong-target shows an unchanged target with other.toml exposed by inventory; stale-basis retains the decision event and performed bytes while standing moves basis_intact to basis_changed. Independent verifier recomputes from ledger plus fixture bytes and rejects four seeded corruptions. [measured: experiments/applied-ai/evidence/decision-to-effect/2026-09-14-a1b562a]

EVIDENCE

  • Measured: experiments/applied-ai/evidence/decision-to-effect/2026-09-14-a1b562a; preregistration.json (frozen before execution), run_decision_to_effect.py, verify_decision_to_effect.py (stdlib-only; –check-corruptions), ledger.sqlite, fixture bytes, invocation_receipts.json, chapter-evidence-report.md. Five cases as above; 9 verifier checks; 4 seeded corruptions rejected. Resolver watches one target file; inventory is verifier-side. CodeAI a1b562a plus uncommitted repair (hashed in codebase.json).
  • Measured: experiments/applied-ai/evidence/execution-ladder/2026-09-14-7a0d43b, authority/authority-denied.json and authority-granted.json; producer independently reads target after the action. These establish only the named control, not general correctness.
  • Demo: experiments/applied-ai/evidence/capstone; README.md, results.json, report.json. Fake cognition, stub resolver, local post-edit check; unpinned, clean reopen only.
  • Measured related boundary: experiments/applied-ai/evidence/working-state/2026-09-13-68f4ba0; README.md and chapter-evidence-report.md. Provider effects, not action crash recovery.
  • Reported: execution-ladder/2026-09-14-7a0d43b/chapter-evidence-report.md retains failing spend claim L-D. No blanket claim that the bundle passed.
  • Source inspection: adapters.py, runtime.py, opencode.py, evidence.py, domain.py, ledger.py.
  • Local regression tests are development checks, not a pinned/preregistered chapter bundle. tests/test_action_observation.py covers honest, lying, partial, wrong-target workers, forged observation, no resolver, refusals, persisted replay and old-payload compatibility.

PAPERS

  • Yao et al. ReAct: Synergizing Reasoning and Acting in Language Models. ICLR 2023. https://arxiv.org/abs/2210.03629 — primary abstract opened. Action/observation distinction; does not establish this runtime’s durability, authority, or verification. No direct quote.
  • Gao et al. PAL: Program-aided Language Models. ICML 2023, PMLR 202:10764–10799. https://proceedings.mlr.press/v202/gao23f.html — primary abstract opened. Generated program versus interpreter execution; not evidence of file-edit correctness. No direct quote.
  • Toolformer omitted: the two selected sources earn the mechanism without a literature dump. All mappings to CodeAI are the book’s.

CONTROLS / LIMITATIONS

  • Resolver independence and coverage are configured, not validated by the runtime.
  • expected-before is recorded; actual before reading is not separately retained by runtime.
  • Legacy resulting_state_hash remains ambiguous; use observed_state_hash for runtime origin.
  • Missing resolver skips the precondition comparison; no automatic expected-after assessment.
  • Caught RuntimeError may follow a partial effect; other exceptions and resolver failures can leave completion absent. No rollback, concurrency, crash, sandboxing or production claim.
  • Git untracked path policy differs from fallback file-content policy.
  • Hash-only observation does not preserve explanatory bytes or establish the correct target.
  • Stage 18 decision link is payload convention only; no automatic undo on basis_changed.
  • Local tests and constructed snippets are not promoted into measured evidence.

DEPENDENCIES

Chapter 18: decision basis and changed standing. Chapter 16: external effects and unknown outcome interval. Chapter 17: preserve observations separately from their interpretations. Chapter 14: worker success and checking/acceptance remain different stages.

FORWARD BRIDGE

A callable worker can act and an observer can inspect the result. Neither says who may request the change. Chapter 20: Capability Is Not Authority. Leave check adequacy to 21 and repeat-effect policy to 22.

OPEN ITEMS

  • stage19-decision-to-effect: EXECUTED 2026-09-14. Follow-ups that remain open: raising-resolver-during-failure-recording, process-death gap, expected-after comparison outside the action contract.

  • Commit identity for the Chapter 19 revision is pending; no commit was requested.

  • Expected implementation property: runtime reading must survive a conflicting adapter report; no automatic status downgrade for wrong/no effect is claimed.

  • Capstone must associate a historical decision with its request and actual observed effect, expose misleading reports and unintended targets, and show what remains after basis change.

  • Editorial branches for stage19-decision-to-effect: if supported, publish only the recovered discrepancies within the declared scope; if challenged, name missing observations or broken links and retain failures; if inconclusive, name missing bytes/receipts and retain owed status.

  • Disagreement is central: export adapter status, original adapter-reported state, runtime observed state, and expected effect separately. Preserve absent adapter returns on raises; never relabel the legacy runtime fallback as an adapter report. Use a whole-fixture inventory as well as target bytes to identify wrong-target changes.

Explain this chapter with AI

Copy this prompt into ChatGPT, Claude, Gemini, a local model, or another AI.

Apply this chapter with AI

Copy this prompt into ChatGPT, Claude, Gemini, a local model, or another AI.

Part 3 — Give Intelligence a Runtime

The edit that has not happened

Constructed scene. A reviewer proposes changing a cache setting. The patch is in an artifact. A person decides to apply it. The file still contains the old value.

Then a worker returns “done” and nobody opens the file.

The decision might have been well supported, the instruction exact, and the worker’s return normal — none of those tells the person reading the record whether the file changed.

Chapter 18 ended: “A decision can now say what it rested on. It still changes nothing.” This chapter crosses that boundary, but it needs to cross it in pieces.

How does a proposed action become an observed effect?

Decided, requested, performed, observed

The distinction is decided ≠ requested ≠ performed ≠ observed.

Stage Question answered What remains open
Decided What change was chosen, and on what basis? Has anyone asked a worker to do it?
Requested What operation was submitted, by whom, against which state? Did execution begin?
Performed What did the worker do? What evidence of that effect survives?
Observed What did a specified reader find afterwards? Does that observation establish the intended result?

“Performed” is a fact about an effect. A worker’s statement that it performed something is a report about that fact. Keeping the report is useful; promoting it silently into independent evidence is the mistake.

An effect report is another claim. Observation is evidence about the effect. This carries Chapter 18’s distinction across the execution boundary: what the actor says and what an observer reads must remain separately inspectable.

ReAct interleaves model reasoning with actions that obtain information from external sources and environments. That supports treating an action and the information returned from acting as distinct steps. Its benchmark results do not establish durable recording or authorization for this runtime. The mapping to CodeAI’s action boundary is the book’s own. Yao et al., 2023

PAL makes a related separation: a language model generates intermediate programs and an interpreter executes them. Moving computation into an interpreter changes who performs it. It does not, by itself, establish that a proposed file edit meets a user’s objective. That application to effectful software is also the book’s mapping, not a result reported by PAL. Gao et al., 2023

The construction here needs ordinary software around that boundary: a request, a callable worker, a record of its answer, and a reading whose origin is explicit.

Write down the request

The source-inspected ActionRequest separates the operation’s identity from its task, instruction, capability, precondition, and execution attribution. directive_id can associate it with a directive; payload carries application data. idempotency_key is also present, but its consequences belong to Chapter 22. 1

Constructed example using the actual API. The identifiers and proposed value below are illustrative. Creating this object does not execute the edit. The decision_id inside payload is an application convention, not a dedicated field. This fragment assumes before_hash was computed from the intended target under a declared policy. 1

from codeai.adapters import ActionRequest

request = ActionRequest(
    action_id="apply-cache-change",
    task_id="cache-task",
    directive_id="cache-directive",
    capability="write",
    instruction="Set cache.toml TTL to 300 seconds.",
    precondition_hash=before_hash,
    idempotency_key="cache-change-once",
    requested_by="human-reviewer",
    actor_id="file-worker",
    adapter="local-file",
    adapter_id="local-file",
    payload={"decision_id": "choose-cache-change"},
)

Who requested, who acts, and through what adapter are different questions. effective_requester() uses requested_by, falling back to actor_id. effective_adapter_id() uses adapter_id, falling back to adapter. These are attribution strings, not evidence that a named person authenticated or approved the request. 1

The missing connection matters. execute_action does not look up a Stage 18 decision or validate a decision_id from the payload. A request can therefore carry the application’s explanation of which decision it implements without the runtime enforcing that relationship. Do not draw an enforced decision-to-action arrow merely because both kinds of record exist. 2

Where the effect happens

The execution interface is small. This is its source-inspected signature, with the surrounding imports omitted; it is not an executed example. 3

class ExecutionAdapter(Protocol):
    def execute(self, request: ActionRequest) -> ActionResult: ...

The adapter is where the side effect belongs. The runtime supplies an explicit request and receives an ActionResult. That result can carry a status, timestamps, transcript, stdio, exit code, and state-related fields. ActionStatus has SUCCEEDED, FAILED, and DENIED; there is no separate “objective verified” member. 4

On a new action, the source-inspected order is as follows. The existing-result branch returns earlier than the main execution path. 2

Order Operation Meaning at this boundary
First Append action.requested Preserve what was submitted
Then Look up a prior result by key May return a reused result; Chapter 22 must examine this
On a new execution Call policy.require The authority boundary, developed in Chapter 20
Before the adapter Read _current_state_hash() and compare a supplied precondition Compare the planning state with an available current reading
Effect Call adapter.execute(request) Hand control to the worker
After return Read _current_state_hash() again; finalize the result Combine the worker’s report with runtime bookkeeping
Last Append action.completed Preserve the completed result record

The boundary as a flow — the report and the reading travel separate paths:

    flowchart TD
    P["proposal<br/><i>decided, not yet requested</i>"] --> R["action.requested<br/><i>recorded before execution</i>"]
    R --> AD["execution adapter<br/><i>the effect happens here</i>"]
    AD --> W["world / target state<br/><i>changed, or not</i>"]
    AD --> REP["adapter report<br/><i>status + claimed state</i>"]
    W --> OBS["runtime observation<br/><i>independent reading, declared scope</i>"]
    REP --> RES["action.completed<br/><i>report and observation kept apart</i>"]
    OBS --> RES
    EXP["expected effect<br/><i>what was intended</i>"] -.->|"compared outside<br/>the action contract"| RES
  

The precondition comparison runs only when both the request’s hash and the current reading are non-null. A missing resolver therefore leaves a supplied precondition unenforced. Also, the actual before reading is used for comparison but is not stored in a separate before-observation field. The request retains the expected before hash. 2

An effect can occur before its completion record exists. Chapter 16’s recorded provider experiment exposed an analogous interval: a started attempt without a later observation left the effect unknown. That measurement concerns provider calls at its recorded commit, not crash recovery for this action path. 5

Who computed that hash?

At CodeAI 7a0d43b, the runtime really does call its configured state resolver after an adapter returns. It does not merely ask the adapter for a hash. With no resolver, _current_state_hash() returns None. Independence therefore depends on what the caller configured that resolver to read. 6

But at that commit, finalization uses the following expression. This is a source excerpt, not an experimental result. 7

resulting_state_hash=result.resulting_state_hash or resulting_state_hash

The left value comes from the adapter. The right value is the reading passed into finalization by the runtime. A nonempty adapter value wins. A reader of the final resulting_state_hash cannot tell from that field alone which path supplied it; a conflicting runtime reading is not retained there. This is the named defect: adapter-first finalization obscures observation provenance. 7

The Chapter 19 revision, now in CodeAI a1b562a, adds a separate observed_state_hash. Finalization sets it from the runtime reading even when the adapter supplied a different value, including a value in that new field itself. The legacy resulting_state_hash keeps its old behavior for compatibility. This is source-inspected behavior of the revision, not a property retroactively established by the older bundles. 7

Field in the revised result How to read it
state_hash Adapter-supplied value; its meaning depends on the adapter
resulting_state_hash Legacy adapter-first value, with runtime reading as fallback
observed_state_hash Runtime-resolver reading, or None when unavailable

Those meanings follow from the result definition and finalization; none denotes a correctness verdict. 7

Old completion records do not acquire an independent observation when reopened. Deserialization leaves the new field null if it was absent. Reusing a recorded result carries its original observation forward; it does not take a fresh reading of today’s world. 8

This separates two reports. Comparison of an expected resulting state with the observed one stays outside the contract, and an adapter’s successful status stands even when the intended file stayed unchanged. 2

An identity has a scope

The default repository resolver is a policy, not a complete description of the environment. In a repository with a resolvable HEAD, default_repository_state_hash hashes a combination of HEAD, staged diff, unstaged diff, and the sorted untracked path list. It deliberately does not read untracked file contents into that Git-based identity. 9

Consequently, changing the contents of an already-present untracked file need not change this identity. That is a source-inspected consequence of the inputs being hashed, not a measured result in this chapter. The function’s fallback path is different: it walks files and hashes paths and bytes, excluding directories and paths containing .codeai. The two paths should not be described as one universal content hash. 9

Keep three meanings separate: reported result/state is what the acting boundary reports; observed state is what a specified observer reads; expected state/effect is what the operation was intended to produce. Those concepts do not depend on an old field name having perfectly clean historical semantics.

Before comparing hashes, ask what each covers. A session identifier, a target-file digest, and a repository identity are not interchangeable. Even comparable before and after hashes answer only whether the selected representation changed. A change to the wrong file can change a repository identity; an unchanged value can be correct for an operation whose desired state already held.

For the teaching fixture, reading the target’s bytes makes the question concrete. Preserving those bytes also makes later inspection possible. A digest alone cannot show a future reader which line changed.

Read the file after the worker

Constructed, reduced example using actual CodeAI APIs. This disposable fixture shows request, execution, and an independent target read. It is teaching code, not the preregistered Stage 19 demonstration. The adapter deliberately supplies a report that should not be mistaken for a file digest. The revised runtime supplies the separate observed field. 10

from hashlib import sha256
from pathlib import Path
from tempfile import TemporaryDirectory

from codeai.adapters import ActionRequest, ActionResult, ActionStatus
from codeai.domain import Authority, Capability
from codeai.ledger import SQLiteLedger
from codeai.runtime import Runtime

with TemporaryDirectory() as directory:
    target = Path(directory) / "cache.toml"
    target.write_bytes(b"ttl_seconds = 60\n")
    expected = b"ttl_seconds = 300\n"

    def read_hash():
        return sha256(target.read_bytes()).hexdigest()

    class FileAdapter:
        def execute(self, request):
            target.write_bytes(expected)
            return ActionResult(
                action_id=request.action_id,
                status=ActionStatus.SUCCEEDED,
                resulting_state_hash="worker-report",
            )

    runtime = Runtime(SQLiteLedger(), state_resolver=read_hash)
    before_bytes = target.read_bytes()
    request = ActionRequest(
        action_id="edit", task_id="cache", capability="write",
        instruction="Apply the fixture TTL change.",
        precondition_hash=sha256(before_bytes).hexdigest(),
        idempotency_key="fixture-edit", adapter="fixture",
        requested_by="reviewer", actor_id="file-worker",
    )
    result = runtime.execute_action(
        request, authority=Authority(frozenset({Capability.WRITE})),
        adapter=FileAdapter(),
    )
    after_bytes = target.read_bytes()  # outside the acting adapter
    observed_hash = sha256(after_bytes).hexdigest()
    effect_matches = after_bytes == expected

The last comparison is the important line. effect_matches is a fixture-local conclusion about exact target bytes. It does not follow from the returned status, and it does not inspect every other file the worker could have changed.

Remove the adapter’s write in a copy of this example. Then make another copy that writes to a different target, and another that writes only part of the content before raising. Those variations are exactly what the pinned Stage 19 run executes, with the results reported below.

At a1b562a the source caught only RuntimeError from execution and recorded a failed result with a state reading; any other exception class escaped and left the request without a completion. That gap turned out to matter: a ConnectionError after a write let a same-key retry write again, and an uncommitted repair made while editing Chapter 22 now records any adapter exception as FAILED. Two holes remain. A resolver that raises while a failure is being recorded still interrupts the record, and a process that dies mid-effect records nothing at all. The ledger then shows a gap where the completion should be, while the target may already hold a partial write. 2

The real adapter has a narrower answer

OpenCodeExecutionAdapter is an existing implementation of the protocol. By source inspection, it creates a session if needed, sends the instruction through client.prompt, joins response text parts into a transcript, and returns SUCCEEDED. Its state_hash is the session ID. It records the session and transcript, leaving the resulting repository and any task-specific postcondition unread. This account is source inspection, not a live OpenCode run. 11

That is a useful transport boundary, but a session ID is not a repository hash: a prompt response and transcript can describe work without establishing the resulting file state, so the surrounding process still owes the reader an observation with a declared target and scope.

What has actually run

The available action evidence is partial — the capstone demo preserves a denied edit under READ with the file unchanged, a successful edit under WRITE attributed to human-approver, and a separate post-edit file check reported PASS. Its scope includes fake cognition, local checks, a stub state resolver, and clean reopen. It is a demo, not a commit-pinned Chapter 19 experiment. 12

The later capstone composition, run under a protocol frozen before execution, keeps the three origins apart on one successful edit: the adapter returned succeeded with no state of its own, the runtime recorded its resolver reading, and an independent reader matched that reading to the preserved after-bytes. The same run shows the first weakness listed below in action. Its resolver hashed one file, so when a separate branch changed a second file and then failed, the recorded observation never saw the change. 13

Stage 29B has a narrower measured control at 7a0d43b. The rule-resolved answer was submitted as a WRITE effect through the action path, first under READ and then under WRITE. 14

Control Recorded effect status Apply-adapter calls Independently recorded target state
READ denied 0 File absent before and after
WRITE succeeded 1 File absent before; afterwards [cache] and ttl_seconds = 300

These rows come from authority/authority-denied.json and authority/authority-granted.json, not inferred from the status labels. The latter includes the resulting text and its SHA-256. 14

This is evidence that a bounded effect happened in that control and was inspected outside its success label. The dishonest worker, partial write, wrong target, and Stage 18 decision whose basis later moves remain uncovered there. Those joined cases are covered by the pinned Stage 19 run below.

Checking it without trusting it

The Stage 29B verifier checks the authority control within its wider experiment. The wider bundle is not wholly green: its report preserves a failed spend claim, L-D, caused by usage lost on an empty-output failure path. The action control should be cited locally, not used to describe the whole bundle as passing. 15

The pinned decision-to-effect run

The dedicated Stage 19 experiment ran under a protocol frozen before execution, in experiments/applied-ai/evidence/decision-to-effect/2026-09-14-a1b562a/. It starts from a real decision.recorded resting on a supported claim, applies a small patch in a disposable workspace per case, and exports the decision, request, result, invocation receipts, before and after bytes, and hashes. An independent stdlib-only verifier derives the byte comparison from the ledger and the fixture files rather than importing the producer’s success predicate.

Case Adapter status Intended bytes present? Runtime observation == actual bytes?
Honest adapter succeeded yes yes — report string and digest keep their origins
Lying adapter succeeded no, target untouched yes — the recorded observation is the before-hash, exposing the discrepancy
Partial adapter failed (ConnectionError mid-write) 8-byte prefix only yes — same-key retry replays FAILED with 2 requests, 1 effect
Wrong-target adapter succeeded no, target untouched yes — the fixture inventory exposes other.toml with the expected bytes
Decision basis moves later succeeded, bytes as intended yes yes — the decision event stays intact while standing moves basis_intactbasis_changed

The partial case exercises the uncommitted repair described above: a ConnectionError, not a RuntimeError, is recorded as FAILED, and the retry returns the FAILED replay with the adapter invoked exactly once. The stale-basis case uses a genuine decision.recorded from an offline recorded call, refuting evidence landed after the effect, and the standing projection — the old decision and the performed effect are retained while current standing changes.

Seeded corruptions distinguish a broken record from a poorly described effect: an altered observed hash, a worker report substituted for the observation, a removed changed-path inventory entry, and a rewritten decision basis are each rejected by the verifier with the failure named. The run’s stated limits are kept: the runtime resolver watches exactly one target file (everything else comes from the verifier-side inventory, not from any ledger field), fixtures are disposable and local with no concurrency, and a process death between effect and completion still records nothing — only the caught-exception path is exercised.

What this is not

  • Authority belongs to Chapter 20. Requester attribution raises the question of permission; that chapter develops it.
  • Check adequacy remains open. Reading a file establishes its bytes within a time and scope; Chapter 21 asks what check can establish the required behavior.
  • Retry safety belongs to Chapter 22. The interval between an effect and a durable completion is handled there.
  • Containment and production validation are out of scope. A disposable file fixture does not establish either.

Where it is still weak

  1. Observation is configurable. A constant or incomplete resolver can supply a poor reading; the runtime does not validate its coverage. 16
  2. The expected outcome is outside the action contract. No built-in comparison judges the intended resulting state. 2
  3. The historical decision link is conventional. Execution does not validate the decision named in application payload data. 2
  4. Failure evidence can still be incomplete. Adapter exceptions are now recorded, but a raising resolver or a process death can leave the completion absent. 2
  5. The joined evidence is now supplied for the five pinned cases. What remains: a resolver that raises while a failure is being recorded still interrupts the record, and the expected-after comparison stays outside the action contract.

The source-inspected project_decision_standing compares a recorded basis with current claim standing and reports changes. That projection is not an operation that reverses a file write. Linking its report to a particular performed action is the remaining construction, not something a shared task name proves. 17

Do this now

Thirty minutes. Take one proposed edit and follow it as far as the file.

  1. Save the original bytes in a disposable workspace. Write the desired bytes separately, before invoking a worker.
  2. Record who requested the change, who will perform it, and the expected before identity. Keep these distinct from the decision that motivated it.
  3. Run the worker, save its report, and read the target independently. Compare the bytes with the desired result and inspect other changed paths.
  4. Replace the worker with one that only says “done.” Identify which record now exposes the discrepancy and which record still says success.

If you are building with an assistant:

Make one disposable file action inspectable from request to effect.
Record the request before execution. Keep the worker's status and state
report separate from a runtime-controlled reading of the target.
Preserve before and after bytes and declare the hash policy.
Do not infer correctness from status or from a changed hash.
Exercise honest, no-op, partial-write, and wrong-target workers.
Count invocations outside the completion record and inventory changed paths.
Show missing observations as missing. Preserve failures without inventing
a successful outcome. Leave authority, check adequacy, and retry policy
as explicit subsequent work.

Failure modes

  • Treating a decision as an effect. A well-supported choice can still leave the file untouched.
  • Treating “done” as an observation. A worker’s report is evidence of what it reported.
  • Comparing hashes without their policies. Different representations can answer different questions.
  • Calling every changed hash correct. The wrong target can change too.
  • Reading failure as rollback. A worker can fail after making part of its change.
  • Rewriting history when the basis moves. New knowledge changes what is justified now, not what happened then.

What this chapter established

  • Decided, requested, performed, and observed answer different questions; observing a state still leaves correctness to be assessed.
  • The inspected action path records a request, invokes an adapter on its execution branch, and records a result. 2
  • The baseline’s adapter-first resulting hash mixes origins; the Chapter 19 revision separately retains the runtime resolver’s reading. 7
  • The measured authority control includes an actual file write and a separately recorded resulting file, within its fixture. 14
  • The pinned decision-to-effect run in experiments/applied-ai/evidence/decision-to-effect/2026-09-14-a1b562a/ demonstrates honest, lying, partial, wrong-target, and changed-basis cases with an independent verifier that recomputes from the ledger and the fixture bytes and rejects four seeded corruptions.

Next

A callable worker gives the process the ability to change something. An observation gives it a way to learn what was left behind. Neither answers who was allowed to request the change.

Continue with Capability Is Not Authority.

References

  • Shunyu Yao, Jeffrey Zhao, Dian Yu, Nan Du, Izhak Shafran, Karthik Narasimhan, and Yuan Cao. ReAct: Synergizing Reasoning and Acting in Language Models. ICLR, 2023. arXiv:2210.03629.
  • Luyu Gao, Aman Madaan, Shuyan Zhou, Uri Alon, Pengfei Liu, Yiming Yang, Jamie Callan, and Graham Neubig. PAL: Program-aided Language Models. ICML, PMLR 202:10764–10799, 2023. Proceedings.

Implementation sources: CodeAI baseline 7a0d43b36bac09be815138a71b9a0a50be5d20dd for historical finalization; a1b562a for the Chapter 19 observation revision; and an uncommitted working-tree repair that records any adapter exception as FAILED, all distinguished above. src/codeai/adapters.py: ActionRequest, ActionResult, ActionStatus, ExecutionAdapter; src/codeai/runtime.py: Runtime.execute_action, _finalize_action_result, _action_result_from_payload, _append_action_result_event, _current_state_hash, default_repository_state_hash; src/codeai/opencode.py: OpenCodeExecutionAdapter.execute; src/codeai/evidence.py: project_decision_standing. Regression source: tests/test_action_observation.py; existing tests: tests/test_runtime.py, tests/test_opencode.py. Evidence: experiments/applied-ai/evidence/capstone/; experiments/applied-ai/evidence/capstone-composition/; experiments/applied-ai/evidence/execution-ladder/2026-09-14-7a0d43b/, especially authority/; experiments/applied-ai/evidence/working-state/2026-09-13-68f4ba0/; experiments/applied-ai/evidence/decision-to-effect/2026-09-14-a1b562a/ (pinned five-case run with independent verifier). Footnotes mark provenance: source notes refer to inspected code, measurement notes to pinned runs, demo notes to preserved unpinned execution.


  1. Source inspection: src/codeai/adapters.py (ActionRequest). ↩︎ ↩︎ ↩︎

  2. Source inspection: src/codeai/runtime.py (Runtime.execute_action). ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  3. Interface inspection: src/codeai/adapters.py (ExecutionAdapter). ↩︎

  4. Source inspection: src/codeai/adapters.py (ActionResult, ActionStatus). ↩︎

  5. Measured run: experiments/applied-ai/evidence/working-state/2026-09-13-68f4ba0↩︎

  6. Runtime inspection: src/codeai/runtime.py (Runtime._current_state_hash, Runtime.execute_action). ↩︎

  7. Finalization inspection: src/codeai/runtime.py (Runtime._finalize_action_result). ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  8. Source inspection: src/codeai/runtime.py (Runtime._action_result_from_payload, Runtime.execute_action). ↩︎

  9. Source inspection: src/codeai/runtime.py (default_repository_state_hash). ↩︎ ↩︎

  10. Source inspection: src/codeai/runtime.py (Runtime.execute_action, Runtime._finalize_action_result). ↩︎

  11. Adapter inspection: src/codeai/opencode.py (OpenCodeExecutionAdapter.execute). ↩︎

  12. Unpinned demonstration: experiments/applied-ai/evidence/capstone↩︎

  13. Measured run: experiments/applied-ai/evidence/capstone-composition↩︎

  14. Measured run: experiments/applied-ai/evidence/execution-ladder/2026-09-14-7a0d43b↩︎ ↩︎ ↩︎

  15. Report: experiments/applied-ai/evidence/execution-ladder/2026-09-14-7a0d43b/chapter-evidence-report.md↩︎

  16. Source inspection: src/codeai/runtime.py (Runtime._current_state_hash). ↩︎

  17. Source inspection: src/codeai/evidence.py (project_decision_standing). ↩︎