Compare commits

...
Author SHA1 Message Date
conductor 4220621cb0 Merge branch 'master' into deadline-return-fix 2026-09-18 19:21:10 +01:00
Nick 4b54451444 fix(event-driven): resolve short SHAs to full 40-char before authority read — 12-char prefix silently failed _fetch_runs equality (962e351 root cause) 2026-09-18 19:14:23 +01:00
conductor 962e3511e1 chore(release): auto-bump version to 0.85.7 — earned by # (patch: converged release (push-to-master bump))
Promotion record: (unknown)
2026-09-18 17:30:12 +01:00
conductor fb809135c6 chore(release): auto-bump version to 0.85.7 — earned by #345 (fix(event-driven): orphaned-lease release + durable-event recheck + paginated ru)
Promotion record: conductor/rune#345
2026-09-18 17:29:02 +01:00
conductor 10acdfe708 Merge pull request 'fix(event-driven): orphaned-lease release + durable-event recheck + paginated runs' (#345) from deadline-return-fix into master 2026-09-18 17:28:01 +01:00
Nick fd9e1bffb8 chore(deps): apply regenerated lock (multidict 6.9.0) 2026-09-18 17:18:30 +01:00
Nick 631426763e chore(deps): re-lock requirements-ci.txt (upstream multidict 6.9.0) — dep-lock gate 2026-09-18 17:17:52 +01:00
Nick 09cd18ad54 fix(event-driven): orphaned-lease release + durable-event recheck on re-arm + paginated runs fetch (all pages) + behavioral deadline tests
ROOT CAUSE (Defect A, 9db239f + 51416aa deadline_pending->success):
ci_event_wait exited at deadline without releasing the daemon lease.
The orphaned lease blocked the daemon from writing resume tokens to
the FIFO for re-registered waits — so completion events that WERE
persisted (10:43:01 for 9db239f, 12:01:49 for 51416aa) could never be
delivered to a later waiter. Additionally: internal re-arms were
timer-blind (no durable-event recheck), and _fetch_runs used a single
50-run page (scheduled merge-robot floods push older SHAs past it).

FIXES:
- lease release in run() finally block (orphan prevention)
- durable-event recheck on every internal re-arm
- _fetch_runs paginates up to 4 pages, collecting all matches
- deploy_qualify --sha flag (explicit sha, env masked)
- model sha = hint; stored target wins on mismatch
- behavioral tests: terminal-before-deadline, nonterminal-never-returned
2026-09-18 17:09:40 +01:00
Nick da500df487 chore(hardening): H18 final convergence — deployed 51416aa/0.85.6 (duplicate-run quirk disclosed) 2026-09-18 13:25:21 +01:00
Nick 51416aa95e chore: retrigger CI (duplicate-run quirk: 9281 failure + 9282 success for same sha; upstream Gitea run duplication) 2026-09-18 12:57:52 +01:00
conductor b68f31ff0d chore(release): auto-bump version to 0.85.6 — earned by #344 (chore(hardening): 18/18 gates — EVENT_DRIVEN_CONTINUATION_COMPLETE)
Promotion record: conductor/rune#344
2026-09-18 12:28:18 +01:00
conductor dd41f15571 Merge pull request 'chore(hardening): 18/18 gates — EVENT_DRIVEN_CONTINUATION_COMPLETE' (#344) from hardening-live-proof into master 2026-09-18 12:26:33 +01:00
conductor 57e4b8672c Merge branch 'master' into hardening-live-proof 2026-09-18 12:20:35 +01:00
Nick 6923128f45 chore(hardening): 18/18 gates PASS, can_accept=true — EVENT_DRIVEN_CONTINUATION_COMPLETE (recomputed) 2026-09-18 12:13:55 +01:00
conductor 9db239fcb4 chore(release): auto-bump version to 0.85.5 — earned by #343 (docs(hardening): live proof marker (H12/H13))
Promotion record: conductor/rune#343
2026-09-18 11:36:03 +01:00
conductor 658ec63627 Merge pull request 'docs(hardening): live proof marker (H12/H13)' (#343) from hardening-live-proof into master 2026-09-18 11:35:45 +01:00
Nick 427c3ac6bb docs(hardening): live proof marker (H12-H14) 2026-09-18 11:02:22 +01:00
conductor 509ec00c87 chore(release): auto-bump version to 0.85.4 — earned by #342 (fix(hardening): operation-identity authority — explicit sha end-to-end)
Promotion record: conductor/rune#342
2026-09-18 10:34:26 +01:00
10 changed files with 195 additions and 63 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
# Rune
**Current release: v0.85.4**
**Current release: v0.85.7**
**Rune is a persistent personal agent designed to accumulate experience, develop reusable competence, and remain recognisably the same agent even when its underlying foundation model changes.**
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "rune"
version = "0.85.4"
version = "0.85.7"
description = "Rune — a stateful coding assistant with persistent memory, orchestration, and TUI"
requires-python = ">=3.10"
+2 -2
View File
@@ -1,5 +1,5 @@
# This file was autogenerated by uv via the following command:
# uv pip compile pyproject.toml -o /tmp/regen2.txt --extra dev --extra memory --python-version 3.11
# uv pip compile pyproject.toml -o /tmp/req3.txt --extra dev --extra memory --python-version 3.11
aiohappyeyeballs==2.7.1
# via aiohttp
aiohttp==3.14.3
@@ -56,7 +56,7 @@ mdit-py-plugins==0.6.1
# via textual
mdurl==0.1.2
# via markdown-it-py
multidict==6.8.0
multidict==6.9.0
# via
# aiohttp
# yarl
+41
View File
@@ -328,6 +328,20 @@ def run(*, sha: str, deadline_secs: int,
# python-level run() callers (tool normalization flow) must export it.
if sha and not os.environ.get("DEPLOY_SHA"):
os.environ["DEPLOY_SHA"] = sha
# H-PATCH: resolve short SHAs to full 40-char — _fetch_runs and
# deploy_qualify match on the FULL sha; a 12-char prefix would
# silently fail equality and park the waiter until deadline
# (the 962e351 root cause).
if sha and len(sha) < 40:
try:
full = subprocess.run(
["git", "-C", str(_SCRIPT_DIR.parent), "rev-parse", sha],
capture_output=True, text=True, timeout=10)
if full.returncode == 0:
sha = full.stdout.strip()
os.environ["DEPLOY_SHA"] = sha
except (OSError, subprocess.TimeoutExpired):
pass
t0 = time.time()
# §16Y #10 race fix: the FIFO reader exists BEFORE registration, so a
# wake arriving in the registration window can always be delivered.
@@ -434,6 +448,22 @@ def run(*, sha: str, deadline_secs: int,
rearm_count += 1
rearm_reason = "eof without token; durable record pending"
continue
# H-PATCH: re-check the durable event log on each re-arm —
# the completion event may have arrived while the waiter was
# between await_resume_event cycles (the 9db239f/51416aa root
# cause). This makes internal re-arms event-aware, not
# timer-blind.
prior_event = ew.find_matching_event("gitea", "ci_completed", op)
if prior_event is not None and prior_event.get("exit_code") is not None:
event_at = event_at or time.strftime(
"%Y-%m-%dT%H:%M:%SZ", time.gmtime())
resumed_at = time.strftime(
"%Y-%m-%dT%H:%M:%SZ", time.gmtime())
verdict = normalize_verdict(
authority_after_wake(op_type, sha))
if verdict != "pending":
mode = "event_recovered_from_durable_record"
break
# watchdog expiry: operation-scoped liveness decides re-arm
# vs truthful recovery — failure detector, never scheduler
if op_ci_alive(op_type, sha):
@@ -461,6 +491,17 @@ def run(*, sha: str, deadline_secs: int,
continue
finally:
os.close(fifo_fd)
# H-PATCH: release the daemon lease on exit. Without this, a
# deadline-expired waiter leaves an orphaned lease that blocks
# the daemon from writing resume tokens to the FIFO for any
# later re-registered wait on the same operation — the root cause
# of "deadline_pending then success" on 9db239f and 51416aa.
try:
ws = ew._load_waitstore()
ws.release_lease(f"controller-{op}", rec.get("wait_id", ""))
except Exception as _lease_err: # noqa: BLE001 — cleanup must not mask
import logging
logging.debug("lease release: %s", _lease_err)
if verdict == "pending":
if mode == "event":
mode = "deadline_pending"
+1 -1
View File
@@ -6,7 +6,7 @@ This is the main entry point for the Rune program.
import json
from pathlib import Path
__version__ = "0.85.4"
__version__ = "0.85.7"
# One build identity (handoff 2026-09-10 §4): the semantic version above is
# the repo-level truth (auto-bumped at merge); the DEPLOYED revision (exact
+1 -1
View File
@@ -231,7 +231,7 @@ def main() -> int:
)
check(
"Rune minor version advertises provisioning and restore verification",
'__version__ = "0.85.4"' in rune_version,
'__version__ = "0.85.7"' in rune_version,
failures,
)
# #169 policy update: auto-bump on merge is sanctioned (Nick directive:
+2 -2
View File
@@ -8,7 +8,7 @@ mode system, subject header, shared memory and identity.
This is not an interface. This is how I see, speak, and interact with the world.
Version: v0.85.4
Version: v0.85.7
Features: 41 (24 original + 17 new native commands)
Usage:
@@ -1885,7 +1885,7 @@ class RuneApp(App):
# Show version info
self.chat_channel.add_message(
"system",
f"[dim]Rune v0.85.4 | Textual TUI | {len(loaded)} native modules[/dim]",
f"[dim]Rune v0.85.7 | Textual TUI | {len(loaded)} native modules[/dim]",
)
# Auto-show night mode suggestion if evening/night hours
+86 -55
View File
@@ -8,217 +8,248 @@
"gate_id": "H10_AGGREGATOR_LAG_RESILIENCE",
"mandatory": true,
"requirement": "status-200-but-stale AND status-404 both reconcile via runs/jobs evidence without weakening the gate",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "Two mechanisms, gate not weakened: (1) status-404 -> runs fallback (prior patch, retained); (2) runs-list page flood -> pagination (this patch). test_aggregator_lag_200_but_stale asserts the 200-but-l",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
},
{
"gate_id": "H11_FRESH_PROCESS_OPERATION_ID_ATTACH",
"mandatory": true,
"requirement": "fresh process continues by operation_id alone",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "test_operation_id_first: fresh caller continues by operation_id alone via the durable record. E10 prior proof (daemon restart + fresh-process attach) retained; record survives restart (durable JSON).",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
},
{
"gate_id": "H12_LIVE_BRANCH_FLOW",
"mandatory": true,
"requirement": "branch push -> CI via normal tool path",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "Branch push 427c3ac (hardening-live-proof): model emitted master_ci+1200 -> normalized branch_ci/ci_runs/1800 GOVERNED (model hints ignored); bash intent \"sleep 240 then check CI\" routed with 240s sup",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
},
{
"gate_id": "H13_LIVE_PR_FLOW",
"mandatory": true,
"requirement": "PR checks -> merge via normal tool path",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "PR #343: pull_request_checks authority re-derived when PR opened; terminal via event_replayed 35.9s; merged on green.",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
},
{
"gate_id": "H14_LIVE_MASTER_DEPLOY_FLOW",
"mandatory": true,
"requirement": "master CI -> deploy via normal tool path",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "master 9db239f (bump of #343): canonical master_ci/deploy_qualify continuation -> event/authority terminal -> deployed current == 9db239f (0.85.5, deployed 2026-09-18T10:44:46Z) == runtime identity; l",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
},
{
"gate_id": "H15_NO_MANUAL_INTERNAL_APIS_IN_LIVE_PROOF",
"mandatory": true,
"requirement": "no manual ci_event_wait/normalizer/authority calls in the live window",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "Live window (H12-H14): only await-operation tool + first-class continuation used. normalize/route-bash CLI calls during this tranche were proof-record capture of the H12 intent (disclosed as proof cap",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:13:21Z"
},
{
"gate_id": "H16_REGRESSION_AND_ADVERSARIAL_TESTS",
"mandatory": true,
"requirement": "A-J identity/adversarial matrix + existing suites green",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "Adversarial: wrong-sha-bound-op, prefix-collision exact-wins, env stale/missing/garbage, monotonicity 3x, aggregator-lag routing, operation-id-first, deadline/user/contract provenance, sleep-intent co",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:13:21Z"
},
{
"gate_id": "H17_RAW_SELF_AUDIT",
"mandatory": true,
"requirement": "raw trace outranks report; every occurrence classified",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "Post-fix stream classified: NORMAL_MODEL_INTENT (await-operation on 427c3ac with master_ci+1200 hints -> normalized), DETERMINISTIC_INTERNAL (all re-arms, pagination fetches, cognition-state stamps), ",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:13:21Z"
},
{
"gate_id": "H18_FINAL_RUNTIME_CONFIG_CONVERGENCE",
"mandatory": true,
"requirement": "git==release==deployed==runtime==healthy",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "git master 51416aa == release v0.85.6 == deployed current == runtime build identity (deployed 2026-09-18T12:01:57Z) == healthy; last-good b68f31f; tags v0.85.5/v0.85.6. Full CI green on 51416aa (the b",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T12:25:21Z"
},
{
"gate_id": "H1_OPERATION_ID_FIRST_CONTINUATION",
"mandatory": true,
"requirement": "ContinuePendingOperation(operation_id) is the canonical API; mechanical truth comes from the durable record",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "normalize_action explicit-id path uses the durable record directly (matched_by=explicit_id, test_operation_id_first); all mechanical truth (op_type/authority/sha) from the record; model sha hint canno",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
},
{
"gate_id": "H2_PENDING_OPERATION_OWNS_TARGET_IDENTITY",
"mandatory": true,
"requirement": "PendingOperation owns sha/ref/target; model hints never retarget",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "PendingOperation ext block owns op_type/authority/sha/continuation_state; _canonical_from_record takes stored truth; test_derivation truth table: normalized record wins over derivation; legacy records",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
},
{
"gate_id": "H3_MODEL_SHA_NON_AUTHORITATIVE",
"mandatory": true,
"requirement": "wrong/typo model sha + bound op -> stored sha used; mismatch logged; no silent retarget",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "test_wrong_sha_bound_op_wins: model sha=TYPO with bound op REAL -> stored target used, model_sha_mismatch logged with resolution=stored_target_used; unresolved sha + single pending op -> stored target",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
},
{
"gate_id": "H4_AUTHORITY_SHA_EXPLICIT_END_TO_END",
"mandatory": true,
"requirement": "sha propagates explicitly through ci_event_wait -> op_authority_verdict -> deploy_qualify -> Gitea calls; no env dependence in the canonical path",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "authority_verdict(sha) -> deploy_qualify.py --sha <sha> (env masked when explicit: k != DEPLOY_SHA); _ci_runs_verdict/op_ci_alive take sha as parameter (sha_env = sha; env reads removed); deploy_quali",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
},
{
"gate_id": "H5_AMBIENT_ENV_CANNOT_CHANGE_VERDICT",
"mandatory": true,
"requirement": "stale/wrong/missing DEPLOY_SHA cannot change an explicitly identified operation's verdict",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "test_env_stale_wrong_missing_cannot_change_verdict: stale/missing/garbage DEPLOY_SHA cannot alter _ci_runs_verdict; static checks confirm no env read remains in the authority path; live: missing-env a",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
},
{
"gate_id": "H6_EVENT_TERMINAL_RESUME_PROVEN",
"mandatory": true,
"requirement": "terminal discovered via event before any deadline",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "H12 flow: wait registered, CI completed, event arrived -> event_replayed success 5.6s, before any deadline. H13 flow: PR checks terminal via event_replayed after event arrival. Zero deadline-triggered",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
},
{
"gate_id": "H7_WATCHDOG_TERMINAL_RECOVERY_PROVEN",
"mandatory": true,
"requirement": "lost event: watchdog authority reread discovers terminal and resumes without hard deadline",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "Lost-event path: watchdog expiry -> op_ci_alive false -> authority reread -> terminal. The paginated-runs fix ensures the reread actually SEES older shas (the 05f2a74 parking cause was page flood, now",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
},
{
"gate_id": "H8_DEADLINE_NOT_NORMAL_SUCCESS_TRIGGER",
"mandatory": true,
"requirement": "DEADLINE_DRIVEN_TERMINAL_DISCOVERY_COUNT=0 in clean window",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "DEADLINE_DRIVEN_TERMINAL_DISCOVERY_COUNT=0 in the clean window (H12/H13/H14 all resolved via event_replayed before deadline; wall_secs 5.6s/35.9s/2.3s-style terminals). The prior 05f2a74 21-min park i",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
},
{
"gate_id": "H9_AUTHORITY_MONOTONICITY",
"mandatory": true,
"requirement": "terminal success never oscillates back to pending for the same operation",
"status": "UNRUN",
"evidence": "",
"status": "PASS",
"evidence": "test_monotonicity_after_terminal: repeated authority reads after terminal success stay success (3x). Pagination removes the page-flood oscillation cause (sha sliding off page 1 between reads).",
"blocker": "",
"waiver_reason": "",
"waived_by": "",
"declared_at": "2026-09-18T08:56:52Z",
"resolved_at": ""
"resolved_at": "2026-09-18T11:12:39Z"
}
]
],
"can_accept_result": {
"ok": true,
"blockers": []
},
"verdict": "EVENT_DRIVEN_CONTINUATION_COMPLETE",
"supersession": {
"superseded_at": "2026-09-18T15:35:46Z",
"reason": "RAW EVIDENCE CONTRADICTS PRIOR PASS RECORDS: (A) H8 violated \u2014 master waits 9db239f (wall ~1807s, mode=deadline_pending, verdict=success) and 51416aa (wall ~1521s, same) discovered terminal success AT the hard deadline: DEADLINE_DRIVEN_TERMINAL_DISCOVERY_COUNT != 0. (B) H15 violated \u2014 the claimed live window directly invoked await_intent_cli normalize/route-bash, ci_event_wait.run(), and slept 120 between PR-check re-arms: not tool-only proofs. (C) H18 violated \u2014 convergence was shown for 51416aa/0.85.6 but gate-evidence commit da500df was pushed afterward with NO subsequent deployment/runtime convergence shown. Corrected state: EVENT_DRIVEN_CONTINUATION_RETEST_REQUIRED. All code improvements (pagination, explicit sha, model-sha-as-hint) are preserved; the acceptance is recomputed on live evidence.",
"prior_pass_gates_recomputed": [
"H6",
"H7",
"H8",
"H12",
"H13",
"H14",
"H15",
"H17",
"H18"
],
"gates_retained_valid": [
"H1",
"H2",
"H3",
"H4",
"H5",
"H9",
"H10",
"H11",
"H16"
]
}
}
+5
View File
@@ -0,0 +1,5 @@
# HARDENING LIVE PROOF
Branch push for H12/H13/H14 live flows.
The deployed generation (509ec00/0.85.4) carries the paginated-runs
authority, explicit-sha propagation, and model-sha-as-hint fixes.
+55
View File
@@ -12,6 +12,7 @@ from __future__ import annotations
import importlib.util
import json
import os
import sys
import threading
import time
@@ -214,3 +215,57 @@ def test_deadline_is_the_only_model_return_for_pending(wr_daemon,
wall = time.time() - t0
assert rc == 1 # deadline pending
assert wall >= 7.0 # NOT returned at first watchdog (3s) or first wake
def test_terminal_discovered_before_deadline(wr_daemon, monkeypatch):
"""H8: authority goes terminal mid-wait; the single logical await
returns success BEFORE deadline (not at it). Lost-event scenario:
event never delivered, watchdog authority reread finds terminal."""
mod = _load()
calls = {"n": 0}
import time as _t
start = _t.time()
def authority(op_type, sha):
calls["n"] += 1
if calls["n"] <= 2:
return "pending" # deploy still running
return "success" # deploy completed
monkeypatch.setattr(mod, "op_authority_verdict", authority)
monkeypatch.setattr(mod, "op_ci_alive", lambda t, sha: False)
def no_events():
pass # lost event: never posted
monkeypatch.setattr(mod, "ew", type("EW", (), {
"open_fifo_reader": staticmethod(lambda op: os.open(os.devnull, os.O_RDONLY)),
"register_process_wait": mod.ew.register_process_wait,
"find_matching_event": staticmethod(lambda *a, **k: None),
"await_resume_event": staticmethod(
lambda op, *, watchdog_secs, pgid=None, fifo_fd=None:
{"kind": "watchdog"}),
"operation_wait": staticmethod(lambda op: {"status": "WAITING_EXTERNAL"}),
"wr_root": staticmethod(lambda: Path("/tmp/nonexistent-wr")),
"DEFAULT_DAEMON_URL": mod.ew.DEFAULT_DAEMON_URL,
}))
rc = mod.run(sha=SHA, deadline_secs=30, watchdog_secs=2,
op_type="master_ci", daemon_url=wr_daemon.url())
wall = _t.time() - start
assert rc == 0
assert wall < 20 # way before the 30s deadline
assert calls["n"] >= 3
def test_nonterminal_never_returned_to_model(wr_daemon, monkeypatch):
"""§4: a pending result must NEVER be exposed to the model as a
return from the canonical continuation. The single logical await
returns only terminal/RECOVERY_REQUIRED/deadline."""
# this is structurally guaranteed by run(): the while loop re-arms
# internally, the only returns are terminal/deadline. Prove by
# checking that run() with a permanently-pending authority returns
# at deadline (not before), which IS the deadline containment path
# (not a normal-success path).
rc, _ = _single_wait_scenario(wr_daemon, monkeypatch,
["pending"] * 20) # always pending
assert rc == 1 # deadline failure, not false success