Skip to content

fjs: keep private types out of authored .mjs (Stage 1) - #1750

Merged
sergey-shandar merged 12 commits into
mainfrom
claude/private-ts-todo-partial-bydyc9
Aug 28, 2026
Merged

fjs: keep private types out of authored .mjs (Stage 1)#1750
sergey-shandar merged 12 commits into
mainfrom
claude/private-ts-todo-partial-bydyc9

Conversation

@sergey-shandar

@sergey-shandar sergey-shandar commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Implements Stage 1 of fjs/todo/separate-private-types.md: no authored .mjs anywhere in the repository carries a file-scope JSDoc @typedef any more (232 removed across 63 files); function-local typedefs remain allowed and are the home for compile-time proofs. The node26 CI job now guards the prohibition, so the clean state stays clean.

What moved where

  • Public declaration closure → types.ts: _Byte (byte_set), _Base (bit_vec), _Signature (crypto/sign), _CompileOp (djs), _EmptyTagMap (bnf/data), _RefCounter/_Refs (djs/serializer), BrowserTestReport (emergent_testing); new types.ts files for fjs/fsm, fjs/fsc, fjs/djs/serializer, and fjs/effects/node/memory.
  • New optional private.ts where separating implementation-private types reads cleaner than inlining: asn.1, bnf (4), common/monoid, djs (3), effects/node, rtti/data, rtti/ts, types/bigfloat, types/btree/remove.
  • Inlined when trivial; proof-function-local for compile-time asserts: edag's 24 Assert<Check<...>> checks became a consistency proof entry, fjs/effects/types.ts no longer imports implementation functions (its ReturnType signature asserts moved to a signatures entry in proof.f.mjs), and fjs/media/revision's pins moved to its proof — per the TODO's dependency-order rule.
  • Recursive RTTI stayed in module.f.mjs (fjs/edag, fjs/media/json/schema) with inline/hand-written annotations; values needed only for proof linkage are exported with a leading _ (linkage, not API) or referenced through type-only @import. No meta/module.f.mjs split was warranted; the option is documented.

Policy documentation

Root AGENTS.md and fjs/AGENTS.md §3.2 state the prohibition and placement rules with live examples; fjs/fsc/README.md's "Private JSDoc typedefs" is rewritten as "Private types"; the blocked wait-for-@internal/stripInternal TODO is deleted as superseded; the prescriptions in todo/migrate-typescript-to-mjs.md, fjs/ci/todo/f-mjs-package-support.md, and fjs/effects/memory/todo/sync-interpreter-owner.md are retargeted to the Stage 1 forms. The design TODO's Stage 1 checkboxes are checked; the file stays for Stage 2 (packaging cleanup: unshipping generated private.d.ts). New .ts helper files carry no @module tag; the pre-existing types.ts files that do are recorded as debt in fjs/todo/module-tag-on-types-ts.md.

Verification

npx tsc clean; npm run ci-update regenerates nothing; full suite 3487/3487 (node), with deno/bun covered by CI. Independently measured in review: export type _* occurrences in emitted declarations drop from 226 on main to 0 on this head, with zero proof coverage lost. The new node26 guard step fails CI on any future file-scope typedef. Importers of every moved public type were updated in this PR; no compatibility re-exports.

Changelog

  • BREAKING CHANGES: type import paths changed: Grammarfjs/fsm/types.ts; MemoryOperationMap, MemoryRun, Uuidfjs/effects/node/memory/types.ts; BrowserTestReportfjs/emergent_testing/types.ts; the JSON-Schema Unknown alias is gone — spell it Ts<typeof unknown>.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LqeS5t2ZKkSu3chRPMXR7n

Implement Stage 1 of fjs/todo/separate-private-types.md: no authored
.mjs anywhere in the repository carries a file-scope JSDoc typedef any
more; function-local typedefs remain allowed.

