edag/todo: end chain lambdas by absence (option), not null - #1755
Conversation
Files the investigation: the explicit null terminator predates closed-by- default tuples (7852819), whose design (930fa65, #1725) already named option as omission. Records the verified runtime and type-level behavior, the AbsentOr/CheckRaw phantom pattern the recursive lambdas need, the trailing- hole widening, and the migration tasks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AQH2SZCpDhyQUgByxkV46e
|
You have reached your Codex usage limits for security reviews. Please try again later. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
functionalscript | 16d59f9 | Commit Preview URL Branch Preview URL |
Aug 28 2026, 05:35 PM |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2981ad5026
ℹ️ 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".
…anisms Review finding on #1755: leaving 'where hole rejection lives' as an open task would let the migration ship a validation regression, which AGENTS.md forbids deferring. The task is now a hard gate with two acceptable mechanisms: arity-split unions (verified — closedness by length rejects every hole with no rtti change), or an rtti rule that tuple absence is the array ending before the position, filed and landed first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AQH2SZCpDhyQUgByxkV46e
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d50a0ba34c
ℹ️ 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".
Review finding on #1755: the schema/type tasks prescribed the option spelling unconditionally, so following them under the arity-split mechanism would put option back at the lambda roots and reintroduce the trailing-hole regression. The mechanism choice is now the first task and the schema and type tasks are spelled per mechanism; the hole-rejection pins move into the proof task, required either way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AQH2SZCpDhyQUgByxkV46e
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8a2957c5b2
ℹ️ 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".
Review finding on #1755, confirmed against trimPrefix in fjs/rtti/data/module.f.mjs: once a hole is no member, [option] and [] denote one array set, and the data form keeps them canonically distinct today exactly because they differ on new Array(1). The prerequisite rtti issue for the past-the-end rule must therefore also specify the arraySet normalization and carry cmp/equal/subset, the data reader, and the printer - a surface mechanism 1 does not touch, which the issue now notes as an argument for mechanism 1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AQH2SZCpDhyQUgByxkV46e
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eab016cf6f
ℹ️ 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".
…igns Two review findings on #1755, both verified. Under the past-the-end rule an absent bit is realizable only in the all-optional trailing run, so interior absence becomes unobservable — [or(option, number), 3] comes to denote [number, 3] while toData keeps its absentBit — and the prerequisite rtti issue must strip it, the same trailing-run split TupleTs makes. And the respelling task now covers the active downstream designs (compile-modules-to-edag.md, interpret-edag.md, the bun-parentheses blocker) plus a repo-wide sweep, leaving released changelog entries as history. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AQH2SZCpDhyQUgByxkV46e
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e8f7b1cdda
ℹ️ 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".
Third mechanism-2 gap in a row on #1755, this one with no local answer: a referenced rule like r = or(option, [r]) can sit where its root absence is unobservable and where it is not, so stripping the rule changes the fixpoint, and the data form declines to see through references. The design now decides instead of choosing later: arity-split unions are the mechanism, and the past-the-end rule is recorded as the rejected alternative with the full cascade (readers/printer docs, [option]/[] collapse, interior bits, referenced nodes) as the reason. Tasks lose their per-mechanism branching. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AQH2SZCpDhyQUgByxkV46e
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 34b0e22860
ℹ️ 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".
…tti's Review finding on #1755, measured both ways: a prototype-supplied member behind an own trailing hole is read by both spellings alike — index 3 supplying ['|()', c, null] validates under today's schema and rejects under the split one, ['|()', c] exactly the reverse — so neither is hole-proof against a polluted Array.prototype and neither ever was, while a pristine prototype rejects every hole in both. The gate's claim is now stated as about the value's own members under rtti's reading model, with the hostile-host question left where it is tracked (hostile-accessor-hermetic-read-path; the Beyond-length caveat). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AQH2SZCpDhyQUgByxkV46e
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2718720440
ℹ️ 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".
… reads it Review finding on #1755, measured: past the end the split 3-arity arm accepts a length-3 dot whatever Array.prototype[3] holds — the unanswered region rtti's Beyond-length caveat states for every bare tuple — while today's schema accepts the same value the moment that index inherits its own null, so the flips are symmetric there too. The claimed executor crash is wrong: amnesia reads nodes by destructuring and the array iterator stops at length, so the inherited index is never read — the fourth slot of a length-3 node is undefined under Array.prototype[3]='junk' while a direct node[3] would read it. The gate paragraph now covers both regions and the amnesia task pins the destructuring read pattern. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AQH2SZCpDhyQUgByxkV46e
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b354654e9
ℹ️ 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".
…tion Review finding on #1755, confirmed in amnesia/module.f.mjs: the three lambda walkers destructure, but skip reads k[0]/k[1]/k[2] directly — safe today only because every step carries an own third member. After the migration a short step's k[2] would read the prototype, so a polluted Array.prototype[2] could hand skip an inherited continuation the step's own trailing null masks today. The gate paragraph now states the exception and the amnesia task requires rewriting skip to destructure in the same change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AQH2SZCpDhyQUgByxkV46e
…k' into claude/option-null-lambdas-mqpwck
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b111a745ac
ℹ️ 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".
Tenth review round on #1755 flips the eighth's remedy: destructuring dispatches an own overridden Symbol.iterator, so it now asks for length-checked indexed reads instead. The doc closes the family: under a hostile host every read style has its own attack, the three lambda walkers destructure today so an iterator-hostile step already misleads the current evaluator identically, and amnesia's README scopes it as deliberately not a VM — its guarantees assume a DJS value on a pristine host, where the styles coincide. skip joins the one uniform pattern; hostile-host executor hardening is out of amnesia's charter. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AQH2SZCpDhyQUgByxkV46e
o2alexanderfedin
left a comment
There was a problem hiding this comment.
Approved — the engineering holds. Three doc-level things below, one of which is worth fixing before merge.
Verified empirically against the real validate at this head: the symmetric prototype-pollution flip reproduces in all four quadrants plus both pristine controls, exactly as the text describes — neither spelling is pollution-proof, and both reject the hole under a clean prototype. The premise mechanism 2's rejection turns on also holds: validate([])(new Array(1)) errors while validate([option])(new Array(1)) is ok, matching trimPrefix's own doc comment. skip's k[2] really is the only indexed continuation read — the other three destructure — and destructuring genuinely does not reach a polluted past-length index while node[3] does. All 18 links resolve; no leftover mechanism-2 language survives the rewrite.
-
Wrong section number, worth fixing. The note cites
AGENTS.md §1, "Merge the knowledge". That paragraph is in §5, Pull requests and releases. The quoted words are exact; only the number is off, and a future reader chasing §1 will not find it. -
The PR description is now superseded, and this repo squash-merges it into history. The body still says the note "names two acceptable mechanisms", but the final text commits to arity-split and rejects the rtti rule. Left as is, mainline history will permanently describe a design the note argues against.
-
One quotation is not verbatim. The note attributes to amnesia's README: "deliberately not a VM to run FunctionalScript on". The README says "It is not a VM for FunctionalScript, and nothing that matters should run on it"; "deliberately" comes from
execution-models.md. The substance is right — amnesia does disclaim VM status — but every other quote in this note is character-exact, so the marks read as verbatim here too.
Minor: the Related bullet offering identity-aware-parse.md as "the Stage 2 validator the hole check could join" presumes a standalone runtime hole check. Under arity-split, rejection is structural and pinned in proofs — there is nothing left to join.
The cascade argued for a hypothetical mechanism-2 redesign could not be run, since that code does not exist. Its premises are real, though: toData keeps the absent bit unconditionally, and _SplitTs/_InteriorTs do make the trailing-run split the note credits TupleTs with.
tsc 0; npm test not run — docs-only against the true merge-base.
Review fixes from the approving human review on #1755: the Merge-the- knowledge paragraph is AGENTS.md §5, not §1; amnesia's README is quoted verbatim with the 'deliberately' phrasing attributed to the edag README where it lives; and the identity-aware-parse Related bullet no longer implies a runtime hole check exists to join — rejection is structural under arity-split. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AQH2SZCpDhyQUgByxkV46e
|
All four review points from @o2alexanderfedin's approval are addressed:
Generated by Claude Code |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3dc5cc74c9
ℹ️ 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".
Review finding on #1755: the implementation changes accepted graphs and the exported types, so per AGENTS.md \u00a75 it needs a changelog/unreleased/<PR>.md entry prefixed BREAKING CHANGES with the matching Changelog section, updating every importer in the same PR. Added as a task. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AQH2SZCpDhyQUgByxkV46e
o2alexanderfedin
left a comment
There was a problem hiding this comment.
Approved. All three checked out:
AGENTS.md§5 is right — "Merge the knowledge" is at line 112 under "Pull requests and releases".- The quotation now splits correctly. Amnesia's README does say "It is not a VM for FunctionalScript, and nothing that matters should run on it" verbatim, and
../README.mddoes say "deliberately not a VM to run FunctionalScript on", across a line break at lines 43-44. Both attributions land where the words actually are. - The
identity-aware-parse.mdbullet no longer offers a hole check to join, which matches arity-split making rejection structural.
The added BREAKING CHANGES task is a good catch on your side — the migration does stop previously valid graphs from validating.
Files
fjs/edag/todo/option-terminated-lambdas.md— an investigation and design for dropping the explicitnullchain terminator, so a plain read is['.', a, 'b']and terminals are genuine 2-tuples (['|()', c],['|!()', c]). Documentation only: no schema, type, or evaluator changes yet — those are the TODO's task list.What the issue records:
null("a two-element terminal handed a real continuation would validate as the terminal with the rest silently dropped") predates closed-by-default tuples: the chain grammar landed 53077ae (2026-08-26) against open tuples, closedness by length landed 7852819 (2026-08-27), and the latter's design (types/rtti/todo: closed containers by default, thenoptionas omission #1725) already namedoptionas omission. Under the closed model the smuggled continuation is rejected by length — verified against the realvalidate, with an acceptance/rejection table in the issue.optionanywhere in the chain schemas — verified against the realvalidate, no rtti change needed, and a trailing hole (sparse array) matches neither arm, so the same change keepsvalidate(exp)rejecting every hole, pinned in the proofs.optionspelling was verified too (optional trailing tuple elements render exactly; the recursive lambda thunks needAbsentOrphantoms pinned withCheckRaw) and is recorded as the rejected alternative: making it canonical requires an rtti rule that tuple absence is the array ending before the position, whose prerequisite cascades through rtti's canonical algebra — reader/printer docs, the[option]/[]collapse intoData, interior absent bits, and referenced nodes where stripping changes the recursive fixpoint.length), amnesia's walkers stay on the destructuring read pattern withskipjoining them, and hostile-host hardening remains rtti's tracked question.types.ts, amnesia'sk === null→k === undefinedplusskipdestructuring), mechanical in proofs and prose (~200 trailingnulls, README tables, and the downstream djs designs that prescribe the old spellings).https://claude.ai/code/session_01AQH2SZCpDhyQUgByxkV46e