Skip to content

fix(runtime): recover the published startup diagnostic after the ready budget - #7743

Open
xiaoan17 wants to merge 6 commits into
loopx-project:mainfrom
xiaoan17:codex/effect-runtime-startup-diagnostic-grace
Open

xiaoan17 wants to merge 6 commits into
loopx-project:mainfrom
xiaoan17:codex/effect-runtime-startup-diagnostic-grace

Conversation

@xiaoan17

@xiaoan17 xiaoan17 commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Goal and source

The windows-powershell lane fails intermittently on operations that start the
Effect runtime and wait for it to become ready. One retrievable run of that
lane (1e3401591) failed with

assert 'runtime_startup_timeout' == 'mutation_lock_timeout'

That assertion is the whole subject of this PR: the runtime computed the exact
typed code, and the host reported a generic timeout instead.

The
S2/S12 startup diagnostic checkpoint
of 2026-10-04 established that a live mutation lock must fail with the exact
mutation_lock_timeout code, and explicitly did not qualify Windows.

The gap

The TypeScript runtime publishes its typed startup envelope to stderr when it
gives up. Python reads that envelope only when process.poll() shows the child
has already exited; otherwise it terminates the child at the ready budget and
reports the generic runtime_startup_timeout.

The envelope is published at the moment the child exits, so there is nothing
typed to read while the child is still running: at one second, three seconds
and five seconds the child is still running and stderr holds zero bytes, and
the envelope appears only once it exits, at about 5.8 s, together with exit
code 2.

runtime_startup_timeout is produced whenever the parent does not observe the
child's exit inside the ready loop. The baseline reads the published envelope
only when a poll within that loop reports an exit; after the loop it polls
once more, but only to decide whether to terminate the child, and it then
always reports the generic timeout. The historical run therefore does not
distinguish two situations: the child had not published yet when the budget
expired, or the child had already published and exited and the parent had not
observed that exit before it left the loop. The same job's pytest step ran 439
cases in 5 min 31 s, and the chunk holding the failure took two minutes
against six seconds for the chunk before it, so the runner was heavily loaded —
a plausible contributor to a late publication, not a demonstrated cause.

The repair below covers both situations, so it does not depend on knowing which
one the recorded failure was.

The startup cost itself has ample headroom — measured on the Windows runner at
2f544f9692 and on macOS at d3d9ad209d:

Host Ready startup Live-lock total Diagnostic delivered
Windows (nt) 0.406 s 5.813 s mutation_lock_timeout
macOS (posix) 0.170 s 5.140 s mutation_lock_timeout

STARTUP_READY_TIMEOUT_SECONDS is 15.0 and the TypeScript
MUTATION_LOCK_TIMEOUT_MS is 5,000. Neither budget is the constraint, and
neither is widened here. The lock in this path is held by a live owner, so the
owner-liveness check behaves correctly for it and is not touched.

The repair

loopx/control_plane/effect_runtime.py now gives a runtime that is still
running when the ready budget expires a bounded window in which it may publish
either its locator or its typed diagnostic. The host reads the published
envelope during that window and reports the exact code it carries.

The window is bounded by a budget the repository already owns rather than by a
new number: STARTUP_DIAGNOSTIC_GRACE_SECONDS is one more ready budget, so a
single startup attempt waits at most twice STARTUP_READY_TIMEOUT_SECONDS.
When the window closes with nothing published, the runtime is terminated and
the generic runtime_startup_timeout is reported, so the wait stays bounded.

A runtime that publishes its locator inside the window is now accepted instead
of terminated. Before the repair, a runtime that was merely slow was killed at
the ready budget; that outcome is unchanged in kind, only later in time.

Two consequences a reviewer should read as intended rather than as regressions:

  • The user-visible guidance changes with the code. A caller that receives the
    exact mutation_lock_timeout is now told to repair or reinstall LoopX,
    where it was previously told that startup timed out, because
    loopx/cli_commands/start_goal.py gives runtime_startup_timeout its own
    remediation text and mutation_lock_timeout falls through to the shared
    default.
  • A failed startup now blocks its caller for up to twice the ready budget
    instead of once, and effect_runtime_request retries a startup failure once
    for a retry-safe call. The retry policy is unchanged here. That is the wait
    this startup path adds, not an absolute ceiling for the whole call: the
    startup lock, the Node probe and the outer retry each carry their own cost.