Each former file-scope typedef moved to its sibling types.ts when it is
part of the public declaration closure, to a new optional private.ts
when implementation-private and worth naming (asn.1, bnf, common/monoid,
djs, effects/node, rtti, types/bigfloat, types/btree/remove), inline
into its annotations when trivial, or function-local into a proof when
it is a compile-time assert (edag, effects, js/keywords, rtti,
djs/parser, media/json/schema, media/revision). fjs/effects/types.ts no
longer imports implementation functions: its ReturnType signature
asserts now live in fjs/effects/proof.f.mjs. Recursive RTTI constants
stay in module.f.mjs with their consistency asserts moved downstream.

Breaking type import-path moves, with importers updated and no
compatibility re-exports: Grammar (fjs/fsm) and MemoryOperationMap,
MemoryRun, Uuid (fjs/effects/node/memory) moved to sibling types.ts
files; the Unknown alias of fjs/media/json/schema is now spelled
Ts<typeof unknown>.

Policy documentation updated: root AGENTS.md and fjs/AGENTS.md state the
prohibition and placement rules; fjs/fsc/README.md's typedef
prescription is rewritten as "Private types"; the blocked
wait-for-@internal/stripInternal TODO is deleted as superseded; the
typedef prescriptions in todo/migrate-typescript-to-mjs.md,
fjs/ci/todo/f-mjs-package-support.md, and
fjs/effects/memory/todo/sync-interpreter-owner.md are retargeted to the
Stage 1 forms. Stage 1 checkboxes in the design TODO are checked; the
file stays for Stage 2 (packaging cleanup).

npx tsc clean; full suite 3487/3487.

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

cloudflare-workers-and-pages Bot commented Aug 28, 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 7695ae3 Commit Preview URL

Branch Preview URL
Aug 28 2026, 03:32 PM

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqeS5t2ZKkSu3chRPMXR7n
Comment thread fjs/media/revision/proof.f.mjs Fixed
Values referenced only through typeof in consistency-proof typedefs do
not need a runtime import; the JSDoc @import serves the type position
and the code-quality unused-import finding goes away.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqeS5t2ZKkSu3chRPMXR7n

@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: e934de8ed1

ℹ️ 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".

Comment thread fjs/fsm/types.ts
Comment thread fjs/asn.1/private.ts Outdated
fjs/AGENTS.md section 2 reserves @module for module.f.mjs / module.mjs
entry points; strip it from the type files this branch added. The
pre-existing types.ts files that carry the tag are recorded as debt in
fjs/todo/module-tag-on-types-ts.md rather than widened into this change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqeS5t2ZKkSu3chRPMXR7n

@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: eebc0078a0

ℹ️ 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".

Comment thread changelog/unreleased/1750.md Outdated
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqeS5t2ZKkSu3chRPMXR7n

@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.

Measured at 141b6204f on node v23.11.0. The leak is genuinely closed, and I measured it rather than trusting the sweep: after purging declarations and running the repo's own emit, main produces 226 export type _* occurrences across 63 .d.mts files, and this head produces 0. That is the claim this PR exists to make, and it holds.

No coverage lost either, which matters at 1733 deleted lines: comparing fully-qualified leaf names (module + path, since bare names collide across files), zero disappear; the +9 are genuinely new proofs. Gates 3487/3487 exit 0, tsc --noEmit exit 0 (main 3478/3478).

Spot-checked fjs/djs/parser and fjs/rtti/data: both private.ts files use import type only and depend on their sibling types.ts, never the reverse — the direction the design requires, no cycle. The parser's tokenKindNames/framingKeywords becoming _-prefixed runtime exports so the proof can re-verify them is deliberate and documented as linkage rather than API; consistent with the private-constant convention.

