mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-15 17:36:54 +00:00
test: publish the preparer's readiness only after its first control read (#1504)
tests/test_late_phase_pause_acceptance.py::test_preparing_acceptance_resume_keeps_pause_under_existing_rails[cancel] failed twice on full-test (windows-latest) on 2026-10-04 with `ok: True, late_phase: settled` where a refusal was expected. The child process published preparer.json as soon as run_historical_acceptance returned "preparing", but review_operation starts the preparation on a background thread and returns without waiting for it, and that thread's first control read is mandatory (_checked_at starts at minus infinity). When the thread ran late, it read the cancellation the parent had just recorded, refused the preparation (stop:historical, nothing dispatched), and the later resume correctly found only settled work. The product behaved as designed; the test raced its own child. The child now publishes readiness only after the preparation thread's first control read returned no stop, so the parent installs its rail after it. No sleep is added and no assertion changes. With a 1 s delay injected before the preparation's work and 1.5 s after the cancellation (a scratch copy only), the unchanged test fails with the exact Windows result and the changed one passed all six rail cases three times; the module passes 15 of 15 without delays. Root cause found by a gpt-6-astra scout lane; the first-read-only assertion is a refinement so later control reads are passed through untouched.
This commit is contained in:
parent
13657849ce
commit
423738f399
1 changed files with 13 additions and 1 deletions
|
|
@ -120,7 +120,7 @@ def test_pause_defers_an_unsent_automatic_late_review_until_resume(late, tmp_pat
|
|||
|
||||
|
||||
_PREPARER = '''
|
||||
import json, sys, time
|
||||
import json, sys, threading, time
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from ouroboros import acceptance_late, review_operation
|
||||
|
|
@ -129,12 +129,24 @@ from ouroboros.llm import LLMClient
|
|||
root, task_id, output = Path(sys.argv[1]), sys.argv[2], Path(sys.argv[3])
|
||||
acceptance_late._historical_writer_live = lambda *_a, **_kw: True
|
||||
review_operation._CONTROL_RECHECK_SEC = 3600.0
|
||||
# Retaining the pointer does not mean the worker has finished its first control
|
||||
# read. Publish readiness only after that read, before the parent installs a rail.
|
||||
ready = threading.Event()
|
||||
control = review_operation.ReviewOperation.control
|
||||
def checked_control(self):
|
||||
result = control(self)
|
||||
if not ready.is_set():
|
||||
assert result is None, result
|
||||
ready.set()
|
||||
return result
|
||||
review_operation.ReviewOperation.control = checked_control
|
||||
LLMClient.chat = lambda *_a, **_kw: (_ for _ in ()).throw(AssertionError("unexpected paid send"))
|
||||
ctx = SimpleNamespace(task_id=task_id, task_attempt=1, drive_root=root, budget_drive_root=root,
|
||||
task_metadata={"root_task_id": task_id, "budget_drive_root": str(root)}, event_queue=None, pending_events=[])
|
||||
row = load_task_result(root, task_id, strict=True)
|
||||
result = acceptance_late.run_historical_acceptance(ctx, task_id=task_id,
|
||||
debt_id=row["acceptance_debt"]["debt_id"], automatic=True)
|
||||
assert ready.wait(10), 'preparation worker did not reach its control wait'
|
||||
temporary = output.with_suffix('.tmp')
|
||||
temporary.write_text(json.dumps(result), encoding="utf-8")
|
||||
temporary.replace(output)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue