Skip to content

Navigation Menu

Sign in
Sign up

fix: let a cold engine finish starting before the launcher gives up - #112

Draft
test1card wants to merge 16 commits into
master from
fix/cold-engine-startup-budget-20260828
Draft

fix: let a cold engine finish starting before the launcher gives up #112
test1card wants to merge 16 commits into
master from
fix/cold-engine-startup-budget-20260828

Conversation

@test1card

@test1card test1card commented Aug 27, 2026

Copy link
Copy Markdown
Owner

What happens today

Measured by running the program on 2026年08月28日 at 00:36, with --mock, on a fresh worktree with no warm caches:

00:36:51 launcher: Engine запущен, PID=82784
00:36:56 launcher: CRITICAL construction failed; phase=engine
00:36:57 launcher: CRITICAL retained all construction owners in HOLD
00:37:01 engine: SafetyManager запущен: состояние=safe_off

Read the last two lines together. The launcher declared the engine failed at 00:36:56. The engine finished starting correctly at 00:37:01 — five seconds after it had been abandoned. Nothing was wrong with the engine.

_wait_engine_ready allowed ten attempts at half a second: a five-second budget. A cold engine needs about ten. It loads interlocks, alarms, channel landmarks, the vacuum guard, sensor diagnostics, the trend predictor, the SQLite writer and the safety broker before it may answer READY — and those are fail-closed startup owners that are supposed to be established first.

When the wait expired the launcher held every construction owner and the operator got a window that never rendered. The program refused to start and said nothing. The standing rule here is that this software must never refuse, because when it refuses the operators wire the hardware outside it and the system loses its ability to see, control or record anything. On a fresh machine this is the operator's first experience.

What this changes

The wait is bounded by a monotonic deadline of one minute — six times the measured cold start, while still reporting a stuck child in about a minute. A deadline rather than an attempt count, so slow polls cannot silently extend it.

Startup progress is now logged rather than passed over in silence, which is what the never-refuse rule asks for: say what is happening.

A dead child or an invalid readiness receipt still fails immediately. Only patience for a healthy, slow start has changed. The replay entry point shared the same five-second default and receives the same correction.

Evidence

tests/launcher/test_launcher_cold_start_budget.py drives the real path: an engine that becomes ready later than the old budget but within the new one must let the launcher proceed. With the production change reverted it fails; three tests redden. The full launcher suite and the documentation gate pass.

Stated limit

This was reproduced on Windows. On Ubuntu 22.04 the same engine reached readiness at attempt 4 of 10 — about two seconds — so the old budget was sufficient there. This fix is not what unblocks the Ubuntu end-to-end run; that is a separate, measured cause.


Written with AI assistance.

Measured by running the program on 2026年08月28日 at 00:36, with --mock, on a fresh
worktree with no warm caches:
 00:36:51 launcher: Engine запущен, PID=82784
 00:36:56 launcher: CRITICAL construction failed; phase=engine
 00:36:57 launcher: CRITICAL retained all construction owners in HOLD
 00:37:01 engine: SafetyManager запущен: состояние=safe_off
Read the last two lines together. The launcher declared the engine failed at
00:36:56. The engine finished starting correctly at 00:37:01, five seconds after
it had been abandoned. Nothing was wrong with the engine.
`_wait_engine_ready` allowed ten attempts at half a second: a five second budget.
A cold engine needs about ten. It loads interlocks, alarms, channel landmarks,
the vacuum guard, sensor diagnostics, the trend predictor, the SQLite writer and
the safety broker before it may answer READY, and those are fail-closed startup
owners that are supposed to be established first.
When the wait expired the launcher held every construction owner and the operator
got a window that never rendered. The program refused to start and said nothing.
The standing rule here is that this software must never refuse, because when it
refuses the operators wire the hardware outside it and the system loses its
ability to see, control or record anything. This was that rule failing in its
most literal form, and on a fresh machine it is the operator's first experience.
The wait is now bounded by a monotonic deadline of one minute — six times the
measured cold start, while still reporting a stuck child in about a minute. The
bound is a deadline rather than an attempt count, so slow polls cannot silently
extend it. Startup progress is now logged rather than passed over in silence,
which is what the never-refuse rule asks for: say what is happening. A dead child
or an invalid readiness receipt still fails immediately; only patience for a
healthy, slow start has changed.
The replay entry point shared the same five second default and receives the same
correction.
The regression drives the real path: an engine that becomes ready later than the
old budget but within the new one must let the launcher proceed. It fails with
the production change reverted.
Measured limit, stated plainly: this was reproduced on Windows. On Ubuntu 22.04
the same engine reached readiness at attempt 4 of 10, about two seconds, so the
old budget was sufficient there and this fix is not what unblocks the Ubuntu
end-to-end run.
Written with AI assistance.

