fix(issue-33): subscriber exit codes are named on both sides; wait keeps a spent budget at rc 2; G-8 is deterministic
- job_subscriber.py returns only named codes (RC_COMPLETED 0, RC_ERROR 1, RC_NO_EVENT 2, RC_BROKER_UNAVAILABLE 3, RC_WALL_CLOCK 4). The wrapper mirrors them as SUB_RC_* and compares subscriber codes only by name; a test keeps the mirror equal to the Python constants and forbids numeric comparisons of $sub_rc/$sub_exit and numeric `return` in the subscriber. - `delegate-job wait` passed the subscriber's code straight through, so a spent budget would have reached its callers as 4; it is 2 again (B7 fails without the mapping). - tier1 G-8 gave both clocks 0.5 s and raced; the wall clock now runs out first by construction (idle 30 s) and the test asserts RC_WALL_CLOCK. Other subscriber rc asserts use the names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9ec0569401
commit
1673818bb5
6 files changed
+77
-37
No files matched your search
@@ -361,7 +361,11 @@ def test_job_subscriber_cli_contract_snapshot(mam_sandbox):
|
||||
for doc in (" 0 all watched jobs reached", " 1 any watched job reached",
|
||||
" 2 no event for the idle window", " 4 the wall-clock budget"):
|
||||
assert doc in src, doc
|
||||
assert 'return 3' in src, "fatal-error exit code 3 must survive"
|
||||
# Issue #33: codes are named; their values are the frozen contract.
|
||||
assert (job_subscriber.RC_COMPLETED, job_subscriber.RC_ERROR, job_subscriber.RC_NO_EVENT,
|
||||
job_subscriber.RC_BROKER_UNAVAILABLE, job_subscriber.RC_WALL_CLOCK) == (0, 1, 2, 3, 4)
|
||||
assert 'return RC_BROKER_UNAVAILABLE' in src, "fatal-error exit code 3 must survive"
|
||||
assert not re.search(r"^\s+return [0-9]+\s*(#.*)?$", src, re.M), "exit codes are returned by name"
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
@@ -378,17 +382,25 @@ def test_event_to_status_mapping_is_frozen(mam_sandbox):
|
||||
}
|
||||
|
||||
|
||||
def test_wrapper_exit_code_mapping_literals_are_frozen():
|
||||
def test_wrapper_exit_code_mapping_literals_are_frozen(mam_sandbox):
|
||||
wrapper = SKILLS / "multi-agent-mux-delegate-job" / "multi-agent-mux-delegate-job"
|
||||
text = wrapper.read_text(encoding="utf-8")
|
||||
for literal in (
|
||||
'if [[ $sub_rc -eq 0 ]]; then',
|
||||
'elif [[ $sub_rc -eq 1 ]]; then',
|
||||
'elif [[ $sub_rc -eq 3 ]]; then',
|
||||
'if [[ $sub_rc -eq $SUB_RC_COMPLETED ]]; then',
|
||||
'elif [[ $sub_rc -eq $SUB_RC_ERROR ]]; then',
|
||||
'elif [[ $sub_rc -eq $SUB_RC_BROKER_UNAVAILABLE ]]; then',
|
||||
'[ "$sub_rc" = "$SUB_RC_WALL_CLOCK" ] && clock="wall"',
|
||||
'job_status="completed"',
|
||||
'job_status="error"',
|
||||
):
|
||||
assert literal in text, literal
|
||||
# Issue #33: the wrapper's SUB_RC_* mirror job_subscriber's RC_* exactly,
|
||||
# and the wrapper compares subscriber codes only by those names.
|
||||
job_subscriber = _import_delegation(mam_sandbox, "job_subscriber")
|
||||
mirror = dict(re.findall(r"\bSUB_RC_([A-Z_]+)=([0-9]+)", text))
|
||||
assert mirror == {name[3:]: str(getattr(job_subscriber, name))
|
||||
for name in dir(job_subscriber) if name.startswith("RC_")}, mirror
|
||||
assert not re.search(r"\$(sub_rc|sub_exit)\"? +(-eq|-ne|=) +\"?[0-9]", text), "subscriber codes compared by value"
|
||||
|
||||
|
||||
def test_reconcile_terminal_mutation_literals_are_frozen():
|
||||
|
||||
Reference in new issue
Block a user