feat(issue-5): shadow watcher that logs would-complete and cannot write

policy.decide is a pure function so the phase-0 scenario fixtures can be fed to
it verbatim. Those seven decisions survived three-reviewer scrutiny, so they are
the contract rather than a fresh set of rules, and the test asserts against
expected_policy.decision directly.

Order inside the policy matters. Transport doubt and staleness invalidate the
evidence itself, so they are answered before anything is inferred from it; a
blocked agent is waiting for a human and can never be completed; attribution
including the execution anchor comes before quiescence, because idle is not
evidence of finishing when a session is reused; and a report must be finalized,
never merely paused, since a debounce is a candidate filter and not an authority.

The shadow runner is never handed the engine, so it has no route to a write.
That is the structural half of P2A-03. The measured half hashes every file under
the registry directory around a full run and asserts it is unchanged, which is a
stronger claim than checking a status field.

Adds the five scenarios P2A-04 requires: a raced double dispatch, a reused
attempt with no anchor, a mid-write report, an unknown status, and a pane
restarted mid-flight. They pass phase 0's own fixture validator, which is what
keeps them honest. The phase-0 test that pinned exactly seven scenarios now
requires the seven to still exist rather than forbidding growth.

Also pins the two phase 1 handoffs as dormant: nothing here spawns a daemon, so
preexec_fn stays unreachable, and nothing calls a frozen shell helper.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrDNJkHmm18uu7n7JwV5Qd
This commit is contained in:
GodopuandClaude Opus 5 committed 2026-09-06 18:56:35 +09:00
1 parent b49af56c3c
commit 964abc6965
10 files changed
+639 -1

No files matched your search

+6 -1
View File
@@ -437,6 +437,10 @@ def test_watcher_manifest_and_all_seven_scenarios_exist():
assert manifest_file.exists(), "watcher manifest.json must exist"
manifest = json.loads(manifest_file.read_text(encoding="utf-8"))
# The phase-0 seven are the contract this file guards. Later phases may add
# scenarios (phase 2a adds five), so this asserts the seven still exist and
# are indexed rather than forbidding growth; validate_watcher_manifest keeps
# every entry, old or new, honest against its file.
expected_scenarios = {
"initial_idle",
"tool_running",
@@ -447,7 +451,8 @@ def test_watcher_manifest_and_all_seven_scenarios_exist():
"restarted_generation",
}
actual_scenarios = set(manifest["scenarios"].keys())
assert actual_scenarios == expected_scenarios, f"Mismatch in watcher scenarios: {actual_scenarios}"
missing = expected_scenarios - actual_scenarios
assert not missing, f"phase-0 watcher scenarios went missing: {missing}"
for sid, sinfo in manifest["scenarios"].items():
sfile = WATCHER_ROOT / sinfo["file"]