From 687f951035d6beb8ece64ce91a616b5ddddf48c5 Mon Sep 17 00:00:00 2001 From: jdalton Date: Fri, 31 Jul 2026 22:45:56 -0400 Subject: [PATCH 1/4] fix(api-manifest): reconcile API_MANIFEST with the native dispatch table The manifest_consistency drift guards in perry-codegen were red on main: - every_native_module_has_at_least_one_manifest_entry / #513: iovalkey (a NATIVE_MODULES member and well_known_bindings entry that normalizes to ioredis in dispatch) had zero API_MANIFEST rows, so strict mode never engaged for it. Add the Redis class + createClient rows, mirroring the redis alias. - every_dispatch_entry_has_manifest_counterpart: dgram Socket.sendto and sqlite DatabaseSync.serialize/deserialize existed in NATIVE_MODULE_TABLE with no manifest counterpart. Add the matching receiver-method rows. - every_well_known_binding_has_manifest_entry: the test's inline TOML parser misread the new [bindings.*.upstream] provenance sub-tables as binding names (dotenv.upstream, iovalkey.upstream, ...). Teach it to skip nested sub-tables so it only checks real routed module names. Regenerate docs/api/perry.d.ts and docs/src/api/reference.md from the manifest (api-docs-drift gate); this also folds in the previously uncommitted node-forge and tls.convertALPNProtocols entries. --- ...513-manifest-consistency-iovalkey-drift.md | 1 + .../perry-api-manifest/src/entries/part_1.rs | 9 +++++ .../perry-api-manifest/src/entries/part_4.rs | 8 +++++ .../tests/manifest_consistency.rs | 31 ++++++++++++---- docs/api/perry.d.ts | 30 +++++++++++++++- docs/src/api/reference.md | 35 ++++++++++++++++++- 6 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 changelog.d/0513-manifest-consistency-iovalkey-drift.md diff --git a/changelog.d/0513-manifest-consistency-iovalkey-drift.md b/changelog.d/0513-manifest-consistency-iovalkey-drift.md new file mode 100644 index 0000000000..6e4800f140 --- /dev/null +++ b/changelog.d/0513-manifest-consistency-iovalkey-drift.md @@ -0,0 +1 @@ +fix(api-manifest): backfill missing `API_MANIFEST` rows so it stays in sync with the codegen native dispatch table and `NATIVE_MODULES` — adds the `iovalkey` alias surface (`Redis` class + `createClient`, mirroring `redis`), the `dgram` `Socket.sendto` receiver method, and the `sqlite` `DatabaseSync.serialize`/`deserialize` receiver methods. Also fixes the `every_well_known_binding_has_manifest_entry` drift test to skip the `[bindings.*.upstream]` provenance sub-tables in `well_known_bindings.toml`, which its naive parser had been misreading as binding names. diff --git a/crates/perry-api-manifest/src/entries/part_1.rs b/crates/perry-api-manifest/src/entries/part_1.rs index aa4b99fe13..6bbf6d5de4 100644 --- a/crates/perry-api-manifest/src/entries/part_1.rs +++ b/crates/perry-api-manifest/src/entries/part_1.rs @@ -247,6 +247,12 @@ pub(crate) const API_MANIFEST_PART_1: &[ApiEntry] = &[ method("sqlite", "@@__perry_wk_dispose", true, None), method("sqlite", "exec", true, None), method("sqlite", "prepare", true, None), + // DatabaseSync.serialize()/deserialize() — dispatched by + // js_node_sqlite_database_sync_{serialize,deserialize}. Receiver + // methods on the base DatabaseSync instance (no class_filter), so + // they mirror the `open`/`close`/`exec`/`prepare` rows above. + method("sqlite", "serialize", true, None), + method("sqlite", "deserialize", true, None), method("sqlite", "function", true, Some("DatabaseSync")), method("sqlite", "aggregate", true, Some("DatabaseSync")), method("sqlite", "enableDefensive", true, Some("DatabaseSync")), @@ -534,6 +540,9 @@ pub(crate) const API_MANIFEST_PART_1: &[ApiEntry] = &[ class("dgram", "Socket"), method("dgram", "Socket", false, None), method("dgram", "send", true, Some("Socket")), + // Socket.sendto() — dispatched by js_dgram_socket_sendto (sibling of + // `send`); same Socket-filtered receiver shape. + method("dgram", "sendto", true, Some("Socket")), method("dgram", "bind", true, Some("Socket")), method("dgram", "close", true, Some("Socket")), method("dgram", "address", true, Some("Socket")), diff --git a/crates/perry-api-manifest/src/entries/part_4.rs b/crates/perry-api-manifest/src/entries/part_4.rs index fa8269c7e2..c2ea42014a 100644 --- a/crates/perry-api-manifest/src/entries/part_4.rs +++ b/crates/perry-api-manifest/src/entries/part_4.rs @@ -699,6 +699,14 @@ pub(crate) const API_MANIFEST_PART_4: &[ApiEntry] = &[ // from the ioredis class entries. --- class("redis", "Redis"), method("redis", "createClient", false, None), + // --- iovalkey — the Valkey fork of ioredis (valkey-io/iovalkey), + // served by the same perry-ext-ioredis surface. Dispatch + // normalizes `iovalkey` → `ioredis` (see native_module_dispatch.rs), + // so there are no iovalkey rows in NATIVE_MODULE_TABLE; these + // entries exist so the module clears the #513 strict-mode gate + // (module_has_any_entries) and mirror the `redis` alias above. --- + class("iovalkey", "Redis"), + method("iovalkey", "createClient", false, None), // --- date-fns — alias for dayjs (well-known routes both to // perry-ext-dayjs). Surface methods are the date-fns // functional API exposed by the wrapper. --- diff --git a/crates/perry-codegen/tests/manifest_consistency.rs b/crates/perry-codegen/tests/manifest_consistency.rs index 38a8ed56cb..a0ee87a780 100644 --- a/crates/perry-codegen/tests/manifest_consistency.rs +++ b/crates/perry-codegen/tests/manifest_consistency.rs @@ -287,19 +287,36 @@ fn every_well_known_binding_has_manifest_entry() { let mut missing: Vec = Vec::new(); for line in toml.lines() { let line = line.trim(); - // `[bindings.]` or `[bindings."">]` (quoted form - // used for names with `/` or `.`). + // `[bindings.]` or `[bindings.""]` (quoted form used + // for names with `/` or `.`). let Some(rest) = line.strip_prefix("[bindings.") else { continue; }; let Some(name_with_bracket) = rest.strip_suffix(']') else { continue; }; - // Strip optional surrounding double quotes. - let name = name_with_bracket - .strip_prefix('"') - .and_then(|s| s.strip_suffix('"')) - .unwrap_or(name_with_bracket); + // Skip nested sub-tables like `[bindings..upstream]` — those + // carry per-binding provenance metadata (version/sha256/repo), not + // a routed module name. Only the bare top-level `[bindings.]` + // header names a module the unimplemented-API gate must cover. + let name = if let Some(after_open_quote) = name_with_bracket.strip_prefix('"') { + // Quoted key such as `"decimal.js"`. A top-level binding header + // ends immediately after the closing quote; anything trailing + // (`"decimal.js".upstream`) is a sub-table. + let Some((inner, trailing)) = after_open_quote.split_once('"') else { + continue; // malformed / not a plain `[bindings."..."]` header + }; + if !trailing.is_empty() { + continue; // sub-table of a quoted binding key + } + inner + } else { + // Bare key. A dotted path (`dotenv.upstream`) is a sub-table. + if name_with_bracket.contains('.') { + continue; + } + name_with_bracket + }; if SIDE_EFFECT_ONLY.contains(&name) { continue; } diff --git a/docs/api/perry.d.ts b/docs/api/perry.d.ts index 54d8383746..9cbf60421d 100644 --- a/docs/api/perry.d.ts +++ b/docs/api/perry.d.ts @@ -1,6 +1,6 @@ // Auto-generated from Perry's API manifest (#465). Do not edit by hand. // Source: perry-api-manifest::API_MANIFEST -// Coverage: 1989 entries across 120 modules +// Coverage: 2000 entries across 122 modules type PerryU32 = number & { readonly __perryU32?: never }; type PerryU64 = number & { readonly __perryU64?: never }; @@ -1934,6 +1934,13 @@ declare module "ioredis" { export function createClient(p0: any): any; } +declare module "iovalkey" { + /** stdlib */ + export class Redis { [key: string]: any; } + /** stdlib */ + export function createClient(...args: any[]): any; +} + declare module "iroh" { /** stdlib */ export function bind(...args: any[]): any; @@ -2186,6 +2193,25 @@ declare module "node-fetch" { export default function (...args: any[]): any; } +declare module "node-forge" { + /** stdlib */ + export function certificateFromPem(...args: any[]): any; + /** stdlib */ + export function certificateToPem(...args: any[]): any; + /** stdlib */ + export function create(...args: any[]): any; + /** stdlib */ + export function createCertificate(...args: any[]): any; + /** stdlib */ + export function generateKeyPair(...args: any[]): any; + /** stdlib */ + export function privateKeyFromPem(...args: any[]): any; + /** stdlib */ + export function privateKeyToPem(...args: any[]): any; + /** stdlib */ + export function publicKeyToPem(...args: any[]): any; +} + declare module "node-pty" { /** stdlib */ const _default: any; @@ -3857,6 +3883,8 @@ declare module "tls" { /** stdlib */ export function connect(p0: any, p1: any, p2: any, p3: any): any; /** stdlib */ + export function convertALPNProtocols(protocols: any, out: any): any; + /** stdlib */ export function createSecureContext(options: any): any; /** stdlib */ export function createServer(options: any, secureConnectionListener: any): any; diff --git a/docs/src/api/reference.md b/docs/src/api/reference.md index bb55e9466d..13c5c18c9a 100644 --- a/docs/src/api/reference.md +++ b/docs/src/api/reference.md @@ -2,7 +2,7 @@ This page is auto-generated from Perry's compile-time API manifest (`perry-api-manifest::API_MANIFEST`). It is the source of truth for what `perry compile` accepts; references to symbols not listed here produce `R005 UnimplementedApi` (issue #463). Stubs (#464) are flagged ⚠ — they link cleanly but no-op at runtime on the chosen target. -Total: 2899 entries across 122 modules. +Total: 2917 entries across 124 modules. ## Modules @@ -50,6 +50,7 @@ Total: 2899 entries across 122 modules. - [`inspector`](#inspector) - [`inspector/promises`](#inspectorpromises) - [`ioredis`](#ioredis) +- [`iovalkey`](#iovalkey) - [`iroh`](#iroh) - [`jsonwebtoken`](#jsonwebtoken) - [`lodash`](#lodash) @@ -63,6 +64,7 @@ Total: 2899 entries across 122 modules. - [`net`](#net) - [`node-cron`](#node-cron) - [`node-fetch`](#node-fetch) +- [`node-forge`](#node-forge) - [`node-pty`](#node-pty) - [`nodemailer`](#nodemailer) - [`os`](#os) @@ -947,6 +949,7 @@ Total: 2899 entries across 122 modules. - `remoteAddress` — instance *(class: `Socket`)* - `removeListener` — instance *(class: `Socket`)* - `send` — instance *(class: `Socket`)* +- `sendto` — instance *(class: `Socket`)* - `setBroadcast` — instance *(class: `Socket`)* - `setMulticastInterface` — instance *(class: `Socket`)* - `setMulticastLoopback` — instance *(class: `Socket`)* @@ -1820,6 +1823,16 @@ Total: 2899 entries across 122 modules. - `quit` — instance - `set` — instance +## `iovalkey` + +### Classes + +- `Redis` + +### Methods + +- `createClient` — module + ## `iroh` ### Methods @@ -2174,6 +2187,23 @@ Total: 2899 entries across 122 modules. - `default` — module +## `node-forge` + +### Methods + +- `certificateFromPem` — module +- `certificateToPem` — module +- `create` — module +- `createCertificate` — module +- `generateKeyPair` — module +- `privateKeyFromPem` — module +- `privateKeyToPem` — module +- `publicKeyToPem` — module +- `setExtensions` — instance *(class: `Certificate`)* +- `setIssuer` — instance *(class: `Certificate`)* +- `setSubject` — instance *(class: `Certificate`)* +- `sign` — instance *(class: `Certificate`)* + ## `node-pty` ### Methods @@ -3128,6 +3158,7 @@ Total: 2899 entries across 122 modules. - `createSession` — instance - `createTagStore` — instance *(class: `DatabaseSync`)* - `db` — instance *(class: `SQLTagStore`)* +- `deserialize` — instance - `enableDefensive` — instance *(class: `DatabaseSync`)* - `enableLoadExtension` — instance - `exec` — instance @@ -3147,6 +3178,7 @@ Total: 2899 entries across 122 modules. - `prepare` — instance - `run` — instance *(class: `SQLTagStore`)* - `run` — instance +- `serialize` — instance - `setAllowBareNamedParameters` — instance - `setAllowUnknownNamedParameters` — instance - `setAuthorizer` — instance *(class: `DatabaseSync`)* @@ -3461,6 +3493,7 @@ Total: 2899 entries across 122 modules. - `checkServerIdentity` — module - `close` — instance *(class: `Server`)* - `connect` — module +- `convertALPNProtocols` — module - `createSecureContext` — module - `createServer` — module - `eventNames` — instance *(class: `Server`)* From 45e7be961fd5b26590e3598f263bd2bea8a688fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sat, 1 Aug 2026 10:57:09 +0200 Subject: [PATCH 2/4] chore(changelog): key the fragment to PR 7159 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit changelog.d fragments are named -.md (changelog.d/README.md), so in-flight PRs never collide. This one was keyed to the issue it fixes (0513) rather than the PR. Also confirms the merge resolution: regenerating both API-docs artifacts from the merged manifest reproduces the committed files byte-for-byte, and their only delta against main is the "Coverage:"/"Total:" header count — main's #7118 regenerated the artifacts without adding manifest rows, so nothing from main is dropped. --- ...alkey-drift.md => 7159-manifest-consistency-iovalkey-drift.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog.d/{0513-manifest-consistency-iovalkey-drift.md => 7159-manifest-consistency-iovalkey-drift.md} (100%) diff --git a/changelog.d/0513-manifest-consistency-iovalkey-drift.md b/changelog.d/7159-manifest-consistency-iovalkey-drift.md similarity index 100% rename from changelog.d/0513-manifest-consistency-iovalkey-drift.md rename to changelog.d/7159-manifest-consistency-iovalkey-drift.md From bb7c5b30adec9873b3ead402d0dcc260a336192a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sat, 1 Aug 2026 11:14:05 +0200 Subject: [PATCH 3/4] fix(api-manifest): cover the dispatch rows main added after this branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `every_dispatch_entry_has_manifest_counterpart` listed 14 missing rows at origin/main; this branch as authored fixed 3 of them (dgram::sendto, sqlite::serialize/deserialize). The other 11 landed on main *after* the branch point, so merging main re-reds the very gate the branch exists to turn green: inspector::log/info/debug/warn/error, inspector/promises::SessionCall — #7090 (47040d586) inspector Node 26.5 parity cluster::listeners/rawListeners/setMaxListeners/getMaxListeners — #7105 (76fa7b4c9) cluster Node 26 semantics lru-cache::peek — #7136 (5f5006aa6) lru-cache faithful JS-value keys/values Rows mirror how each surface is already modelled: - the five `inspector.console.*` forwarders carry the dispatch table's `class_filter: Some("console")`, so they hang off the existing `property("inspector", "console")` instead of becoming named exports; - `inspector/promises::SessionCall` is the synthetic row for `Session(...)` called without `new` (js_node_inspector_session_call_without_new), so it is `internal_method` — the .d.ts keeps advertising `Session`; - the four `cluster` EventEmitter methods join their eleven siblings as `internal_method` + paired `internal_property`, matching the #3687 block directly above them. All rows use the plain `method`/`internal_method` helpers (params: &[], returns: Any), which `manifest_param_counts_match_dispatch_table` deliberately skips as the "no signature data" fallback — so no arity claim is invented for them. manifest_consistency: 5 passed / 0 failed (was 2 passed / 3 failed at origin/main, 4 passed / 1 failed with only the original branch fix). docs/src/api/reference.md regenerated: +6 rendered entries (the internal_* rows stay out of the public surface, as cluster's existing ones already do); docs/api/perry.d.ts is byte-unchanged, since nothing added is a named export. --- crates/perry-api-manifest/src/entries/part_1.rs | 15 +++++++++++++++ crates/perry-api-manifest/src/entries/part_3.rs | 11 +++++++++++ docs/src/api/reference.md | 8 +++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/crates/perry-api-manifest/src/entries/part_1.rs b/crates/perry-api-manifest/src/entries/part_1.rs index 6bbf6d5de4..18f572a2bd 100644 --- a/crates/perry-api-manifest/src/entries/part_1.rs +++ b/crates/perry-api-manifest/src/entries/part_1.rs @@ -333,6 +333,14 @@ pub(crate) const API_MANIFEST_PART_1: &[ApiEntry] = &[ "returns immediately after open(); there is no debugger to wait for (#4916)", ), property("inspector", "console"), + // `inspector.console.*` — the Chrome-DevTools console forwarders (#7090). + // Dispatch carries `class_filter: Some("console")`, so these hang off the + // `console` property above rather than being named exports of the module. + method("inspector", "log", false, Some("console")), + method("inspector", "info", false, Some("console")), + method("inspector", "debug", false, Some("console")), + method("inspector", "warn", false, Some("console")), + method("inspector", "error", false, Some("console")), property("inspector", "Network"), class("inspector", "Session"), method("inspector", "Session", false, None), @@ -361,6 +369,11 @@ pub(crate) const API_MANIFEST_PART_1: &[ApiEntry] = &[ property("inspector/promises", "default"), class("inspector/promises", "Session"), method("inspector/promises", "Session", false, None), + // Synthetic dispatch row for `Session(...)` invoked without `new` + // (js_node_inspector_session_call_without_new, #7090). It is not a public + // named export, so it stays `internal_method` — the .d.ts must keep + // advertising `Session`, not `SessionCall`. + internal_method("inspector/promises", "SessionCall", false, None), method("inspector/promises", "connect", true, Some("Session")), method( "inspector/promises", @@ -1000,6 +1013,8 @@ pub(crate) const API_MANIFEST_PART_1: &[ApiEntry] = &[ method("lru-cache", "delete", true, None), method("lru-cache", "clear", true, None), method("lru-cache", "size", true, None), + // `peek(key)` — read without refreshing recency (#7136). + method("lru-cache", "peek", true, None), method("commander", "name", true, None), method("commander", "description", true, None), method("commander", "version", true, None), diff --git a/crates/perry-api-manifest/src/entries/part_3.rs b/crates/perry-api-manifest/src/entries/part_3.rs index 0f7e0f893c..70b9642ac3 100644 --- a/crates/perry-api-manifest/src/entries/part_3.rs +++ b/crates/perry-api-manifest/src/entries/part_3.rs @@ -605,6 +605,13 @@ pub(crate) const API_MANIFEST_PART_3: &[ApiEntry] = &[ internal_method("cluster", "removeListener", false, None), internal_method("cluster", "off", false, None), internal_method("cluster", "removeAllListeners", false, None), + // #7105 (Node 26 cluster semantics) added these four to + // NATIVE_MODULE_TABLE; they belong to the same EventEmitter surface, so + // they are `internal_method` like their siblings above. + internal_method("cluster", "listeners", false, None), + internal_method("cluster", "rawListeners", false, None), + internal_method("cluster", "setMaxListeners", false, None), + internal_method("cluster", "getMaxListeners", false, None), // Keep property reads registered so the #463 strict gate accepts the // namespace-export shape; `get_native_module_constant` returns undefined // for these names at runtime. @@ -619,6 +626,10 @@ pub(crate) const API_MANIFEST_PART_3: &[ApiEntry] = &[ internal_property("cluster", "emit"), internal_property("cluster", "eventNames"), internal_property("cluster", "listenerCount"), + internal_property("cluster", "listeners"), + internal_property("cluster", "rawListeners"), + internal_property("cluster", "setMaxListeners"), + internal_property("cluster", "getMaxListeners"), // =========================================================== // #513 Phase A: backfill receiver-less surface for modules that // previously had zero entries. Without these, `module_has_any_entries` diff --git a/docs/src/api/reference.md b/docs/src/api/reference.md index 13c5c18c9a..202fd18e3a 100644 --- a/docs/src/api/reference.md +++ b/docs/src/api/reference.md @@ -2,7 +2,7 @@ This page is auto-generated from Perry's compile-time API manifest (`perry-api-manifest::API_MANIFEST`). It is the source of truth for what `perry compile` accepts; references to symbols not listed here produce `R005 UnimplementedApi` (issue #463). Stubs (#464) are flagged ⚠ — they link cleanly but no-op at runtime on the chosen target. -Total: 2917 entries across 124 modules. +Total: 2923 entries across 124 modules. ## Modules @@ -1769,13 +1769,18 @@ Total: 2917 entries across 124 modules. - `close` — module - `connect` — instance *(class: `Session`)* - `connectToMainThread` — instance *(class: `Session`)* +- `debug` — module *(class: `console`)* - `disconnect` — instance *(class: `Session`)* +- `error` — module *(class: `console`)* +- `info` — module *(class: `console`)* +- `log` — module *(class: `console`)* - `on` — instance *(class: `Session`)* - `once` — instance *(class: `Session`)* - `open` — module ⚠ **stub** — accepts port/host but binds no real WebSocket inspector endpoint; sessions are in-process fakes (#4916) - `post` — instance *(class: `Session`)* ⚠ **stub** — only Runtime.enable and a canned Runtime.evaluate subset respond; every other protocol method throws Inspector error -32601 (#4916) - `url` — module ⚠ **stub** — always undefined: Perry never exposes a real inspector endpoint (#4916) - `waitForDebugger` — module ⚠ **stub** — returns immediately after open(); there is no debugger to wait for (#4916) +- `warn` — module *(class: `console`)* ### Properties @@ -1900,6 +1905,7 @@ Total: 2917 entries across 124 modules. - `delete` — instance - `get` — instance - `has` — instance +- `peek` — instance - `set` — instance - `size` — instance From f567a9899967a0c30e34191fdbe96d8c4f99a934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sat, 1 Aug 2026 11:14:30 +0200 Subject: [PATCH 4/4] docs(changelog): note the post-branch dispatch rows in the 7159 fragment --- changelog.d/7159-manifest-consistency-iovalkey-drift.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/7159-manifest-consistency-iovalkey-drift.md b/changelog.d/7159-manifest-consistency-iovalkey-drift.md index 6e4800f140..9c883ddaed 100644 --- a/changelog.d/7159-manifest-consistency-iovalkey-drift.md +++ b/changelog.d/7159-manifest-consistency-iovalkey-drift.md @@ -1 +1 @@ -fix(api-manifest): backfill missing `API_MANIFEST` rows so it stays in sync with the codegen native dispatch table and `NATIVE_MODULES` — adds the `iovalkey` alias surface (`Redis` class + `createClient`, mirroring `redis`), the `dgram` `Socket.sendto` receiver method, and the `sqlite` `DatabaseSync.serialize`/`deserialize` receiver methods. Also fixes the `every_well_known_binding_has_manifest_entry` drift test to skip the `[bindings.*.upstream]` provenance sub-tables in `well_known_bindings.toml`, which its naive parser had been misreading as binding names. +fix(api-manifest): backfill missing `API_MANIFEST` rows so it stays in sync with the codegen native dispatch table and `NATIVE_MODULES` — adds the `iovalkey` alias surface (`Redis` class + `createClient`, mirroring `redis`), the `dgram` `Socket.sendto` receiver method, and the `sqlite` `DatabaseSync.serialize`/`deserialize` receiver methods. Also fixes the `every_well_known_binding_has_manifest_entry` drift test to skip the `[bindings.*.upstream]` provenance sub-tables in `well_known_bindings.toml`, which its naive parser had been misreading as binding names. Also covers the dispatch rows that landed while this was in review — `inspector.console.log`/`info`/`debug`/`warn`/`error` and `inspector/promises`'s no-`new` `Session` shim (#7090), `cluster.listeners`/`rawListeners`/`setMaxListeners`/`getMaxListeners` (#7105), and `lru-cache`'s `peek` (#7136) — so `manifest_consistency` is fully green rather than merely less red.