@reticlehq/test 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.
Version 2.8.0. 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.
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, JUnit, SKIP_REASON_REAL_INPUT, PROBE_TESTID.
Turning a session into a suite
Writing specs that bind to signals rather than DOM structure.