Copy link
Copy Markdown
Owner Author

@codex review this pull request at head dcd8bb59f6a47ecaf85a9318c787979c2f40a5e7.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dcd8bb59f6

i️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cryodaq/launcher.py
remaining_s = deadline - time.monotonic()
if remaining_s <= 0:
break
time.sleep(min(interval_s, remaining_s))

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 27, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the one-minute readiness wait off the Qt thread

When an engine crashes after the UI is running, the QTimer.singleShot callback _do_restart calls _start_engine() synchronously, which reaches this sleep-and-probe loop on the Qt thread. With the new defaults, a slow or stuck replacement can therefore block repainting, tray interaction, the audible down alarm, status timers, and operator shutdown for roughly 60 seconds; use nonblocking timer/worker polling for runtime restarts, or restrict the expanded synchronous budget to construction before app.exec().

AGENTS.md reference: AGENTS.md:L540-L542

Useful? React with 👍 / 👎.

Comment thread src/cryodaq/launcher.py Outdated
Comment on lines +133 to +134
# the exact readiness receipt. The laboratory Ubuntu cold-start measurement
# was about ten seconds, so retain a six-times margin while still reporting a

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 27, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Attribute the cold-start measurement to Windows

The reviewed commit's own evidence says the approximately ten-second --mock cold start was reproduced on Windows, while Ubuntu 22.04 became ready in about two seconds; calling this a laboratory Ubuntu ten-second measurement records both the platform and evidence context incorrectly and may cause later timeout tuning or acceptance work to rely on evidence that was never collected.

AGENTS.md reference: AGENTS.md:L16-L18

Useful? React with 👍 / 👎.

Comment on lines +12 to +13
@pytest.mark.parametrize("replay", [False, True], ids=["live", "replay"])
def test_launcher_waits_past_old_budget_for_exact_cold_start_readiness(monkeypatch, replay: bool) -> None:

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 27, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register the cold-start regression in prevention governance

This commit explicitly corrects a confirmed operator-impacting startup failure and adds deterministic regression tests, but neither new pytest node appears in governance/agent_preventions.yaml under a stable prevention ID with the failure consequence and evidence disposition. Without that registration, the guard can later be removed, skipped, or displaced from default CI without automatically reopening the prevention, so add the required prevention record and guard linkage for this failure mode.

AGENTS.md reference: AGENTS.md:L361-L365

Useful? React with 👍 / 👎.

soak measurement added 2 commits August 28, 2026 03:28
Landed by the batch lander. The lane's own report and the coordinator's
verification are recorded on the pull request.

Copy link
Copy Markdown
Owner Author

@codex review

Head under review: 5f4545fc0e978a40fcd31698aa7b74a111fd1fa8. Please bind your verdict to that exact SHA — the
branch moved with this correction round, so a verdict against the previous head
does not carry.

This round answers the findings you raised on the prior head. The coordinator
verified the lane's claim by reverting the production change and confirming the
new assertions redden, and checked each deleted assertion against its
replacement rather than trusting a net-positive count.

The derived pair was regenerated to a fixed point as the last commit and the docs
gate is green at this head. If a finding is unaddressed rather than fixed, please
name it rather than treating silence as agreement.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5f4545fc0e

i️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cryodaq/launcher.py
)

def _start_engine(self) -> None:
def _start_engine(self, *, wait_for_ready: bool = True) -> None:

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep manual readiness waits off the Qt thread

The correction at this head routes only the automatic _do_restart path through the worker: both manual restart slots still call _restart_engine(), which invokes _start_engine() with this True default. When an operator retries after fixing a configuration error and the replacement is cold or stuck, _wait_engine_ready() therefore blocks the Qt thread for up to the new one-minute budget, preventing repaint, alarms, tray interaction, and operator shutdown; route manual post-app.exec() restarts through the same nonblocking readiness state machine.

AGENTS.md reference: AGENTS.md:L540-L542

Useful? React with 👍 / 👎.

Comment thread src/cryodaq/launcher.py
Comment on lines +3185 to +3186
if not wait_for_ready:
return

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep unverified live replacements visibly down

