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:
GodopuandClaude Fable 5.1 committed 2026-09-22 16:43:00 +00:00
1 parent 4805fd4511
commit 087eacaf21
7 files changed
+250 -72

No files matched your search

+21 -14
View File
@@ -708,13 +708,14 @@ _G9_PATTERNS = {
def _code_only(src):
"""`src` with comments and docstrings blanked, line structure kept."""
"""`src` as code only: the docstring expression statements are removed from
the AST and the rest is unparsed, which also drops comments (they are not
in the AST). Only the docstring *node* goes, so a statement that shares
its line - a docstring followed by `; call()` - is kept, as are string literals in code
(a path or `source` in a subprocess argv is exactly what must be caught).
Review R2: blanking the docstring's whole line range hid same-line code."""
import ast
import io
import tokenize
lines = src.splitlines(True)
blank = set()
tree = ast.parse(src)
for node in ast.walk(tree):
if isinstance(node, (ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
@@ -722,15 +723,10 @@ def _code_only(src):
first = body[0] if body else None
if (isinstance(first, ast.Expr) and isinstance(first.value, ast.Constant)
and isinstance(first.value.value, str)):
blank.update(range(first.lineno, first.end_lineno + 1))
out = []
for tok in tokenize.generate_tokens(io.StringIO(src).readline):
if tok.type == tokenize.COMMENT:
(r, c), (_, c2) = tok.start, tok.end
lines[r - 1] = lines[r - 1][:c] + " " * (c2 - c) + lines[r - 1][c2:]
for i, line in enumerate(lines, 1):
out.append("\n" if i in blank else line)
return "".join(out)
del body[0]
if not body:
body.append(ast.Pass())
return ast.unparse(tree)
def _g9_offenders(src):
@@ -769,6 +765,17 @@ def test_g9b_the_guard_still_catches_an_invocation_and_ignores_prose():
' return hashlib.sha256(b"x").hexdigest()\n'
)
assert _g9_offenders(prose) == []
# Review R2: code sharing a line with a docstring must still be seen -
# module, one-line body, one-line def, and the last line of a multi-line
# docstring. Only the docstring node is removed, never its line.
same_line = [
'"""module doc"""; ensure_subscriber_daemon()\n',
'def f():\n """function doc"""; ensure_subscriber_daemon()\n',
'def f(): """function doc"""; ensure_subscriber_daemon()\n',
'def f():\n """multi\n line"""; ensure_subscriber_daemon()\n',
]
for snippet in same_line:
assert _g9_offenders(snippet) == ["ensure_subscriber_daemon"], snippet
# ==============================================================================