Filed unassigned from the #13768 dispatch (which closed the completeness gap on
CLOSURE_CONTEXT_KEY_BY_TYPE, one package over). Not fixed there: it lands in
packages/lint, a different lane, and it is a different constant.
The claim in the docblock is not what the code does
packages/lint/src/runtime-gate.ts:116 says, of RuntimeStackContext:
Each key here doubles as the stack key the collection occupies in the
per-write snapshot -- see CONTEXT_STACK_KEYS, which is derived from this
shape and keeps the two from drifting.
CONTEXT_STACK_KEYS is not derived. Line 293:
constCONTEXT_STACK_KEYS=['objects','permissions','books','datasets','pages']asconstsatisfiesreadonly(keyofRuntimeStackContext)[];
A hand-written literal with a satisfies clause. That clause asks that every
entry it NAMES is a real RuntimeStackContext key -- validity. It does not ask
that every key of RuntimeStackContext HAS an entry -- completeness. The same
asymmetry #13390 removed from NAME_KEYED_STACK_KEYS and TOP_LEVEL_INDEX, and
the same one #13768 has just removed from CLOSURE_CONTEXT_KEY_BY_TYPE.
The #13768 card's own table lists BOTH CONTEXT_STACK_KEYS and
CLOSURE_CONTEXT_KEY_BY_TYPE as "satisfies -- validity, not completeness", and
then says the latter is "the one still hand-maintained". Measured, that is one
row short: two rows carry validity only.
What it costs, measured
buildRuntimeWriteSnapshots iterates CONTEXT_STACK_KEYS (line 393) to fill the
per-write snapshot. A context collection declared on RuntimeStackContext but
missing from CONTEXT_STACK_KEYS is therefore never carried: the caller passes
it in and the gate silently drops it, so every rule resolving references into
that collection judges a universe that is empty. That is the shyx_customer_ds
shape -- correct-looking findings against a universe that is not there.
Measured incidentally while ablating #13768: adding
widgets?: readonlyunknown[];
to RuntimeStackContext and rebuilding gives pnpm --filter @objectstack/lint buildexit 0. Nothing in packages/lint goes red. (The neighbouring guard
in runtime-gate.test.ts pins that every ENTRY of CONTEXT_STACK_KEYS is read
by some runtime-wired rule -- membership, which is again validity.)
The blast radius is bounded today by a second-order effect rather than by a
guard: protocol.ts's -? accumulator in @objectstack/metadata-protocol DOES
go red on the same edit, so a developer is told to act -- but in a different
package, about a different constant, with nothing naming CONTEXT_STACK_KEYS.
Shape a fix could take
The derivation the docblock already claims is available in-file: both inputs
(RuntimeStackContext as a type, and the literal) are in runtime-gate.ts, so
this is the same-file case where #13390's template DOES transfer -- unlike
#13768, where the constant lived one package away. A -? mapped type over
keyof RuntimeStackContext is the cheapest spelling and is the mechanism
already proven in this repo at protocol.ts:15983.
Not measured by me: whether the ordering CONTEXT_STACK_KEYS encodes ("in
stack-key order") is load-bearing anywhere, which would constrain how a
derivation is spelled.
Generated by Claude Code
Filed unassigned from the #13768 dispatch (which closed the completeness gap on
CLOSURE_CONTEXT_KEY_BY_TYPE, one package over). Not fixed there: it lands inpackages/lint, a different lane, and it is a different constant.The claim in the docblock is not what the code does
packages/lint/src/runtime-gate.ts:116says, ofRuntimeStackContext:CONTEXT_STACK_KEYSis not derived. Line 293:A hand-written literal with a
satisfiesclause. That clause asks that everyentry it NAMES is a real
RuntimeStackContextkey -- validity. It does not askthat every key of
RuntimeStackContextHAS an entry -- completeness. The sameasymmetry #13390 removed from
NAME_KEYED_STACK_KEYSandTOP_LEVEL_INDEX, andthe same one #13768 has just removed from
CLOSURE_CONTEXT_KEY_BY_TYPE.The #13768 card's own table lists BOTH
CONTEXT_STACK_KEYSandCLOSURE_CONTEXT_KEY_BY_TYPEas "satisfies -- validity, not completeness", andthen says the latter is "the one still hand-maintained". Measured, that is one
row short: two rows carry validity only.
What it costs, measured
buildRuntimeWriteSnapshotsiteratesCONTEXT_STACK_KEYS(line 393) to fill theper-write snapshot. A context collection declared on
RuntimeStackContextbutmissing from
CONTEXT_STACK_KEYSis therefore never carried: the caller passesit in and the gate silently drops it, so every rule resolving references into
that collection judges a universe that is empty. That is the
shyx_customer_dsshape -- correct-looking findings against a universe that is not there.
Measured incidentally while ablating #13768: adding
to
RuntimeStackContextand rebuilding givespnpm --filter @objectstack/lint buildexit 0. Nothing inpackages/lintgoes red. (The neighbouring guardin
runtime-gate.test.tspins that every ENTRY ofCONTEXT_STACK_KEYSis readby some runtime-wired rule -- membership, which is again validity.)
The blast radius is bounded today by a second-order effect rather than by a
guard:
protocol.ts's-?accumulator in@objectstack/metadata-protocolDOESgo red on the same edit, so a developer is told to act -- but in a different
package, about a different constant, with nothing naming
CONTEXT_STACK_KEYS.Shape a fix could take
The derivation the docblock already claims is available in-file: both inputs
(
RuntimeStackContextas a type, and the literal) are inruntime-gate.ts, sothis is the same-file case where #13390's template DOES transfer -- unlike
#13768, where the constant lived one package away. A
-?mapped type overkeyof RuntimeStackContextis the cheapest spelling and is the mechanismalready proven in this repo at
protocol.ts:15983.Not measured by me: whether the ordering
CONTEXT_STACK_KEYSencodes ("instack-key order") is load-bearing anywhere, which would constrain how a
derivation is spelled.
Generated by Claude Code