@reticlehq/server 3.2.0 → 3.4.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/LICENSE-ENTERPRISE +1 -1
- package/README.md +3 -3
- package/SKILL.md +23 -22
- package/dist/chromium-launch-options.d.ts +4 -3
- package/dist/chromium-launch-options.js +6 -2
- package/dist/command/cli/adhoc-suite.d.ts +41 -0
- package/dist/command/cli/adhoc-suite.js +159 -0
- package/dist/command/cli/adhoc-verdict.d.ts +58 -2
- package/dist/command/cli/adhoc-verdict.js +108 -13
- package/dist/command/cli/answers/gate-hook-message.d.ts +6 -0
- package/dist/command/cli/answers/gate-hook-message.js +6 -0
- package/dist/command/cli/auth/cloud-keystore.d.ts +0 -2
- package/dist/command/cli/auth/cloud-keystore.js +0 -3
- package/dist/command/cli/auth/cloud-session.d.ts +4 -0
- package/dist/command/cli/auth/cloud-session.js +2 -0
- package/dist/command/cli/cli-doctor.js +30 -8
- package/dist/command/cli/cli-flow-commands.d.ts +3 -6
- package/dist/command/cli/cli-flow-commands.js +67 -4
- package/dist/command/cli/cli-parse-grammar.d.ts +7 -0
- package/dist/command/cli/cli-parse-grammar.js +7 -0
- package/dist/command/cli/cli-parse-verify.d.ts +2 -5
- package/dist/command/cli/cli-parse-verify.js +86 -13
- package/dist/command/cli/cli-parse.d.ts +14 -1
- package/dist/command/cli/cli-parse.js +54 -8
- package/dist/command/cli/cli-update-commands.js +8 -1
- package/dist/command/cli/cli-verify.d.ts +80 -0
- package/dist/command/cli/cli-verify.js +181 -26
- package/dist/command/cli/cloud-cli.js +5 -2
- package/dist/command/cli/cloud-kit.d.ts +0 -2
- package/dist/command/cli/cloud-kit.js +3 -5
- package/dist/command/cli/cloud-login.js +21 -3
- package/dist/command/cli/demo/demo-page.js +6 -3
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +5 -0
- package/dist/command/cli/doctor/browser/chromium-hint.js +5 -0
- package/dist/command/cli/doctor/doctor-sessions-line.d.ts +2 -1
- package/dist/command/cli/launch/cli-launch.d.ts +9 -29
- package/dist/command/cli/launch/cli-launch.js +27 -49
- package/dist/command/cli/launch/sdk-version-match.d.ts +84 -0
- package/dist/command/cli/launch/sdk-version-match.js +206 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +2 -16
- package/dist/command/cli/ports/resolve/cli-port.d.ts +38 -0
- package/dist/command/cli/ports/resolve/cli-port.js +169 -15
- package/dist/command/cli/report-command.d.ts +16 -0
- package/dist/command/cli/report-command.js +97 -0
- package/dist/command/cli/setup-mcp-cli.js +13 -13
- package/dist/command/cli/tutorial.d.ts +2 -2
- package/dist/command/cli/tutorial.js +21 -7
- package/dist/command/cli.js +67 -10
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +44 -2
- package/dist/command/daemon/binding/daemon-status-probe.js +79 -6
- package/dist/command/daemon/binding/port-presence.d.ts +1 -1
- package/dist/command/daemon/daemon-resolve.d.ts +9 -0
- package/dist/command/daemon/daemon-resolve.js +1 -1
- package/dist/command/daemon/idle-shutdown.d.ts +2 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +7 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.js +30 -7
- package/dist/command/dev/stale-issue-guard.d.ts +3 -4
- package/dist/command/dev/stale-issue-guard.js +8 -8
- package/dist/command/setup/bringup/connect-lease.d.ts +17 -0
- package/dist/command/setup/bringup/connect-lease.js +38 -0
- package/dist/command/setup/bringup/dev-server-files.d.ts +68 -0
- package/dist/command/setup/bringup/dev-server-files.js +131 -0
- package/dist/command/setup/bringup/dev-server-log-cap.d.ts +35 -0
- package/dist/command/setup/bringup/dev-server-log-cap.js +121 -0
- package/dist/command/setup/bringup/ensure-daemon.d.ts +65 -4
- package/dist/command/setup/bringup/ensure-daemon.js +135 -16
- package/dist/command/setup/bringup/static-page-server.d.ts +35 -0
- package/dist/command/setup/bringup/static-page-server.js +151 -0
- package/dist/command/setup/desktop-shape.d.ts +4 -0
- package/dist/command/setup/desktop-shape.js +18 -2
- package/dist/command/setup/init/init-host.js +8 -0
- package/dist/command/setup/init/init-port.d.ts +15 -0
- package/dist/command/setup/init/init-port.js +44 -0
- package/dist/command/setup/init/init-runtime.d.ts +1 -1
- package/dist/command/setup/init/init-runtime.js +39 -2
- package/dist/command/setup/node-effects.d.ts +27 -14
- package/dist/command/setup/node-effects.js +80 -44
- package/dist/command/setup/probe/loopback-probe-urls.d.ts +7 -0
- package/dist/command/setup/probe/loopback-probe-urls.js +40 -0
- package/dist/command/setup/probe/page-probe.d.ts +13 -2
- package/dist/command/setup/probe/page-probe.js +18 -3
- package/dist/command/setup/run-setup.d.ts +26 -1
- package/dist/command/setup/run-setup.js +96 -13
- package/dist/command/setup/session-pick.d.ts +13 -0
- package/dist/command/setup/session-pick.js +20 -0
- package/dist/command/setup/setup-command.d.ts +2 -14
- package/dist/command/setup/setup-command.js +105 -31
- package/dist/command/setup/setup-mcp.js +3 -3
- package/dist/command/setup/terminal/wait-progress.d.ts +8 -0
- package/dist/command/setup/terminal/wait-progress.js +17 -0
- package/dist/features/crawl/crawl-empty.js +1 -1
- package/dist/features/crawl/crawl-tools.d.ts +6 -0
- package/dist/features/crawl/crawl-tools.js +57 -0
- package/dist/features/crawl/crawl.d.ts +2 -0
- package/dist/features/crawl/crawl.js +171 -156
- package/dist/features/exhaust/app-coverage.d.ts +33 -0
- package/dist/features/exhaust/app-coverage.js +31 -0
- package/dist/features/exhaust/code-coverage.d.ts +47 -0
- package/dist/features/exhaust/code-coverage.js +77 -0
- package/dist/features/exhaust/exhaust-run.d.ts +33 -0
- package/dist/features/exhaust/exhaust-run.js +82 -0
- package/dist/features/exhaust/explorer.d.ts +103 -0
- package/dist/features/exhaust/explorer.js +195 -0
- package/dist/features/exhaust/failure-branches.d.ts +33 -0
- package/dist/features/exhaust/failure-branches.js +92 -0
- package/dist/features/exhaust/ledger.d.ts +202 -0
- package/dist/features/exhaust/ledger.js +252 -0
- package/dist/features/exhaust/session-fold.d.ts +31 -0
- package/dist/features/exhaust/session-fold.js +79 -0
- package/dist/features/exhaust/session-port.d.ts +36 -0
- package/dist/features/exhaust/session-port.js +102 -0
- package/dist/features/harness/drive-report.d.ts +0 -9
- package/dist/features/harness/drive-report.js +18 -0
- package/dist/features/harness/driver.js +4 -7
- package/dist/features/harness/fill-values.d.ts +6 -0
- package/dist/features/harness/fill-values.js +3 -0
- package/dist/features/harness/jev-driver.js +36 -22
- package/dist/features/harness/openai-driver.js +4 -7
- package/dist/features/harness/platform-config.d.ts +10 -0
- package/dist/features/harness/platform-config.js +13 -6
- package/dist/index.d.ts +10 -1
- package/dist/index.js +78 -104
- package/dist/judgement/capsule/capsule-store.d.ts +31 -2
- package/dist/judgement/capsule/capsule-store.js +58 -6
- package/dist/judgement/capsule/capsule.js +5 -0
- package/dist/judgement/capsule/causal-summary.d.ts +13 -1
- package/dist/judgement/capsule/causal-summary.js +7 -0
- package/dist/judgement/domain/domain-model.js +40 -9
- package/dist/judgement/oracles/self-instrument.js +4 -2
- package/dist/judgement/outcome/assertion-integrity.d.ts +3 -3
- package/dist/judgement/runs/artifact/gap-report.d.ts +17 -0
- package/dist/judgement/runs/artifact/gap-report.js +46 -0
- package/dist/judgement/runs/artifact/gap-summary.d.ts +58 -0
- package/dist/judgement/runs/artifact/gap-summary.js +85 -0
- package/dist/judgement/runs/artifact/render-report.js +4 -1
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +2 -2
- package/dist/judgement/runs/artifact/replay-mapping.js +44 -9
- package/dist/judgement/runs/artifact/to-artifact.d.ts +7 -2
- package/dist/judgement/runs/artifact/to-artifact.js +1 -1
- package/dist/judgement/runs/context-tools.js +21 -3
- package/dist/judgement/runs/drive-run.js +7 -2
- package/dist/judgement/runs/verification-sync.d.ts +3 -1
- package/dist/judgement/runs/verification-sync.js +1 -1
- package/dist/language/flows/annotate-notes/annotate.d.ts +0 -15
- package/dist/language/flows/annotate-notes/annotate.js +66 -18
- package/dist/language/flows/change/affected.d.ts +9 -0
- package/dist/language/flows/change/affected.js +35 -0
- package/dist/language/flows/change/gate.d.ts +20 -0
- package/dist/language/flows/change/gate.js +12 -1
- package/dist/language/flows/flow-anchor.d.ts +12 -0
- package/dist/language/flows/flow-anchor.js +20 -0
- package/dist/language/flows/flow-author.d.ts +7 -0
- package/dist/language/flows/flow-author.js +19 -0
- package/dist/language/flows/flow-expect-grammar.d.ts +2 -2
- package/dist/language/flows/flow-expect-grammar.js +92 -11
- package/dist/language/flows/flow-intent.js +1 -1
- package/dist/language/flows/flow-journey.d.ts +54 -0
- package/dist/language/flows/flow-journey.js +119 -0
- package/dist/language/flows/flow-learning.d.ts +3 -0
- package/dist/language/flows/flow-learning.js +39 -8
- package/dist/language/flows/flow-replay-run.d.ts +33 -12
- package/dist/language/flows/flow-replay-run.js +131 -52
- package/dist/language/flows/flow-replay-types.d.ts +9 -0
- package/dist/language/flows/flow-replay.d.ts +27 -12
- package/dist/language/flows/flow-replay.js +127 -111
- package/dist/language/flows/flow-result.d.ts +2 -0
- package/dist/language/flows/flow-result.js +25 -0
- package/dist/language/flows/flow-step-runners.d.ts +26 -10
- package/dist/language/flows/flow-step-runners.js +20 -4
- package/dist/language/flows/flow-success.d.ts +21 -6
- package/dist/language/flows/flow-success.js +50 -138
- package/dist/language/flows/flow-tools.js +17 -5
- package/dist/language/flows/flows.d.ts +18 -5
- package/dist/language/flows/flows.js +101 -16
- package/dist/language/flows/heal.d.ts +9 -1
- package/dist/language/flows/heal.js +24 -2
- package/dist/language/flows/learned-sources.d.ts +21 -0
- package/dist/language/flows/learned-sources.js +84 -0
- package/dist/language/flows/recording/recording-backtrack.d.ts +0 -7
- package/dist/language/flows/recording/recording-backtrack.js +1 -1
- package/dist/language/flows/recording/replay-halt.d.ts +2 -1
- package/dist/language/flows/recording/replay-halt.js +3 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +31 -2
- package/dist/language/flows/recording/tape/recordings.js +55 -1
- package/dist/language/flows/replay.d.ts +19 -0
- package/dist/language/flows/replay.js +53 -13
- package/dist/language/flows/stores/annotation-store.d.ts +5 -5
- package/dist/language/flows/stores/assertion-tiers-store.js +2 -3
- package/dist/language/flows/stores/flake-store.js +2 -3
- package/dist/language/flows/suite/coverage.d.ts +1 -1
- package/dist/language/flows/suite/parallel-suite.d.ts +2 -1
- package/dist/language/flows/verify-change-tools.js +19 -2
- package/dist/launch-chromium.d.ts +80 -0
- package/dist/launch-chromium.js +218 -0
- package/dist/memory/cloud/cloud-config.d.ts +11 -2
- package/dist/memory/cloud/cloud-config.js +47 -7
- package/dist/memory/cloud/cloud-sync.d.ts +4 -3
- package/dist/memory/cloud/cloud-sync.js +7 -10
- package/dist/memory/cloud/harness-config.d.ts +2 -0
- package/dist/memory/cloud/harness-offer.js +5 -6
- package/dist/memory/cloud/harness-switch.js +5 -6
- package/dist/memory/cloud/sync-cycle.d.ts +78 -1
- package/dist/memory/cloud/sync-cycle.js +321 -53
- package/dist/memory/cloud/sync-daemon.d.ts +13 -0
- package/dist/memory/cloud/sync-daemon.js +45 -20
- package/dist/memory/cloud/sync-disk.js +27 -8
- package/dist/memory/impact/impact-store.js +6 -0
- package/dist/memory/intent/inline-intent.js +8 -3
- package/dist/memory/intent/intent-shard-store.d.ts +33 -47
- package/dist/memory/intent/intent-shard-store.js +252 -140
- package/dist/memory/intent/intent-shard.d.ts +0 -23
- package/dist/memory/intent/intent-shard.js +1 -11
- package/dist/memory/intent/intent-store.d.ts +13 -8
- package/dist/memory/intent/intent-store.js +46 -90
- package/dist/memory/intent/intent-tools.js +34 -23
- package/dist/memory/journal/ambient-store.js +2 -3
- package/dist/memory/journal/deviation-report.js +4 -1
- package/dist/memory/journal/deviation-service.js +10 -2
- package/dist/memory/journal/drive-flow.d.ts +19 -14
- package/dist/memory/journal/drive-flow.js +65 -48
- package/dist/memory/journal/envelope-store.js +2 -3
- package/dist/memory/journal/journal-recorder.d.ts +21 -0
- package/dist/memory/journal/on-disk/retain-policy.d.ts +34 -0
- package/dist/memory/journal/on-disk/retain-policy.js +44 -0
- package/dist/memory/journal/on-disk/retention.d.ts +14 -10
- package/dist/memory/journal/on-disk/retention.js +13 -12
- package/dist/memory/journal/on-disk/startup-maintenance.d.ts +42 -0
- package/dist/memory/journal/on-disk/startup-maintenance.js +14 -0
- package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +0 -11
- package/dist/memory/journal/on-disk/workspace-gitignore.js +6 -45
- package/dist/memory/journal/on-disk/workspace-tiers.d.ts +52 -0
- package/dist/memory/journal/on-disk/workspace-tiers.js +90 -0
- package/dist/memory/journal/route-template.d.ts +22 -0
- package/dist/memory/journal/route-template.js +44 -0
- package/dist/memory/journal/session-end.d.ts +13 -2
- package/dist/memory/journal/session-end.js +88 -18
- package/dist/memory/journal/session-journal.d.ts +8 -1
- package/dist/memory/journal/session-journal.js +10 -0
- package/dist/memory/project/baselines.d.ts +1 -1
- package/dist/memory/project/dir/reticle-dir.d.ts +3 -1
- package/dist/memory/project/dir/reticle-dir.js +1 -0
- package/dist/memory/project/fs/fs-port.js +44 -5
- package/dist/memory/project/fs/write-atomic.d.ts +26 -0
- package/dist/memory/project/fs/write-atomic.js +48 -0
- package/dist/memory/project/project-tools.js +3 -1
- package/dist/memory/recall/prior/connection-memory.d.ts +6 -0
- package/dist/memory/recall/prior/connection-memory.js +8 -0
- package/dist/memory/recall/registered-projects.d.ts +2 -1
- package/dist/portal/bridge/auth-failure-reason.d.ts +1 -1
- package/dist/portal/bridge/auth-failure-reason.js +9 -4
- package/dist/portal/bridge/bridge.d.ts +8 -0
- package/dist/portal/bridge/bridge.js +43 -2
- package/dist/portal/input/js-coverage.d.ts +25 -0
- package/dist/portal/input/js-coverage.js +30 -0
- package/dist/portal/input/real-input.d.ts +11 -0
- package/dist/portal/input/real-input.js +31 -5
- package/dist/portal/pool/browser-pool.d.ts +10 -0
- package/dist/portal/pool/browser-pool.js +10 -0
- package/dist/portal/pool/playwright-launcher.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +21 -13
- package/dist/portal/pool/pool-contract.d.ts +2 -0
- package/dist/portal/pool/storage-seed.d.ts +2 -1
- package/dist/portal/pool/zero-install.d.ts +27 -0
- package/dist/portal/pool/zero-install.js +54 -0
- package/dist/portal/realm/web-realm.js +6 -0
- package/dist/portal/session/dev-server/dev-command.d.ts +2 -2
- package/dist/portal/session/dev-server/dev-command.js +30 -12
- package/dist/portal/session/dev-server/dev-server-probe.d.ts +2 -1
- package/dist/portal/session/dev-server/dev-server-probe.js +17 -1
- package/dist/portal/session/dev-server/route-status-probe.d.ts +24 -0
- package/dist/portal/session/dev-server/route-status-probe.js +57 -0
- package/dist/portal/session/dev-server/served-document.d.ts +21 -0
- package/dist/portal/session/dev-server/served-document.js +57 -0
- package/dist/portal/session/facts/background-traffic.d.ts +9 -0
- package/dist/portal/session/facts/background-traffic.js +25 -0
- package/dist/portal/session/facts/observed-state.d.ts +11 -0
- package/dist/portal/session/facts/observed-state.js +34 -0
- package/dist/portal/session/live-control-tools.js +48 -3
- package/dist/portal/session/no-session-diagnosis.d.ts +29 -0
- package/dist/portal/session/no-session-diagnosis.js +62 -5
- package/dist/portal/session/no-session-next-action.d.ts +13 -0
- package/dist/portal/session/no-session-next-action.js +48 -9
- package/dist/portal/session/no-session-watch.d.ts +18 -0
- package/dist/portal/session/no-session-watch.js +88 -2
- package/dist/portal/session/presence/attachment-history.d.ts +13 -2
- package/dist/portal/session/session-health.d.ts +61 -0
- package/dist/portal/session/session-health.js +43 -3
- package/dist/portal/session/session-info.d.ts +6 -0
- package/dist/portal/session/session-manager.d.ts +29 -6
- package/dist/portal/session/session-manager.js +42 -5
- package/dist/portal/session/session-reaper.d.ts +28 -0
- package/dist/portal/session/session-reaper.js +49 -1
- package/dist/portal/session/session-successor.d.ts +11 -0
- package/dist/portal/session/session-successor.js +14 -1
- package/dist/portal/session/session-verdict-facts.d.ts +28 -0
- package/dist/portal/session/session-verdict-facts.js +15 -0
- package/dist/portal/session/session.d.ts +10 -0
- package/dist/portal/session/session.js +34 -26
- package/dist/surface/http-server.d.ts +2 -1
- package/dist/surface/http-server.js +4 -2
- package/dist/surface/mcp/mcp-proxy.d.ts +2 -2
- package/dist/surface/mcp/mcp-proxy.js +5 -5
- package/dist/surface/mcp/mcp.js +4 -2
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +13 -1
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +27 -2
- package/dist/surface/mcp/proxy-idle-exit.d.ts +2 -1
- package/dist/surface/mcp/server-instructions.js +21 -8
- package/dist/surface/tools/act/after-match.d.ts +17 -0
- package/dist/surface/tools/act/after-match.js +30 -0
- package/dist/surface/tools/act/already-true.d.ts +40 -0
- package/dist/surface/tools/act/already-true.js +36 -0
- package/dist/surface/tools/act/durable.d.ts +69 -0
- package/dist/surface/tools/act/durable.js +139 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +19 -6
- package/dist/surface/tools/act/navigation/navigate-arrival.js +60 -10
- package/dist/surface/tools/act/navigation/navigate-result.d.ts +1 -12
- package/dist/surface/tools/act/navigation/navigate-result.js +21 -3
- package/dist/surface/tools/act/proved-controls.d.ts +9 -0
- package/dist/surface/tools/act/proved-controls.js +11 -0
- package/dist/surface/tools/act/sequence-grade.d.ts +5 -0
- package/dist/surface/tools/act/sequence-grade.js +32 -1
- package/dist/surface/tools/act/settle-in-flight.d.ts +6 -2
- package/dist/surface/tools/act/settle-in-flight.js +29 -6
- package/dist/surface/tools/act-capsule.d.ts +7 -0
- package/dist/surface/tools/act-capsule.js +68 -8
- package/dist/surface/tools/act-sequence-tool.js +25 -8
- package/dist/surface/tools/act-tools.js +51 -74
- package/dist/surface/tools/args/numeric-bounds.d.ts +15 -2
- package/dist/surface/tools/args/numeric-bounds.js +30 -3
- package/dist/surface/tools/assert/assert-grade.js +26 -3
- package/dist/surface/tools/assert/assert-verdict.js +32 -6
- package/dist/surface/tools/browser-tools.js +17 -4
- package/dist/surface/tools/coverage-tools.js +27 -1
- package/dist/surface/tools/dynamic-tools.js +6 -0
- package/dist/surface/tools/error-recovery.js +4 -4
- package/dist/surface/tools/feedback-invite.js +6 -4
- package/dist/surface/tools/feedback-tools.d.ts +2 -1
- package/dist/surface/tools/feedback-tools.js +1 -1
- package/dist/surface/tools/harness-explore.d.ts +3 -2
- package/dist/surface/tools/harness-explore.js +7 -6
- package/dist/surface/tools/invoke-tool.js +17 -4
- package/dist/surface/tools/lease-hint.d.ts +8 -0
- package/dist/surface/tools/lease-hint.js +10 -0
- package/dist/surface/tools/lease-tools.d.ts +16 -28
- package/dist/surface/tools/lease-tools.js +68 -11
- package/dist/surface/tools/lineage-tools.js +1 -1
- package/dist/surface/tools/live-call-text.js +4 -1
- package/dist/surface/tools/observe-tools.js +23 -7
- package/dist/surface/tools/real-input-attempt.d.ts +0 -5
- package/dist/surface/tools/real-input-attempt.js +1 -1
- package/dist/surface/tools/tool-kit.d.ts +5 -0
- package/dist/surface/tools/tool-kit.js +6 -1
- package/dist/surface/tools/tool-surface.js +17 -5
- package/dist/surface/tools/tools.js +68 -29
- package/dist/telemetry/argument-shape.d.ts +0 -2
- package/dist/telemetry/argument-shape.js +0 -4
- package/dist/telemetry/error-class.d.ts +2 -3
- package/dist/telemetry/error-class.js +2 -3
- package/dist/telemetry/feedback-local.d.ts +1 -0
- package/dist/telemetry/feedback-local.js +1 -3
- package/dist/telemetry/feedback.d.ts +1 -1
- package/dist/telemetry/harness-drive.d.ts +0 -2
- package/dist/telemetry/harness-drive.js +0 -4
- package/dist/telemetry/hud-metrics.d.ts +32 -0
- package/dist/telemetry/hud-metrics.js +95 -0
- package/dist/telemetry/session-metrics.d.ts +5 -0
- package/dist/telemetry/session-metrics.js +15 -1
- package/dist/wire-journal.d.ts +34 -0
- package/dist/wire-journal.js +104 -0
- package/dist/workspace-packages.d.ts +0 -2
- package/dist/workspace-packages.js +1 -9
- package/docs/README.md +5 -2
- package/docs/actions.mdx +3 -3
- package/docs/agent-cheatsheet.md +4 -4
- package/docs/architecture.md +6 -6
- package/docs/best-practices.mdx +1 -1
- package/docs/capabilities.mdx +32 -1
- package/docs/cli/cloud.mdx +21 -9
- package/docs/cli/doctor.mdx +4 -0
- package/docs/cli/feedback.mdx +2 -2
- package/docs/cli/gate.mdx +9 -4
- package/docs/cli/init.mdx +1 -0
- package/docs/cli/report.mdx +44 -0
- package/docs/cli/update.mdx +10 -0
- package/docs/cli.mdx +9 -0
- package/docs/desktop.mdx +14 -10
- package/docs/environment.mdx +87 -0
- package/docs/faq.mdx +18 -16
- package/docs/first-drive.md +5 -13
- package/docs/flows.md +32 -2
- package/docs/frameworks.mdx +140 -9
- package/docs/getting-started.md +3 -3
- package/docs/http-transport.md +6 -3
- package/docs/human-control.md +2 -2
- package/docs/index.mdx +16 -6
- package/docs/install-agentic.mdx +37 -1
- package/docs/install-manual.mdx +1 -1
- package/docs/integration-patterns.md +3 -3
- package/docs/multi-agent-testing.md +1 -1
- package/docs/packages/babel-plugin.mdx +1 -1
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/react.mdx +2 -2
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages/vite-plugin.mdx +2 -2
- package/docs/packages.mdx +9 -9
- package/docs/predicates.mdx +176 -7
- package/docs/quickstart.mdx +2 -2
- package/docs/recipes.mdx +2 -2
- package/docs/state-management.mdx +2 -2
- package/docs/system-map.md +1 -1
- package/docs/telemetry.md +1 -1
- package/docs/token-efficiency.md +3 -3
- package/docs/tools/act-and-wait.mdx +1 -0
- package/docs/tools/act-sequence.mdx +1 -1
- package/docs/tools/console.mdx +1 -1
- package/docs/tools/inspect.mdx +1 -1
- package/docs/tools/network.mdx +1 -1
- package/docs/tools/query.mdx +11 -11
- package/docs/tools/sessions.mdx +1 -1
- package/docs/tools/snapshot.mdx +1 -1
- package/docs/tools/state.mdx +1 -1
- package/docs/tools/wait-for.mdx +1 -1
- package/docs/usage.md +8 -7
- package/docs/use-cases.mdx +157 -0
- package/docs/vs-screenshots.mdx +3 -3
- package/docs/what-is-recorded.md +62 -0
- package/docs/what-is-reticle.mdx +17 -20
- package/docs/why-reticle.mdx +14 -21
- package/package.json +11 -10
- package/dist/judgement/capsule/minimize.d.ts +0 -12
- package/dist/judgement/capsule/minimize.js +0 -22
- package/dist/judgement/outcome/predicate-to-expect.d.ts +0 -44
- package/dist/judgement/outcome/predicate-to-expect.js +0 -148
- package/dist/judgement/phenomena/phenomena.d.ts +0 -26
- package/dist/judgement/phenomena/phenomena.js +0 -101
- package/dist/language/flows/flow-report.d.ts +0 -22
- package/dist/language/flows/flow-report.js +0 -106
package/docs/predicates.mdx
CHANGED
|
@@ -7,7 +7,7 @@ icon: filter
|
|
|
7
7
|
|
|
8
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
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 [`
|
|
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_assert { action: "wait" }`](/tools/wait-for).
|
|
11
11
|
|
|
12
12
|
Every predicate is `{ kind, ...fields }`.
|
|
13
13
|
|
|
@@ -35,6 +35,7 @@ Take the advice. `element` and `text` are convenient and weak; `signal`, `state`
|
|
|
35
35
|
| `element` / `text` | weak | Something rendered. A mock renders too |
|
|
36
36
|
| `animation` | weak | An animation ran or finished |
|
|
37
37
|
| `settled` | timing | The page went quiet. A precondition, not a proof |
|
|
38
|
+
| `compare` | its sides | Two readings agree: the stronger side's grade |
|
|
38
39
|
|
|
39
40
|
## The leaves
|
|
40
41
|
|
|
@@ -48,7 +49,17 @@ Take the advice. `element` and `text` are convenient and weak; `signal`, `state`
|
|
|
48
49
|
}
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
`query` accepts `role`, `name`, `text`, `label`, `placeholder`, `testid`, `alt`, `scope`. `state` is one of `visible`, `hidden`, `enabled`, `disabled`, `checked`, `expanded`, `focused`, `present`.
|
|
52
|
+
`query` accepts `role`, `name`, `text`, `label`, `placeholder`, `testid`, `alt`, `value`, `by`, `component`, `source`, `scope`, `self`, `attrs`. `state` is one of `visible`, `hidden`, `enabled`, `disabled`, `checked`, `expanded`, `pressed`, `focused`, `inViewport`, `present`.
|
|
53
|
+
|
|
54
|
+
`value` is the one worth knowing about. `{ "role": "textbox", "name": "Amount" }` collapses to "a textbox called Amount exists", which passes against an empty field; adding `value` is what makes it an assertion about what the user will actually send. `by` selects the locator strategy explicitly, `component` and `source` match React component identity and source location, and `self` restricts a scoped match to the scope element itself rather than its descendants.
|
|
55
|
+
|
|
56
|
+
`attrs` is a list of attribute **names**, such as `["href", "data-status"]`. It **projects** those attributes onto each match so you can read them back, and it does not filter. `["data-status=complete"]` is refused: it would otherwise look up an attribute literally named `data-status=complete`, find none, and leave the predicate resting on the locator alone, which is a green for any value the element happens to hold.
|
|
57
|
+
|
|
58
|
+
To READ the values, call `reticle_look { action: "find" }` with `attrs` and look at the `attrs` map on each match. To ASSERT one: a form field's value goes in the query as `value`, while a state expressed as a data attribute is better asserted as the consequence it stands for, such as a `signal`, a `net` call, or the text the user actually sees.
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{ "kind": "element", "query": { "role": "textbox", "name": "Amount", "value": "274.58" } }
|
|
62
|
+
```
|
|
52
63
|
|
|
53
64
|
Add `"absent": true` to assert something is **gone**. The predicate for a removal, a dismissed toast, or a regression check.
|
|
54
65
|
|
|
@@ -62,6 +73,26 @@ Add `"absent": true` to assert something is **gone**. The predicate for a remova
|
|
|
62
73
|
|
|
63
74
|
Use `scope` to restrict the search to a CSS selector or element ref. If Reticle reports that the text is split across the scope's children, retry its generated predicate with `"self": true`; that checks the scope root's combined subtree text while still enforcing `contains`.
|
|
64
75
|
|
|
76
|
+
`satisfies` asserts a PROPERTY of the rendered text instead of its exact bytes: the same shape and the same properties [`state` accepts](#satisfies-asserting-a-property-instead-of-exact-bytes), applied where the user actually reads the value:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"kind": "text",
|
|
81
|
+
"scope": "[data-testid=\"release-note\"]",
|
|
82
|
+
"self": true,
|
|
83
|
+
"satisfies": { "property": "nonEmpty" }
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
That is the assertion a generated feature can pass twice. `contains` demands bytes a model's output does not repeat, so it either fails on correct output or gets weakened to a word so common it proves nothing; "the box rendered and there is something in it" is the failure the feature actually has.
|
|
88
|
+
|
|
89
|
+
Two rules, both refusals rather than guesses:
|
|
90
|
+
|
|
91
|
+
- **One of `contains` or `satisfies` is required.** With neither, the predicate matches any element with any text and cannot fail.
|
|
92
|
+
- **`satisfies` on its own needs a `scope`** (the element whose text is read, with `self: true` for the root's own subtree). Without one the locator is every element on the page, and the property would run against whichever matched first.
|
|
93
|
+
|
|
94
|
+
Both may be given, and then both must hold. `satisfies` narrows, it never excuses.
|
|
95
|
+
|
|
65
96
|
### net
|
|
66
97
|
|
|
67
98
|
```json
|
|
@@ -88,7 +119,7 @@ Two fields read the RESPONSE BODY, so a request that succeeded with the wrong nu
|
|
|
88
119
|
{ "kind": "net", "urlContains": "/api/job", "bodyMatches": { "status": "completed" } }
|
|
89
120
|
```
|
|
90
121
|
|
|
91
|
-
`bodyMatches` is a
|
|
122
|
+
`bodyMatches` is a field match, keyed like `signal.dataMatches` and taking the same operators (`*`, `$gte`, `$contains`). A key can be a dotted path into a wrapped answer: `{ "data.status": "completed" }`, `{ "items.0.id": "a1" }`, `{ "items.length": 3 }`. A nested object written out as a value is compared whole, so use the path to match one field inside it. Reach for it whenever the value is an enum, because that is the case a substring cannot decide: `bodyContains: "completed"` passes on a body carrying `"completedAt": null`, where the needle matched the KEY and the job was still queued. `completed`/`completedAt`, `success`/`successRate`, `sent`/`unsent` all have that shape.
|
|
92
123
|
|
|
93
124
|
Both require body capture, `reticle({ captureNetworkBodies: true })`, and say so when the body was never recorded rather than reporting an ordinary mismatch.
|
|
94
125
|
|
|
@@ -131,7 +162,7 @@ A passing route verdict quotes the transition itself:
|
|
|
131
162
|
{ "kind": "signal", "name": "webhook:received", "dataMatches": { "provider": "stripe", "id": "*" } }
|
|
132
163
|
```
|
|
133
164
|
|
|
134
|
-
The app emitted a named signal via `reticle.signal()`. `dataMatches`
|
|
165
|
+
The app emitted a named signal via `reticle.signal()`. `dataMatches` matches field by field, a key can be a dotted path (`"order.total"`), and `*` means "present, any value".
|
|
135
166
|
|
|
136
167
|
`count` works here exactly as it does on `net`:
|
|
137
168
|
|
|
@@ -174,6 +205,72 @@ Omit `equals` entirely to assert presence. A real `$length` check against this f
|
|
|
174
205
|
|
|
175
206
|
Note `grade: state` in the `because` line, and that the evidence collapses each row to a size marker rather than returning forty objects.
|
|
176
207
|
|
|
208
|
+
#### satisfies: asserting a property instead of exact bytes
|
|
209
|
+
|
|
210
|
+
`equals` cannot express output that is right differently every run. A generated summary, a classification, a computed total: each is correct without being predictable, and an assertion that demands exact bytes either fails on correct output or gets weakened until it proves nothing. `satisfies` is the other half of `state`, and it stays decidable here with no model and no network.
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{ "kind": "state", "store": "app", "path": "summary.text", "satisfies": { "property": "nonEmpty" } }
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
| `property` | Extra fields | Holds when |
|
|
217
|
+
| --- | --- | --- |
|
|
218
|
+
| `nonEmpty` | none | it produced something at all, the honest floor for any output |
|
|
219
|
+
| `oneOf` | `values` | it landed inside the allowed set, exact membership, no coercion |
|
|
220
|
+
| `withinTolerance` | `of`, `tolerance` | a number near enough to the expected one, inclusive on the bound |
|
|
221
|
+
| `matchesPattern` | `pattern` | its string form matches that regular expression |
|
|
222
|
+
| `type` | `is` | it is that kind of thing; `array` and `object` are distinguished, which `typeof` cannot do |
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"kind": "allOf",
|
|
227
|
+
"predicates": [
|
|
228
|
+
{
|
|
229
|
+
"kind": "state",
|
|
230
|
+
"path": "invoice.total",
|
|
231
|
+
"satisfies": { "property": "withinTolerance", "of": 274.58, "tolerance": 0.01 }
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"kind": "state",
|
|
235
|
+
"path": "invoice.status",
|
|
236
|
+
"satisfies": { "property": "oneOf", "values": ["draft", "sent"] }
|
|
237
|
+
},
|
|
238
|
+
{ "kind": "state", "path": "invoice.lines", "satisfies": { "property": "type", "is": "array" } }
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
`equals` and `satisfies` may both be given, and then both must hold. A failing check always says what it saw, because an assertion that reports only `false` sends you back for another call to find out why, and that round trip is most of what a verdict costs.
|
|
244
|
+
|
|
245
|
+
#### The relative properties: comparing with before
|
|
246
|
+
|
|
247
|
+
The properties above read the present tense. Four read the past one. They compare this value with the reading taken **before the action**, which is the window a verdict is already scoped to:
|
|
248
|
+
|
|
249
|
+
| property | holds when |
|
|
250
|
+
| ----------- | --------------------------- |
|
|
251
|
+
| `unchanged` | the value is the one it was |
|
|
252
|
+
| `changed` | it is not |
|
|
253
|
+
| `increased` | the number went up |
|
|
254
|
+
| `decreased` | the number went down |
|
|
255
|
+
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"kind": "state",
|
|
259
|
+
"path": "cart.total",
|
|
260
|
+
"satisfies": { "property": "decreased", "by": { "op": "equals", "value": 12 } }
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
`increased` and `decreased` take an optional `by`, in the protocol's own comparison vocabulary: `op` is `equals`, `at-least` or `at-most`, with an optional `tolerance` on all three. Without `by`, any movement in the right direction holds.
|
|
265
|
+
|
|
266
|
+
**Give a money assertion a tolerance.** `100 - 88.13` is `11.870000000000005`, so `{ "op": "equals", "value": 11.87 }` misses by a rounding error and reads as the app charging the wrong amount. `"tolerance": 0.01` is the fix; the failure message says so when the miss is that small.
|
|
267
|
+
|
|
268
|
+
A number is read out of displayed text, so `"$1,234.50"` compares as `1234.5`. A reading with no digits in it is **not** zero. `"sold out"` reports that nothing was subtracted rather than pretending the total fell to nothing.
|
|
269
|
+
|
|
270
|
+
These only work where a before-reading was taken, which today is `reticle_act_and_wait`. Asked anywhere else they report `unknown` with "no before-reading was taken", never a failure, because nothing was compared and a `false` would blame the app for a reading nobody took. For the same reason a relative property is never `already_true`: `unchanged` is trivially true against a baseline taken a microsecond earlier, so the pre-action check skips it and lets the action decide.
|
|
271
|
+
|
|
272
|
+
A saved flow cannot keep one yet: `FlowExpect` has no slot for `satisfies`, and rather than record a weaker assertion than the one you made, the converter records none.
|
|
273
|
+
|
|
177
274
|
A miss quotes the real value back at you:
|
|
178
275
|
|
|
179
276
|
```json
|
|
@@ -207,6 +304,77 @@ This is the predicate that catches a UI-versus-store desync. A deploy that only
|
|
|
207
304
|
|
|
208
305
|
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.
|
|
209
306
|
|
|
307
|
+
### compare
|
|
308
|
+
|
|
309
|
+
```json
|
|
310
|
+
{
|
|
311
|
+
"kind": "compare",
|
|
312
|
+
"left": { "from": "text", "scope": "#refunded" },
|
|
313
|
+
"right": { "from": "net", "urlContains": "/api/refund", "path": "refunded" },
|
|
314
|
+
"as": "number"
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Two observed values must agree, and you do not have to know either one. Every other predicate checks one reading against a value you write down. That cannot catch a page that echoes the user's input instead of the server's answer unless you already know the right answer: a refund of `1187.01` that the server read as paise answered `{"refunded":11.87}`, the page showed `₹1,187.01`, and every other channel was green.
|
|
319
|
+
|
|
320
|
+
Each side reads one value from a source:
|
|
321
|
+
|
|
322
|
+
| `from` | Fields | Reads |
|
|
323
|
+
| --- | --- | --- |
|
|
324
|
+
| `net` | `urlContains`, `path`, `method?`, `body?` | A field of the LAST matching call's body. `body` is `response` (default) or `request` |
|
|
325
|
+
| `signal` | `name`, `path` | A field of the last `name` signal's payload |
|
|
326
|
+
| `state` | `path`, `store?` | A store value, as `state` reads it |
|
|
327
|
+
| `text` | `scope` | The whole text of the element `scope` names (a CSS selector) |
|
|
328
|
+
|
|
329
|
+
A `path` is a field name or a dotted path into nested data (`data.total`, `items.0.price`), and it must end on a single value, not an object.
|
|
330
|
+
|
|
331
|
+
`as` is `value` (the default) or `number`:
|
|
332
|
+
|
|
333
|
+
- `value` is strict equality. The text `"11.87"` and the number `11.87` are different, and the failure says so and suggests `as: "number"`.
|
|
334
|
+
- `number` reads one number from each side. A JSON number is used as it is; a string contributes its only number, so `"Refunded ₹1,187.01"` reads as `1187.01`. A string holding two numbers is unknown rather than guessed at, so scope the text to the one value. Add `tolerance` for a band.
|
|
335
|
+
|
|
336
|
+
A side nobody could read is never a pass. A call that never happened, a missing element or a missing field fails. A body that was not recorded, was truncated before the field, or had the field redacted is unknown. Comparing a source with itself is refused before it runs, because it would agree on every page.
|
|
337
|
+
|
|
338
|
+
Two live sides (`text`, `state`) are also read before the action. If they already agreed, the result is `already_true`, because their agreement says nothing about what the action did. Put a `net` or `signal` side in the comparison to tie it to the action.
|
|
339
|
+
|
|
340
|
+
Comparing request with response on the same call is legitimate, and it is the direct check for a unit mismatch:
|
|
341
|
+
|
|
342
|
+
```json
|
|
343
|
+
{
|
|
344
|
+
"kind": "compare",
|
|
345
|
+
"left": { "from": "net", "urlContains": "/api/refund", "body": "request", "path": "amount" },
|
|
346
|
+
"right": { "from": "net", "urlContains": "/api/refund", "path": "refunded" },
|
|
347
|
+
"as": "number"
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Asserting a failure on purpose
|
|
352
|
+
|
|
353
|
+
A fault-injection test (mock a 500, check the error UI) is asserting that a request **fails**. Say so in the predicate, or the verdict will disagree with you:
|
|
354
|
+
|
|
355
|
+
```json
|
|
356
|
+
{
|
|
357
|
+
"kind": "allOf",
|
|
358
|
+
"predicates": [
|
|
359
|
+
{ "kind": "text", "contains": "Something went wrong" },
|
|
360
|
+
{ "kind": "net", "method": "POST", "urlContains": "/api/save", "status": 503 }
|
|
361
|
+
]
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Asserting only the error text is not enough, and this is the most common way to get a confusing `verified: "no"`. Reticle watches for "the UI moved forward while a request failed", a swallowed rejection, and one of the most expensive bugs it catches. A fault-injection test looks **exactly** like that bug from the outside: a request failed, the screen changed, nothing said that was the plan.
|
|
366
|
+
|
|
367
|
+
Naming the failing call is what tells the two apart. A `net` clause with `status` of 400 or more, or `"ok": false`, which is the honest field for IPC, where there is no status code, marks that call as expected to fail, and a failure there stops being a disagreement.
|
|
368
|
+
|
|
369
|
+
<Warning>
|
|
370
|
+
Declare it in the top level or inside `allOf`. A declaration inside `anyOf` is deliberately
|
|
371
|
+
ignored: only one branch of an `anyOf` has to hold, so honouring it would suppress a real
|
|
372
|
+
contradiction on the strength of a branch that never ran. `not` is ignored for the same reason in
|
|
373
|
+
reverse: `not { net status: 500 }` asserts the call did *not* fail.
|
|
374
|
+
</Warning>
|
|
375
|
+
|
|
376
|
+
One case is inferred for you: an `element` or `text` clause naming an access denial ("not authorised", "sign in to continue") marks the auth statuses as expected, because a denial screen is a correct outcome rather than a fault. Everything else has to be named.
|
|
377
|
+
|
|
210
378
|
## Combinators
|
|
211
379
|
|
|
212
380
|
```json
|
|
@@ -226,7 +394,8 @@ Network and DOM idle. This is the deterministic replacement for a fixed sleep, n
|
|
|
226
394
|
```
|
|
227
395
|
|
|
228
396
|
<Warning>
|
|
229
|
-
A combinator needs its own `kind`, and the children go in `predicates
|
|
397
|
+
A combinator needs its own `kind`, and the children go in `predicates`, or in `of`, which is
|
|
398
|
+
accepted as an alias for it. `not` takes a single child in `predicate`, or in `of`. A bare
|
|
230
399
|
`{ "allOf": [ … ] }` does not parse, and neither does `{ "kind": "allOf", "allOf": [ … ] }`.
|
|
231
400
|
|
|
232
401
|
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.
|
|
@@ -265,9 +434,9 @@ On a failure each child reports separately too, so you can see which one broke:
|
|
|
265
434
|
| Field | Where | What it does |
|
|
266
435
|
| --- | --- | --- |
|
|
267
436
|
| `timeout_ms` | `assert`, `wait_for`, `act_and_wait` | Wait up to N ms for it to become true |
|
|
268
|
-
| `since` | `net` and `
|
|
437
|
+
| `since` | `net`, `route`, `console`, `animation` and `signal` leaves, and the call itself | Only consider events after this cursor |
|
|
269
438
|
|
|
270
|
-
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.
|
|
439
|
+
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. That applies to every one of the five kinds above, not only to network calls: an unscoped `signal` matches a fire from an earlier action just as readily.
|
|
271
440
|
|
|
272
441
|
<Card title="Where predicates get used" icon="circle-check" href="/tools/act-and-wait">
|
|
273
442
|
Naming the consequence before the action is the whole point.
|
package/docs/quickstart.mdx
CHANGED
|
@@ -61,7 +61,7 @@ The `reticle_*` tools are already there. In your project directory:
|
|
|
61
61
|
npx @reticlehq/server init
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
Like `npm init` or `git init`, this is the per-project step: it installs the SDK, wires it into your app's entry point,
|
|
64
|
+
Like `npm init` or `git init`, this is the per-project step: it installs the SDK, wires it into your app's entry point, starts your dev server and then opens your app to prove a session connected. It is idempotent. Run it twice and the second run reports what was already done rather than doing it again.
|
|
65
65
|
|
|
66
66
|
Step 1 is once per machine. This step is once per project.
|
|
67
67
|
|
|
@@ -236,7 +236,7 @@ That third state is the one that matters. Most tools have two outcomes and quiet
|
|
|
236
236
|
<Accordion title="How long does it actually take to install Reticle?">
|
|
237
237
|
Two commands, and no restart if you run them in order. The installer registers the MCP server
|
|
238
238
|
with every agent on the machine, so opening your agent afterwards is all that is needed. Then
|
|
239
|
-
`npx @reticlehq/server init` wires the project
|
|
239
|
+
`npx @reticlehq/server init` wires the project, starts your dev server and opens the app for you. The MCP
|
|
240
240
|
registration is global, so every later project starts with the tools already there.
|
|
241
241
|
</Accordion>
|
|
242
242
|
|
package/docs/recipes.mdx
CHANGED
|
@@ -5,7 +5,7 @@ description: Worked examples for the situations you are actually in, with the re
|
|
|
5
5
|
icon: book-open
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Each recipe is a situation, the call that verifies it, and what came back. Every response on this page was captured from a running app on 2026-08-16. Refs like `e605` are session-specific: take your own from [`
|
|
8
|
+
Each recipe is a situation, the call that verifies it, and what came back. Every response on this page was captured from a running app on 2026-08-16. Refs like `e605` are session-specific: take your own from [`reticle_look { action: "find" }`](/tools/query) before copying a call.
|
|
9
9
|
|
|
10
10
|
## "Did the login actually work?"
|
|
11
11
|
|
|
@@ -135,7 +135,7 @@ The right predicate was sitting in the response the whole time:
|
|
|
135
135
|
|
|
136
136
|
## "Nothing matched, and I do not know why"
|
|
137
137
|
|
|
138
|
-
An empty `
|
|
138
|
+
An empty `reticle_look { action: "find" }` result tells you what _is_ there:
|
|
139
139
|
|
|
140
140
|
```json
|
|
141
141
|
{
|
|
@@ -179,7 +179,7 @@ worker.onmessage = (e) => push(e.data);
|
|
|
179
179
|
{ "store": "app", "path": "cart.items.0.qty" }
|
|
180
180
|
```
|
|
181
181
|
|
|
182
|
-
`
|
|
182
|
+
`reticle_look { action: "state" }` walks a dot-path with numeric array indices. `depth` collapses anything deeper to a size marker, which is the difference between a cheap look and a very expensive one on a large store.
|
|
183
183
|
|
|
184
184
|
You can also assert on it directly, which is stronger than reading it afterwards:
|
|
185
185
|
|
|
@@ -190,7 +190,7 @@ You can also assert on it directly, which is stronger than reading it afterwards
|
|
|
190
190
|
That catches a UI-versus-store desync, a deploy that only looks shipped, deterministically and with no model involved.
|
|
191
191
|
|
|
192
192
|
<CardGroup cols={2}>
|
|
193
|
-
<Card title="
|
|
193
|
+
<Card title='reticle_look { action: "state" }' icon="database" href="/tools/state">
|
|
194
194
|
Arguments, depth control, and reading a component's hook state.
|
|
195
195
|
</Card>
|
|
196
196
|
<Card title="Instrument your app" icon="wrench" href="/instrumentation">
|
package/docs/system-map.md
CHANGED
|
@@ -66,7 +66,7 @@ These three were each wrong by the time somebody checked: the table read 68 / 48
|
|
|
66
66
|
|
|
67
67
|
| Token | Produced by | Consumed by | Invalidated by |
|
|
68
68
|
| --- | --- | --- | --- |
|
|
69
|
-
| `sessionId` | `
|
|
69
|
+
| `sessionId` | `reticle_session { action: "list" }`, HELLO | ~every tool | tab close; **survives reload** via `sessionStorage` |
|
|
70
70
|
| `ref` (`e115`) | `snapshot`, `query`, `scroll_to` | `inspect`, `act`, `act_and_wait`, `state` | any re-render; refuses cleanly rather than clicking the new occupant |
|
|
71
71
|
| `actionId` (`a2`) | `act`, `act_and_wait` | `observe`, `network`, `console` filters | window expiry |
|
|
72
72
|
| `since` cursor | every read | every read | ring-buffer eviction |
|
package/docs/telemetry.md
CHANGED
|
@@ -122,7 +122,7 @@ Reticle verifies apps for AI agents, and for a long time it had no way to hear w
|
|
|
122
122
|
|
|
123
123
|
| Who | How | What it carries |
|
|
124
124
|
| --- | --- | --- |
|
|
125
|
-
| Your agent | Calls the `
|
|
125
|
+
| Your agent | Calls the `reticle_session { action: "feedback" }` MCP tool (after a failure, **or to ask for a feature**) | What it wrote: an analysis, or a request with the goal behind it, what would improve, and how it works around the gap today. Plus the model it is running, which it tells us because MCP cannot |
|
|
126
126
|
| You | Run `npx @reticlehq/server feedback [--rating 1-5] [--bug] "your words"` | Your words and your rating |
|
|
127
127
|
| Your agent, before Reticle works | Runs `reticle feedback --agent --kind <bug\|gap\|ambiguity\|feature_request\|improvement> "what happened"` | The same words, filed from the shell. This exists for the phase where there is no daemon and no MCP tools (a failed `init`, half-finished wiring), which is the failure we are otherwise never told about |
|
|
128
128
|
|
package/docs/token-efficiency.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Why asking narrow questions costs a fraction of feeding the whole a
|
|
|
4
4
|
icon: coins
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
A full Reticle verify loop costs about **100 tokens**, against roughly **7,300** for a full-tree accessibility snapshot on the same page: about 73x leaner, because Reticle asks a narrow question instead of returning the page. If you re-look after an action with `
|
|
7
|
+
A full Reticle verify loop costs about **100 tokens**, against roughly **7,300** for a full-tree accessibility snapshot on the same page: about 73x leaner, because Reticle asks a narrow question instead of returning the page. If you re-look after an action with `reticle_look({ action: "page", diff: true })`, the re-read costs about 99% less again.
|
|
8
8
|
|
|
9
9
|
Agent browser tools that feed the **whole accessibility tree** to the model every step get expensive fast. Playwright MCP's own ecosystem notes its snapshots _"can exceed 50,000 tokens on complex pages,"_ with a _typical task ~114,000 tokens through MCP._ Reticle is built to ask **narrow questions** instead, so the per-interaction cost stays tiny.
|
|
10
10
|
|
|
@@ -28,7 +28,7 @@ Measured against the bench dashboard (`apps/bench-app`) **with a 1,000-item list
|
|
|
28
28
|
|
|
29
29
|
## Diffed snapshots: pay once, then only for changes
|
|
30
30
|
|
|
31
|
-
After the first snapshot, pass `
|
|
31
|
+
After the first snapshot, pass `reticle_look({ action: "page", diff: true })` to get back **only what changed** since your last look of the same scope/mode (`mode:delta` with added/removed lines, or `mode:unchanged`). A route change auto-resets to a full snapshot, so you never read a misleading cross-page diff.
|
|
32
32
|
|
|
33
33
|
Measured on a representative 150-row dashboard (the shipped regression benchmark `server/src/surface/tools/snapshot-cost.test.ts`, char/4 proxy):
|
|
34
34
|
|
|
@@ -40,7 +40,7 @@ Measured on a representative 150-row dashboard (the shipped regression benchmark
|
|
|
40
40
|
|
|
41
41
|
**~99% fewer tokens** to re-look after an action, and because a `delta` carries no stale full tree, it also removes the 60K to 80K-token stale-context buildup that makes long-running agents start hallucinating selectors that no longer exist.
|
|
42
42
|
|
|
43
|
-
Every `
|
|
43
|
+
Every `reticle_look { action: "page" }`/`reticle_look { action: "find" }` result also carries `cost:{ bytes, tokens }` (estimated) so you can **re-scope before reading** a large body (`mode:interactive`/`status`, a tighter `scope`, or a narrower `query`) instead of paying for it first.
|
|
44
44
|
|
|
45
45
|
## The other tax: tool schemas, paid on every request
|
|
46
46
|
|
|
@@ -162,6 +162,7 @@ A verification tool that cannot describe the quality of its own evidence is aski
|
|
|
162
162
|
| `predicate` | Alias for `until`, the name `assert` and `wait_for` use |
|
|
163
163
|
| `timeout_ms` | Maximum wait, defaulting to 4000. `0` evaluates the predicate once, no wait |
|
|
164
164
|
| `refuseWhenThrottled` | Throw rather than act on a throttled tab |
|
|
165
|
+
| `durable` | After a `yes`, reload the page and require the element, text and state parts of `until` to hold again. A consequence that is gone after the reload turns the verdict into `no`: the UI advanced, the change did not persist |
|
|
165
166
|
|
|
166
167
|
Omitting `until` waits for network and DOM idle, which is the deterministic replacement for a fixed sleep. To assert a consequence **and** settle, combine them:
|
|
167
168
|
|
|
@@ -5,7 +5,7 @@ icon: layer-group
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<Warning>
|
|
8
|
-
**reticle_act_sequence is not advertised.** The default surface is the merged
|
|
8
|
+
**reticle_act_sequence is not advertised.** The default surface is the merged ten, so an agent does not see
|
|
9
9
|
this name. Call **`reticle_act { steps: [...] }`** instead: the same call, routed on the steps array. Everything below describes what that call does;
|
|
10
10
|
only the spelling changed. A call to the old name is answered with the new one, but an
|
|
11
11
|
`allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
|
package/docs/tools/console.mdx
CHANGED
|
@@ -5,7 +5,7 @@ icon: terminal
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<Warning>
|
|
8
|
-
**reticle_console is not advertised.** The default surface is the merged
|
|
8
|
+
**reticle_console is not advertised.** The default surface is the merged ten, so an agent does not see
|
|
9
9
|
this name. Call **`reticle_observe { action: "console" }`** instead. Everything below describes what that call does;
|
|
10
10
|
only the spelling changed. A call to the old name is answered with the new one, but an
|
|
11
11
|
`allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
|
package/docs/tools/inspect.mdx
CHANGED
|
@@ -5,7 +5,7 @@ icon: crosshairs
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<Warning>
|
|
8
|
-
**reticle_inspect is not advertised.** The default surface is the merged
|
|
8
|
+
**reticle_inspect is not advertised.** The default surface is the merged ten, so an agent does not see
|
|
9
9
|
this name. Call **`reticle_look { action: "element" }`** instead. Everything below describes what that call does;
|
|
10
10
|
only the spelling changed. A call to the old name is answered with the new one, but an
|
|
11
11
|
`allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
|
package/docs/tools/network.mdx
CHANGED
|
@@ -5,7 +5,7 @@ icon: satellite-dish
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<Warning>
|
|
8
|
-
**reticle_network is not advertised.** The default surface is the merged
|
|
8
|
+
**reticle_network is not advertised.** The default surface is the merged ten, so an agent does not see
|
|
9
9
|
this name. Call **`reticle_observe { action: "network" }`** instead. Everything below describes what that call does;
|
|
10
10
|
only the spelling changed. A call to the old name is answered with the new one, but an
|
|
11
11
|
`allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
|
package/docs/tools/query.mdx
CHANGED
|
@@ -5,7 +5,7 @@ icon: magnifying-glass
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<Warning>
|
|
8
|
-
**reticle_query is not advertised.** The default surface is the merged
|
|
8
|
+
**reticle_query is not advertised.** The default surface is the merged ten, so an agent does not see
|
|
9
9
|
this name. Call **`reticle_look { action: "find" }`** instead. Everything below describes what that call does;
|
|
10
10
|
only the spelling changed. A call to the old name is answered with the new one, but an
|
|
11
11
|
`allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
|
|
@@ -52,16 +52,16 @@ Seven buttons, each with a `ref` to act on and a source file to open, for 236 to
|
|
|
52
52
|
|
|
53
53
|
## Arguments
|
|
54
54
|
|
|
55
|
-
| Argument
|
|
56
|
-
|
|
|
57
|
-
| `by`
|
|
58
|
-
| `value`
|
|
59
|
-
| `name`
|
|
60
|
-
| `scope`
|
|
61
|
-
| `limit`
|
|
62
|
-
| `count_only` | Return just `{ count }`, around 30x smaller
|
|
63
|
-
| `attrs`
|
|
64
|
-
| `self`
|
|
55
|
+
| Argument | What it does |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| `by` | `role` · `text` · `label` · `placeholder` · `testid` · `alt` · `component` |
|
|
58
|
+
| `value` | The query value for that strategy |
|
|
59
|
+
| `name` | Accessible-name filter. Narrows a broad `role` query |
|
|
60
|
+
| `scope` | CSS selector or ref, to search inside a subtree |
|
|
61
|
+
| `limit` | Cap the descriptors returned. Cuts tokens hard on broad queries |
|
|
62
|
+
| `count_only` | Return just `{ count }`, around 30x smaller |
|
|
63
|
+
| `attrs` | Attribute NAMES to project per match, e.g. `['href']` to inventory links. The values come back in each match's `attrs` map. It does not filter, so `name=value` is refused |
|
|
64
|
+
| `self` | Return the `scope` element itself instead of searching inside it |
|
|
65
65
|
|
|
66
66
|
Every argument also has a predicate spelling, so `{ "role": "button" }` works as shorthand for `{ "by": "role", "value": "button" }`.
|
|
67
67
|
|
package/docs/tools/sessions.mdx
CHANGED
|
@@ -5,7 +5,7 @@ icon: list-check
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<Warning>
|
|
8
|
-
**reticle_sessions is not advertised.** The default surface is the merged
|
|
8
|
+
**reticle_sessions is not advertised.** The default surface is the merged ten, so an agent does not see
|
|
9
9
|
this name. Call **`reticle_session { action: "list" }`** instead. Everything below describes what that call does;
|
|
10
10
|
only the spelling changed. A call to the old name is answered with the new one, but an
|
|
11
11
|
`allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
|
package/docs/tools/snapshot.mdx
CHANGED
|
@@ -5,7 +5,7 @@ icon: camera
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<Warning>
|
|
8
|
-
**reticle_snapshot is not advertised.** The default surface is the merged
|
|
8
|
+
**reticle_snapshot is not advertised.** The default surface is the merged ten, so an agent does not see
|
|
9
9
|
this name. Call **`reticle_look { action: "page" }`** instead. Everything below describes what that call does;
|
|
10
10
|
only the spelling changed. A call to the old name is answered with the new one, but an
|
|
11
11
|
`allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
|
package/docs/tools/state.mdx
CHANGED
|
@@ -5,7 +5,7 @@ icon: database
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<Warning>
|
|
8
|
-
**reticle_state is not advertised.** The default surface is the merged
|
|
8
|
+
**reticle_state is not advertised.** The default surface is the merged ten, so an agent does not see
|
|
9
9
|
this name. Call **`reticle_look { action: "state" }`** instead. Everything below describes what that call does;
|
|
10
10
|
only the spelling changed. A call to the old name is answered with the new one, but an
|
|
11
11
|
`allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
|
package/docs/tools/wait-for.mdx
CHANGED
|
@@ -5,7 +5,7 @@ icon: hourglass
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<Warning>
|
|
8
|
-
**reticle_wait_for is not advertised.** The default surface is the merged
|
|
8
|
+
**reticle_wait_for is not advertised.** The default surface is the merged ten, so an agent does not see
|
|
9
9
|
this name. Call **`reticle_assert { action: "wait" }`** instead. Everything below describes what that call does;
|
|
10
10
|
only the spelling changed. A call to the old name is answered with the new one, but an
|
|
11
11
|
`allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
|
package/docs/usage.md
CHANGED
|
@@ -4,7 +4,7 @@ description: 'The full reference and cookbook: every tool, flag, and workflow, w
|
|
|
4
4
|
icon: book
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Every Reticle drive is the same four steps: **look** (`reticle_look { action: "page" }` / `reticle_look { action: "find" }`), **act** (`reticle_act` / `reticle_act { steps: [...] }`), **observe** (`reticle_observe` / `reticle_observe { action: "network" }` / `reticle_look { action: "state" }`), **assert** (`reticle_assert`).
|
|
7
|
+
Every Reticle drive is the same four steps: **look** (`reticle_look { action: "page" }` / `reticle_look { action: "find" }`), **act** (`reticle_act` / `reticle_act { steps: [...] }`), **observe** (`reticle_observe` / `reticle_observe { action: "network" }` / `reticle_look { action: "state" }`), **assert** (`reticle_assert`). Verdicts come from `reticle_act_and_wait`, `reticle_assert`, and `reticle_act { steps }` when a step declares `expect`, so a run that ends anywhere else proved nothing. This page is the full reference for every tool, predicate, action and flag in that loop.
|
|
8
8
|
|
|
9
9
|
If you haven't set up Reticle yet, start with [Getting Started](getting-started.md).
|
|
10
10
|
|
|
@@ -183,7 +183,7 @@ The timeline + summary of what happened.
|
|
|
183
183
|
|
|
184
184
|
Act, then wait for a predicate: the whole act→observe→assert loop in one hop.
|
|
185
185
|
|
|
186
|
-
- **args:** `ref`, `action`, `args?`, `until: <predicate>`, `timeout_ms?` (default 4000; 0 = evaluate once), `refuseWhenThrottled?`, `intent?`, `sessionId?`.
|
|
186
|
+
- **args:** `ref`, `action`, `args?`, `until: <predicate>`, `timeout_ms?` (default 4000; 0 = evaluate once), `refuseWhenThrottled?`, `intent?`, `durable?` (after a yes, reload and require `until`'s element/text/state parts to hold again), `sessionId?`.
|
|
187
187
|
- **returns:** `{ effect, verdict, trace, session, warning? }`. `effect` is the action result (`{ ok, ref, action }`), `verdict` is `{ pass, evidence?, failureReason? }`, `trace` is the reaction report of everything the app did after the action, and `session` is the tab-health block `{ lastSeenMs, throttled, focused }` (with a `warning` when throttled). A failing `verdict` still returns `effect` + `trace` so you can see what _did_ happen. The predicate is automatically floored at this act's cursor, so it only matches events the action actually caused.
|
|
188
188
|
|
|
189
189
|
### `reticle_assert { action: "wait" }`
|
|
@@ -200,7 +200,7 @@ Verify a predicate; optionally wait for it.
|
|
|
200
200
|
- **args:** `predicate`, `timeout_ms?` (0 = evaluate once), `since?`, `intent?`, `sessionId?`.
|
|
201
201
|
- Same `since` default as `reticle_assert { action: "wait" }`: scoped to your last act so a stale buffered event can't fake a pass; override with an explicit `since`.
|
|
202
202
|
- **returns:** `{ verified, because, pass, evidence, contradictions?, coverage?, failureReason?, session, warning? }`. On failure includes a **near-miss** (e.g. "found the dialog but not visible", or "no button named 'Submit'; saw: Cancel"). The `session` block `{ lastSeenMs, throttled, focused }` reports tab health on every assert; when throttled a `warning` is attached so you never assert against a tab that is silently no-oping.
|
|
203
|
-
- **`intent` declares what the change was FOR, inline.** Pass a sentence in your own words and it lands in `.reticle/intent
|
|
203
|
+
- **`intent` declares what the change was FOR, inline.** Pass a sentence in your own words and it lands in `.reticle/intent/` (one directory per subject, a flow's name being its subject), the same git-checked ledger `reticle_intent` writes, before the verdict is drawn; a green verdict then marks it proved and names itself as the proof. Pass the **id** of an intent you already declared instead, to point several verdicts at one statement rather than restating it. `reticle_act_and_wait` takes the same argument. Omit it and nothing is written.
|
|
204
204
|
- **Read `verified`, not `pass`.** `pass` says the predicate held; `verified` says whether that means anything. It is `"no"` when a channel contradicts the assertion (a failed write under a green screen, a batch whose body reports per-item failures, a request still in flight), and `"unknown"` when the outcome could not be known yet: a `202 Accepted` that has not reconciled, or a write whose response body was never recorded. `because` names the deciding evidence in one sentence.
|
|
205
205
|
|
|
206
206
|
### `reticle_reconcile`
|
|
@@ -350,7 +350,7 @@ A **predicate** declares what should be true. `reticle_assert` / `reticle_assert
|
|
|
350
350
|
// An element exists / is in a state
|
|
351
351
|
{ "kind": "element", "query": { "role": "dialog", "name": "Confirm" }, "state": "visible" }
|
|
352
352
|
// query supports: role, name, text, label, placeholder, testid, alt, scope
|
|
353
|
-
// state: visible | hidden | enabled | disabled | checked | expanded | focused | present | inViewport
|
|
353
|
+
// state: visible | hidden | enabled | disabled | checked | expanded | pressed | focused | present | inViewport
|
|
354
354
|
// inViewport asserts the element is in the viewport NOW (not just in the DOM), so a scrollIntoView is gradeable
|
|
355
355
|
// add "absent": true to assert it is NOT there (regression / removal)
|
|
356
356
|
|
|
@@ -396,7 +396,7 @@ A `state` assertion is graded as a **consequence** (a wrong element or stale ren
|
|
|
396
396
|
- `timeout_ms` (on `assert`/`wait_for`): wait up to N ms for it to become true.
|
|
397
397
|
- `since` (on `net`/`console` leaves): only consider events after this cursor (from `act`).
|
|
398
398
|
|
|
399
|
-
`dataMatches`
|
|
399
|
+
`dataMatches` matches field by field; a key can be a dotted path (`"order.total"`, `"items.0.id"`), and `*` means "present, any value".
|
|
400
400
|
|
|
401
401
|
---
|
|
402
402
|
|
|
@@ -640,7 +640,7 @@ Capture everything that happens across a span. Useful for "run my whole checkout
|
|
|
640
640
|
|
|
641
641
|
```jsonc
|
|
642
642
|
reticle_record {action:"start"}({ recordingName: "checkout" })
|
|
643
|
-
// …agent performs the flow (reticle_act
|
|
643
|
+
// …agent performs the flow (reticle_act, single or with steps)…
|
|
644
644
|
reticle_record {action:"stop"}({ recordingName: "checkout" })
|
|
645
645
|
// → {
|
|
646
646
|
// recordingName,
|
|
@@ -732,7 +732,7 @@ No, for basic look/act/observe. You'll get better results by adding `data-testid
|
|
|
732
732
|
|
|
733
733
|
### Does it work without React?
|
|
734
734
|
|
|
735
|
-
The core (DOM/network/route/console/animation/snapshot/actions) is framework-agnostic and is gated against a vanilla-TS app.
|
|
735
|
+
The core (DOM/network/route/console/animation/snapshot/actions) is framework-agnostic and is gated against a vanilla-TS app. What is proven for each framework, from driven to a verdict in CI to install-gated to wired but unverified, is listed once in [Frameworks](frameworks.mdx). See also [what Svelte support is and is not](getting-started.md#what-svelte-support-is-and-what-it-is-not).
|
|
736
736
|
|
|
737
737
|
### Can it judge whether my UI looks good?
|
|
738
738
|
|
|
@@ -858,6 +858,7 @@ It does **not** freeze `requestAnimationFrame`/microtasks (React's scheduler kee
|
|
|
858
858
|
"dataMatches": { "count": { "$gte": 1 }, "sections": { "$contains": "hook" } },
|
|
859
859
|
}
|
|
860
860
|
// operators: $gte $lte $gt $lt $contains (array/substring) $length ; "*" = present
|
|
861
|
+
// keys: a field name, or a dotted path into nested data ("order.total", "items.0.id")
|
|
861
862
|
```
|
|
862
863
|
|
|
863
864
|
On a failed signal assert, the result includes a **near-miss**: the signals that _did_ fire with that name + their data. And `reticle_observe`'s summary now includes `domChanged` (in-place text/attribute re-renders, not just added/removed nodes).
|