Skip to content

types/uint8array: derive toVec from listToVec instead of a size bound - #1543

Merged
sergey-shandar merged 4 commits into
mainfrom
claude/todo-implementation-rifq4g
Aug 14, 2026
Merged

types/uint8array: derive toVec from listToVec instead of a size bound#1543
sergey-shandar merged 4 commits into
mainfrom
claude/todo-implementation-rifq4g

Conversation

@sergey-shandar

Copy link
Copy Markdown
Contributor

Implements fjs/types/uint8array/todo/tovec-precomputed-bound.md (deleted here).

Why

Two adjacent functions did the same job with opposite discipline:

export const toVec = input => {
    assert(input.length <= maxLengthBytes, "the array is too big")
    return u8ListToVecMsb(fromArrayLike(input))
}

export const listToVec = input =>
    assertNotNullish(tryU8ListToVecMsb(flat(m(input))), "the array is too big")

AGENTS.md §5.6 — "never precompute a size to predict whether something fits" — names tryU8ListToVec as the try* variant to use instead. listToVec obeyed it; toVec re-derived a byte-count bound. The guard was also redundant: u8ListToVec is mapUnwrap(tryU8ListToVec(bo)), so the real check already ran inside it.

What

export const toVec = input => listToVec([input]).

flat(map(fromArrayLike)([input])) is the same byte list fromArrayLike(input) produced, and assertNotNullish carries the same "the array is too big" message, so behavior is unchanged. assert, maxLengthBytes and u8ListToVec drop out of the imports. listToVec moves above toVec so the primitive precedes what derives from it, and picks up a JSDoc note on why the bound isn't precomputed.

Verification

  • npx tsc clean.
  • fjs test: 2617 pass, 0 fail. The proof already pinned both boundaries — maxLength: () => toVec(new Uint8Array(Number(maxLengthBytes))) and throw.toVec one byte over — and both still pass unchanged.
  • Probed the boundary directly to confirm the error path is identical, not just still-throwing: at exactly maxLengthBytes the result is 1048576 bits, and one byte over throws the string "the array is too big" as before.
  • npm run cov: fjs/types/uint8array/module.f.mjs at 100% lines/branches/functions.

🤖 Generated with Claude Code

https://claude.ai/code/session_016HvbYkBMYWwQECL7myLhqs


Generated by Claude Code

claude added 2 commits August 14, 2026 06:14
toVec precomputed a byte-count bound to predict whether the input would fit,
which AGENTS.md 5.6 rules out: attempt the real conversion and branch on its
result. The guard was also redundant, since u8ListToVec is the unwrapping
form of tryU8ListToVec and the real check already ran inside it.

toVec is now listToVec([input]) -- the same byte list, the same "the array is
too big" message on overflow -- and assert, maxLengthBytes and u8ListToVec
drop out of the imports. listToVec moves above it as the primitive.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016HvbYkBMYWwQECL7myLhqs
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
functionalscript 3758356 Commit Preview URL

Branch Preview URL
Aug 14 2026, 07:28 AM

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6bbb8647bb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

*
* @type {(input: Uint8Array) => Vec}
*/
export const toVec = input => listToVec([input])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the required changelog entry

When this exported-code change is released, the Unreleased section will contain no record of it because the diff changes module.f.mjs but never updates CHANGELOG.md. Repository workflow requires every code-changing PR, unlike documentation-only work, to add a changelog entry using the real PR number, so add that entry before merging.

AGENTS.md reference: AGENTS.md:L132-L135

Useful? React with 👍 / 👎.

@o2alexanderfedin o2alexanderfedin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. The risk in a DRY derivation like this is that the two forms are not actually equivalent, so I swept them against each other rather than reasoning about it.

Baseline: origin/main = 699c960e ("types/object: add structurallySame ... (#1538)").

Equivalence sweep. Imported toVec from an origin/main tree and from this head, and compared outcomes — not just return values, but (kind, typeof thrown, String(thrown)) so a differing throw would show up:

  • empty array, and every single-byte value 0..255;
  • every 2-byte pair over a 32-value subset and every 3-byte triple over a 16-value subset;
  • every length 0..3000 x three fill patterns, including all-zero (which is the pattern most likely to expose a Vec normalization difference) and all-0xFF;
  • a dense band straight across the bound the old code enforced — every length in [131000..131200] x 2 fills — plus 131071 / 131072 / 131073 / 131074 / 140000 / 200000 / 300000;
  • a stride sample across the middle of the range.