When an automatic mock/live replacement reaches this early return, its process handle is already published but exact readiness is still pending. The health timer continues running, and unlike replay, _is_engine_alive() has no live-readiness check, so its next tick sees the child process alive, paints the Engine status green, and clears the down banner and audible alarm before the worker verifies the receipt. Make the in-flight live readiness state force health to remain false until _readiness_succeeded completes.

AGENTS.md reference: AGENTS.md:L533-L536

Useful? React with 👍 / 👎.

Comment thread src/cryodaq/launcher.py
LauncherWindow._announce_soak_bridge_turnover(self)
phase = "ui-authority-bind"
LauncherWindow._publish_replay_ui_authority(self)
self._start_engine(wait_for_ready=False)

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve shutdown authority during asynchronous readiness

If the operator quits while an automatic mock/live replacement is waiting asynchronously, this path has already shut down the bridge and left the new child alive. Shutdown then revokes runtime callbacks, so even successful readiness cannot restart that bridge, while _stop_engine() can obtain its required exact shutdown receipt only by dispatching launcher_shutdown through the now-stopped bridge; the command is rejected and launcher shutdown remains in HOLD. Keep a shutdown-capable transport available or explicitly settle the in-flight replacement before revoking its completion callback.

AGENTS.md reference: AGENTS.md:L20-L22

Useful? React with 👍 / 👎.

soak measurement added 2 commits August 28, 2026 11:00
Landed by the batch lander. The lane's own report and the coordinator's
verification are recorded on the pull request.

Copy link
Copy Markdown
Owner Author

@codex review

Head under review: bda91eb5eb3415a6a08d136fa451a85c922f08bf (verified equal to
origin/fix/cold-engine-startup-budget-20260828 at the moment of writing).

Reversion control, run this turn on Ubuntu 22.04. Production change reverted, the round's
tests kept:

PHASE 1 tests only, no production change -> 5 failed, 121 passed
 test_manual_restart_keeps_cold_readiness_wait_off_qt_callback[confirmed-dialog]
 test_live_replacement_health_stays_visibly_down_until_exact_readiness
 test_shutdown_during_runtime_readiness_restores_command_transport
PHASE 2 with production -> 126 passed

Documentation gate: tests/docs 68 passed. No test was deleted, skipped, weakened or
restated; the control applies the production half only and keeps every test.

What I want you to attack. This round is about cold start on the laboratory machine, so the
failure it must not introduce is a start that looks ready before it is. Two questions:

  1. Does health stay visibly DOWN until exact readiness on every path, including the one where
    the operator confirms a dialog — or is there a window where the interface reports ready
    while the runtime is still coming up?
  2. On shutdown during runtime readiness, is the command transport genuinely restored, or
    merely re-created in a state that would accept a command it cannot deliver?

Disclosure: this change was produced and verified with AI assistance.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bda91eb5eb

