@reticlehq/server 2.7.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 +64 -29
- 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 +33 -5
- package/dist/cli/chromium-hint.js +82 -12
- package/dist/cli/cli-doctor.js +28 -25
- package/dist/cli/cli-kill.d.ts +58 -0
- package/dist/cli/cli-kill.js +162 -0
- package/dist/cli/cli-launch.d.ts +17 -0
- package/dist/cli/cli-launch.js +28 -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 +8 -0
- package/dist/cli/cli-port.js +16 -0
- package/dist/cli/cli-verify.js +2 -2
- 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 +99 -14
- 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 +14 -0
- package/dist/events/contradictions.js +26 -17
- package/dist/events/predicate-eval.d.ts +20 -1
- package/dist/events/predicate-eval.js +125 -3
- package/dist/events/predicate.js +170 -18
- 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/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 +3 -3
- package/dist/index.js +3 -0
- package/dist/init/agent-rules.d.ts +16 -0
- package/dist/init/agent-rules.js +96 -22
- 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/non-js-project.js +29 -5
- package/dist/init/plan-framework.js +53 -15
- package/dist/init/plan.d.ts +40 -4
- package/dist/init/plan.js +163 -40
- package/dist/init/run.d.ts +24 -2
- package/dist/init/run.js +148 -42
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/slash-command.js +46 -6
- package/dist/init/snippets.d.ts +44 -4
- package/dist/init/snippets.js +161 -37
- 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 +205 -20
- package/dist/mcp/mcp.js +23 -7
- package/dist/mcp/proxy-lifecycle.d.ts +1 -1
- package/dist/mcp/proxy-lifecycle.js +1 -1
- 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/session/no-session-diagnosis.d.ts +19 -0
- package/dist/session/no-session-diagnosis.js +71 -18
- 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/session-lease.js +1 -1
- 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/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.js +5 -0
- package/dist/telemetry/telemetry.d.ts +5 -1
- package/dist/telemetry/telemetry.js +15 -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 +45 -1
- package/dist/tools/assert-grade.d.ts +1 -1
- package/dist/tools/assert-grade.js +27 -5
- package/dist/tools/assert-verdict.js +18 -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 +64 -4
- package/dist/tools/feedback-invite.js +1 -1
- package/dist/tools/invoke-tool.js +139 -23
- 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-surface.js +19 -0
- package/dist/tools/tools.js +16 -4
- package/docs/README.md +56 -11
- package/docs/actions.mdx +87 -0
- package/docs/agent-cheatsheet.md +93 -29
- 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 +22 -18
- 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 +93 -50
- package/docs/telemetry-events.mdx +430 -0
- package/docs/telemetry.md +57 -52
- 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
package/docs/benchmarks.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Benchmarks
|
|
3
|
+
description: 'How we measure Reticle, what each number actually means, and the places Reticle loses.'
|
|
4
|
+
icon: chart-column
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
> This page assumes **zero** testing background. By the end you'll understand what software testing is, why AI coding agents made it urgent, how to tell a good verification tool from a bad one, and exactly how Reticle measures up against the main alternatives
|
|
7
|
+
> This page assumes **zero** testing background. By the end you'll understand what software testing is, why AI coding agents made it urgent, how to tell a good verification tool from a bad one, and exactly how Reticle measures up against the main alternatives, including the places Reticle loses. If a term looks like jargon, it's defined the first time it appears.
|
|
4
8
|
>
|
|
5
|
-
> _Where the pictures come from, stated accurately: the **SVG charts** (`bench/artifacts/`) are generated from measured raws by `bench/harness/charts.mjs`. The **headline PNG cards** (`assets/readme/`) are screenshots of hand-authored HTML in `assets/benchmarks/src/`, rendered by `assets/benchmarks/render.mjs
|
|
9
|
+
> _Where the pictures come from, stated accurately: the **SVG charts** (`bench/artifacts/`) are generated from measured raws by `bench/harness/charts.mjs`. The **headline PNG cards** (`assets/readme/`) are screenshots of hand-authored HTML in `assets/benchmarks/src/`, rendered by `assets/benchmarks/render.mjs`; their figures are typed in by a human and are **not** read from `history.jsonl`, which this line previously claimed. Treat a card as a design asset that has to be updated by hand when a number moves, and `bench/SCORECARD.md` (plus its freshness banner) as the source of truth._
|
|
6
10
|
|
|
7
11
|
---
|
|
8
12
|
|
|
9
|
-
## Part 1
|
|
13
|
+
## Part 1: The problem, from first principles
|
|
10
14
|
|
|
11
15
|
### What is "testing"?
|
|
12
16
|
|
|
@@ -14,46 +18,46 @@ When you build software, **testing** is the act of checking that it actually doe
|
|
|
14
18
|
|
|
15
19
|
There are roughly three ways teams do this:
|
|
16
20
|
|
|
17
|
-
1. **Manual testing
|
|
18
|
-
2. **Automated tests
|
|
19
|
-
3. **Nothing
|
|
21
|
+
1. **Manual testing.** A human clicks through the app. Accurate, but slow and easy to skip.
|
|
22
|
+
2. **Automated tests**: code that clicks through the app for you. Fast to re-run, but expensive to write and notoriously **flaky** (they break for reasons unrelated to real bugs).
|
|
23
|
+
3. **Nothing.** Ship and hope. More common than anyone admits: surveys put ~44% of teams with no automated testing at all.
|
|
20
24
|
|
|
21
|
-
A **regression** is the specific bug this page cares about: something that _used to work_ and quietly _stopped working_ after a change. Regressions are insidious because the feature looked done
|
|
25
|
+
A **regression** is the specific bug this page cares about: something that _used to work_ and quietly _stopped working_ after a change. Regressions are insidious because the feature looked done, right up until a later edit broke it and nobody noticed.
|
|
22
26
|
|
|
23
27
|
### Why AI agents made this urgent
|
|
24
28
|
|
|
25
|
-
AI coding agents now write a large share of new code. They're good at _producing_ code. They are bad at one specific thing: **knowing whether the code they wrote actually works.** The agent edits files, says _"done ✅"_, and moves on. It never opened the browser. So **you** become its QA department
|
|
29
|
+
AI coding agents now write a large share of new code. They're good at _producing_ code. They are bad at one specific thing: **knowing whether the code they wrote actually works.** The agent edits files, says _"done ✅"_, and moves on. It never opened the browser. So **you** become its QA department, clicking around to discover that "done" wasn't done. Developers describe being _"gaslit by coding assistants."_ This is the **"done lie."**
|
|
26
30
|
|
|
27
|
-
The honest fix is to give the agent a way to _check its own work_
|
|
31
|
+
The honest fix is to give the agent a way to _check its own work_: to drive the real running app and confirm the feature actually happened. That's what Reticle does. This page is about proving it does it **well**, not just that it does it.
|
|
28
32
|
|
|
29
33
|
---
|
|
30
34
|
|
|
31
|
-
## Part 2
|
|
35
|
+
## Part 2: How would you even measure "good"?
|
|
32
36
|
|
|
33
37
|
Imagine three verification tools. How do you decide which is best? Two things matter, and they pull against each other:
|
|
34
38
|
|
|
35
|
-
- **Coverage
|
|
36
|
-
- **Cost
|
|
39
|
+
- **Coverage.** Does it actually _catch the bug_? A tool that misses regressions is useless no matter how cheap it is.
|
|
40
|
+
- **Cost.** How much does it make the AI _read_ to do its job? Every observation is fed into the model's limited context as **tokens** (the unit models read/write in). A tool that floods the context with thousands of tokens per look is slow, expensive, and crowds out the actual work.
|
|
37
41
|
|
|
38
42
|
A tool can cheat either axis: be cheap by looking at almost nothing (and miss bugs), or be thorough by dumping everything (and blow the budget). So the real metric has to reward **both at once**, with coverage as a hard floor.
|
|
39
43
|
|
|
40
44
|
That gives us our headline number:
|
|
41
45
|
|
|
42
|
-
> **Verification Efficiency = real regressions caught per 1,000 tokens spent looking
|
|
46
|
+
> **Verification Efficiency = real regressions caught per 1,000 tokens spent looking**, and it only counts once the tool catches **100% of the bugs** with **zero false alarms** on a known-good control. (Historically abbreviated "VE"; the catch-rate floor was "RCR".)
|
|
43
47
|
|
|
44
|
-
A **false positive** (or "false alarm") is crying wolf
|
|
48
|
+
A **false positive** (or "false alarm") is crying wolf: flagging a bug when nothing is wrong. We require zero, because a tool you can't trust when it's quiet is a tool you'll learn to ignore.
|
|
45
49
|
|
|
46
|
-

|
|
47
51
|
|
|
48
52
|
---
|
|
49
53
|
|
|
50
|
-
## Part 3
|
|
54
|
+
## Part 3: The competitors, and why these three
|
|
51
55
|
|
|
52
56
|
We compare Reticle against the two most credible agent-native browser tools, because they're what an AI agent would otherwise reach for:
|
|
53
57
|
|
|
54
|
-
- **Playwright MCP
|
|
55
|
-
- **Chrome DevTools MCP**
|
|
56
|
-
- **(Baseline) screenshot agents
|
|
58
|
+
- **Playwright MCP**: Microsoft's tool that lets an agent drive a browser via the accessibility tree (not screenshots). The closest "serious" alternative.
|
|
59
|
+
- **Chrome DevTools MCP** is Google's tool exposing Chrome's DevTools to an agent.
|
|
60
|
+
- **(Baseline) screenshot agents**, the common "let the model look at a picture" approach.
|
|
57
61
|
|
|
58
62
|
Why not compare to traditional test frameworks (Playwright the library, Cypress, etc.)? Because those are written and maintained by humans; they don't answer "can an _agent_ verify its own work in the loop." The three above do, so it's an apples-to-apples agent comparison.
|
|
59
63
|
|
|
@@ -61,22 +65,22 @@ Tool versions are pinned in `bench/raw/run-meta.json` so any run is reproducible
|
|
|
61
65
|
|
|
62
66
|
---
|
|
63
67
|
|
|
64
|
-
## Part 4
|
|
68
|
+
## Part 4: The three measurement passes
|
|
65
69
|
|
|
66
70
|
Different questions need different rigs. We run three (kept in the raw files as "Layer A/B/C"):
|
|
67
71
|
|
|
68
|
-
1. **Observation-cost pass** (no AI model involved). For each bug scenario we run each tool's natural recipe and measure the exact size of what it returns
|
|
72
|
+
1. **Observation-cost pass** (no AI model involved). For each bug scenario we run each tool's natural recipe and measure the exact size of what it returns: characters, bytes, and a tokenizer count. No model means no randomness, so the token cost of _looking_ is measured precisely and repeatably.
|
|
69
73
|
2. **Full-agent-loop pass** (a real model drives). Here an actual AI agent uses each tool end-to-end, and we record the _authoritative_ token usage the model reports. This is the real-world number; it needs an API key, so it's run periodically rather than on every change.
|
|
70
|
-
3. **Replay pass** (no model). Reticle can record a flow once and _replay_ it deterministically with no AI at all. This measures the cost of re-checking a known flow
|
|
74
|
+
3. **Replay pass** (no model). Reticle can record a flow once and _replay_ it deterministically with no AI at all. This measures the cost of re-checking a known flow, the thing a test suite does over and over, every commit.
|
|
71
75
|
|
|
72
76
|
### The scenarios
|
|
73
77
|
|
|
74
|
-
We inject **10 realistic regressions** into a demo app plus **one no-bug control** (to catch false alarms). The bugs span the failure modes that matter
|
|
78
|
+
We inject **10 realistic regressions** into a demo app plus **one no-bug control** (to catch false alarms). The bugs span the failure modes that matter, and deliberately include ones that are _invisible to a screenshot_:
|
|
75
79
|
|
|
76
80
|
| Scenario | The bug | Why it's here |
|
|
77
81
|
| --- | --- | --- |
|
|
78
82
|
| Hidden API 500 | A request silently returns a server error | A screenshot can't see a failed network call |
|
|
79
|
-
| Wrong status 404 | A request 404s | Same
|
|
83
|
+
| Wrong status 404 | A request 404s | Same, non-visual |
|
|
80
84
|
| CORS blocked | A request is blocked by the browser | Non-visual |
|
|
81
85
|
| Silent DOM removal | A KPI card vanishes with no error | Tests "did content disappear?" |
|
|
82
86
|
| Route break | Navigation doesn't change the page | Common SPA bug |
|
|
@@ -87,17 +91,17 @@ We inject **10 realistic regressions** into a demo app plus **one no-bug control
|
|
|
87
91
|
| Network timeout | A request hangs forever | A request that never resolves never "logs" |
|
|
88
92
|
| **Control (no bug)** | Nothing is wrong | Anything flagged here is a false positive |
|
|
89
93
|
|
|
90
|
-
Three of these (failed request, console error, hung request) are **categorically invisible to a screenshot
|
|
94
|
+
Three of these (failed request, console error, hung request) are **categorically invisible to a screenshot**. No number of pictures will ever catch them. That's a core part of the story.
|
|
91
95
|
|
|
92
96
|
---
|
|
93
97
|
|
|
94
|
-
## Part 5
|
|
98
|
+
## Part 5: The honest results, on two very different apps
|
|
95
99
|
|
|
96
|
-
We ran this two ways on purpose: a **controlled toy app** (where we can inject exact bugs and measure detection) and a **real production app** (where the numbers are messy and honest). The story holds in both
|
|
100
|
+
We ran this two ways on purpose: a **controlled toy app** (where we can inject exact bugs and measure detection) and a **real production app** (where the numbers are messy and honest). The story holds in both, and the real app is where it gets interesting.
|
|
97
101
|
|
|
98
|
-

|
|
99
103
|
|
|
100
|
-
### 5a
|
|
104
|
+
### 5a. The controlled toy app (the demo)
|
|
101
105
|
|
|
102
106
|
Measured on the observation-cost pass (numbers regenerate from `bench/raw/`):
|
|
103
107
|
|
|
@@ -108,92 +112,92 @@ Measured on the observation-cost pass (numbers regenerate from `bench/raw/`):
|
|
|
108
112
|
| Playwright MCP | 9 / 10 | 0.91 | 1,292 | 6.97 |
|
|
109
113
|
|
|
110
114
|
- **Reticle is the only tool that caught every regression**, with zero false alarms on the control.
|
|
111
|
-
- DevTools is a hair cheaper per look (758 vs 815 tokens)
|
|
115
|
+
- DevTools is a hair cheaper per look (758 vs 815 tokens), **but it's cheaper because it catches less.** On the metric that combines both (Verification Efficiency), Reticle leads at 12.27 vs 10.55.
|
|
112
116
|
- Playwright catches a lot but is ~1.6× more expensive per look, so its efficiency is lowest here.
|
|
113
117
|
|
|
114
|
-
### 5b
|
|
118
|
+
### 5b. A real production app (the Reticle dashboard)
|
|
115
119
|
|
|
116
|
-
A toy app is a fair lab, but it's small. The harder, more honest test is a **real, complex app
|
|
120
|
+
A toy app is a fair lab, but it's small. The harder, more honest test is a **real, complex app**. Take the [Reticle](https://reticle.sh) dashboard itself: React 19, authentication, live data, ~15 routes, a node-graph view, virtualized lists. We embedded the SDK (the Vite plugin) and drove the authenticated app with all three tools. Observing it **once** (the primary snapshot + the network log):
|
|
117
121
|
|
|
118
|
-

|
|
119
123
|
|
|
120
124
|
| Tool | Snapshot | Network | **Observe total** | Can it assert success? |
|
|
121
125
|
| --- | --- | --- | --- | --- |
|
|
122
|
-
| **Reticle** | 678 | 345 | **1,023** | ✅ via the app's own `auth:logged-in` signal
|
|
123
|
-
| Chrome DevTools MCP | 1,105 | 252 | 1,357 | ❌ DOM/network only
|
|
126
|
+
| **Reticle** | 678 | 345 | **1,023** | ✅ via the app's own `auth:logged-in` signal (**46 tok, un-fakeable**) |
|
|
127
|
+
| Chrome DevTools MCP | 1,105 | 252 | 1,357 | ❌ DOM/network only; can describe, can't verify intent |
|
|
124
128
|
| Playwright MCP | 1,522 | 671 | 2,193 | ❌ DOM/network only |
|
|
125
129
|
|
|
126
|
-
On a small page everything is cheap and the gap is modest. On a **big** page the structured read pulls ahead: Reticle is **2.1× leaner than Playwright MCP** and the cheapest overall. And only Reticle can read the app's own program state (`authenticated`, `userId`, `activeProjectId`) and assert the login _actually worked_ from a signal the app emits
|
|
130
|
+
On a small page everything is cheap and the gap is modest. On a **big** page the structured read pulls ahead: Reticle is **2.1× leaner than Playwright MCP** and the cheapest overall. And only Reticle can read the app's own program state (`authenticated`, `userId`, `activeProjectId`) and assert the login _actually worked_ from a signal the app emits. The others can only look at the DOM and the network and **guess**.
|
|
127
131
|
|
|
128
|
-
### 5c
|
|
132
|
+
### 5c. The kicker: a real bug, caught live
|
|
129
133
|
|
|
130
|
-
Here's what makes the real-app test matter. On the **very first run, before we instrumented anything**, Reticle's network observation flagged two endpoints returning **`500
|
|
134
|
+
Here's what makes the real-app test matter. On the **very first run, before we instrumented anything**, Reticle's network observation flagged two endpoints returning **`500`**: `GET /api/v1/projects` and `/recovery/incidents`, both failing with `column "deleted_at" does not exist` (a database migration that hadn't been applied). **The page rendered fine.** The sidebar loaded, nothing looked broken; a screenshot agent would have called it _"done."_ Reticle saw the broken backend underneath, in one look.
|
|
131
135
|
|
|
132
136
|
That is the whole thesis, demonstrated on a real app we didn't cherry-pick: **"looks done" ≠ "is done," and the difference is usually non-visual.** (We then fixed the migration; all endpoints 200.)
|
|
133
137
|
|
|
134
|
-
### 5d
|
|
138
|
+
### 5d. What each tool can actually do
|
|
135
139
|
|
|
136
|
-
Cost is half the story; capability is the other half. The marks below are what's **first-class and built-in** to each tool (all three are good tools
|
|
140
|
+
Cost is half the story; capability is the other half. The marks below are what's **first-class and built-in** to each tool (all three are good tools, and they compose: drive with theirs, assert with Reticle):
|
|
137
141
|
|
|
138
|
-

|
|
139
143
|
|
|
140
144
|
### Where Reticle loses (stated plainly)
|
|
141
145
|
|
|
142
146
|
Honesty is the point of this page, so here are the places Reticle does **not** win:
|
|
143
147
|
|
|
144
148
|
- **Raw cheapness on a single trivial look:** DevTools can be a few percent cheaper per observation when it isn't catching the harder bugs. We trade those tokens for catching more.
|
|
145
|
-
- **True pixels:** a screenshot is the _actual rendered frame_. A bug that only shows up in real paint (a font that failed to load, a GPU/compositing glitch) can be caught by a screenshot diff and missed by Reticle's structural reads
|
|
149
|
+
- **True pixels:** a screenshot is the _actual rendered frame_. A bug that only shows up in real paint (a font that failed to load, a GPU/compositing glitch) can be caught by a screenshot diff and missed by Reticle's structural reads, _unless_ Reticle is explicitly asked to do a visual diff. We're honest that structure isn't pixels.
|
|
146
150
|
- **The full-agent-loop pass** is measured for the three tools but a screenshot-agent variant is still future work; we don't claim a head-to-head there we haven't run.
|
|
147
151
|
|
|
148
152
|
### The part that compounds: re-running
|
|
149
153
|
|
|
150
|
-
The numbers above are for **one** verification. But a test suite's real job is the **same** check, over and over
|
|
154
|
+
The numbers above are for **one** verification. But a test suite's real job is the **same** check, over and over, every commit, every CI run. Here the picture changes shape:
|
|
151
155
|
|
|
152
|
-
- Reticle records a flow once, then **replays it with no AI model at all**
|
|
156
|
+
- Reticle records a flow once, then **replays it with no AI model at all** (re-resolving each element and re-asserting the outcome) for **~175-210 tokens per run**.
|
|
153
157
|
- Playwright MCP and DevTools MCP have no replay: re-checking means an agent **re-drives the whole flow with the model every time**, costing tens of thousands of tokens per run.
|
|
154
158
|
|
|
155
|
-
That's a **~128
|
|
159
|
+
That's a **~128-184× cost difference per re-run**, and it grows with how often you run. This is where the "much cheaper than screenshots" claim becomes dramatic rather than incremental.
|
|
156
160
|
|
|
157
|
-

|
|
158
162
|
|
|
159
163
|
### The large-page test (where the wedge is biggest)
|
|
160
164
|
|
|
161
165
|
On a small page, structured reads and screenshots are both cheap, so the gap is modest. The advantage shows on a **big** page. On a deliberately large grid (thousands of DOM nodes):
|
|
162
166
|
|
|
163
167
|
- A **full page snapshot** costs ~**3,636 tokens**.
|
|
164
|
-
- A **targeted verify loop** (find one button → act → assert the success signal) costs ~**279 tokens
|
|
168
|
+
- A **targeted verify loop** (find one button → act → assert the success signal) costs ~**279 tokens**, and stays flat as the page grows to 5,000 rows.
|
|
165
169
|
|
|
166
170
|
That's a **13× difference**, and against a screenshot agent it's larger still: one screenshot at a normal window size is ~**1,365 image tokens** _per look_, a real loop takes several looks, and the screenshot still can't see the non-visual bugs.
|
|
167
171
|
|
|
168
172
|
### Many agents at once (the parallel wedge)
|
|
169
173
|
|
|
170
|
-
A fleet of agents (or a parallel suite) verifying the same app doesn't need a browser each. Reticle keeps **one** headless Chromium and leases each agent an **isolated context** (separate cookies/storage/DOM). Measured on 16 flows: **35.4s** one-at-a-time vs **5.2s** across 8 leased contexts
|
|
174
|
+
A fleet of agents (or a parallel suite) verifying the same app doesn't need a browser each. Reticle keeps **one** headless Chromium and leases each agent an **isolated context** (separate cookies/storage/DOM). Measured on 16 flows: **35.4s** one-at-a-time vs **5.2s** across 8 leased contexts, so **6.78× faster**, ~30s saved per batch, 8-way peak concurrency. The alternative, a browser per agent, costs hundreds of MB and seconds of startup each; the pool's edge grows with agent count up to its cap (`multi-agent-throughput`).
|
|
171
175
|
|
|
172
176
|
---
|
|
173
177
|
|
|
174
|
-
## Part 6
|
|
178
|
+
## Part 6: Why we believe these numbers (fairness + reproducibility)
|
|
175
179
|
|
|
176
180
|
- **Every number comes from a committed harness**, not a slide. Re-run it: `pnpm bench`.
|
|
177
181
|
- **A no-bug control** runs in every pass, so a tool can't look good by flagging everything.
|
|
178
182
|
- **Tool versions are pinned**; the raw payloads are saved to `bench/raw/` so anyone can audit them.
|
|
179
|
-
- **The token counter is a tokenizer**, not a guess
|
|
183
|
+
- **The token counter is a tokenizer**, not a guess. Where we use a proxy tokenizer instead of a specific model's, we say so and lean on _relative_ differences, which are robust to the choice.
|
|
180
184
|
- **We publish where we lose** (above). A benchmark that only flatters its author isn't a benchmark.
|
|
181
185
|
|
|
182
186
|
---
|
|
183
187
|
|
|
184
|
-
## Part 7
|
|
188
|
+
## Part 7: What this means for you
|
|
185
189
|
|
|
186
|
-
- **If you're a developer using an AI agent:** Reticle lets the agent confirm its own change actually worked
|
|
187
|
-
- **If you run CI / a platform team:** the replay pass is the headline
|
|
190
|
+
- **If you're a developer using an AI agent:** Reticle lets the agent confirm its own change actually worked (across the network call, the console, the route, the state) for a few hundred tokens, not a screenshot's thousands. "Done" starts meaning done.
|
|
191
|
+
- **If you run CI / a platform team:** the replay pass is the headline: deterministic re-verification with no model, so a known flow re-checks for ~200 tokens at ~0% flake, every commit.
|
|
188
192
|
- **If you're evaluating tools:** the axis that matters is _bugs caught per token, gated on catching them all_. Cheapness that misses regressions is a false economy.
|
|
189
193
|
|
|
190
|
-
You are now equipped to read any verification benchmark critically: ask "what's the catch rate, what's the false-positive rate, and what's the cost _per re-run_?"
|
|
194
|
+
You are now equipped to read any verification benchmark critically: ask "what's the catch rate, what's the false-positive rate, and what's the cost _per re-run_?" Then be suspicious of any vendor that won't show you where they lose.
|
|
191
195
|
|
|
192
196
|
---
|
|
193
197
|
|
|
194
|
-
## Part 8
|
|
198
|
+
## Part 8: At enterprise scale (the honest "if a big company used this")
|
|
195
199
|
|
|
196
|
-
A fair question from a large engineering org (think a Datadog- or Salesforce-sized team): _does this actually pay off at our scale, or only in a demo?_ Here's the honest accounting, with every assumption stated so you can re-run it with your own numbers. **These are projections from the measured per-run costs above, not a measured enterprise deployment
|
|
200
|
+
A fair question from a large engineering org (think a Datadog- or Salesforce-sized team): _does this actually pay off at our scale, or only in a demo?_ Here's the honest accounting, with every assumption stated so you can re-run it with your own numbers. **These are projections from the measured per-run costs above, not a measured enterprise deployment; they are labeled as such.**
|
|
197
201
|
|
|
198
202
|
### Where it pays off (and compounds)
|
|
199
203
|
|
|
@@ -208,17 +212,17 @@ The win is **regression re-runs**, because that cost is paid over and over. Take
|
|
|
208
212
|
| Reticle replay (no model) | ~200 tok | ~2.0 M tok/day | ~0.5 B tok/yr |
|
|
209
213
|
| Agent re-drive (LLM) | ~30,000 tok | ~300 M tok/day | ~75 B tok/yr |
|
|
210
214
|
|
|
211
|
-
That's a **~150× difference on the recurring axis**, and it scales linearly with flows × runs
|
|
215
|
+
That's a **~150× difference on the recurring axis**, and it scales linearly with flows × runs, the two numbers an enterprise has _a lot_ of. The deterministic replay also means **~0% verdict flake** (measured over repeated identical runs), which at this scale is the difference between a trusted gate and one engineers learn to ignore (recall: flaky suites get abandoned; that's the documented failure mode Reticle is built to avoid). And because replay needs no model, it has **no per-run API spend and no rate-limit ceiling**. It runs in CI like any other deterministic check.
|
|
212
216
|
|
|
213
217
|
### Where it does NOT help (stated plainly)
|
|
214
218
|
|
|
215
219
|
An honest enterprise evaluation has to include the limits:
|
|
216
220
|
|
|
217
|
-
- **Authoring still costs model tokens.** Recording/annotating a flow the first time uses an agent. The economics only turn positive once a flow is re-run enough times to amortize that
|
|
218
|
-
- **Tier-1 oracles need instrumentation.** The strongest, un-fakeable assertions come from app signals the team adds (a ~30-second opt-in per success event). Without them you get Tier-2 (network/route/ state)
|
|
221
|
+
- **Authoring still costs model tokens.** Recording/annotating a flow the first time uses an agent. The economics only turn positive once a flow is re-run enough times to amortize that, which is exactly the enterprise case (many runs), but a flow you run twice won't pay off.
|
|
222
|
+
- **Tier-1 oracles need instrumentation.** The strongest, un-fakeable assertions come from app signals the team adds (a ~30-second opt-in per success event). Without them you get Tier-2 (network/route/ state): still strong, still better than "element exists," but not the magic tier. Be realistic about the instrumentation rollout across a large codebase.
|
|
219
223
|
- **It is not a full replacement for an existing E2E platform on day one.** Reticle is verification in the dev/agent loop and in CI; a large org with a mature Playwright/Cypress estate adopts it alongside, starting with the agent loop and the highest-value journeys, not as a big-bang migration.
|
|
220
224
|
- **Structure is not pixels.** As in Part 5: a purely visual regression (a paint/font/compositing bug) needs the opt-in visual diff; the always-on structured reads won't catch it.
|
|
221
|
-
- **Per-project isolation is a setup step.** Multiple apps/teams on one machine each want their own bridge port (`RETICLE_PORT`)
|
|
225
|
+
- **Per-project isolation is a setup step.** Multiple apps/teams on one machine each want their own bridge port (`RETICLE_PORT`). Trivial, but it's configuration a platform team should standardize.
|
|
222
226
|
|
|
223
227
|
### The honest bottom line for a large org
|
|
224
228
|
|
|
@@ -226,21 +230,21 @@ Reticle is **most efficient exactly where big companies hurt most**: a large num
|
|
|
226
230
|
|
|
227
231
|
---
|
|
228
232
|
|
|
229
|
-
### Appendix
|
|
233
|
+
### Appendix: the raw artifacts
|
|
230
234
|
|
|
231
|
-
- `bench/SCORECARD.md
|
|
232
|
-
- `bench/METRIC.md`
|
|
233
|
-
- `bench/METHODOLOGY.md
|
|
234
|
-
- `bench/history.jsonl
|
|
235
|
-
- `bench/raw/`
|
|
235
|
+
- `bench/SCORECARD.md`: the one-page standing with the plain-language legend.
|
|
236
|
+
- `bench/METRIC.md` holds the exact metric definitions.
|
|
237
|
+
- `bench/METHODOLOGY.md`: full design (controls, scenarios, fairness).
|
|
238
|
+
- `bench/history.jsonl`: every measured run over time.
|
|
239
|
+
- `bench/raw/` holds raw payloads and per-scenario results (gitignored locally; regenerated by `pnpm bench`).
|
|
236
240
|
|
|
237
241
|
---
|
|
238
242
|
|
|
239
|
-
## SDK overhead
|
|
243
|
+
## SDK overhead: what instrumentation costs the app
|
|
240
244
|
|
|
241
|
-
An observability layer that slows the app corrupts its own performance verdicts, so Reticle holds itself to a budget: **total instrumentation overhead below 3% of main-thread time**, measured on the _hostile_ fixture
|
|
245
|
+
An observability layer that slows the app corrupts its own performance verdicts, so Reticle holds itself to a budget: **total instrumentation overhead below 3% of main-thread time**, measured on the _hostile_ fixture, a page that never goes quiet (a ~10 messages/sec feed, a 60fps ticker, a large list), not a polite demo.
|
|
242
246
|
|
|
243
|
-
The same page is loaded in one browser under three conditions
|
|
247
|
+
The same page is loaded in one browser under three conditions (full SDK, observers with the HUD suppressed, and Reticle absent), and Chrome's own cumulative `TaskDuration` is compared over an identical window (condition order rotated so warm-up can't favour any one of them). Three conditions, not two, so that what instrumenting costs is separable from what the HUD costs.
|
|
244
248
|
|
|
245
249
|
| | main-thread task time | busy |
|
|
246
250
|
| ----------------------------- | --------------------- | ------------------------------ |
|
|
@@ -250,6 +254,6 @@ The same page is loaded in one browser under three conditions — full SDK, obse
|
|
|
250
254
|
| **instrumentation** | **+0.21 pp** | below the ±1.23 pp noise floor |
|
|
251
255
|
| **presenter HUD** | **+0.53 pp** | opt out with `present: false` |
|
|
252
256
|
|
|
253
|
-
**Result: instrumentation overhead is smaller than the method can resolve
|
|
257
|
+
**Result: instrumentation overhead is smaller than the method can resolve. Report it as `< 1.2 percentage points`, inside the 3% budget.** The HUD is measured and reported separately, because it is a developer-visible affordance you can turn off, not a cost of observing.
|
|
254
258
|
|
|
255
|
-
Getting here required correcting the method and then fixing a real bug. The two-condition version of this bench compared the full SDK against Reticle being absent, so it charged instrumentation for the HUD too, and reported **+5.85 pp
|
|
259
|
+
Getting here required correcting the method and then fixing a real bug. The two-condition version of this bench compared the full SDK against Reticle being absent, so it charged instrumentation for the HUD too, and reported **+5.85 pp (FAIL)**. Decomposing it showed almost none of that was instrumentation: a single `backdrop-filter: blur(24px)` on the HUD panel was costing **+4.03 pp** on its own, more than every observer combined, because a backdrop blur re-samples everything behind it whenever that content changes, and this fixture repaints at 60fps. Removing it took total main-thread time down 22%. Full history, including a fix that measured nothing and was reverted rather than kept, is in `bench/overhead/README.md`. Reproduce it yourself with `node bench/overhead/measure.mjs` (see `bench/overhead/README.md`, which records the ruled-out causes). causes).
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Best practices: how to earn a green
|
|
3
|
+
sidebarTitle: Best practices
|
|
4
|
+
description: The habits that make a verdict worth trusting, and the specific mistakes that produce a confident, wrong pass.
|
|
5
|
+
icon: star
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Everything here comes from driving Reticle against real apps and getting it wrong first. None of it is theoretical, and several entries exist because they bit us while writing these docs.
|
|
9
|
+
|
|
10
|
+
## Name the consequence before you act
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{ "ref": "e5", "action": "click", "until": { "kind": "signal", "name": "auth:granted" } }
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Not this:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{ "ref": "e5", "action": "click" }
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
...followed by looking around for something that seems fine.
|
|
23
|
+
|
|
24
|
+
An agent that acts first and then decides what counts as success will always find something that counts. Stating the expectation up front is the entire difference between a check and a rationalisation, and it costs no extra calls: [`reticle_act_and_wait`](/tools-act-and-wait) does both in one hop.
|
|
25
|
+
|
|
26
|
+
## A drive that ends on `reticle_act` proved nothing
|
|
27
|
+
|
|
28
|
+
Only `reticle_act_and_wait` and `reticle_assert` produce a verdict. Everything else moves or reads the app.
|
|
29
|
+
|
|
30
|
+
This is not pedantry. Here is a real `act_sequence` response:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{ "ok": true, "count": 3, "effects": [ … "appeared": "Signing in… | Invalid email or password | Sign in" ] }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`ok: true`, and the login **failed**. `ok` means the actions were dispatched, which they were. If your run ends there, you have a green tick attached to a broken flow.
|
|
37
|
+
|
|
38
|
+
**Batch the setup, prove the outcome.** Use `act_sequence` for the fills, then `act_and_wait` on the final click.
|
|
39
|
+
|
|
40
|
+
## Treat `unknown` as unknown
|
|
41
|
+
|
|
42
|
+
`verified: "unknown"` is not a pass, not a failure, and not a rounding error. It means Reticle drove the app and could not tell.
|
|
43
|
+
|
|
44
|
+
We hit one writing the instrumentation page: a login that genuinely succeeded, signal fired, state changed, `verdict.pass: true`. And still came back `unknown`, because the page never settled inside the observation window.
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{ "verified": "unknown", "verifiedReason": "unsettled", "verdict": { "pass": true } }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Report it as unknown. If you believe it is wrong, [say so](/tools-session-and-feedback). Reticle calls an unknown verdict its own defect, and the response literally invites the report.
|
|
51
|
+
|
|
52
|
+
## Focus the tab
|
|
53
|
+
|
|
54
|
+
A backgrounded tab has its timers throttled, which suppresses the quiescence detection Reticle uses to decide a page has settled. Symptoms: actions that appear to do nothing, and `unknown` verdicts on things that plainly worked.
|
|
55
|
+
|
|
56
|
+
Every response tells you when this is happening:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
"warning": "tab throttled; timer/rAF/pointer gestures may silently no-op; refocus before driving"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Believe it. Pass `refuseWhenThrottled: true` if you would rather fail loudly than act into a throttled tab.
|
|
63
|
+
|
|
64
|
+
## Read the buffer note before trusting a negative
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
"buffer": { "held": 15, "dropped": 529, "note": "…a negative result here may be a false negative…" }
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
"No requests found" and "I no longer have the requests you asked about" are different answers. Reticle distinguishes them; make sure your conclusion does. Grade sooner, or widen the buffer.
|
|
71
|
+
|
|
72
|
+
## Assert exact counts, not vague ones
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{ "until": { "kind": "net", "method": "POST", "count": 1 } }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
"A POST fired" passes when two fired. Double-submit is one of the most expensive bugs on this list. It charges customers twice. And it is completely invisible on screen. The exact count is free to ask for.
|
|
79
|
+
|
|
80
|
+
## Scope assertions with `since`
|
|
81
|
+
|
|
82
|
+
Without a cursor, an assertion evaluates against the recent buffer, which may contain events from before your action. That passes for the wrong reason: the request you asserted did fire, two clicks ago.
|
|
83
|
+
|
|
84
|
+
Every `act` response returns a `since`. Use it.
|
|
85
|
+
|
|
86
|
+
## Climb the grade ladder
|
|
87
|
+
|
|
88
|
+
Every verdict reports `honesty.grade`. Aim for `signal`.
|
|
89
|
+
|
|
90
|
+
| Grade | What it means | How much it proves |
|
|
91
|
+
| ---------- | ---------------------------- | ------------------------ |
|
|
92
|
+
| `presence` | Something appeared | A mock does this too |
|
|
93
|
+
| `state` | A registered store changed | The app accepted it |
|
|
94
|
+
| `signal` | The app fired a named signal | The app declared success |
|
|
95
|
+
|
|
96
|
+
Getting from `presence` to `signal` is roughly ten minutes of [instrumentation](/instrumentation) per flow, and it is the highest-leverage work available.
|
|
97
|
+
|
|
98
|
+
**Emit failure signals too.** With `auth:denied` defined, a failed login reports `signals seen in this window: auth:denied` instead of just "never fired". The app names its own outcome and the diagnosis lands in one response.
|
|
99
|
+
|
|
100
|
+
## A signal-grade pass can still miss the bug
|
|
101
|
+
|
|
102
|
+
This is the subtlest entry on the page, and it comes from a controlled A/B run against the same app minutes apart.
|
|
103
|
+
|
|
104
|
+
Same login, same predicate, `until: { kind: "signal", name: "auth:granted" }`, run twice. Once against a healthy build, once against a build with a persistence bug injected, where the auth token is never written to storage.
|
|
105
|
+
|
|
106
|
+
**Healthy:**
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"verified": "yes",
|
|
111
|
+
"honesty": { "grade": "signal", "integrity": { "clean": true } },
|
|
112
|
+
"summary": {
|
|
113
|
+
"signals": ["auth:granted"],
|
|
114
|
+
"stateDiffs": [{ "path": "auth", "from": null, "to": "{…}" }],
|
|
115
|
+
"storageKeysChanged": ["reticle.bench.authToken", "reticle.bench.sessionId"]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Broken:**
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"verified": "yes",
|
|
125
|
+
"honesty": { "grade": "signal", "integrity": { "clean": true } },
|
|
126
|
+
"summary": {
|
|
127
|
+
"signals": ["auth:granted"],
|
|
128
|
+
"stateDiffs": [{ "path": "auth", "from": null, "to": "{…}" }],
|
|
129
|
+
"storageKeysChanged": []
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Identical verdict. Identical grade. The signal fired in both, because the login genuinely did succeed, in memory. Reload the page and the second one is logged out.
|
|
135
|
+
|
|
136
|
+
The only difference in the entire response is `storageKeysChanged`, and the assertion never asked about it.
|
|
137
|
+
|
|
138
|
+
<Warning>
|
|
139
|
+
The lesson is not that signals are unreliable. It is that **a verdict answers the question you
|
|
140
|
+
asked**. `auth:granted` asked "did the app authenticate", and the honest answer was yes. Nobody
|
|
141
|
+
asked "does it survive a reload".
|
|
142
|
+
</Warning>
|
|
143
|
+
|
|
144
|
+
If persistence is part of what "it worked" means, assert it:
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"until": {
|
|
149
|
+
"kind": "allOf",
|
|
150
|
+
"predicates": [
|
|
151
|
+
{ "kind": "signal", "name": "auth:granted" },
|
|
152
|
+
{ "kind": "state", "store": "app", "path": "auth.email", "equals": "admin@reticle.dev" }
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Reticle put the evidence in the response either way. `storageKeysChanged: []` was right there. Read the `summary` block, not just `verified`.
|
|
159
|
+
|
|
160
|
+
## Never weaken a check to make it pass
|
|
161
|
+
|
|
162
|
+
The temptation, when an assertion fails, is to loosen it until it goes green. Sometimes the assertion really was wrong. Usually the app is.
|
|
163
|
+
|
|
164
|
+
If you relax a predicate, you should be able to say why the _original_ expectation was incorrect. "It kept failing" is not that reason.
|
|
165
|
+
|
|
166
|
+
## Ask narrowly
|
|
167
|
+
|
|
168
|
+
| Instead of | Use | Why |
|
|
169
|
+
| --- | --- | --- |
|
|
170
|
+
| `snapshot` on every turn | `snapshot { mode: "status" }` | 25 tokens to answer "where am I?" |
|
|
171
|
+
| `snapshot` then reading it | `query { by: "testid" }` | Retrieval, not orientation |
|
|
172
|
+
| `query` to count rows | `query { count_only: true }` | Around 30x smaller |
|
|
173
|
+
| `observe` for one question | `network` or `console` | The summary block is usually enough |
|
|
174
|
+
| Re-snapshotting after each act | Reuse the `ref` | Refs stay valid until the element leaves the DOM |
|
|
175
|
+
|
|
176
|
+
Every response carries `cost: { bytes, tokens }`. If a loop is expensive, that field tells you which call to fix.
|
|
177
|
+
|
|
178
|
+
## Bind to meaning, not structure
|
|
179
|
+
|
|
180
|
+
Query by testid, role or label. Assert on signals and state. Both survive the refactor that moves your markup around; a CSS selector does not.
|
|
181
|
+
|
|
182
|
+
## End your sessions
|
|
183
|
+
|
|
184
|
+
Sessions accumulate. Four stale ones pointing at dev servers you restarted an hour ago look completely plausible in a list, and one of them will get picked.
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{ "action": "yield", "mode": "waiting" }
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`yield` when you are pausing, `end` when you are done. Both revive on your next action, so neither is destructive. The difference is what the human watching the panel is told. The panel reads "live" until you say otherwise.
|
|
191
|
+
|
|
192
|
+
## Confirm a navigation landed
|
|
193
|
+
|
|
194
|
+
`reticle_navigate` returns `confirmed: false` by design. A navigation destroys the document the SDK lives in, so it can only report dispatch. Check [`reticle_sessions`](/tools-sessions) for a session at the new URL before acting, and re-snapshot: refs do not survive a new document.
|
|
195
|
+
|
|
196
|
+
## Start with what the app says about itself
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{ "tool": "reticle_capabilities" }
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
On an unfamiliar app this beats snapshotting and guessing. It returns the testids, signals, stores and named flows the app advertises. What the team considered testable, rather than what you inferred from element names.
|
|
203
|
+
|
|
204
|
+
<CardGroup cols={2}>
|
|
205
|
+
<Card title="Instrument your app" icon="wrench" href="/instrumentation">
|
|
206
|
+
Where most of the quality actually comes from.
|
|
207
|
+
</Card>
|
|
208
|
+
<Card title="Lock it into CI" icon="vial" href="/testing">
|
|
209
|
+
So the habits survive the week you are not looking.
|
|
210
|
+
</Card>
|
|
211
|
+
</CardGroup>
|