Skip to content

UNOBSERVED_REASONS has no compile-time witness, so an UnobservedReason added to the type can silently miss the runtime array #2366

Description

@lex00

What

Found during the second adversarial review of #2365, as a contrast rather than a finding against that PR.

UnobservedReason (packages/core/src/observation.ts:53) is a closed union, and UNOBSERVED_REASONS (:61) is its runtime witness — "every legal UnobservedReason, for validation and conformance checks." The two are kept in sync by hand. The reviewer added "quiesced" to the union and left the array untouched: tsc was clean and every observation test stayed green. The type now admits a value the validator and the conformance suite reject.

The same tamper against BehaviourUnpredictedReason in behaviour.ts fails at compile time at behaviour.ts(366,7), because #2365 derives its runtime array from Object.keys of a Record<BehaviourUnpredictedReason, true> — every member of the type must appear as a key or the object literal does not type-check. That construction is applied to all four closed sets in that module.

Why it matters

UnobservedReason is the vocabulary every observing lexicon's tri-state runs on, and observation-conformance.ts enforces it end to end. A reason added to the type and forgotten in the array is a reason the conformance suite refuses while the type permits — a lexicon can construct a value its own suite rejects. That is the exact desync the behaviour contract closed for itself, and it should not remain open one module over.

Do

Replace the hand-written array with the witness pattern from behaviour.ts:366: const WITNESS: Record<UnobservedReason, true> = { … } and UNOBSERVED_REASONS = Object.keys(WITNESS) as UnobservedReason[]. Check NotAttemptedReason in packages/core/src/apply.ts for the same gap — it is the other closed reason enum the conformance suite reads.

Proof

Add a member to UnobservedReason without touching the array: today tsc is clean; after, it fails in observation.ts. Shown red by the tamper, then green with the array updated.

Refs #2365, #2356.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions