Observation-class finding, measured while working #4415 on origin/main @ 8e2bbba24. Nothing a user hits today — filing so it is recorded rather than rediscovered.
What happens
Under OS_EAGER_SCHEMAS=1 (the documented lazySchema rollback switch, which gen:schema / check:authorable-surface set), importing a deep leaf schema module as the process entry point throws during module evaluation:
$ OS_EAGER_SCHEMAS=1 node --import tsx --input-type=module \
-e "import './packages/spec/src/automation/control-flow.zod.ts'; console.log('ok');"
/packages/spec/src/shared/strict-object.ts:255
DECLARATIONS.push({ options, shape });
^
ReferenceError: Cannot access 'DECLARATIONS' before initialization
at strictObject (packages/spec/src/shared/strict-object.ts:255:3)
at (packages/spec/src/data/field.zod.ts:113:52)
at lazySchema (packages/spec/src/shared/lazy-schema.ts:23:12)
at (packages/spec/src/data/field.zod.ts:113:35)
at Object. (packages/spec/src/data/field.zod.ts:1076:1)
Why
DECLARATIONS is a module-level const declared after the strictObject function in strict-object.ts. Eager mode makes lazySchema(factory) run its factory at module-evaluation time, so when the import graph re-enters strictObject() while strict-object.ts is itself still evaluating, the const is still in its temporal dead zone.
Whether that re-entry happens is decided purely by which module the process enters first — it is an ordering property of the graph, not of any one file.
Why it is dormant
Every real entry point goes through a barrel (src/index.ts, src/automation/index.ts), which establishes an order in which strict-object.ts finishes evaluating before anything calls into it. Both eager-mode gates use those barrels, so CI never reaches the bad order. Verified green on the same commit:
$ OS_EAGER_SCHEMAS=1 node ... -e "await import('./packages/spec/src/automation/index.ts')" # ok
Reproduces on origin/main, not caused by any in-flight branch
Measured by checking out the pristine origin/main copies of the two files I was editing and re-running — identical failure, so #4415's changes are not involved.
Possible fix
Hoist DECLARATIONS above strictObject (a one-line move), which removes the TDZ regardless of evaluation order. Worth a quick check for the same shape elsewhere in packages/spec/src/shared/ — alias-table-registry has a similar registry pattern.
No pm:queue: dormant, no user-visible symptom, and the fix is cheap enough to ride along with any future strict-object.ts work.
Observation-class finding, measured while working #4415 on
origin/main@8e2bbba24. Nothing a user hits today — filing so it is recorded rather than rediscovered.What happens
Under
OS_EAGER_SCHEMAS=1(the documentedlazySchemarollback switch, whichgen:schema/check:authorable-surfaceset), importing a deep leaf schema module as the process entry point throws during module evaluation:Why
DECLARATIONSis a module-levelconstdeclared after thestrictObjectfunction instrict-object.ts. Eager mode makeslazySchema(factory)run its factory at module-evaluation time, so when the import graph re-entersstrictObject()whilestrict-object.tsis itself still evaluating, theconstis still in its temporal dead zone.Whether that re-entry happens is decided purely by which module the process enters first — it is an ordering property of the graph, not of any one file.
Why it is dormant
Every real entry point goes through a barrel (
src/index.ts,src/automation/index.ts), which establishes an order in whichstrict-object.tsfinishes evaluating before anything calls into it. Both eager-mode gates use those barrels, so CI never reaches the bad order. Verified green on the same commit:Reproduces on origin/main, not caused by any in-flight branch
Measured by checking out the pristine
origin/maincopies of the two files I was editing and re-running — identical failure, so #4415's changes are not involved.Possible fix
Hoist
DECLARATIONSabovestrictObject(a one-line move), which removes the TDZ regardless of evaluation order. Worth a quick check for the same shape elsewhere inpackages/spec/src/shared/—alias-table-registryhas a similar registry pattern.No
pm:queue: dormant, no user-visible symptom, and the fix is cheap enough to ride along with any futurestrict-object.tswork.