fix(issue-23): both ack waits share one contract - the wrapper's status bypass is gone; the G9 guard removes only the docstring node
Review round 3 (planner-reviewer-codex-01, reviewer-opencode-01): R1 blocking, R2 and the "ONE builder" wording non-blocking. All three reproduced; none rebutted. R1. _wait_started still returned 0 as soon as the record left `pending`, ahead of the shared predicate, so `error`, `cancelled` and another dispatch's `running` all acknowledged an injection that create's delegate_wait_started rejects - the two contracts the ack-finding brief said must not diverge. Both reviewers reproduced it with the real producer and no record manipulation; my S7n had hidden it by resetting the record to `pending`. The bypass and the console grep are gone. The wrapper's evidence is now delegate_started_recorded alone, the same call create makes. For a remote agent - no shared disk, so no line in events.log - the predicate reads the subscriber's audit record of the `started` it received. That record is attributable by construction: the subscriber verifies the HMAC and drops any event whose data.dispatch_token differs from the job's expected token before it writes anything. The token itself cannot go into the record (the audit redactor masks token-named keys), so the subscriber now stamps a boolean dispatch_verified, and with a token demanded only a verified record counts; an untokened legacy record fails closed. The registry status is never evidence; a terminal job is preserved by the terminal logic, not turned into an acknowledgement. Tests: W1/W2/W3 are the reviewers' three negatives and the positive, real producer, record left exactly as it produced it; W4 runs create's wait and the wrapper's wait side by side on all four and requires the same answer; W5 pins the remote route (a verified record counts, an unverified or non-started one does not); W6 drives the real _Watcher and shows it stamps the flag on a matching started and records nothing for a mismatch; S7n no longer resets the record. test_issue14's "registry ack over unconfirmed inject" produced its ack with a bare status flip - the very bypass - and now produces it the way an agent does, publish_event with the dispatch token; its scenario and every other assertion are unchanged. test_issue18's remote e2e accepted the status messages and now expects the shared one. R2. _code_only blanked a docstring's whole line range, so code sharing that line - `"""doc"""; call()` - vanished from the scan. It now removes only the docstring node from the AST and unparses the rest (comments drop out with it, code strings stay); test_g9b pins the four same-line shapes. The builder comment claimed one builder for create and the wrapper; the wrapper keeps _pub_cmd and S7l pins the two equal argument-for-argument. The comment now says that. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
4805fd4511
commit
087eacaf21
7 files changed
+250
-72
No files matched your search
@@ -261,13 +261,21 @@ def test_wrapper_honours_registry_ack_over_unconfirmed_inject(tmp_path, mock_her
|
||||
time.sleep(0.1)
|
||||
assert jid, "job was not registered"
|
||||
|
||||
# The agent acknowledges the job out of band, as codex does via publish_event.
|
||||
ack = subprocess.run(
|
||||
[sys.executable, str(REGISTRY_PY), "--registry-dir", str(reg_dir),
|
||||
"update", "--job", jid, "--status", "running"],
|
||||
capture_output=True, text=True, cwd=str(tmp_path), env=env,
|
||||
# The agent acknowledges the job out of band, as codex does via
|
||||
# publish_event - the real producer, with the dispatch token the wrapper
|
||||
# handed it. (Issue #23 review R1: a bare registry status flip is not an
|
||||
# acknowledgement any more; only the agent's own `started` naming this
|
||||
# dispatch is. publish_event writes that line locally before its wire
|
||||
# publish, so an unreachable broker does not hide it.)
|
||||
tok = json.loads((reg_dir / f"{jid}.json").read_text()).get("dispatch_token") or ""
|
||||
subprocess.run(
|
||||
[sys.executable, str(PUBLISH_PY), "--registry-dir", str(reg_dir), "--job", jid,
|
||||
"--event", "started", "--expect-dispatch-token", tok, "--attempt-id", "1"],
|
||||
capture_output=True, text=True, cwd=str(tmp_path), env=env, timeout=60,
|
||||
)
|
||||
assert ack.returncode == 0, ack.stderr
|
||||
started_lines = [l for l in (reg_dir / f"{jid}.events.log").read_text().splitlines()
|
||||
if '"started"' in l]
|
||||
assert started_lines and tok in started_lines[-1], started_lines
|
||||
|
||||
stdout, stderr = proc.communicate(timeout=90)
|
||||
assert "treating the prompt as delivered and continuing" in stderr, f"ack guard did not fire:\n{stderr}\n{stdout}"
|
||||
|
||||
Reference in new issue
Block a user