Acceptance evidence

The repair is pinned by a test that reproduces the observed failure without a
Windows runner. With the ready budget shortened and a live mutation lock held
by the current process, the pre-repair host reports the generic timeout, and
the assertion is the one the Windows lane produced:

assert 'runtime_startup_timeout' == 'mutation_lock_timeout'

After the repair the same fixture delivers the exact mutation_lock_timeout,
the published envelope, and the preserved live lock.

A second test pins the other situation the historical record cannot separate:
an envelope the runtime published before the deadline, whose exit the parent's
poll missed, is still read — the parent polls once more after the loop, but
the baseline never reads the capture again. Both tests report the generic
timeout on the baseline.

A third test pins the bound: a runtime that publishes nothing is terminated and
still reports runtime_startup_timeout after exactly one ready budget plus one
grace window.

Validation

uv run --extra test python -m pytest \
  tests/control_plane/test_effect_runtime_integration.py -q                    102 passed, 1 skipped
uv run --extra test python -m pytest \
  tests/control_plane/test_effect_runtime_host_permission.py \
  tests/control_plane/test_effect_runtime_node_probe.py \
  tests/control_plane/test_start_goal_compact_projection.py \
  tests/test_chat_server_cors.py -q                                            201 passed
python examples/control_plane/control-plane-maintainability-ratchet-smoke.py    ok
python examples/semantic-vocabulary-drift-smoke.py                             ok
python examples/docs-governance-smoke.py                                       docs-governance-smoke ok
python scripts/generate_rfc_status_index.py --check                            clean
scripts/check_rfc_bilingual.py --base origin/main --head HEAD                  passed
mkdocs build --strict                                                          built, 0 warnings
uv run --extra test loopx canary premerge --from-git-diff --git-diff-base origin/main
                                                                               17 selected, 0 failures, 0 warnings

The maintainability ratchet caught the first form of this change: the grace
window had pushed _start_runtime past the repository's decision-function
limit. The window now lives in _await_startup_publication, and the ratchet
reports no oversized decision function.

Changed surfaces and boundary notes

  • loopx/control_plane/effect_runtime.py — runtime startup path. No public
    protocol, schema or state vocabulary changes; the diagnostic codes involved
    (runtime_startup_timeout, mutation_lock_timeout) already existed.
  • tests/control_plane/test_effect_runtime_integration.py — the file the
    Windows lane runs explicitly, so both new tests run on that lane.
  • docs/architecture/rfcs/ledger/loopx-overall-roadmap-v0/2026-10-30-s2-s12-startup-diagnostic-grace.md
    and its Chinese mirror, plus the derived status index.

No first screen changed: this is a runtime startup path, and there is no user
interface, screenshot or preview to show for it.

The measurement that motivated the repair is re-derivable rather than taken on
trust: the commit that produced the two figures is reachable as
codex/measurement-archive on the fork, and it raises a deliberate
AssertionError carrying the measurement, because the Windows lane runs pytest
without -s and would otherwise capture the output.

@xiaoan17

Copy link
Copy Markdown
Contributor Author

Browser-lane flake analysis and CI retrigger

The dashboard-browser (2) lane failed once on this branch. It is a transient
render-budget outlier, not a code cause, and the head has been retriggered
with a no-content commit (bf376820d).

What failed. examples/personal-workspace-browser/goal-workspace-m2.mjs
asserts that every loaded lens switch stays inside the frozen 1 s budget:

Loaded lens switches exceeded frozen 1s budget:
733.0120410000673, 511.1724310000427, 764.8517420000862,
1344.8827139999485, 617.4658089999575

Four of the five samples measured 511-765 ms. One sample reached 1345 ms,
about twice the median of its own run.

Why it is not this branch. The same lane, in the same window, on two other
branches:

