test(compat): published pg-wasm/pg-js readers open HEAD-sealed samples - #267
Conversation
The Node half of the wire-compat gate (#261). `pg-compat-js` opens the sample set the Rust half seals with the npm readers in COMPATIBILITY.md's support window: `@e4a/pg-wasm` 0.6.1 through both of its unsealers, and `@e4a/pg-js` 2.3.3 and 1.11.0 through `PostGuard#open`, the entry point consumers actually use. Versions are installed side by side as `npm:` aliases and pinned by a committed lockfile. Each case runs in its own process for the reason the Rust half does it: a reader handed a shifted header can die on the allocation rather than returning an error, and one dead case should not take the run down. Two limitations of the published SDKs are declared rather than hidden, because both would otherwise look like a gate that reads nothing. pg-js is stream-mode only in both directions, so the memory-mode cases are the pg-wasm reader's alone; and pg-js 1.x never surfaces a private signing policy, so for that reader the gate requires it absent. The run prints which reader opened which case, and fails when a case is opened by no reader or a reader opens no case. Also corrects `pg-compat/README.md`, which had memory mode as what pg-js `toBytes` produces — it seals with `sealStream`. The `wire-compat-js` job that runs this in CI cannot be pushed by the app; the YAML is in a PR comment. Refs #247, #251 Closes #261
Workflow YAML for a maintainer to apply
Part 1: two lines of
|
There was a problem hiding this comment.
VERDICT: request-changes
Rules Dobby 2 — cycle 1
Rule sweep over the diff plus review findings from the previous pass, merged. Three blocking, four non-blocking. Both blocking code/docs claims were reproduced rather than argued.
What is good here: the gate-teeth test is the right instinct — "good bytes open" is an assertion a broken harness also satisfies, and committing the damaged-set half is what stops this becoming another green check that reads nothing. Both SDK limitations are declared and asserted on (1.x's absent private policy is required absent, not skipped) rather than papered over. The per-case child process, the coverage invariant, and the npm ci pin are all pointed at the same failure mode. The CI job is correctly phrased as pending in CLAUDE.md and the PR body.
Blocking
-
bin/pg-compat-case.mjs:57— a missing artifact reports Node's version banner instead of the error.verifyCaseruns outside any try/catch, andloadArtifactsreads the ciphertext/plaintext/usk files before the first guarded block. Reproduced with a manifest naming astream.binthat is not on disk:@e4a/pg-wasm@0.6.1: stream: reader exited with status 1 before it could report: Node.js v22.23.1The ENOENT and the path are both gone: stdout is empty, so
childFailuresfalls through tostderrTail, which drops everyatframe and lands on the last surviving line — Node's banner. This is exactly the state the new CI job makes reachable (a partialactions/download-artifact, or a manifest naming a file the sealer did not write), and it is the one path where a maintainer reading the log has nothing to go on. -
COMPATIBILITY.md:111andpg-compat-js/README.md:84promise a drift check that does not exist. Reproduced: adding2.4.0to thenpm @e4a/pg-jsline inCOMPATIBILITY.md, leavingreaders.mjsuntouched, still gives# pass 5 / # fail 0.test/manifest.test.mjs:47comparesreaders()against a literal hardcoded in the test file and never opensCOMPATIBILITY.md. The drift direction that matters — the support window growing in the normative doc while the gate keeps testing the old set — goes undetected. Worth fixing rather than softening: the fenced block atCOMPATIBILITY.md:88-94is already machine-readable (npm @e4a/pg-js 2.3.3 1.11.0), so parse thenpmrows and compare againstreaders(). -
pg-compat/README.md:153describes a job that does not exist, doing something the proposed job does not do. The added text states thatpg-compat-js's job "repeats the samedorny/paths-filterstep and gates every expensive step on it". The YAML in the workflow comment does the opposite, deliberately and with an argument for it: it publishesoutputs.sealed: ${{ steps.seal.outcome }}fromwire-compat-rustand gates onneeds.wire-compat-rust.outputs.sealedprecisely to avoid two copies of a six-pattern filter. Two defects in one sentence — present tense for a job no workflow file defines, and a mechanism that contradicts the patch this PR ships. Phrase it as pending and describe theoutputs.sealedapproach the comment actually proposes.
Non-blocking
src/readers.mjs:43— the declaredversionand thenpm:alias inpackage.jsonare independent strings with nothing comparing them. Installed versions are correct today; the trap is the documented two-place edit.load()already imports fromspecifier, so reading${specifier}/package.json'sversioncloses it in a line or two.bin/pg-compat-case.mjs:58—process.stdout.writeimmediately followed byprocess.exit(). Node's pipe writes are synchronous on Linux and Windows but asynchronous on macOS, so a hand-run on a maintainer's Mac (documented atREADME.md:38-41) can lose the per-recipient detail. CI is ubuntu, so the gate itself is unaffected.process.exitCode = 1lets the writes drain.test/wire-compat.test.mjs:44—openedByis pushed whether or notrunCasereturned failures, so the coverage invariant below it is satisfied by a reader that failed to open the case. Harmless today, but it does not check what the comment above it says.CLAUDE.md:8(pre-existing line, not touched by this PR, but the bullet right below it is) — "the YAML sits in a comment on PR #265" is stale: #265 is merged and thewire-compat-rustYAML is on #266.CLAUDE.mdis auto-loaded agent context, so a later run follows that pointer to a merged PR looking for a spent patch comment. Your new bullet gets this right for #267; worth fixing the neighbour in the same commit.
Checked and clean
Conventional-commit PR title (the Conventional Commit check passes), the patch-in-comment pending phrasing in CLAUDE.md and the PR body, shell: bash on both run: steps in the proposed YAML (no unguarded pipes), cross-repo link format, prose style, draft state with the reason stated. CI on this branch is green. standardized-readmes does not apply — pg-compat-js/README.md is a package README inside the monorepo, not a repo root.
Findings 1-3 are worth a push. 4-7 are yours to take or leave; if you fold them in, none needs more than a couple of lines.
…ATIBILITY.md Review follow-ups on the Node half of the wire-compat gate. An error raised before the first reader call left `verifyCase` as an unhandled rejection: the child exited 1 with an empty stdout, and `childFailures` fell through to the last stderr line that is not a stack frame, which for Node's uncaught-error dump is its own version banner. A missing `stream.bin` reported `reader exited with status 1 before it could report: Node.js v22.23.1`, with the errno and the path gone. That is the state a partial artifact download in CI reaches. It is reported on stdout now like every other failure, and `gate-teeth.test.mjs` holds it there. `COMPATIBILITY.md` and the package README both promised that `test/manifest.test.mjs` fails when `src/readers.mjs` and the document's reader list drift. It did not: the test compared `readers()` against a literal in the test file, so adding a version to the document alone stayed green, which is the drift direction that matters. `src/support-window.mjs` parses the fenced `<registry> <package> <versions...>` block instead and the test compares the `npm` rows, refusing a document with no block and one with no npm row rather than comparing against nothing. Also: each reader's declared `version` is compared against the `package.json` of the alias npm resolved, so the two-place edit cannot run one version while labelling it another; the coverage invariant counts readers pointed at a case rather than readers that pushed a name whether or not the case opened; `process.exitCode` replaces `process.exit()` so a hand-run on macOS, where pipe writes are asynchronous, cannot lose the failure lines it is being read for; and `pg-compat/README.md` describes the JS job as pending and gating on the seal outcome the sealing job publishes, which is what the proposed YAML does, rather than as a job that re-runs the path filter. CLAUDE.md's pointer to the `wire-compat-rust` YAML follows it from the merged #265 to #266. Refs #261, #251, #247. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review cycle 1 addressed, all seven threadsAll three blocking findings are fixed and pushed as 7caa296; the four non-blocking ones are folded in too. Each thread has a reply with what changed and how I checked it; this is the short version. The two that were reproduced as broken are reproduced as fixed: Both are committed checks, not scratch runs. The first is a new
One deviation from a suggestion, on the version cross-check: read off disk rather than imported, because Verified after the changes: 22 tests pass in |
The installed-version check closed the version half of the two-place edit
and left the package half open: an alias resolving to the right version of
the wrong package passed while every message carried the declared package
name, which is the mislabelled support window the check exists to catch.
`installedPackage` returns `{name, version}` and the test asserts both.
Two docs pointers with it. `pg-compat/README.md` called #261 a PR; the
work item is issue #261 and the YAML is a comment on PR #267.
`COMPATIBILITY.md` claimed the whole `crates.io` line is spelled out in
`pg-compat/Cargo.toml`, which pins 0.6.1 only; 0.5.10 is declared in the
window and opened by no gate.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
VERDICT: approve
Rules Dobby 2 — cycle 2
Re-swept the delta 5a22923..7caa296 against the rules that apply to a Node/CI diff, merged with the reviewer's four findings. Zero blocking issues. All three cycle-1 blockers are genuinely fixed, three of the four new nits I fixed on the branch myself (a605734), and the fourth is a latent hazard the reviewer explicitly asked not to "fix" — it is recorded below and inline instead.
Verified, not taken on trust
The PR body claims 22 passing tests. I sealed a set with HEAD and ran it rather than believing the summary line, because # fail 0 is not the same as green — one unresolved await reports as # cancelled and still exits 1:
# tests 22
# pass 22
# fail 0
# cancelled 0
EXIT=0
The widened alias check was reproduced as catching what the old one missed. Adding "pg-js-0-6-1": "npm:@e4a/pg-wasm@0.6.1" — an alias with the version the entry declares and the wrong package — passed the version-only check and fails now:
not ok 6 - every reader is labelled with the package npm installed for it
+ name: '@e4a/pg-wasm',
- name: '@e4a/pg-js',
Fixed on the branch (a605734)
Nits, so they were cheaper to push than to hand back:
src/readers.mjs—installedVersionclosed the version half of the documented two-place edit and left the package half open, so a right-version/wrong-package alias passed while every failure message carried the declared package name. That is the mislabelled support window the function exists to prevent. NowinstalledPackagereturns{name, version}and the test asserts both, withCLAUDE.mdandpg-compat-js/README.mdupdated to match.pg-compat/README.md:153— called #261 a PR. #261 is an issue (compat gate: published pg-wasm/pg-js old-reader step (Node)) and the link ref resolves to/issues/261, so a maintainer following the pointer for the YAML landed on an issue that has none. Now reads "([#261], YAML in a comment on PR #267 and not applied yet)", matching whatCLAUDE.mdalready said two paragraphs away.COMPATIBILITY.md:114— asserted the wholecrates.ioline is spelled out inpg-compat/Cargo.toml. That manifest pins one version (=0.6.1, onereader!);0.5.10sits in the declared window and is opened by no gate. Qualified rather than papered over, since adding the Rust-side check is out of this PR's scope.
Non-blocking, left alone deliberately
src/readers/pkg-stub.mjs— theprocess.exitCodefix means the child now exits when the event loop drains, and the stub is a listening server with nounref(). Not reachable today:startPkgStubis the last fallible statement in the pg-js reader'sload(), soverify.mjs's early return on load failure cannot leak a live server, and itsfinallyalways runsunload. I confirmed both. Flagging the hazard, not a live bug — aserver.unref()would make the drain fix robust against a later change to that ordering, but it is not worth a commit for a failure that cannot currently happen.
The crates.io drift check (0.5.10 in the window, no gate opening it) is worth a follow-up on #247 rather than scope creep here.
Checked and clean
shell: bash on both run: steps in the proposed YAML, and no pipes in either — the default bash -e {0} has no pipefail, so this matters and the comment gets it right and says why. The job cannot be path-filtered out of reporting: it is needs: + if: ${{ !cancelled() }} with the seal outcome gating the expensive steps, so it always reports and can become a required check. No pull_request.base.* dependency, so the trigger does not need edited. The patch-in-comment phrasing is pending-tense in CLAUDE.md, pg-compat/README.md and the PR body, and no doc claims a gate that no workflow file defines. Conventional-commit title passes. Prose style holds: 1 em dash in 603 words of added prose across the delta, against base files that sit at 0–1. standardized-readmes does not apply to a package README inside the monorepo. CI on 7caa296 was green across all 20 checks.
Two SDK limitations are declared and asserted on rather than skipped, and 1.x's absent private policy is required absent. That is the right instinct for a gate, and the gate-teeth test is what keeps it honest.
|
/dobby workflow half applied verbatim (both parts: the |
|
Got it — I'm on it. I'll re-review the latest commit on #267, checking that both the |
There was a problem hiding this comment.
VERDICT: request-changes
Re-reviewed 40af2e9. Part 2 landed verbatim — the wire-compat-js job is byte-identical to the YAML in the comment. Part 1 did not land: the commit is 57 insertions(+), 0 deletions(-) and touches no existing line, so neither the outputs.sealed declaration nor the pg-compat-js/** filter entry reached wire-compat-rust. Without them the new job is a green no-op, which is the failure mode #262 would then make required.
Three things to fix, all in .github/workflows/build.yml, plus three doc lines this commit made false. Details inline; the short version:
outputs.sealedis undeclared.git show origin/main:.github/workflows/build.yml | grep -n 'outputs:'matches nothing. Actions does not error on an undeclared output —needs.wire-compat-rust.outputs.sealedis the empty string,== 'success'is false on every run, and all four steps skip.Wire compat (published pg-wasm/pg-js)reports success having downloaded nothing and opened no container.pg-compat-js/**is not in thewire:filter. A PR touching only the gate's own harness leaveswire == 'false', sosealedis'skipped'and the JS gate no-ops. The gate would not run on changes to itself.- The branch is not rebased.
mergeable: CONFLICTING/DIRTY: #266 appendedwire-compat-rust+pg-compat-lintto the end of this file as 1237573, this branch appendswire-compat-jsat the same place from base 2ba47a1. Consequences:gh pr checks 267reports no checks and the head sha has zero check-runs, so neither half has actually run on this PR yet; andneeds: wire-compat-rustnames a job absent from this branch's own build.yml, so the file is invalid when evaluated from the head commit rather than a merge commit.
Rebase onto main, then apply Part 1 on main's wire-compat-rust job — two edits, both between runs-on: and permissions: / inside the existing filters: block:
wire-compat-rust:
name: Wire compat (published pg-core)
runs-on: ubuntu-latest
outputs:
sealed: ${{ steps.seal.outcome }}
permissions: wire:
- 'pg-core/**'
- 'pg-wasm/**'
- 'pg-compat/**'
- 'pg-compat-js/**'What is right about the applied job
The parts that are easy to get wrong are correct. The artifact name matches the upload character for character, including the github.event.pull_request.head.sha || github.sha fallback — a plain github.sha would look for an artifact that does not exist on a pull_request event. if: ${{ !cancelled() }} with the upload placed before wire-compat-rust's own read step means a red Rust half still leaves the bytes and still lets the JS half report. Every expensive step is behind the seal outcome rather than a second copy of the path filter, so there is one filter to keep honest instead of two. shell: bash on both run: steps gets -eo pipefail instead of the default bash -e {0}. npm ci with cache-dependency-path pinned to pg-compat-js/package-lock.json means the readers are the lockfile's, not whatever resolves. setup-node@v6 matches delivery.yml's publish-wasm, and download-artifact@v4 pairs with the existing upload-artifact@v4.
Test run at HEAD
npm ci && npm test in pg-compat-js: 22 tests, 17 pass, 5 fail, 0 cancelled, and all five failures are the artifact-dependent cases reporting ENOENT ... manifest.json / Seal the sample set first — which is what they should do with no sealed set present. The support-window check (ok 18) parses the real COMPATIBILITY.md and agrees with readers.mjs, and ok 20 confirms each npm alias resolved to the declared package and version.
One forward note for #262
Before adding Wire compat (published pg-wasm/pg-js) as required, note that a workflow_dispatch from main is the one entry point where dorny/paths-filter decides from main's single most recent commit rather than a real diff (isBaseSameAsHead is true, beforeSha is null outside push, so it falls back to getChangesInLastCommit()). That reports the gate green whenever that one commit misses the filtered surface — a false pass on the entry point a human explicitly asked for. It is inherited from #266's already-merged job, not introduced here; || github.event_name == 'workflow_dispatch' on each conditional step's if closes it if you want the dispatch to force both halves.
|
Re-reviewed 40af2e9 — changes requested: #267 (review) Part 2 (
Left 7 inline comments with the exact YAML for both fixes, plus 3 doc lines (CLAUDE.md, pg-compat/README.md, COMPATIBILITY.md) that are now stale since #266 merged. What did land is solid — artifact naming, Once rebased with those two edits applied, this should be ready to go. |
|
/dobby all blocking items addressed, properly this time:
This push trips the filter, so both halves run here — the JS gate should now actually download and open the set (not the no-op green). Please re-review. |
|
Got it, thanks for the detailed rundown! I'm on it — kicking off a fresh review of #267 now that main is merged in, the |
There was a problem hiding this comment.
VERDICT: approve
Re-reviewed at 1016d8b4. All three blocking items from the last cycle are genuinely addressed this time — I verified each against the tree and the run rather than the description.
The gate is not a no-op green. wire-compat-rust declares outputs.sealed: ${{ steps.seal.outcome }} and the filter carries pg-compat-js/**, both on the branch (2e91728, 40af2e9). More to the point, Wire compat (published pg-wasm/pg-js) ran every step on this head sha — download, npm ci, npm test all success, tests 22 / pass 22 / fail 0 / cancelled 0 — and the log shows the set actually opening: mem and mem-privsig by @e4a/pg-wasm@0.6.1, the three stream cases by all three readers. That is five cases read, not a skipped job reporting green.
Branch is MERGEABLE. Docs match the tree: CLAUDE.md is present-tense with the workflow file in the same commit range, pg-compat/README.md drops the proposed/not-applied framing and carries the head-sha artifact name.
Rule check clean on the ones that bear on this diff:
actions-default-shell-has-no-pipefail— both newrun:steps declareshell: bash.path-filtered-job-cannot-be-a-required-check— the filter stays step-level,build.ymlis in it, andif: ${{ !cancelled() }}meanswire-compat-jsalways reports. I walked the four skip paths (filter false, seal fails,wire-compat-rustdies before seal, dispatch/push): each leavessealedat something other than'success', every step skips, the job reports. Fit for a required check.pull-request-default-types-exclude-edited—editedis in the trigger, so a retarget re-runs the base-dependent filter.patch-in-comment-gate-is-not-live-yet— this was the previous blocker and it is resolved in the right direction: the workflow and the present-tense prose now sit in the same tree.node-test-cancelled-is-not-green— checkedcancelled 0in the CI log, not justfail 0.
Six non-blocking notes inline: one reachable diagnostic drop in stderrTail, one docs sentence that contradicts the caveat six lines below it, one remaining member of the silent-green class, one sibling guard, and two wrap-width nits.
The only one I would fix before merge is the COMPATIBILITY.md sentence — it overstates gate coverage in the normative document, which is the exact class this PR exists to close, and it is a one-phrase edit. The rest can ride or land as a follow-up.
|
Re-reviewed at I posted the review as a COMMENT (GitHub won't let me APPROVE my own PR) with Six non-blocking notes inline — none are merge blockers, but I'd fix the first one before merging since it's a one-phrase edit: |
|
COMPATIBILITY.md fixed — you were right that it overstated the crates.io half, and the fix went further than the one phrase because the surrounding lines had the same problem:
Left alone as you asked: the pkg-stub Ready to merge from my side. |
- stderrTail kept the LAST non-frame stderr line, which for a Node crash dump is the 'Node.js vX' banner — the real error was dropped on exactly the run where the message matters most (a child that died before it could report). Now takes the first informative line and filters the banner/preamble too. - readManifest refused a set with no cases but not a case with no recipients; verify.mjs loops over recipients, so an empty list reported as opened cleanly without checkOpened ever running. - The coverage invariants were computed from the manifest, so a sealer that stopped emitting the memory-mode cases kept them satisfied and both halves went green having lost the only coverage memory mode has. The case-name set is now asserted explicitly. Verified against a freshly sealed set: 22/22 pass, and removing the mem cases from the manifest now fails the assertion (it previously passed).
|
Went back through every open thread, verified each against HEAD, and resolved the ten that are genuinely fixed. Three of them needed new work — all in the silent-green class this PR exists to close, so I fixed them rather than deferring:
Verified against a freshly sealed set: Left open deliberately, both yours:
The wasm-browser flake you flagged on main is pre-existing and matches the chronic ChromeDriver flakiness already tracked in this repo — not fallout from here, as you said. |
Ten measured lines plus the one prose line I left over 80 when editing pg-compat/README.md in place. No behaviour change; the suite still passes 22/22. Cheaper now than as churn when a formatter lands.
|
Both wrap nits fixed — the ten code lines you measured, plus the README prose line, which was my own leftover from editing that paragraph in place. No behaviour change; suite still 22/22 against a freshly sealed set, and nothing in the package now exceeds 100 chars. I also resolved your pre-existing wasm-browser-flake thread: it is real but tracked as chronic ChromeDriver flakiness in this repo, and as you said it is not fallout from this branch. That leaves only the |
… case process The child ends with `process.exitCode = ...` rather than process.exit(), because pipe writes are async on macOS and exiting outright truncates the failure lines the gate exists to print. So it exits only when the loop drains, and a listening server is a live handle: a stub that outlived its case would hold the child open until runCase's 300s spawnSync timeout, turning a one-line failure into a stall. Today nothing reaches that: startPkgStub is the last fallible statement in the pg-js reader's load(), and verify.mjs always unloads. But that invariant lives in two other files and nothing stated or enforced it, which is what made this worth closing rather than just recording. Three parts: unref() so the leak cannot hang anything, a comment at the one place the invariant can be violated, and a test that spawns a child leaking a stub and asserts it still exits. Verified: the probe exits in 0s (it would previously have run to the timeout), and the full gate still passes 23/23 against a freshly sealed set with all five cases opened — unref does not stop the stub serving, since the in-flight request and pg-js's own client socket keep the loop alive.
|
Closed the Fixed the shape of the problem rather than just the symptom.
Verified both directions: the leak probe exits in 0s (previously it would have run to runCase's 300s timeout), and the full gate still passes 23/23 against a freshly sealed set with all five cases opened by their expected readers — so That was the last open thread on this PR. |
Closes #261. Part of #251, epic #247 (workstream C).
pg-compatopens the HEAD-sealed sample set with publishedpg-corereleases. This addspg-compat-js, which does the same job for the npm side of the support window inCOMPATIBILITY.md:@e4a/pg-wasm0.6.1UnsealerandStreamUnsealerdirectly@e4a/pg-js2.3.3stream*casesPostGuard#open().inspect()then.decrypt()@e4a/pg-js1.11.0stream*casesThat is 11 case/reader pairs, each opened with both recipients' keys, so 22 unseals per run. The three versions are installed side by side through
npm:aliases (several versions of one package have to coexist in onenode_modules) and pinned by a committed lockfile; CI installs withnpm ci.Each case runs in its own process,
bin/pg-compat-case.mjs, for the reason the Rust half does it: a reader handed a shifted header can die on the allocation instead of returning an error, and one dead case should not take the run down.What it checks per case
Recovering the right bytes, plus the things a consumer acts on once a container opens: the recipient list the reader read out of the header, the public signing policy it verified the header signature against, and the private signing policy for the
*-privsigcases.pg-compat's reporting style is kept, including the first-differing-offset message, so a red run in either half reads the same in a CI log.Two SDK limitations, declared rather than hidden
Both were found while building this, and both are the shape of thing that turns into a gate that passes without reading anything.
pg-jsis stream mode only, in both directions. Its decrypt path builds aStreamUnsealerwhichever input mode it is given, so a memory-mode container comes back asmode is not supported: InMemory { size: N }, andtoBytes()seals withsealStreamrather thanseal. So the memory-mode cases are thepg-wasmreader's alone. This also correctspg-compat/README.md, which had memory mode as "what pg-jstoBytesproduces".pg-js1.11.0 never surfaces a private signing policy: it discards whatStreamUnsealer.unseal()returns and reportspublic_identity()instead, which only sees the header. 2.3.3 keeps the unseal result. That is a fixed limitation of the 1.x line, not something a wire change can move, so the reader declares it and the gate then requires the private policy to be absent for that reader. A 1.x that suddenly reported one is a mismatch rather than a quiet pass.Neither is a silent skip. The run prints which reader opened which case, and fails if a case ends up opened by no reader or a reader opens no case:
Does it actually go red
I checked both directions against
pg-coreHEAD rather than trusting the green run.A field inserted before
algoinHeader(non-additive) turns all 22 unseals red, per case and per reader:The same field appended at the end of
Header(additive, trailing bytes ignored) stays green. Both probes were reverted; there is nopg-corechange in this PR.test/gate-teeth.test.mjscommits the cheap half of that: it hands the readers a damaged copy of the set (flipped payload byte, truncated container, bumpedwireVersion, a case file the set does not have) and requires a failure naming the case. It is what catches a harness that has stopped reading, since "good bytes open" is an assertion a broken harness also satisfies.The support window is checked against the document that declares it
COMPATIBILITY.md'sReader listis the normative window andsrc/readers.mjsis this package's copy of the npm half of it, sosrc/support-window.mjsparses the fenced<registry> <package> <versions...>block andtest/manifest.test.mjscompares thenpmrows againstreaders(). Adding a version to the document and not to the gate is a red run, which is the drift direction that matters: the window growing where it is declared while the gate goes on testing the set it always tested. The parser refuses a document with no such block, one with nonpmrow, and annpmrow with no version, because an empty list would compare equal to nothing.Each reader's declared
versionis also compared against thepackage.jsonof the alias npm resolved, so the two-place editREADME.mddocuments cannot end up running one version while labelling every message with another.Verified locally
pg-compat-js: 22 tests pass, 0 failed, 0 cancelled, exit 0 (PG_COMPAT_ARTIFACTS=... npm --prefix pg-compat-js test).cargo test --manifest-path pg-core/Cargo.toml --features test,rust,stream: pass.PG_COMPAT_ARTIFACTS=... cargo test --manifest-path pg-compat/Cargo.toml --locked: pass, so one sealed set still feeds both halves.cargo testforpg-pkg,pg-cli,pg-ffiwith--all-features: 69 tests, all pass.cargo fmt --all -- --checkand the same forpg-compat: clean. No Rust source changed.The CI job needs a maintainer
dobby-coder[bot]has noworkflows: writeon this repo, so thewire-compat-jsjob is in a PR comment for you to apply, as with #266. It needs two lines added to thewire-compat-rustjob in #266 as well:outputs.sealed: ${{ steps.seal.outcome }}, which is already the exact condition the artifact upload is behind, pluspg-compat-js/**in that job's path filter. Re-running the filter in the JS job instead would mean two copies of a six-pattern list that have to agree forever. Details, and what I checked, are in the comment.Until that is applied nothing runs this in CI, the same caveat #266 carries.