Skip to content

refactor(stdlib): remove uuid native binding - #10701

Closed
proggeramlug wants to merge 2 commits into
mainfrom
wip/remove-uuid-binding
Closed

proggeramlug wants to merge 2 commits into
mainfrom
wip/remove-uuid-binding

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Removes the native uuid binding so import { v4, parse, stringify, NIL } from "uuid" (no perry.compilePackages entry) resolves to the real npm
package, compiled from source, per the owner's decision to stop shipping
hand-written Rust reimplementations of npm packages.

This is the last of five verified binding removals in this campaign
(#10687 jsonwebtoken, #10690 validator, #10691 dotenv, #10693 nanoid).

The bug

The native binding is missing parse/stringify entirely — a
parse/stringify roundtrip throws (bytes comes back undefined, so
bytes.length crashes with TypeError). NIL reads as undefined:
js_uuid_nil exists in the deleted source but was never wired into either
the NativeModSig dispatch table or the API manifest, so property access
on the uuid module namespace fell through to undefined.
v1/v3/v4/v5/validate/version were correct in both engines.

What was found and removed

Per #10678 (duplicate extern "C" exports across perry-ext-*/perry-stdlib
pairs), this binding existed twice:

  • crates/perry-ext-uuid/ — the governance-tracked binding crate, wired
    into well_known_bindings.toml's [bindings.uuid].
  • crates/perry-stdlib/src/uuid.rs (127 lines) — a second, independent
    implementation behind the bundled-uuid feature, exporting the same
    js_uuid_* symbols.

Removed both crates, the 7-entry NativeModSig dispatch block
(native_table/utils_crypto.rs), the well_known_bindings.toml entry, the
"uuid" NATIVE_MODULES entry and its 7 manifest rows, the bundled-uuid
stdlib feature (ids umbrella now expands to just ["bundled-nanoid"]),
and the 6 Android stub exports. Dropped "uuid" from the shared
shipped_unproven_bindings_are_partial test array in
crates/perry/src/commands/compile/well_known.rs (kept dotenv/nanoid
their own PRs in this campaign are still open, not yet merged).

The crypto/random.rs entanglement

crypto.randomUUID() / crypto.randomUUID({ v7: true })
(crates/perry-stdlib/src/crypto/random.rs) and nodemailer's message-id
generation (crates/perry-stdlib/src/nodemailer.rs) both call the uuid
Cargo crate directly and unconditionally
(uuid::Uuid::new_v4()/now_v7()) — neither call site has a
cfg(feature = ...) gate anywhere above it. The uuid crate dependency in
perry-stdlib/Cargo.toml was declared optional = true, gated behind the
bundled-uuid npm-binding feature, but was never actually optional in
practice — those two call sites needed it unconditionally regardless of
whether bundled-uuid was enabled. Removing the bundled-uuid feature
without also dropping optional = true would have broken the build the
moment that feature stopped being enabled by default.

Fix: made uuid a required (non-optional) dependency in
perry-stdlib/Cargo.toml, with a comment explaining why, and retargeted
the ids feature umbrella to drop bundled-uuid. crypto.randomUUID() /
randomUUID({ v7: true }) and node:crypto's randomBytes are unaffected
— verified below (also relevant given #10643's recent Web Crypto
method-closure-caching fix touched adjacent code).

Also retargeted the one dts-shape regression test that used uuid.v4() as
its zero-arg-module-function fixture
(perry-api-manifest::emit::tests::dts_uuid_v4_has_no_args) to
perry/gc.minor(), which is unrelated to any binding-removal churn.

Acceptance test: real uuid, no compilePackages entry, roundtrip + NIL

{ "dependencies": { "uuid": "^11.0.0" } }
import { v4, v1, v3, v5, validate, version, parse, stringify, NIL } from "uuid";

const id = v4();
const bytes = parse(id);            // was: undefined (crash on the next read)
const back = stringify(bytes);      // roundtrip
console.log(back === id);           // true
console.log(NIL);                   // "00000000-0000-0000-0000-000000000000" (was: undefined)

Built on perrymaster (--profile perry-dev). Ran the binary and diffed
against node --experimental-strip-types (Node 26.5.1): byte-for-byte
identical
— including the deterministic v5/v3 (name+namespace) values.

Proved the bug on a pristine baseline first: built origin/main
(8df83f8c1) in a separate worktree and ran the identical program there.
It printed NIL: undefined, parse instanceof Uint8Array: false, then
crashed: TypeError: Cannot read properties of undefined (reading 'length') — reproducing the reported bug exactly, before the fix was
applied.