14,928 probes, 0 divergences. 14,662 of them returned a Vec and 266 threw, so the throwing path is genuinely exercised and the sweep is not vacuously one-sided. Negative control: injecting a one-element truncation into the new side makes the same comparison report 4,099 divergences, so the harness can see a divergence when there is one.

The boundary specifically. The removed bound was input.length <= maxLengthBytes, and maxLengthBytes = maxLength >> 3n = 131072. Both old and new accept exactly 131072 bytes and both throw from 131073 up — the 266 throws are precisely the lengths above 131072 in the swept set, in both implementations. So tryU8ListToVec's real check lands on the same cut point the precomputed bound did, which is the substance of the §5.6 claim.

Worth noting since it is the kind of thing that hides a difference: the old guard compared a number (input.length) against a bigint (maxLengthBytes). That is legal in JS and, per the band above, agrees with the new path at the exact boundary.

Error identity. assertNotNullish delegates to assert, and assert throws the raw msg rather than an Error. Both sides therefore throw the string "the array is too big" — same type, same value, which is what the CHANGELOG's "error message unchanged" needs to mean here. My comparison checked typeof as well as the string, so a swap from string to Error would have been caught.

No divergence appeared, so there is nothing to classify as regression-vs-latent-bug (unlike #1533, where the single boundary divergence turned out to be a latent bug being fixed).

Battery

  • npx tsc --noEmit — exit 0.
  • npm run prepack from a freshly cleaned tree — exit 0.
  • npm test — 2617 pass / 0 fail, identical to origin/main.
  • Dual-axis public surface diff (prepack in both trees): exported type aliases identical; exported const signatures identical apart from toVec moving after listToVec in declaration order, which is the source reordering and not a signature change. toVec still emits (input: Uint8Array) => Vec.
  • Emitted fjs/types/uint8array/module.f.d.mts: @module header survives (the blank line after it is intact), and no elided or type-level any — the new listToVec JSDoc emits with its full List<Uint8Array> signature.
  • node bin/linkcheck.mjs — broken-link sets identical to main. The deleted todo/tovec-precomputed-bound.md has no remaining references anywhere in the tree (grep across the repo, not just the link checker).

Conventions. §8.3: code change, entry present, links only /pull/1543 — correct. §8.4: no **BREAKING CHANGES:** prefix, right given the sweep shows identical outcomes including the throw. §6.2: no new exported types, so the _-prefix rule is not engaged. The toVec/listToVec JSDoc reshuffle keeps both public and documented.

@sergey-shandar
sergey-shandar added this pull request to the merge queue Aug 14, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Aug 14, 2026

@o2alexanderfedin o2alexanderfedin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at 151b3a47 after the previous approval at df48c0b5. The delta is merge-only.

What moved, precisely:

  • git diff df48c0b5 151b3a47 -- fjs/types/uint8array/ is empty — the implementation and its proof are byte-identical to what was reviewed.
  • The only CHANGELOG delta is the arrival of #1541's basen/base64 entry from main; this PR's own types/uint8array entry is unchanged.
  • git merge-base origin/main 151b3a47 is e692e595, i.e. the branch is exactly current main plus this change, with no conflict resolution touching the PR's own files.

Since neither the implementation nor the proof moved, I did not re-run the 14,928-probe old-vs-new equivalence sweep or the 131,072/131,073 cut-point reproduction from the previous round; those results still describe this code verbatim. I did re-run the gates that the merge could plausibly disturb, at 151b3a47 from a freshly cleaned tree:

  • npx tsc --noEmit — exit 0.
  • npm test — pass: 2617, fail: 0. Baseline fs-main at e692e595 (current origin/main): pass: 2617, fail: 0. Exact match.
  • npm run prepack — exit 0 from a clean tree (both passes).

No new findings. Approving.

@sergey-shandar
sergey-shandar added this pull request to the merge queue Aug 14, 2026
Merged via the queue into main with commit 6684bef Aug 14, 2026
19 checks passed
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.

3 participants