Run Branch Lens-switch samples (ms)
41051838831 codex/quota-capture-poll-receipt 713, 573, 517, 688, 544
41051265985 codex/context-selection-error-loading 450, 622, 645, 673, 377
41061064996 this branch 733, 511, 765, 1345, 617

The failing sample is outside the distribution the same lane produces. The
scenario renders an in-page fake runtime (page.__loopxRuntime) over
synthetic status and map data, so it never starts the Python control plane or
the managed TypeScript runtime; this branch's change is to the Python runtime
startup path and has no path to the render timing.

Why the budget was not touched. The 1 s budget is frozen, and this entry
does not relabel an earlier failure or relax it. A single sample on a shared
runner is a measurement artefact, not a drift signal.

Why checks also failed. It is a consequence, not a second defect:
dashboard-acceptance is gated on needs.dashboard-browser.result == 'success',
so the failed browser lane skipped it, and the aggregate gate then requires
that job to have succeeded. With the browser lane green, dashboard-acceptance
runs and checks follows.

One condition outside this PR. The six test-shard jobs are queued rather
than running, and the same is true for the shards of two other branches' runs
in the same window (codex/quota-capture-poll-receipt,
codex/context-selection-error-loading). That stall is shared across the
repository's shard lane, not specific to this head; the remaining lanes of this
run completed green, including windows-powershell.

@xiaoan17

Copy link
Copy Markdown
Contributor Author

Browser-lane update: the same scenario failed a second time, on a fresh runner

The retriggered head (bf376820d) failed on the same scenario and the same
frozen assertion, with different numbers. Recording the measurements rather
than only the outcome.

The measurements

Run (UTC) Head Lens-switch samples (ms) Max
11:15:42 codex/pr5677-windows-host-20261030 333, 487, 600, 420, 337 600
11:15:47 codex/pr5677-private-io-20261030 637, 817, 595, 324, 489 817
11:53:03 codex/context-selection-error-location-20261030 (base = current main) 450, 622, 645, 673, 377 673
11:57:56 codex/quota-capture-poll-receipt-20261030 713, 573, 517, 688, 544 713
13:10:40 this branch 733, 511, 765, 1345, 617 1345 — fail
13:32:57 codex/pr5677-review-fixes-20261030 433, 763, 775, 343, 507 775
14:51:19 this branch (retriggered) 736, 521, 872, 977, 1061 1061 — fail

The frozen 1 s budget carries roughly 30% headroom on a healthy runner
(600-817 ms observed) and no margin on a loaded one. Both failures are the
same shape: the first samples sit in the normal range and the tail crosses
1 s.

Why this branch's diff is not the cause

The diff is Python-only (loopx/control_plane/effect_runtime.py, its test,
and the RFC ledger entry). The dashboard-browser lane serves the dashboard
with vite and answers the API inside the page
(examples/personal-workspace-browser/fixture.mjs installApi), so it never
executes the changed Python path, and the managed Effect runtime is not
started by that lane at all — the changed code is not on its execution path.

The shard-2 job was also measurably slower end to end on both runs of this
branch than another branch's shard-2 job in the same window: every scenario
duration is higher (team-evidence 16304/16020 ms vs 15218 ms,
goal-masthead 72439/72858 ms vs 59875 ms, research-history 182016/180078 ms
vs 153404 ms, blocked-notice-settings 7424/7296 ms vs 6185 ms), and the
other two shards of the same run move in both directions — shard 1 was faster
and shard 3 slower. That is the signature of runner load, not of a content
change.

What was done, and what was not

The head was retriggered again (ef93a4591, no content change) so the lane
runs on fresh runners. The frozen budget is not touched and neither failure
is relabelled: the criterion keeps its authority, and this branch does not get
to relax it.

The observation worth the maintainer's attention is a shared-lane property
rather than a property of this branch: a 1 s budget with ~30% headroom
against a render that normally takes 600-800 ms fails on a loaded runner, and
the aggregate checks job then reports dashboard-acceptance must be success because a failed browser lane skips that job. If the frozen criterion
is to stay frozen, the lane needs a runner with a stable performance
envelope; if it is to change, that is a separate decision with its own
evidence, and it is not taken here.

