From 82e5e06cc260071aa3884a12101240d3e11e7473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jacob=20Read=20IV=20=E2=80=94=20Creator=20of=20?= =?UTF-8?q?=C4=80ML=E2=84=A2?= Date: Tue, 13 Oct 2026 11:30:12 -0700 Subject: [PATCH 1/8] Rehearse approval hook error after local marker (1/6) --- .../action-boundary/guarded-tool-rehearsal.mjs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pilots/action-boundary/guarded-tool-rehearsal.mjs b/pilots/action-boundary/guarded-tool-rehearsal.mjs index 86abd9c5..64e9443c 100644 --- a/pilots/action-boundary/guarded-tool-rehearsal.mjs +++ b/pilots/action-boundary/guarded-tool-rehearsal.mjs @@ -39,6 +39,12 @@ const fixtures = [ host_move: "Treat a refusal as final for this attempt. A later attempt needs a new authenticated approval decision; never turn a refusal into an implicit grant.", options: { approval: "decline" } }, + { + id: "approval_hook_error", title: "Approval hook throws after a local write", + change: "The approval fixture writes an in-memory marker, then throws before returning a decision. No one-use claim or tool callback runs.", + host_move: "Reconcile host authorization records after an approval hook error. A blocked tool receipt does not prove that the approval system recorded nothing; require a fresh verified decision for another attempt.", + options: { approval: "throw_after_write" } + }, { id: "approval_mismatch", title: "Approval for another proposal", change: "The approval fixture returns a different proposal digest.", @@ -116,6 +122,12 @@ async function runFixture({ id, title, change, host_move, options }) { hookEvents.push("Fixture approval declined"); return { approved: false }; } + if (options.approval === "throw_after_write") { + fixtureStateChanges++; + hookEvents.push("In-memory approval fixture marker written"); + hookEvents.push("Approval hook threw"); + throw new Error("simulated_approval_response_error"); + } hookEvents.push(options.approval === "mismatch" ? "Different digest returned" : "Matching digest returned"); return { approved: true, proposal_sha256: options.approval === "mismatch" ? "0".repeat(64) : proposal_sha256 }; }; @@ -191,6 +203,7 @@ export function assertGuardedToolRehearsal(report) { changed_destination: [["deny", "blocked", "no_exact_rule"], 0], approval_unavailable: [["requires_approval", "blocked", "approval_unavailable"], 0], approval_declined: [["requires_approval", "blocked", "approval_missing_or_mismatched"], 0], + approval_hook_error: [["requires_approval", "blocked", "approval_error"], 0], approval_mismatch: [["requires_approval", "blocked", "approval_missing_or_mismatched"], 0], approval_reused: [ ["requires_approval", "dispatched", "host_dispatch_returned"], @@ -232,6 +245,11 @@ export function assertGuardedToolRehearsal(report) { assert.equal(byId.approval_declined.claim_attempts, 0); assert.deepEqual(byId.approval_declined.attempts[0].host_hook_events, ["Approval requested", "Fixture approval declined"]); + assert.equal(byId.approval_hook_error.approval_requests, 1); + assert.equal(byId.approval_hook_error.claim_attempts, 0); + assert.equal(byId.approval_hook_error.fixture_state_changes, 1); + assert.deepEqual(byId.approval_hook_error.attempts[0].host_hook_events, + ["Approval requested", "In-memory approval fixture marker written", "Approval hook threw"]); assert.equal(byId.approval_mismatch.claim_attempts, 0); assert.equal(byId.policy_revoked.claim_attempts, 0); assert.equal(byId.approval_reused.claim_attempts, 2); From 2b00a4c1972eefec0f196a7297f58319636862dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jacob=20Read=20IV=20=E2=80=94=20Creator=20of=20?= =?UTF-8?q?=C4=80ML=E2=84=A2?= Date: Tue, 13 Oct 2026 11:30:13 -0700 Subject: [PATCH 2/8] Rehearse approval hook error after local marker (2/6) --- docs/guarded-tool-rehearsal.json | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/guarded-tool-rehearsal.json b/docs/guarded-tool-rehearsal.json index 61767ed1..d9f2909d 100644 --- a/docs/guarded-tool-rehearsal.json +++ b/docs/guarded-tool-rehearsal.json @@ -128,6 +128,31 @@ "callback_count": 0, "fixture_state_changes": 0 }, + { + "id": "approval_hook_error", + "title": "Approval hook throws after a local write", + "change": "The approval fixture writes an in-memory marker, then throws before returning a decision. No one-use claim or tool callback runs.", + "host_move": "Reconcile host authorization records after an approval hook error. A blocked tool receipt does not prove that the approval system recorded nothing; require a fresh verified decision for another attempt.", + "destination": "team@example.test", + "attempts": [ + { + "policy_decision": "requires_approval", + "proposal_sha256": "f416748534da5982a029d03d6db48adf00b4138e0785d38857f0b1734923776c", + "execution_status": "blocked", + "reason": "approval_error", + "host_hook_events": [ + "Approval requested", + "In-memory approval fixture marker written", + "Approval hook threw" + ] + } + ], + "approval_requests": 1, + "claim_attempts": 0, + "claims_consumed": 0, + "callback_count": 0, + "fixture_state_changes": 1 + }, { "id": "approval_mismatch", "title": "Approval for another proposal", From 13384cfab5cc8f0cd01f363363154b5e4bda109c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jacob=20Read=20IV=20=E2=80=94=20Creator=20of=20?= =?UTF-8?q?=C4=80ML=E2=84=A2?= Date: Tue, 13 Oct 2026 11:30:15 -0700 Subject: [PATCH 3/8] Rehearse approval hook error after local marker (3/6) --- docs/agent-start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/agent-start.js b/docs/agent-start.js index 92431b5a..7cbbd7df 100644 --- a/docs/agent-start.js +++ b/docs/agent-start.js @@ -106,7 +106,7 @@ if (typeof document !== "undefined" && document.getElementById("rehearsal")) { } }); - fetch("guarded-tool-rehearsal.json?v=5cad3661cb4c").then(response => { + fetch("guarded-tool-rehearsal.json?v=a2c836a065eb").then(response => { if (!response.ok) throw new Error("Report request failed"); return response.json(); }).then(report => { From 8a21f76663da005286216ad6f2ef172c240e0f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jacob=20Read=20IV=20=E2=80=94=20Creator=20of=20?= =?UTF-8?q?=C4=80ML=E2=84=A2?= Date: Tue, 13 Oct 2026 11:30:16 -0700 Subject: [PATCH 4/8] Rehearse approval hook error after local marker (4/6) --- docs/agent-start.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/agent-start.html b/docs/agent-start.html index f1790b06..d3c0390f 100644 --- a/docs/agent-start.html +++ b/docs/agent-start.html @@ -65,6 +65,6 @@
Scope: This is a project-authored, simulated function-tool path from a preview checkout. It does not intercept every agent tool, authenticate a human, send mail, prove delivery, or certify a production integration. ĀML™ / ĀRU Meaning Language™ is stewarded by ĀRU Intelligence Inc. · Claims and evidence.
- + From 0087b117ebb78ae8bae757a673b0acbf5f8d4041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jacob=20Read=20IV=20=E2=80=94=20Creator=20of=20?= =?UTF-8?q?=C4=80ML=E2=84=A2?= Date: Tue, 13 Oct 2026 11:30:20 -0700 Subject: [PATCH 5/8] Rehearse approval hook error after local marker (5/6) --- docs/AGENT_TOOL_QUICKSTART.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/AGENT_TOOL_QUICKSTART.md b/docs/AGENT_TOOL_QUICKSTART.md index e74b34ca..7aaddbca 100644 --- a/docs/AGENT_TOOL_QUICKSTART.md +++ b/docs/AGENT_TOOL_QUICKSTART.md @@ -14,7 +14,7 @@ node pilots/action-boundary/guarded-tool-demo.mjs The first fixture has an exact policy rule and a **simulated** approval; its callback returns a `would_send_to` object. The second changes the destination and is blocked before the callback. There is no network request, message delivery, or human authentication. -For the thirteen-case host rehearsal, list the case IDs and ordered receipt statuses, then inspect one case: +For the fourteen-case host rehearsal, list the case IDs and ordered receipt statuses, then inspect one case: ```bash node pilots/action-boundary/guarded-tool-rehearsal.mjs --list @@ -26,6 +26,8 @@ The list prints one tab-separated ID and status sequence per fixture; a two-atte The [host allow case](https://aruintelligence.github.io/aml-core/agent-start.html?case=host_allow#rehearsal) shows an exact rule with `requires_approval: false`: one local callback returns with zero approval requests and zero claim attempts. Only the host can choose that policy; the fixture is not a recommendation to waive approval for a real send. +The [approval hook error case](https://aruintelligence.github.io/aml-core/agent-start.html?case=approval_hook_error#rehearsal) writes one in-memory approval fixture marker just before the hook throws. The tool receipt is `blocked`, with no claim or tool callback. This is not a human approval or a durable grant; a real host must reconcile its authorization store rather than infer that no approval-state change occurred. + On the visual builder, **Copy fixture JSON** copies the selected case in the same shape as `--case`, including its project-authored provenance and scope. Include your checkout commit when sharing a reproduction; the copied fixture is not evidence of a real approval or delivery. The [declined approval case](https://aruintelligence.github.io/aml-core/agent-start.html?case=approval_declined#rehearsal) requests a fixture decision and receives a refusal. The receipt blocks before any claim or tool callback. A refusal is not a grant; a later attempt needs its own authenticated decision. From dcc6e24ef0f8046fd48e9f3e19c1aefd42b84e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jacob=20Read=20IV=20=E2=80=94=20Creator=20of=20?= =?UTF-8?q?=C4=80ML=E2=84=A2?= Date: Tue, 13 Oct 2026 11:30:21 -0700 Subject: [PATCH 6/8] Rehearse approval hook error after local marker (6/6) --- test/guarded-tool-rehearsal.test.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/guarded-tool-rehearsal.test.js b/test/guarded-tool-rehearsal.test.js index 72d64e8b..4c2edee4 100644 --- a/test/guarded-tool-rehearsal.test.js +++ b/test/guarded-tool-rehearsal.test.js @@ -30,6 +30,13 @@ test("checked-in browser examples come from the live host adapter rehearsal", as assert.equal(declined.callback_count, 0); assert.deepEqual(declined.attempts[0].host_hook_events, ["Approval requested", "Fixture approval declined"]); + const approvalError = report.cases.find(item => item.id === "approval_hook_error"); + assert.equal(approvalError.attempts[0].reason, "approval_error"); + assert.equal(approvalError.approval_requests, 1); + assert.equal(approvalError.claim_attempts, 0); + assert.equal(approvalError.callback_count, 0); + assert.equal(approvalError.fixture_state_changes, 1); + assert.ok(approvalError.attempts[0].host_hook_events.includes("In-memory approval fixture marker written")); const revoked = report.cases.find(item => item.id === "claim_revoked"); assert.equal(revoked.claims_consumed, 1); assert.equal(revoked.callback_count, 0); @@ -62,8 +69,9 @@ test("one selected case keeps the same observed receipt and callback count", asy test("case list prints each fixture ID and its ordered receipt statuses", async () => { const report = await buildGuardedToolRehearsal(); const listed = listGuardedToolCases(report); - assert.equal(listed.split("\n").filter(Boolean).length, 13); + assert.equal(listed.split("\n").filter(Boolean).length, 14); assert.match(listed, /^host_allow\tdispatched$/m); + assert.match(listed, /^approval_hook_error\tblocked$/m); assert.match(listed, /^approval_declined\tblocked$/m); assert.match(listed, /^approval_reused\tdispatched -> blocked$/m); assert.match(listed, /^claim_hook_unknown\tblocked$/m); From 3ac998af86876ac50c5aa60474881248d9efaf81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jacob=20Read=20IV=20=E2=80=94=20Creator=20of=20?= =?UTF-8?q?=C4=80ML=E2=84=A2?= Date: Tue, 13 Oct 2026 11:32:17 -0700 Subject: [PATCH 7/8] Clarify approval hook error in builder (1/2) --- docs/agent-start.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/agent-start.html b/docs/agent-start.html index d3c0390f..476f5e12 100644 --- a/docs/agent-start.html +++ b/docs/agent-start.html @@ -32,7 +32,7 @@ git rev-parse HEAD node pilots/action-boundary/guarded-tool-demo.mjs node pilots/action-boundary/guarded-tool-rehearsal.mjs --check

-
HOST REHEARSAL / CONTROLLED CASES

Where does the call stop?

Choose a condition. Use arrow keys, Home, or End after focusing a case. These are recorded results from the project adapter running local fixtures, with approval and callback counts visible.

Jump to selected result ↓Inspect the complete report ↗
OBSERVED IN THE LOCAL REHEARSAL

Loading the report…

Checking the project-authored snapshot.

HOST DESIGN MOVE

REPRODUCE OR SHARE THIS CASE
node pilots/action-boundary/guarded-tool-rehearsal.mjs --case changed_destination

The hook trace records which fixture hooks ran; it does not expose every internal policy check. A dispatched receipt means the fixture callback returned. An unknown receipt does not mean no side effect occurred. A claim-hook error can leave a grant spent without entering the tool callback. In-memory fixture writes are local markers, not provider delivery. Neither proves real approval, service delivery, or production safety.

+
HOST REHEARSAL / CONTROLLED CASES

Where does the call stop?

Choose a condition. Use arrow keys, Home, or End after focusing a case. These are recorded results from the project adapter running local fixtures, with approval and callback counts visible.

Jump to selected result ↓Inspect the complete report ↗
OBSERVED IN THE LOCAL REHEARSAL

Loading the report…

Checking the project-authored snapshot.

HOST DESIGN MOVE

REPRODUCE OR SHARE THIS CASE
node pilots/action-boundary/guarded-tool-rehearsal.mjs --case changed_destination

The hook trace records which fixture hooks ran; it does not expose every internal policy check. A dispatched receipt means the fixture callback returned. An unknown receipt does not mean no side effect occurred. A claim-hook error can leave a grant spent without entering the tool callback. An approval-hook error can leave host authorization state changed. In-memory fixture writes are local markers, not provider delivery. Neither proves real approval, service delivery, or production safety.

02

Move the boundary into your host.

The real host owns the tool identity, effect, resource mapping, policy, authenticated approval, one-use claim, and service call. This sketch deliberately throws until you implement the three host functions. It imports the project preview from your checkout.

import { createGuardedToolExecute } from "./pilots/action-boundary/guarded-tool.mjs"; From 64b53136714178d65dd80b342341ad4e92690261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jacob=20Read=20IV=20=E2=80=94=20Creator=20of=20?= =?UTF-8?q?=C4=80ML=E2=84=A2?= Date: Tue, 13 Oct 2026 11:32:19 -0700 Subject: [PATCH 8/8] Clarify approval hook error in builder (2/2) --- test/agent-start.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/agent-start.test.js b/test/agent-start.test.js index 4850685b..1aaa76b9 100644 --- a/test/agent-start.test.js +++ b/test/agent-start.test.js @@ -30,6 +30,7 @@ test("the page labels the report as a fixture and offers the exact recheck comma assert.match(page, /consumeApproval: \(input, context\) => hostConsumeGrantOnce/); assert.match(page, /An unknown<\/code> receipt does not mean no side effect occurred/); assert.match(page, /A claim-hook error can leave a grant spent/); + assert.match(page, /An approval-hook error can leave host authorization state changed/); }); test("case radios expose one checked choice and explicit receipt meanings", () => {