Repository navigation
Conversation
Contributor
There was a problem hiding this comment.
Code Review
This pull request documents design guidelines for integration test fixtures, emphasizing that they must not install from the npm registry to prevent test flakiness. To enforce this, the redirector test was updated to use a vendored fixture and asserts that no npm install is spawned. Additionally, a fetchWithTimeout helper was introduced to replace standard fetch calls in the test suite. Feedback on the changes suggests improving fetchWithTimeout to combine any caller-provided abort signal with the timeout signal using AbortSignal.any().
Contributor
|
Reviewed; no blockers found. |
This was referenced Sep 28, 2026
This was referenced Oct 5, 2026
This was referenced Oct 5, 2026
kriszyp
force-pushed
the
fix/deflake-redirector-integration-test
branch
4 times, most recently
from
October 27, 2026 22:37
d4b22a0 to
9d605a2
Compare
kriszyp
force-pushed
the
fix/deflake-redirector-integration-test
branch
4 times, most recently
from
October 29, 2026 19:48
145bca1 to
72a2b82
Compare
kriszyp
force-pushed
the
fix/deflake-redirector-integration-test
branch
3 times, most recently
from
November 1, 2026 18:00
25eac9a to
571e3c3
Compare
kriszyp
force-pushed
the
fix/deflake-redirector-integration-test
branch
from
November 2, 2026 16:00
571e3c3 to
8b2d1bb
Compare
kriszyp
force-pushed
the
fix/deflake-redirector-integration-test
branch
2 times, most recently
from
November 2, 2026 19:04
96668de to
0216e98
Compare
`Component: redirector` deploys a fixture that declares a production dependency and ships no `node_modules`, so `deploy_component` runs a real `npm install` against the public registry on the suite's critical path. Harper's spawn bound there is an hour, and every request the test makes was an unbounded `fetch`, so a stalled install produced five minutes of silence and then `UND_ERR_HEADERS_TIMEOUT`, cancelling all 23 tests. That is how `main` went red at 576d333, a release-only commit, on one leg of 36. Vendor `papaparse` into the archive so `installApplication` takes its `node_modules`-present early return and the deploy spawns nothing, and give every request in the suite an `AbortSignal` deadline — the readiness poll's own 60s deadline was unreachable, because it is checked only after each `fetch` settles. Dispatch-Task: main-red-kriszyp_harper_576d33330_f0efc59e Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Dispatch-Task: pr-maint-f92ba507f948d116e9103e9b6cf148ff Co-Authored-By: GPT-5 Codex <noreply@openai.com>
…note The negative `[redirector:spawn:npm]` assertion only means something at debug level, so pin it rather than inherit the harness default. The design note claimed a repository-wide rule that only one suite checks, and omitted the local-file dependency pattern `deploy/redeploy-runtime-equivalence.test.ts` already uses. Dispatch-Task: main-red-kriszyp_harper_576d33330_f0efc59e Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Dispatch-Task: pr-maint-f92ba507f948d116e9103e9b6cf148ff Co-Authored-By: GPT-5 Codex <noreply@openai.com>
…gistry The note claimed `redeploy-runtime-equivalence.test.ts` was the only integration coverage of the automatic install path. `mqtt/mqtt.test.ts` and `components/acl-connect.test.ts` also take it, from the public registry, so a reader would mis-diagnose the next timeout there as a new bug. Dispatch-Task: main-red-kriszyp_harper_576d33330_f0efc59e Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Dispatch-Task: pr-maint-f92ba507f948d116e9103e9b6cf148ff Co-Authored-By: GPT-5 Codex <noreply@openai.com>
…stry fixtures A headers timeout in those suites is a reason to check the instance log for a `spawn:npm` line, not proof on its own that the install was the cause. Dispatch-Task: main-red-kriszyp_harper_576d33330_f0efc59e Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Dispatch-Task: pr-maint-f92ba507f948d116e9103e9b6cf148ff Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Reuse the prose-only cleanup already merged in #3429 so this branch passes the design-documentation lint gate without unrelated runtime changes. Dispatch-Task: pr-maint-f92ba507f948d116e9103e9b6cf148ff Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Use an audited table for the eviction case that requires older patches to merge. Keep explicit record versions behind audit append times so the additional-ref traversal case does not depend on test execution speed. Replace the redirector design note's CI history with its PR link. Dispatch-Task: pr-maint-f92ba507f948d116e9103e9b6cf148ff Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Adopt the unaudited absent-key fixture merged in #3471, preserving its stale-caller and index preconditions. Anchor the traversal scenario to its initial committed record version, then wait for the clock to pass its latest explicit version. Backdating a fresh store's seed can place it below retained audit history. Dispatch-Task: pr-maint-f92ba507f948d116e9103e9b6cf148ff Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Retain the explicit on-disk and wire compatibility invariant while pruning audit-clock notes. State that the registry-independent fixture rule covers all integration fixtures, including component and MQTT subdirectories. Dispatch-Task: pr-maint-f92ba507f948d116e9103e9b6cf148ff Co-Authored-By: GPT-5 Codex <noreply@openai.com>
…e ceiling Main now holds design docs to byte ceilings (#3857, #3856), so the line-budget joins earlier on this branch no longer help. Restore main's index text, add the integrationTests/ section, and drop redundant file-path parentheticals from the adjacent utility/ and build-tools/ index labels so DESIGN.md stays under its 68,166-byte ceiling. Dispatch-Task: pr-maint-f92ba507f948d116e9103e9b6cf148ff Co-Authored-By: Gemini <noreply@google.com>
The clock wait was added before #3510 made every local log key a fresh mint. With minted keys the original fixture passes on main, and the wait moved every out-of-order version below its key, dropping the version-above-key case the test covered. Dispatch-Task: pr-maint-f92ba507f948d116e9103e9b6cf148ff Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kriszyp
force-pushed
the
fix/deflake-redirector-integration-test
branch
from
November 5, 2026 15:21
0216e98 to
17e2667
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
⊙ Problem
Redirector’s routing tests can hang on a public-registry install or on unbounded HTTP waits. The new fixture note needs a root-index entry that fits the index's byte ceiling.
🤔 Keep real installation coverage?
Recommendation: keep routing tests isolated and retain separate installation coverage.
💡 Solution
Bundle pinned PapaParse, cap requests, and assert that npm never starts. Add one short root-index entry. Production behavior is unchanged.
🔧 Changes
DESIGN.mdadds one index entry for the fixture note (67,209 bytes, under its 68,166-byte ceiling after Trim the root DESIGN.md index back under its byte ceiling #4036 and Keep 40% of room under a DESIGN.md ceiling, and make a forced trim cut it to 60% #4046).🤔 Timing suggestions
The skip-install log is written before deployment returns, so the helper’s fresh 15-second wait is enough. The two seed requests have no demonstrated path to the logger’s 10,000-entry throttle, so keep the check where it is. The readiness loop’s fixed 500 ms pacing is bounded: each poll times out, and the 60-second deadline throws.
🤔 Retained constraints
fetchWithTimeoutstays a byte-identical copy ofearly-hints.test.ts's. Share it whensendOperationgets a timeout.✅ Verification
31a0e39e5, which carries Trim the root DESIGN.md index back under its byte ceiling #4036 and Keep 40% of room under a DESIGN.md ceiling, and make a forced trim cut it to 60% #4046's root-index trims and ceiling headroom rules (DESIGN.mdis 67,209 bytes, under its 68,166-byte ceiling). Locally afternpm ciand a build: redirector integration (23/23),check:design-docs,check:design-docs --base origin/main,lint:requiredand Prettier pass.17e2667.Broad local-run limitations
Earlier broad local runs were limited: core timed out at 900 seconds; integration had 2,586 passes, six loopback-collision failures, and six Ollama JSON-import cancellations. Both collision-affected files passed separately (10/10); the shared-temp credential probe remains a local harness failure.
Related scan: partial (all open PRs, first 50 of 342 merged). The overlaps are documentation edits; none supersedes the redirector fix. #3856 merged on
main, superseding the branch's earlierresources/DESIGN.mdcompatibility line; #3899, #4036 and #4046 mergedmain's design-docs fixes and root-index trims.PR QA: skipped @ 17e2667 — test fixtures and internal notes only.
Docs companion: none; this changes test infrastructure and internal notes only.
Original implementation, alternatives, and historical verification
mainwent green → red at 576d333 — a release-only commit that changes three lines ofpackage.jsonandpackage-lock.json— on one of 36 Integration Tests legs (run 35740846737,Integration Tests 6/6 (Node.js v26)). TheComponent: redirectorsuite'sbeforehook died after 307,394 ms withUND_ERR_HEADERS_TIMEOUT, cancelling all 23 tests. I re-ran that leg on the same SHA and it passed, somainis green again; this PR removes the cause rather than the symptom.The suite deploys
template-redirector-3.0.1.tgz, whose manifest pinspapaparseand which ships nonode_modules, sodeploy_componentruns a realnpm installagainst the public npm registry on the test's critical path. Nothing bounds that: Harper's spawn budget there isDEFAULT_COMMAND_TIMEOUT_MS = 60 * 60 * 1000, and every request the suite made was a barefetchwhose only limit was undici's implicit ~300 s headers timeout. The instance log for the failed run shows the server going silent 1.9 s after the npm child was spawned and never speaking again.The fix bundles
papaparsein the archive so deployment spawns nothing. Every request in the suite gets an enforceable deadline.For the human reviewer
Framing-Verdict: chosen-approach-sound (planning review, codex/xhigh). No alternative was overruled or adopted; the reviewer independently derived the same layer (fixture + harness, not Harper's production deploy defaults) before reading the plan.
Decisions raised in review and resolved rather than implemented, each cheap to revisit:
integrationTests/mqtt/mqtt.test.tsandintegrationTests/components/acl-connect.test.tsdeploy fixtures pinning@harperdb/acl-connectandjsonwebtokenwith no committednode_modules, through the same unboundedsendOperation. Left out of this PR to keep the red-main fix reviewable; recorded in the design note so the next timeout there is not diagnosed as a new bug.sendOperationitself still has no timeout, across 118 call sites in 25 suites. Adding one in@harperfast/integration-testingwould fix all of them at once and would have been the wider fix; this PR only stops using it in the one suite that was failing.fetchWithTimeoutis copied verbatim fromearly-hints.test.ts:21-34rather than moved next towaitForLog.ts. Two copies can drift. I kept them byte-identical so a later consolidation is mechanical.npm pack template-redirector@3.0.1would silently drop the vendored tree and restore the flake; that is why the filename diverges from the package it came from, and why the test asserts the skip-install path rather than trusting the archive.integrationTests/**/fixturesthat fails on production dependencies with no bundlednode_moduleswould have caught the two suites above. Cheap to add later, out of scope here.Declined review nits, all in
fetchWithTimeout, all inherited from theearly-hints.test.tscopy: the helper names a timeout only during the headers phase (a body stall rejects with a plainTimeoutError, so the test still fails, just less legibly);{ ...init, signal }overwrites a caller-suppliedsignal, which no caller passes; and the 90 s deploy cap is below undici's implicit ~300 s, which is deliberate — redirector no longer runs an install, so it does strictly less work thanearly-hintsunder the same budget.Changes
integrationTests/fixtures/template-redirector-3.0.1.tgzis removed and replaced bytemplate-redirector-3.0.1-vendored.tgz: byte-for-byte the same publishedtemplate-redirector@3.0.1contents pluspackage/node_modules/papaparse/, which is the publishedpapaparse@5.5.3— the exact version the fixture's own manifest pins — with its MIT license, no symlinks, and no other package. It was packed withtar --sort=name --mtime=... --owner=0 --group=0 --numeric-owner --format=ustar | gzip -9 -n, so regenerating it reproduces the same bytes. 28,894 → 75,196 bytes. Withnode_modulespresent,installApplicationtakes its early return and the deploy runs no package manager at all.integrationTests/components/redirector.test.tsstops callingsendOperationfor the deploy — that was the exact call that hung — and issues it throughctx.harper.operationsAPIURLwith a 90 s deadline and an explicit status check before parsing. Every other request in the suite now goes through afetchWithTimeouthelper carryingAbortSignal.timeout. That is not only defensive: the readiness poll declared a 60 s deadline but checked it after eachfetchsettled, so a single hung request made the deadline unreachable. Thebeforehook now also asserts, from the instance log, that the deploy loggedalready has node_modules; skipping installand no[redirector:spawn:npm]line, which is what stops a future fixture refresh from quietly reintroducing the registry. That assertion reads debug-level lines, so the suite pinslogging: { level: 'debug' }instead of inheriting the harness default.integrationTests/DESIGN.mdis new andDESIGN.mdgains its index line: one note recording why a deployed fixture must not install from the registry, the four hermetic patterns available, which one this fixture uses, and the two suites that still violate it.Verification
End-to-end route: the existing integration suite, extended with the skip-install log assertion.
npm run build, thennpm run test:integration -- integrationTests/components/redirector.test.ts— 23/23 pass, and suite wall time drops 26.5 s → 14.5 s, the npm install being the difference.[main/0] [debug] [redirector:spawn:npm]: Executing npm install --force --omit=dev --no-audit --no-fund --ignore-scriptsand never logged the skip line, so both new assertions fail on the old fixture.npm run test:integration -- "integrationTests/components/**/*.test.ts"— 149/149 pass.npm run test:integration -- "integrationTests/deploy/**/*.test.ts"— 85 pass, 1 skipped, 0 fail.npm run test:integration:all— 2159 tests, 2135 pass, 0 fail, 18 skipped. The 6 cancelled belong tointegrationTests/server/ollama-backend.test.ts, which self-skips unless a local Ollama is reachable; on this machine it is, and it fails on an unrelatedERR_IMPORT_ATTRIBUTE_MISSINGloadingjson/systemSchema.jsonunder Node v26.2.0. Untouched by this diff and skipped in CI.npm run format:check,npx oxlint --deny-warningson the changed test file, andnpm run check:design-docsall clean.PR checks only run Node 24, so the failing leg (Node 26, shard 6) is not reproduced by this PR's own checks.
Origin — the dispatch brief this PR was written from
harper main is red: Integration Tests at 576d333
mainon HarperFast/harper went from green to red. Failing workflows: Integration Tests at 576d333.Find the merge that broke it and get main green. Walk that workflow's recent push runs on
mainoldest-to-newest to find the FIRST red head SHA and the PR it came from, then pull the failing test names from that run and from the current head. A failure on every matrix leg (Node version, Bun, uWS, Windows) is a regression; one leg is usually a flake.Then either fix it or, when the culprit is a single merge and the fix is not obvious, open a revert and say so — main being green is worth more than the change being preserved. Record the fingerprint on the
initiatives/ci-healthboard doc either way (prose row beginning with the test path, never a bare ref), and add your PR toinitiatives/testing-and-deploy> "CI & build infrastructure" as a bare-ref line so it renders as a card.Dispatch: task
main-red-kriszyp_harper_576d33330_f0efc59e· queued by automation · ran by claude/opus/high · worker kzyp-xps-1🤖 Generated by OpenAI Codex; posted via @kriszyp.
Release target: v5.4 — preserve this PR’s current milestone; test-infrastructure maintenance.
Related PRs: #562 overlaps, #3527 overlaps, #3576 overlaps, #3856 overlaps, #3899 overlaps, #4036 overlaps, #4046 overlaps, 62 others independent
Complexity: medium
Deferred findings
f51259a1ca0a2c49nit reliability —integrationTests/components/redirector.test.ts— readiness loop uses fixed sleep pacing and a manual deadline instead of a condition-wait helper — NOT FILED28cfd97cfbfb87d7nit test-coverage —integrationTests/components/redirector.test.ts— Hosts seed POST response status not checked — NOT FILEDa03f2e67169f0ea5nit reliability —unitTests/resources/transaction.test.js— sibling out-of-order tests still base versions on Date.now() after a put with small offsets — NOT FILEDLate Grok findings — 6 surviving (highest minor) — Grok reviewed e26ad09
integrationTests/components/redirector.test.ts:116— Startup or fixture-seeding log throttling fails redirector setup even when deployment correctly skips npm (previously adjudicated)unitTests/resources/transaction.test.js:481— Wall time falling more than 2 seconds behind the stored version exhausts the new wait before audit assertions run (previously adjudicated)integrationTests/DESIGN.md:35— Fixture guidance omits deployment-window scoping and checking logs before fixture seeding (previously adjudicated)integrationTests/components/redirector.test.ts:69— Optional added-comment cleanup remains at the logging setup, deploy timeout, and transaction-version setup (previously adjudicated)resources/DESIGN.md:136— An unrelated compatibility promise is restored without establishing which stage it describes (previously adjudicated)integrationTests/components/redirector.test.ts:20— The timeout helper duplicates its sibling implementation, creating two maintenance sites (previously adjudicated)Not standing: 1 dropped-factually-wrong. Grok ran 2m (3m from launch to report); the fold adjudication (
cursor-grok@e26ad09002f9-abb0a1fa, codex) took 3m.Late Grok findings — 5 surviving (highest minor) — Grok reviewed 25eac9a
unitTests/resources/transaction.test.js:481— the fixture waits for wall time to catch up to the record version, and a backward clock step of 2 s or more makes the wait time out before any of the patches run (previously adjudicated)unitTests/resources/transaction.test.js:479— the new comment states a storage invariant that the store does not keep (previously adjudicated)resources/DESIGN.md:145— the compatibility note ends with "no on-disk or on-wire identifiers change in this stage" (previously adjudicated)integrationTests/components/redirector.test.ts:112— with debug logging on, a throttle line inhdb.logfails the suite even when npm was never spawned (previously adjudicated)integrationTests/components/redirector.test.ts:80— the readiness loop sleeps a fixed 500 ms between polls (previously adjudicated)Not standing: 1 merged, 1 separate-issue. Grok ran 3m (3m from launch to report); the fold adjudication (
cursor-grok@25eac9aa1d6d-f9f6b710, claude) took 1m.Review-Coverage: authored=gemini; ran=cursor-composer,codex,claude,cursor-grok,muse; late=cursor-grok:adjudicated; adjudicated=domain; rounds=24; full=21 @ 17e2667
Review-Attention: skim ~2m (decisions: vendor-node-modules-vs-install-command, log-scrape-proof-of-no-install) @ 17e2667