fix: make the smoke test reliably green#5
Open
philcunliffe wants to merge 1 commit into
Open
Conversation
The end-to-end smoke test was failing — partly flaky, partly broken —
from three independent causes. It exits on the first failed check, so
each masked the next; this fixes all three.
1. Mover "run now" could silently no-op (the real ~50% flake).
mover.tick() had a `running` guard that made a *concurrent* call
return 0 immediately. The 200ms background timer and the admin
/v1/admin/mover/run endpoint both call it, so when runMover() landed
mid-pass it returned 200 without committing the just-spooled row, and
the follow-up query saw 0 rows. Split into opportunistic tick() (the
timer keeps skipping, never piles up) and guaranteed drain() (waits
out any in-flight pass, then runs a fresh pass whose pending()
snapshot is guaranteed to include rows spooled just before the call).
The admin endpoint and the shutdown drain now use drain().
2. Config pin format diverged from the kernel's config wire schema.
The save pipeline emitted a lock-file-shaped plugin entry into a
config document — object `source` ({kind,raw,path}) and `content_hash`
— but the kernel's parseConfigShape requires `source` to be a string
and the client verifies the pin under `artifact_hash` (hypaware
config/apply_deps.js). The server thus produced a document its own
shape parser rejects on re-submission. Emit `version` +
`artifact_hash`, keep `source` as the operator's raw string the client
re-resolves; validatePrePinned validates that shape.
3. Smoke proxy row predated the ai_gateway_messages schema. That dataset
gained a required non-null `session_id` column (schema v6); add it.
Verified: 50/50 runs green (was ~50% flaky).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Summary
The end-to-end smoke test (
npm run smoke) was failing — partly flaky, partly outright broken. It exits on the first failedcheck(), so each cause masked the next. This fixes all three. Verified 50/50 runs green (previously ~50% flaky).Root causes & fixes
1. Mover "run now" could silently no-op — the real ~50% flake
mover.tick()had arunningboolean guard that made a concurrent call return0immediately. The 200ms background timer and the adminPOST /v1/admin/mover/runendpoint both call it, so when the test'srunMover()landed while a background tick was mid-pass, the endpoint returned200without committing the just-spooled row — and the follow-up query saw 0 rows (Cannot read properties of undefined (reading 'client_received_at')).tick()(the timer keeps skipping when busy, never piles up) and guaranteeddrain()(waits out any in-flight pass, then runs a fresh pass whosespool.pending()snapshot is guaranteed to include rows spooled just before the call).POST /v1/admin/mover/runand the shutdown drain now usedrain().2. Config pin format diverged from the kernel's config wire schema
The save pipeline emitted a lock-file-shaped plugin entry into a config document —
sourceas an object spec ({kind,raw,path}) and the hash undercontent_hash. But the kernel'sparseConfigShaperequiressourceto be a string, and the client verifies the pin underartifact_hash(hypawareconfig/apply_deps.js). The server was producing a document its own shape parser rejects on re-submission (thepre-pinned document accepted without re-resolutioncheck).version+artifact_hash; keepsourceas the operator's raw string the client re-resolves.validatePrePinnednow validates that shape.3. Smoke proxy row predated the
ai_gateway_messagesschemaThat dataset gained a required non-null
session_idcolumn (schema v6, kernel LLP 0030) →422 missing_required_column. Addedsession_idto the proxy row.Test plan
npm run smoke→all 80 checks passed🤖 Generated with Claude Code