feat(issue-5): auto-completion gate and durable pending notification
The gate does not re-decide anything. Phase 2a already decides, fail-closed and against contract-pinned vectors; this supplies real evidence to that decision and commits only if three independent gates agree: the global switch is on, the backend has observed evidence that it reaches a quiescent state, and the job itself opted in. Default is off. The backend allowlist is evidence-based rather than optimistic. Inferring "finished" from stillness requires stillness to have been observed, and only three of six backends have that. An operator may narrow the list; a name that was never observed is dropped rather than trusted, so it cannot be widened by configuration. Everything else holds. An unreadable manifest, a report rewritten after finalization, missing evidence for a declared side effect, a policy that raises: all refuse. "Could not decide" is never "done". A late explicit terminal wins, because the boundary refuses the inferred one -- that is the boundary working, not an error in the gate. Notification intent is committed in the same atomic write as the state, so a crash between committing and publishing leaves a record that still says the world has not been told. Recovery re-sends; duplicates are already dropped by sequence. A failed send deliberately leaves the intent pending, because an unsent notification that looks sent is worse than one that is retried. Two fixes this surfaced. Settling used a request that terminal records refuse, which made an owed notification impossible to ever settle; settling is now a status-free bookkeeping request, valid in any state. And the phase 2a guard against shell references was matching hashlib.sha256 as a substring, so it now requires a word boundary. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDNJkHmm18uu7n7JwV5Qd
This commit is contained in:
1 parent
3ea5757cfe
commit
803ba43884
5 files changed
+513
-4
No files matched your search
@@ -691,10 +691,19 @@ def test_g9_loop_package_imports_only_delegation_among_siblings():
|
||||
def test_g9_phase2a_spawns_no_daemon_and_calls_no_frozen_helper():
|
||||
"""The two phase 1 handoffs stay dormant: preexec_fn is unreachable because
|
||||
nothing here spawns, and no frozen lib.sh helper is invoked."""
|
||||
import re
|
||||
|
||||
# `lib.sh` needs a word boundary: a bare substring search also matches
|
||||
# hashlib.sha256, which is how this guard first fired on innocent code.
|
||||
patterns = {
|
||||
"ensure_subscriber_daemon": re.compile(r"ensure_subscriber_daemon"),
|
||||
"preexec_fn": re.compile(r"preexec_fn"),
|
||||
"lib.sh": re.compile(r"\blib\.sh\b"),
|
||||
}
|
||||
for path in sorted((SKILLS / "lib_py" / "loop").glob("*.py")):
|
||||
src = path.read_text(encoding="utf-8")
|
||||
for forbidden in ("ensure_subscriber_daemon", "preexec_fn", "lib.sh"):
|
||||
assert forbidden not in src, "{} references {}".format(path.name, forbidden)
|
||||
for name, pattern in patterns.items():
|
||||
assert not pattern.search(src), "{} references {}".format(path.name, name)
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
|
||||
Reference in new issue
Block a user