@songoow songoow left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewer: model_agent | GPT-6 Astra Pro | OpenAI | self_reported

REQUEST_CHANGES — 1 项 P2:请修正启动超时的排他性因果解释。 当前实现能保留迟到或已经发布的精确诊断;没有从本轮检查中发现新的 runtime 正确性缺陷。审阅 head ef93a4591e2558a5e7dffe39440ff7ff870efdc9,merge-base 0ad08b15dd7331051c93566075561a390a9cd7e2;已完整阅读6个改动文件、TS发布端和Python/CLI消费者。该head的parent是bf376820,两者与9091a785的tree完全相同(92e9e640…);两个后续commit均只重触发CI。本轮在当前ef93隔离worktree重新执行了2个新增测试、deadline反例和文档检查;此前完整矩阵保留实际bf出处。

动机 / Motivation

受影响的是通过CLI、Chat或其他Python入口启动共享Effect runtime、需要据错误码选择修复动作的操作者。

原先父进程跨过ready截止点时,已发布的mutation_lock_timeout也可能丢失;当前head在有界grace内读取locator或安全错误信封,真实合成反例从通用超时变为精确错误。

Linux真实runtime的迟到错误和有界无输出路径成立;额外等待是明确行为变化,原Windows故障的唯一根因仍不能由一个timeout码推出。

本PR不改变mutation-lock业务语义、任务结算、重试授权或模型成绩;也不要求它证明整个Windows CI、安装矩阵或父roadmap已完成。

改动思路 / Approach

错误码继续由TS发布,Python只负责读取、等待和OS生命周期;本次无需建立第二套故障分类器。 保留现有startup owner中的局部grace与安全解析,修正双语ledger和相关排他性注释;不扩展成通用retry或Windows总资格项目。

这里有两个不同边界:其一,child已经发布并退出,parent在最后一次sleep后跨过deadline而没有补读;其二,child确实需要更长时间才发布。末尾补读能修复前者,grace进一步覆盖后者。新代码实际覆盖了二者,功能收益无需依赖“原Windows失败必定属于第二种”的推断。

等待成本也应直说:原ready阶段15秒之后,现在可以再等15秒。TS mutation-lock常量未变,但默认启动等待阶段确实增加;启动锁、Node probe和外层既有retry仍各有其成本,所以这不是整个调用的绝对30秒上限。正常ready调用仍立即返回。

具体改动 / Changes

关键代码讲解

  1. _published_startup_failure 把旧exited分支的安全解析提取出来;它仍只接受原typed schema/code/message,保留host-permission特殊异常,不把任意Node堆栈或stderr文本当作业务诊断。
  2. _await_startup_publication 在deadline前验证locator,再检查进程和typed envelope;可接受窗口内晚到的locator,也可在child仍活着时转交已经公布的精确错误。无信封而退出仍返回runtime_exited_before_ready;没有任何publication则返回调用方完成最终补读和终止。
  3. _start_runtime 保留原启动锁和Popen owner,将原ready之后分支接入grace;effect_runtime_request.retry_safe仍拥有重试决定,新增helper不授权交易重放。foreign mutation lock也不因等待结束而删除。

总计464新增、13删除,包含局部生产等待/解析、115行测试、259行双语ledger和两个STATUS机械计数。两个新测试分别使用真实TS runtime与live mutation lock验证迟到精确码,以及受控时钟验证无任何输出时仍有界并terminate;测试并非只重复helper内部判断。

既有规格采用docs/architecture/rfcs/ledger/loopx-overall-roadmap-v0/2026-10-04-s2-s12-startup-diagnostic.md,修订0ad08b15dd7331051c93566075561a390a9cd7e2。本次映射标签exact-startup-code、bounded-safe-envelope、foreign-occupant-preserved分别要求精确码、安全有界信封和foreign占用保护;它们不是从新ledger反向定义的验收。当前功能证据覆盖这些要求,但不能据此补写原Windows现场的唯一因果。

