@reticlehq/server 2.7.0 → 2.9.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 +130 -778
- package/dist/bridge/bridge.d.ts +23 -0
- package/dist/bridge/bridge.js +80 -2
- package/dist/bridge/bridge.test-harness.d.ts +5 -0
- package/dist/bridge/bridge.test-harness.js +6 -4
- package/dist/capsule/causal-summary.d.ts +36 -1
- package/dist/capsule/causal-summary.js +33 -7
- package/dist/capsule/divergence.js +7 -1
- package/dist/cli/chromium-hint.d.ts +33 -5
- package/dist/cli/chromium-hint.js +82 -12
- package/dist/cli/cli-doctor.js +65 -34
- package/dist/cli/cli-flow-commands.js +8 -8
- package/dist/cli/cli-kill.d.ts +58 -0
- package/dist/cli/cli-kill.js +162 -0
- package/dist/cli/cli-parse.d.ts +16 -1
- package/dist/cli/cli-parse.js +32 -9
- package/dist/cli/cli-port.d.ts +32 -0
- package/dist/cli/cli-port.js +82 -35
- package/dist/cli/cli-verify.d.ts +21 -0
- package/dist/cli/cli-verify.js +47 -3
- package/dist/cli/config-discovery.d.ts +35 -0
- package/dist/cli/config-discovery.js +163 -0
- package/dist/cli/doctor-daemon-line.js +4 -1
- package/dist/cli/doctor-project-line.d.ts +37 -0
- package/dist/cli/doctor-project-line.js +35 -0
- package/dist/cli/doctor-rows.d.ts +51 -0
- package/dist/cli/doctor-rows.js +54 -0
- package/dist/cli/doctor-sessions-line.d.ts +12 -0
- package/dist/cli/doctor-sessions-line.js +69 -0
- package/dist/cli/drive-attach.d.ts +99 -0
- package/dist/cli/drive-attach.js +203 -0
- package/dist/cli/drive-command.d.ts +13 -0
- package/dist/cli/drive-command.js +103 -0
- package/dist/cli/open-note.d.ts +20 -0
- package/dist/cli/open-note.js +37 -0
- package/dist/cli/port-holder.d.ts +10 -0
- package/dist/cli/port-holder.js +26 -2
- package/dist/cli/status-next-action.d.ts +37 -0
- package/dist/cli/status-next-action.js +61 -0
- package/dist/cli.js +119 -39
- 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/events/contradictions.d.ts +46 -0
- package/dist/events/contradictions.js +35 -22
- package/dist/events/declared.d.ts +45 -0
- package/dist/events/declared.js +73 -0
- package/dist/events/echo-mismatch.d.ts +8 -2
- package/dist/events/echo-mismatch.js +33 -5
- package/dist/events/event-filters.d.ts +1 -1
- package/dist/events/event-filters.js +7 -4
- package/dist/events/predicate-eval.d.ts +17 -72
- package/dist/events/predicate-eval.js +117 -236
- package/dist/events/predicate-parse.js +31 -3
- package/dist/events/predicate-route.d.ts +32 -0
- package/dist/events/predicate-route.js +102 -0
- package/dist/events/predicate-schema.d.ts +99 -0
- package/dist/events/predicate-schema.js +309 -0
- package/dist/events/predicate.d.ts +8 -0
- package/dist/events/predicate.js +296 -22
- package/dist/flows/annotate-tools.js +3 -0
- package/dist/flows/annotate.js +18 -4
- package/dist/flows/coverage.d.ts +38 -5
- package/dist/flows/coverage.js +30 -4
- package/dist/flows/decision.d.ts +15 -0
- package/dist/flows/decision.js +1 -1
- package/dist/flows/flow-classify.js +1 -1
- package/dist/flows/flow-replay-run.js +8 -1
- package/dist/flows/flow-tools.js +21 -5
- package/dist/flows/heal-run.js +1 -1
- package/dist/flows/replay.js +3 -0
- package/dist/honesty/accepted-write.d.ts +3 -0
- package/dist/honesty/accepted-write.js +8 -3
- package/dist/honesty/already-true.js +15 -0
- package/dist/honesty/blind-spots.js +10 -2
- package/dist/honesty/honesty.d.ts +11 -0
- package/dist/honesty/honesty.js +1 -0
- package/dist/honesty/uncaptured-bodies.d.ts +4 -2
- package/dist/honesty/uncaptured-bodies.js +9 -2
- package/dist/honesty/unread-outcome.d.ts +8 -0
- package/dist/honesty/unread-outcome.js +36 -9
- package/dist/honesty/unsettled.d.ts +49 -0
- package/dist/honesty/unsettled.js +82 -0
- package/dist/honesty/verified.d.ts +36 -3
- package/dist/honesty/verified.js +54 -11
- package/dist/http-server.d.ts +19 -0
- package/dist/http-server.js +202 -4
- package/dist/impact/impact-recorder.d.ts +23 -0
- package/dist/impact/impact-recorder.js +76 -0
- package/dist/impact/impact-store.d.ts +47 -0
- package/dist/impact/impact-store.js +171 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +47 -10
- package/dist/init/agent-rules.d.ts +36 -0
- package/dist/init/agent-rules.js +132 -22
- package/dist/init/astro-patch.d.ts +2 -1
- package/dist/init/astro-patch.js +38 -6
- package/dist/init/capabilities.d.ts +22 -0
- package/dist/init/capabilities.js +76 -0
- package/dist/init/confirm.d.ts +114 -0
- package/dist/init/confirm.js +165 -0
- package/dist/init/connect-steps.d.ts +2 -0
- package/dist/init/connect-steps.js +50 -0
- package/dist/init/cra.js +3 -2
- package/dist/init/csp-check.d.ts +38 -0
- package/dist/init/csp-check.js +92 -0
- package/dist/init/csp-doctor.d.ts +25 -0
- package/dist/init/csp-doctor.js +49 -0
- package/dist/init/cursor.js +6 -9
- package/dist/init/detect.d.ts +8 -0
- package/dist/init/detect.js +33 -3
- package/dist/init/mcp-clients.d.ts +1 -0
- package/dist/init/mcp-clients.js +51 -7
- package/dist/init/mcp.d.ts +15 -0
- package/dist/init/mcp.js +19 -0
- package/dist/init/non-js-project.js +29 -5
- package/dist/init/plan-framework.d.ts +10 -0
- package/dist/init/plan-framework.js +78 -16
- package/dist/init/plan.d.ts +47 -6
- package/dist/init/plan.js +228 -69
- package/dist/init/run.d.ts +24 -2
- package/dist/init/run.js +157 -45
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/slash-command.js +54 -6
- package/dist/init/snippets.d.ts +45 -5
- package/dist/init/snippets.js +194 -42
- package/dist/init/vite-config.js +53 -5
- package/dist/input/network-mock-tools.d.ts +19 -0
- package/dist/input/network-mock-tools.js +37 -5
- package/dist/input/scroll-find.d.ts +6 -0
- package/dist/input/scroll-find.js +17 -2
- package/dist/input/scroll-tools.js +4 -0
- package/dist/journal/attach-journal.d.ts +9 -1
- package/dist/journal/attach-journal.js +13 -2
- package/dist/journal/journal-query.d.ts +13 -0
- package/dist/journal/journal-query.js +28 -3
- package/dist/journal/workspace-gitignore.d.ts +10 -0
- package/dist/journal/workspace-gitignore.js +59 -0
- package/dist/license/license.js +1 -1
- package/dist/mcp/mcp-proxy.d.ts +35 -30
- package/dist/mcp/mcp-proxy.js +306 -104
- package/dist/mcp/mcp.d.ts +10 -1
- package/dist/mcp/mcp.js +70 -34
- package/dist/mcp/proxy-handshake.d.ts +82 -1
- package/dist/mcp/proxy-handshake.js +109 -2
- package/dist/mcp/proxy-lifecycle.d.ts +11 -2
- package/dist/mcp/proxy-lifecycle.js +12 -3
- package/dist/mcp/proxy-log.d.ts +63 -0
- package/dist/mcp/proxy-log.js +132 -0
- package/dist/mcp/server-instructions.d.ts +17 -0
- package/dist/mcp/server-instructions.js +56 -0
- package/dist/mcp/shared-params.d.ts +27 -0
- package/dist/mcp/shared-params.js +36 -0
- package/dist/pool/browser-pool.d.ts +22 -2
- package/dist/pool/browser-pool.js +44 -4
- 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/project/reticle-dir.d.ts +2 -0
- package/dist/project/reticle-dir.js +1 -0
- package/dist/session/connection-memory.d.ts +59 -0
- package/dist/session/connection-memory.js +108 -0
- package/dist/session/dev-command.d.ts +26 -0
- package/dist/session/dev-command.js +89 -0
- package/dist/session/dev-server-probe.d.ts +68 -0
- package/dist/session/dev-server-probe.js +102 -7
- package/dist/session/last-act.d.ts +26 -0
- package/dist/session/last-act.js +35 -1
- package/dist/session/live-control-tools.js +42 -5
- package/dist/session/no-session-diagnosis.d.ts +67 -2
- package/dist/session/no-session-diagnosis.js +236 -27
- package/dist/session/no-session-next-action.d.ts +46 -0
- package/dist/session/no-session-next-action.js +85 -0
- package/dist/session/no-session-watch.d.ts +40 -1
- package/dist/session/no-session-watch.js +174 -8
- package/dist/session/pending-commands.d.ts +12 -0
- package/dist/session/pending-commands.js +13 -1
- package/dist/session/ref-provenance.d.ts +54 -0
- package/dist/session/ref-provenance.js +61 -0
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +4 -0
- package/dist/session/session-manager.js +35 -5
- package/dist/session/session.d.ts +10 -1
- package/dist/session/session.js +146 -5
- package/dist/telemetry/app-instrumented.d.ts +8 -0
- package/dist/telemetry/app-instrumented.js +10 -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 +15 -17
- package/dist/telemetry/bug-found.js +52 -21
- package/dist/telemetry/cli-telemetry.js +7 -1
- package/dist/telemetry/daemon-telemetry.js +24 -0
- package/dist/telemetry/feedback.js +1 -1
- package/dist/telemetry/init-telemetry.js +9 -1
- package/dist/telemetry/install-source.d.ts +56 -0
- package/dist/telemetry/install-source.js +74 -0
- package/dist/telemetry/instrumentation-stall.d.ts +50 -0
- package/dist/telemetry/instrumentation-stall.js +90 -0
- package/dist/telemetry/run-telemetry.js +4 -1
- package/dist/telemetry/session-metrics.d.ts +8 -0
- package/dist/telemetry/session-metrics.js +15 -0
- package/dist/telemetry/telemetry.d.ts +7 -1
- package/dist/telemetry/telemetry.js +26 -2
- package/dist/telemetry/tool-refused.d.ts +19 -0
- package/dist/telemetry/tool-refused.js +55 -0
- package/dist/telemetry/verification-of.js +46 -7
- package/dist/temp-dir.d.ts +27 -0
- package/dist/temp-dir.js +46 -0
- package/dist/tools/act-tools.js +209 -28
- package/dist/tools/assert-grade.d.ts +1 -1
- package/dist/tools/assert-grade.js +27 -5
- package/dist/tools/assert-verdict.js +49 -7
- package/dist/tools/browser-tools.js +10 -0
- package/dist/tools/contract-tools.js +4 -0
- package/dist/tools/dynamic-tools.js +16 -7
- package/dist/tools/error-recovery.d.ts +10 -0
- package/dist/tools/error-recovery.js +73 -9
- package/dist/tools/feedback-invite.js +1 -1
- package/dist/tools/invoke-tool.js +159 -26
- package/dist/tools/lease-hint.d.ts +30 -8
- package/dist/tools/lease-hint.js +65 -14
- package/dist/tools/lease-tools.d.ts +40 -1
- package/dist/tools/lease-tools.js +181 -83
- package/dist/tools/navigate-result.js +1 -1
- package/dist/tools/observe-tools.js +10 -4
- package/dist/tools/observed-nothing.js +1 -1
- package/dist/tools/react-grace.d.ts +52 -0
- package/dist/tools/react-grace.js +90 -0
- package/dist/tools/read-tools.js +67 -5
- package/dist/tools/resolve-target.d.ts +33 -0
- package/dist/tools/resolve-target.js +53 -0
- package/dist/tools/settle-in-flight.d.ts +94 -0
- package/dist/tools/settle-in-flight.js +146 -0
- package/dist/tools/snapshot-delta.d.ts +10 -0
- package/dist/tools/snapshot-delta.js +194 -9
- package/dist/tools/tool-kit.d.ts +8 -0
- package/dist/tools/tool-surface.d.ts +60 -0
- package/dist/tools/tool-surface.js +86 -1
- package/dist/tools/tools.js +61 -8
- package/dist/tools/unadvertised-help.js +9 -6
- package/docs/README.md +55 -11
- package/docs/actions.mdx +120 -0
- package/docs/agent-cheatsheet.md +95 -29
- package/docs/architecture.md +52 -35
- package/docs/benchmarks.md +127 -75
- package/docs/best-practices.mdx +216 -0
- package/docs/capabilities.mdx +534 -0
- package/docs/cli/affected.mdx +53 -0
- package/docs/cli/capsules.mdx +70 -0
- package/docs/cli/cloud.mdx +153 -0
- package/docs/cli/doctor.mdx +109 -0
- package/docs/cli/drive.mdx +74 -0
- package/docs/cli/feedback.mdx +67 -0
- package/docs/cli/gate.mdx +64 -0
- package/docs/cli/help.mdx +94 -0
- package/docs/cli/hunt.mdx +73 -0
- package/docs/cli/identify.mdx +76 -0
- package/docs/cli/init.mdx +121 -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 +74 -0
- package/docs/cli/restart.mdx +60 -0
- package/docs/cli/rollback.mdx +35 -0
- package/docs/cli/serve.mdx +69 -0
- package/docs/cli/status.mdx +88 -0
- package/docs/cli/stop.mdx +64 -0
- package/docs/cli/telemetry.mdx +57 -0
- package/docs/cli/update.mdx +52 -0
- package/docs/cli/verify.mdx +76 -0
- package/docs/cli/version.mdx +39 -0
- package/docs/cli/watch.mdx +62 -0
- package/docs/cli.mdx +241 -0
- package/docs/debugging.md +16 -10
- package/docs/deploy-checks.md +45 -12
- package/docs/desktop.mdx +384 -0
- package/docs/docs.json +364 -0
- package/docs/enterprise.md +22 -16
- package/docs/faq.mdx +102 -0
- package/docs/fixtures.md +13 -7
- package/docs/flows.md +29 -23
- package/docs/for-agents.md +71 -0
- package/docs/frameworks.mdx +238 -0
- package/docs/gate-plan.md +48 -42
- package/docs/gates.md +28 -22
- package/docs/getting-started.md +129 -77
- package/docs/human-control.md +24 -18
- package/docs/index.mdx +143 -0
- package/docs/install-agentic.mdx +224 -0
- package/docs/install-manual.mdx +196 -0
- package/docs/instrumentation.mdx +238 -0
- package/docs/integration-patterns.md +41 -35
- package/docs/local-registry.md +19 -13
- package/docs/multi-agent-testing.md +19 -13
- package/docs/packages/babel-plugin.mdx +79 -0
- package/docs/packages/browser.mdx +111 -0
- package/docs/packages/core.mdx +116 -0
- package/docs/packages/electron.mdx +85 -0
- package/docs/packages/eslint-plugin.mdx +86 -0
- package/docs/packages/next.mdx +89 -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 +119 -0
- package/docs/packages/vite-plugin.mdx +94 -0
- package/docs/packages.mdx +218 -0
- package/docs/platform-integration.md +35 -26
- package/docs/predicates.mdx +248 -0
- package/docs/quickstart.mdx +257 -0
- package/docs/recipes.mdx +282 -0
- package/docs/skill-file.mdx +107 -0
- package/docs/state-management.mdx +199 -0
- package/docs/style.css +14 -0
- package/docs/system-map.md +50 -33
- package/docs/telemetry-contract.md +106 -52
- package/docs/telemetry-events.mdx +475 -0
- package/docs/telemetry.md +63 -55
- package/docs/testing.md +44 -6
- package/docs/token-efficiency.md +37 -27
- package/docs/tools-act-and-wait.mdx +173 -0
- package/docs/tools-act-sequence.mdx +137 -0
- package/docs/tools-act.mdx +95 -0
- package/docs/tools-assert.mdx +129 -0
- package/docs/tools-console.mdx +63 -0
- package/docs/tools-inspect.mdx +88 -0
- package/docs/tools-navigate.mdx +99 -0
- package/docs/tools-network.mdx +119 -0
- package/docs/tools-observe.mdx +145 -0
- package/docs/tools-overview.mdx +160 -0
- package/docs/tools-query.mdx +126 -0
- package/docs/tools-session-and-feedback.mdx +112 -0
- package/docs/tools-sessions.mdx +94 -0
- package/docs/tools-snapshot.mdx +129 -0
- package/docs/tools-state.mdx +104 -0
- package/docs/tools-tools-and-run.mdx +156 -0
- package/docs/tools-wait-for.mdx +85 -0
- package/docs/troubleshooting.mdx +263 -0
- package/docs/usage.md +209 -165
- package/docs/vs-chrome-devtools-mcp.mdx +123 -0
- package/docs/vs-playwright-mcp.mdx +125 -0
- package/docs/vs-screenshots.mdx +137 -0
- package/docs/what-is-reticle.mdx +63 -0
- package/docs/why-reticle.mdx +109 -0
- package/package.json +5 -5
- package/docs/desktop-apps.md +0 -256
- 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
|
@@ -1,17 +1,26 @@
|
|
|
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
|
-
|
|
7
|
+
Two ways in. **A team** runs `npx @reticlehq/server init` once and points its coding agent at the MCP server. **A platform** adds the dev-only SDK to its generated-app template, runs `npx @reticlehq/server serve --http --drive <preview-url>` beside each preview, and `POST`s to `/verify` after every generate or edit, gating the ship on `run.verdict.status === 'pass'` and feeding `run.repair.failurePackets[]` back to a fixer agent.
|
|
8
|
+
|
|
9
|
+
> 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
10
|
|
|
5
11
|
## The loop
|
|
6
12
|
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
```mermaid
|
|
14
|
+
flowchart TB
|
|
15
|
+
G["generate / edit"] --> B["boot the preview"]
|
|
16
|
+
B --> V["Reticle verifies<br/>the critical flows"]
|
|
17
|
+
V --> R{"verdict + evidence"}
|
|
18
|
+
R -- "PASS" --> S["ship, and attach<br/>the verified marker"]
|
|
19
|
+
R -- "FAIL" --> F["gate the deploy<br/>feed repair packets<br/>to the fixer agent"]
|
|
20
|
+
F --> G
|
|
12
21
|
```
|
|
13
22
|
|
|
14
|
-
One call replays the app's key journeys and asserts **program truth**
|
|
23
|
+
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
24
|
|
|
16
25
|
---
|
|
17
26
|
|
|
@@ -28,7 +37,7 @@ Paste to your agent (Claude Code / Cursor / any MCP agent): `Follow https://raw.
|
|
|
28
37
|
### B. A platform / CI, driven from your pipeline (no MCP, no human)
|
|
29
38
|
|
|
30
39
|
```bash
|
|
31
|
-
|
|
40
|
+
npx @reticlehq/server serve --http --http-token "$TOKEN" --drive "$PREVIEW_URL" # localhost:7331
|
|
32
41
|
```
|
|
33
42
|
|
|
34
43
|
```js
|
|
@@ -49,21 +58,21 @@ if (run.verdict.status !== 'pass') {
|
|
|
49
58
|
} else attachToDeploy(run); // "verified ✓"; set profile:"prod-preview" to redact internals downstream
|
|
50
59
|
```
|
|
51
60
|
|
|
52
|
-
Or skip the HTTP server entirely with the one-shot CLI
|
|
61
|
+
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
62
|
|
|
54
63
|
---
|
|
55
64
|
|
|
56
|
-
## In-app SDK integration
|
|
65
|
+
## In-app SDK integration: the effort, by layer
|
|
57
66
|
|
|
58
67
|
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
68
|
|
|
60
69
|
| Layer | What you add | Unlocks | Effort |
|
|
61
70
|
| --- | --- | --- | --- |
|
|
62
|
-
| **1
|
|
63
|
-
| **2
|
|
64
|
-
| **3
|
|
71
|
+
| **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) |
|
|
72
|
+
| **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) |
|
|
73
|
+
| **3: governance (optional)** | `registerCapabilities(...)` (signals/stores/risk zones) + recorded flows with success oracles | risk policy + sharper verdicts | **Medium**, optional |
|
|
65
74
|
|
|
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
|
|
75
|
+
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
76
|
|
|
68
77
|
---
|
|
69
78
|
|
|
@@ -71,14 +80,14 @@ Copyable patterns: `apps/bench-app/src/reticle-dev.ts`, `apps/next-smoke/app/ret
|
|
|
71
80
|
|
|
72
81
|
| Silent failure (a generated app ships it) | How Reticle catches it |
|
|
73
82
|
| --- | --- |
|
|
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
|
|
83
|
+
| Mock data (POST 200, row shows, nothing persists) | persistence/`state` oracle (doesn't survive reload) |
|
|
84
|
+
| Dead handler (looks done, store never changed) | `state` desync |
|
|
85
|
+
| Double-submit (one click, two POSTs) | `net { count: 1 }` |
|
|
86
|
+
| Forbidden call (a must-never-fire endpoint fired) | `net { count: 0 }` |
|
|
87
|
+
| Missing validation (`"abc"` becomes data) | flow oracle (error shown AND nothing created) |
|
|
88
|
+
| Silent console error (logged, UI still renders) | `console { absent: true }` |
|
|
89
|
+
| UI-vs-store desync (the total lies) | reads the store, contradicts the display |
|
|
90
|
+
| Blast-radius (an action corrupts unrelated state) | `state { hold:true }` invariant |
|
|
82
91
|
|
|
83
92
|
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
93
|
|
|
@@ -97,8 +106,8 @@ The shape is identical (in-app SDK in the template → verify in the sandbox →
|
|
|
97
106
|
|
|
98
107
|
### Lovable (Vite/React generated apps, hosted preview)
|
|
99
108
|
|
|
100
|
-
1. Add the Reticle Vite plugin + dev-only `reticle.connect` to the project template (Lovable already templates Vite/React
|
|
101
|
-
2. Run `
|
|
109
|
+
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).
|
|
110
|
+
2. Run `npx @reticlehq/server serve --http --drive <preview-url>` against the preview build in the generation worker.
|
|
102
111
|
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
112
|
|
|
104
113
|
### Bolt.new / StackBlitz (WebContainer, in-browser runtime)
|
|
@@ -115,7 +124,7 @@ The shape is identical (in-app SDK in the template → verify in the sandbox →
|
|
|
115
124
|
|
|
116
125
|
`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
126
|
|
|
118
|
-
**Why trust it:** the verdict is mechanical
|
|
127
|
+
**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
128
|
|
|
120
129
|
## Licensing for embedding
|
|
121
130
|
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'Predicate reference: the grammar of green'
|
|
3
|
+
sidebarTitle: Predicates
|
|
4
|
+
description: The grammar for declaring what should be true. Every kind, every field, and which ones actually prove something.
|
|
5
|
+
icon: filter
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
A predicate declares what should be true, as `{ kind, ...fields }`. Reticle has twelve kinds: `signal`, `state`, `net`, `route`, `element`, `text`, `console`, `animation`, `settled`, and the combinators `allOf`, `anyOf` and `not`. Which one you pick decides whether a green means anything.
|
|
9
|
+
|
|
10
|
+
It is the argument to `until` on [`reticle_act_and_wait`](/tools-act-and-wait), and to `predicate` on [`reticle_assert`](/tools-assert) and [`reticle_wait_for`](/tools-wait-for).
|
|
11
|
+
|
|
12
|
+
Every predicate is `{ kind, ...fields }`.
|
|
13
|
+
|
|
14
|
+
## Not all predicates prove the same thing
|
|
15
|
+
|
|
16
|
+
Reticle grades them, and it will tell you when you have chosen a weak one. This is a real response from a `text` assertion:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"verified": "yes",
|
|
21
|
+
"pass": true,
|
|
22
|
+
"advice": "This predicate only checks element/text presence, not an observable consequence. A locator healed to the wrong element (or a stale render) can satisfy it while the feature is broken. Prefer a { signal } or { net } assertion (or allOf it with one) so green means the feature actually worked."
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Take the advice. `element` and `text` are convenient and weak; `signal`, `state` and `net` are what make a green trustworthy.
|
|
27
|
+
|
|
28
|
+
| Kind | Grade | Proves |
|
|
29
|
+
| ------------------ | --------- | ------------------------------------------------------ |
|
|
30
|
+
| `signal` | strongest | The app declared success in its own words |
|
|
31
|
+
| `state` | strong | A registered store actually changed |
|
|
32
|
+
| `net` | strong | A specific request fired, with a countable cardinality |
|
|
33
|
+
| `route` | medium | Navigation happened |
|
|
34
|
+
| `console` | medium | Something logged, or nothing did |
|
|
35
|
+
| `element` / `text` | weak | Something rendered. A mock renders too |
|
|
36
|
+
| `animation` | weak | An animation ran or finished |
|
|
37
|
+
| `settled` | timing | The page went quiet. A precondition, not a proof |
|
|
38
|
+
|
|
39
|
+
## The leaves
|
|
40
|
+
|
|
41
|
+
### element
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"kind": "element",
|
|
46
|
+
"query": { "role": "dialog", "name": "Confirm" },
|
|
47
|
+
"state": "visible"
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`query` accepts `role`, `name`, `text`, `label`, `placeholder`, `testid`, `alt`, `scope`. `state` is one of `visible`, `hidden`, `enabled`, `disabled`, `checked`, `expanded`, `focused`, `present`.
|
|
52
|
+
|
|
53
|
+
Add `"absent": true` to assert something is **gone**. The predicate for a removal, a dismissed toast, or a regression check.
|
|
54
|
+
|
|
55
|
+
### text
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{ "kind": "text", "contains": "Saved successfully", "visible": true }
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`contains` is the field the published schema declares. `value` and `text` are accepted as aliases and rewritten to it. `absent: true` asserts the text is not present, and `visible: true` requires it to be on screen rather than merely in the DOM.
|
|
62
|
+
|
|
63
|
+
### net
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{ "kind": "net", "method": "POST", "urlContains": "/api/order", "status": 200 }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The one that catches the expensive bug:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{ "kind": "net", "method": "POST", "urlContains": "/api/order", "count": 1 }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Exactly one.** Double-submit fails at two, and nothing on screen would have told you.
|
|
76
|
+
|
|
77
|
+
### route
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{ "kind": "route", "pathname": "/success" }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`pathname` matches the path exactly. `contains` matches anywhere in the whole route, path, query and fragment together. `path` is accepted as an alias for `pathname`, and `url` and `urlContains` as aliases for `contains`, but the canonical spellings are the ones above.
|
|
84
|
+
|
|
85
|
+
A passing route verdict quotes the transition itself:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
"evidence": {
|
|
89
|
+
"from": "http://localhost:4310/",
|
|
90
|
+
"to": "http://localhost:4310/deployments",
|
|
91
|
+
"pathname": "/deployments",
|
|
92
|
+
"search": "",
|
|
93
|
+
"hash": ""
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
<Warning>
|
|
98
|
+
`route` asserts the route **changed**. Asserting the route you are already on always fails, and
|
|
99
|
+
the response says so: `expected: "a route change to /"`, `assertion: "route.changed"`.
|
|
100
|
+
</Warning>
|
|
101
|
+
|
|
102
|
+
### console
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{ "kind": "console", "level": "error", "absent": true }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
"The flow completed and logged nothing." Worth attaching to most actions. Plenty of features work while quietly throwing.
|
|
109
|
+
|
|
110
|
+
### signal
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{ "kind": "signal", "name": "webhook:received", "dataMatches": { "provider": "stripe", "id": "*" } }
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The app emitted a named signal via `reticle.signal()`. `dataMatches` is shallow JSON matching, and `*` means "present, any value".
|
|
117
|
+
|
|
118
|
+
Nothing outranks this. A `200` proves the server was reachable; a rendered row proves React ran. A signal is the application itself saying the thing succeeded. See [instrumentation](/instrumentation) for how to emit them.
|
|
119
|
+
|
|
120
|
+
### state
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{ "kind": "state", "store": "app", "path": "deployments.0.status", "equals": "live" }
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Walks a dot-path, with numeric array indices. `equals` takes a literal, or an operator pattern:
|
|
127
|
+
|
|
128
|
+
| Operator | Meaning |
|
|
129
|
+
| ------------------------- | ----------------------------- |
|
|
130
|
+
| `$gte` `$lte` `$gt` `$lt` | numeric comparison |
|
|
131
|
+
| `$contains` | substring or array membership |
|
|
132
|
+
| `$length` | collection size |
|
|
133
|
+
|
|
134
|
+
Omit `equals` entirely to assert presence. A real `$length` check against this fixture:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{ "kind": "state", "store": "app", "path": "deployments", "equals": { "$length": 40 } }
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"verified": "yes",
|
|
143
|
+
"because": "assertion held at state grade over a clean capture with no channel disagreeing",
|
|
144
|
+
"pass": true,
|
|
145
|
+
"evidence": { "store": "app", "path": "deployments", "value": ["{…11 keys}", "…"] }
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Note `grade: state` in the `because` line, and that the evidence collapses each row to a size marker rather than returning forty objects.
|
|
150
|
+
|
|
151
|
+
A miss quotes the real value back at you:
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"pass": false,
|
|
156
|
+
"failureReason": "state 'deployments.0.status' is \"queued\", expected \"live\"",
|
|
157
|
+
"observed": "deployments.0.status = \"queued\"",
|
|
158
|
+
"expected": "deployments.0.status = \"live\"",
|
|
159
|
+
"assertion": "state.equals",
|
|
160
|
+
"evidence": { "store": "app", "path": "deployments.0.status", "value": "queued" }
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`store` is optional. Leave it out and the path picks the store: if exactly one registered store exposes it, that one is read. Registering several is normal, so this is the common case rather than a shortcut. Name a store when two of them genuinely expose the same path, which is the only situation Reticle cannot resolve on its own. If no store exposes it, that is a failure rather than a question, and the miss names the stores it searched.
|
|
165
|
+
|
|
166
|
+
This is the predicate that catches a UI-versus-store desync. A deploy that only _looks_ shipped. Deterministically, in one call, with no model involved. On a miss it names the real store value and the keys that were available, so a failure is legible rather than a blind "no".
|
|
167
|
+
|
|
168
|
+
### animation
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{ "kind": "animation", "name": "dialog-in", "completed": true }
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`target` narrows it to one element. `completed: false` matches an animation that is still running.
|
|
175
|
+
|
|
176
|
+
### settled
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{ "kind": "settled", "quietMs": 500 }
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Network and DOM idle. This is the deterministic replacement for a fixed sleep, not a proof of anything: combine it with a real consequence rather than asserting it alone. Omitting `until` on [`reticle_act_and_wait`](/tools-act-and-wait) waits for exactly this.
|
|
183
|
+
|
|
184
|
+
## Combinators
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"kind": "allOf",
|
|
189
|
+
"predicates": [
|
|
190
|
+
{ "kind": "signal", "name": "auth:granted" },
|
|
191
|
+
{ "kind": "net", "method": "POST", "urlContains": "/api/login", "count": 1 },
|
|
192
|
+
{ "kind": "console", "level": "error", "absent": true }
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
{ "kind": "anyOf", "predicates": [ … ] }
|
|
199
|
+
{ "kind": "not", "predicate": { … } }
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
<Warning>
|
|
203
|
+
A combinator needs its own `kind`, and the children go in `predicates`. A bare
|
|
204
|
+
`{ "allOf": [ … ] }` does not parse, and neither does `{ "kind": "allOf", "allOf": [ … ] }`.
|
|
205
|
+
|
|
206
|
+
Reticle refuses the call rather than running half of it. The real refusal names the field, the fix and the consequence: _"predicates: Required; unknown field allOf. Nothing ran, the predicate was not evaluated, so no verdict was produced. allOf accepts: predicates."_ Better to be told than to get a green from an assertion that never executed.
|
|
207
|
+
|
|
208
|
+
</Warning>
|
|
209
|
+
|
|
210
|
+
`allOf` is the workhorse. "The signal fired **and** exactly one request went out **and** the console stayed clean" is a genuinely strong check, and it is one call.
|
|
211
|
+
|
|
212
|
+
Each child reports its own evidence, in order. That is a real verdict for exactly the `allOf` above:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
"evidence": [
|
|
216
|
+
{ "name": "auth:granted", "data": { "email": "admin@reticle.dev" } },
|
|
217
|
+
{ "matched": 1 },
|
|
218
|
+
{ "absent": true }
|
|
219
|
+
]
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
On a failure each child reports separately too, so you can see which one broke:
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
"evidence": [
|
|
226
|
+
{
|
|
227
|
+
"pass": false,
|
|
228
|
+
"expected": "an element matching {\"role\":\"dialog\"} in state 'visible'",
|
|
229
|
+
"observed": "no matching element on the page",
|
|
230
|
+
"assertion": "element.present",
|
|
231
|
+
"evidence": { "presentTestids": ["deploy-table", "filter-search", "env-filter"] }
|
|
232
|
+
},
|
|
233
|
+
{ "pass": true, "evidence": { "absent": true } }
|
|
234
|
+
]
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Timing and scoping
|
|
238
|
+
|
|
239
|
+
| Field | Where | What it does |
|
|
240
|
+
| --- | --- | --- |
|
|
241
|
+
| `timeout_ms` | `assert`, `wait_for`, `act_and_wait` | Wait up to N ms for it to become true |
|
|
242
|
+
| `since` | `net` and `console` leaves, and the call itself | Only consider events after this cursor |
|
|
243
|
+
|
|
244
|
+
The `since` cursor comes from a prior `reticle_act` response. Use it whenever you are asserting something an action was supposed to cause. Without it, an assertion can pass on an event from two clicks ago.
|
|
245
|
+
|
|
246
|
+
<Card title="Where predicates get used" icon="circle-check" href="/tools-act-and-wait">
|
|
247
|
+
Naming the consequence before the action is the whole point.
|
|
248
|
+
</Card>
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'Quickstart: proof in five minutes'
|
|
3
|
+
sidebarTitle: Quickstart
|
|
4
|
+
description: Install Reticle, connect your coding agent, and get a real verdict with evidence in about five minutes.
|
|
5
|
+
icon: rocket
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
**Run `npx @reticlehq/server init` in your project, restart your dev server and your agent, then ask your agent to drive a flow.** That is the whole install. Reticle is a verification layer for AI coding agents: it embeds a dev-only SDK in your running web app so an agent can prove a change works instead of guessing.
|
|
9
|
+
|
|
10
|
+
By the end of this page your agent will click a button in your app and prove what happened: the request that fired, the state that changed, and the file to open if it didn't.
|
|
11
|
+
|
|
12
|
+
<Note>
|
|
13
|
+
Every response on this page is real output, captured against a running app while writing it.
|
|
14
|
+
Nothing here is illustrative.
|
|
15
|
+
</Note>
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
- Node 20 or newer
|
|
20
|
+
- A web app you can run locally, and its dev server
|
|
21
|
+
- A coding agent that speaks MCP (Claude Code, Cursor, Codex, OpenCode, Windsurf, VS Code)
|
|
22
|
+
|
|
23
|
+
## Step 1. Let your agent install it
|
|
24
|
+
|
|
25
|
+
The fastest path is to hand the job to the agent that will use it. In your project directory:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx @reticlehq/server init
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This registers the MCP server, installs the SDK, wires it into your app's entry point, and writes the agent rule files. It is idempotent. Run it twice and the second run reports what was already done rather than doing it again.
|
|
32
|
+
|
|
33
|
+
Prefer to install the skill and let the agent run setup itself? On Claude Code the plugin registers the MCP server and the skill in one step:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
/plugin marketplace add reticlehq/reticle
|
|
37
|
+
/plugin install reticle@reticlehq
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
On Cursor, Codex, Copilot, Gemini and anything else the skills CLI supports:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx skills add reticlehq/reticle
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then type `/reticle` and the agent runs the install from the skill.
|
|
47
|
+
|
|
48
|
+
Useful flags, from `reticle init` itself:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
reticle init [--dry-run] [--port N] [--no-mcp] [--no-install] [--app <dir>]
|
|
52
|
+
--app picks WHICH app in a monorepo, when several are found
|
|
53
|
+
--no-mcp skips MORE than the server registration: also the agent rule files
|
|
54
|
+
(CLAUDE.md / AGENTS.md / .cursor) and the /reticle command, because all three
|
|
55
|
+
only make sense once the tools are reachable.
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Start with `--dry-run` if you want to see the plan before anything is written.
|
|
59
|
+
|
|
60
|
+
<Tip>
|
|
61
|
+
In a monorepo, `init` will find several apps and ask which one you mean. `--app packages/web`
|
|
62
|
+
skips the question.
|
|
63
|
+
</Tip>
|
|
64
|
+
|
|
65
|
+
Prefer to wire it yourself? See [Manual install](/install-manual). It is about ten lines, and worth reading once even if you let the agent do it, because it explains what the agent actually changed.
|
|
66
|
+
|
|
67
|
+
## Step 2. Check the setup
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npx @reticlehq/server doctor
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`doctor` diagnoses the whole setup in one command: Chromium, the daemon, the port. If something is wrong this is the page you want before you start debugging your app instead.
|
|
74
|
+
|
|
75
|
+
## Step 3. Start your app and open it
|
|
76
|
+
|
|
77
|
+
Start your dev server as usual, then:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npx @reticlehq/server open
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
This reuses the tab you already have open, or opens one if you don't. The SDK connects to the local daemon over a WebSocket, and your agent can now see the app.
|
|
84
|
+
|
|
85
|
+
<Warning>
|
|
86
|
+
If the browser console says `[Reticle] this page could not open a websocket to …`, your app and
|
|
87
|
+
the daemon disagree about the port. Set it explicitly:
|
|
88
|
+
`VITE_RETICLE_WS_URL=ws://localhost:4400/reticle` for Vite, or `reticle.connect({url})`. This is
|
|
89
|
+
the single most common setup problem, and the console message names the exact address it tried.
|
|
90
|
+
</Warning>
|
|
91
|
+
|
|
92
|
+
## Step 4. Look at the page
|
|
93
|
+
|
|
94
|
+
Ask your agent to take a snapshot. This is what came back from a real app:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"tree": "- textbox \"Email\" (ref=e3) [value=\"admin@reticle.dev\"]\n- textbox \"Password\" (ref=e4) [value=\"[REDACTED]\"]\n- button \"Sign in\" (ref=e5)",
|
|
99
|
+
"nodes": 3,
|
|
100
|
+
"truncated": false,
|
|
101
|
+
"status": { "route": "/", "title": "Reticle · Mission Control" },
|
|
102
|
+
"cost": { "bytes": 243, "tokens": 61 }
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Three things to notice.
|
|
107
|
+
|
|
108
|
+
The password is already `[REDACTED]`. Reticle redacts credential-shaped values before they leave the page, so your agent's context never holds your test password, let alone your real one.
|
|
109
|
+
|
|
110
|
+
The whole snapshot cost **61 tokens**. This is `mode: "interactive"`, which returns only the controls. A full accessibility-tree dump of a comparable page runs to thousands. Reticle is built to ask narrow questions, because the answer gets re-sent to the model on every turn.
|
|
111
|
+
|
|
112
|
+
Each control has a `ref` like `e5`. That's the handle for the next step, and it stays valid until the element leaves the DOM, so you don't re-snapshot between actions.
|
|
113
|
+
|
|
114
|
+
## Step 5. Act, and prove it
|
|
115
|
+
|
|
116
|
+
Here is the whole idea of Reticle in one call. Your agent names the expected consequence **before** it acts:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"ref": "e5",
|
|
121
|
+
"action": "click",
|
|
122
|
+
"until": { "kind": "net", "method": "POST" }
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Stating the expectation up front is the difference between a check and a rationalisation. An agent that clicks first and then decides what counts as success will always find something that counts.
|
|
127
|
+
|
|
128
|
+
The real response, trimmed for length:
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"verified": "yes",
|
|
133
|
+
"verifiedReason": "proved",
|
|
134
|
+
"because": "assertion held at presence grade over a clean capture with no channel disagreeing",
|
|
135
|
+
"effect": {
|
|
136
|
+
"ok": true,
|
|
137
|
+
"testid": "login-submit",
|
|
138
|
+
"component": "Login",
|
|
139
|
+
"name": "Sign in",
|
|
140
|
+
"effect": { "domMutatedWithin": 7, "appeared": "Signing in…" },
|
|
141
|
+
"source": { "file": "src/components/Login.tsx", "line": 81, "column": 8 }
|
|
142
|
+
},
|
|
143
|
+
"verdict": {
|
|
144
|
+
"pass": true,
|
|
145
|
+
"evidence": {
|
|
146
|
+
"method": "POST",
|
|
147
|
+
"url": "http://localhost:8787/api/login",
|
|
148
|
+
"status": 200,
|
|
149
|
+
"durationMs": 67,
|
|
150
|
+
"requestBody": "{\"email\":\"admin@reticle.dev\",\"password\":\"[REDACTED]\"}",
|
|
151
|
+
"responseBody": "{\"token\":\"[REDACTED]\",\"user\":{\"email\":\"admin@reticle.dev\"}}"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"summary": {
|
|
155
|
+
"net": { "total": 1, "errors": 0 },
|
|
156
|
+
"consoleErrors": 0,
|
|
157
|
+
"stateDiffs": [{ "path": "auth", "from": null, "to": "{\"email\":\"admin@reticle.dev\"}" }],
|
|
158
|
+
"signals": ["auth:granted"]
|
|
159
|
+
},
|
|
160
|
+
"honesty": {
|
|
161
|
+
"grade": "presence",
|
|
162
|
+
"coverage": { "pct": 100, "partial": false },
|
|
163
|
+
"integrity": { "clean": true, "issues": [] }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+

|
|
169
|
+
|
|
170
|
+
Read what that actually proves:
|
|
171
|
+
|
|
172
|
+
| Field | What it tells you |
|
|
173
|
+
| --- | --- |
|
|
174
|
+
| `verified: "yes"` | The named consequence happened. Not "the page looks different", the specific thing. |
|
|
175
|
+
| `source.file` / `line` | The button lives at `src/components/Login.tsx:81`. If this had failed, your agent knows which file to open. |
|
|
176
|
+
| `verdict.evidence` | The exact request: one `POST /api/login`, `200`, 67ms. Not a summary of it. The request. |
|
|
177
|
+
| `stateDiffs` | Application state moved from `null` to a real user. A mock that returns 200 without changing state is caught right here. |
|
|
178
|
+
| `signals` | The app's own `auth:granted` fired. This is the strongest signal available, because the app declared success itself. |
|
|
179
|
+
| `net.total: 1` | Exactly one request. Double-submit bugs show up as `2`. |
|
|
180
|
+
| `integrity.clean` | Nothing interfered with the capture, so the verdict is trustworthy. |
|
|
181
|
+
|
|
182
|
+
## What "verified" can say
|
|
183
|
+
|
|
184
|
+
<CardGroup cols={3}>
|
|
185
|
+
<Card title="yes" icon="circle-check">
|
|
186
|
+
The consequence you named actually happened, and the evidence is attached.
|
|
187
|
+
</Card>
|
|
188
|
+
<Card title="no" icon="circle-xmark">
|
|
189
|
+
It did not happen. This is a finding, with the source pointer to go fix it.
|
|
190
|
+
</Card>
|
|
191
|
+
<Card title="unknown" icon="circle-question">
|
|
192
|
+
Reticle drove the app and could not tell. **This is not a pass.** Report it as unknown.
|
|
193
|
+
</Card>
|
|
194
|
+
</CardGroup>
|
|
195
|
+
|
|
196
|
+
That third state is the one that matters. Most tools have two outcomes and quietly file "I couldn't tell" under "fine". A verification layer that cannot admit uncertainty is just a very confident random number generator.
|
|
197
|
+
|
|
198
|
+
## Frequently asked questions
|
|
199
|
+
|
|
200
|
+
<AccordionGroup>
|
|
201
|
+
<Accordion title="How long does it actually take to install Reticle?">
|
|
202
|
+
One command and two restarts. `npx @reticlehq/server init` registers the MCP server, installs
|
|
203
|
+
the SDK, wires your build config and writes the agent rules. Then restart your dev server, so it
|
|
204
|
+
picks up the new build plugin, and restart your agent, so it re-reads its MCP server list. The
|
|
205
|
+
MCP registration is global, so every later project starts with the tools already there.
|
|
206
|
+
</Accordion>
|
|
207
|
+
|
|
208
|
+
<Accordion title="Do I need to write tests or learn a query syntax?">
|
|
209
|
+
No. You talk to your agent in plain language and it calls the tools. There is no spec file, no
|
|
210
|
+
selector language and no test runner involved in the loop on this page. If you later want the same
|
|
211
|
+
flow to block a bad merge, you can save it and run it in CI, but that is a separate step.
|
|
212
|
+
</Accordion>
|
|
213
|
+
|
|
214
|
+
<Accordion title="Will any of this end up in my production build?">
|
|
215
|
+
No. The SDK is dev-only. The Vite plugin declares `apply: 'serve'`, so it is dropped from `vite
|
|
216
|
+
build` entirely, and `connect()` self-disables when the build reports `NODE_ENV=production`. The
|
|
217
|
+
recommended wiring also guards the call behind `import.meta.env.DEV`, so there are two independent
|
|
218
|
+
locks.
|
|
219
|
+
</Accordion>
|
|
220
|
+
|
|
221
|
+
<Accordion title="Does my code or my app's data leave my machine?">
|
|
222
|
+
No. The daemon runs on your machine and the SDK runs in your browser. No application code, DOM,
|
|
223
|
+
network traffic or console output leaves it. Anonymous product analytics are sent unless you opt
|
|
224
|
+
out with `npx @reticlehq/server telemetry disable`, and they never include your code or your app's
|
|
225
|
+
data.
|
|
226
|
+
</Accordion>
|
|
227
|
+
|
|
228
|
+
<Accordion title="What if my framework is not React or Next.js?">
|
|
229
|
+
The core is framework-neutral, so DOM, network, console, routing and source mapping work anywhere
|
|
230
|
+
JavaScript runs. What varies is how `connect()` gets into your app and whether you get component
|
|
231
|
+
identity on top. See [Frameworks](/frameworks) for the exact wiring per stack and an honest table
|
|
232
|
+
of what is gated in CI versus wired but unverified.
|
|
233
|
+
</Accordion>
|
|
234
|
+
|
|
235
|
+
<Accordion title="Nothing connected. What is the first thing to check?">
|
|
236
|
+
Run `npx @reticlehq/server doctor`. If it shows the daemon running but `sessions` at zero, your
|
|
237
|
+
app is not reaching the bridge, and the usual cause is a port mismatch. The bridge port is not
|
|
238
|
+
your dev-server port. See [Troubleshooting](/troubleshooting).
|
|
239
|
+
</Accordion>
|
|
240
|
+
</AccordionGroup>
|
|
241
|
+
|
|
242
|
+
## Where to go next
|
|
243
|
+
|
|
244
|
+
<CardGroup cols={2}>
|
|
245
|
+
<Card title="Instrument your app" icon="wrench" href="/instrumentation">
|
|
246
|
+
Emit signals like `auth:granted` so your verdicts get stronger than "the DOM changed".
|
|
247
|
+
</Card>
|
|
248
|
+
<Card title="Every tool" icon="list" href="/tools-overview">
|
|
249
|
+
The 18 tools your agent sees by default, and the 30 more it can reach on demand.
|
|
250
|
+
</Card>
|
|
251
|
+
<Card title="Run it in CI" icon="circle-check" href="/testing">
|
|
252
|
+
Turn this session into a suite that blocks a bad merge.
|
|
253
|
+
</Card>
|
|
254
|
+
<Card title="Why Reticle" icon="lightbulb" href="/why-reticle">
|
|
255
|
+
The false-green problem, and the measured case that this fixes it.
|
|
256
|
+
</Card>
|
|
257
|
+
</CardGroup>
|