@reticlehq/test is Reticle’s spec runner for CI. It turns an interactive session into a suite that runs on every pull request, without an agent and without paying model tokens to re-derive the same checks. Install it when you want unattended verification; skip it if you only ever drive Reticle interactively through MCP.
Licensed under SEE LICENSE IN LICENSE. Depends on @reticlehq/core and @reticlehq/server. Optional peer dependency: vitest ^3.2.6.
Why it exists
Driving interactively is reconnaissance. At some point you want the same checks to run unattended. This package invokes the tool layer directly, so a spec is the same evidence an agent would have gathered, minus the model.Booting a session
BootOptions
bootSession resolves to BootedRun: { invoke: ToolInvoker; close: () => Promise<void> }.
Writing specs
reticleTest(name: string, fn: SpecFn): void registers into a module-level registry. register, getRegistered and clearRegistry are exported for anyone driving it themselves.
SpecFn types its argument as SpecContext, which declares only invoke. The runner actually
hands the spec whatever buildContext returned, which is a TestContext. In plain JavaScript
this is invisible; in TypeScript you get Property 'fill' does not exist on type 'SpecContext'
unless you narrow it as above.The test context
createTestContext(invoke: ToolInvoker, options?: TestContextOptions): TestContext, where TestContextOptions is { sessionId?: string; defaultTimeoutMs?: number } and the timeout defaults to DEFAULT_ASSERT_TIMEOUT_MS.
Running and reporting
RunnerOptions is { invoke; buildContext; now; print?; specs? }. RunSummary is { total, passed, failed, skipped, ok }.
Saved flows as specs
A recorded flow becomes a spec without being rewritten:flowToSpec, flowsAsSpecs, registerFlowSpecs, with assertSuccess, successToPredicate, and the FlowSpec, FlowSpecOptions and FlowsAsSpecsOptions types. Malformed flows raise FlowMalformedError.
Errors and control flow
ReticleSkip and isSkip for skipping, ReticleAssertionError with an AssertionDetail, ReticleQueryEmptyError when a query matched nothing.
Constants: TestStatus, SpecKind, SpecOutcome, SpecMessage, PredicateKind, STATUS_GLYPH, SUMMARY_FOOTER_PREFIX, JUnit, DEFAULT_JUNIT_SUITE_NAME, SKIP_REASON_REAL_INPUT, PROBE_TESTID, FLOW_LOAD_ERROR_PREFIX, DEFAULT_ASSERT_TIMEOUT_MS.
Also exported: resolveTestid, and the standalone expectInputModeReal, InputModeTracker and readInputMode.
Turning a session into a suite
Writing specs that bind to signals rather than DOM structure.