i️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cryodaq/launcher.py
self._clear_engine_down_banner()
self._data_timer.start()
self._health_timer.start()
LauncherWindow._begin_engine_restart_readiness(

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Mark tray-only manual restarts visibly down

When a tray-only operator confirms the restart dialog, _on_restart_engine skips the visible label update, and this route has already stopped the health timer before beginning the asynchronous readiness wait. The existing tray icon and tooltip can therefore remain green for the full cold-start budget even though the old bridge is down and exact readiness is pending; publish an explicit disconnected/down tray state before returning to the event loop.

AGENTS.md reference: AGENTS.md:L533-L536

Useful? React with 👍 / 👎.

Comment on lines +398 to +401
def send_command(self, command: dict[str, object]) -> dict[str, object]:
assert self.alive, "launcher_shutdown reached a stopped transport"
calls.append("launcher_shutdown")
process.alive = False

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exercise the real shutdown transport

When shutdown races a cold engine before its safe REP endpoint is bound, this test cannot distinguish genuine delivery from local bridge admission: the fake start() only flips a boolean, and the fake send_command() itself kills the process and fabricates the verified receipt. It therefore passes even if the production ZmqBridge accepts or queues launcher_shutdown but never delivers it across the subprocess/ZMQ boundary; exercise the production bridge and engine ingress over loopback and require the exact returned receipt.

AGENTS.md reference: AGENTS.md:L380-L386

Useful? React with 👍 / 👎.

Comment on lines +7317 to +7318
- node: tests/launcher/test_launcher_cold_start_budget.py::test_runtime_restart_keeps_cold_readiness_wait_off_qt_callback
ci_partition: remaining

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register the three new corrective guards

The fresh correction adds guards for manual restart callbacks, visible DOWN state, and shutdown transport restoration, but this prevention record still ends after the original four guards (and the mapping test hard-codes that incomplete set). Consequently, any of these three new regressions can later be removed, skipped, or displaced from default CI without reopening LAUNCHER-COLD-START-READINESS-001; add all three nodes and extend the record's scope/invariant accordingly.

AGENTS.md reference: AGENTS.md:L387-L390

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Correction to my review request above — do not review bda91eb5e yet.

I asked for a review of bda91eb5eb3415a6a08d136fa451a85c922f08bf and presented it as
verified. The remaining partition is red on both platforms, and part of that is my push.

Measured this turn on Ubuntu 22.04 over tests/test_launcher_backoff.py and
tests/e2e/test_d7_4_phase2_acceptance.py:

origin/master (d5a0f315e) 46 passed, 0 failed
this branch, last round reverted 6 failed, 40 passed
this branch as pushed 10 failed, 36 passed

Six nodes were already broken by this branch before my round — all six pass on master, so
the branch causes them:

e2e::test_t3_static_all_five_invalidate_before_start
backoff::test_replay_bridge_attach_failure_settles_verified_child_before_retry
backoff::test_replay_readiness_failure_settles_child_before_scheduling_next_generation
backoff::test_restart_shot_fires_when_still_pending
backoff::test_stale_restart_generation_cannot_consume_a_new_crash_restart
backoff::test_start_engine_has_no_readiness_bypass_and_health_does_not_call_it_directly

Four more came from my round. Reverting only src/cryodaq/launcher.py makes exactly
these four pass again:

e2e::test_t3_real_restart_via_restart_engine
backoff::test_clean_pre_spawn_live_restart_recovery_stays_operator_retryable
backoff::test_manual_live_restart_startup_failure_settles_new_child_before_backoff
backoff::test_manual_replay_bridge_attach_failure_settles_new_child_before_backoff

How I missed them. I verified the round against the test files its brief named —
test_launcher_cold_start_budget.py and test_d7_1b_repair.py — and the documentation gate.
I did not run tests/test_launcher_backoff.py, although the round modified
src/cryodaq/launcher.py, which that file exists to cover. Running the tests a change names
is not the same as running the tests a change can reach. This is the second time today I have
made that mistake; it is now written into the working rules as a pre-push gate over every file
a round touches.

These ten are not cosmetic. They govern what happens to a child engine process across a
restart: settling a failed child before the next attempt, refusing a stale restart generation
that would swallow a new crash's restart, and keeping the operator able to retry. A launcher
that mishandles those does not lose a test — over a week-long run it loses the engine, and
with it the measurement.

A lane is working all ten now. I will post a fresh review request when there is a head that has
actually passed. Please disregard the previous request rather than reviewing a head I have
since found red.

lane and others added 3 commits August 30, 2026 15:18
# Conflicts:
#	docs/CLAIM_CORRECTIONS.md
#	docs/architecture-montana-important.svg
#	docs/current_candidate_metrics.md
#	governance/agent_preventions.yaml
The tuning note beside the raised budget stated a Windows cold start of
about ten seconds and an Ubuntu 22.04 cold start of about two seconds.
Neither was measured. The two-second figure is the design-system target
for the app becoming interactive, which is a different quantity from the
engine emitting its exact readiness receipt, and the ten-second figure
has no source at all. A ten-second cold start also contradicts what the
old five-second budget did: it succeeded.
Replaced with the measurement actually taken on 2026年08月30日, on Windows
with --mock, from a clean slate with every cryodaq process killed between
attempts: three starts, one of which never reached readiness, and both
successes needing attempt 6 of 10, about three seconds of a five-second
budget. The note now says the target platform has not been measured this
way and claims no figure for it.
The guard that pinned the note is rewritten to pin the property rather
than the prose: the note must name the platform, mode, date and sample it
measured, and must not present the untested platform or a design-system
target as a measurement of this receipt. Control: with the Ubuntu claim
put back in place, the guard fails at pytest exit 1 naming that exact
sentence; restored byte-identically afterwards.

Copy link
×ばつ 0.5 s to 120 ×ばつ 0.5 s. A runtime restart no longer performs that wait on the Qt thread: `_start_engine(wait_for_ready=False)` hands it to `_begin_runtime_engine_readiness`, which runs the same already-bounded wait on a worker and rejoins through short `singleShot` ticks, so alarms, repaint and shutdown stay responsive while a replacement engine comes up. Bridge attachment stays on the Qt thread and still cannot happen before readiness proves. **The correction.** The tuning note beside the raised budget asserted a Windows cold start of about ten seconds and an Ubuntu 22.04 cold start of about two seconds. **Neither was measured.** The two-second figure is the design-system target for the app becoming interactive, which is a different quantity from the engine emitting its readiness receipt. The ten-second figure has no source, and it contradicts the old five-second budget succeeding at all. It is replaced with the measurement actually taken on 2026-08-30, on Windows with `--mock`, from a clean slate with every cryodaq process killed between attempts: **three starts, one of which never reached readiness, and both successes needing attempt 6 of 10** — about three seconds of a five-second budget, with the failure above it. The spread is what has to fit, so the bound is set well clear of it rather than trimmed to the observed maximum. The note now states plainly that the target platform has not been measured this way and claims no figure for it. The guard that pinned that note is rewritten to pin the **property** instead of the prose: the note must name the platform, mode, date and sample it measured, and must not present the untested platform or a design-system target as a measurement of this receipt. **Controls, all run at this head, each restored byte-identically afterwards.** | control | result | |---|---| | budget constant put back to 10 (5 s) | **3 failed, 34 passed**, exit 1 — `assert 60.0 <= 7.0` | | readiness wait moved back onto the Qt thread, worker object kept so the mutation is fair | **3 failed, 6 passed**, exit 1 on the three off-thread guards | | unsupported Ubuntu sentence put back into the note | **1 failed**, exit 1, naming that exact sentence | Production file restored to blob `8881e8cc` and then to the corrected blob; verified byte-identical each time. **No test was weakened.** Eight assertions changed in `tests/test_launcher_backoff.py`, and every one became stricter or was replaced by a stricter statement of the new truth: the `_start_engine` signature pin now also pins the parameter's name, keyword-only kind and default; `assert_called_once_with()` became `assert_called_once_with(wait_for_ready=False)`; the `singleShot` counts were corrected to the new real values; and new assertions were added that the bridge is **not** attached and `_restart_pending` is still true while readiness is in flight. No test removed, no skip, no xfail, no threshold loosened. **Local evidence at this head.** `tests/launcher/test_launcher_cold_start_budget.py` + `tests/test_launcher_backoff.py`: **37 passed**. `tests/governance`: **309 passed**. `tests/docs/test_docs_freshness.py`: **68 passed**, after the derived pair was regenerated as the last commit. `ruff check` and `ruff format --check`: clean. The candidate partitions run in CI; they were not run here. Written with AI assistance. Nothing above is reported that the evidence does not support, and the one claim that was not supported is the reason for the second commit. " data-view-component="true"> Copy Markdown
Owner Author

@codex review 9232639

Master is merged in at 9695c43c and master did not contain this fix — it still carried the five-second readiness budget. Two commits sit on top of that merge, and the second one corrects the first.

The change. _wait_engine_ready now takes a monotonic deadline rather than a bare attempt count, logs progress and the attempt it succeeded on, and the budget rises from 10 ×ばつ 0.5 s to 120 ×ばつ 0.5 s. A runtime restart no longer performs that wait on the Qt thread: _start_engine(wait_for_ready=False) hands it to _begin_runtime_engine_readiness, which runs the same already-bounded wait on a worker and rejoins through short singleShot ticks, so alarms, repaint and shutdown stay responsive while a replacement engine comes up. Bridge attachment stays on the Qt thread and still cannot happen before readiness proves.

The correction. The tuning note beside the raised budget asserted a Windows cold start of about ten seconds and an Ubuntu 22.04 cold start of about two seconds. Neither was measured. The two-second figure is the design-system target for the app becoming interactive, which is a different quantity from the engine emitting its readiness receipt. The ten-second figure has no source, and it contradicts the old five-second budget succeeding at all.

It is replaced with the measurement actually taken on 2026年08月30日, on Windows with --mock, from a clean slate with every cryodaq process killed between attempts: three starts, one of which never reached readiness, and both successes needing attempt 6 of 10 — about three seconds of a five-second budget, with the failure above it. The spread is what has to fit, so the bound is set well clear of it rather than trimmed to the observed maximum. The note now states plainly that the target platform has not been measured this way and claims no figure for it.

The guard that pinned that note is rewritten to pin the property instead of the prose: the note must name the platform, mode, date and sample it measured, and must not present the untested platform or a design-system target as a measurement of this receipt.

Controls, all run at this head, each restored byte-identically afterwards.

control result
budget constant put back to 10 (5 s) 3 failed, 34 passed, exit 1 — assert 60.0 <= 7.0
readiness wait moved back onto the Qt thread, worker object kept so the mutation is fair 3 failed, 6 passed, exit 1 on the three off-thread guards
unsupported Ubuntu sentence put back into the note 1 failed, exit 1, naming that exact sentence

Production file restored to blob 8881e8cc and then to the corrected blob; verified byte-identical each time.

No test was weakened. Eight assertions changed in tests/test_launcher_backoff.py, and every one became stricter or was replaced by a stricter statement of the new truth: the _start_engine signature pin now also pins the parameter's name, keyword-only kind and default; assert_called_once_with() became assert_called_once_with(wait_for_ready=False); the singleShot counts were corrected to the new real values; and new assertions were added that the bridge is not attached and _restart_pending is still true while readiness is in flight. No test removed, no skip, no xfail, no threshold loosened.

Local evidence at this head. tests/launcher/test_launcher_cold_start_budget.py + tests/test_launcher_backoff.py: 37 passed. tests/governance: 309 passed. tests/docs/test_docs_freshness.py: 68 passed, after the derived pair was regenerated as the last commit. ruff check and ruff format --check: clean. The candidate partitions run in CI; they were not run here.

Written with AI assistance. Nothing above is reported that the evidence does not support, and the one claim that was not supported is the reason for the second commit.

chatgpt-codex-connector Bot commented Aug 30, 2026
edited
Loading

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026年08月31日T08:36:36.573960Z c4e555d Manual request
i️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 92326392a9

i️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cryodaq/launcher.py
self._data_timer.start()
self._health_timer.start()

LauncherWindow._begin_engine_restart_readiness(

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 30, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Suspend the bridge watchdog during readiness

For an automatic mock/replay restart after the launcher has been running for at least 60 seconds, _data_timer remains active at its 10 ms cadence when this asynchronous wait returns. _poll_bridge_data() then observes the intentionally stopped bridge and calls _replace_bridge_from_watchdog(), which starts the bridge before readiness succeeds; in mock mode this opens the command/data transport prematurely, while in replay mode _publish_replay_ui_authority() rejects the unverified session and latches _bridge_restart_fault, which the readiness-success callback never clears, leaving the recovered child unhealthy and provoking another restart. Stop the data timer during this flight or make the watchdog ignore an owned _runtime_engine_readiness_state.

AGENTS.md reference: AGENTS.md:L470-L471

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

This is red at 92326392, and the cause is the design change, not the test harness. Recording it here before it is fixed, because the finding is worth more than the fix.

What fails

remaining fails on Ubuntu in both workflows, one node per invocation, taken from the failure receipts rather than from a summary line:

invocation 1 tests/e2e/test_d7_4_phase2_acceptance.py::test_t3_static_all_five_invalidate_before_start
invocation 2 tests/e2e/test_d7_4_phase2_acceptance.py::test_t3_real_restart_via_restart_engine

Reproduced by hand at the same head: 2 failed, 16 passed in that file.

Cause 1 — necessary, and not sufficient

This branch adds a third bare restart_epoch = self._runtime_callback_epoch in the manual restart path, earlier in the flow than the two reads master already had. The module's own idiom everywhere else — including _runtime_callback_is_current, which compares against getattr(self, "_runtime_callback_epoch", -1) — is the defaulted read, and this same commit two lines later already uses vars(self).get("_restart_generation", 0). Making the new read consistent removes the AttributeError.

The tests still fail after that, so this is not the real cause.

Cause 2 — the real one

test_t3_real_restart_via_restart_engine calls the real LauncherWindow._restart_engine and asserts bridge.start is called exactly once during that call.

Master attached the bridge inside _restart_engine. This branch defers it: the readiness wait moves to a worker, and bridge attachment, the replay-session bind and the UI authority bind all happen later from a QTimer.singleShot callback. So _restart_engine returns with bridge.start not yet called, and the assertion fails at 0 calls. The observed sequence is:

phase=readiness failure=TypeError -> bounded retry
phase=readiness-reschedule failure=AttributeError -> HOLD

The TypeError appears because the new path calls self._wait_engine_ready() directly, while master reached it only through _start_engine. Readiness waiting moved out from behind that call.

Why this is not a test to update

A restart now completes only if the Qt event loop is pumping. In any context where it is not, the engine comes back up and the bridge never attaches — and the only signal is a log line while _restart_pending stays true. For an unattended week that is a restart that half-completes, which is the first failure mode on this project's list.

Whether the loop is always running when restart is invoked was not established either way. The acceptance suite asserts the opposite contract and is the only thing that noticed. It will not be edited to accommodate production.

What happens next

Two options, and the choice is being made caller-by-caller from the code rather than by preference:

  • A. Keep the off-thread wait and make completion independent of the loop, so _restart_engine still attaches the bridge before returning when no loop is running. The acceptance contract must hold again.
  • B. Take the off-thread wait out of this branch and keep only the budget raise, the monotonic deadline and the progress and attempt logging — the part that was asked for, is proven by controls, and does not touch this contract. The off-thread work then gets its own round with its own evidence.

The 37 guards this branch adds and updates stay green under either.

Written with AI assistance.

Copy link
Copy Markdown
Owner Author

@codex review

Please review exact head 9dd1fd815e6641ac26859b5921ded609024a10c2.

Why this correction is on PR #112: the newly active acceptance selection exposed a pre-existing resource leak in tests/e2e/_zmq_harness.py. The failing test_t3_real_restart_via_restart_engine and the fixture are byte-identical on the previous PR head and on the baseline that was compared. The fixture created an asyncio event loop, stopped its thread, but never called loop.close(). The remaining-suite resource observer therefore found one loop and its two self-pipe sockets after teardown. This is test-harness cleanup, not a weakening of the PR #112 startup contract.

What changed:

  • successful fixture teardown now stops the loop, performs a bounded thread join, fails if the thread remains alive, and then closes the loop;
  • the sentinel setup-failure path uses the same cleanup sequence;
  • a direct lifecycle regression test uses a real event loop and thread and observes the side effects: the thread is dead, the loop is not running, and loop.is_closed() is true.

Coordinator verification on this exact content:

  • Windows focused lifecycle guard plus the real ZMQ loopback: 2 passed;
  • removing only loop.close() was asserted to change the file and made the lifecycle guard fail with pytest exit 1 on loop.is_closed() is True;
  • the file was restored byte-for-byte to blob d24ef6c5ee68ccca04a3c91ed5605fcabce7d3f9, after which the same focused set passed again;
  • Ruff lint, Ruff format check, and git diff --check passed;
  • a standalone privacy scan found no new private-layer names or internal identifiers in the tree delta, commit message, or PR metadata.

An Ubuntu 22.04 implementation run also passed the lifecycle guard plus real ZMQ loopback. Its broader local candidate runner stopped before pytest because that WSL clone lacked an immutable defective commit referenced by an existing prevention receipt; this comment does not claim a green remaining partition.

Please focus on exception paths during fixture setup, double-stop races, Windows Proactor versus Ubuntu selector-loop behavior, and whether the direct guard could remain green while the actual fixture still leaks.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9dd1fd815e

i️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests/e2e/test_zmq_harness_lifecycle.py Outdated
thread = threading.Thread(target=_run_loop, args=(loop,), daemon=True)
thread.start()

_stop_join_close_loop(loop, thread)

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 30, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exercise the fixture instead of only its cleanup helper

When fixture setup raises after the loop thread starts—for example from publisher.start(), bridge.start(), sentinel publication, or bridge polling—pytest never reaches either _stop_join_close_loop call in zmq_harness; the loop and possibly the publisher therefore remain live. This new guard invokes the helper directly, so it stays green while those actual fixture paths still leak; drive the fixture through successful teardown and an injected setup failure and observe its owned resources instead.

AGENTS.md reference: AGENTS.md:L380-L386

Useful? React with 👍 / 👎.

from tests.e2e._zmq_harness import _run_loop, _stop_join_close_loop


def test_stop_join_close_loop_releases_the_real_event_loop() -> None:

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 30, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register the new lifecycle prevention guard

The newly added test_stop_join_close_loop_releases_the_real_event_loop corrects a confirmed resource-leak failure, but a repo-wide search finds the node only at its definition and not in governance/agent_preventions.yaml. Without a stable prevention record linking this guard to default CI and its red/green evidence, the test can later be removed, skipped, or deselected without automatically reopening the failure disposition.

AGENTS.md reference: AGENTS.md:L387-L390

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

@codex review d994288

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d9942889c4

i️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".



@pytest.mark.skipif(sys.platform != "win32", reason="Windows event-loop contract")
def test_fixture_loop_factory_is_selector_capable_on_windows() -> None:

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 31, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register the Windows selector-loop guard

The parent commit registered the other ZMQ harness lifecycle nodes under TEST-RUNTIME-FIXTURE-OWNERSHIP-023, but this newly added Windows false-green regression is absent from governance/agent_preventions.yaml. If this test is later skipped, renamed, removed, or displaced from the Windows CI partition, the prevention record will not automatically reopen; add this exact node and its remaining partition to that record.

AGENTS.md reference: AGENTS.md:L387-L390

Useful? React with 👍 / 👎.

Comment thread docs/current_candidate_metrics.md Outdated
Comment on lines +9 to +10
| Source snapshot tree | `5f22d2d7b1b02f05a6d8c78092e243bbd5522b3c` |
| Source snapshot object manifest SHA-256 | `db65258558e6f9a0d0e4b13603b24c39d43e02ead2de1f37a516c8d3a84d3061` |

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 31, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Regenerate metrics after the final source changes

At d9942889, the generator's current index snapshot has tree 9204becf3fd43bd3918c9d2801dd66b709611fd0 and 1,265 source paths, but this document and the paired SVG still identify tree 5f22d2d7b1b02f05a6d8c78092e243bbd5522b3c and 1,264 paths from the earlier 9232639 regeneration. The later 9dd1fd8, 63d8522, and d9942889 source/test changes therefore leave the advertised current-candidate counts and immutable binding stale; rerun the paired generator after the final content commit.

AGENTS.md reference: AGENTS.md:L445-L450

Useful? React with 👍 / 👎.

Comment thread tests/e2e/test_zmq_harness_lifecycle.py Outdated
del harness, generator
samples.append(_process_resource_count())

assert max(samples) - min(samples) <= 4, f"process resource count grew across fixture cycles: {samples}"

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 31, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject a one-handle-per-cycle leak

With exactly five samples, a deterministic leak of one handle or file descriptor per fixture lifecycle produces values such as [N, N+1, N+2, N+3, N+4], for which this assertion still passes because the range is exactly four. That is the per-cycle owner retention this regression claims to detect, and repeated E2E use can therefore accumulate resources while the guard remains green; make the terminal samples flat or explicitly mutation-test a one-resource-per-cycle leak.

AGENTS.md reference: AGENTS.md:L361-L365

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

@codex review

Exact-head review requested for c4e555d8f69357c64cfd10744fbaedde0e2881c0.

Why this head was pushed:

  • It closes the three findings on the previous head: the Windows selector-loop guard is now registered in the default remaining partition; the resource guard rejects any per-cycle growth; the generated metrics/SVG pair was regenerated after the content commit.
  • Independent coordinator mutation control changed the exact-flat assertion back to the previous max - min <= 4 tolerance. The new +1 resource per cycle regression then failed because no AssertionError was raised. The mutation was removed and the scratch tree returned clean.
  • Exact-head Windows checks: the three corrected lifecycle nodes passed; the complete prevention-registry test file passed 55 passed with PYTHONPATH explicitly bound to this worktree. The first attempt without that binding imported production modules from a different checkout and was discarded as invalid evidence.
  • Native Ubuntu 22.04 qualification on this exact candidate reported the lifecycle, prevention, and freshness closure green, with the Windows-only selector test skipped as designed.
  • A second generator run on a clean Ubuntu exact-head checkout produced no tracked diff. Ruff check/format, changed-path diff, and publication privacy scans are clean.

This is still a draft. Hosted CI and a clean Codex verdict bound to this exact head remain open gates. Please review this exact head for correctness, especially lifecycle ownership and whether the new exact-flat resource assertion can false-green under any platform-specific counter behaviour.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c4e555d8f6

i️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +175 to +178
def test_resource_slope_guard_rejects_one_per_cycle_growth() -> None:
"""The lifecycle guard must reject the exact leak its old range admitted."""
with pytest.raises(AssertionError, match="resource count grew"):
_assert_flat_resource_samples([100, 101, 102, 103, 104])

@chatgpt-codex-connector chatgpt-codex-connector Bot Aug 31, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register the resource-slope mutation guard

This head newly adds a deterministic control for the prior false-green resource tolerance, but a repo-wide rg finds neither this node nor test_repeated_real_fixture_lifecycle_has_flat_resource_slope in governance/agent_preventions.yaml; TEST-RUNTIME-FIXTURE-OWNERSHIP-023 registers the selector and teardown nodes only. Consequently, the exact-flat lifecycle guard or its mutation control can later be removed, skipped, or displaced from default CI without the prevention registry detecting that coverage loss, so register both nodes (and the false-green obligation they enforce) in the required remaining partition.

AGENTS.md reference: AGENTS.md:L387-L390

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@chatgpt-codex-connector chatgpt-codex-connector[bot] chatgpt-codex-connector[bot] left review comments

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /