Skip to main content
@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.
Last modified on August 16, 2026