One substantive point: Stage 1 is a sweep, not a check. I added /** @typedef {number} _Probe */ to fjs/path/module.f.mjs — a file this PR does not touch — and tsc --noEmit, npm test (3487/3487) and npm run prepack all exit 0 while _Probe leaks straight into the emitted fjs/path/module.f.d.mts. So the 226→0 result is real but unguarded: nothing prevents the next file-scope typedef from putting it back. The design document does frame verification as a repo-wide search, so this is consistent rather than contradictory — but a one-line grep in CI would turn a state that happens to be clean into one that stays clean, and it is much cheaper to add now than after the next regression.

Minor: the entry is 438 characters against the ~250 guideline in changelog/README.md — soft wording, so a nit. The breaking marker is right: moved public types (Grammar, MemoryOperationMap, MemoryRun, Uuid, BrowserTestReport) change import path and the Unknown alias is removed.

The node26 invariants job now fails when any authored .mjs contains a
file-scope JSDoc typedef, so the 226-to-0 declaration-leak result stays
guarded instead of merely achieved. Verified against a probe typedef in
an untouched module: clean tree passes, probe fails. The changelog entry
is trimmed toward the ~250-character guideline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqeS5t2ZKkSu3chRPMXR7n

Copy link
Copy Markdown
Contributor Author

Both review points are addressed in f0ca31b:

  • Sweep → check: the node26 invariants job now runs ! grep -rnE '^(/\*\*.*@typedef| \* *@typedef)' --include='*.mjs' --exclude-dir=node_modules . between the ci-update diff check and npx tsc, so a new file-scope typedef fails CI instead of quietly reopening the leak. Verified with your exact probe (_Probe in fjs/path/module.f.mjs): clean tree passes, probe exits 1.
  • Changelog length: the entry is trimmed to the moved-path list plus the removed Unknown alias (~300 bytes, three wrapped lines).

Thanks for measuring 226→0 and the coverage-by-qualified-leaf comparison — that's exactly the claim this PR needed checked independently.


Generated by Claude Code

ci.yml is generated by npm run ci-update, so the guard added directly to
it was erased by regeneration and failed the diff check. The step now
comes from node26Steps in fjs/ci/node/module.f.mjs; the regenerated
ci.yml is byte-identical to the previous hand edit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqeS5t2ZKkSu3chRPMXR7n

@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.

Approved. The guard works, and I tested it rather than taking its presence as proof: re-running my probe at ec77b6293/** @typedef {number} _Probe */ in untouched fjs/path/module.f.mjsnpm test still exits 0 (3487/3487) and tsc --noEmit exits 0, but the CI grep exits 1 and catches it. No false positive on the clean tree, and ci.yml genuinely round-trips: node ./fjs/module.mjs ci regenerates it byte-identical.

One narrow gap, non-blocking: the pattern requires a literal space before the continuation * ( \* *@typedef), so a JSDoc block indented with a tab slips past — /**\n\t* @typedef … \n */ is not caught, while the no-space form /**@typedef …*/ and an ordinary multi-line block both are. Changing that space to \s closes it.

The guard required a literal space before the continuation asterisk, so
a JSDoc block indented with a tab slipped past; \s closes that gap
without matching function-local blocks, whose continuations are
indented by four or more characters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqeS5t2ZKkSu3chRPMXR7n

Copy link
Copy Markdown
Contributor Author