crypto.randomUUID() re-checked against the fixed build: still returns a
valid v4 UUID (validate() true, version() 4).

Verification

  • cargo build --profile perry-dev -p perry -p perry-runtime-static -p perry-stdlib-static: clean; confirmed .a mtimes moved.
  • cargo check --workspace --all-targets (host-compatible exclusion set
    via workspace_architecture.py --print-excluded-scope host-compatible)
    under -D warnings: clean.
  • cargo test -p perry-api-manifest: 39+4 passing (after retargeting the
    uuid-fixture test above).
  • cargo test -p perry-codegen --test manifest_consistency: 5/5 passing.
  • cargo test -p perry --bin perry -- well_known: 27/27 passing.
  • cargo test -p perry-hir: full suite passing
    (test_lower_native_module_registration uses "uuid" only as synthetic
    example data for a generic register/lookup mechanism — unaffected by the
    registry removal).
  • python3 scripts/binding_governance.py --check: OK (39 extension
    crates).
  • node scripts/binding_pins.mjs --check: OK (37 pinned, lock-step holds).
  • python3 scripts/workspace_architecture.py --check: OK.
  • python3 scripts/native_result_ledger.py: OK, 371 rows / 322 providers
    unchanged (none of uuid's dispatch rows used a ledger-tracked NR_*
    kind).
  • python3 scripts/string_payload_access_inventory.py --write-baseline:
    perry-stdlib inline-offset 40 → 38 (uuid.rs's own 2 sites).
  • Regenerated docs/api/perry.d.ts + docs/src/api/reference.md
    (perry --print-api-manifest) and
    docs/src/native-libraries/governance.md (binding_governance.py --table) from the fixed manifest.
  • cargo fmt --all -- --check: clean.
  • scripts/run_lint_gates.sh (SKIP_COMPILE_GATES=1): 76 of 77
    passed
    ; the one failure (Public benchmark evidence freshness) is the
    pre-existing, known-red-on-every-PR gate per this campaign's contract.

Not run / out of scope

  • Compile tier of run_lint_gates.sh (known-red on Linux per this
    campaign's contract).
  • Full gap suite (host stalls under auto-optimize per contract); ran the
    targeted registry/consistency tests plus the direct acceptance test
    (including a pristine-baseline A/B) instead.
  • test-files/test_parity_uuid.ts is already excluded from the parity
    gate (test-parity/known_failures.json, "ci-env": Node's own oracle
    run fails ERR_MODULE_NOT_FOUND because uuid was never added to the
    repo's root package.json/package-lock.json — the same pre-existing
    gap documented for nanoid, parity: 2026-08-17 dark-debt audit — 93 parity + 27 compile failures unlisted after six dark weeks (90.7% aggregate) #8271). Its @covers comment now points at a
    deleted file (crates/perry-stdlib/src/uuid.rs); left untouched,
    matching how the sibling nanoid PR (refactor(stdlib): remove nanoid native binding #10693) left its own equivalent
    parity fixture alone.
  • No version bump / CLAUDE.md edit — per this campaign's convention, the
    maintainer bumps at merge time.
  • crates/perry-ui-android/src/stdlib_stubs.rs was edited to remove the
    matching 6 js_uuid_* stub exports (following the established pattern
    from the sibling removals) but not build-verified — this host has no
    Android NDK and the package is excluded from the host-compatible check
    scope.

Rebase onto main @ 7c5d04d0e (v0.5.1601), 2026-09-19

Force-pushed a rebase (5ade053d8a067f2ae86e) past #10690 (validator)
and an unrelated jsonwebtoken removal that also landed on main in the
meantime (fbac2de2f). #10691 (dotenv) has not merged yet — still open
— so its entries are untouched here.

Conflicts (7 files) resolved by intent — main's current state minus
uuid's entries, never either side's copy wholesale:
crates/perry-codegen/src/lower_call/native_table/utils_crypto.rs,
crates/perry-ui-android/src/stdlib_stubs.rs, docs/api/perry.d.ts,
docs/src/api/reference.md, docs/src/native-libraries/governance.md,
scripts/string_payload_access_baseline.txt, workspace-architecture.json.
Two of these (utils_crypto.rs, stdlib_stubs.rs) hit the brace-folding
trap CLAUDE.md documents (both sides' trailing } lines were textually
identical, so git folded them into "common" context) — resolved by
inspection, confirmed by a clean compile.

Cargo.lock: took main's side, then regenerated via
cargo metadata --offline (not hand-merged). Verified: zero conflict
markers, zero perry-ext-uuid entries, uuid itself resolves once
(1.26.0, required dep), cargo metadata accepts the tree.

Counts recomputed from the resolved tree, independently confirmed:

metric value confirmed via
workspace members 79 workspace_architecture.py --check --print-summary
decision_counts externalize=30, keep=44, merge=1, remove=1, review=3 (sum 79) same
binding governance 37 extension crates binding_governance.py --check
binding pins 35 pinned binding_pins.mjs --check (Node 26.5.1)
perry-stdlib inline-offset 39 → 37 string_payload_access_inventory.py --write-baseline

Docs regenerated for real (not hand-edited): built target/perry-dev/perry
in-tree and ran --print-api-manifest=markdown/=dts directly (avoids the
scripts/regen_api_docs.sh out-of-tree-truncation trap — CLAUDE.md's ★★
gate — while still matching its output, since the manifest is compile-time
data unaffected by optimization level). docs/api/perry.d.ts: 2085→2078
entries, 134→133 modules. docs/src/api/reference.md: 3027→3020 entries,
136→135 modules. git diff --stat on both: 2 lines each (header only,
tail intact) — the regeneration signature, not the truncation one.

native_result_ledger.py now fails (expected 371 classified rows, found 376) — but this is pre-existing on pristine origin/main
(7c5d04d0e) too, confirmed byte-for-byte identical via a throwaway
git worktree: the script's EXPECTED_ROWS constant wasn't bumped when
the validator/jsonwebtoken removals landed. It's not in
run_lint_gates.sh's suite and its own workflow
(native-result-ledger.yml) is path-filtered to only run when
scripts/native_result_ledger.py/.tsv themselves change — neither of
which this PR touches — so it doesn't gate this PR's CI. Reporting
separately rather than fixing it here (out of scope per this PR's
contract: don't block a removal on pre-existing drift it didn't cause).

Re-verified after rebase (rebuilt perry-dev in-tree, .a/binary
mtimes moved):

  • Acceptance test re-run end to end: fresh npm install uuid (v11, real
    npm package, no perry.compilePackages entry), compiled, diffed against
    node --experimental-strip-types (Node 26.5.1) — byte-for-byte
    identical
    , including deterministic v3/v5, NIL, and the parse/stringify
    roundtrip. crypto.randomUUID() re-checked standalone: valid v4 UUID.
  • cargo check --workspace --all-targets (host-compatible exclusion set)
    under -D warnings: clean, 0 warnings.
  • cargo fmt --all -- --check: clean.
  • scripts/run_lint_gates.sh (SKIP_COMPILE_GATES=1): 76 of 77 passed —
    same pre-existing Public benchmark evidence freshness failure as
    before.
  • scripts/check_file_size.sh: OK.
    crates/perry-runtime/src/object/native_module/module_keys.rs untouched
    by this PR's resolution (still 1992/2000 lines; this removal didn't add
    to it).
  • git diff --stat checked before every commit — no destructive doc
    truncation.
  • No resurrected workspace member: cargo metadata accepts the tree; root
    Cargo.toml's members list and path-dependency table both lack
    perry-ext-uuid.
  • Changelog fragment (changelog.d/10701-uuid-native-binding-removal.md)
    survived the rebase and the Cargo.lock conflict resolution intact.
  • Not re-run this round (ran clean pre-rebase per the section above;
    source of these crates is byte-identical after the rebase, only shared
    registry files changed): the per-crate cargo test runs, and the compile
    tier of run_lint_gates.sh (still known-red on Linux).

Pushed SHA: 067f2ae86ee5cfd2aa6c8dc702971047b18ae4ed.

Summary by CodeRabbit

  • Bug Fixes

    • The uuid package now compiles from its standard npm source, improving Node.js compatibility, including parse, stringify, and NIL behavior.
    • UUID functionality remains available for crypto.randomUUID(), UUID v7 generation, and email message IDs.
  • Breaking Changes

    • Perry’s bundled native uuid module and helper APIs (v1, v3, v4, v5, v7, validate, and version) are no longer provided.
  • Documentation

    • Removed the bundled uuid module from API declarations and reference documentation.

@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 7f2e077c-ffb5-462b-91e0-3b0a2fa76053

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 44dbc822-4a58-487a-8a4d-dc253a4f70c8

📥 Commits

Reviewing files that changed from the base of the PR and between 5ade053 and 067f2ae.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • Cargo.toml
  • crates/perry-api-manifest/src/entries/part_1.rs
  • crates/perry-codegen/src/lower_call/native_table/utils_crypto.rs
  • crates/perry-stdlib/Cargo.toml
  • crates/perry-stdlib/src/lib.rs
  • crates/perry-ui-android/src/stdlib_stubs.rs
  • crates/perry/src/commands/stdlib_features.rs
  • docs/api/perry.d.ts
  • docs/src/api/reference.md
  • docs/src/native-libraries/governance.md
  • scripts/string_payload_access_baseline.txt
  • scripts/unrooted_local_shape_baseline.json
  • workspace-architecture.json
💤 Files with no reviewable changes (8)
  • Cargo.toml
  • docs/src/native-libraries/governance.md
  • crates/perry-codegen/src/lower_call/native_table/utils_crypto.rs
  • crates/perry-stdlib/src/lib.rs
  • crates/perry/src/commands/stdlib_features.rs
  • scripts/unrooted_local_shape_baseline.json
  • crates/perry-api-manifest/src/entries/part_1.rs
  • crates/perry-ui-android/src/stdlib_stubs.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • scripts/string_payload_access_baseline.txt
  • docs/src/api/reference.md

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


📝 Walkthrough

Walkthrough

The pull request removes the bundled native uuid binding, duplicate runtime implementations, compiler registrations, API declarations, documentation, and tracking entries. The Rust uuid dependency remains required for standard-library crypto functions.

Changes

UUID Binding Removal

Layer / File(s) Summary
Remove the UUID binding crate
Cargo.toml, crates/perry-ext-uuid/*, crates/perry/well_known_bindings.toml, docs/src/native-libraries/governance.md, workspace-architecture.json
The perry-ext-uuid crate and its workspace, binding registry, governance, and architecture entries were deleted.
Remove duplicate UUID runtime symbols
crates/perry-stdlib/Cargo.toml, crates/perry-stdlib/src/lib.rs, crates/perry-stdlib/src/uuid.rs, crates/perry-ui-android/src/stdlib_stubs.rs
The bundled-uuid feature, native UUID module, FFI exports, and Android stubs were removed. The uuid Cargo dependency is now required for crypto functionality.
Remove UUID compiler and manifest entries
crates/perry-api-manifest/*, crates/perry-codegen/src/lower_call/native_table/utils_crypto.rs, crates/perry/src/commands/*
UUID was removed from native-module resolution, API signatures, dispatch rows, feature mapping, and compatibility tests.
Update published references and baselines
changelog.d/10701-uuid-native-binding-removal.md, docs/api/perry.d.ts, docs/src/api/reference.md, scripts/*
The changelog, generated API references, and baseline files were updated for the removed binding.

Priority: ➖ Normal

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

Change: Refactor

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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 3 functions across 3 files. (4 skipped: 4 …
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.
Title check ✅ Passed The title clearly and concisely identifies the main change: removal of the native UUID binding.
Description check ✅ Passed The description is detailed and covers the summary, implementation changes, related issues, test plan, verification results, known failures, and out-of-scope items. It does not use every template head…
✨ Finishing Touches 💡 2
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch wip/remove-uuid-binding
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • 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.

Ralph Küpper added 2 commits September 19, 2026 14:46
Removes the native uuid binding so `import { v4, parse, stringify, NIL }
from "uuid"` (no perry.compilePackages entry) resolves to the real npm
package compiled from source, per the owner's decision to stop shipping
hand-written Rust reimplementations of npm packages.

The native binding is missing `parse`/`stringify` entirely (a
`parse`/`stringify` roundtrip throws: `bytes` comes back `undefined`),
and `NIL` reads as `undefined` (js_uuid_nil exists in the deleted source
but was never wired into either NativeModSig dispatch table or the API
manifest, so property access on the uuid module namespace fell through
to undefined). v1/v3/v4/v5/validate/version were correct in both.

Per #10678 (duplicate extern "C" exports across perry-ext-*/perry-stdlib
pairs), this binding existed twice: crates/perry-ext-uuid/ (the
governance-tracked binding crate) and crates/perry-stdlib/src/uuid.rs (a
second, independent implementation behind the now-removed bundled-uuid
feature). Both are deleted, along with the 7-entry NativeModSig dispatch
block in native_table/utils_crypto.rs, the well_known_bindings.toml
entry, the "uuid" NATIVE_MODULES entry and its 7 manifest rows, and the
6 Android stub exports.

crypto/random.rs entanglement: crypto.randomUUID()/randomUUID({v7:true})
and nodemailer's message-id generation both call the `uuid` Cargo crate
(uuid::Uuid::new_v4()/now_v7()) directly and unconditionally — neither
site has a cfg(feature) gate. The `uuid` crate dependency in
perry-stdlib/Cargo.toml was therefore never actually optional in
practice even though it was declared `optional = true` behind the
bundled-uuid npm-binding feature; removing that feature without also
dropping `optional = true` would have broken the build the moment
bundled-uuid stopped being enabled. Made `uuid` a required (non-optional)
dependency and retargeted the `ids` feature umbrella to
`["bundled-nanoid"]`. crypto.randomUUID()/randomUUID({v7:true}) and
node:crypto's randomBytes are unaffected — verified below.

Retargeted the one dts-shape regression test that used uuid.v4() as its
zero-arg-module-function fixture (perry-api-manifest's
dts_uuid_v4_has_no_args) to perry/gc.minor(), which is unrelated to any
binding-removal churn.

Built on perrymaster (--profile perry-dev). A real `npm install uuid`
project (no perry.compilePackages entry) exercising v1/v4/v5/v3,
validate/version, a parse/stringify roundtrip, and NIL, diffed against
`node --experimental-strip-types` (Node 26.5.1): byte-for-byte identical,
including the deterministic v5/v3 (name+namespace) values. Confirmed
against a pristine origin/main (8df83f8) baseline build that the same
program crashes there: `NIL: undefined`, `parse instanceof Uint8Array:
false`, then `TypeError: Cannot read properties of undefined (reading
'length')` on the roundtrip — reproducing the reported bug exactly.
crypto.randomUUID() and node:crypto's randomBytes/randomUUID were
re-checked against the fix and still work (both call the `uuid` crate
directly, unaffected by the binding removal).

- cargo build --profile perry-dev -p perry -p perry-runtime-static -p
  perry-stdlib-static: clean; confirmed .a mtimes moved.
- cargo check --workspace --all-targets (host-compatible exclusion set
  via workspace_architecture.py --print-excluded-scope) under
  -D warnings: clean.
- cargo test -p perry-api-manifest: 39+4 passing (after retargeting the
  uuid-fixture test).
- cargo test -p perry-codegen --test manifest_consistency: 5/5 passing.
- cargo test -p perry --bin perry -- well_known: 27/27 passing.
- cargo test -p perry-hir: full suite passing (test_lower_native_module_
  registration uses "uuid" only as synthetic example data for a generic
  register/lookup mechanism — unaffected by the registry removal).
- python3 scripts/binding_governance.py --check: OK (39 extension
  crates).
- node scripts/binding_pins.mjs --check: OK (37 pinned, lock-step
  holds).
- python3 scripts/workspace_architecture.py --check: OK.
- python3 scripts/native_result_ledger.py: OK, 371 rows/322 providers
  unchanged (none of uuid's dispatch rows used a ledger-tracked NR_*
  kind).
- python3 scripts/string_payload_access_inventory.py --write-baseline:
  perry-stdlib inline-offset 40 -> 38 (uuid.rs's own 2 sites).
- Regenerated docs/api/perry.d.ts + docs/src/api/reference.md
  (perry --print-api-manifest) and docs/src/native-libraries/
  governance.md (binding_governance.py --table) from the fixed manifest.
- cargo fmt --all -- --check: clean.
- scripts/run_lint_gates.sh (SKIP_COMPILE_GATES=1): 76 of 77 passed; the
  one failure (Public benchmark evidence freshness) is the pre-existing,
  known-red-on-every-PR gate per this campaign's contract.

- Compile tier of run_lint_gates.sh (known-red on Linux per this
  campaign's contract).
- Full gap suite (host stalls under auto-optimize per contract).
- test-files/test_parity_uuid.ts is already excluded from the parity
  gate (test-parity/known_failures.json, "ci-env": Node's own oracle run
  fails ERR_MODULE_NOT_FOUND because uuid was never added to the repo's
  root package.json/package-lock.json — the same pre-existing gap
  documented for nanoid, #8271). Its `@covers` comment now points at a
  deleted file (crates/perry-stdlib/src/uuid.rs); leaving it untouched,
  matching how the sibling nanoid PR (#10693) left its own equivalent
  parity fixture alone.
- No version bump / CLAUDE.md edit — per this campaign's convention, the
  maintainer bumps at merge time.
- crates/perry-ui-android/src/stdlib_stubs.rs was edited to remove the
  matching 6 js_uuid_* stub exports (following the established pattern
  from the sibling removals) but not build-verified — this host has no
  Android NDK and the package is excluded from the host-compatible
  check scope.
@proggeramlug

Copy link
Copy Markdown
Contributor Author

Landed in merge train 225 (#10748), released as v0.5.1604 — main is now 053b9ccac4.

Closing rather than merging is how trains work here: both PRs were cherry-picked onto one tree, validated together, and landed under the train's own commit, so GitHub cannot mark this one merged even though your change is on main.

Verified after the merge: native_result_ledger.py now passes on main — 376 rows, 326 providers; NR_FOREIGN_PTR=4 NR_GCPTR=131 NR_HANDLE_ID=226 NR_JS_VALUE=13 NR_NULLABLE_GCPTR=2. That gate had been red since merge train 221 and went unnoticed because its workflow is path-filtered to the ledger's own two files.

For #10701 specifically: both of its conflicts were absolute-count files, which auto-merge cleanly while being wrong. Resolved by re-derivation against the assembled tree rather than hand-merge — Cargo.lock regenerated from the resolved manifest (no perry-ext-uuid entries, cargo metadata accepting 1071 packages), and unrooted_local_shape_baseline.json re-derived from 580 to 578, since removing the binding drops two findings. --check agrees at the new number.

Validation: all nine cheap gates, cargo check --workspace --all-targets under -D warnings, the release build of all five pinned artifacts, every unit suite, and a 5-area gap sweep with zero unexplained regressions and each area asserted to have run a non-zero number of tests.

proggeramlug pushed a commit that referenced this pull request Sep 19, 2026
customAlphabet(alphabet, size) is documented to return a generator
function; native customAlphabet instead returns the generated id string
directly (js_nanoid_custom's own doc comment: "For simplicity, we combine
this into one call"), so the only documented usage --
const gen = customAlphabet(...); gen(); -- crashes with
TypeError: value is not a function.

Per #10678 (duplicate extern "C" exports across perry-ext-*/perry-stdlib
pairs), this binding existed twice: crates/perry-ext-nanoid/ (governance-
tracked) and crates/perry-stdlib/src/nanoid.rs (a second, independent
implementation behind the default-on bundled-nanoid feature, exporting the
same js_nanoid/js_nanoid_sized/js_nanoid_custom symbols).

customAlphabet is declared to codegen (data_stores.rs's js_nanoid_custom)
but has no call-site wiring anywhere in perry-codegen -- no NativeModSig
row, no lower_call special case. Only plain nanoid(size) had a dispatch row
(native_table/utils_crypto.rs, routing to js_nanoid_sized), consistent with
customAlphabet not being a first-class compiled call at all.

Removed both crates, the 1-entry NativeModSig dispatch row, the 2
js_nanoid* FFI declarations, the well_known_bindings.toml entry, the
"nanoid" NATIVE_MODULES entry + manifest row, the bundled-nanoid stdlib
feature, and 2 Android stub exports. Dropped "nanoid" from the shared
shipped_unproven_bindings_are_partial test array (dotenv remains; it is
#10691's to remove).

The "ids" umbrella is now EMPTY. It was retargeted to ["bundled-nanoid"]
when #10701 removed uuid (train 225); removing bundled-nanoid leaves it
with no members, so it is kept as `ids = []` -- an intentionally harmless
no-op that preserves `--features ids` for existing callers rather than
breaking them. The stale comments that described the two-member split
(perry-stdlib/Cargo.toml, perry-stdlib/src/lib.rs, stdlib_features.rs) are
rewritten to say so. perry-stdlib's `uuid` crate dependency is untouched:
#10701 already made it non-optional because crypto/random.rs calls it
unconditionally.

test-files/test_parity_nanoid.ts (the exact customAlphabet(...)();
reproduction) is already excluded from the parity gate --
known_failures.json classifies it "ci-env": Node's own oracle fails with
ERR_MODULE_NOT_FOUND in CI because nanoid was never added to the repo's
root package.json, so npm ci never installs it. Not touched -- provisioning
a real npm dependency in the root package.json is out of scope for a
binding-removal PR, and the test remains excluded before and after this
change for the same underlying reason.

Regenerated docs/api/perry.d.ts (2067 entries/132 modules -> 2066/131) and
docs/src/api/reference.md (3009/134 -> 3008/133) by running the built
binary's --print-api-manifest, not by editing the headers. Regenerated
docs/src/native-libraries/governance.md's row. Re-derived
workspace-architecture.json from scripts/workspace_architecture.py against
the resolved tree: workspace_members 78 -> 77, externalize 29 -> 28,
keep 44 (unchanged). Cargo.lock regenerated with `cargo metadata`, not
hand-merged.

Rebased onto v0.5.1605 (train 226). The pre-rebase branch was written when
uuid, jsonwebtoken and validator still existed, so most conflicts were of
the form "main deleted X, this branch deleted Y, in one hunk"; every one
was resolved to main's current content minus nanoid's own entries, which
is neither side. Two of them (entries/part_1.rs, native_table/
utils_crypto.rs) fold on a shared trailing `),`/`},`, which silently left a
dangling `method_sig(` opener until `cargo fmt --all -- --check` rejected
it.
proggeramlug pushed a commit that referenced this pull request Sep 19, 2026
customAlphabet(alphabet, size) is documented to return a generator
function; native customAlphabet instead returns the generated id string
directly (js_nanoid_custom's own doc comment: "For simplicity, we combine
this into one call"), so the only documented usage --
const gen = customAlphabet(...); gen(); -- crashes with
TypeError: value is not a function.

Per #10678 (duplicate extern "C" exports across perry-ext-*/perry-stdlib
pairs), this binding existed twice: crates/perry-ext-nanoid/ (governance-
tracked) and crates/perry-stdlib/src/nanoid.rs (a second, independent
implementation behind the default-on bundled-nanoid feature, exporting the
same js_nanoid/js_nanoid_sized/js_nanoid_custom symbols).

customAlphabet is declared to codegen (data_stores.rs's js_nanoid_custom)
but has no call-site wiring anywhere in perry-codegen -- no NativeModSig
row, no lower_call special case. Only plain nanoid(size) had a dispatch row
(native_table/utils_crypto.rs, routing to js_nanoid_sized), consistent with
customAlphabet not being a first-class compiled call at all.

Removed both crates, the 1-entry NativeModSig dispatch row, the 2
js_nanoid* FFI declarations, the well_known_bindings.toml entry, the
"nanoid" NATIVE_MODULES entry + manifest row, the bundled-nanoid stdlib
feature, and 2 Android stub exports.

DELETED the shipped_unproven_bindings_are_partial test rather than emptying
it. Its subject population was exactly the hand-written wrappers shipped
without proven upstream parity: uuid (#10701), dotenv (#10691, landed in
train 227) and nanoid (here). With the last one gone the array would read
`for name in []` -- a test that compiles, runs, asserts nothing and reports
green forever, which is failure mode #4 in CLAUDE.md's "four ways a gate can
be unable to fail". Coverage is not lost: shipped_subset_bindings_are_partial
is a separate test and still asserts the same property for undici,
node-forge, lru-cache and qs. Verified the deletion orphans nothing --
the test module is `use super::*`, lookup_well_known has 9 other callers,
BindingCompat::Partial has 5 other uses, and nothing in the tree keys on
the test's name.

The "ids" umbrella is now EMPTY. It was retargeted to ["bundled-nanoid"]
when #10701 removed uuid (train 225); removing bundled-nanoid leaves it
with no members, so it is kept as `ids = []` -- an intentionally harmless
no-op that preserves `--features ids` for existing callers rather than
breaking them. The stale comments that described the two-member split
(perry-stdlib/Cargo.toml, perry-stdlib/src/lib.rs, stdlib_features.rs) are
rewritten to say so. perry-stdlib's `uuid` crate dependency is untouched:
#10701 already made it non-optional because crypto/random.rs calls it
unconditionally.

test-files/test_parity_nanoid.ts (the exact customAlphabet(...)();
reproduction) is already excluded from the parity gate --
known_failures.json classifies it "ci-env": Node's own oracle fails with
ERR_MODULE_NOT_FOUND in CI because nanoid was never added to the repo's
root package.json, so npm ci never installs it. Not touched -- provisioning
a real npm dependency in the root package.json is out of scope for a
binding-removal PR, and the test remains excluded before and after this
change for the same underlying reason.

Every absolute count re-derived from its own script against the resolved
tree, never carried across the rebase and never hand-merged:
workspace-architecture.json 77 -> 76 members, externalize 28 -> 27, keep 44
(scripts/workspace_architecture.py; git auto-merged this file with NO
conflict and left the stale 77/28, which the script caught);
docs/api/perry.d.ts 2065 entries/131 modules -> 2064/130 and
docs/src/api/reference.md 3007/133 -> 3006/132, both regenerated by running
the built binary's --print-api-manifest rather than editing the headers;
Cargo.lock regenerated with `cargo metadata`. native_result_ledger
(376 rows/326 providers), unrooted_local_shape (578) and
string_payload_access (perry-stdlib inline-offset 37) confirmed unchanged
by running them, not by assuming.

Rebased onto v0.5.1606 (train 227). Conflicts were all of the form "main
deleted dotenv, this branch deleted nanoid, in one hunk"; every one was
resolved to main's current content minus nanoid's own entries, which is
neither side.
proggeramlug pushed a commit that referenced this pull request Sep 19, 2026
customAlphabet(alphabet, size) is documented to return a generator
function; native customAlphabet instead returns the generated id string
directly (js_nanoid_custom's own doc comment: "For simplicity, we combine
this into one call"), so the only documented usage --
const gen = customAlphabet(...); gen(); -- crashes with
TypeError: value is not a function.

Per #10678 (duplicate extern "C" exports across perry-ext-*/perry-stdlib
pairs), this binding existed twice: crates/perry-ext-nanoid/ (governance-
tracked) and crates/perry-stdlib/src/nanoid.rs (a second, independent
implementation behind the default-on bundled-nanoid feature, exporting the
same js_nanoid/js_nanoid_sized/js_nanoid_custom symbols).

customAlphabet is declared to codegen (data_stores.rs's js_nanoid_custom)
but has no call-site wiring anywhere in perry-codegen -- no NativeModSig
row, no lower_call special case. Only plain nanoid(size) had a dispatch row
(native_table/utils_crypto.rs, routing to js_nanoid_sized), consistent with
customAlphabet not being a first-class compiled call at all.

Removed both crates, the 1-entry NativeModSig dispatch row, the 2
js_nanoid* FFI declarations, the well_known_bindings.toml entry, the
"nanoid" NATIVE_MODULES entry + manifest row, the bundled-nanoid stdlib
feature, and 2 Android stub exports.

DELETED the shipped_unproven_bindings_are_partial test rather than emptying
it. Its subject population was exactly the hand-written wrappers shipped
without proven upstream parity: uuid (#10701), dotenv (#10691, landed in
train 227) and nanoid (here). With the last one gone the array would read
`for name in []` -- a test that compiles, runs, asserts nothing and reports
green forever, which is failure mode #4 in CLAUDE.md's "four ways a gate can
be unable to fail". Coverage is not lost: shipped_subset_bindings_are_partial
is a separate test and still asserts the same property for undici,
node-forge, lru-cache and qs. Verified the deletion orphans nothing --
the test module is `use super::*`, lookup_well_known has 9 other callers,
BindingCompat::Partial has 5 other uses, and nothing in the tree keys on
the test's name.

The "ids" umbrella is now EMPTY. It was retargeted to ["bundled-nanoid"]
when #10701 removed uuid (train 225); removing bundled-nanoid leaves it
with no members, so it is kept as `ids = []` -- an intentionally harmless
no-op that preserves `--features ids` for existing callers rather than
breaking them. The stale comments that described the two-member split
(perry-stdlib/Cargo.toml, perry-stdlib/src/lib.rs, stdlib_features.rs) are
rewritten to say so. perry-stdlib's `uuid` crate dependency is untouched:
#10701 already made it non-optional because crypto/random.rs calls it
unconditionally.

test-files/test_parity_nanoid.ts (the exact customAlphabet(...)();
reproduction) is already excluded from the parity gate --
known_failures.json classifies it "ci-env": Node's own oracle fails with
ERR_MODULE_NOT_FOUND in CI because nanoid was never added to the repo's
root package.json, so npm ci never installs it. Not touched -- provisioning
a real npm dependency in the root package.json is out of scope for a
binding-removal PR, and the test remains excluded before and after this
change for the same underlying reason.

Every absolute count re-derived from its own script against the resolved
tree, never carried across the rebase and never hand-merged:
workspace-architecture.json 77 -> 76 members, externalize 28 -> 27, keep 44
(scripts/workspace_architecture.py; git auto-merged this file with NO
conflict and left the stale 77/28, which the script caught);
docs/api/perry.d.ts 2065 entries/131 modules -> 2064/130 and
docs/src/api/reference.md 3007/133 -> 3006/132, both regenerated by running
the built binary's --print-api-manifest rather than editing the headers;
Cargo.lock regenerated with `cargo metadata`. native_result_ledger
(376 rows/326 providers), unrooted_local_shape (578) and
string_payload_access (perry-stdlib inline-offset 37) confirmed unchanged
by running them, not by assuming.

Rebased onto v0.5.1606 (train 227). Conflicts were all of the form "main
deleted dotenv, this branch deleted nanoid, in one hunk"; every one was
resolved to main's current content minus nanoid's own entries, which is
neither side.
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.

1 participant