对主干的风险 / Risks

P2,新增英文ledger第20–24行及对应中文说明:runtime_startup_timeout不能证明child在15秒内没有退出。 base的ready循环结束后只在poll() is None时执行terminate(),随后无条件抛出generic timeout;即使最后的poll()已经是2,也不会再读stderr。这使“最后一次观察后child退出、parent恢复时已跨过deadline”成为真实可达分支。

我用生产_start_runtime和真实Python child验证这个边界:child先写合法的mutation_lock_timeout信封并退出2;仅parent的时钟/恢复时点受控。相同脚本得到:

可观察量 原base实现 当前head
child最终退出码 2 2
已公布的诊断 mutation_lock_timeout mutation_lock_timeout
返回给调用方的code runtime_startup_timeout mutation_lock_timeout
生产capture读取次数 0 1
自己的startup lock 已清除 已清除

因此,成功样本的约5.8秒耗时和历史generic timeout都不能排除parent错过最后观测;这不是原Windows故障现场的重现,也不证明它实际走了哪一支。请在双语ledger、PR说明及相关排他性注释中保留这个不确定性,分别说明两种边界及grace的实际等待成本。无需为了更正文档另开一个完整历史调查项目,也无需撤销已经验证有效的诊断修复。

当前ef93上重新运行的两个新增测试为 2通过 / 6.20秒;上述deadline反例也重新得到精确码、exit2和startup lock清除,git diff --check、RFC索引、双语差分和docs-governance均通过。同tree的bf376上先前5文件完整矩阵是303通过、1个既有Node取消probe失败;相同最小取消用例在不可变main也失败,bf单跑则通过。该测试与node_probe未改、取消发生在runtime spawn之前,因此不归因于新grace,也不宣称其稳定性已解决。没有把bf的执行日志改写成ef93的全矩阵通过。

我阅读了作者对9091/bf两次浏览器render-budget失败和两个空提交重触发的说明;本PR没有dashboard/browser源码diff。旧run的聚合结果与当前pending CI不是本轮native Windows或浏览器资格证据,不能用来补足未执行项。没有运行整套pytest、真实Windows、安装、PostgreSQL或模型实验。

语义与 CI 对齐

本次复用原typed错误词汇,未增加权限、任务义务或重试owner。新head的诊断行为与既有合同一致;阻塞点是新增说明把可观察结果扩张成了已证明的唯一原因。需要调整的是证据叙述,并保留可覆盖deadline竞争的回归;不应通过放松错误码断言来消除争议。

我的整体评价 / Verdict

小范围启动修复方向合理,真实迟到错误与最终无输出都已有有用的独立验证。精确诊断对操作者和长任务恢复有正向价值,新增等待成本也可清楚说明。但仓库ledger应能指导下一次排障,当前排他性推断会让维护者跳过一个已经复现的分支,因此建议修正后再接受。原Windows失败原因保持未确定;这不等于要求本PR承担全平台或父roadmap总验收。本次检查的 effect_runtime.py、内部启动测试与两份RFC ledger 不改变已渲染的产品页面;无需用截图证明内部行为。 未进行产品页面视觉检查,因为本次没有页面改动。

English verdict: REQUEST_CHANGES — ef93a4591e2558a5e7dffe39440ff7ff870efdc9 has one P2 documentation defect: a generic startup timeout does not establish that the child was still running at the ready deadline. A real-child, controlled-parent boundary probe disproves that exclusive inference while confirming the current implementation preserves the published typed error. Targeted Linux behavior is verified within the stated limits; native Windows and full CI qualification are not claimed.

@xiaoan17

Copy link
Copy Markdown
Contributor Author

The shard-2 pattern is reproduced on another branch

The third attempt failed the same way (814, 792, 612, 607, 1213 ms). The
shard-2 job repeated its shape exactly: 22 scenarios in 788 s, against 644 s
for 27 scenarios in shard 1 of the same run.

That shape is not this branch's. The run of codex/quota-demand-loading-20261030
at 08:12 today failed the same assertion on the same scenario, with the same
split:

