Skip to content

fix(api-manifest): add 15 missing dispatch-table entries; move drift check onto cargo-test - #10817

Closed
proggeramlug wants to merge 3 commits into
mainfrom
fix/manifest-consistency-drift-15-entries
Closed

proggeramlug wants to merge 3 commits into
mainfrom
fix/manifest-consistency-drift-15-entries

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

What

crates/perry-codegen/tests/manifest_consistency.rs's every_dispatch_entry_has_manifest_counterpart
was red on origin/main: API_MANIFEST was missing 15 entries that exist in NATIVE_MODULE_TABLE.
This is the pr-gate-blocking failure surfacing in e2e-scoped for every PR in the repo.

The 15 rows, credited to the two commits that actually added the dispatch-table rows without a
manifest counterpart:

From b36554a2d7 (#10467/#10468/#10469, node:http client response surface,
class_filter = IncomingMessage):

  • http::rawHeaders, http::__get_rawHeaders
  • http::httpVersionMajor, http::httpVersionMinor
  • http::complete

From 64ca0ebfe7 (#10441/#10442/#10444/#10465, net.Socket surface cluster,
class_filter = Some("Socket") or None):

  • net::prependListener, net::prependOnceListener (class_filter: Some("Socket"))
  • net::pipe, net::unpipe (class_filter: Some("Socket"))
  • net::writable, net::readable, net::writableEnded, net::readableEnded,
    net::_writableState, net::_readableState (class_filter: None, matching the dispatch
    table's native_table/net_events.rs rows exactly)

All 15 are receiver-based property/getter reads (has_receiver: true), not module-level
functions — they're registered via method(module, name, true, class_filter), which is this
file's established convention for representing a zero-arg NativeMethodCall property read (see
the pre-existing net::pending/net::bytesRead/http::statusCode/stream::writable rows and
the #2549 comment in entries/part_1.rs explaining why). ApiKind::Property is reserved for a
different mechanism entirely — module-level constants/accessors dispatched via custom Expr::*
variants outside NATIVE_MODULE_TABLE (see entries.rs's module doc, category 2) — so using it
here would both misdescribe these APIs and fail to satisfy the drift check, which only matches
ApiKind::Method.

Added to crates/perry-api-manifest/src/entries/part_1.rs (net) and part_4.rs (http), next to
their respective existing IncomingMessage/Socket blocks. Both files stay well under the
2000-line cap (1318 and 1131 lines respectively after the addition).

Why nothing caught this drift on the PR that introduced it — and the fix for that

e2e-scoped only runs an integration suite under crates/*/tests/*.rs per-PR when the diff
names that file (CLAUDE.md, "Integration suites ... run per-PR only when the diff names them").
Neither b36554a2d7 nor 64ca0ebfe7 touched manifest_consistency.rs, so the one test built
specifically to catch this drift never ran on either PR. It surfaced later, on an unrelated PR
that happened to touch the test file, and looked like that PR's fault.

Put precisely: every_dispatch_entry_has_manifest_counterpart's trigger condition and its
subject are disjoint by construction.
It's a consistency check between two tables
(NATIVE_MODULE_TABLE and API_MANIFEST), so it's tripped by an edit to either table — but it
only runs, per-PR, when the diff touches its own file, which is the one file a drifting PR
(one that only adds dispatch-table rows) has no reason to touch. This is a fifth way a gate can
be unable to fail
, distinct from the four CLAUDE.md already tracks under "Four ways a gate can
be unable to fail" (continue-on-error, not in required contexts, cancelled by concurrency, or
the gate runs but its subject never does). Here the gate itself is fine — it runs, it can go red,
it's required — and the change class it exists to guard structurally never triggers it.

Fix for that (this PR, not just the report): every_dispatch_entry_has_manifest_counterpart
moved to a #[cfg(test)] unit test, perry_codegen::manifest_consistency in
crates/perry-codegen/src/manifest_consistency.rs (declared in lib.rs). This puts it on the
cargo-test-visible per-PR gate unconditionally, regardless of which files a diff touches
(CLAUDE.md: "Prefer putting acceptance coverage in cargo-test-visible unit tests (#5960)").
Feasibility: perry-codegen already depends on perry-api-manifest as an ordinary (non-dev)
dependency (see crates/perry-codegen/Cargo.toml), so reaching API_MANIFEST from inside this
crate's own lib.rs test module adds no new dependency edge.

The integration test's copy of this check was removed, not kept as a duplicate: its trigger
condition is a strict subset of the new unit test's (unit test: every PR; integration test: only
PRs that touch its own file), so a passing integration-test copy could never catch anything the
unit test doesn't already catch first — it would just be two copies of identical matching logic
to keep in sync. The integration file's other checks are untouched and stay where they are:
manifest_param_counts_match_dispatch_table (#512), every_native_module_has_at_least_one_manifest_entry
(#513), cjs_style_node_builtins_have_default_entries, and every_well_known_binding_has_manifest_entry
(#513). Note manifest_param_counts_match_dispatch_table has the same disjoint-trigger shape as
the check that moved — left as-is, out of scope for this pass; flagging it here so it isn't
mistaken for an oversight.

Verified the new unit test is non-vacuous: with the manifest fix temporarily reverted (dispatch
table unchanged), cargo test -p perry-codegen --lib manifest_consistency fails with the same
15-entry list; with the fix restored, it passes.

Validation

  • Baseline (pristine origin/main @ b9ba951ff, via a separate worktree):
    cargo test -p perry-codegen --test manifest_consistencyevery_dispatch_entry_has_manifest_counterpart
    FAILS listing exactly these 15 entries; the other 4 tests in that file pass.
  • This branch: cargo test -p perry-codegen --test manifest_consistency — 4/4 pass (the
    moved test is gone from this file, by design).
  • This branch: cargo test -p perry-codegen --lib manifest_consistency — the new unit test
    passes (1 passed).
  • This branch: cargo test -p perry-codegen (full lib + all integration suites + doc-tests) —
    all green, 1653 lib tests + every integration suite, 0 failures.
  • This branch: cargo test -p perry-api-manifest — all green (36 unit + 4 integration).
  • Non-vacuity check on the new unit test (described above): fails without the manifest fix,
    passes with it.
  • cargo check --workspace --all-targets --exclude perry-ui-gtk4 under RUSTFLAGS=-D warnings
    on the default dev profile (matching CI; perry-ui-gtk4 excluded — the build host lacks its
    system libs) — clean, 0 warnings, 0 errors, exit 0.
  • scripts/run_lint_gates.sh with SKIP_COMPILE_GATES=178 of 79 script gates passed
    (compile tier skipped, 2 CI-only gates skipped for lack of GitHub Actions context). The one
    failure, [Public benchmark evidence freshness] python3 benchmarks/ci_public_baseline_check.py,
    is the pre-existing red on every PR in this repo (ci: two reds on main fail every PR — gap-suite shard 5 parity regression (test_gap_10430) and a stale public benchmark baseline #10707) — not touched by this change.
    git diff --stat confirmed clean (no destructive regen) before and after the lint run.

Not run

  • No gap suite (host has no free port for it / runs a fixed port already in use by concurrent
    campaign work; also out of scope for a manifest-only change).
  • No --release build/tests — this change has no runtime code path, only static manifest data
    and a moved test; perry-dev/dev-profile checks are sufficient here.
  • CI on this PR is not being watched — per this campaign's standing instruction, the stop
    condition is the PR being open, not CI going green. pr-gate's "Public benchmark evidence
    freshness" step is red on every PR in this repo already (ci: two reds on main fail every PR — gap-suite shard 5 parity regression (test_gap_10430) and a stale public benchmark baseline #10707) and is not mine to fix.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed property access for net.Socket streams, including listener methods and readable/writable state fields.
    • Fixed access to additional http.IncomingMessage properties such as raw headers, HTTP version values, and completion status.
    • Improved API compatibility so these properties and methods dispatch correctly at runtime.
  • Tests

    • Manifest consistency checks now run reliably during standard test runs, helping detect missing API coverage earlier.

@coderabbitai

coderabbitai Bot commented Sep 20, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: ce866932-9276-4d62-8583-71f7e4749873

📥 Commits

Reviewing files that changed from the base of the PR and between b9ba951 and c45875d.

📒 Files selected for processing (6)
  • changelog.d/10817-manifest-drift-fix.md
  • crates/perry-api-manifest/src/entries/part_1.rs
  • crates/perry-api-manifest/src/entries/part_4.rs
  • crates/perry-codegen/src/lib.rs
  • crates/perry-codegen/src/manifest_consistency.rs
  • crates/perry-codegen/tests/manifest_consistency.rs

Included review availability: Your plan provides up to 8 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

The change adds 15 missing API manifest entries for Node.js properties. It moves dispatch-manifest coverage validation into a perry-codegen unit test and removes that check from the integration test.

Changes

Manifest dispatch coverage

Layer / File(s) Summary
Node.js manifest entries
crates/perry-api-manifest/src/entries/part_1.rs, crates/perry-api-manifest/src/entries/part_4.rs, changelog.d/10817-manifest-drift-fix.md
Adds net.Socket listener, stream, and state entries. Adds http.IncomingMessage accessors and the __get_rawHeaders rewrite entry.
Manifest consistency validation
crates/perry-codegen/src/lib.rs, crates/perry-codegen/src/manifest_consistency.rs, crates/perry-codegen/tests/manifest_consistency.rs
Registers a #[cfg(test)] module containing the dispatch-signature coverage check. Removes the duplicate integration-test check and retains the other integration checks.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to c4587

The changelog accurately documents the manifest coverage and test-gate relocation. No concrete merge-blocking issue remains.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies both main changes: adding 15 missing API manifest entries and moving the drift check to the cargo-test gate.
Description check ✅ Passed The description provides a detailed summary, concrete changes, rationale, validation results, and known test limitations. It does not use the template headings exactly and omits an explicit Related is…
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 5 files. (1 skipped: 1 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Verifying the Method-vs-Property decision independently, since it is the one judgement call in an otherwise mechanical PR and several of these rows (writable, readable, writableEnded, readableEnded, complete, httpVersionMajor, httpVersionMinor, _writableState, _readableState, __get_rawHeaders) are properties rather than methods in JavaScript terms.

The choice is correct, and here is the evidence rather than the assertion. The drift test's matcher is:

matches!(e.kind, ApiKind::Method { has_receiver, .. } if has_receiver == sig.has_receiver)

It matches only ApiKind::Method. A property(...) entry — of which the manifest has many, 397 in part_3.rs alone — could not satisfy it, so this is not a case of an exotic kind being avoided out of convenience.

More importantly the classification is accurate rather than merely test-satisfying: these rows exist in NATIVE_MODULE_TABLE as receiver-taking dispatch entries, and this test asserts consistency between the dispatch table and the manifest. Describing them as Method { has_receiver: true } is a faithful description of how dispatch treats them. The has_receiver/class_filter/arg shapes were confirmed against the two originating commits (b36554a2d7, 64ca0ebfe7) rather than inferred.

The question I would still put to a reviewer, because this PR cannot settle it: the drift test's stated purpose is that inconsistency here makes the unimplemented-API check (#463) error on real implementations. If #463 or tsc surface generation consumes ApiKind::Method as "this is callable", then an accessor registered as a receiver-taking method could in principle surface res.complete() as callable where Node exposes a plain property.

That is a pre-existing property of how the dispatch table and manifest model accessors — these fifteen rows inherit it, they do not introduce it, and the alternative (registering them as Property and leaving the gate red) would be strictly worse. But if the two tables genuinely need a third classification for accessor-style dispatch, that is a real follow-up and this PR is the moment the question became visible.

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Landed via merge train 242 (#10830) as v0.5.1621e2a0839074.

Eight PRs travelled together because their file sets are disjoint — 30 files, +1,514/−101, zero overlap. Validated as one tree: ten cheap gates, -D warnings across all targets, five pinned artifacts byte-identical before and after, seven unit suites with an empty failing set, both compiler-output suites at failed_workloads=[], repsel_census rc=0, and a 250-fixture sweep with one area per PR (class 84, string 50, object 40, map 21, stream 18, bind 14, url 12, regex 11) — zero unexplained regressions.

Two of the eight needed a fix before they could land, both made in the train rather than bounced back.

#10816 bound sep_jv unconditionally in string/split.rs while reading it only inside #[cfg(feature = "regex-engine")], so RUSTFLAGS="-D warnings" cargo check -p perry --bins failed. Worth knowing why this is invisible in normal review: a one-invocation whole-workspace build unifies cargo features, so the regex engine is always on and the binding always read — only the per-package command, one of six run_lint_gates.sh derives, sees it. Same family as cargo check --lib not compiling cfg(test) code. Gated behind the feature that reads it; lim_jv on the next line was checked separately and is genuinely used outside the block.

#10817 added 15 dispatch entries without regenerating the docs, so the API-docs-drift check failed. Regenerated from a built binary: 2855 → 2870, exactly your 15, with perry.d.ts correctly unchanged at 2026 since those rows are dispatch-table rather than public surface. it_manifest_consistency passes on the assembled tree, which is the stronger signal — a green drift check only proves the files match the binary; that suite proves the manifest is internally consistent.

For future PRs in this area: scripts/regen_api_docs.sh hardcodes <worktree>/target/release/perry and, with that binary absent, regenerates from nothing and leaves both files truncated. A real regeneration moves the header counts and leaves the tail intact — worth checking the tail, not just the count.

One more thing, aimed at whoever cuts the next PR here: verify() flagged an exponential-backoff manifest entry in #10817 as missing from the train. That was correct — train 240 removed the binding, and restoring the entry would have failed manifest sync. main is moving several times an hour at the moment, so a PR cut against a base more than a few hours old is worth rebasing before review rather than after.

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