The tab gap is closed in 4b8c1d9: the continuation pattern is now \s\* *@typedef, changed in the generator (fjs/ci/node/module.f.mjs) and regenerated. Validated all three cases locally: clean tree passes, your tab probe (/**\n\t* @typedef …) exits 1, the plain space probe still exits 1. This push resets your approval — re-approve when convenient.


Generated by Claude Code

@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.

Approved.

Verified the tab-indentation fix: the guard now catches a tab-indented @typedef continuation, does not fire on prose that merely mentions @typedef, and ci.yml still round-trips byte-identical through node ./fjs/module.mjs ci.

@sergey-shandar
sergey-shandar added this pull request to the merge queue Aug 28, 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 28, 2026
claude added 2 commits August 28, 2026 15:27
Resolves the conflicts the merge queue rejected. main's option-as-omission
work (`option` as a nullary schema, `or(option, t)`), its hardened parse
rebuilds, and its new reporter/RunTotals types all land here; the Stage 1
transformation is re-applied on top of them:

- fjs/rtti/parse: main's new recursive `_Entries`/`_Rebuild`/`_Declared`
  move to a new private.ts; the dead `_ItemResult` goes.
- fjs/rtti/data/proof: the shared recursive schemas are declared inside a
  `recursiveSchemas()` factory and destructured back out, so their
  typedefs are function-local and every use site is unchanged.
- fjs/rtti/ts/proof, fjs/rtti/validate/proof, fjs/media/json/schema:
  main's rewritten content with the typedefs moved into proof entries;
  validate's rewritten `rows` table is spliced into the existing thunk.
- fjs/emergent_testing/types.ts keeps both sides' additions.
- docs: main's `todo/rtti-type-system.md` prose and the `fjs/AGENTS.md`
  assert example retargeted to the Stage 1 forms.

npx tsc clean; npm run ci-update regenerates nothing; full suite
3517/3517; the file-scope typedef guard passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqeS5t2ZKkSu3chRPMXR7n
Comment thread fjs/rtti/data/proof.f.mjs Fixed
Comment thread fjs/rtti/data/proof.f.mjs Fixed
Comment thread fjs/rtti/data/proof.f.mjs Fixed
Comment thread fjs/rtti/data/proof.f.mjs Fixed
Comment thread fjs/rtti/data/proof.f.mjs Fixed
Comment thread fjs/rtti/data/proof.f.mjs Fixed
Comment thread fjs/rtti/data/proof.f.mjs Fixed
Comment thread fjs/rtti/data/proof.f.mjs Fixed
claude added 2 commits August 28, 2026 15:29
main's ENOTDIR re-check added a use of the `_TooLarge`/`_NotRegular`
typedefs this branch inlined away; spell them inline there too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqeS5t2ZKkSu3chRPMXR7n
The mutually-recursive partners (orB, t2, w, topOr, b2, closedChildren,
absCycleB, optNumB) are reached through their counterparts inside the
factory, so destructuring them at file scope left them unused.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqeS5t2ZKkSu3chRPMXR7n
@sergey-shandar
sergey-shandar added this pull request to the merge queue Aug 28, 2026
Merged via the queue into main with commit 789709f Aug 28, 2026
19 checks passed
sergey-shandar pushed a commit that referenced this pull request Aug 28, 2026
…d claim

Three review findings, all against text I wrote in the previous commit.

§1.2 still showed a `proof.f.mjs` leading block carrying `@module`, while the
§2 I had just rewritten said a proof's documentation is never published so
the tag has nothing to attach to. Same file, opposite instructions for the
same shape. The example loses the tag and says why.

The restore issue omitted `private.ts`. All 16 have prose and none carry the
tag — stripped by #1750 rather than #1756, so they were not in the 102 I
counted, but it is the same reading and the same fix, and §2 names
`private.ts` explicitly. As written the issue could have been completed in
full while leaving 16 files violating the rule it exists to satisfy, and its
last task says to delete §2's non-compliance note.

And the publication rationale was overstated. The website issue plans
`deno doc --html **/module.f.mjs`, a glob excluding every `types.ts` and
`private.ts`, so restoring the tag alone puts nothing on the website. The
tag is necessary, not sufficient: it decides whether `deno doc` *can* see a
file's module documentation, and what the build is pointed at is a separate
question. Both §2 and the issue say that now, and widening the glob is named
as the other half.

Also added: a task to correct `todo/migrate-typescript-to-mjs.md`, which
restates the old rule verbatim under "Module header and import ordering"
rather than linking §2, so it did not move when §2 did.

Documentation only. `npx tsc` clean, suite 3533/3533.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T7vAocRuxfmWJDnujCoMup
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