Run Branch shard-2 samples (ms) shard-2 total shard-1 total shard-3 total
08:12 codex/quota-demand-loading-20261030 733, 551, 1515, 983, 592 800 s / 22 scenarios 831 s / 27 475 s / 27
13:10 this branch 733, 511, 765, 1345, 617 788 s / 22 721 s / 27 376 s / 27
14:51 this branch 736, 521, 872, 977, 1061 784 s / 22 621 s / 27 464 s / 27
15:31 this branch 814, 792, 612, 607, 1213 788 s / 22 644 s / 27 365 s / 27
13:32 codex/pr5677-review-fixes-20261030 433, 763, 775, 343, 507 731 s / 27 835 s / 27 364 s / 27

A loaded shard-2 runner ends the shard early — the failing assertion aborts
the smoke — and the loaded lens switch is what crosses the frozen budget. The
branch that failed first had no relation to this diff, and this diff is
Python-only in a lane that serves the dashboard with vite and answers the
API inside the page.

For scale, the same scenario on a developer machine measures 128, 134, 156,
106 and 121 ms — the render itself is not near the limit; the CI runner is
about five times slower than that machine.

The head has been retriggered again (2373fe5b8, no content change) so the
lane runs on fresh runners. The frozen budget is not touched, and the earlier
failures are not relabelled.

@xiaoan17

Copy link
Copy Markdown
Contributor Author

Review response: the attribution is corrected, and the missed-exit case is now pinned

The finding is accepted, and the correction is on the branch (4cb54a10e).

What was wrong. The ledger and the PR body stated that
runtime_startup_timeout is produced only when the ready budget expires while
the child is still running, and inferred from that the recorded Windows failure
was a child that had not exited inside 15 s. The source does not support the
exclusive form: the baseline reads the published envelope only when a poll
within the ready loop reports an exit. After the loop it polls once more,
but that poll only decides whether to terminate the child, and the function
then always raises runtime_startup_timeout without reading the capture
again. The recorded run therefore does not distinguish a child that had not
published yet from a child that had published and exited with the parent not
observing it.

What changed.

  • The ledger entry and its Chinese mirror now state both possibilities and
    keep the historical attribution undetermined; the runner load is described
    as a plausible contributor to a late publication, not a demonstrated cause.
    The entry also records that the repair covers both situations, so its
    benefit does not depend on knowing which one the recorded failure was.
  • A second sentence was added to the ledger's "where the diagnostic is lost"
    section: a child that has published and exited leaves the envelope in the
    capture, and the baseline leaves it unread.
  • The added wait is now stated as the startup path's own cost rather than an
    absolute ceiling for the whole call — the startup lock, the Node probe and
    the outer retry each carry their own cost.
  • The PR body carries the same corrections.

New test for the missed exit. test_an_envelope_published_before_the_deadline_is_still_read
pins the case the review demonstrated: the exit lands after the parent's last
in-loop poll, so the parent polls once more after the loop and finds it, but
the baseline never reads the capture again and reports the generic timeout for
an envelope that is already there. The repair reports the exact code. Both
this test and the existing late-publication test report the generic timeout on
the baseline.

Unchanged. The implementation itself is not changed by this response: the
review found no new runtime correctness defect, and the repair already reads
an envelope whether it was published late or published before the deadline.
The frozen budget is not touched, and the earlier failures are not relabelled.

xiaoan17 added 6 commits October 31, 2026 00:59
…y budget

A managed runtime publishes its typed startup diagnostic when it gives up, so
the host could only read the envelope after the child had already exited. On a
loaded runner the child was still running when the ready budget expired, so the
host terminated it and reported the generic runtime_startup_timeout instead of
the mutation_lock_timeout the runtime had already computed. That is the
assertion the Windows lane produced.

Give a runtime that is still running at the ready budget a bounded window to
publish its locator or its typed diagnostic, and read the published envelope
during that window. The window is one more ready budget, so no new budget
enters the code and a startup attempt waits at most twice the ready budget.
When the window closes with nothing published the runtime is terminated and the
generic timeout is still reported.

