@reticlehq/server 2.6.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -5
- package/SKILL.md +251 -33
- package/dist/bridge/bridge.d.ts +6 -1
- package/dist/bridge/bridge.js +27 -4
- package/dist/capsule/causal-summary.d.ts +20 -0
- package/dist/capsule/causal-summary.js +31 -6
- package/dist/capsule/divergence.js +7 -1
- package/dist/cli/chromium-hint.d.ts +68 -0
- package/dist/cli/chromium-hint.js +145 -0
- package/dist/cli/cli-doctor.js +31 -36
- package/dist/cli/cli-kill.d.ts +58 -0
- package/dist/cli/cli-kill.js +162 -0
- package/dist/cli/cli-launch.d.ts +18 -0
- package/dist/cli/cli-launch.js +32 -1
- package/dist/cli/cli-parse.d.ts +16 -1
- package/dist/cli/cli-parse.js +32 -9
- package/dist/cli/cli-port.d.ts +8 -0
- package/dist/cli/cli-port.js +36 -1
- package/dist/cli/cli-verify.js +2 -2
- package/dist/cli/doctor-daemon-line.js +10 -1
- package/dist/cli/doctor-sessions-line.d.ts +12 -0
- package/dist/cli/doctor-sessions-line.js +66 -0
- package/dist/cli/port-holder.d.ts +10 -0
- package/dist/cli/port-holder.js +26 -2
- package/dist/cli.js +139 -23
- package/dist/crawl/crawl-tools.js +12 -2
- package/dist/crawl/crawl.d.ts +13 -0
- package/dist/crawl/crawl.js +50 -2
- package/dist/daemon/daemon-resilience.js +2 -2
- package/dist/daemon/loopback-alias.d.ts +32 -0
- package/dist/daemon/loopback-alias.js +80 -0
- package/dist/events/contradictions.d.ts +14 -0
- package/dist/events/contradictions.js +26 -17
- package/dist/events/predicate-eval.d.ts +26 -3
- package/dist/events/predicate-eval.js +284 -95
- package/dist/events/predicate-parse.d.ts +1 -1
- package/dist/events/predicate-parse.js +18 -4
- package/dist/events/predicate.js +170 -18
- package/dist/events/ring-buffer.d.ts +8 -0
- package/dist/events/ring-buffer.js +34 -1
- package/dist/flows/flow-classify.js +1 -1
- package/dist/flows/flow-tools.js +12 -4
- package/dist/flows/heal-run.js +1 -1
- package/dist/honesty/blind-spots.js +6 -2
- package/dist/honesty/honesty.d.ts +11 -0
- package/dist/honesty/honesty.js +9 -1
- package/dist/honesty/unsettled.d.ts +40 -0
- package/dist/honesty/unsettled.js +78 -0
- package/dist/honesty/verified.d.ts +7 -0
- package/dist/honesty/verified.js +27 -4
- package/dist/http-server.js +11 -1
- package/dist/index.js +29 -2
- package/dist/init/agent-rules.d.ts +16 -0
- package/dist/init/agent-rules.js +96 -17
- package/dist/init/astro-patch.d.ts +2 -1
- package/dist/init/astro-patch.js +11 -5
- package/dist/init/capabilities.d.ts +22 -0
- package/dist/init/capabilities.js +76 -0
- package/dist/init/confirm.d.ts +96 -0
- package/dist/init/confirm.js +138 -0
- package/dist/init/cra.js +3 -2
- package/dist/init/detect.d.ts +8 -0
- package/dist/init/detect.js +15 -0
- package/dist/init/non-js-project.d.ts +27 -0
- package/dist/init/non-js-project.js +84 -0
- package/dist/init/plan-framework.d.ts +9 -0
- package/dist/init/plan-framework.js +72 -16
- package/dist/init/plan.d.ts +40 -4
- package/dist/init/plan.js +183 -43
- package/dist/init/run.d.ts +24 -2
- package/dist/init/run.js +181 -46
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/slash-command.js +46 -6
- package/dist/init/snippets.d.ts +64 -5
- package/dist/init/snippets.js +220 -31
- package/dist/init/vite-config.js +29 -3
- package/dist/input/network-mock-tools.d.ts +19 -0
- package/dist/input/network-mock-tools.js +29 -4
- package/dist/license/license.js +1 -1
- package/dist/mcp/mcp-proxy.d.ts +69 -3
- package/dist/mcp/mcp-proxy.js +215 -23
- package/dist/mcp/mcp.js +58 -7
- package/dist/mcp/proxy-lifecycle.d.ts +20 -1
- package/dist/mcp/proxy-lifecycle.js +22 -1
- package/dist/pool/browser-pool.d.ts +22 -2
- package/dist/pool/browser-pool.js +44 -4
- package/dist/pool/playwright-launcher.js +10 -2
- package/dist/project/learned-routes.d.ts +24 -0
- package/dist/project/learned-routes.js +63 -0
- package/dist/project/project-store.d.ts +6 -0
- package/dist/project/project-store.js +25 -1
- package/dist/session/live-control-tools.js +39 -2
- package/dist/session/live-control.d.ts +7 -9
- package/dist/session/live-control.js +34 -1
- package/dist/session/no-session-diagnosis.d.ts +23 -4
- package/dist/session/no-session-diagnosis.js +128 -44
- package/dist/session/no-session-watch.js +12 -0
- package/dist/session/ref-provenance.d.ts +54 -0
- package/dist/session/ref-provenance.js +61 -0
- package/dist/session/review-store.d.ts +19 -0
- package/dist/session/review-store.js +34 -5
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +9 -0
- package/dist/session/session-manager.js +11 -0
- package/dist/session/session.d.ts +8 -0
- package/dist/session/session.js +12 -0
- package/dist/status-payload.d.ts +13 -1
- package/dist/status-payload.js +6 -1
- package/dist/telemetry/app-instrumented.d.ts +37 -0
- package/dist/telemetry/app-instrumented.js +54 -0
- package/dist/telemetry/automation-hint.d.ts +17 -0
- package/dist/telemetry/automation-hint.js +71 -0
- package/dist/telemetry/bug-found.d.ts +0 -20
- package/dist/telemetry/bug-found.js +38 -21
- package/dist/telemetry/cli-telemetry.js +7 -1
- package/dist/telemetry/daemon-telemetry.d.ts +4 -4
- package/dist/telemetry/daemon-telemetry.js +8 -4
- package/dist/telemetry/feedback.js +1 -1
- package/dist/telemetry/init-telemetry.js +9 -1
- package/dist/telemetry/install-source.d.ts +36 -0
- package/dist/telemetry/install-source.js +40 -0
- package/dist/telemetry/run-telemetry.js +4 -1
- package/dist/telemetry/session-metrics.d.ts +3 -3
- package/dist/telemetry/session-metrics.js +9 -4
- package/dist/telemetry/telemetry.d.ts +7 -1
- package/dist/telemetry/telemetry.js +17 -2
- package/dist/telemetry/tool-refused.d.ts +19 -0
- package/dist/telemetry/tool-refused.js +55 -0
- package/dist/telemetry/verification-of.js +72 -8
- package/dist/temp-dir.d.ts +27 -0
- package/dist/temp-dir.js +46 -0
- package/dist/tools/act-tools.js +77 -11
- package/dist/tools/assert-grade.d.ts +1 -1
- package/dist/tools/assert-grade.js +27 -5
- package/dist/tools/assert-verdict.js +25 -1
- package/dist/tools/contract-tools.js +4 -0
- package/dist/tools/dynamic-tools.js +1 -1
- package/dist/tools/error-recovery.d.ts +10 -0
- package/dist/tools/error-recovery.js +70 -5
- package/dist/tools/feedback-invite.js +1 -1
- package/dist/tools/invoke-tool.js +149 -32
- package/dist/tools/lease-tools.d.ts +29 -1
- package/dist/tools/lease-tools.js +58 -5
- package/dist/tools/navigate-result.js +1 -1
- package/dist/tools/observe-tools.js +1 -1
- package/dist/tools/observed-nothing.js +1 -1
- package/dist/tools/react-grace.d.ts +48 -0
- package/dist/tools/react-grace.js +87 -0
- package/dist/tools/read-tools.js +20 -4
- package/dist/tools/settle-in-flight.d.ts +79 -0
- package/dist/tools/settle-in-flight.js +106 -0
- package/dist/tools/snapshot-delta.d.ts +7 -0
- package/dist/tools/snapshot-delta.js +130 -6
- package/dist/tools/tool-kit.d.ts +52 -5
- package/dist/tools/tool-kit.js +55 -24
- package/dist/tools/tool-surface.js +28 -9
- package/dist/tools/tools.js +38 -4
- package/dist/visual/capture-cleanup.d.ts +16 -0
- package/dist/visual/capture-cleanup.js +39 -0
- package/dist/visual/visual-tools.d.ts +18 -0
- package/dist/visual/visual-tools.js +24 -5
- package/docs/README.md +56 -11
- package/docs/actions.mdx +87 -0
- package/docs/agent-cheatsheet.md +102 -26
- package/docs/architecture.md +46 -35
- package/docs/benchmarks.md +76 -72
- package/docs/best-practices.mdx +211 -0
- package/docs/capabilities.mdx +387 -0
- package/docs/cli/affected.mdx +51 -0
- package/docs/cli/capsules.mdx +69 -0
- package/docs/cli/cloud.mdx +127 -0
- package/docs/cli/doctor.mdx +76 -0
- package/docs/cli/drive.mdx +55 -0
- package/docs/cli/feedback.mdx +67 -0
- package/docs/cli/gate.mdx +64 -0
- package/docs/cli/help.mdx +92 -0
- package/docs/cli/hunt.mdx +57 -0
- package/docs/cli/identify.mdx +62 -0
- package/docs/cli/init.mdx +94 -0
- package/docs/cli/kill.mdx +91 -0
- package/docs/cli/license.mdx +48 -0
- package/docs/cli/mcp.mdx +61 -0
- package/docs/cli/open.mdx +68 -0
- package/docs/cli/restart.mdx +60 -0
- package/docs/cli/rollback.mdx +35 -0
- package/docs/cli/serve.mdx +62 -0
- package/docs/cli/status.mdx +64 -0
- package/docs/cli/stop.mdx +56 -0
- package/docs/cli/telemetry.mdx +57 -0
- package/docs/cli/update.mdx +52 -0
- package/docs/cli/verify.mdx +57 -0
- package/docs/cli/version.mdx +39 -0
- package/docs/cli/watch.mdx +55 -0
- package/docs/cli.mdx +219 -0
- package/docs/debugging.md +13 -9
- package/docs/deploy-checks.md +15 -11
- package/docs/desktop-apps.md +49 -40
- package/docs/desktop.mdx +188 -0
- package/docs/docs.json +363 -0
- package/docs/enterprise.md +20 -16
- package/docs/faq.mdx +88 -0
- package/docs/fixtures.md +11 -7
- package/docs/flows.md +26 -22
- package/docs/for-agents.md +71 -0
- package/docs/frameworks.mdx +199 -0
- package/docs/gate-plan.md +45 -41
- package/docs/gates.md +23 -19
- package/docs/getting-started.md +76 -65
- package/docs/human-control.md +22 -18
- package/docs/index.mdx +101 -0
- package/docs/install-agentic.mdx +215 -0
- package/docs/install-manual.mdx +194 -0
- package/docs/instrumentation.mdx +232 -0
- package/docs/integration-patterns.md +38 -34
- package/docs/local-registry.md +16 -12
- package/docs/multi-agent-testing.md +16 -12
- package/docs/packages/babel-plugin.mdx +70 -0
- package/docs/packages/browser.mdx +111 -0
- package/docs/packages/core.mdx +106 -0
- package/docs/packages/electron.mdx +87 -0
- package/docs/packages/eslint-plugin.mdx +84 -0
- package/docs/packages/next.mdx +91 -0
- package/docs/packages/react.mdx +91 -0
- package/docs/packages/server.mdx +98 -0
- package/docs/packages/tauri.mdx +63 -0
- package/docs/packages/test.mdx +108 -0
- package/docs/packages/vite-plugin.mdx +96 -0
- package/docs/packages.mdx +217 -0
- package/docs/platform-integration.md +33 -26
- package/docs/predicates.mdx +179 -0
- package/docs/quickstart.mdx +211 -0
- package/docs/recipes.mdx +242 -0
- package/docs/skill-file.mdx +105 -0
- package/docs/state-management.mdx +197 -0
- package/docs/style.css +14 -0
- package/docs/system-map.md +47 -32
- package/docs/telemetry-contract.md +101 -46
- package/docs/telemetry-events.mdx +430 -0
- package/docs/telemetry.md +63 -50
- package/docs/testing.md +10 -6
- package/docs/token-efficiency.md +30 -26
- package/docs/tools-act-and-wait.mdx +123 -0
- package/docs/tools-act-sequence.mdx +120 -0
- package/docs/tools-act.mdx +82 -0
- package/docs/tools-assert.mdx +70 -0
- package/docs/tools-console.mdx +59 -0
- package/docs/tools-inspect.mdx +85 -0
- package/docs/tools-navigate.mdx +63 -0
- package/docs/tools-network.mdx +86 -0
- package/docs/tools-observe.mdx +89 -0
- package/docs/tools-overview.mdx +157 -0
- package/docs/tools-query.mdx +82 -0
- package/docs/tools-session-and-feedback.mdx +91 -0
- package/docs/tools-sessions.mdx +65 -0
- package/docs/tools-snapshot.mdx +75 -0
- package/docs/tools-state.mdx +81 -0
- package/docs/tools-tools-and-run.mdx +125 -0
- package/docs/tools-wait-for.mdx +66 -0
- package/docs/troubleshooting.mdx +193 -0
- package/docs/usage.md +175 -153
- package/docs/vs-chrome-devtools-mcp.mdx +82 -0
- package/docs/vs-playwright-mcp.mdx +85 -0
- package/docs/vs-screenshots.mdx +96 -0
- package/docs/what-is-reticle.mdx +63 -0
- package/docs/why-reticle.mdx +107 -0
- package/package.json +2 -2
- package/docs/matrix/2.5.0/claude-code-machine.json +0 -19
- package/docs/matrix/2.5.0/codex-machine.json +0 -20
- package/docs/matrix/2.5.0/cursor-machine.json +0 -22
- package/docs/matrix/2.5.0/gemini-machine.json +0 -22
- package/docs/matrix/2.5.0/opencode-machine.json +0 -22
- package/docs/matrix/2.5.0/vscode-machine.json +0 -22
- package/docs/matrix/2.5.0/windsurf-machine.json +0 -22
- package/docs/matrix/MATRIX.md +0 -26
- package/docs/matrix/README.md +0 -59
- package/docs/mint.json +0 -61
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'reticle-tauri'
|
|
3
|
+
description: The Rust crate that gives a Tauri app Reticle screenshots and headless mode.
|
|
4
|
+
icon: rust
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
`reticle-tauri` is a Rust crate on crates.io, not an npm package. It is versioned **independently** of the ten npm packages, and is currently **0.1.0**.
|
|
8
|
+
|
|
9
|
+
**MIT licensed, unlike the Apache 2.0 npm packages. Edition 2021.**
|
|
10
|
+
|
|
11
|
+
## Why it exists, and why it is small
|
|
12
|
+
|
|
13
|
+
Tauri IPC observation needs nothing on the Rust side. An `invoke('load_todos')` already reaches Reticle as `ipc://load_todos` through the SDK in the webview. You only need this crate for two things: screenshots, and headless mode.
|
|
14
|
+
|
|
15
|
+
<Warning>
|
|
16
|
+
Do not expect this to be version `2.7`. Matching the npm version would imply a release cadence the
|
|
17
|
+
crate does not have, and it would make every npm release a crates.io release for no reason.
|
|
18
|
+
</Warning>
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```toml
|
|
23
|
+
[dependencies]
|
|
24
|
+
reticle-tauri = "0.1"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Its own dependencies are `tauri = "2"` plus the per-platform capture backends: `objc2` and friends on macOS, `webview2-com` and `windows` on Windows, `webkit2gtk` and `cairo-rs` on Linux and the BSDs.
|
|
28
|
+
|
|
29
|
+
## Wiring
|
|
30
|
+
|
|
31
|
+
```rust
|
|
32
|
+
tauri::Builder::default()
|
|
33
|
+
.invoke_handler(tauri::generate_handler![reticle_tauri::reticle_capture])
|
|
34
|
+
.on_page_load(reticle_tauri::on_page_load)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Nothing is needed on the JavaScript side. Tauri has no preload stage, so the SDK reaches `reticle_capture` through Tauri's own internals.
|
|
38
|
+
|
|
39
|
+
## The public API
|
|
40
|
+
|
|
41
|
+
| Item | Signature |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| `reticle_capture` | `#[tauri::command] pub async fn reticle_capture(window: tauri::WebviewWindow, full_page: Option<bool>) -> Result<String, String>` |
|
|
44
|
+
| `on_page_load` | `pub fn on_page_load<R: Runtime>(webview: &Webview<R>, payload: &tauri::webview::PageLoadPayload<'_>)` |
|
|
45
|
+
| `FULL_PAGE_UNSUPPORTED` | `pub const FULL_PAGE_UNSUPPORTED: &str = "full-page-unsupported"` |
|
|
46
|
+
|
|
47
|
+
There are no public traits and no macros.
|
|
48
|
+
|
|
49
|
+
### `reticle_capture`
|
|
50
|
+
|
|
51
|
+
Runs on a blocking thread, writes a PNG into a temporary directory as `reticle-capture-<nanos>.png`, and returns the **path**, not the bytes. Full-page capture is supported on Linux only; elsewhere it returns `FULL_PAGE_UNSUPPORTED`, which is pinned to core's `VisualReason` value so the refusal is the same string everywhere.
|
|
52
|
+
|
|
53
|
+
### `on_page_load`
|
|
54
|
+
|
|
55
|
+
Hides the window on `PageLoadEvent::Finished` when the environment variable `RETICLE_HEADLESS=1` is set. That ordering matters: the window exists and renders, then it is hidden, so the capture path still works.
|
|
56
|
+
|
|
57
|
+
## The silent failure to know about
|
|
58
|
+
|
|
59
|
+
A Tauri app with the default CSP runs perfectly and never connects to Reticle, with nothing said anywhere. [`reticle doctor`](/cli/doctor) diagnoses this explicitly on a desktop project, because it is invisible otherwise.
|
|
60
|
+
|
|
61
|
+
<Card title="Desktop setup in full" icon="display" href="/desktop-apps">
|
|
62
|
+
The CSP entry, the headless flag, and how capture differs from a browser screenshot.
|
|
63
|
+
</Card>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: '@reticlehq/test'
|
|
3
|
+
description: The Reticle spec runner for CI, driving the tools directly with no MCP or stdio in the loop.
|
|
4
|
+
icon: vial
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
`@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.
|
|
8
|
+
|
|
9
|
+
**Version 2.8.0. Licensed under `SEE LICENSE IN LICENSE`. Depends on `@reticlehq/core` and `@reticlehq/server`. Optional peer dependency: `vitest ^3.2.6`.**
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i -D @reticlehq/test
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Why it exists
|
|
16
|
+
|
|
17
|
+
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.
|
|
18
|
+
|
|
19
|
+
## Booting a session
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { bootSession, createTestContext, runSpecs, printSummary } from '@reticlehq/test';
|
|
23
|
+
|
|
24
|
+
const run = await bootSession({ driveUrl: 'http://localhost:5173' });
|
|
25
|
+
const { results, summary } = await runSpecs({
|
|
26
|
+
invoke: run.invoke,
|
|
27
|
+
buildContext: (invoke) => createTestContext(invoke),
|
|
28
|
+
now: () => Date.now(),
|
|
29
|
+
});
|
|
30
|
+
printSummary(summary);
|
|
31
|
+
await run.close();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### `BootOptions`
|
|
35
|
+
|
|
36
|
+
| Option | Type | Default |
|
|
37
|
+
| ------------- | ------------------------------------- | ----------------------- |
|
|
38
|
+
| `driveUrl` | `string` | required |
|
|
39
|
+
| `headless` | `boolean` | `true` |
|
|
40
|
+
| `port` | `number` | the default bridge port |
|
|
41
|
+
| `reticleRoot` | `string` | `cwd()/.reticle` |
|
|
42
|
+
| `now` | `() => number` | `Date.now` |
|
|
43
|
+
| `buildDeps` | `(server: RunningServer) => ToolDeps` | built in |
|
|
44
|
+
|
|
45
|
+
`bootSession` resolves to `BootedRun`: `{ invoke: ToolInvoker; close: () => Promise<void> }`.
|
|
46
|
+
|
|
47
|
+
## Writing specs
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { reticleTest } from '@reticlehq/test';
|
|
51
|
+
|
|
52
|
+
reticleTest('login grants a session', async (t) => {
|
|
53
|
+
await t.fill('email', 'user@example.com');
|
|
54
|
+
await t.fill('password', 'hunter2');
|
|
55
|
+
await t.actAndWait('login-submit', 'click', { kind: 'signal', name: 'auth:granted' });
|
|
56
|
+
await t.expectNet('POST', '/api/session', 200);
|
|
57
|
+
await t.expectNoConsoleErrors();
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`reticleTest(name: string, fn: SpecFn): void` registers into a module-level registry. `register`, `getRegistered` and `clearRegistry` are exported for anyone driving it themselves.
|
|
62
|
+
|
|
63
|
+
## The test context
|
|
64
|
+
|
|
65
|
+
`createTestContext(invoke: ToolInvoker, options?: TestContextOptions): TestContext`, where `TestContextOptions` is `{ sessionId?: string; defaultTimeoutMs?: number }` and the timeout defaults to `DEFAULT_ASSERT_TIMEOUT_MS`.
|
|
66
|
+
|
|
67
|
+
| Method | Signature |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| `act` | `(testid: string, action: ActionType, args?: Record<string, unknown>) => Promise<void>` |
|
|
70
|
+
| `fill` | `(testid: string, value: string) => Promise<void>` |
|
|
71
|
+
| `actAndWait` | `(testid: string, action: ActionType, until: Predicate) => Promise<void>` |
|
|
72
|
+
| `expectSignal` | `(name: string, dataMatches?: Record<string, unknown>) => Promise<void>` |
|
|
73
|
+
| `expectNet` | `(method: string, urlContains: string, status?: number) => Promise<void>` |
|
|
74
|
+
| `expectElement` | `(query: ElementQuery, state?: ElementState) => Promise<void>` |
|
|
75
|
+
| `expectText` | `(contains: string) => Promise<void>` |
|
|
76
|
+
| `expectAbsent` | `(query: ElementQuery) => Promise<void>` |
|
|
77
|
+
| `expectNoConsoleErrors` | `() => Promise<void>` |
|
|
78
|
+
| `state` | `(storeOrRef: string) => Promise<unknown>` |
|
|
79
|
+
| `expectInputModeReal` | `() => Promise<void>` |
|
|
80
|
+
| `invoke` | The raw `ToolInvoker` |
|
|
81
|
+
| `clock` | `TestClock` |
|
|
82
|
+
|
|
83
|
+
## Running and reporting
|
|
84
|
+
|
|
85
|
+
| Export | Signature |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| `runOne` | `(spec: ReticleSpec, opts: RunnerOptions) => Promise<SpecResult>` |
|
|
88
|
+
| `runSpecs` | `(opts: RunnerOptions) => Promise<{ results: SpecResult[]; summary: RunSummary }>` |
|
|
89
|
+
| `summarize` / `printSummary` | Build and print the summary |
|
|
90
|
+
| `toJUnitXml` / `writeJUnit` | JUnit output for CI |
|
|
91
|
+
|
|
92
|
+
`RunnerOptions` is `{ invoke; buildContext; now; print?; specs? }`. `RunSummary` is `{ total, passed, failed, skipped, ok }`.
|
|
93
|
+
|
|
94
|
+
## Saved flows as specs
|
|
95
|
+
|
|
96
|
+
A recorded flow becomes a spec without being rewritten:
|
|
97
|
+
|
|
98
|
+
`flowToSpec`, `flowsAsSpecs`, `registerFlowSpecs`, with `assertSuccess`, `successToPredicate`, and the `FlowSpec`, `FlowSpecOptions` and `FlowsAsSpecsOptions` types. Malformed flows raise `FlowMalformedError`.
|
|
99
|
+
|
|
100
|
+
## Errors and control flow
|
|
101
|
+
|
|
102
|
+
`ReticleSkip` and `isSkip` for skipping, `ReticleAssertionError` with an `AssertionDetail`, `ReticleQueryEmptyError` when a query matched nothing.
|
|
103
|
+
|
|
104
|
+
Constants: `TestStatus`, `SpecKind`, `SpecOutcome`, `SpecMessage`, `PredicateKind`, `STATUS_GLYPH`, `JUnit`, `SKIP_REASON_REAL_INPUT`, `PROBE_TESTID`.
|
|
105
|
+
|
|
106
|
+
<Card title="Turning a session into a suite" icon="flask" href="/testing">
|
|
107
|
+
Writing specs that bind to signals rather than DOM structure.
|
|
108
|
+
</Card>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: '@reticlehq/vite-plugin'
|
|
3
|
+
description: One line in vite.config.ts that stamps source locations on your JSX and auto-injects reticle.connect().
|
|
4
|
+
icon: bolt
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
`@reticlehq/vite-plugin` does the two things everyone forgets: wiring the SDK, and enabling source mapping.
|
|
8
|
+
|
|
9
|
+
**Version 2.8.0. Apache 2.0. Depends on `@babel/core`, `@reticlehq/babel-plugin` and `@reticlehq/core`. Optional peer dependency: `vite >= 4`.**
|
|
10
|
+
|
|
11
|
+
## Install and use
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i -D @reticlehq/vite-plugin @reticlehq/react
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { defineConfig } from 'vite';
|
|
19
|
+
import react from '@vitejs/plugin-react';
|
|
20
|
+
import { reticle } from '@reticlehq/vite-plugin';
|
|
21
|
+
|
|
22
|
+
export default defineConfig({
|
|
23
|
+
plugins: [reticle(), react()],
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
<Warning>
|
|
28
|
+
The import specifier is `@reticlehq/vite-plugin` and the export is `reticle`. An old README
|
|
29
|
+
documented `@reticlehq/core/vite`; that subpath has never existed in core's exports map, and `npx
|
|
30
|
+
@reticlehq/core init` is not a command either. The binary is `reticle`, from
|
|
31
|
+
[`@reticlehq/server`](/packages/server).
|
|
32
|
+
</Warning>
|
|
33
|
+
|
|
34
|
+
## Why it exists
|
|
35
|
+
|
|
36
|
+
`apply: 'serve'` means it never reaches a production build. That is the guarantee that lets you leave it in the config permanently: there is no way to ship Reticle to your users by forgetting to remove a line.
|
|
37
|
+
|
|
38
|
+
## What it injects
|
|
39
|
+
|
|
40
|
+
A virtual module at `/@reticle-connect`, added to the HTML, whose contents are:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { reticle, install } from '@reticlehq/react';
|
|
44
|
+
install();
|
|
45
|
+
reticle.connect({
|
|
46
|
+
/* resolved args */
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Plus a dynamic import of your dev module if one exists. It looks for `src/reticle-dev.ts`, `.js`, `.tsx` or `.jsx`, in that order, and leaves the file alone if you have one: that file is yours to edit.
|
|
51
|
+
|
|
52
|
+
## Options
|
|
53
|
+
|
|
54
|
+
`reticle(options?: ReticleVitePluginOptions): ReticleVitePlugin`. Every option is optional.
|
|
55
|
+
|
|
56
|
+
| Option | Type | Default |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
| `port` | `number` | Discovered from the daemon registry for this project, falling back to `4400` |
|
|
59
|
+
| `root` | `string` | Vite's `root`, else `process.cwd()` |
|
|
60
|
+
| `sdkVersion` | `string` | Read from the installed `@reticlehq/react` |
|
|
61
|
+
| `session` | `string` | Omitted, so the SDK generates one |
|
|
62
|
+
| `projectId` | `string` | Derived from the package name plus the root path |
|
|
63
|
+
| `token` | `string` | `readPairingToken()`, from `~/.reticle/pairing-token` |
|
|
64
|
+
| `sourceMapping` | `boolean` | `true`. Set `false` to skip the Babel pass |
|
|
65
|
+
| `inject` | `boolean` | `true`. Set `false` to call `connect()` yourself |
|
|
66
|
+
| `desktop` | `boolean` | `false`. When `true` the plugin also applies to `vite build` and passes `allowInProduction: true`, which is what an Electron or Tauri renderer needs |
|
|
67
|
+
| `captureNetworkBodies` | `boolean` | `false`. Also enabled by `VITE_RETICLE_CAPTURE_BODIES=1` |
|
|
68
|
+
| `onWarn` | `(message: string) => void` | `console.warn` |
|
|
69
|
+
|
|
70
|
+
## Other exports
|
|
71
|
+
|
|
72
|
+
| Export | Type | What it is |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| `RETICLE_VITE_PLUGIN_NAME` | `'reticle'` | The plugin name |
|
|
75
|
+
| `RETICLE_TOKEN_GLOBAL` | `'__RETICLE_TOKEN__'` | The global the token is stamped onto |
|
|
76
|
+
| `RETICLE_CONNECT_MODULE` | `'/@reticle-connect'` | The virtual module id |
|
|
77
|
+
| `RENDER_PREHOOK_SOURCE` | `string` | The inline render prehook snippet |
|
|
78
|
+
| `RETICLE_DEV_MODULE_CANDIDATES` | readonly tuple | The four dev-module paths it looks for |
|
|
79
|
+
| `readPairingToken` | `() => string \| undefined` | Read the pairing token from disk |
|
|
80
|
+
| `findDevModule` | `(root: string, exists: (p: string) => boolean) => string \| null` | Locate the dev module |
|
|
81
|
+
| `connectModuleSource` | `(options: ReticleVitePluginOptions, devModule?: string \| null) => string` | Build the injected module source |
|
|
82
|
+
| `cjsDepIncludes` | `(appRoot: string, canResolve?: (dep: string) => boolean) => string[]` | CommonJS deps to add to `optimizeDeps` |
|
|
83
|
+
|
|
84
|
+
Types: `ReticleVitePluginOptions`, `ReticleVitePlugin`, `ViteDevServerLike`.
|
|
85
|
+
|
|
86
|
+
## Desktop
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
reticle({ desktop: true });
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
A packaged Electron or Tauri renderer is a production build loaded from a custom protocol, so the default guards would refuse. This one flag drops `apply: 'serve'` and opts the connect call into production.
|
|
93
|
+
|
|
94
|
+
<Card title="Desktop wiring in full" icon="display" href="/desktop-apps">
|
|
95
|
+
Including the Tauri CSP whose failure is completely silent.
|
|
96
|
+
</Card>
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Packages: what is in the box
|
|
3
|
+
sidebarTitle: Packages
|
|
4
|
+
description: Every Reticle package. What it does, why it exists, when you actually need it, and what it depends on.
|
|
5
|
+
icon: cubes
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Reticle is eleven packages, and most people install two. This page exists so you can tell which two, and so that the ones you skip stop looking mysterious.
|
|
9
|
+
|
|
10
|
+
<Note>
|
|
11
|
+
Ten npm packages ship together at the same version, all at **2.8.0**, verified against the
|
|
12
|
+
registry. The Rust crate is versioned **independently** and is currently **0.1.0**. That is
|
|
13
|
+
deliberate, not drift.
|
|
14
|
+
</Note>
|
|
15
|
+
|
|
16
|
+
## Which ones do I need?
|
|
17
|
+
|
|
18
|
+
<CardGroup cols={2}>
|
|
19
|
+
<Card title="A React app on Vite" icon="bolt">
|
|
20
|
+
`@reticlehq/react` + `@reticlehq/vite-plugin`. That's it, two dev dependencies.
|
|
21
|
+
</Card>
|
|
22
|
+
<Card title="A React app on Next.js" icon="n">
|
|
23
|
+
`@reticlehq/react` + `@reticlehq/next`. Keeps SWC; no Babel migration.
|
|
24
|
+
</Card>
|
|
25
|
+
<Card title="Not React, or no bundler plugin" icon="code">
|
|
26
|
+
`@reticlehq/browser` alone, and call `connect()` yourself.
|
|
27
|
+
</Card>
|
|
28
|
+
<Card title="Electron or Tauri" icon="display">
|
|
29
|
+
The web packages, plus `@reticlehq/electron` or the `reticle-tauri` crate.
|
|
30
|
+
</Card>
|
|
31
|
+
</CardGroup>
|
|
32
|
+
|
|
33
|
+
You never install `@reticlehq/server`. Your agent runs it with `npx`.
|
|
34
|
+
|
|
35
|
+
## The architecture in one rule
|
|
36
|
+
|
|
37
|
+
The package split is not cosmetic. Each boundary enforces something:
|
|
38
|
+
|
|
39
|
+
| Package | Runs in | Hard rule |
|
|
40
|
+
| --------- | ----------- | ------------------------------------------------------ |
|
|
41
|
+
| `core` | both | Depends only on `zod`. Sits at the bottom of the graph |
|
|
42
|
+
| `browser` | the browser | Never imports a Node API |
|
|
43
|
+
| `server` | Node | Never imports a DOM API |
|
|
44
|
+
| `react` | the browser | Optional. Core must work without it |
|
|
45
|
+
|
|
46
|
+
Those rules are why the browser SDK cannot accidentally pull in a filesystem call, and why the server cannot accidentally depend on `window`. They are enforced by the dependency graph, not by discipline.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## @reticlehq/core
|
|
51
|
+
|
|
52
|
+
**The wire contract.** Every constant and zod schema that crosses a boundary. Browser to bridge, bridge to agent, is defined here.
|
|
53
|
+
|
|
54
|
+
**Why it exists:** without one shared definition, the browser and the server drift. One renames a field, the other keeps reading the old one, and the failure is silent. A verdict that quietly stops carrying evidence. Core makes that a type error instead.
|
|
55
|
+
|
|
56
|
+
**Depends on:** `zod`, and nothing else. That is the whole point; it sits at the bottom of the graph so everything can depend on it without cycles.
|
|
57
|
+
|
|
58
|
+
**Do you install it?** No. It arrives as a dependency of whatever you did install.
|
|
59
|
+
|
|
60
|
+
<Note>
|
|
61
|
+
Before 2.0, `@reticlehq/core` was an umbrella package re-exporting everything under subpaths. That
|
|
62
|
+
was retired: the umbrella meant every consumer pulled the whole world to get one constant.
|
|
63
|
+
</Note>
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## @reticlehq/browser
|
|
68
|
+
|
|
69
|
+
**The SDK that lives in your page.** It installs the observers (DOM, network, console, routing, animations), builds semantic snapshots, executes actions, and talks to the bridge.
|
|
70
|
+
|
|
71
|
+
**Why it exists:** this is the part that makes Reticle different from a browser-automation tool. Because it runs _inside_ the app rather than driving it from outside, it can see a state mutation and a fired signal, not just a rendered pixel.
|
|
72
|
+
|
|
73
|
+
**Depends on:** `@reticlehq/core`, `@testing-library/dom`. The latter is why [`reticle_query`](/tools-query) speaks role, label and testid rather than CSS selectors.
|
|
74
|
+
|
|
75
|
+
**Install it directly when** you are not on React, or you want `connect()` under your own control:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { reticle } from '@reticlehq/browser';
|
|
79
|
+
if (import.meta.env.DEV) reticle.connect();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## @reticlehq/server
|
|
85
|
+
|
|
86
|
+
**The bridge, the MCP server, and the `reticle` CLI.** Hosts the WebSocket your app dials into and exposes the tools your agent calls.
|
|
87
|
+
|
|
88
|
+
**Why it exists:** agents speak MCP; browsers speak WebSocket; somebody has to be fluent in both and own the `.reticle/` directory where flows, baselines and runs are stored.
|
|
89
|
+
|
|
90
|
+
**Depends on:** `@modelcontextprotocol/sdk`, `@reticlehq/core`, `ws`, `zod`.
|
|
91
|
+
|
|
92
|
+
**Do you install it?** Not into your project. Your agent launches it:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx @reticlehq/server mcp
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## @reticlehq/react
|
|
101
|
+
|
|
102
|
+
**The kit most React users install.** It re-exports the browser sensor. So one install gives you both `reticle` and `registerCapabilities`. And maps a DOM node to its React component and source file through the fiber tree.
|
|
103
|
+
|
|
104
|
+
**Why it exists:** this is the package that turns "the button is broken" into `src/components/Login.tsx:81`. Finding a bug is half the job. Reticle's answer to the other half lives here.
|
|
105
|
+
|
|
106
|
+
**Depends on:** `@reticlehq/browser`, `@reticlehq/core`. **Peer:** `react`.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npm i -D @reticlehq/react
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## @reticlehq/vite-plugin
|
|
115
|
+
|
|
116
|
+
**One line in `vite.config.ts`, two jobs.** It stamps source locations onto your JSX and injects `connect()` so you cannot forget to.
|
|
117
|
+
|
|
118
|
+
**Why it exists:** the two things everyone forgets are wiring the SDK and enabling source mapping. This does both, and `apply: 'serve'` guarantees it never reaches a production build.
|
|
119
|
+
|
|
120
|
+
**Depends on:** `@babel/core`, `@reticlehq/babel-plugin`, `@reticlehq/core`. **Peer:** `vite`.
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { reticle } from '@reticlehq/vite-plugin';
|
|
124
|
+
export default defineConfig({ plugins: [reticle(), react()] });
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## @reticlehq/babel-plugin
|
|
130
|
+
|
|
131
|
+
**Stamps `data-reticle-source="file:line:col"` onto JSX host elements.**
|
|
132
|
+
|
|
133
|
+
**Why it exists:** React 19 dropped `_debugSource`, which is what source mapping used to rely on. This plugin puts the information back, at build time, in dev only. Without it, `reticle_inspect` can still tell you the component name but not the file.
|
|
134
|
+
|
|
135
|
+
**Depends on:** nothing. **Peer:** `@babel/core`.
|
|
136
|
+
|
|
137
|
+
**Do you install it?** Only directly if you are wiring Babel yourself. The Vite and Next packages both pull it in.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## @reticlehq/next
|
|
142
|
+
|
|
143
|
+
**Next.js source mapping without giving up SWC.**
|
|
144
|
+
|
|
145
|
+
**Why it exists:** Next users are on SWC, and telling them to switch to Babel for a dev tool would be a rude trade. This adds a dev-only webpack pre-loader that stamps the same attribute, and leaves your build pipeline alone.
|
|
146
|
+
|
|
147
|
+
**Depends on:** `@babel/core`, `@reticlehq/babel-plugin`. **Peer:** `next`.
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
const { withReticle } = require('@reticlehq/next');
|
|
151
|
+
module.exports = withReticle({
|
|
152
|
+
/* your config */
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
<Note>
|
|
157
|
+
This one is CommonJS on purpose. `next.config.js` is loaded by Node before any ESM transform, so
|
|
158
|
+
an ESM-only helper would fail at the least helpful moment.
|
|
159
|
+
</Note>
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## @reticlehq/test
|
|
164
|
+
|
|
165
|
+
**The spec runner for CI.** `reticleTest` registers declarative specs and runs them against Reticle's tools directly, with no MCP or stdio in the loop.
|
|
166
|
+
|
|
167
|
+
**Why it exists:** driving interactively is reconnaissance. At some point you want the same checks to run on every pull request without an agent. And without paying model tokens to re-derive them.
|
|
168
|
+
|
|
169
|
+
**Depends on:** `@reticlehq/core`, `@reticlehq/server`. **Peer:** `vitest`.
|
|
170
|
+
|
|
171
|
+
<Card title="Turn a session into a suite" icon="vial" href="/testing">
|
|
172
|
+
Writing specs that bind to signals rather than DOM structure.
|
|
173
|
+
</Card>
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## @reticlehq/eslint-plugin
|
|
178
|
+
|
|
179
|
+
**One rule: state changed, so a signal must fire.**
|
|
180
|
+
|
|
181
|
+
**Why it exists:** signals are the strongest evidence Reticle can use, and they rot the way all conventions rot. Someone adds a mutation, forgets the signal, and every verdict for that flow quietly drops a grade. Nothing breaks. The tests still pass. The evidence just gets weaker.
|
|
182
|
+
|
|
183
|
+
A lint rule makes the signal layer self-enforcing, which is the only way a convention survives contact with a deadline.
|
|
184
|
+
|
|
185
|
+
**Depends on:** `@typescript-eslint/utils`. **Peer:** `eslint`.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## @reticlehq/electron
|
|
190
|
+
|
|
191
|
+
**Reaches the two places the renderer cannot.**
|
|
192
|
+
|
|
193
|
+
**Why it exists:** in Electron, the interesting failures often happen across the IPC boundary. The renderer asks the main process for something and the main process quietly fails. The renderer-side SDK cannot see that. This package makes main-process IPC observable and the window screenshottable.
|
|
194
|
+
|
|
195
|
+
**Depends on:** `@reticlehq/core`. **Peer:** `electron`.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## reticle-tauri
|
|
200
|
+
|
|
201
|
+
**The Rust crate**, on crates.io, currently **0.1.0**. Versioned independently of the npm packages.
|
|
202
|
+
|
|
203
|
+
**Why it exists:** Tauri IPC observation needs nothing on the Rust side; an `invoke('load_todos')` already reaches Reticle as `ipc://load_todos`. You only need this crate for screenshots and headless mode.
|
|
204
|
+
|
|
205
|
+
```toml
|
|
206
|
+
[dependencies]
|
|
207
|
+
reticle-tauri = "0.1"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
<Warning>
|
|
211
|
+
Do not expect this to be `2.7`. Matching the npm version would imply a release cadence the crate
|
|
212
|
+
does not have, and it would make every npm release a crates.io release for no reason.
|
|
213
|
+
</Warning>
|
|
214
|
+
|
|
215
|
+
<Card title="Desktop setup in full" icon="display" href="/desktop-apps">
|
|
216
|
+
Electron and Tauri wiring, including the Tauri CSP whose failure is silent.
|
|
217
|
+
</Card>
|
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Integrating Reticle
|
|
3
|
+
description: 'Adopt Reticle on your own codebase, or embed it inside an AI app-builder’s generation pipeline.'
|
|
4
|
+
icon: plug
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
> The one guide for adopting Reticle
|
|
7
|
+
> The one guide for adopting Reticle, both for a team using a coding agent on its own app and for an AI app-builder platform (Lovable / Emergent / Bolt) embedding Reticle in its generation pipeline. Reticle reads the program from _inside_ a running app and returns a **verdict with evidence** ("did it actually work?"), not a screenshot. Enterprise/premium access lives in [`enterprise.md`](./enterprise.md).
|
|
4
8
|
|
|
5
9
|
## The loop
|
|
6
10
|
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
```mermaid
|
|
12
|
+
flowchart TB
|
|
13
|
+
G["generate / edit"] --> B["boot the preview"]
|
|
14
|
+
B --> V["Reticle verifies<br/>the critical flows"]
|
|
15
|
+
V --> R{"verdict + evidence"}
|
|
16
|
+
R -- "PASS" --> S["ship, and attach<br/>the verified marker"]
|
|
17
|
+
R -- "FAIL" --> F["gate the deploy<br/>feed repair packets<br/>to the fixer agent"]
|
|
18
|
+
F --> G
|
|
12
19
|
```
|
|
13
20
|
|
|
14
|
-
One call replays the app's key journeys and asserts **program truth**
|
|
21
|
+
One call replays the app's key journeys and asserts **program truth** (network cardinality, store/state, emitted signals, console), then returns a deterministic, un-hallucinatable verdict.
|
|
15
22
|
|
|
16
23
|
---
|
|
17
24
|
|
|
@@ -28,7 +35,7 @@ Paste to your agent (Claude Code / Cursor / any MCP agent): `Follow https://raw.
|
|
|
28
35
|
### B. A platform / CI, driven from your pipeline (no MCP, no human)
|
|
29
36
|
|
|
30
37
|
```bash
|
|
31
|
-
|
|
38
|
+
npx @reticlehq/server serve --http --http-token "$TOKEN" --drive "$PREVIEW_URL" # localhost:7331
|
|
32
39
|
```
|
|
33
40
|
|
|
34
41
|
```js
|
|
@@ -49,21 +56,21 @@ if (run.verdict.status !== 'pass') {
|
|
|
49
56
|
} else attachToDeploy(run); // "verified ✓"; set profile:"prod-preview" to redact internals downstream
|
|
50
57
|
```
|
|
51
58
|
|
|
52
|
-
Or skip the HTTP server entirely with the one-shot CLI
|
|
59
|
+
Or skip the HTTP server entirely with the one-shot CLI: `reticle verify <preview-url>` drives the preview, replays the saved flows, prints the verdict, and exits non-zero on fail (ideal for a CI step).
|
|
53
60
|
|
|
54
61
|
---
|
|
55
62
|
|
|
56
|
-
## In-app SDK integration
|
|
63
|
+
## In-app SDK integration: the effort, by layer
|
|
57
64
|
|
|
58
65
|
Reticle embeds a **dev/preview-only** SDK (`@reticlehq/browser`, Apache-2.0, tree-shaken from production). For a platform you add this **once to your generated-app template** → every generated app is verifiable.
|
|
59
66
|
|
|
60
67
|
| Layer | What you add | Unlocks | Effort |
|
|
61
68
|
| --- | --- | --- | --- |
|
|
62
|
-
| **1
|
|
63
|
-
| **2
|
|
64
|
-
| **3
|
|
69
|
+
| **1: drive + DOM/network/console** | 1 build-plugin line + ~10-line dev-only `reticle.connect({…})` file (`npx @reticlehq/server init` does it) | broken routes, network status/cardinality (double-submit), console errors, persistence-after-reload | **Easy** (~15 min) |
|
|
70
|
+
| **2: program-state truth** | `registerStore('app', store)` (1/store, pass the store itself so mutations emit diffs) + `reticle.signal('order:saved', …)` (1/consequence) + `data-testid`s | UI-vs-store desync, dead handlers, blast-radius, source mapping | **Easy to Medium** (an afternoon, once) |
|
|
71
|
+
| **3: governance (optional)** | `registerCapabilities(...)` (signals/stores/risk zones) + recorded flows with success oracles | risk policy + sharper verdicts | **Medium**, optional |
|
|
65
72
|
|
|
66
|
-
Copyable patterns: `apps/bench-app/src/reticle-dev.ts`, `apps/next-smoke/app/reticle-dev.tsx`. Without instrumentation, Layer-1 checks still work via the driven browser; Layers 2
|
|
73
|
+
Copyable patterns: `apps/bench-app/src/reticle-dev.ts`, `apps/next-smoke/app/reticle-dev.tsx`. Without instrumentation, Layer-1 checks still work via the driven browser; Layers 2 and 3 are what no out-of-page tool can see.
|
|
67
74
|
|
|
68
75
|
---
|
|
69
76
|
|
|
@@ -71,14 +78,14 @@ Copyable patterns: `apps/bench-app/src/reticle-dev.ts`, `apps/next-smoke/app/ret
|
|
|
71
78
|
|
|
72
79
|
| Silent failure (a generated app ships it) | How Reticle catches it |
|
|
73
80
|
| --- | --- |
|
|
74
|
-
| Mock data
|
|
75
|
-
| Dead handler
|
|
76
|
-
| Double-submit
|
|
77
|
-
| Forbidden call
|
|
78
|
-
| Missing validation
|
|
79
|
-
| Silent console error
|
|
80
|
-
| UI-vs-store desync
|
|
81
|
-
| Blast-radius
|
|
81
|
+
| Mock data (POST 200, row shows, nothing persists) | persistence/`state` oracle (doesn't survive reload) |
|
|
82
|
+
| Dead handler (looks done, store never changed) | `state` desync |
|
|
83
|
+
| Double-submit (one click, two POSTs) | `net { count: 1 }` |
|
|
84
|
+
| Forbidden call (a must-never-fire endpoint fired) | `net { count: 0 }` |
|
|
85
|
+
| Missing validation (`"abc"` becomes data) | flow oracle (error shown AND nothing created) |
|
|
86
|
+
| Silent console error (logged, UI still renders) | `console { absent: true }` |
|
|
87
|
+
| UI-vs-store desync (the total lies) | reads the store, contradicts the display |
|
|
88
|
+
| Blast-radius (an action corrupts unrelated state) | `state { hold:true }` invariant |
|
|
82
89
|
|
|
83
90
|
Live, clickable demo of each: `apps/vibe-builder-demo/` (set `BUG_MODE=…`). Proven in CI: `packages/server/src/runs/generated-app-bugs.test.ts`.
|
|
84
91
|
|
|
@@ -97,8 +104,8 @@ The shape is identical (in-app SDK in the template → verify in the sandbox →
|
|
|
97
104
|
|
|
98
105
|
### Lovable (Vite/React generated apps, hosted preview)
|
|
99
106
|
|
|
100
|
-
1. Add the Reticle Vite plugin + dev-only `reticle.connect` to the project template (Lovable already templates Vite/React
|
|
101
|
-
2. Run `
|
|
107
|
+
1. Add the Reticle Vite plugin + dev-only `reticle.connect` to the project template (Lovable already templates Vite/React, so it's one plugin line + the connect file).
|
|
108
|
+
2. Run `npx @reticlehq/server serve --http --drive <preview-url>` against the preview build in the generation worker.
|
|
102
109
|
3. Call `/verify` after each generate/edit; gate the "your app is ready" signal on `verdict.status === 'pass'`; feed repair packets back into the edit agent.
|
|
103
110
|
|
|
104
111
|
### Bolt.new / StackBlitz (WebContainer, in-browser runtime)
|
|
@@ -115,7 +122,7 @@ The shape is identical (in-app SDK in the template → verify in the sandbox →
|
|
|
115
122
|
|
|
116
123
|
`POST /verify` (and `reticle_run_export`) return a stable, versioned `ReticleVerificationRun` (defined in `@reticlehq/core`): `verdict` (pass/fail/partial, confidence, blockingRisks), `flows[]`, `checks[]`, `risks[]` (auth/payment/db/…), `repair.failurePackets[]` (what + where to fix), `evidence`. Render a legible report with `renderRunReport()` or `reticle_run_export { format: "report" }`. Profiles: `dev` (full) vs `prod-preview` (source + state redacted for downstream sharing).
|
|
117
124
|
|
|
118
|
-
**Why trust it:** the verdict is mechanical
|
|
125
|
+
**Why trust it:** the verdict is mechanical, derived only from observed outcomes, so it can't report green for something it never ran (a severed backend reads as _fail_, never a confident pass). Proof: `packages/server/src/runs/false-green.test.ts`.
|
|
119
126
|
|
|
120
127
|
## Licensing for embedding
|
|
121
128
|
|