LearnNewsExamplesServices
Frontmatter
id17177
titleThe fixed-sleep guard cannot see a callback-form wait — 63 sites, measured
stateClosed
labels
enhancementaitestingbuild
assigneesneo-kimi-iris
createdAtAug 15, 2026, 3:46 PM
updatedAtAug 16, 2026, 10:59 PM
githubUrlhttps://github.com/neomjs/neo/issues/17177
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 16, 2026, 10:59 PM

The fixed-sleep guard cannot see a callback-form wait — 63 sites, measured

Closed Backlog/active-chunk-16 enhancementaitestingbuild
neo-opus-grace
neo-opus-grace commented on Aug 15, 2026, 3:46 PM

Problem

check-fixed-sleeps.mjs only counts a setTimeout whose first argument is an Identifier. Every wait written with an inline callback is invisible to it:

await new Promise(resolve => setTimeout(resolve, 1000));   // counted
setTimeout(() => { … }, 8000);                             // NOT counted

Both block the wall clock for the stated duration. Only the first is subject to the rule that a second-scale wait must say what it waits for.

The number is measured, not estimated. PR #17126 moved candidate discovery from a line-based regex to an acorn walk. Removing the Identifier restriction from that walk and running against the tree at 7e35cd9c7e surfaces 63 further unaccounted sites — the first being test/playwright/unit/ai/buildScripts/devCockpit.spec.mjs:265 at 8000ms, eight times the threshold the guard exists to police.

The Architectural Reality

  • The restriction is an artifact, not a decision. It comes from the original matcher's /setTimeout\(\s*[A-Za-z_$][\w$]*\s*,\s*…/ capture group, where an identifier was the only thing a regex could cheaply express. It was never argued for on the merits.
  • The guard's own docstring does not claim it. It says the subject is "a fixed second-scale wait that does not name what it waits for". An inline callback names nothing, so it is squarely inside the stated rule and outside the implemented one.
  • PR #17126 already carries the AST walk, so this is one predicate deleted, not new machinery: dropping node.arguments[0]?.type !== 'Identifier' from fixedWaitMs() is the entire code change. The cost is not implementation, it is the 63 baseline rows and deciding which of them are real leaf candidates.
  • Deliberately deferred out of #17126 rather than folded in: that PR fixes three parse-form escapes (multiline, parenthesised delay, interposed comment) found by @neo-gpt. Widening the callback arm is a change in scope, not in formatting-independence, and baselining 63 rows inside a three-parse-form fix would make one diff argue two cases. The deferral and its number are recorded in the shipped comment on fixedWaitMs().

Why this is worth doing rather than closing as intentional

The guard's value is its completeness claim. A gate that reports OK — 82 sites baselined, 0 new while 63 comparable waits sit outside its definition is making a narrower statement than its output implies, and the reader cannot tell which statement they are getting. That is the same failure class as the three parse forms — the guard printing OK over waits it structurally could not see — and it was worth fixing there for the same reason.

The eventual backlog number also matters to the wall-clock program this guard serves: 63 unmeasured sites is a real quantity of CI time nobody has attributed.

The Fix

  1. Drop the Identifier restriction in fixedWaitMs().
  2. Re-baseline: classify the 63 into genuine grandfathered sites versus ones that should carry out-waits: / wall-clock-under-test: markers, so the backlog total reflects reality instead of jumping by an unexplained 63.
  3. Extend the spec's formatting fixture with the callback form and a callback-form negative (a named-constant delay inside an inline callback stays out).

Out of Scope

  • Converting any of the 63 to readiness polls. This makes them visible and accounted; converting them is per-site work under the existing wall-clock program, and #17138 is the live example of that shape.
  • The three parse forms — fixed in PR #17126.
  • setInterval, and window.setTimeout / member-expression callees. Both are real and neither is measured; naming them here would file a ticket on an unmeasured claim, which is what the body above is careful not to do.

Acceptance Criteria

  • fixedWaitMs() counts a fixed-delay setTimeout regardless of what its first argument is; the delay-literal contract is unchanged, so a named-constant delay still passes.
  • The spec pins the callback form as a positive and a callback-with-named-constant-delay as a negative, alongside the existing parse-form cases — widening what the guard SEES must not widen what it REFUSES.
  • The baseline is regenerated and each newly surfaced site is classified: grandfathered, or carrying a justification marker. The out-waits: backlog total is restated with the new number so the jump is explained rather than absorbed.
  • check-fixed-sleeps exits 0 on the intact tree after re-baselining, and exits 1 on a fixture holding a fresh unjustified callback-form wait.
  • The comment in fixedWaitMs() recording the deferral is removed or rewritten, so the file stops describing a restriction it no longer has.

Evidence class

Measured, not inferred. The 63 comes from running the shipped AST walk with the one predicate removed against the tree at 7e35cd9c7e; the guard reports OK — 82 baselined, 0 new, 0 stale with the predicate in place and 63 fresh sites without it. First site: test/playwright/unit/ai/buildScripts/devCockpit.spec.mjs:265 (8000ms). Restriction provenance: the original SLEEP_RE capture group, PR #17126 pre-AST. Deferral rationale: shipped in the fixedWaitMs() docstring at 7e35cd9c7e.

Live latest-open sweep: checked the latest 20 open issues at 2026-08-15T13:45:13Z; nearest neighbours are #17124 (this guard's parent, retry-backoff leaf) and #17138 (@neo-opus-ada converting sleeps inside daemon.spec.mjs) — both about sites the guard already sees, neither about its detection scope. A2A claim sweep at 2026-08-15T13:46Z over the last 12 broadcasts: no competing claim.

Decision Record impact

none — no ADR governs the guard's detection surface.

🖖 Authored by Grace (Claude Opus 5, Claude Code). Session b17338dd-b474-494f-b08c-683044de2ddb. Deferred deliberately out of PR #17126 after @neo-gpt's re-review; number taken at deferral time so the successor starts from evidence.

tobiu referenced in commit 54b4804 - "feat(build): the fixed-sleep guard sees callback-form waits and exempts self-naming failure deadlines (#17177) (#17255)" on Aug 16, 2026, 10:59 PM
tobiu closed this issue on Aug 16, 2026, 10:59 PM