Skip to content

feat(oracles): declared states on enumerated history sources - #19

Merged
KyleAMathews merged 11 commits into
mainfrom
overnight/b-f23
Nov 11, 2026
Merged

KyleAMathews merged 11 commits into
mainfrom
overnight/b-f23

Conversation

@KyleAMathews

Copy link
Copy Markdown
Contributor

Summary

An enumerated source can now declare its law's states with histories: { enumerated, states?, measured? }. Before, only generated sources declared reach. For example, query-db-collection's "failureReason follows the still-failing Queries" has 1296 histories and 4 model-derived states, but uses a grammar calibration.

This branch starts from overnight/b-f20 (F20) and origin/main 5db40e8 (#14). Merge it after F20. F23's own changes are in src/oracle-property.ts, the self-oracle and its fixture, and .changeset/enumerated-declared-states.md.

API and design

The new type is EnumeratedHistories<THistory> { enumerated; states?: ReadonlyArray<string>; measured?: ReadonlyArray<string> }, exported through export * with no runtime export. Plain arrays work as before and register no reach test. Generated and enumerated tests now share assertDeclared, declaredOf, and countsText.

An enumerated source runs every history, so its reach is deterministic and needs no constructed history.

  • At least one history must enter each states entry. reach counts measured states without requiring them.
  • A history enters a state through reached(plan, trace, expected) or observe("reach", state).
  • A history counts only when judged, as a pass or a law violation. A history that fails before a verdict enters nothing.
  • The reach test runs last and fails with the counts when a required state has no history. Otherwise it prints <id> enumerated reach {...}, like a campaign.
  • A history's test records an empty entry at start, and a judgment only if its signal is not aborted. A late judgment records nothing.
  • The reach test reuses these records. It runs only histories whose test never started, such as histories that -t excluded, and applies assertDeclared to them.
  • A judged history that enters an undeclared state fails its own test, before the law check, as in a campaign's predicate.
  • The reach test's route is calibration, because it checks the declaration, not production. A mutant that only changes which states production enters is thus not a kill.

Registration rejects a states or measured entry that is not a string. It rejects a label that names a history reach or determinism, also for plain lists, because the rerun pattern would select the framework test. It rejects keys other than enumerated, states, and measured, and names the key. Load errors come in this order: owns, predict on an enumerated source, extra keys, non-name states, a history that is not data, and a reserved label.

Evidence

  • Law: At least one judged history enters each required state, and reach reports judged counts per declared state. An undeclared state fails before a law violation. Authority: maintainer decision of 2026-11-10 in the F23 brief. The undeclared-state order comes from the generated-source contract in oracle-property.ts and generatedTests.
  • Reach: New self-oracle dimensions are enumeratedStates, enumeratedShape, and two reserved label styles. enumeratedStates makes each of saw-0, saw-1, and saw-2 required, measured, or undeclared, or declares no states. enumeratedShape adds an extra key or a cast probe-bound state. A new probe, reach alone, reruns each declaring source with -t selecting only the reach test, and owes the full run's outcome and counts. A pinned fixed history enters each of nine new measured states. Campaigns reached only enumerated-reach-alone and enumerated-reach-counted, once each, so the pins carry most evidence.
  • RED: On the parent, pnpm exec vitest --run --config vitest.oracle-framework.config.mjs --maxWorkers=2 -t "enumerated source|enumerated history that enters|reach test runs|violating enumerated" gave 5 failed and 3 passed, each as failed to load. For the review round, a wider -t on 6b6a6c5 gave 7 failed and 6 passed.
  • GREEN: After a merge of origin/main 05acfdf (fix(eslint-plugin-oracles): report empty tests, leave out tool config files, cite only shipped files #16), the framework suite gave 120 of 120 in 278 s. The default suite gave 55 of 55. tsc and eslint were clean, with one existing warning.
  • Mutant: All twelve mutants fail:
    • M1 counts only passing histories: 2 failed.
    • M2 lets a law violation outrank an undeclared state: 1 failed.
    • M3 never fails the reach test: 1 failed.
    • M4 skips unrecorded histories: it survived until the reach-alone probe existed, and now 4 fail.
    • M5 gives the reach test route case: 1 failed.
    • MA always reruns every history: 6 failed.
    • MB records only on a verdict, as the reviewed code did.
    • MC records late judgments.
    • MD skips assertDeclared for reach-test runs.
    • ME accepts a reserved label.
    • MF accepts an extra key.
    • MG accepts a state that is not a name.
  • Limits: The grammar never draws a partial selection with some histories recorded. Slow specs skip the reach-alone probe, because their reach test times out anyway. Probe-bound states and predict stay generated-only. A cancelled run's aborted signal also records nothing.

Review round (2026-11-11)

The review found no high-severity bug. Each fix went oracle-first, with RED on 6b6a6c5 and a killed mutant.

  1. (Medium) The reach test reran histories whose driver threw or timed out, possibly beside a running driver. The start record and signal rule stop this. MB, MC.
  2. (Medium) Nothing checked record reuse. The self-oracle now requires zero reach-test driver runs in a full run. MA.
  3. (Low) Non-string states now fail, so missed compares names only. MG.
  4. (Low) Reserved labels now fail. ME.
  5. (Low) Reruns skipped assertDeclared, and the reach-alone probe now owes undeclared-state. MD.
  6. (Note) Extra keys such as constructed now fail to load. MF.

In the timeout witness, the first of four slow histories enters the required state late. A fixture beforeEach holds the reach test until all slow driver runs finish, then waits one macrotask. The witness thus ignores timing, still kills MC, and passed 20 runs at 2 workers beside two yes > /dev/null processes.

Decisions for the maintainer

  • States are names only. Probe-bound states need predict, the agreement law, and forced-probe calibrations, which can come later in this shape.
  • An undeclared state outranks a law violation, as in campaigns. The other order shows the more useful violation, but the generated predicate would need the same change.
  • The reach test reruns only never-started histories instead of failing as incomplete. Thus -t reach and gap-report subsets stay valid, at the cost of extra runs.
  • A label of reach or determinism can collide with a framework test. Registration now rejects both.
  • I chose the load-error order above.

This pull request and its description were written by Isaac.

KyleAMathews and others added 11 commits November 10, 2026 23:07
…ies can be taken back

A fixed or constructed history whose arbitrary's canShrinkWithoutContext is
false now fails the property's load, naming the property, the history, and
the fix for a .map without an unmapper. Before, the check ran only when the
history's test ran, after the campaigns, and its message blamed the language.

Co-authored-by: Isaac <no-reply@databricks.com>
…ration; precise take-back fix text

Registration builds constructed histories when the file loads, so a build
that throws there now fails the load with an error that names the property
and the constructed history, with the build's error as its cause. The fix
text no longer recommends fc.constantFrom for objects. The changeset states
that one such history blocks the whole file and that builds run at import.

Co-authored-by: Isaac <no-reply@databricks.com>
An enumerated source can pass { enumerated, states, measured }. Every
history runs, so a required state needs no constructed history: a reach
test after the histories fails when no history entered a required state,
and otherwise prints the entry counts. A judged history that enters an
undeclared state fails its test, as a campaign's history does.

Co-authored-by: Isaac <no-reply@databricks.com>
# Conflicts:
#	packages/oracles/tests/oracle-property-self-oracle-fixture.ts
#	packages/oracles/tests/oracle-property-self-oracle.test.ts
…f-oracle

A reach test selected alone by -t judges every history itself, and owes
the same outcome and counts as in the full run. This kills a reach test
that counts unrecorded histories as entering nothing.

Co-authored-by: Isaac <no-reply@databricks.com>
…vernight/b-f23

Co-authored-by: Isaac <no-reply@databricks.com>
…est never started

A history whose test started and failed before a verdict, or timed out, now
counts for nothing instead of running again beside a driver that may still
be running; a judgment that arrives after its test timed out is not
recorded. A history the reach test runs is checked for undeclared states.
An enumerated source rejects keys other than enumerated, states, and
measured, and states that are not names, and a label may not name the
reach or determinism test. The self-oracle requires that a full run reruns
no history, and gains witnesses for each rule.

Co-authored-by: Isaac <no-reply@databricks.com>
…gments arrive

The self-oracle's timeout witness relied on a 100 ms margin between the
first history's late judgment and the reach test's start. The fixture now
holds the reach test until every slow driver run of its file has finished,
so the order is fixed whatever the machine's load.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
@changeset-bot

changeset-bot Bot commented Nov 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 254d37d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@tanstack/oracles Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Nov 11, 2026

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 04f4395c-a6e7-41c3-b94a-57f6ed19df9b
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@pkg-pr-new

pkg-pr-new Bot commented Nov 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/TanStack/oracles/@tanstack/eslint-plugin-oracles@19
npm i https://pkg.pr.new/TanStack/oracles/@tanstack/oracles@19

commit: 254d37d

@KyleAMathews
KyleAMathews merged commit 1bb905a into main Nov 11, 2026
8 checks passed
@KyleAMathews
KyleAMathews deleted the overnight/b-f23 branch November 11, 2026 08:21
KyleAMathews added a commit that referenced this pull request Nov 11, 2026
Replace interim rules with what landed: the take-back check now fails at
load (#15); lint reports empty tests, and the presets ignore tool configs, so
a custom ignores must merge the preset's (#16); the gap check's TypeScript is
an npm alias, with only a .bin/tsc residue (#18); enumerated sources declare
states, and the reach test enforces them (#19).

Co-authored-by: Isaac <no-reply@databricks.com>
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.

1 participant