A runtime that publishes its locator inside the window is now accepted instead
of terminated, and the published envelope stays authoritative for the caller:
mutation_lock_timeout reaches the shared remediation in start_goal.py where
runtime_startup_timeout had its own text.

Signed-off-by: xiaoan17 <anbc1224@gmail.com>
Record the gap the repair closes, the two acceptance points and how they are
met, what the entry deliberately does not establish, and the validation that
pins the behaviour. The repair is a diagnostic improvement rather than a
correctness repair, and the entry does not qualify Windows CI as passing.

Signed-off-by: xiaoan17 <anbc1224@gmail.com>
The dashboard-browser (2) lane failed once on a single lens-switch sample:
1345 ms against the frozen 1 s budget, while the other four samples in the same
run measured 511-765 ms. The same lane on two other branches in the same
window measured 377-765 ms, and the failing run's `checks` failure is its
consequence: a failed browser lane skips dashboard-acceptance, and the
aggregate gate then requires that job to have succeeded.

The budget is frozen and stays as it is. The smoke renders an in-page fake
runtime over synthetic status and map data, so this branch's Python runtime
startup change has no path to the render timing. No content change.

Signed-off-by: xiaoan17 <anbc1224@gmail.com>
The same scenario failed again on a fresh runner. The loaded lens switches
measured 736, 521, 872, 977 and 1061 ms against the frozen 1 s budget; the
first attempt measured 733, 511, 765, 1345 and 617 ms. Other branches' runs
of the same lane in the same window measured maxima of 673, 713, 775 and
817 ms, so the budget carries about 30% headroom on a healthy runner and none
on a loaded one.

This branch's diff is Python-only. The dashboard-browser lane serves the
dashboard with vite and answers the API inside the page, so it never executes
the changed Python path, and the managed Effect runtime is not started by that
lane. The shard-2 job ran about 20% slower end to end than another branch's
shard-2 job in the same window, with every scenario duration higher, which is
the signature of a loaded runner rather than a code cause.

The frozen budget is not touched, and neither failure is relabelled. No
content change.

Signed-off-by: xiaoan17 <anbc1224@gmail.com>
…elsewhere

The third attempt failed the same way (814, 792, 612, 607, 1213 ms), and the
shard-2 job repeated its shape exactly: 22 scenarios in 788 s, against 644 s
for 27 scenarios in shard 1 of the same run.

That shape is not this branch's. The run of codex/quota-demand-loading from
08:12 today failed the same assertion on the same scenario (733, 551, 1515,
983, 592 ms) with the same split: 22 scenarios in 800 s for shard 2 against
831 s for 27 scenarios in shard 1, and 475 s for shard 3. A loaded shard-2
runner ends the shard early, and the loaded lens switch is what crosses the
frozen budget.

The budget is not touched and the earlier failures are not relabelled. No
content change.

Signed-off-by: xiaoan17 <anbc1224@gmail.com>
…missed exit

The review is right about the attribution. The baseline reads the published
envelope only when a poll *inside* the ready loop reports an exit; after the
loop it polls once more, but only to decide whether to terminate the child,
and it then always reports the generic timeout. So the recorded failure does
not distinguish a child that had not published yet from a child that had
published and exited without the parent observing it, and the entry claimed
the first one as established.

State both possibilities and keep the historical attribution undetermined. The
repair covers both, so it does not depend on which one the recorded failure
was: a late publication is read inside the grace window, and an envelope that
was already published is read without waiting for the child to exit.

Pin the second case with a test. The exit lands after the parent's last
in-loop poll; the baseline reports the generic timeout for an envelope that
is already in the capture, and the repair reports the exact code. Both new
tests report the generic timeout on the baseline.

Also state that the added wait is the startup path's own cost, not an
absolute ceiling for the whole call: the startup lock, the Node probe and the
outer retry each carry their own.

Signed-off-by: xiaoan17 <anbc1224@gmail.com>
@xiaoan17
xiaoan17 force-pushed the codex/effect-runtime-startup-diagnostic-grace branch from 4cb54a1 to ed1c2ae Compare October 30, 2026 17:00

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants