AgentDeck · Build health

Test Report

The latest master run, in full: what passed, what each check proves and does not prove, and where the gaps are. This is the maintainer's evidence that the build works — not a product analytics dashboard.

Pass · 5,578 tests · commit 369cc8a · generated 2026-10-04 13:45 UTC

Build health

Latest run

What this page ran on the merged master commit. Suites that need a macOS runner or physical boards run elsewhere and are marked as not run here — never counted as passing.

Result

Pass

5,578 tests executed

Passed

5,578

100.00% of executed

Failed

0

across every suite run here

Wall time

1 min 7 s

summed per-file test time

Line coverage

60.4%

TypeScript packages

Vitest

Pass

TypeScript unit, contract and integration tests.

Passed
5,119
Failed
0
Files
341
Time
54.1 s

Coverage + JSON report

Daemon E2E

Pass

The real daemon process, driven from outside.

Passed
10
Failed
0
Files
2
Time
1.7 s

Real daemon process, loopback, temp HOME

Android

Pass

JUnit + Robolectric.

Passed
449
Failed
0
Suites
42
Time
11.1 s

JUnit + Robolectric on ubuntu

Apple (XCTest)

Not run

macOS app and in-process Swift daemon.

Runs in the Apple Tests workflow (macos-26) on apple/** changes; not re-run here

ESP32 Robot

Not run

Flash, boot and serial protocol on real boards.

Physical hardware suite; run in the maintainer's lab, not on GitHub-hosted runners

Transparency

What we verify

Every check the project runs, where it runs, and what it does not prove. Generated from scripts/verification-catalog.json; CI fails if that file stops matching the repository.

Merge policy · checked 2026-09-29

Branch protection on master requires one status check, ESP32 Sim Compile. Every other gate turns a pull request red when it fails but does not stop GitHub from merging it.

unitcontractintegration
Passed here

TypeScript suite (Vitest) + coverage floor

Every PR and push to master · ubuntu-latest

Proves

  • Node/Swift daemon policy mirror drift and common turn-boundary vectors; CLI rejects explicit Swift identity as Node readiness (native policy execution also runs on macOS)
  • bridge, shared, hooks, plugin, plugin-ulanzi, setup and scripts behave as their tests specify (the domain table below counts them)
  • line/function/branch/statement coverage of bridge, shared, plugin and hooks stays above the floor in vitest.config.ts
  • generated Swift/Kotlin/C++ mirrors match their TypeScript source of truth (contract tests)

Does not prove

  • anything about native apps, firmware or real devices
  • macOS-only halves (Swift toolchain, launchd) — those cases are marked skipped on Linux and appear as skipped in this report

npx vitest run --coverage

ci.yml runs

e2e
Passed here

Daemon hub end-to-end

Every PR and push to master · ubuntu-latest

Proves

  • the real agentdeck daemon start process boots in an empty HOME and answers /health
  • Claude hook events posted to /hooks/* drive the WebSocket state stream (idle → processing → idle)
  • the exact hook shell snippet the installer writes finds the daemon through daemon.json and delivers events
  • PreToolUse and Stop hooks answer immediately when no device can approve, so Claude never stalls
  • the timeline is persisted to timeline.json and replayed to a new client after a restart
  • SIGTERM shuts the daemon down on its own and withdraws daemon.json
  • loopback posture: the port is closed on the host's LAN address

Does not prove

  • a real Claude/Codex process (events are posted with the documented payload shape)
  • LAN pairing and default-deny from another machine (every same-host address is trusted by design; the gate itself is unit-tested in http-auth-gate.test.ts)
  • device modules (all disabled with --local)

pnpm test:e2e

ci.yml runs

e2ehardware
Informational

Sandboxed Swift Codex live acceptance

Opt-in after deploying the Swift app · Maintainer Mac running the signed sandboxed app

Proves

  • A verified sandboxed Swift process accepts Codex lifecycle HTTP events and broadcasts matching WebSocket roster states
  • Late OTel cannot override hook ownership, prevent real-time terminal expiry, or resurrect an expired hook row
  • OTel-only and notify-plus-OTel fallback and turn-ID guards remain functional

Does not prove

  • Real Codex CLI emission, interactive trust, or installer configuration preservation (events are synthetic)
  • App Store distribution, Kiro folder consent/revocation, OpenCode SSE reconnect, or physical display pixels
  • Unattended hosted CI: requires an explicitly selected running Mac app and leaves labeled synthetic timeline entries

node scripts/verify-swift-codex-live.mjs --port PORT --pid PID --app /path/AgentDeck.app

Lab only — results are not recorded here

staticcontract
Fails CI

Build + type check + version and protocol drift

Every PR and push to master · ubuntu-latest

Proves

  • every workspace package compiles under strict TypeScript
  • target versions agree with the repository compatibility major
  • generated protocol types (Swift, Kotlin, JSON Schema) are committed in sync with shared/src/protocol.ts

Does not prove

  • runtime behaviour

pnpm verify-version && pnpm build && pnpm typecheck && pnpm generate-protocol (no diff)

ci.yml runs

integration
Fails CI

Windows native runtime (Node 22 / 24 / 26)

Every PR and push to master · windows-latest

Proves

  • better-sqlite3 opens under each supported Node ABI on Windows
  • hook installer, observer, state machine and daemon launcher/supervisor tests pass on Windows paths and shells
  • native Codex hook runner sends the launcher PID through cmd.exe, preserves UTF-8 payloads, and omits guessed identity when CIM evidence is unavailable or cyclic

Does not prove

  • the Scheduled Task autostart on a real login session
  • the Stream Deck app on Windows

node bridge/dist/cli.js diag native --json && vitest run <10 Windows-sensitive files>

ci.yml runs

staticcontract
Fails CI

Design system, docs and mirror gates

Every PR and push to master · ubuntu-latest

Proves

  • colour/type tokens are identical across CSS, JS, TS, Swift, Kotlin and C++ mirrors
  • every Markdown link and anchor resolves; every docs/ file is cataloged or excluded with a reason
  • the AGENTS.md chain (root + nested) stays under Codex's 32 KiB project_doc_max_bytes and no CLAUDE.md-family file shadows it inside the checkout
  • the development log aggregates match their entries

Does not prove

  • how anything looks on a device

verify-tokens-sync.py, design-system:check, docs:check, devlog:check, surface/GNB/spec-card parity, design/lint.sh

design-system.yml runs

unitintegration
Passed here

Android JUnit + Robolectric

PRs and pushes touching android/** · ubuntu-latest

Proves

  • the Android app compiles
  • protocol parsing, timeline store and state logic behave as specified on the JVM

Does not prove

  • Compose UI on a device or e-ink panel (there are no instrumented tests)

./gradlew :app:testDebugUnitTest

android-test.yml runs

unitintegrationcompile
Fails CI

Apple XCTest (macOS) + iOS build

PRs and pushes touching apple/**, shared/** or generated/protocol/** · macos-26

Proves

  • the macOS app and its in-process Swift daemon build and pass XCTest
  • the iOS app builds unsigned
  • App Store metadata, screenshots and privacy manifest are within submission limits

Does not prove

  • UI flows (there is no UI-test target)
  • behaviour on a signed build with real TCC grants

xcodebuild test -scheme AgentDeck_macOS; xcodebuild build -scheme AgentDeck_iOS; validate-appstore-submission.sh

apple-test.yml runs

compile
Required to merge

ESP32 firmware host build

PRs touching esp32/** · ubuntu-latest

Proves

  • the real esp32/src render trees compile for the native simulator

Does not prove

  • the Xtensa build, flashing, or behaviour on a board

pio run (every esp32/sim env)

esp32-sim.yml runs

unitintegratione2e
Informational

This report

Every push to master · ubuntu-latest

Proves

  • re-runs the Vitest, E2E and Android suites on the merged master commit and publishes every result

Does not prove

  • Apple, hardware or release gates — those run in their own workflows and are linked, not re-run here

vitest --coverage, pnpm test:e2e, gradlew testDebugUnitTest, then generate-html-report.py

test-report.yml runs

hardware
Informational

ESP32 Robot Framework (flash, boot, serial protocol, performance)

Manually before an ESP32 firmware release · maintainer lab (boards on USB)

Proves

  • the firmware flashes, boots, parses the serial JSON protocol and meets heap/latency budgets on real boards

Does not prove

  • anything in GitHub CI — no hosted runner has the boards, so this page shows it as not run

bash esp32/robot/run.sh hw|protocol|perf

Lab only — results are not recorded here

hardware
Informational

Build-and-deploy smoke on real devices

Manually, agent-driven, before releases · maintainer lab (Mac, phones, e-ink readers, Stream Deck, ESP32)

Proves

  • the built artifacts install and connect to a daemon on the maintainer's hardware

Does not prove

  • repeatability — results are not recorded here

agentdeck-deploy skill (preflight: bash scripts/macos-preflight.sh)

Lab only — results are not recorded here

release
Fails CI

npm publish

Tag npm-v* · ubuntu-latest

Proves

  • the published packages were built from a green test run and are visible on npm

Does not prove

  • a fresh npx @agentdeck/setup install on a clean machine

build, pnpm test, publish, verify packages visible on the registry

npm-release.yml runs

release
Fails CI

App Store / TestFlight upload

Tag apple-v* or manual dispatch · macos

Proves

  • the signed archive satisfies the App Store self-containment invariants before upload

Does not prove

  • runs no XCTest itself — relies on apple-test.yml having passed on master

archive, export, verify-appstore-archive.sh, upload

apple-release.yml runs

release
Fails CI

Android APK / Play upload

Tag android-v* · ubuntu-latest

Proves

  • the release APK is signed with the release key

Does not prove

  • runs no tests itself — relies on android-test.yml having passed on master

assembleSideload, apksigner verify, bundle, Play internal track

android-release.yml runs

releasecompile
Fails CI

ESP32 firmware release

Tag esp32-v* · ubuntu-latest

Proves

  • every supported board's firmware builds and ships with a checksummed manifest

Does not prove

  • boot on hardware (see the Robot suites)

pio run per board, merge factory image, checksums, manifest

esp32-release.yml runs

release
Fails CI

Stream Deck plugin package

Tag streamdeck-v* · ubuntu-latest

Proves

  • the .streamDeckPlugin bundle packages

Does not prove

  • the DRM-processed Marketplace build (verified manually through Elgato's review loop)

package-plugin.sh

streamdeck-release.yml runs

release
Fails CI

Ulanzi plugin package

Tag ulanzi-v* · ubuntu-latest

Proves

  • the Ulanzi plugin bundle packages with its WASM renderer and fonts

Does not prove

  • behaviour inside Ulanzi Studio (manual, plugin-ulanzi/VERIFY.md)

package-ulanzi-plugin.sh

ulanzi-release.yml runs

Not verified automatically

A real Claude Code / Codex / OpenCode process driving the daemon

Why not

Needs an authenticated agent CLI and spends model tokens on every run.

What we do instead

The daemon E2E posts the documented hook payloads through the installed snippet; parsers are covered by recorded-output unit tests.

LAN default-deny against a second machine

Why not

Every address of the host itself is trusted by design, so a single CI runner cannot be a LAN peer.

What we do instead

The access gate is a pure function with a full truth-table test (http-auth-gate.test.ts); the E2E checks the loopback bind.

Stream Deck, Ulanzi, Android, iOS and macOS user interfaces

Why not

No UI-test or instrumented-test targets exist; the host apps (Stream Deck, Ulanzi Studio) cannot run in CI.

What we do instead

Renderer snapshot tests, layout-engine tests and manual device deploys.

Firmware behaviour on ESP32 boards, Pixoo, Timebox, iDotMatrix, TC001

Why not

Physical hardware.

What we do instead

Host-simulator compile gate, protocol unit tests, and the lab Robot suites before firmware releases.

macOS privacy prompts (Automation, Accessibility, Screen Recording, Local Network, Bluetooth, firewall)

Why not

They belong to a signed app on a real user session.

What we do instead

scripts/macos-preflight.sh checks the grants before a lab run and stops with the exact System Settings path instead of hanging on a dialog.

Evidence levels

unit
One module in isolation; collaborators faked.
contract
A wire format, generated mirror or SSOT table compared against its canonical source. Catches drift, not behaviour.
integration
Several real modules together in one process (real sockets on ephemeral loopback ports, real files in a temp dir).
e2e
The shipped entry point started as a separate process and driven only through its public interfaces.
compile
Source builds for the target; nothing executes.
static
Lint, type check, link or token checks over source text.
hardware
Runs against a connected physical device.
release
Packaging, signing and store-submission checks on a release tag.

Vitest + E2E

Test domains

Every TypeScript and end-to-end test file, grouped by the question it answers. Open a domain for its files and each test.

9 files

Pass

LAN security & pairing

Is the LAN surface default-deny, and does pairing hand out credentials only with the operator's consent?

Passed
159
Failed
0
Skipped
0
Time
479 ms
9 test files
bridge/src/__tests__/auth-token-adoption.test.ts10/10
  • ✓adoptPeerToken › serves the incumbent daemon token after adopting it89 ms
  • ✓adoptPeerToken › keeps accepting the superseded token so devices are not locked out mid-convergence19 ms
  • ✓adoptPeerToken › survives a restart — adoption is persisted, not in-memory28 ms
  • ✓adoptPeerToken › is a no-op when the incumbent serves the token we already hold15 ms
  • ✓adoptPeerToken › refuses a missing or malformed peer token rather than adopting a blank credential14 ms
  • ✓adoptPeerToken › bounds the accepted key ring instead of growing it forever21 ms
  • ✓adoptPeerToken › never records the same token twice in the ring13 ms
  • ✓rotateToken › retires the accepted ring too, or rotation would not retire a leaked token14 ms
  • ✓data-dir resolution › keeps the token beside the rest of the daemon state15 ms
  • ✓data-dir resolution › inherits the legacy credential instead of un-pairing an existing fleet24 ms
bridge/src/__tests__/discovery-security.test.ts5/5
  • ✓LAN discovery credential boundary (issue #145) › never serializes a pairing credential into a UDP discovery beacon9 ms
  • ✓LAN discovery credential boundary (issue #145) › keeps the beacon secret-free across periodic re-sends1 ms
  • ✓LAN discovery credential boundary (issue #145) › does not materialize or log a token-bearing pairing URL in ../index.ts1 ms
  • ✓LAN discovery credential boundary (issue #145) › does not materialize or log a token-bearing pairing URL in ../daemon-server.ts2 ms
  • ✓LAN discovery credential boundary (issue #145) › keeps per-session project metadata off LAN discovery1 ms
bridge/src/__tests__/http-auth-gate.test.ts19/19
  • ✓gateHttpRequest (issue #145 LAN default-deny) › lets an authorized request reach any route2 ms
  • ✓gateHttpRequest (issue #145 LAN default-deny) › serves only the public health payload to unauthorized peers0 ms
  • ✓gateHttpRequest (issue #145 LAN default-deny) › denies every other route to unauthorized peers1 ms
  • ✓POST /pair — only a route while the operator holds a window open › is denied exactly like an unknown path when no window is open0 ms
  • ✓POST /pair — only a route while the operator holds a window open › opens for redemption only while a window is open0 ms
  • ✓POST /pair — only a route while the operator holds a window open › defaults to closed when the caller forgets to pass the window state0 ms
  • ✓POST /pair — only a route while the operator holds a window open › an open window does not unlock anything else, or any other method0 ms
  • ✓POST /pair — only a route while the operator holds a window open › still serves public health while a window is open0 ms
  • ✓buildPublicHealth › never contains credentials, module inventory, or session state1 ms
  • ✓isAuthorizedHttpRequest › trusts same-machine connections without a token0 ms
  • ✓isAuthorizedHttpRequest › accepts a valid query token from a remote peer0 ms
  • ✓isAuthorizedHttpRequest › accepts a valid Authorization: Bearer header0 ms
  • ✓isAuthorizedHttpRequest › rejects remote peers with a wrong or missing token1 ms
  • ✓operator approval authorizes an HTTP request › refuses an unapproved remote peer1 ms
  • ✓operator approval authorizes an HTTP request › admits it once the operator approves its address1 ms
  • ✓operator approval authorizes an HTTP request › admits a device-approved peer from a DIFFERENT address1 ms
  • ✓operator approval authorizes an HTTP request › does not admit a different device sharing an approved device grant1 ms
  • ✓operator approval authorizes an HTTP request › still admits a header-less client approved by address1 ms
  • ✓operator approval authorizes an HTTP request › stops admitting once revoked1 ms
bridge/src/__tests__/network-posture.test.ts18/18
  • ✓module registry SSOT › MODULE_NAMES matches the modules createDefaultModules actually registers4 ms
  • ✓module registry SSOT › allModulesOff() switches off every registered module1 ms
  • ✓module registry SSOT › covers the two modules the hand-written --local records used to omit0 ms
  • ✓resolveDaemonPosture › defaults to the open posture0 ms
  • ✓resolveDaemonPosture › reads the env switch0 ms
  • ✓resolveDaemonPosture › treats --loopback and the env var as the same switch0 ms
  • ✓resolveDaemonPosture › keeps --local and --loopback independent0 ms
  • ✓daemonModuleConfigs › leaves everything enabled in the default posture0 ms
  • ✓daemonModuleConfigs › loopback silences every module that touches the network0 ms
  • ✓daemonModuleConfigs › loopback keeps USB serial — a board on a cable is not a network peer0 ms
  • ✓daemonModuleConfigs › loopback keeps ADB reverse — the tunnel rides USB into the host loopback0 ms
  • ✓daemonModuleConfigs › loopback still honours the AGENTDECK_DAEMON_NO_SERIAL diagnostic gate0 ms
  • ✓daemonModuleConfigs › --local turns off every module, serial included0 ms
  • ✓daemonModuleConfigs › --local wins over loopback when both are set0 ms
  • ✓daemonModuleConfigs › a module added later defaults to off in both restricted postures0 ms
  • ✓describeDaemonPosture › names what is off, not just the bind address1 ms
  • ✓describeDaemonPosture › folds the USB channels into the off-list when --local is also set0 ms
  • ✓describeDaemonPosture › points at the switch when the daemon is wide open0 ms
bridge/src/__tests__/pairing-knocks.test.ts28/28
  • ✓peerKey › prefers the device id and never emits a bare string5 ms
  • ✓peerKey › normalizes so one device cannot appear as several1 ms
  • ✓peerKey › falls back to the address for an id that is not the accepted shape1 ms
  • ✓recording knocks › counts repeat attempts on one row rather than growing the list2 ms
  • ✓recording knocks › carries the stale-token distinction, which reads differently to an operator1 ms
  • ✓recording knocks › ignores an empty address1 ms
  • ✓recording knocks › does not record a peer that is already approved1 ms
  • ✓recording knocks › tells two devices behind one address apart1 ms
  • ✓recording knocks › returns newest first2 ms
  • ✓expiry is applied on read, not by a timer › drops a knock older than the TTL when the list is read1 ms
  • ✓expiry is applied on read, not by a timer › keeps a peer alive while it is still knocking1 ms
  • ✓bounded › caps the list and drops the oldest, so a flood cannot grow it without limit1 ms
  • ✓approving › admits the peer and retires its knock1 ms
  • ✓approving › a device-keyed approval survives an address change1 ms
  • ✓approving › an address-keyed approval does not admit a different address1 ms
  • ✓approving › does not admit a different device that shares the approved address1 ms
  • ✓approving › still admits a device approved by ADDRESS before it learned to send an id1 ms
  • ✓approving › revokes1 ms
  • ✓approving › refuses an empty key rather than minting a blank grant1 ms
  • ✓dismiss is not a block › drops the row but lets the peer knock again1 ms
  • ✓persistence › round-trips approvals across a daemon restart1 ms
  • ✓persistence › migrates the pre-device-id shape instead of discarding it1 ms
  • ✓persistence › reads a Foundation reference-date stamp written by the Swift daemon1 ms
  • ✓persistence › reads an epoch-ms stamp written by this daemon1 ms
  • ✓persistence › does not overwrite an unparseable file — it is evidence, not garbage1 ms
  • ✓persistence › writes through a temp file and leaves none behind1 ms
  • ✓deviceIdFromHeaders › reads and normalizes the handshake header1 ms
  • ✓deviceIdFromHeaders › is null when the client sends nothing, so the address stays the key1 ms
bridge/src/__tests__/pairing-window.test.ts41/41
  • ✓window lifecycle › is closed until the operator opens one3 ms
  • ✓window lifecycle › mints a code of the documented shape1 ms
  • ✓window lifecycle › never repeats a code across many opens (i.e. it is actually random)4 ms
  • ✓window lifecycle › replaces a window rather than leaving two valid secrets running2 ms
  • ✓window lifecycle › closes on the operator asking1 ms
  • ✓window lifecycle › clamps a window length nobody should be able to ask for1 ms
  • ✓window lifecycle › clamps the device count to at least one1 ms
  • ✓redemption › hands over the token for the right code and closes a one-device window1 ms
  • ✓redemption › reads the token live, so a handover reaches a device pairing right now0 ms
  • ✓redemption › never mints a token for a refused redemption2 ms
  • ✓redemption › refuses everything once the window is closed1 ms
  • ✓redemption › pairs several devices from one window, then closes0 ms
  • ✓redemption › closes the window after the attempt budget, across peers1 ms
  • ✓redemption › does not spend the budget on malformed submissions0 ms
  • ✓redemption › accepts the code with the separators the display added0 ms
  • ✓cable-free ESP32 re-arm › is off unless the operator named the board0 ms
  • ✓cable-free ESP32 re-arm › adopts only the addresses the operator named0 ms
  • ✓cable-free ESP32 re-arm › is closed when no window is open at all0 ms
  • ✓cable-free ESP32 re-arm › expires with the window, enforced on read2 ms
  • ✓cable-free ESP32 re-arm › re-arms a board once, not on every reconnect0 ms
  • ✓cable-free ESP32 re-arm › reports the re-armed board to the operator2 ms
  • ✓cable-free ESP32 re-arm › sanitizes a board name from an unauthenticated peer0 ms
  • ✓cable-free ESP32 re-arm › names an unflashed board generically rather than blank1 ms
  • ✓cable-free ESP32 re-arm › rejects malformed addresses instead of storing them1 ms
  • ✓cable-free ESP32 re-arm › bounds how many boards one window may re-arm1 ms
  • ✓cable-free ESP32 re-arm › shows what is still pending, so a silent board is visible1 ms
  • ✓cable-free ESP32 re-arm › stays open for the code after every board is re-armed0 ms
  • ✓cable-free ESP32 re-arm › stays open for a pending board after the code is redeemed0 ms
  • ✓cable-free ESP32 re-arm › closes once both halves are spent0 ms
  • ✓expiry is enforced on read, not by the timer › refuses a window whose deadline passed even if no timer ran1 ms
  • ✓expiry is enforced on read, not by the timer › refuses the right code after expiry1 ms
  • ✓expiry is enforced on read, not by the timer › is still open one millisecond before the deadline1 ms
  • ✓operator status › never contains the code — the opener already has it, nothing else needs it1 ms
  • ✓operator status › reports who paired and who guessed wrong1 ms
  • ✓operator status › sanitizes the device label, which is untrusted text from an unpaired peer1 ms
  • ✓operator status › falls back to a placeholder rather than an empty or non-string label0 ms
  • ✓operator status › still reports the pairing after the window closed on it1 ms
  • ✓operator status › does not report a previous run's pairings to the next window1 ms
  • ✓operator status › forgets the receipt rather than keeping it for the life of the daemon1 ms
  • ✓operator status › the receipt carries no code and no token0 ms
  • ✓operator status › counts down in whole seconds and never shows 0 while open0 ms
bridge/src/__tests__/ws-server-auth.test.ts3/3
  • ✓WsServer remote auth › rejects a remote connection with no token31 ms
  • ✓WsServer remote auth › accepts the machine token64 ms
  • ✓WsServer remote auth › rejects any non-machine token — the machine token is the ONLY accepted credential4 ms
shared/src/__tests__/pairing-code-rules-sync.test.ts12/12
  • ✓generated mirrors in sync › apple/AgentDeck/Model/PairingCodeRules.generated.swift matches the SSOT2 ms
  • ✓generated mirrors in sync › android/app/src/main/kotlin/dev/agentdeck/net/PairingCodeRules.generated.kt matches the SSOT0 ms
  • ✓generated mirrors in sync › emitters embed the SSOT constants (sanity on the emitters themselves)0 ms
  • ✓generated mirrors in sync › the Swift mirror emits every outcome the TS union declares0 ms
  • ✓generated mirrors in sync › Kotlin stays a client mirror — no evaluator, no HTTP statuses0 ms
  • ✓status contract the mirrors must agree on › accepted answers 200 in TS and in the Swift mirror2 ms
  • ✓status contract the mirrors must agree on › no-window answers 401 in TS and in the Swift mirror0 ms
  • ✓status contract the mirrors must agree on › expired answers 410 in TS and in the Swift mirror0 ms
  • ✓status contract the mirrors must agree on › exhausted answers 429 in TS and in the Swift mirror0 ms
  • ✓status contract the mirrors must agree on › mismatch answers 401 in TS and in the Swift mirror0 ms
  • ✓status contract the mirrors must agree on › malformed answers 400 in TS and in the Swift mirror0 ms
  • ✓status contract the mirrors must agree on › the Swift mirror decides expiry before it reads the code0 ms
shared/src/__tests__/pairing-code.test.ts23/23
  • ✓normalizePairingCode › accepts a bare code3 ms
  • ✓normalizePairingCode › accepts the separators a human or a formatted display introduces0 ms
  • ✓normalizePairingCode › rejects anything that is not exactly the code length0 ms
  • ✓normalizePairingCode › rejects non-strings rather than coercing them0 ms
  • ✓normalizePairingCode › isPairingCode only accepts the canonical form0 ms
  • ✓formatPairingCode › groups the code in half for reading aloud0 ms
  • ✓formatPairingCode › normalizes before grouping, so formatting is idempotent0 ms
  • ✓formatPairingCode › passes through anything it cannot parse instead of mangling it0 ms
  • ✓evaluatePairingRedemption › accepts the right code and closes a single-redemption window1 ms
  • ✓evaluatePairingRedemption › keeps a multi-device window open until its last credential0 ms
  • ✓evaluatePairingRedemption › accepts a code that arrives with the separators it was displayed with0 ms
  • ✓evaluatePairingRedemption › answers 401 with no window, so a closed daemon looks like an unknown route0 ms
  • ✓evaluatePairingRedemption › decides expiry before it looks at the code0 ms
  • ✓evaluatePairingRedemption › treats the expiry instant as closed, not as the last open millisecond0 ms
  • ✓evaluatePairingRedemption › burns one attempt per well-formed wrong code and closes on the last one1 ms
  • ✓evaluatePairingRedemption › refuses a window whose attempts are already spent, even for the right code0 ms
  • ✓evaluatePairingRedemption › refuses a window with no credentials left0 ms
  • ✓evaluatePairingRedemption › does not spend an attempt on a malformed submission1 ms
  • ✓evaluatePairingRedemption › gives an attacker at most PAIRING_MAX_FAILED_ATTEMPTS guesses per window0 ms
  • ✓pairingWindowSecondsRemaining › rounds up so a countdown never shows 0 while the window is still open0 ms
  • ✓pairingWindowSecondsRemaining › floors at zero for an expired or absent window0 ms
  • ✓constants › keeps the code long enough that the attempt cap is the real defence0 ms
  • ✓constants › keeps the window short enough to be an operator action, not a mode0 ms

2 files

Pass

End-to-end (real processes)

Does the shipped daemon work when started and driven from outside?

Passed
10
Failed
0
Skipped
0
Time
1.7 s
2 test files
tests/e2e/daemon-hub.e2e.test.ts7/7
  • ✓daemon hub (real CLI process) › announces itself on /health and in daemon.json, from the temp data dir only6 ms
  • ✓daemon hub (real CLI process) › binds loopback only: the same port is closed on a LAN address2 ms
  • ✓daemon hub (real CLI process) › turns Claude hook events into the state stream and the persisted timeline635 ms
  • ✓daemon hub (real CLI process) › replays real Hermes CLI/Gateway captures without calling cancelled turns completed126 ms
  • ✓daemon hub (real CLI process) › answers the request-response hooks (PreToolUse, Stop) promptly when no device can approve5 ms
  • ✓daemon hub (real CLI process) › accepts events from the installed hook snippet, which finds the daemon via daemon.json185 ms
  • ✓daemon hub (real CLI process) › shuts down cleanly and replays the persisted timeline after a restart583 ms
tests/e2e/hooks-uninstall.e2e.test.ts3/3
  • ✓hooks uninstall (real CLI entry point) › removes the Codex block and keeps what Codex added inside it55 ms
  • ✓hooks uninstall (real CLI entry point) › keeps a config it cannot edit safely and says so40 ms
  • ✓hooks uninstall (real CLI entry point) › does nothing to Codex when it was never configured37 ms

27 files

Pass

Timeline

Is every turn, task and approval recorded once, attributed to the right session, and kept across restarts?

Passed
358
Failed
0
Skipped
0
Time
2.2 s
27 test files
bridge/src/__tests__/apme-sample-timeline.test.ts13/13
  • ✓sampleEventToTimeline projection › projects user_message → chat_start5 ms
  • ✓sampleEventToTimeline projection › projects a text assistant_message → chat_response but skips tool_only1 ms
  • ✓sampleEventToTimeline projection › projects tool → tool_resolved with status mapping + input summary1 ms
  • ✓sampleEventToTimeline projection › skips model and state events (no standalone row)0 ms
  • ✓OpenClaw session.tool / session.message → spans › maps a tool start to a tool_call span carrying the args2 ms
  • ✓OpenClaw session.tool / session.message → spans › maps a tool result to a tool_result span carrying the output0 ms
  • ✓OpenClaw session.tool / session.message → spans › reports an errored tool as a result, not as a success0 ms
  • ✓OpenClaw session.tool / session.message → spans › treats phase 'error' as a failure even when isError is absent0 ms
  • ✓OpenClaw session.tool / session.message → spans › maps a user message to turn_start — the prompt no other channel carries1 ms
  • ✓OpenClaw session.tool / session.message → spans › maps an assistant message to session_meta + turn_response1 ms
  • ✓OpenClaw session.tool / session.message → spans › does not count an assistant toolCall block as a tool0 ms
  • ✓OpenClaw session.tool / session.message → spans › drops a toolResult message — session.tool already reported it0 ms
  • ✓OpenClaw session.tool / session.message → spans › drops empty/blank messages and nameless tools0 ms
bridge/src/__tests__/claude-hook-timeline.test.ts3/3
  • ✓claude hook timeline projection › creates a bounded request with correlation and safe input summary4 ms
  • ✓claude hook timeline projection › maps success and failure completions without exposing arbitrary payloads0 ms
  • ✓claude hook timeline projection › ignores unrelated hook events0 ms
bridge/src/__tests__/fallback-task-timeline.test.ts4/4
  • ✓FallbackTaskTimeline › opens a task on the first prompt and closes it on session end5 ms
  • ✓FallbackTaskTimeline › segments on /clear and numbers the next task1 ms
  • ✓FallbackTaskTimeline › ignores session end with no open task and tracks sessions independently2 ms
  • ✓FallbackTaskTimeline › drops the per-session counter on session_end so counts cannot grow unbounded1 ms
bridge/src/__tests__/gateway-timeline-attribution.test.ts3/3
  • ✓enrichGatewayTimelineEntry › stamps agentType/projectName onto a bare OpenClaw adapter entry4 ms
  • ✓enrichGatewayTimelineEntry › preserves agentType/projectName the adapter already set0 ms
  • ✓enrichGatewayTimelineEntry › does not mutate the input entry0 ms
bridge/src/__tests__/kiro-timeline-feed.test.ts6/6
  • ✓KiroTimelineFeed › emits the last turn — and only that — the first time it sees a session4 ms
  • ✓KiroTimelineFeed › cuts the seeded turn at its prompt, never mid-turn1 ms
  • ✓KiroTimelineFeed › emits only what appeared after the first sighting1 ms
  • ✓KiroTimelineFeed › emits the first row of a session that was empty when first seen0 ms
  • ✓KiroTimelineFeed › forgets a session that is no longer observed0 ms
  • ✓KiroTimelineFeed › orders rows across sessions and survives a reader that throws0 ms
bridge/src/__tests__/kiro-transcript-timeline.test.ts8/8
  • ✓kiroTimelineForSession › reads the shared nested-v3 fixture and excludes reasoning6 ms
  • ✓kiroTimelineForSession › turns a real transcript into paired chat rows2 ms
  • ✓kiroTimelineForSession › reads the timestamp as seconds, not milliseconds1 ms
  • ✓kiroTimelineForSession › gives every reply record in one turn its own timestamp2 ms
  • ✓kiroTimelineForSession › keeps thinking blocks and tool results out of the rows3 ms
  • ✓kiroTimelineForSession › honours since and limit against the row timestamps2 ms
  • ✓kiroTimelineForSession › returns nothing rather than guessing when it cannot read2 ms
  • ✓kiroTimelineForSession › finds a session under a workspace-hash directory, not just cli/1 ms
bridge/src/__tests__/openclaw-timeline-completion.test.ts4/4
  • ✓OpenClaw chat final → single completion row › emits chat_response only (no chat_end) when the turn has response text63 ms
  • ✓OpenClaw chat final → single completion row › marks gateway-initiated (cron) turns automated and skips LLM enrichment11 ms
  • ✓OpenClaw chat final → single completion row › emits chat_end (not chat_response) for a response-less turn1 ms
  • ✓captured external run → adapter state › starts WORKING during preparation and stays working through tool execution3 ms
bridge/src/__tests__/openclaw-timeline-feed.test.ts12/12
  • ✓OpenClawTimelineFeed › emits NOTHING on a first sighting — unlike the Kiro feed, deliberately5 ms
  • ✓OpenClawTimelineFeed › emits work that arrives after the position it adopted2 ms
  • ✓OpenClawTimelineFeed › reads only recently-active sessions — the store never forgets one0 ms
  • ✓OpenClawTimelineFeed › forgets a session that falls out of the window, and re-seeds it silently0 ms
  • ✓OpenClawTimelineFeed › names WHICH OpenClaw session produced each row, at the head of detail0 ms
  • ✓OpenClawTimelineFeed › attributes rows to the virtual Gateway session, never to a new one0 ms
  • ✓OpenClawTimelineFeed › preserves a row detail the parser already wrote, under the session line0 ms
  • ✓OpenClawTimelineFeed › returns rows oldest-first across all sessions0 ms
  • ✓OpenClawTimelineFeed › survives a reader that throws, and keeps going for the others0 ms
  • ✓OpenClawTimelineFeed › returns nothing when the store cannot be listed at all0 ms
  • ✓shortSessionKey › drops the constant agent prefix and keeps what says which work this is0 ms
  • ✓shortSessionKey › leaves a key it does not recognize alone rather than emptying it0 ms
bridge/src/__tests__/openclaw-transcript-timeline.test.ts13/13
  • ✓listOpenClawSessions › reads the index keys as session keys, newest first3 ms
  • ✓listOpenClawSessions › returns nothing rather than throwing when there is no store0 ms
  • ✓openClawTimelineForSession › emits one row per tool CALL — the gap the Gateway stream leaves2 ms
  • ✓openClawTimelineForSession › names the tool and what it was asked to do0 ms
  • ✓openClawTimelineForSession › stamps the row at the CALL, not at the result0 ms
  • ✓openClawTimelineForSession › completes the row from its matching result, keyed by toolCallId1 ms
  • ✓openClawTimelineForSession › puts the outcome and the model ahead of unbounded tool output1 ms
  • ✓openClawTimelineForSession › marks a failure reported by isError alone — there is no exit code1 ms
  • ✓openClawTimelineForSession › keeps a call that never got a result — that is the abandoned approval0 ms
  • ✓openClawTimelineForSession › honors since/limit so a poller only sees new work1 ms
  • ✓openClawTimelineForSession › returns nothing for a session that is not in the store0 ms
  • ✓summarizeToolArguments › prefers the command for exec — the whole story of the call0 ms
  • ✓summarizeToolArguments › degrades to something TRUE for a tool this build has never seen0 ms
bridge/src/__tests__/orphan-chat-reaper-loop.test.ts3/3
  • ✓orphan chat_start reaper › closes an orphan even when an identical close already sits NEWER in the buffer5 ms
  • ✓orphan chat_start reaper › does not collapse two different turns that close with the same text1 ms
  • ✓orphan chat_start reaper › still absorbs a duplicate emit of the same close0 ms
bridge/src/__tests__/session-timeline-relay.test.ts5/5
  • ✓SessionTimelineRelay › connects to sibling and relays timeline_event316 ms
  • ✓SessionTimelineRelay › relays timeline_history entries303 ms
  • ✓SessionTimelineRelay › removes subscription when session disappears201 ms
  • ✓SessionTimelineRelay › ignores daemon sessions0 ms
  • ✓SessionTimelineRelay › handles upsert timeline events403 ms
bridge/src/__tests__/session-transcript-timeline.test.ts5/5
  • ✓lastAssistantTextFromTranscript › returns the last assistant text at Stop time4 ms
  • ✓lastAssistantTextFromTranscript › skips tool_result user continuations between assistant messages1 ms
  • ✓lastAssistantTextFromTranscript › returns empty when the tail is an unanswered prompt1 ms
  • ✓lastAssistantTextFromTranscript › returns empty for a missing file0 ms
  • ✓lastAssistantTextFromTranscript › skips malformed tail lines1 ms
bridge/src/__tests__/subagent-timeline-store.test.ts6/6
  • ✓subagent rows in the bridge timeline store › keeps siblings of one fan-out that share a label and an instant4 ms
  • ✓subagent rows in the bridge timeline store › still dedups identical rows that claim no child identity0 ms
  • ✓subagent rows in the bridge timeline store › upserts a growing dispatch burst in place, past the 1s tolerance1 ms
  • ✓subagent rows in the bridge timeline store › does not shed the dispatch row ahead of ordinary tool rows25 ms
  • ✓OpenClaw transcript rows vs. tool_exec-first eviction › does not let an OpenClaw row evict itself on insertion into a full buffer10 ms
  • ✓OpenClaw transcript rows vs. tool_exec-first eviction › sheds an observed agent's tool rows ahead of OpenClaw's25 ms
bridge/src/__tests__/subagent-timeline.test.ts16/16
  • ✓SubagentTimelineTracker › consumes Claude internal suggestion stops without inventing a worker or APME event5 ms
  • ✓SubagentTimelineTracker › preserves typed/legacy orphan and non-Claude stops (claude-code, {"agent_type":"Explore"})1 ms
  • ✓SubagentTimelineTracker › preserves typed/legacy orphan and non-Claude stops (claude-code, {})0 ms
  • ✓SubagentTimelineTracker › preserves typed/legacy orphan and non-Claude stops (codex-cli, {"agent_type":""})0 ms
  • ✓SubagentTimelineTracker › closes a known child even when its stop loses its type2 ms
  • ✓SubagentTimelineTracker › collapses start and stop into existing compatible Timeline types2 ms
  • ✓SubagentTimelineTracker › consumes child tool hooks without producing per-tool noise or controls0 ms
  • ✓SubagentTimelineTracker › keeps future child notification and stop hooks out of parent control0 ms
  • ✓SubagentTimelineTracker › summarizes team task completion without exposing team settings1 ms
  • ✓SubagentTimelineTracker › records a task-list completion as info, never as a child agent1 ms
  • ✓SubagentTimelineTracker › recognizes Codex lifecycle names and preserves the provider0 ms
  • ✓SubagentTimelineTracker › keeps a simultaneous fan-out distinguishable and counted1 ms
  • ✓SubagentTimelineTracker › reports an explicit zero once a fan-out drains, never absence0 ms
  • ✓SubagentTimelineTracker › does not pin a parent at "running" when a stop hook is lost0 ms
  • ✓SubagentTimelineTracker › separates one wave from the next0 ms
  • ✓SubagentTimelineTracker › reports a census change so the parent row can be rebroadcast0 ms
bridge/src/__tests__/timeline-integration.test.ts43/43
  • ✓BridgeTimelineStore › adds and retrieves entries5 ms
  • ✓BridgeTimelineStore › filters history by timestamp1 ms
  • ✓BridgeTimelineStore › returns timeline history sorted by timestamp after late upserts2 ms
  • ✓BridgeTimelineStore › returns per-session history sorted by timestamp after late upserts1 ms
  • ✓BridgeTimelineStore › normalizes OpenClaw cron prompt dumps before storing history1 ms
  • ✓BridgeTimelineStore › drops low-signal OpenClaw placeholder tool rows before broadcast/history1 ms
  • ✓BridgeTimelineStore › drops Codex tool_exec rows before broadcast/history1 ms
  • ✓BridgeTimelineStore › loads persisted timeline history without replaying Codex tool_exec firehose1 ms
  • ✓BridgeTimelineStore › calls listeners on new entries0 ms
  • ✓BridgeTimelineStore › upsert updates existing entry with same ts+type1 ms
  • ✓BridgeTimelineStore › upsert keeps timeline attribution fields1 ms
  • ✓BridgeTimelineStore › does not upsert a different OpenClaw run inside the timestamp tolerance1 ms
  • ✓BridgeTimelineStore › upsert adds new entry when no match exists0 ms
  • ✓BridgeTimelineStore › task_end updates in place by taskId and carries judge fields1 ms
  • ✓BridgeTimelineStore › updateEntryStatus updates approval status0 ms
  • ✓BridgeTimelineStore › getLastEntry returns most recent of given type0 ms
  • ✓BridgeTimelineStore › getLastEntry returns null when type not found0 ms
  • ✓BridgeTimelineStore › removeListener stops notifications0 ms
  • ✓BridgeTimelineStore › enforces MAX_ENTRIES (200) with FIFO10 ms
  • ✓deduplicateEntry pipeline › exact duplicate within 8s → skip0 ms
  • ✓deduplicateEntry pipeline › same type, different content → add0 ms
  • ✓deduplicateEntry pipeline › different type, same raw → add0 ms
  • ✓deduplicateEntry pipeline › chat_response identical raw 6s apart → skip (multi-producer race)0 ms
  • ✓deduplicateEntry pipeline › chat_response near-duplicate beyond 8s → repetitive merge0 ms
  • ✓BridgeTimelineStore.setAttributor — history replay attribution › addEntry passes the entry through the attributor before storage0 ms
  • ✓BridgeTimelineStore.setAttributor — history replay attribution › caller-set fields take precedence over attributor (idempotent)0 ms
  • ✓BridgeTimelineStore.setAttributor — history replay attribution › listener (broadcast) receives the same attributed entry0 ms
  • ✓BridgeTimelineStore.setAttributor — history replay attribution › upsertEntry propagates summaryKind to existing entry (LLM enrichment regression)0 ms
  • ✓BridgeTimelineStore.setAttributor — history replay attribution › upsertEntry routes through the attributor too1 ms
  • ✓BridgeTimelineStore.setAttributor — history replay attribution › late upsert preserves the original entry attribution after task rotation (regression)4 ms
  • ✓BridgeTimelineStore.setAttributor — history replay attribution › merge path (repetitive dedup) does not re-attribute after task rotation1 ms
  • ✓BridgeTimelineStore.setAttributor — history replay attribution › upsert with no existing match falls through to attributor (insert path)0 ms
  • ✓BridgeTimelineStore.setAttributor — history replay attribution › caller-set taskId on upsert overrides existing entry attribution0 ms
  • ✓BridgeTimelineStore.setAttributor — history replay attribution › history replay returns entries with taskId/runId set (regression)0 ms
  • ✓completion multi-producer double-emit (regression) › identical chat_response from two emit paths is collapsed by store1 ms
  • ✓Timeline → WS broadcast pipeline › store entries trigger broadcast via listener0 ms
  • ✓Timeline → WS broadcast pipeline › upsert entries broadcast with upsert flag0 ms
  • ✓Timeline → WS broadcast pipeline › exact duplicate within 5s is skipped by store0 ms
  • ✓Timeline → WS broadcast pipeline › different content within 5s is NOT deduped0 ms
  • ✓TimelineEntry types › common entry types have expected shape1 ms
  • ✓TimelineEntry types › entries with status field0 ms
  • ✓TimelineEntry types › entries with detail field0 ms
  • ✓TimelineEntry types › entries with automated flag0 ms
bridge/src/__tests__/timeline-orphan-reaper.test.ts5/5
  • ✓BridgeTimelineStore.reapOrphanTaskStarts › synthesizes task_end for orphaned task_start rows only4 ms
  • ✓BridgeTimelineStore.reapOrphanTaskStarts › anchors the synthetic end after the task's last row with an approx duration1 ms
  • ✓BridgeTimelineStore.reapOrphanTaskStarts › closes stale orphaned chat_start rows without touching live or completed turns1 ms
  • ✓BridgeTimelineStore.reapOrphanTaskStarts › anchors the synthetic chat_end before the session's next chat_start1 ms
  • ✓BridgeTimelineStore.reapOrphanTaskStarts › lets a real task_end merge over the synthetic by taskId0 ms
bridge/src/__tests__/timeline-persistence.test.ts13/13
  • ✓persistence is opt-in › writes nothing until enablePersistence is called10 ms
  • ✓persistence is opt-in › writes once enabled5 ms
  • ✓write behaviour › coalesces a burst of entries into a single write2 ms
  • ✓write behaviour › leaves no temp file behind2 ms
  • ✓write behaviour › flushes pending entries on shutdown instead of losing them2 ms
  • ✓write behaviour › stopPersistence is safe when nothing is pending2 ms
  • ✓write behaviour › survives an unwritable path rather than taking the daemon down1 ms
  • ✓round-trip › a written file reloads into an equivalent buffer2 ms
  • ✓round-trip › restored history is included in the next write, not truncated away3 ms
  • ✓cross-implementation format contract (Swift DaemonTimelineStore) › writes a plain JSON array, not a wrapper object1 ms
  • ✓cross-implementation format contract (Swift DaemonTimelineStore) › every row carries the ts/type/raw triple both implementations require1 ms
  • ✓cross-implementation format contract (Swift DaemonTimelineStore) › reads a file in the shape the Swift daemon writes1 ms
  • ✓cross-implementation format contract (Swift DaemonTimelineStore) › treats a corrupt file as empty history rather than throwing1 ms
bridge/src/__tests__/timeline-projection-cutover.test.ts4/4
  • ✓BridgeTimelineStore projection cutover › default OFF: every entry passes (no behavior change)4 ms
  • ✓BridgeTimelineStore projection cutover › ON: drops locally-emitted chat/tool rows0 ms
  • ✓BridgeTimelineStore projection cutover › ON: projected (bypass) chat/tool rows pass through0 ms
  • ✓BridgeTimelineStore projection cutover › ON: task hierarchy + error rows are never suppressed0 ms
bridge/src/__tests__/timeline-summarizer-chain.test.ts5/5
  • ✓summarizeResponse provider chain › prefers on-device Foundation Models over MLX5 ms
  • ✓summarizeResponse provider chain › falls to MLX when the helper is unavailable42 ms
  • ✓summarizeResponse provider chain › never dials the removed Ollama tier, even when 11434 would answer3 ms
  • ✓summarizeResponse provider chain › returns null when every provider is down so the caller uses the heuristic1 ms
  • ✓summarizeResponse provider chain › probes the helper once, not once per summary1 ms
bridge/src/__tests__/timeline-task-retention.test.ts6/6
  • ✓BridgeTimelineStore task-row retention › keeps an in-flight task_start alive through FIFO overflow364 ms
  • ✓BridgeTimelineStore task-row retention › evicts closed task pairs once the task cap is exceeded, oldest first104 ms
  • ✓BridgeTimelineStore task-row retention › sheds tool_exec before chat_start so a tool-heavy turn survives replay3 ms
  • ✓BridgeTimelineStore task-row retention › getHistoryForSession returns task rows beyond the per-session limit6 ms
  • ✓BridgeTimelineStore task-row retention › getHistoryForSession canonicalizes observed session ids1 ms
  • ✓BridgeTimelineStore task-row retention › loadPersistedEntries keeps task rows past the generic trim1 ms
shared/src/__tests__/task-title.test.ts16/16
  • ✓deriveTaskTitle › uses the first non-empty line, trimmed and whitespace-collapsed3 ms
  • ✓deriveTaskTitle › returns null for empty / missing prompts0 ms
  • ✓deriveTaskTitle › skips bare slash-command lines and finds the first real line0 ms
  • ✓deriveTaskTitle › a slash PATH is not a slash command — path-leading bug reports keep their title0 ms
  • ✓deriveTaskTitle › a non-ASCII word after the slash is not a command (commands are ASCII)0 ms
  • ✓deriveTaskTitle › a prompt STARTING with markup is machine plumbing — null, never its inner body0 ms
  • ✓deriveTaskTitle › markup after a real first line does not block that line0 ms
  • ✓deriveTaskTitle › a fence swallows its whole body — the ask wins, never the pasted code0 ms
  • ✓deriveTaskTitle › strips markdown furniture0 ms
  • ✓deriveTaskTitle › rejects fragments shorter than the minimum0 ms
  • ✓deriveTaskTitle › caps long prompts at a word boundary with an ellipsis0 ms
  • ✓deriveTaskTitle › hard-cuts boundary-less CJK text without dropping most of the budget0 ms
  • ✓deriveTaskTitle › all index math is in code points, not UTF-16 units0 ms
  • ✓deriveTaskTitle › every derived title passes the timeline meaningful-title gate1 ms
  • ✓deriveTaskTitle › a prompt that literally says "Task 3" still reads as non-meaningful downstream0 ms
  • ✓cross-language parity vectors › has enough vectors to be a gate, and each passes1 ms
shared/src/__tests__/timeline-approval-status.test.ts5/5
  • ✓approval status icons › separates the three outcomes on the resolution row4 ms
  • ✓approval status icons › never draws a denial or an abandonment as success1 ms
  • ✓approval status icons › an abandonment is not an error — nobody refused it0 ms
  • ✓approval status icons › pending and abandoned share the no-decision icon on the request row0 ms
  • ✓approval status icons › an unknown status degrades, it does not claim an outcome0 ms
shared/src/__tests__/timeline-rotating-glyph.test.ts6/6
  • ✓rotating rows draw the running glyph, not their own › an open task_start rotates and renders `running`, not `task`4 ms
  • ✓rotating rows draw the running glyph, not their own › a closed task returns to its own `task` glyph0 ms
  • ✓rotating rows draw the running glyph, not their own › a non-rotating row is unchanged by the display key0 ms
  • ✓rotating rows draw the running glyph, not their own › a stale orphaned task_start stops rotating and shows `task`0 ms
  • ✓an `error` closes a turn for rotation purposes › a later same-session error stops the spinner0 ms
  • ✓an `error` closes a turn for rotation purposes › an error in a DIFFERENT session leaves it spinning0 ms
shared/src/__tests__/timeline-summarizer.test.ts13/13
  • ✓extractTopicHintWithKind — Korean polite-closer robustness › returns the cleaned topic when the response opens with "네, …"3 ms
  • ✓extractTopicHintWithKind — Korean polite-closer robustness › returns the original (kind=fallback) when the entire response is just a polite closer0 ms
  • ✓extractTopicHintWithKind — Korean polite-closer robustness › returns null for empty / very short text (still)0 ms
  • ✓extractTopicHintWithKind — Korean polite-closer robustness › skips lone heading markers and code fences0 ms
  • ✓extractTopicHintWithKind — Korean polite-closer robustness › a heading WITH text becomes the topic (heading is the title)0 ms
  • ✓extractTopicHintWithKind — Korean polite-closer robustness › strips list bullet markers0 ms
  • ✓extractTopicHintWithKind — Korean polite-closer robustness › extractTopicHint convenience returns just the hint string0 ms
  • ✓promptSnippetFallback › returns first sentence trimmed0 ms
  • ✓promptSnippetFallback › returns whole string when no sentence terminator0 ms
  • ✓promptSnippetFallback › truncates to maxLen with ellipsis0 ms
  • ✓promptSnippetFallback › cuts at first newline if no sentence terminator0 ms
  • ✓promptSnippetFallback › returns null for empty / very short input0 ms
  • ✓promptSnippetFallback › handles Korean prompts0 ms
shared/src/__tests__/timeline-task-display.test.ts16/16
  • ✓timelineIsMeaningfulTaskTitle › rejects empty and auto-minted numeric titles3 ms
  • ✓timelineIsMeaningfulTaskTitle › accepts real titles0 ms
  • ✓timelineTaskClosure › finds the matching task_end by taskId0 ms
  • ✓timelineTaskClosure › ignores mismatched ids and non-headers0 ms
  • ✓timelineShouldRenderTaskRow › never renders task_end standalone — including interrupted reaper rows0 ms
  • ✓timelineShouldRenderTaskRow › passes non-task rows through0 ms
  • ✓timelineShouldRenderTaskRow › hides bare "Task N" headers with no eval payload (open or interrupted)0 ms
  • ✓timelineShouldRenderTaskRow › shows headers with meaningful titles0 ms
  • ✓timelineShouldRenderTaskRow › shows judged tasks via the closure eval payload0 ms
  • ✓timelineShouldRenderTaskRow › _empty category hides regardless of source0 ms
  • ✓timelineShouldRenderTaskRow › a non-_empty closure category alone qualifies as eval payload0 ms
  • ✓timelineTaskHeaderDisplay › folds closure label, badge fields, and closedAt into the header1 ms
  • ✓timelineTaskHeaderDisplay › the judge summary outranks a meaningful own title (outcome > intent in the one line a row gets)0 ms
  • ✓timelineTaskHeaderDisplay › a meaningful own title holds the slot while the task is unjudged0 ms
  • ✓timelineTaskHeaderDisplay › open task: not closed, no closure text, falls back to own raw0 ms
  • ✓timelineTaskHeaderDisplay › closedAtMs falls back to the closure ts when endedAt is absent0 ms
shared/src/__tests__/timeline-task-hierarchy.test.ts47/47
  • ✓deduplicateEntry — task hierarchy › always adds task_start even with identical raw within 8s3 ms
  • ✓deduplicateEntry — task hierarchy › always adds task_end with same boundarySignal back-to-back0 ms
  • ✓deduplicateEntry — task hierarchy › always adds task_milestone even with identical raw within 8s0 ms
  • ✓deduplicateEntry — task hierarchy › still dedupes ordinary chat_start within 8s0 ms
  • ✓timelineIconKey › maps task entries to "task"0 ms
  • ✓timelineIconKey › maps tool_request status to success/error/awaiting0 ms
  • ✓timelineIconKey › chat_start in flight is "running"; chat_end is "success"0 ms
  • ✓timelineIconKey › error → error; user_action → user; memory_recall → memory0 ms
  • ✓timelineIconKey › every key has an e-ink glyph of constant 4-char width0 ms
  • ✓isInFlightTask › task_start without matching task_end is in flight0 ms
  • ✓isInFlightTask › task_start whose task_end (same taskId) appeared is finished0 ms
  • ✓isInFlightTask › mismatched taskId on task_end does not close it0 ms
  • ✓isInFlightTask › task_start without taskId is never considered in flight0 ms
  • ✓isInFlightTask › non-task_start entries are never in flight0 ms
  • ✓isInFlightTask › task_start older than the staleness cap is no longer in flight0 ms
  • ✓isRotatingEntry › chat_start without a timestamp rotates (icon-key running, legacy rows)0 ms
  • ✓isRotatingEntry › fresh chat_start rotates0 ms
  • ✓isRotatingEntry › chat_start older than the age cap stops rotating0 ms
  • ✓isRotatingEntry › chat_start with a later same-session completion stops rotating0 ms
  • ✓isRotatingEntry › chat_start superseded by a newer same-session prompt stops rotating0 ms
  • ✓isRotatingEntry › other sessions' completions do not stop a running chat_start0 ms
  • ✓isRotatingEntry › orphan task_start rotates via in-flight predicate0 ms
  • ✓isRotatingEntry › closed task_start does not rotate0 ms
  • ✓isRotatingEntry › static rows do not rotate0 ms
  • ✓isRotatingEntry › eval_result and task_end never rotate0 ms
  • ✓parseTimelineMarkdown › returns single text line for plain text1 ms
  • ✓parseTimelineMarkdown › parses headings 1-6 with required space1 ms
  • ✓parseTimelineMarkdown › parses bullets and numbered lists0 ms
  • ✓parseTimelineMarkdown › handles code fence — verbatim lines, not interpreted0 ms
  • ✓parseTimelineMarkdown › blank line → blank kind0 ms
  • ✓parseTimelineMarkdown › parses tables with header separator1 ms
  • ✓parseTimelineMarkdown › parses tables without separator (no header)0 ms
  • ✓parseTimelineMarkdown › table block ends at first non-table line0 ms
  • ✓parseTimelineMarkdown › quote lines parse0 ms
  • ✓parseInlineSpans › returns empty for empty string0 ms
  • ✓parseInlineSpans › returns single plain span for plain text0 ms
  • ✓parseInlineSpans › parses **bold**0 ms
  • ✓parseInlineSpans › parses *italic* (single star)0 ms
  • ✓parseInlineSpans › parses `code` inline0 ms
  • ✓parseInlineSpans › parses [text](url) link0 ms
  • ✓parseInlineSpans › unclosed ** falls back to plain0 ms
  • ✓parseInlineSpans › multiple spans in one line0 ms
  • ✓parseInlineSpans › first-match-wins: ** consumed before * (no double-italic split)0 ms
  • ✓prepareMarkdownDetail › preserves markdown markers (chat-response detail goes to client)1 ms
  • ✓prepareMarkdownDetail › still filters system JSON blobs0 ms
  • ✓prepareMarkdownDetail › collapses 3+ blank lines but keeps double-blank paragraph break0 ms
  • ✓prepareMarkdownDetail › contrasts with cleanDetailText which strips markdown (non-chat path)1 ms
shared/src/__tests__/timeline.test.ts78/78
  • ✓cleanDetailText › returns empty/falsy input unchanged2 ms
  • ✓cleanDetailText › strips markdown bold0 ms
  • ✓cleanDetailText › strips markdown headings0 ms
  • ✓cleanDetailText › strips code fences0 ms
  • ✓cleanDetailText › strips inline backticks0 ms
  • ✓cleanDetailText › strips markdown links0 ms
  • ✓cleanDetailText › strips blockquotes0 ms
  • ✓cleanDetailText › strips list markers0 ms
  • ✓cleanDetailText › collapses multiple blank lines0 ms
  • ✓cleanDetailText › filters system JSON blobs (connectionId)0 ms
  • ✓cleanDetailText › extracts error from JSON blob0 ms
  • ✓cleanDetailText › compacts other JSON0 ms
  • ✓cleanRawText › strips bold, headings, links, backticks0 ms
  • ✓cleanRawText › returns empty/falsy unchanged0 ms
  • ✓cleanNopMarkers › removes NOP markers0 ms
  • ✓cleanNopMarkers › collapses resulting blank lines0 ms
  • ✓cleanNopMarkers › returns empty/falsy unchanged0 ms
  • ✓normalizeTimelineEntryForStorage › summarizes OpenClaw cron model_call prompts instead of storing shell-like instructions2 ms
  • ✓normalizeTimelineEntryForStorage › drops low-signal OpenClaw placeholder tool rows0 ms
  • ✓normalizeTimelineEntryForStorage › keeps OpenClaw placeholder tool rows when detail carries input or output0 ms
  • ✓normalizeTimelineEntryForStorage › keeps coalesced Codex subagent lifecycle rows0 ms
  • ✓normalizeTimelineEntryForStorage › drops OpenClaw NO_REPLY polling responses from the user-facing timeline0 ms
  • ✓normalizeTimelineEntryForStorage › drops OpenClaw automated polling chat starts from the user-facing timeline0 ms
  • ✓normalizeTimelineEntryForStorage › drops Claude task notification chat starts from the user-facing timeline0 ms
  • ✓normalizeTimelineEntryForStorage › keeps OpenClaw LINE userId notification failures visible0 ms
  • ✓normalizeTimelineEntryForStorage › drops Codex command tool rows even when detail carries input0 ms
  • ✓normalizeTimelineEntryForStorage › drops OpenCode tool rows so a tool-heavy turn does not flood the strip0 ms
  • ✓normalizeTimelineEntryForStorage › drops Antigravity tool rows too (forward-compat with the observed classifier)0 ms
  • ✓normalizeTimelineEntryForStorage › extracts readable labels from cron headers0 ms
  • ✓isAssistantProgressUpdate › detects non-terminal build/status updates1 ms
  • ✓isAssistantProgressUpdate › does not classify final completion reports as progress1 ms
  • ✓extractSemanticCore › strips duration suffix for chat_end0 ms
  • ✓extractSemanticCore › keeps full text for chat_end without separator0 ms
  • ✓extractSemanticCore › keeps full text for non-chat_end types0 ms
  • ✓extractSemanticCore › trims whitespace0 ms
  • ✓isRepetitiveEntry › detects exact duplicate chat_end entries0 ms
  • ✓isRepetitiveEntry › detects keyword-similar entries0 ms
  • ✓isRepetitiveEntry › returns -1 for non-matching entries0 ms
  • ✓isRepetitiveEntry › ignores entries outside window0 ms
  • ✓isRepetitiveEntry › only applies to chat_end, chat_start, and error types0 ms
  • ✓isRepetitiveEntry › dedupes repeated error entries within 1h window0 ms
  • ✓isRepetitiveEntry › matches chat_start entries0 ms
  • ✓isRepetitiveEntry › dedupes automated entries regardless of content0 ms
  • ✓isRepetitiveEntry › exempts error rows from the content-agnostic automated collapse0 ms
  • ✓isRepetitiveEntry › does not dedup automated vs non-automated0 ms
  • ✓isRepetitiveEntry › uses 8h window for automated entries0 ms
  • ✓isRepetitiveEntry › expires automated dedup after 8h0 ms
  • ✓parseLogLine › returns null for null/undefined/non-object1 ms
  • ✓parseLogLine › returns null for empty message0 ms
  • ✓parseLogLine › returns null for model start/complete (suppressed)0 ms
  • ✓parseLogLine › parses memory/recall (legacy structured)0 ms
  • ✓parseLogLine › parses tool execution (legacy structured)0 ms
  • ✓parseLogLine › filters gateway/ws subsystem0 ms
  • ✓parseLogLine › filters infrastructure noise1 ms
  • ✓parseLogLine › filters channel infra reconnect noise1 ms
  • ✓parseLogLine › filters connection status JSON blobs0 ms
  • ✓parseLogLine › filters transient fetch timeouts0 ms
  • ✓parseLogLine › filters edit mismatch errors (agent retries)0 ms
  • ✓parseLogLine › filters failover cascade noise0 ms
  • ✓parseLogLine › parses genuine errors1 ms
  • ✓parseLogLine › parses error from message pattern (not level)1 ms
  • ✓parseLogLine › filters model/inference patterns (suppressed)0 ms
  • ✓parseLogLine › does not synthesize memory_recall from message text alone0 ms
  • ✓parseLogLine › does not synthesize tool_exec from message text alone0 ms
  • ✓parseLogLine › parses ISO timestamp correctly via error pattern0 ms
  • ✓parseLogLine › falls back to Date.now() for invalid timestamp on error message0 ms
  • ✓parseLogLine › truncates long tool raw to 500 chars0 ms
  • ✓parseLogLine › filters whatsapp noise from channel infra subsystem0 ms
  • ✓parseSlashCommandPrompt › parses a bare command1 ms
  • ✓parseSlashCommandPrompt › parses a command with arguments0 ms
  • ✓parseSlashCommandPrompt › parses plugin-scoped command names0 ms
  • ✓parseSlashCommandPrompt › parses the Claude Code command XML envelope0 ms
  • ✓parseSlashCommandPrompt › normalizes an envelope name missing the leading slash2 ms
  • ✓parseSlashCommandPrompt › rejects ordinary prompts and filesystem paths0 ms
  • ✓normalizeCommandPrompt › collapses an XML envelope to "/name args"0 ms
  • ✓normalizeCommandPrompt › passes ordinary prompts through unchanged0 ms
  • ✓deduplicateEntry — interrupted close + cross-session guards › never repetitive-merges rows from different sessions1 ms
  • ✓deduplicateEntry — interrupted close + cross-session guards › adds interrupted chat_end rows instead of merging them by semantic core0 ms

15 files

Pass

Usage & quota

Are Claude, Codex and Z.ai quota windows read, relayed and aged correctly?

Passed
283
Failed
0
Skipped
0
Time
2.4 s
15 test files
bridge/src/__tests__/codex-rate-limits-live.test.ts66/66
  • ✓parseLiveCodexRateLimits › does not activate an additional reserve pool while ordinary quota is available3 ms
  • ✓parseLiveCodexRateLimits › maps the app-server shape onto the wire shape2 ms
  • ✓parseLiveCodexRateLimits › accepts the rollout spelling of the window length0 ms
  • ✓parseLiveCodexRateLimits › clamps out-of-range percentages0 ms
  • ✓parseLiveCodexRateLimits › returns null when the result carries no usable limits0 ms
  • ✓parseLiveCodexRateLimits — limit id provenance › takes the limit id from the map key when the value carries none0 ms
  • ✓pickAccountWideLiveLimits › keeps the top-level block, which is what the Codex CLI itself shows0 ms
  • ✓pickAccountWideLiveLimits › falls back to the unnamed family when the top level is scoped to one model0 ms
  • ✓pickAccountWideLiveLimits › answers null when every family is model-scoped0 ms
  • ✓pickAccountWideLiveLimits › does not let the top-level block fingerprint against its own mirror0 ms
  • ✓pickAccountWideLiveLimits › prefers a windowed entry even on the last-resort rung0 ms
  • ✓pickAccountWideLiveLimits › never adopts a model-scoped entry's key for the account block0 ms
  • ✓pickAccountWideLiveLimits › adopts a matching entry key when the top-level block names no id0 ms
  • ✓pickAccountWideLiveLimits › carries the map key out as the limit id0 ms
  • ✓pickBestCodexRateLimits › prefers the newer capture1 ms
  • ✓pickBestCodexRateLimits › keeps a passive reading that is newer than the cached live one0 ms
  • ✓pickBestCodexRateLimits › preserves Luna metadata when the newer passive reading wins0 ms
  • ✓pickBestCodexRateLimits › does not resurrect an old passive reserve when a fresh live answer omits it0 ms
  • ✓pickBestCodexRateLimits › does not carry cached live reserve into recovered ordinary quota0 ms
  • ✓pickBestCodexRateLimits › handles either side being absent0 ms
  • ✓pickBestCodexRateLimits › lets a stamped snapshot beat an unstamped one0 ms
  • ✓pickBestCodexRateLimits › takes the live reading over a NEWER rollout minted under the old plan0 ms
  • ✓pickBestCodexRateLimits › still prefers a fresh passive reading once the rollout carries the new plan0 ms
  • ✓pickBestCodexRateLimits › takes the live reading over a NEWER rollout describing a different limit family0 ms
  • ✓pickBestCodexRateLimits › does not let the family guard outrank the plan test0 ms
  • ✓pickBestCodexRateLimits › stops arbitrating once the live answer is older than a few query intervals1 ms
  • ✓pickBestCodexRateLimits › ignores a family fingerprint whose own weekly window has already elapsed0 ms
  • ✓pickBestCodexRateLimits › lets a caller with no live reading opt out of the family guard entirely0 ms
  • ✓pickBestCodexRateLimits › lets a credits-only live answer win over a leftover windowed rollout0 ms
  • ✓pickBestCodexRateLimits › does not let a contentless block win on recency, in either direction0 ms
  • ✓pickBestCodexRateLimits › does not let a windowless live block veto a fully-windowed passive reading0 ms
  • ✓pickBestCodexRateLimits › keeps preferring the fresher passive reading within the account family0 ms
  • ✓codexSnapshotsShareLimitFamily › separates the Spark pool from the account even when both claim `codex`0 ms
  • ✓codexSnapshotsShareLimitFamily › treats a missing side as no information rather than a mismatch0 ms
  • ✓codexSnapshotsShareLimitFamily › tolerates the seconds of jitter Codex puts on the same weekly window0 ms
  • ✓codexSnapshotsShareLimitFamily › ignores the 5h window, whose reset instant slides with every request0 ms
  • ✓codexSnapshotsShareLimitFamily › reads the weekly fingerprint off whichever slot still carries a reset0 ms
  • ✓codexSnapshotsShareLimitFamily › reports a different id as a different family0 ms
  • ✓codexLiveFamilyAuthorityExpiry › holds for the fresher rollout the picker keeps, not only for the live block itself1 ms
  • ✓codexLiveFamilyAuthorityExpiry › is false with no live reading, and false when the live one cannot reject0 ms
  • ✓codexLiveFamilyAuthorityExpiry › refuses a rolling window, which is a countdown rather than an anchor0 ms
  • ✓codexLiveFamilyAuthorityExpiry › is false when what was published belongs to another family0 ms
  • ✓liveCorroboratesPassiveFamily › is NOT the complement of liveRejectsPassiveFamily, which is the point0 ms
  • ✓liveCorroboratesPassiveFamily › lets a plan that structurally has no weekly window earn the skip0 ms
  • ✓liveCorroboratesPassiveFamily › does not let a content-free answer corroborate its way into the deadlock0 ms
  • ✓liveCorroboratesPassiveFamily › earns the skip only with a present, current, agreeing answer0 ms
  • ✓codexRateLimitsWithLiveRefresh — throttle wiring › spends a query when the cached live answer has aged out, mid-turn or not17 ms
  • ✓codexRateLimitsWithLiveRefresh — throttle wiring › keeps the mid-turn skip while the cached answer is fresh and agrees0 ms
  • ✓shouldQueryCodexRateLimitsLive › queries when the passive snapshot has gone quiet0 ms
  • ✓shouldQueryCodexRateLimitsLive › skips while Codex is mid-turn (the rollout is writing fresh readings)0 ms
  • ✓shouldQueryCodexRateLimitsLive › always asks once, even mid-turn, when nothing has been asked yet0 ms
  • ✓shouldQueryCodexRateLimitsLive › queries a fresh passive snapshot when its plan is one the account no longer holds0 ms
  • ✓shouldQueryCodexRateLimitsLive › does not let a plan mismatch defeat the spawn throttles0 ms
  • ✓shouldQueryCodexRateLimitsLive › queries a fresh passive snapshot that describes a different limit family0 ms
  • ✓shouldQueryCodexRateLimitsLive › stops treating an elapsed live fingerprint as a family mismatch0 ms
  • ✓shouldQueryCodexRateLimitsLive › honours the minimum interval between spawns0 ms
  • ✓shouldQueryCodexRateLimitsLive › backs off hard after repeated misses (no Codex CLI installed)0 ms
  • ✓codexSpawnPlan › runs a plain binary directly on posix0 ms
  • ✓codexSpawnPlan › asks for a shell on Windows .cmd/.bat shims, which spawn refuses to run bare0 ms
  • ✓codexSpawnPlan › quotes a shim path with spaces, since the shell re-parses the command line0 ms
  • ✓codexSpawnPlan › leaves a real Windows executable alone — the shell is only for the shims0 ms
  • ✓codexSpawnPlan › never asks for a shell on posix, even for a file that happens to end in .cmd0 ms
  • ✓queryCodexRateLimitsLive › reads the rate limits out of the JSON-RPC stream47 ms
  • ✓queryCodexRateLimitsLive › resolves null when the server never answers, without hanging306 ms
  • ✓queryCodexRateLimitsLive › resolves null when the binary does not exist5 ms
  • ✓queryCodexRateLimitsLive › resolves null when the server exits before replying55 ms
bridge/src/__tests__/codex-rate-limits.test.ts24/24
  • ✓parseCodexRateLimitsFromText › parses primary/secondary windows and converts resets_at to ISO3 ms
  • ✓parseCodexRateLimitsFromText › returns the most recent snapshot when multiple lines are present0 ms
  • ✓parseCodexRateLimitsFromText › clamps used_percent into 0..1000 ms
  • ✓parseCodexRateLimitsFromText › tolerates a truncated leading line (tail window cut mid-line)0 ms
  • ✓parseCodexRateLimitsFromText › returns null when no rate_limits line exists0 ms
  • ✓parseCodexRateLimitsFromText › omits a window missing required fields0 ms
  • ✓parseCodexRateLimitsFromText › keeps a credit-based snapshot when windows are null0 ms
  • ✓parseCodexRateLimitsFromText › prefers a windowed snapshot over an older credits-only one0 ms
  • ✓parseCodexRateLimitsFromText › still returns null when neither windows, credits, nor limitId are present0 ms
  • ✓pickCodexRateLimits (file selection across day-dirs) › returns null when the tree does not exist1 ms
  • ✓pickCodexRateLimits (file selection across day-dirs) › picks the active prior-day rollout over a newer empty day-dir10 ms
  • ✓pickCodexRateLimits (file selection across day-dirs) › falls through a newer empty file to an older one with data in the same day-dir1 ms
  • ✓pickCodexRateLimits (file selection across day-dirs) › chooses the newest captured snapshot across concurrent active rollouts1 ms
  • ✓pickCodexRateLimits (file selection across day-dirs) › invalidates the cache when a non-leading rollout writes a newer snapshot2 ms
  • ✓pickCodexRateLimits (file selection across day-dirs) › reaches across a month boundary for a still-active prior-month session1 ms
  • ✓pickCodexRateLimits (plan-aware selection) › takes the older snapshot that matches the account tier1 ms
  • ✓pickCodexRateLimits (plan-aware selection) › keeps pure newest-wins when the account tier is unknown1 ms
  • ✓pickCodexRateLimits (plan-aware selection) › still returns a mismatched snapshot when it is the only one1 ms
  • ✓pickCodexRateLimits (limit family) › scans past a newer per-model line to the account-wide one behind it1 ms
  • ✓pickCodexRateLimits (limit family) › reports no snapshot rather than one model's quota as the account's1 ms
  • ✓pickCodexRateLimits (limit family) › keeps the credit-plan family, which is account-wide despite its odd id1 ms
  • ✓capturedAt (snapshot freshness anchor) › stamps the snapshot line timestamp, not the file mtime1 ms
  • ✓capturedAt (snapshot freshness anchor) › falls back to the file mtime when the line carries no timestamp1 ms
  • ✓capturedAt (snapshot freshness anchor) › falls back to the file mtime when the line timestamp is unparseable1 ms
bridge/src/__tests__/hook-server-usage-relay.test.ts3/3
  • ✓GET /usage — relay source contract › serves numbers together with a positive fetchedAt93 ms
  • ✓GET /usage — relay source contract › withholds numbers that carry no timestamp (fetchedAt 0)14 ms
  • ✓GET /usage — relay source contract › reports the same empty shape when no usage source is registered7 ms
bridge/src/__tests__/relayed-usage.test.ts18/18
  • ✓resolveRelayedUsageEvent — branch selection › never builds the daemon aggregate when the relay carries Claude quota5 ms
  • ✓resolveRelayedUsageEvent — branch selection › counts a lone sevenDayPercent as Claude quota too0 ms
  • ✓resolveRelayedUsageEvent — branch selection › splits by half when the relay carries no Claude quota1 ms
  • ✓resolveRelayedUsageEvent — Codex block reconciliation (#253) › replaces the bridge's voided block with the daemon's live window1 ms
  • ✓resolveRelayedUsageEvent — Codex block reconciliation (#253) › keeps the bridge's block when it is the newer reading0 ms
  • ✓resolveRelayedUsageEvent — Codex block reconciliation (#253) › keeps the daemon's block when the bridge relays a different limit family1 ms
  • ✓resolveRelayedUsageEvent — Codex block reconciliation (#253) › lets the authority lapse on this event's clock, not the build's0 ms
  • ✓resolveRelayedUsageEvent — Codex block reconciliation (#253) › falls back to recency when the daemon has no live reading to arbitrate with0 ms
  • ✓resolveRelayedUsageEvent — Codex block reconciliation (#253) › never lets the daemon's contentless block displace a windowed relayed one0 ms
  • ✓resolveRelayedUsageEvent — Codex block reconciliation (#253) › supplies the daemon's block when the relay says nothing about Codex0 ms
  • ✓resolveRelayedUsageEvent — Codex block reconciliation (#253) › leaves the relayed event untouched when the daemon has never built one0 ms
  • ✓resolveRelayedUsageEvent — Codex block reconciliation (#253) › keeps the relayed block on an exact tie rather than churning the object0 ms
  • ✓resolveRelayedUsageEvent — Codex block reconciliation (#253) › does not reconcile Codex on the merged branch — that block is already the daemon's0 ms
  • ✓resolveRelayedUsageEvent — z.ai provider block › re-attaches the daemon's remembered z.ai block onto a Claude-bearing relay0 ms
  • ✓resolveRelayedUsageEvent — z.ai provider block › retains the daemon z.ai plan alongside a session subscription snapshot []2 ms
  • ✓resolveRelayedUsageEvent — z.ai provider block › retains the daemon z.ai plan alongside a session subscription snapshot [ { name: 'Claude' }, …(1) ]1 ms
  • ✓resolveRelayedUsageEvent — z.ai provider block › replaces an old z.ai plan once and removes it on explicit retirement2 ms
  • ✓resolveRelayedUsageEvent — z.ai provider block › keeps the identity return when there is no z.ai block to attach0 ms
bridge/src/__tests__/usage-api-recovery.test.ts14/14
  • ✓usage fetching and recovery › stamps retryAfter onto the cache as it is now, not the snapshot it started with190 ms
  • ✓usage fetching and recovery › recovers an expired credential and retries with the newly read token in the same poll44 ms
  • ✓usage fetching and recovery › does not claim success when CLI exits but leaves credentials expired27 ms
  • ✓usage fetching and recovery › recovers a server-rejected credential that records no local expiry20 ms
  • ✓usage fetching and recovery › recovers a server-rejected token even when its local expiry is in the future21 ms
  • ✓usage fetching and recovery › retries a transient 401 with an unchanged valid token (recovery=false)16 ms
  • ✓usage fetching and recovery › retries a transient 401 with an unchanged valid token (recovery=true)17 ms
  • ✓usage fetching and recovery › does not loop on a 403 by spending quota on authentication probes13 ms
  • ✓usage fetching and recovery › never spawns recovery from a session bridge or when opted out15 ms
  • ✓usage fetching and recovery › uses a token with five minutes remaining rather than inventing early expiry16 ms
  • ✓usage fetching and recovery › keeps Retry-After separate from freshness, including after module restart23 ms
  • ✓usage fetching and recovery › honors Retry-After even without an existing cache11 ms
  • ✓usage fetching and recovery › deduplicates simultaneous API polls14 ms
  • ✓usage fetching and recovery › backs off from failures even without a successful cache and recovers on rotation12 ms
bridge/src/__tests__/usage-api.test.ts25/25
  • ✓resolveOAuthCredentials › darwin: parses the Keychain blob and never reads a file6 ms
  • ✓resolveOAuthCredentials › win32: reads <configDir>/.credentials.json and never spawns the security CLI2 ms
  • ✓resolveOAuthCredentials › win32: CLAUDE_CODE_OAUTH_TOKEN takes precedence over the file, expiresAt undefined0 ms
  • ✓resolveOAuthCredentials › win32: an empty CLAUDE_CODE_OAUTH_TOKEN falls through to the file0 ms
  • ✓resolveOAuthCredentials › win32: a whitespace-only CLAUDE_CODE_OAUTH_TOKEN falls through to the file0 ms
  • ✓resolveOAuthCredentials › win32: a padded env token is trimmed before use0 ms
  • ✓resolveOAuthCredentials › linux: honors a relocated configDir (CLAUDE_CONFIG_DIR)1 ms
  • ✓resolveOAuthCredentials › win32: neither file nor env → null, no throw0 ms
  • ✓resolveOAuthCredentials › win32: malformed / partial JSON → null, never throws1 ms
  • ✓resolveOAuthCredentials › win32: valid token but non-numeric expiresAt → token kept, expiresAt undefined1 ms
  • ✓resolveOAuthCredentials › darwin: security CLI returns null (no keychain entry) → null0 ms
  • ✓buildCredSources (production wiring) › wires the real platform, env, and default configDir1 ms
  • ✓buildCredSources (production wiring) › honors CLAUDE_CONFIG_DIR when set0 ms
  • ✓buildCredSources (production wiring) › passes the security + file readers as UN-invoked function references (lazy thunks)0 ms
  • ✓parseScopedLimits › keeps only model-scoped entries (drops session / weekly_all)1 ms
  • ✓parseScopedLimits › sorts worst-first: active ahead of inactive, then by descending percent1 ms
  • ✓parseScopedLimits › inactive limits sort by descending percent0 ms
  • ✓parseScopedLimits › falls back to kind when the model has no display_name0 ms
  • ✓parseScopedLimits › skips entries without a numeric percent0 ms
  • ✓parseScopedLimits › returns [] for non-array / missing input0 ms
  • ✓fileCacheExpired — TTL slack › expires a cache that is a fetch-latency hair under the nominal TTL0 ms
  • ✓fileCacheExpired — TTL slack › expires at the nominal TTL and beyond0 ms
  • ✓fileCacheExpired — TTL slack › still holds a genuinely recent cache — the cross-session 429 dedupe survives0 ms
  • ✓fileCacheExpired — TTL slack › treats a future or unusable stamp as expired, never as the freshest cache0 ms
  • ✓fileCacheExpired — TTL slack › slack stays well inside the poll interval0 ms
bridge/src/__tests__/usage-event.test.ts39/39
  • ✓buildUsageEvent subscription quota scoping › omits Anthropic quota when the model is not yet known3 ms
  • ✓buildUsageEvent subscription quota scoping › omits Anthropic 5h/7d quota for GLM/API-backed models0 ms
  • ✓buildUsageEvent subscription quota scoping › keeps Anthropic 5h/7d quota on the daemon hub even with a non-Claude aggregate model0 ms
  • ✓buildUsageEvent subscription quota scoping › keeps Anthropic 5h/7d quota for Claude model aliases0 ms
  • ✓buildUsageEvent scoped limits › forwards scopedLimits for a subscription Claude model1 ms
  • ✓buildUsageEvent scoped limits › omits scopedLimits for an API/GLM-backed model even when present0 ms
  • ✓buildUsageEvent scoped limits › omits scopedLimits when the scoped array is empty (no phantom key)0 ms
  • ✓buildUsageEvent staleness contract › flags usageStale when no quota numbers ride the frame (never fetched)0 ms
  • ✓buildUsageEvent staleness contract › does not flag usageStale when fresh quota numbers are present0 ms
  • ✓buildUsageEvent staleness contract › always puts usageStale on the wire, never omits it0 ms
  • ✓buildUsageEvent staleness contract › retires cached quota on a stale frame1 ms
  • ✓buildUsageEvent staleness contract › does not force usageStale when the cost-based API-billing percent is present0 ms
  • ✓buildUsageEvent Codex window normalization › retires reserve for recovered or ended windows (19, expired=false)1 ms
  • ✓buildUsageEvent Codex window normalization › retires reserve for recovered or ended windows (100, expired=false)0 ms
  • ✓buildUsageEvent Codex window normalization › retires reserve for recovered or ended windows (100, expired=true)0 ms
  • ✓buildUsageEvent Codex window normalization › marks an expired window stale and drops its resetsAt (no misleading "now")0 ms
  • ✓buildUsageEvent Codex window normalization › leaves a live window untouched0 ms
  • ✓buildUsageEvent Codex window normalization › forwards capturedAt so consumers can age the reading themselves0 ms
  • ✓buildUsageEvent Codex window normalization › does NOT set `stale` on a merely-old snapshot of a live window1 ms
  • ✓buildUsageEvent Codex window normalization › keeps capturedAt through the credits-only synthetic window0 ms
  • ✓buildUsageEvent Codex window normalization › treats each window independently (5h expired, 7d live)0 ms
  • ✓buildUsageEvent Codex window normalization › routes a weekly window arriving in Codex's primary slot to the secondary (7D) wire slot0 ms
  • ✓buildUsageEvent Codex window normalization › maps exhausted credit-based plan (null windows) to a 100% primary gauge0 ms
  • ✓buildUsageEvent Codex window normalization › maps an unlimited credit plan to a 0% primary gauge0 ms
  • ✓buildUsageEvent Codex window normalization › leaves a partial credit balance absent (no honest percentage)0 ms
  • ✓mergeRelayedSessionUsage › keeps the relay session counters0 ms
  • ✓mergeRelayedSessionUsage › keeps the daemon account half authoritative — a quota-less relay cannot blank the gauge0 ms
  • ✓mergeRelayedSessionUsage › leaves the daemon value in place for a session field the relay omits0 ms
  • ✓mergeRelayedSessionUsage › does not mutate either input0 ms
  • ✓buildUsageEvent Codex plan reconciliation › retires expired Luna metadata on the common wire for WiFi and serial clients0 ms
  • ✓buildUsageEvent Codex plan reconciliation › voids a snapshot minted under a plan the account no longer holds0 ms
  • ✓buildUsageEvent Codex plan reconciliation › keeps a snapshot whose plan still matches the account0 ms
  • ✓buildUsageEvent Codex plan reconciliation › keeps a genuine free-tier snapshot — free is a plan, not a synonym for void0 ms
  • ✓buildUsageEvent Codex plan reconciliation › keeps the snapshot when either side reports no plan (absence is no information)0 ms
  • ✓buildUsageEvent Codex plan reconciliation › emits the account tier even with no rollout at all, so a client can RETRACT0 ms
  • ✓buildUsageEvent Codex plan reconciliation › stays absent for a user with no Codex account at all0 ms
  • ✓buildUsageEvent Codex plan reconciliation › still names the plan in the subscriptions footer after voiding0 ms
  • ✓subscription replacement snapshots › does not treat session billing or windowless cache as a current subscription0 ms
  • ✓subscription replacement snapshots › removes only Claude on failure and restores it on a fresh reading0 ms
bridge/src/__tests__/usage-relay-fallback.test.ts5/5
  • ✓fetchUsageRelayed — direct-API fallback (regression: fa1d98c9) › no siblings → uses the direct API3 ms
  • ✓fetchUsageRelayed — direct-API fallback (regression: fa1d98c9) › siblings exist but the relay yields nothing → falls back to the direct API24 ms
  • ✓fetchUsageRelayed — direct-API fallback (regression: fa1d98c9) › siblings exist but only serve null usage → falls back to the direct API25 ms
  • ✓fetchUsageRelayed — direct-API fallback (regression: fa1d98c9) › sibling serves fresh usage → relay wins, direct API not called (429 prevention preserved)6 ms
  • ✓fetchUsageRelayed — direct-API fallback (regression: fa1d98c9) › stale sibling data (>5 min old) is ignored → falls back to the direct API4 ms
bridge/src/__tests__/usage-relay.test.ts14/14
  • ✓Usage relay — HTTP (Tier 1) › fetches usage from sibling GET /usage39 ms
  • ✓Usage relay — HTTP (Tier 1) › returns null usage when sibling has no data9 ms
  • ✓Usage relay — HTTP (Tier 1) › rejects stale data (>5 min old)4 ms
  • ✓Usage relay — WebSocket (Tier 2) › receives usage_update from sibling WS on connect266 ms
  • ✓Usage relay — WebSocket (Tier 2) › does not send usage_update when sibling has no data257 ms
  • ✓Usage relay — scoped limits round-trip › preserves scopedLimits (active + severity) over the sibling WS relay254 ms
  • ✓Usage relay — scoped limits round-trip › carries scopedLimits through the HTTP /usage relay tier4 ms
  • ✓HookServer GET /usage (relay source) › returns cached usage via onApiUsage getter8 ms
  • ✓HookServer GET /usage (relay source) › returns fresh usage after update7 ms
  • ✓429 prevention — relay-first strategy › sibling with data prevents direct API call3 ms
  • ✓429 prevention — relay-first strategy › multiple siblings — first with data wins4 ms
  • ✓ApiUsageData shape › has all required fields0 ms
  • ✓ApiUsageData shape › serializes to valid JSON and back0 ms
  • ✓ApiUsageData shape › handles null optional fields0 ms
bridge/src/__tests__/zai-usage.test.ts14/14
  • ✓zaiCacheExpired (TTL/slack pair) › expires at TTL minus slack, so a 60s poll dividing the 120s TTL cannot double it141 ms
  • ✓fetchZaiQuota › answers null data with no key configured anywhere33 ms
  • ✓fetchZaiQuota › maps the Max-plan standard schema onto wire windows and caches the reading35 ms
  • ✓fetchZaiQuota › treats an HTTP-200 error envelope as a failure and serves the cache as not-fresh38 ms
  • ✓fetchZaiQuota › reports a pay-as-you-go key as an explicit windowless block, never as gauges14 ms
  • ✓fetchZaiQuota › does not reuse another account cache or backoff when the key changes21 ms
  • ✓fetchZaiQuota › retires cached plan windows immediately for a pay-as-you-go replacement21 ms
  • ✓fetchZaiQuota › discards a response whose credential was replaced in flight14 ms
  • ✓fetchZaiQuota › discovers the key from the Claude Code settings hint only when the base URL is z.ai16 ms
  • ✓fetchZaiQuota › ignores the hint when Claude Code points elsewhere15 ms
  • ✓fetchZaiQuota › never writes the key into a log line16 ms
  • ✓buildUsageEvent z.ai block › normalizes windows (ended window → stale, reset cleared) and keeps plan axes7 ms
  • ✓buildUsageEvent z.ai block › adds the GLM Coding Plan row with the tier when windows are live0 ms
  • ✓buildUsageEvent z.ai block › omits the block entirely when the provider is not configured0 ms
shared/src/__tests__/codex-freshness-rules.test.ts11/11
  • ✓codex freshness threshold invariants › is an absolute duration, not a fraction of any window3 ms
  • ✓codex freshness threshold invariants › is a whole number of minutes, so every platform can print it0 ms
  • ✓generated mirrors in sync › apple/AgentDeck/Model/CodexFreshnessRules.generated.swift matches the SSOT1 ms
  • ✓generated mirrors in sync › android/app/src/main/kotlin/dev/agentdeck/util/CodexFreshnessRules.generated.kt matches the SSOT0 ms
  • ✓generated mirrors in sync › emits the threshold in each platform's own unit0 ms
  • ✓generated mirrors in sync › mirrors the plan-reconciliation rule to Swift (the other PRODUCER of the wire snapshot)1 ms
  • ✓generated mirrors in sync › mirrors the plan-aware RANKING to Swift, not just the void rule0 ms
  • ✓generated mirrors in sync › emits every plan display name from the SSOT table to BOTH mirrors2 ms
  • ✓generated mirrors in sync › emits the same three footnote states the TS SSOT resolves0 ms
  • ✓TS side still produces what the mirrors promise › names a tier the build predates without dropping it or showing it raw1 ms
  • ✓TS side still produces what the mirrors promise › threshold boundary matches the emitted constant2 ms
shared/src/__tests__/usage-presentation.test.ts11/11
  • ✓shared usage display policy › keeps regular windows until exhausted, then restores them after reset4 ms
  • ✓shared usage display policy › expires reserve and regular windows independently0 ms
  • ✓shared usage display policy › attributes reported subscription metadata without inventing a provider1 ms
  • ✓shared usage display policy › prints the plan tier beside the brand mark, never the provider twice1 ms
  • ✓shared usage display policy › generates the firmware predicate and provider table from the actual source2 ms
  • ✓shared usage display policy › Codex credits after an exhausted plan window › formats the balance from the shared vectors1 ms
  • ✓shared usage display policy › Codex credits after an exhausted plan window › gates on an exhausted window and a positive balance0 ms
  • ✓shared usage display policy › Codex credits after an exhausted plan window › reads the wire balance, treating hasCredits:false as zero0 ms
  • ✓shared usage display policy › Codex credits after an exhausted plan window › shows credits only while a live window is exhausted and a balance remains1 ms
  • ✓shared usage display policy › Codex credits after an exhausted plan window › reports the latest exhausted reset as the end of credit spending0 ms
  • ✓shared usage display policy › Codex credits after an exhausted plan window › ignores an unparseable reset instead of letting it win the sort0 ms
shared/src/__tests__/usage-severity.test.ts16/16
  • ✓quota severity across dashboards › classifies 0% normal consumed as undefined6 ms
  • ✓quota severity across dashboards › classifies 69.9% normal consumed as undefined0 ms
  • ✓quota severity across dashboards › classifies 70% warning consumed as undefined0 ms
  • ✓quota severity across dashboards › classifies 82% warning consumed as undefined0 ms
  • ✓quota severity across dashboards › classifies 89.9% warning consumed as undefined0 ms
  • ✓quota severity across dashboards › classifies 90% critical consumed as undefined0 ms
  • ✓quota severity across dashboards › classifies 100% critical consumed as undefined0 ms
  • ✓quota severity across dashboards › classifies 101% critical consumed as undefined0 ms
  • ✓quota severity across dashboards › does not portray unknown undefined as healthy zero0 ms
  • ✓quota severity across dashboards › does not portray unknown null as healthy zero0 ms
  • ✓quota severity across dashboards › does not portray unknown -1 as healthy zero0 ms
  • ✓quota severity across dashboards › does not portray unknown NaN as healthy zero0 ms
  • ✓quota severity across dashboards › does not portray unknown Infinity as healthy zero0 ms
  • ✓quota severity across dashboards › keeps remaining and consumed readings equivalent, with freshness first0 ms
  • ✓quota severity across dashboards › keeps small percentage text legible on dark screens and paper1 ms
  • ✓quota severity across dashboards › generates every native palette and boundary from the source1 ms
shared/src/__tests__/zai-pair-view.test.ts3/3
  • ✓z.ai pair view › cycles both → first → second → both3 ms
  • ✓z.ai pair view › never invents a missing window1 ms
  • ✓z.ai pair view › accepts only known saved modes0 ms
shared/src/__tests__/zai-quota.test.ts20/20
  • ✓zai quota vectors (shared with the Swift suite) › standard schema, Max plan — live capture 2026-09-19: TOKENS_LIMIT is the 5h credits window (percentage only), TIME_LIMIT the monthly MCP window with full numbers3 ms
  • ✓zai quota vectors (shared with the Swift suite) › credit schema, lite plan — CREDIT_LIMIT items told apart by unit (3=hours×5 session, 6=weeks×1 weekly); percent derived from currentValue/total when percentage is absent0 ms
  • ✓zai quota vectors (shared with the Swift suite) › CREDIT_LIMIT capacity may ride `usage` instead of `total` — currentValue stays the consumed amount0 ms
  • ✓zai quota vectors (shared with the Swift suite) › credit item with an unrecognized unit is skipped as a window but still marks the response's family0 ms
  • ✓zai quota vectors (shared with the Swift suite) › 5h window with no nextResetTime — absent is unknown, never a fabricated reset0 ms
  • ✓zai quota vectors (shared with the Swift suite) › weekly preferred over MCP for the secondary slot when a schema reports both0 ms
  • ✓zai quota vectors (shared with the Swift suite) › percentage above 100 clamps — the wire never carries >100%0 ms
  • ✓zai quota vectors (shared with the Swift suite) › unclassifiable items (unknown type, non-object) carry nothing; a plan level alone still rides0 ms
  • ✓zai quota vectors (shared with the Swift suite) › empty item list read nothing — plan rides, family does not0 ms
  • ✓zai quota vectors (shared with the Swift suite) › absent limits and absent level — no information at all0 ms
  • ✓zai pay-as-you-go key detection › community PAYG marker prefix0 ms
  • ✓zai pay-as-you-go key detection › payg anywhere in the key0 ms
  • ✓zai pay-as-you-go key detection › case-insensitive0 ms
  • ✓zai pay-as-you-go key detection › a normal coding-plan key0 ms
  • ✓zai pay-as-you-go key detection › empty is not a PAYG claim — it is no key at all0 ms
  • ✓zai pay-as-you-go key detection › null is no key0 ms
  • ✓zai pay-as-you-go key detection › an unknown tier still displays, never drops0 ms
  • ✓generated mirrors in sync › apple/AgentDeck/Model/ZaiQuotaRules.generated.swift matches the SSOT0 ms
  • ✓generated mirrors in sync › android/app/src/main/kotlin/dev/agentdeck/util/ZaiQuotaRules.generated.kt matches the SSOT0 ms
  • ✓generated mirrors in sync › carries the window constants so a unit slip cannot hide1 ms

25 files

Pass

Daemon lifecycle & CLI

Does exactly one daemon own the port, start, upgrade, restart and hand over cleanly on every OS?

Passed
381
Failed
0
Skipped
3
Time
10.6 s
25 test files
bridge/src/__tests__/agent-cli-diagnostics.test.ts4/4
  • ✓agent CLI compatibility diagnostics › reports installed versions against package-owned compatibility ranges5 ms
  • ✓agent CLI compatibility diagnostics › reports a missing binary without exposing command output2 ms
  • ✓agent CLI compatibility diagnostics › formats an actionable daemon-first report1 ms
  • ✓does not confuse written configuration with active observation1 ms
bridge/src/__tests__/cli.test.ts71/71
  • ✓agentdeck CLI parser › registers a weather command with set/show/clear subcommands4 ms
  • ✓agentdeck CLI parser › reports a misspelled top-level command as unknown and suggests the closest command6 ms
  • ✓agentdeck CLI parser › marks every managed per-session command as legacy compatibility in --help metadata1 ms
  • ✓managed PTY compatibility contract › points agentdeck claude users to daemon-first direct launch1 ms
  • ✓managed PTY compatibility contract › points agentdeck codex users to daemon-first direct launch1 ms
  • ✓managed PTY compatibility contract › points agentdeck opencode users to daemon-first direct launch1 ms
  • ✓managed PTY compatibility contract › points monitor users to all normal agent entry points0 ms
  • ✓esp32-ota target resolution › maps short aliases to their canonical device_info.board string1 ms
  • ✓esp32-ota target resolution › leaves a canonical board string unchanged0 ms
  • ✓esp32-ota target resolution › passes an unknown target (e.g. a raw IP) through untouched for IP targeting0 ms
  • ✓esp32-ota target resolution › every alias resolves to a board that is itself a canonical entry (self-consistent SSOT)2 ms
  • ✓esp32-ota target resolution › drops the retired esp32_c6_147 board from the OTA target set0 ms
  • ✓esp32-ota manifest identity resolution › selects one exact product + board + channel tuple1 ms
  • ✓esp32-ota manifest identity resolution › fails instead of guessing when a board has no registered identity2 ms
  • ✓esp32-ota sandbox retry payload › preserves stage and the exact Surface namespace on inline retry0 ms
  • ✓esp32-ota sandbox retry payload › keeps the live retry a live OTA request0 ms
  • ✓tokenizeArgString › splits bare flags on whitespace1 ms
  • ✓tokenizeArgString › collapses runs of whitespace and trims0 ms
  • ✓tokenizeArgString › keeps a quoted value with spaces as one token0 ms
  • ✓tokenizeArgString › returns [] for empty or undefined0 ms
  • ✓applyGlobalEnvArgs › splices env tokens immediately after the session subcommand0 ms
  • ✓applyGlobalEnvArgs › inserts env tokens before user-typed flags (scalar options: retyping wins on last-write)0 ms
  • ✓applyGlobalEnvArgs › applies to monitor as well0 ms
  • ✓applyGlobalEnvArgs › is a no-op for non-session commands1 ms
  • ✓applyGlobalEnvArgs › is a no-op for a non-session command whose positional VALUE is a session word0 ms
  • ✓applyGlobalEnvArgs › is a no-op when the var is unset or empty0 ms
  • ✓applyGlobalEnvArgs › does not match a session word that appears as a flag value0 ms
  • ✓applyGlobalEnvArgs › skips the splice entirely when the user typed --no-env-args0 ms
  • ✓applyGlobalEnvArgs › strips an env-smuggled --no-env-args instead of letting it half-disable the layers0 ms
  • ✓weaveAgentCommand › appends the per-agent var to the default command0 ms
  • ✓weaveAgentCommand › appends onto an explicit -c command without replacing it4 ms
  • ✓weaveAgentCommand › uses the agent-specific var and ignores others0 ms
  • ✓weaveAgentCommand › returns the command unchanged when the var is unset or blank1 ms
  • ✓weaveAgentCommand › leaves the user's shell quoting and metacharacters untouched0 ms
  • ✓weaveAgentCommand › does not re-quote a Windows-style command0 ms
  • ✓weaveAgentCommand › appends a quoted env value verbatim rather than tokenizing it0 ms
  • ✓resolveAgentCommand (per-agent half of the --no-env-args hatch) › weaves when env args are enabled0 ms
  • ✓resolveAgentCommand (per-agent half of the --no-env-args hatch) › returns the command untouched when the hatch disabled env args0 ms
  • ✓env defaults through real option parsing › an env-provided boolean flag sticks as the default1 ms
  • ✓env defaults through real option parsing › --no-env-args genuinely overrides an env boolean per-invocation0 ms
  • ✓env defaults through real option parsing › a retyped scalar option wins over the env default (last-write through parseArg)1 ms
  • ✓daemon autostart posture › --enterprise is the admin spelling of the loopback posture8 ms
  • ✓daemon autostart posture › does not fold --local into --enterprise1 ms
  • ✓daemon autostart posture › a plain install carries no posture flags0 ms
  • ✓daemon autostart posture › the LaunchAgent argv carries the posture, one <string> per flag10 ms
  • ✓daemon autostart posture › a plain LaunchAgent is byte-identical to the pre-posture one14 ms
  • ✓worktree checkout guard (autostart/plugin installs) › classifies a worktree path by path segment0 ms
  • ✓worktree checkout guard (autostart/plugin installs) › accepts the main checkout and npm-global install paths0 ms
  • ✓worktree checkout guard (autostart/plugin installs) › matches only the whole path segment, not a substring of another name0 ms
  • ✓worktree checkout guard (autostart/plugin installs) › classifies Windows-style paths on any platform0 ms
  • ✓claude action wiring (parseAsync end-to-end) › prints the compatibility notice before starting the still-functional bridge6 ms
  • ✓claude action wiring (parseAsync end-to-end) › without the hatch: env flags parse in and the agent command is woven2 ms
  • ✓claude action wiring (parseAsync end-to-end) › --no-env-args disables BOTH layers for the invocation1 ms
  • ✓claude action wiring (parseAsync end-to-end) › a session bridge drives no device but ADB, even without --local1 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › reports an incumbent Swift daemon rather than false Node readiness603 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › waits through Swift health until Node takes ownership302 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › finds Node on a fallback while Swift still answers on the preferred port0 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › returns no-daemon when nothing ever answers602 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › does NOT accept the daemon it was trying to replace601 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › accepts the spawned pid on the preferred port1 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › accepts a supervisor respawn on the same build and flags it as not ours0 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › survives the child exiting deliberately before the supervisor binds602 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › refuses a respawn serving a DIFFERENT build than the one on disk601 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › does not call an unknown build a mismatch1 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › ignores an answer that is explicitly not a daemon601 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › keeps waiting past the floor while the child is still alive601 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › gives up once the child has EXITED without anything answering300 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › finds the daemon on a fallback port and reports THAT port1 ms
  • ✓waitForRestartedDaemon — `daemon restart` reports what it measured › waits rather than giving up on the first miss602 ms
  • ✓observed run environment arguments › inserts defaults before typed run options0 ms
  • ✓observed run environment arguments › keeps the typed both-layer escape hatch for run0 ms
bridge/src/__tests__/connection-diagnostics.test.ts10/10
  • ✓local connection diagnostics › probes real health and ping/pong without copying private frames or modifying the registry57 ms
  • ✓local connection diagnostics › keeps an EPERM PID inconclusive and still probes the endpoint9 ms
  • ✓local connection diagnostics › skips dead entries without deleting them and uses the next usable registry17 ms
  • ✓local connection diagnostics › reports missing, unreadable and invalid registries without probing an assumed default port7 ms
  • ✓local connection diagnostics › rejects a reused PID even when health and WebSocket work5 ms
  • ✓local connection diagnostics › supports separate HTTP and WS ports and an explicit target without registry identity claims11 ms
  • ✓local connection diagnostics › distinguishes HTTP reachability from a rejected WebSocket and strips rejection text5 ms
  • ✓local connection diagnostics › bounds an endless HTTP body and a WebSocket which never pongs119 ms
  • ✓local connection diagnostics › rejects malformed or non-daemon health and never echoes its body32 ms
  • ✓local connection diagnostics › caps a large HTTP response instead of retaining a full diagnostic dump37 ms
bridge/src/__tests__/daemon-build-identity.test.ts13/13
  • ✓findStaleSources › reports nothing when every build is newer than its source7 ms
  • ✓findStaleSources › names the package and the newest unbuilt file3 ms
  • ✓findStaleSources › compares per package, not against one global build time2 ms
  • ✓findStaleSources › treats a package that has never been built as stale1 ms
  • ✓findStaleSources › ignores test sources — they compile to nothing the daemon loads1 ms
  • ✓findStaleSources › says nothing about a package it cannot see1 ms
  • ✓findSourceCheckout › finds the workspace root from a package inside it1 ms
  • ✓findSourceCheckout › returns null for an installed package (a dist with no src beside it)1 ms
  • ✓distBuildIdentity › answers the same digest every time within a process21 ms
  • ✓distBuildIdentity › recomputes on demand for the before/after comparison around a build39 ms
  • ✓distBuildIdentity › is a short hex digest over real build output1 ms
  • ✓buildPackages › does nothing when nothing is stale1 ms
  • ✓buildPackages › reports a missing package manager as a reason, not as a failed build4 ms
bridge/src/__tests__/daemon-launcher.test.ts2/2
  • ✓detached daemon launcher › rejects an asynchronous OS spawn failure instead of reporting success8 ms
  • ✓detached daemon launcher › waits for successful creation before releasing the child26 ms
bridge/src/__tests__/daemon-lifecycle.test.ts21/21
  • ✓daemon.json lifecycle › writeDaemonInfo creates daemon.json with correct content4 ms
  • ✓daemon.json lifecycle › readDaemonInfo returns info when PID is alive1 ms
  • ✓daemon.json lifecycle › readDaemonInfo returns null and removes file when PID is dead1 ms
  • ✓daemon.json lifecycle › readDaemonInfo returns null when file does not exist0 ms
  • ✓daemon.json lifecycle › removeDaemonInfo deletes daemon.json1 ms
  • ✓daemon.json lifecycle › removeDaemonInfo is safe when file already gone1 ms
  • ✓daemon.json lifecycle › self-heals daemon.json after an external deletion2 ms
  • ✓daemon.json lifecycle › repairs a malformed daemon.json without rewriting a healthy record1 ms
  • ✓session registry with real files › register creates sessions.json3 ms
  • ✓session registry with real files › register replaces existing entry with same id1 ms
  • ✓session registry with real files › deregister removes session by id2 ms
  • ✓session registry with real files › listActive prunes dead PIDs2 ms
  • ✓session registry with real files › multiple sessions coexist2 ms
  • ✓session registry with real files › findExistingDaemon returns daemon session1 ms
  • ✓session registry with real files › findExistingDaemon returns null when no daemon1 ms
  • ✓findDaemonPort › returns port from daemon.json (priority 1)1 ms
  • ✓findDaemonPort › falls back to sessions.json daemon entry1 ms
  • ✓findDaemonPort › returns null when no daemon anywhere0 ms
  • ✓findDaemonPort › daemon.json takes precedence over sessions.json1 ms
  • ✓probeDaemonHealth › returns health JSON from running server29 ms
  • ✓probeDaemonHealth › returns null for closed port3 ms
bridge/src/__tests__/daemon-listen.test.ts9/9
  • ✓startup preferred-port reclaim › binds after a 58s silent macOS hold without a fallback or leaked listeners20 ms
  • ✓startup preferred-port reclaim › bounds a permanently silent conflict on darwin13 ms
  • ✓startup preferred-port reclaim › bounds a permanently silent conflict on linux3 ms
  • ✓startup preferred-port reclaim › bounds a permanently silent conflict on win326 ms
  • ✓startup preferred-port reclaim › stops retrying when a newly discovered peer requires concede1 ms
  • ✓startup preferred-port reclaim › stops retrying when a newly discovered peer requires fallback1 ms
  • ✓startup preferred-port reclaim › propagates a non-contention error without probing or retrying3 ms
  • ✓startup preferred-port reclaim › cleans up after a synchronous listen failure2 ms
  • ✓startup preferred-port reclaim › retains a real port after the incumbent socket closes, using the resolved bind address512 ms
bridge/src/__tests__/daemon-port.test.ts15/15
  • ✓daemon port — validation › accepts only integers inside the bindable range4 ms
  • ✓daemon port — validation › rejects text that parseInt would silently truncate1 ms
  • ✓daemon port — precedence › falls back to the documented default when nothing is set1 ms
  • ✓daemon port — precedence › orders flag › env › settings › default1 ms
  • ✓daemon port — precedence › reads a persisted port stored as a string (hand-edited settings.json)0 ms
  • ✓daemon port — precedence › ignores an unusable persisted port instead of refusing to start1 ms
  • ✓daemon port — precedence › ignores a malformed env override the same way0 ms
  • ✓daemon port — precedence › names every source0 ms
  • ✓daemon port — precedence › waits long enough to outlast the measured kernel port reservation0 ms
  • ✓daemon port — persistence round trip › persists, reads back, and clears4 ms
  • ✓daemon port — persistence round trip › preserves unrelated settings when writing1 ms
  • ✓daemon port — persistence round trip › writes over a malformed settings.json rather than refusing1 ms
  • ✓daemon port — persistence round trip › never records the port the daemon actually landed on3 ms
  • ✓port window exhaustion message › separates ports this install owns from ports it cannot see into1 ms
  • ✓port window exhaustion message › does not tell a user to stop sessions they do not have0 ms
bridge/src/__tests__/daemon-socket-drain.test.ts2/2
  • ✓daemon socket handover › resets an open http peer and releases the listener40 ms
  • ✓daemon socket handover › resets an open upgrade peer and releases the listener2 ms
bridge/src/__tests__/daemon-supervisor.test.ts40/40
  • ✓stop plans — a stop that stays stopped › launchd boots the job OUT; `launchctl stop` is not a stop here3 ms
  • ✓stop plans — a stop that stays stopped › a job that was never loaded is a legitimate state, not a failure0 ms
  • ✓stop plans — a stop that stays stopped › systemd and Task Scheduler stop through their own verbs0 ms
  • ✓start plans › launchd bootstraps before it kickstarts0 ms
  • ✓start plans › systemd and Task Scheduler start through their own verbs0 ms
  • ✓unit posture — read from what the installers write › reads the posture back out of the real LaunchAgent plist13 ms
  • ✓unit posture — read from what the installers write › reads the posture back out of the real systemd unit (quoted ExecStart words)1 ms
  • ✓unit posture — read from what the installers write › reads the posture back out of the real scheduled-task XML (bare argv)1 ms
  • ✓unit posture — read from what the installers write › a longer flag that merely starts with the same letters is not a match0 ms
  • ✓unit posture — read from what the installers write › the posture list is comparable as a string — same order as daemonPostureArgs4 ms
  • ✓routeDaemonLifecycle › an installed unit performs the command0 ms
  • ✓routeDaemonLifecycle › no unit means the CLI does it, as it always did0 ms
  • ✓routeDaemonLifecycle › a one-off flag the unit cannot carry takes it back0 ms
  • ✓routeDaemonLifecycle › a posture the unit does not carry takes it back — the enterprise downgrade0 ms
  • ✓routeDaemonLifecycle › an absent posture pair is not a mismatch0 ms
  • ✓routeDaemonLifecycle › flags outrank posture — both are reported one at a time0 ms
  • ✓oneOffFlagsBlockingSupervisor › a plain invocation blocks nothing0 ms
  • ✓oneOffFlagsBlockingSupervisor › every flag the unit cannot express is named0 ms
  • ✓runSupervisorPlan › an optional failure is not a failed plan24 ms
  • ✓runSupervisorPlan › a required failure is31 ms
  • ✓runSupervisorPlan › a missing binary never throws — the machine must not be left daemonless3 ms
  • ✓runSupervisorPlan › runs the plan in order and reports each command54 ms
  • ✓describeSupervisor › names the thing the user would have to go look at1 ms
  • ○supervisorJobRunning / supervisorLivenessProbe › answers for the real launchd job on this machine
  • ✓supervisorJobRunning / supervisorLivenessProbe › an unknown answer keeps the wait alive rather than ending it3 ms
  • ✓supervisorJobRunning / supervisorLivenessProbe › systemd transitional states are not an outcome0 ms
  • ✓supervisorJobRunning / supervisorLivenessProbe › a localized schtasks answer is unreadable, not "not running"1 ms
  • ✓supervisorJobRunning / supervisorLivenessProbe › a finished launcher defers to the daemon's own startedBy stamp0 ms
  • ✓supervisorJobRunning / supervisorLivenessProbe › preserves unknown ownership while a healthy daemon has no readable registry1 ms
  • ✓supervisorJobRunning / supervisorLivenessProbe › only a supervisor kind we recognise may come out of the environment0 ms
  • ✓supervisorJobRunning / supervisorLivenessProbe › only a command that ran and exited non-zero has answered1 ms
  • ✓supervisorJobRunning / supervisorLivenessProbe › a supervisor with no unit file has no posture to compare, not a default one0 ms
  • ✓supervisorJobRunning / supervisorLivenessProbe › an unreadable unit posture leaves the restart with the supervisor0 ms
  • ○supervisorJobRunning / supervisorLivenessProbe › caches within the throttle window
  • ✓classifySupervision — does the unit own the daemon this machine is running › a daemon answering with the job not running is the state install must converge0 ms
  • ✓classifySupervision — does the unit own the daemon this machine is running › a running job owns whatever daemon is up, or is about to be0 ms
  • ✓classifySupervision — does the unit own the daemon this machine is running › nothing answering with the job down is a failed start, not a handover0 ms
  • ✓classifySupervision — does the unit own the daemon this machine is running › a supervisor that did not answer is unknown, never unsupervised0 ms
  • ✓classifySupervision — does the unit own the daemon this machine is running › another user's daemon is never touched, whatever this machine's job is doing0 ms
  • ✓launchd removal barrier › waits through a loaded job and unknown probes before accepting removal1 ms
  • ✓launchd removal barrier › bounds a job that never proves removal (false)0 ms
  • ✓launchd removal barrier › bounds a job that never proves removal (undefined)0 ms
bridge/src/__tests__/daemon-takeover.test.ts23/23
  • ✓negotiateIncumbentDaemon › takes neither the token nor the port from another user's daemon6 ms
  • ✓negotiateIncumbentDaemon › refuses loudly when that port was asked for by name0 ms
  • ✓negotiateIncumbentDaemon › still hands the port over between two daemons of the same user1 ms
  • ✓negotiateIncumbentDaemon › captures the Swift activity projection before the listener stands down1 ms
  • ✓negotiateIncumbentDaemon › proceeds when ownership cannot be proven, because absence is not evidence1 ms
  • ✓negotiateIncumbentDaemon › falls back to /shutdown when the app is too old for /stand-down1 ms
  • ✓negotiateIncumbentDaemon › a live Node daemon of ours means "already running", not a takeover1 ms
  • ✓negotiateIncumbentDaemon › replaces a daemon of ours that is serving an older build1 ms
  • ✓negotiateIncumbentDaemon › leaves a matching build alone — that daemon IS this build1 ms
  • ✓negotiateIncumbentDaemon › treats a missing build on either side as no information, never as different1 ms
  • ✓negotiateIncumbentDaemon › --no-upgrade leaves the older daemon running, and says which build it is1 ms
  • ✓negotiateIncumbentDaemon › never evicts another user's daemon over a build mismatch0 ms
  • ✓negotiateIncumbentDaemon › refuses rather than silently moving when the replaced daemon will not free an explicit port1 ms
  • ✓negotiateIncumbentDaemon › an empty port is not a negotiation1 ms
  • ✓findDaemonPortAsync port scan › will not join another user's daemon, and says so instead of failing silently3 ms
  • ✓findDaemonPortAsync port scan › joins a daemon of ours found by the same scan1 ms
  • ✓findDaemonPortAsync port scan › joins a daemon whose ownership is unproven1 ms
  • ✓localPeerOwnership › reads this process as ours and a dead pid as unknown1 ms
  • ✓localPeerOwnership › isForeignDaemon reports only a definite refusal0 ms
  • ✓discovered-daemon selection › prefers this user's daemon over whichever answered first51 ms
  • ✓discovered-daemon selection › demotes but never drops another user's daemon0 ms
  • ✓discovered-daemon selection › an explicitly named host outranks even our own daemon0 ms
  • ✓discovered-daemon selection › ranks a daemon that predates the TXT keys last, without excluding it1 ms
bridge/src/__tests__/daemon-target.test.ts27/27
  • ✓daemon-target helpers › isLoopbackHost › treats loopback forms as local3 ms
  • ✓daemon-target helpers › isLoopbackHost › treats LAN/remote hosts as non-local0 ms
  • ✓daemon-target helpers › formatHostForUrl › brackets bare IPv6 literals0 ms
  • ✓daemon-target helpers › formatHostForUrl › leaves IPv4 and already-bracketed hosts untouched0 ms
  • ✓daemon-target helpers › parseHostHint › defaults the port to 9120 when none is given2 ms
  • ✓daemon-target helpers › parseHostHint › parses host:port0 ms
  • ✓daemon-target helpers › parseHostHint › does not mistake bare IPv6 for host:port0 ms
  • ✓daemon-target helpers › parseHostHint › parses bracketed IPv6 with and without a port0 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › prefers a local daemon and never consults remote paths2 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › refuses (null + warn) an incapable loopback daemon under --remote-daemon with no host hint3 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › falls through to a capable named host when the loopback daemon is incapable1 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › refuses an ssh -L tunnel to an incapable daemon named via --daemon-host 127.0.0.11 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › prefers an explicit tokenHint over the daemon-advertised token0 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › uses tokenHint on the mDNS path too0 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › warns once (with provisioning hint) when a remote target has no token at all1 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › still returns an incapable local daemon without the switch (local default untouched)0 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › warnRemoteOnce logs a given key at most once (resolver re-runs on every backoff)0 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › ignores an explicit host without --remote-daemon: the switch gates BOTH remote paths1 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › resolves an explicit host with --remote-daemon, without touching mDNS1 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › returns null for an unreachable explicit host without falling through to mDNS1 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › refuses (null + warn) an explicit remote daemon lacking sameSocketControl1 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › runs mDNS discovery ONLY when the --remote-daemon switch is set1 ms
  • ✓daemon-target helpers › resolveDaemonTarget precedence + opt-in gate › never leaves the machine with no opt-in (no host, no switch)1 ms
  • ✓daemon-target helpers › deriveRemoteAttachOpts (shared CLI/index derivation) › is inert with no flags and no env0 ms
  • ✓daemon-target helpers › deriveRemoteAttachOpts (shared CLI/index derivation) › flags a host given without the switch as ignored0 ms
  • ✓daemon-target helpers › deriveRemoteAttachOpts (shared CLI/index derivation) › does not warn when the opt-in arrives via env alone0 ms
  • ✓daemon-target helpers › deriveRemoteAttachOpts (shared CLI/index derivation) › honors env host aliases in precedence order0 ms
bridge/src/__tests__/daemon-ws-client.test.ts7/7
  • ✓DaemonWsClient register frame › always includes an explicit integer weight (unweighted ⇒ 0, never omitted)6 ms
  • ✓DaemonWsClient register frame › carries the launch-time --weight value0 ms
  • ✓DaemonWsClient register frame › normalizes garbage weights so the wire only ever sees in-range integers0 ms
  • ✓DaemonWsClient target re-resolution (real socket, short backoff) › clears the cached target on a null resolution and only redials once a valid target resolves again220 ms
  • ✓DaemonWsClient target re-resolution (real socket, short backoff) › capability downgrade through the REAL resolver: incapable local daemon ⇒ no new socket (reviewer end-to-end case)208 ms
  • ✓DaemonWsClient target re-resolution (real socket, short backoff) › client guard: refuses to dial a non-null capability-less target under remote intent152 ms
  • ✓DaemonWsClient shutdown › does not leak an asynchronous error when closed while CONNECTING27 ms
bridge/src/__tests__/event-loop-telemetry.test.ts1/1
  • ✓event-loop delay telemetry (#327) › reports a sane snapshot and resets the window per read247 ms
bridge/src/__tests__/hub-state-identity.test.ts14/14
  • ✓resolveHubFrameIdentity › a Claude hook driving the machine yields an aggregate frame with that session, never OpenClaw4 ms
  • ✓resolveHubFrameIdentity › a hook without a session id still drops the OpenClaw label0 ms
  • ✓resolveHubFrameIdentity › the Gateway driving the machine names its own row and takes its own snapshot0 ms
  • ✓resolveHubFrameIdentity › no driver yet: OpenClaw while the Gateway is alive (unattributed), daemon otherwise0 ms
  • ✓resolveHubFrameIdentity › a hook driver outranks a live Gateway; a Gateway driver with the Gateway gone is plain daemon0 ms
  • ✓HubStateDriverTracker › remembers the latest driver and clears it on that session's end only0 ms
  • ✓HubStateDriverTracker › gateway activity replaces a hook driver and is cleared when the Gateway goes away0 ms
  • ✓HubStateDriverTracker › an anonymous hook driver (no session id) is cleared by an anonymous end0 ms
  • ✓HubStateDriverTracker › normalises empty ids and projects to absent0 ms
  • ✓shapeHubFrame › Gateway (re)connect during a Claude turn announces the Gateway's own idle state, not the machine's turn1 ms
  • ✓shapeHubFrame › a Gateway that is itself waiting keeps its prompt fields0 ms
  • ✓shapeHubFrame › a hook-driven frame keeps the machine's turn but wears the hook session's identity0 ms
  • ✓shapeHubFrame › a hook-driven frame without a project drops the Gateway's leftover label rather than carrying it0 ms
  • ✓shapeHubFrame › does not mutate its input0 ms
bridge/src/__tests__/kiro-diagnostics.test.ts14/14
  • ✓Kiro privacy-safe diagnostics › correlates an explicit resume id without serializing content, paths, ids, or commands14 ms
  • ✓Kiro privacy-safe diagnostics › distinguishes cwd correlation, ACP records, and process-only IDE observation3 ms
  • ✓Kiro privacy-safe diagnostics › reports missing prerequisites without treating them as a command failure1 ms
  • ✓Kiro privacy-safe diagnostics › only exposes allowlisted transcript schema markers1 ms
  • ✓Kiro privacy-safe diagnostics › summarizes measured v3 message envelopes and recognizes completed turns2 ms
  • ✓Kiro privacy-safe diagnostics › takes the spoken reply, not the trailing redacted Reasoning block1 ms
  • ✓Kiro privacy-safe diagnostics › separates a not-yet-prompted transcript from an unrecognized one2 ms
  • ✓Kiro privacy-safe diagnostics › reports an unreadable transcript as unrecognized, not as not-yet-prompted1 ms
  • ✓Kiro privacy-safe diagnostics › counts only non-blank transcript lines as evidence of content1 ms
  • ✓Kiro privacy-safe diagnostics › discovers the measured v3 workspace/session directory layout8 ms
  • ✓Kiro privacy-safe diagnostics › projects measured SQLite v2 rows and recognizes their schema marker1 ms
  • ✓Kiro privacy-safe diagnostics › uses prompt-history mtime as a content-free pending-turn marker1 ms
  • ✓native Kiro explicit turn boundaries › does not reopen a completed or interrupted turn for a delayed assistant record0 ms
  • ✓native Kiro explicit turn boundaries › keeps an unmatched start processing through silent tools and reasoning1 ms
bridge/src/__tests__/legacy-rearm-ledger.test.ts6/6
  • ✓LegacyRearmLedger › asks for the re-arm the first time and never again for that token7 ms
  • ✓LegacyRearmLedger › re-arms once more when the token actually changes0 ms
  • ✓LegacyRearmLedger › tracks each port independently1 ms
  • ✓LegacyRearmLedger › counts an ack as served, so a later device_info cannot reschedule it0 ms
  • ✓LegacyRearmLedger › forgets a port on unplug so a replug is evaluated fresh0 ms
  • ✓LegacyRearmLedger › records an ATTEMPT, not a delivery0 ms
bridge/src/__tests__/linux-service.test.ts38/38
  • ✓buildUnitFile › produces a well-formed unit with the three standard sections3 ms
  • ✓buildUnitFile › runs the daemon in the foreground so systemd supervises the real process0 ms
  • ✓buildUnitFile › carries the network posture in ExecStart, quoted like every other unit word0 ms
  • ✓buildUnitFile › mirrors LaunchAgent KeepAlive via Restart=on-failure0 ms
  • ✓buildUnitFile › waits for the network to be online0 ms
  • ✓buildUnitFile › defaults the working directory to ~/.agentdeck0 ms
  • ✓buildUnitFile › derives BOTH WorkingDirectory and Environment from one dataDirOverride0 ms
  • ✓buildUnitFile › reflects the AGENTDECK_DATA_DIR override in WorkingDirectory and Environment0 ms
  • ✓unit-file escaping (golden bytes) › emits NO Environment= line when the override is not set1 ms
  • ✓unit-file escaping (golden bytes) › passes a plain path with a space through quoted and unescaped0 ms
  • ✓unit-file escaping (golden bytes) › escapes % as %% (specifier expansion) in Environment and WorkingDirectory0 ms
  • ✓unit-file escaping (golden bytes) › C-escapes backslash and double quote inside the quoted Environment value0 ms
  • ✓unit-file escaping (golden bytes) › keeps $ SINGLE in Environment= (systemd does not expand $ there)0 ms
  • ✓unit-file escaping (golden bytes) › doubles $ and % in ExecStart words (expanded there even inside quotes)0 ms
  • ✓unit-file escaping (golden bytes) › escapeUnitEnvAssignment / escapeExecWord expose the per-directive rules0 ms
  • ✓unit-file escaping (golden bytes) › rejects control characters in the override (newline)1 ms
  • ✓unit-file escaping (golden bytes) › rejects control characters in the override (carriage return)0 ms
  • ✓unit-file escaping (golden bytes) › rejects control characters in the override (NUL)0 ms
  • ✓unit-file escaping (golden bytes) › rejects non-absolute overrides (relative path)0 ms
  • ✓unit-file escaping (golden bytes) › rejects non-absolute overrides (bare tilde)0 ms
  • ✓unit-file escaping (golden bytes) › rejects non-absolute overrides (tilde path)0 ms
  • ✓unit-file escaping (golden bytes) › rejects non-absolute overrides (ignore-missing prefix)0 ms
  • ✓unit-file escaping (golden bytes) › rejects edge whitespace the unit parser would trim (leading space)0 ms
  • ✓unit-file escaping (golden bytes) › rejects edge whitespace the unit parser would trim (trailing space)0 ms
  • ✓unit-file escaping (golden bytes) › rejects edge whitespace the unit parser would trim (trailing tab)0 ms
  • ✓unit-file escaping (golden bytes) › validateDataDirOverride accepts a hostile-but-legal absolute path0 ms
  • ✓Environment= round-trip (systemd parse semantics) › round-trips the raw value (space)0 ms
  • ✓Environment= round-trip (systemd parse semantics) › round-trips the raw value (percent specifier)0 ms
  • ✓Environment= round-trip (systemd parse semantics) › round-trips the raw value (double quote)0 ms
  • ✓Environment= round-trip (systemd parse semantics) › round-trips the raw value (backslash)0 ms
  • ✓Environment= round-trip (systemd parse semantics) › round-trips the raw value (dollar)0 ms
  • ✓Environment= round-trip (systemd parse semantics) › round-trips the raw value (everything at once)0 ms
  • ✓getDataDir › defaults to ~/.agentdeck0 ms
  • ✓getDataDir › honors AGENTDECK_DATA_DIR0 ms
  • ✓installUnit (fresh data dir) › creates a not-yet-existing data directory before touching systemd138 ms
  • ✓installUnit (fresh data dir) › rejects an invalid override BEFORE creating any directory1 ms
  • ✓getUnitPath / getUnitDir › defaults to ~/.config/systemd/user0 ms
  • ✓getUnitPath / getUnitDir › honors $XDG_CONFIG_HOME0 ms
bridge/src/__tests__/native-dependency-diagnostics.test.ts5/5
  • ✓native dependency diagnostics › supports only the maintained even Node lines AgentDeck verifies3 ms
  • ✓native dependency diagnostics › opens an in-memory database instead of treating require success as binding success2 ms
  • ✓native dependency diagnostics › reports a missing native binding with the exact daemon runtime identity and recovery1 ms
  • ✓native dependency diagnostics › rejects Node 20 before probing a binding that may happen to exist1 ms
  • ✓native dependency diagnostics › closes a database even when the smoke query fails0 ms
bridge/src/__tests__/session-registry.test.ts28/28
  • ✓Session Registry Logic › pruneDeadSessions › keeps alive sessions5 ms
  • ✓Session Registry Logic › pruneDeadSessions › removes sessions with dead PIDs1 ms
  • ✓Session Registry Logic › pruneDeadSessions › keeps old sessions if PID is alive1 ms
  • ✓Session Registry Logic › pruneDeadSessions › handles mix of alive and dead sessions2 ms
  • ✓Session Registry Logic › port allocation logic › returns base port when no ports are used0 ms
  • ✓Session Registry Logic › port allocation logic › returns next port when base is taken0 ms
  • ✓Session Registry Logic › port allocation logic › skips used ports0 ms
  • ✓Session Registry Logic › port allocation logic › finds gaps in used ports0 ms
  • ✓Session Registry Logic › port allocation logic › throws when all ports are taken1 ms
  • ✓Session Registry Logic › atomic write › write-then-rename produces valid JSON3 ms
  • ✓Session Registry Logic › shouldConcedePortToOccupant (startup-race hardening) › does not concede to a non-daemon occupant (session bridge)0 ms
  • ✓Session Registry Logic › shouldConcedePortToOccupant (startup-race hardening) › does not concede when the probe failed (null occupant)0 ms
  • ✓Session Registry Logic › shouldConcedePortToOccupant (startup-race hardening) › concedes to a daemon backed by a live, distinct PID0 ms
  • ✓Session Registry Logic › shouldConcedePortToOccupant (startup-race hardening) › does NOT concede to a forged/stale daemon whose PID is dead0 ms
  • ✓Session Registry Logic › shouldConcedePortToOccupant (startup-race hardening) › does NOT concede when the reported PID is our own0 ms
  • ✓Session Registry Logic › shouldConcedePortToOccupant (startup-race hardening) › trusts the mode when no PID is reported (e.g. Swift App Store daemon)0 ms
  • ✓Session Registry Logic › scanDaemonPortWindow / waitForDaemonExit (split-brain guard) › finds a Swift daemon sitting on a fallback port inside the window37 ms
  • ✓Session Registry Logic › scanDaemonPortWindow / waitForDaemonExit (split-brain guard) › ignores non-daemon occupants (session bridges) and skipped ports6 ms
  • ✓Session Registry Logic › scanDaemonPortWindow / waitForDaemonExit (split-brain guard) › returns empty when the window is quiet2 ms
  • ✓Session Registry Logic › scanDaemonPortWindow / waitForDaemonExit (split-brain guard) › waitForDaemonExit resolves true once /health stops answering506 ms
  • ✓Session Registry Logic › scanDaemonPortWindow / waitForDaemonExit (split-brain guard) › waitForDaemonExit resolves false when the daemon never leaves758 ms
  • ✓Session Registry Logic › scanDaemonPortWindow / waitForDaemonExit (split-brain guard) › waitForPortBindable refuses a port that is still bound457 ms
  • ✓Session Registry Logic › scanDaemonPortWindow / waitForDaemonExit (split-brain guard) › waitForPortBindable resolves true once the socket is actually released302 ms
  • ✓Session Registry Logic › scanDaemonPortWindow / waitForDaemonExit (split-brain guard) › waitForPortBindable resolves true immediately for a free port1 ms
  • ✓daemon port window › defaults when unset or blank1 ms
  • ✓daemon port window › accepts a well-formed range1 ms
  • ✓daemon port window › falls back to the default for anything it cannot trust1 ms
  • ✓daemon port window › reports whether the resolved window is the documented one0 ms
bridge/src/__tests__/wake-detector.test.ts5/5
  • ✓classifyClockTick › reads a late tick where both clocks advanced together as lag, not a wake3 ms
  • ✓classifyClockTick › reads a wall-clock jump the monotonic clock did not follow as a wake0 ms
  • ✓classifyClockTick › is on schedule below the gap on either clock0 ms
  • ✓classifyClockTick › keeps the gap rule on win32, whose monotonic clock counts through sleep0 ms
  • ✓classifyClockTick › monotonicNowMs is monotonic and in milliseconds0 ms
bridge/src/__tests__/windows-child-window.test.ts5/5
  • ✓no child process may open a console window › every child_process call hides its window, names a POSIX-only binary, or says why not541 ms
  • ✓the gate reads what it claims to read › counts only child_process imports, aliases included1 ms
  • ✓the gate reads what it claims to read › never reads a function name out of prose0 ms
  • ✓the gate reads what it claims to read › extracts a call across newlines and nested parens0 ms
  • ✓the gate reads what it claims to read › reads the binary out of a literal first argument1 ms
bridge/src/__tests__/windows-service.test.ts9/9
  • ✓xmlEscape › escapes XML metacharacters3 ms
  • ✓buildScheduledTaskXml › produces a well-formed v1.2 task with a logon trigger1 ms
  • ✓buildScheduledTaskXml › mirrors LaunchAgent KeepAlive / no-stop semantics1 ms
  • ✓buildScheduledTaskXml › uses node as unquoted Command and quotes the cli.js path in Arguments0 ms
  • ✓buildScheduledTaskXml › runs the detached launcher, never the daemon itself0 ms
  • ✓buildScheduledTaskXml › carries the network posture in Arguments — Task Scheduler has no env element0 ms
  • ✓buildScheduledTaskXml › emits no trailing space when there is no posture to carry0 ms
  • ✓buildScheduledTaskXml › XML-escapes special characters in the user id and paths0 ms
  • ✓buildScheduledTaskXml › places both UserId fields (trigger + principal) with the same user1 ms
setup/src/__tests__/setup-args.test.ts6/6
  • ✓Node runtime support contract › accepts the maintained even release lines2 ms
  • ✓Node runtime support contract › rejects EOL Node 20 and short-lived odd release lines0 ms
  • ✓parseSetupArgs › prints next steps by default instead of installing the daemon0 ms
  • ✓parseSetupArgs › --yes / -y finishes the install without a second command0 ms
  • ✓parseSetupArgs › --enterprise implies --yes0 ms
  • ✓parseSetupArgs › ignores unknown flags rather than refusing to install0 ms
shared/src/__tests__/daemon-parity.test.ts6/6
  • ✓Node/Swift daemon policies › keeps the native policy generated from the canonical source4 ms
  • ✓Node/Swift daemon policies › start without end remains processing0 ms
  • ✓Node/Swift daemon policies › interrupted end closes turn0 ms
  • ✓Node/Swift daemon policies › resumed activity starts another turn0 ms
  • ✓Node/Swift daemon policies › user text alone is not a boundary0 ms
  • ✓Node/Swift daemon policies › shares legacy tool-turn and runtime acceptance decisions1 ms
  • ○Node/Swift daemon policies › executes the same vectors through generated Swift

45 files

Pass

Agent observation & hooks

Are Claude, Codex, OpenCode, Kiro, Antigravity and Hermes sessions seen, attributed and gated correctly from their hooks and transcripts?

Passed
776
Failed
0
Skipped
6
Time
8.8 s
45 test files
bridge/src/__tests__/antigravity-transcript.test.ts4/4
  • ✓antigravity transcript parsing › extracts goal, model (from settings change), state and tool task4 ms
  • ✓antigravity transcript parsing › reports processing when the newest step is RUNNING; later model change wins1 ms
  • ✓antigravity transcript parsing › reports idle when only historical steps are RUNNING (newest step is DONE)0 ms
  • ✓antigravity transcript parsing › matches the agy CLI binary as an Antigravity process2 ms
bridge/src/__tests__/ask-gate-decisions.test.ts20/20
  • ✓askGateDecision — who pays for the hold › holds only when there is no way to type into the session3 ms
  • ✓askGateDecision — who pays for the hold › never holds when the answer could be typed instead0 ms
  • ✓askGateDecision — who pays for the hold › never holds a session no device can see0 ms
  • ✓askGateDecision — who pays for the hold › never holds with the gate off or nobody connected0 ms
  • ✓askPressVerdict — what may commit an answer › accepts a press that names the live question1 ms
  • ✓askPressVerdict — what may commit an answer › refuses a press that does not name its question0 ms
  • ✓askPressVerdict — what may commit an answer › refuses a press aimed at a question the prompt moved past0 ms
  • ✓askPressVerdict — what may commit an answer › refuses to answer an overlay the held gate does not own0 ms
  • ✓askPressVerdict — what may commit an answer › refuses anything that is not an option selection0 ms
  • ✓askPressVerdict — what may commit an answer › refuses an index that is not in the live option list0 ms
  • ✓askPressVerdict — what may commit an answer › matches on the option index, not its array position0 ms
  • ✓askPressVerdict — what may commit an answer › refuses when no question is open at all0 ms
  • ✓askPressVerdict — what may commit an answer › accepts an answer from a device that could only hold part of the question1 ms
  • ✓askEchoMatches — a truncated echo still names its question › accepts an exact echo0 ms
  • ✓askEchoMatches — a truncated echo still names its question › accepts a long enough prefix0 ms
  • ✓askEchoMatches — a truncated echo still names its question › refuses a prefix too short to identify the question0 ms
  • ✓askEchoMatches — a truncated echo still names its question › refuses an echo that diverges from the live question0 ms
  • ✓askEchoMatches — a truncated echo still names its question › refuses an echo LONGER than the live question0 ms
  • ✓askEchoMatches — a truncated echo still names its question › accepts an echo cut inside a grapheme, as the Swift mirror does0 ms
  • ✓askEchoMatches — a truncated echo still names its question › refuses a superseded question that shares a long prefix0 ms
bridge/src/__tests__/ask-gate.test.ts6/6
  • ✓AskUserQuestion ask-gate (device answer → agent) › delivers a single-question answer as the hook decision reason12 ms
  • ✓AskUserQuestion ask-gate (device answer → agent) › walks a multi-question call one question at a time, then answers with all pairs3 ms
  • ✓AskUserQuestion ask-gate (device answer → agent) › releases with an EMPTY body when nobody answers, so the terminal picker shows2 ms
  • ✓AskUserQuestion ask-gate (device answer → agent) › does not hold when nothing could answer, leaving zero added latency1 ms
  • ✓AskUserQuestion ask-gate (device answer → agent) › keeps a partially answered prompt out of the agent when the hold expires1 ms
  • ✓AskUserQuestion ask-gate (device answer → agent) › clears the prompt on PostToolUse when the tool did run (injection path)1 ms
bridge/src/__tests__/claude-interrupt-marker.test.ts7/7
  • ✓isClaudeInterruptRecord › matches both marker spellings Claude Code writes4 ms
  • ✓isClaudeInterruptRecord › matches on `interruptedMessageId` even if the marker text changes0 ms
  • ✓isClaudeInterruptRecord › does NOT match the marker quoted inside a tool_result0 ms
  • ✓isClaudeInterruptRecord › does NOT match an assistant that mentions the marker0 ms
  • ✓isClaudeInterruptRecord › does not match ordinary records or junk0 ms
  • ✓isClaudeInterruptRecord › tolerates an empty `interruptedMessageId` instead of trusting its presence0 ms
  • ✓isClaudeInterruptMessage › reads string content as well as text blocks0 ms
bridge/src/__tests__/claude-transcript-reader.test.ts23/23
  • ✓readLastTurn › extracts assistant text and user prompt from a text-only turn6 ms
  • ✓readLastTurn › flags tool-only turns (tool_use blocks, no text blocks)1 ms
  • ✓readLastTurn › concatenates text across multiple assistant records for one turn1 ms
  • ✓readLastTurn › picks the LAST user prompt when multiple turns are in the file1 ms
  • ✓readLastTurn › returns null for missing file1 ms
  • ✓readLastTurn › skips malformed lines and continues parsing1 ms
  • ✓readLastTurn › accepts legacy string content on both roles1 ms
  • ✓readTurnEndProbe › reports end_turn with the record timestamp on a finished turn2 ms
  • ✓readTurnEndProbe › skips trailing non-message records (mode markers)1 ms
  • ✓readTurnEndProbe › reports tool_use mid-turn (prompt open / tools pending)1 ms
  • ✓readTurnEndProbe › reports the user role while a tool_result is the tail (assistant pending)1 ms
  • ✓readTurnEndProbe › returns null timestamp when the record has none, and null on unreadable file1 ms
  • ✓readTurnEndProbe › skips a truncated final line (concurrent write)1 ms
  • ✓readTurnEndProbe › flags the ESC interrupt marker as the turn end1 ms
  • ✓readTurnEndProbe › does not read a quoted marker inside a tool_result as an interrupt1 ms
  • ✓readOpenTurnEvidence › finds a marker buried under the retyped prompt that followed it1 ms
  • ✓readOpenTurnEvidence › will not pay this turn for the previous turn's cancel1 ms
  • ✓readOpenTurnEvidence › a stampless record does not hide markers behind it1 ms
  • ✓readOpenTurnEvidence › returns null on a clean turn and on an unreadable file1 ms
  • ✓readOpenTurnEvidence › distinguishes an unreadable transcript from one holding no evidence1 ms
  • ✓readOpenTurnEvidence › reports the client abort that ends a turn with no Stop hook1 ms
  • ✓readOpenTurnEvidence › will not pay this turn for the previous turn's abort1 ms
  • ✓readOpenTurnEvidence › reports a turn that never ran — two prompts, no assistant record between1 ms
bridge/src/__tests__/claude-turn-watchdog.test.ts15/15
  • ✓ClaudeTurnWatchdog › closes a turn whose Stop was dropped once the transcript shows end_turn9 ms
  • ✓ClaudeTurnWatchdog › fires at most once per turn2 ms
  • ✓ClaudeTurnWatchdog › ignores the previous turn's end_turn (timestamp before turn open)1 ms
  • ✓ClaudeTurnWatchdog › closes a turn the user cancelled, tagged as an interrupt rather than a loss1 ms
  • ✓ClaudeTurnWatchdog › closes a turn the client aborted (usage limit / auth / API error)1 ms
  • ✓ClaudeTurnWatchdog › ignores the previous turn's abort1 ms
  • ✓ClaudeTurnWatchdog › ignores the previous turn's interrupt marker1 ms
  • ✓ClaudeTurnWatchdog › a plain user record is not an interrupt1 ms
  • ✓ClaudeTurnWatchdog › never closes a genuine wait (stop_reason tool_use)1 ms
  • ✓ClaudeTurnWatchdog › does not probe while the hook channel is active2 ms
  • ✓ClaudeTurnWatchdog › disarms when the real Stop arrives1 ms
  • ✓ClaudeTurnWatchdog › skips the transcript read when mtime has not advanced1 ms
  • ✓ClaudeTurnWatchdog › disarms on SessionEnd without killing later recovery (/clear fires SessionEnd mid-session)1 ms
  • ✓ClaudeTurnWatchdog › stop() (bridge shutdown) disarms permanently1 ms
  • ✓ClaudeTurnWatchdog › waits without a transcript path and recovers once one is learned1 ms
bridge/src/__tests__/claude-usage-recovery.test.ts16/16
  • ✓ClaudeUsageRecovery › resolves a relative executable before recovery changes cwd4 ms
  • ✓ClaudeUsageRecovery › identifies an overridden Windows .cmd shim1 ms
  • ✓ClaudeUsageRecovery › identifies an overridden Windows .BAT shim0 ms
  • ✓ClaudeUsageRecovery › does not restart the quick tier on repeatedly rotated rejected credentials4 ms
  • ✓ClaudeUsageRecovery › never recovers a different macOS Keychain namespace1 ms
  • ✓ClaudeUsageRecovery › resolves a relative credential directory before changing the child cwd0 ms
  • ✓ClaudeUsageRecovery › removes inherited credential/provider and session overrides without changing the parent0 ms
  • ✓ClaudeUsageRecovery › preserves the configured Claude executable through recovery environment sanitization1 ms
  • ✓ClaudeUsageRecovery › deduplicates concurrent callers and survives restarts without storing credentials1 ms
  • ✓ClaudeUsageRecovery › backs off six hours after three attempts, including failed child processes1 ms
  • ✓ClaudeUsageRecovery › retries an unchanged credential after one minute before using the normal backoff1 ms
  • ✓ClaudeUsageRecovery › persists a sanitized recovery outcome for detached-daemon diagnostics1 ms
  • ✓ClaudeUsageRecovery › fails closed if the retry budget cannot be persisted1 ms
  • ✓ClaudeUsageRecovery › resolves the CLI off PATH, honours PATHEXT, and reports a shell shim as unusable2 ms
  • ✓ClaudeUsageRecovery › resolves the cooldown record and the usage cache under AGENTDECK_DATA_DIR113 ms
  • ✓ClaudeUsageRecovery › disables tools and customizations while keeping OAuth available1 ms
bridge/src/__tests__/codex-ambient-hooks.test.ts29/29
  • ✓isCodexAmbientPrompt (shared vectors) › hyperpersonalized suggestions generator (live 2026-09-11)4 ms
  • ✓isCodexAmbientPrompt (shared vectors) › generator prompt with its Markdown heading, verbatim from the hook (live 2026-09-29)0 ms
  • ✓isCodexAmbientPrompt (shared vectors) › a user prompt headed Overview is not ambient0 ms
  • ✓isCodexAmbientPrompt (shared vectors) › safety and compliance review of ambient suggestions (live 2026-09-11)0 ms
  • ✓isCodexAmbientPrompt (shared vectors) › leading whitespace and case do not hide the signature0 ms
  • ✓isCodexAmbientPrompt (shared vectors) › a user prompt that mentions suggestions is not ambient0 ms
  • ✓isCodexAmbientPrompt (shared vectors) › the signature must be at the start, not quoted mid-prompt0 ms
  • ✓isCodexAmbientPrompt (shared vectors) › ordinary work prompt0 ms
  • ✓isCodexAmbientPrompt (shared vectors) › codex exec image batch driven by another agent is real work0 ms
  • ✓isCodexAmbientPrompt (shared vectors) › empty prompt0 ms
  • ✓isCodexAmbientPrompt (shared vectors) › non-string prompts are never ambient0 ms
  • ✓isCodexBackgroundCwd (shared vectors) › memory consolidation under the default home (live 2026-09-27)0 ms
  • ✓isCodexBackgroundCwd (shared vectors) › trailing slash0 ms
  • ✓isCodexBackgroundCwd (shared vectors) › a file inside the memory store0 ms
  • ✓isCodexBackgroundCwd (shared vectors) › custom CODEX_HOME0 ms
  • ✓isCodexBackgroundCwd (shared vectors) › custom CODEX_HOME still recognises the default suffix0 ms
  • ✓isCodexBackgroundCwd (shared vectors) › codex home itself is not the memory agent0 ms
  • ✓isCodexBackgroundCwd (shared vectors) › a user project named memories0 ms
  • ✓isCodexBackgroundCwd (shared vectors) › a project that merely contains .codex in its path0 ms
  • ✓isCodexBackgroundCwd (shared vectors) › ordinary project0 ms
  • ✓isCodexBackgroundCwd (shared vectors) › empty cwd0 ms
  • ✓CodexAmbientSessions — memory consolidation agent › drops the thread from its very first hook, the session start, and keeps dropping it2 ms
  • ✓CodexAmbientSessions — memory consolidation agent › leaves a real project session alone0 ms
  • ✓codexHookPromptText › reads prompt, then user_prompt, then message.content0 ms
  • ✓CodexAmbientSessions › classifies the thread on its prompt and every later hook on that id1 ms
  • ✓CodexAmbientSessions › a user thread on the same host is untouched, even at cwd /0 ms
  • ✓CodexAmbientSessions › only codex_* hooks with a session id are considered0 ms
  • ✓CodexAmbientSessions › a background thread is forgotten after the TTL, and re-identified by a fresh prompt0 ms
  • ✓CodexAmbientSessions › a hook on a live background thread refreshes its TTL0 ms
bridge/src/__tests__/codex-auth.test.ts7/7
  • ✓resolveChatGptRenewalDate › rolls a stale monthly window forward to the next renewal boundary4 ms
  • ✓resolveChatGptRenewalDate › rolls forward across many missed cycles in one step0 ms
  • ✓resolveChatGptRenewalDate › rolls a stale annual window forward by a year0 ms
  • ✓resolveChatGptRenewalDate › passes a future date through untouched0 ms
  • ✓resolveChatGptRenewalDate › leaves a past date raw when there is no start to derive a period0 ms
  • ✓resolveChatGptRenewalDate › leaves an untrustworthy (too-short) window raw0 ms
  • ✓resolveChatGptRenewalDate › passes malformed and empty inputs through0 ms
bridge/src/__tests__/codex-exec-children.test.ts26/26
  • ✓codex exec shape predicates › recognises the exec subcommand behind node and timeout wrappers, not the helpers4 ms
  • ✓codex exec shape predicates › reads the working directory from -C / --cd / --cd=1 ms
  • ✓codex exec shape predicates › treats only codex_exec as headless, case-insensitively1 ms
  • ✓nearestAncestorPeer › walks the wrapper chain up to the launching session1 ms
  • ✓nearestAncestorPeer › never matches the process itself and returns null with no peer ancestor0 ms
  • ✓nearestAncestorPeer › survives a cyclic or truncated table0 ms
  • ✓nearestAncestorPeer › is deterministic when several peers share a pid (Codex Desktop conversations)0 ms
  • ✓CodexExecChildren.noteHook › attaches a headless run to its launcher on SessionStart when the process is visible4 ms
  • ✓CodexExecChildren.noteHook › matches the argv -C path against the canonical rollout cwd (/tmp vs /private/tmp)0 ms
  • ✓CodexExecChildren.noteHook › completes the child on its Stop with the inline reply, once1 ms
  • ✓CodexExecChildren.noteHook › falls back to the rollout reply when the Stop payload carries none0 ms
  • ✓CodexExecChildren.noteHook › lets an interactive TUI session through untouched1 ms
  • ✓CodexExecChildren.noteHook › judges a headless run with no session ancestor standalone1 ms
  • ✓CodexExecChildren.noteHook › resolves a run launched without -C as the only unclaimed codex exec in the table0 ms
  • ✓CodexExecChildren.noteHook › asks for a fresh table once when the process is not visible yet, and lets the hooks flow meanwhile1 ms
  • ✓CodexExecChildren.noteHook › does not ask for a fresh table twice for one session1 ms
  • ✓CodexExecChildren.noteHook › gives up on a pending run after PENDING_TTL_MS and after a terminal hook1 ms
  • ✓CodexExecChildren.noteHook › re-checks a rollout that was not on disk at SessionStart1 ms
  • ✓CodexExecChildren.noteHook › ignores non-codex hooks and hooks without a session id0 ms
  • ✓CodexExecChildren.noteHook › does not let two same-cwd siblings claim one process1 ms
  • ✓CodexExecChildren.reconcile › attaches observer-found children and completes them when their process exits1 ms
  • ✓CodexExecChildren.reconcile › treats an empty process table as "could not look", not as every child finishing0 ms
  • ✓CodexExecChildren.reconcile › attaches a pending hook-side run from the tick table when the observer did not report it1 ms
  • ✓CodexExecChildren.reconcile › marks a parentless observation standalone so its later hooks flow0 ms
  • ✓CodexExecChildren.reconcile › corrects a best-effort hook-side pid with the observer's exact one1 ms
  • ✓CodexExecChildren.reconcile › does not complete a guessed-pid child while a same-cwd run is still alive1 ms
bridge/src/__tests__/codex-hook-silence.test.ts5/5
  • ✓CodexHookSilenceWarning › warns once when the PTY is active but no codex_* event ever arrived6 ms
  • ✓CodexHookSilenceWarning › stays quiet when any codex hook event arrives (channel proven)1 ms
  • ✓CodexHookSilenceWarning › stays quiet when the terminal was barely used (session may not have started)1 ms
  • ✓CodexHookSilenceWarning › warns late when activity only crosses the threshold after the deadline0 ms
  • ✓CodexHookSilenceWarning › stop() disarms (session shutdown)0 ms
bridge/src/__tests__/codex-otel.test.ts21/21
  • ✓codex OTLP span parser › reads a full turn from dotted span names6 ms
  • ✓codex OTLP span parser › normalizes underscore and slash span-name variants0 ms
  • ✓codex OTLP span parser › rejects short numeric ids and prefers the durable alias on the same span1 ms
  • ✓codex OTLP span parser › ignores a tool span own cwd but inherits the thread-scoped one2 ms
  • ✓codex OTLP span parser › falls through to resource-level attributes0 ms
  • ✓codex OTLP span parser › drops unknown spans and spans with no identity at all1 ms
  • ✓codex OTLP span parser › summarizes span names for diagnostics0 ms
  • ✓codex OTLP span parser › finds no durable identity in a live ChatGPT.app Codex batch1 ms
  • ✓CodexOtelTracker › overlays state and tool onto the matching observed row instead of duplicating it3 ms
  • ✓CodexOtelTracker › synthesizes a codex-app row only when the process scan found nothing1 ms
  • ✓CodexOtelTracker › hook-owned Codex threads › keeps the hook row when a finished exec session's spans arrive late2 ms
  • ✓CodexOtelTracker › hook-owned Codex threads › still synthesizes a row for a thread no hook has reported0 ms
  • ✓CodexOtelTracker › Codex background threads › never synthesizes a row for a thread the hook classifier already dropped1 ms
  • ✓CodexOtelTracker › Codex background threads › retracts a row its spans opened before the identifying hook arrived1 ms
  • ✓CodexOtelTracker › Codex background threads › leaves the user's own thread alone0 ms
  • ✓CodexOtelTracker › rejects a superseded turnEnd so a stale span cannot close the live turn1 ms
  • ✓CodexOtelTracker › lets a follow-up turn revive a thread its own turnEnd retired0 ms
  • ✓CodexOtelTracker › expires a finished thread, and a quiet one, instead of pinning a creature1 ms
  • ✓CodexOtelTracker › stops overriding observer state once its own observation goes stale1 ms
  • ✓CodexOtelTracker › never lets progress-only activity spans open or revive a thread1 ms
  • ✓CodexOtelTracker › leaves non-codex rows untouched0 ms
bridge/src/__tests__/codex-rollout-response.test.ts15/15
  • ✓codex rollout response reader › locates a rollout by the session uuid embedded in the filename7 ms
  • ✓codex rollout response reader › prefers task_complete.last_agent_message (authoritative turn close)2 ms
  • ✓codex rollout response reader › reads the same outcome from a path the hook already carried2 ms
  • ✓codex rollout response reader › falls back to the newest agent_message when no task_complete follows1 ms
  • ✓codex rollout response reader › returns empty for missing rollouts, malformed lines, and bad ids2 ms
  • ✓codex rollout response reader › failed turns › reports the error a task_complete carries instead of a reply2 ms
  • ✓codex rollout response reader › failed turns › reports a standalone error record1 ms
  • ✓codex rollout response reader › failed turns › unwraps the upstream JSON body Codex nests in the message1 ms
  • ✓codex rollout response reader › failed turns › never inherits the previous turn's reply2 ms
  • ✓codex rollout response reader › failed turns › still returns a successful reply unchanged2 ms
  • ✓codex rollout response reader › codexTurnCompletionSince › returns the task_complete newer than the turn, with its reply3 ms
  • ✓codex rollout response reader › codexTurnCompletionSince › carries the failure Codex wrote on the completion — such a turn fires no Stop1 ms
  • ✓codex rollout response reader › codexTurnCompletionSince › never attributes an older turn's completion to this one1 ms
  • ✓codex rollout response reader › codexTurnCompletionSince › a later turn opening after the completion does not hide it1 ms
  • ✓codex rollout response reader › codexTurnCompletionSince › records without a timestamp cannot be placed, and a missing rollout is no evidence1 ms
bridge/src/__tests__/codex-turn-manager.test.ts12/12
  • ✓CodexTurnManager (hook-primary path) › happy path: UPS → tool_start → tool_end → stop emits one chat33 ms
  • ✓CodexTurnManager (hook-primary path) › a benign `message` on codex_stop is never rendered as an error9 ms
  • ✓CodexTurnManager (hook-primary path) › an explicit `error` on codex_stop renders the error close row9 ms
  • ✓CodexTurnManager (hook-primary path) › codex_stop does not reset subsequent turn_index numbering18 ms
  • ✓CodexTurnManager (hook-primary path) › codex_stop finalizes APME turn (endedAt set, tool_calls flushed)15 ms
  • ✓CodexTurnManager (hook-primary path) › next prompt opens a fresh chat_start16 ms
  • ✓CodexTurnManager (hook-primary path) › turn-complete notify closes a turn when codex_stop is absent13 ms
  • ✓CodexTurnManager (hook-primary path) › long-bash: hook tool_start + tool_end keep the same turn open15 ms
  • ✓CodexTurnManager (stale-turn recovery on prompt-submit) › missed codex_stop: next prompt closes the stale turn and opens a fresh row14 ms
  • ✓CodexTurnManager (stale-turn recovery on prompt-submit) › late prompt text fills a tool_start-opened turn without splitting it10 ms
  • ✓CodexTurnManager (stale-turn recovery on prompt-submit) › duplicate prompt echo within the window is a no-op16 ms
  • ✓CodexTurnManager (stale-turn recovery on prompt-submit) › text-less stale turn older than the fill window is closed, not filled10 ms
bridge/src/__tests__/coordination-evidence.test.ts18/18
  • ✓parseCrossSessionEnvelope › reads the sender pid, name and body off a real envelope4 ms
  • ✓parseCrossSessionEnvelope › is null for an ordinary user prompt, even one that mentions a peer0 ms
  • ✓parseSendMessageInput › splits a uds address into a pid and keeps a name as a name0 ms
  • ✓isAgentSpawnCommand › matches the measured headless worker launch and nothing else1 ms
  • ✓findAncestorSession › walks bash wrappers up to the parent agent and stops at launchd0 ms
  • ✓CoordinationTracker › links a claude -p worker to its parent by ancestry and closes it when it exits3 ms
  • ✓CoordinationTracker › counts a background job the session is waiting on once, by its outermost process2 ms
  • ✓CoordinationTracker › resolves a message peer through the sender pid and learns its name1 ms
  • ✓CoordinationTracker › records a spawn intent from a Bash launch and folds it into the child once seen1 ms
  • ✓CoordinationTracker › never invents a relation from shared project membership0 ms
  • ✓shared coordination-evidence vectors › envelopes0 ms
  • ✓shared coordination-evidence vectors › sendMessage0 ms
  • ✓shared coordination-evidence vectors › spawn and agent commands1 ms
  • ✓shared coordination-evidence vectors › labels1 ms
  • ✓shared coordination-evidence vectors › ancestry0 ms
  • ✓shared coordination-evidence vectors › background jobs and spawned workers from the measured process table2 ms
  • ✓shared coordination-evidence vectors › makes no claim when the process table could not be read1 ms
  • ✓shared coordination-evidence vectors › registers a hook pid and walks a wrapper shell up to the agent process2 ms
bridge/src/__tests__/deferred-reply-read.test.ts3/3
  • ✓scheduleDeferredReplyRead › retries on the bounded schedule and applies the first non-empty read8 ms
  • ✓scheduleDeferredReplyRead › gives up silently after the last delay, and a throwing reader counts as empty1 ms
  • ✓scheduleDeferredReplyRead › never holds the process open for the retry timer0 ms
bridge/src/__tests__/hermes-sessions.test.ts14/14
  • ✓Hermes conversation lifetime › replays captured real CLI tool work through Stop and finalization5 ms
  • ✓Hermes conversation lifetime › survives multiple turns and keeps tools separate from a final response2 ms
  • ✓Hermes conversation lifetime › finalizes and rejects trailing callbacks until explicit reopening0 ms
  • ✓Hermes conversation lifetime › expires a missing stop without inventing successful completion0 ms
  • ✓Hermes conversation lifetime › recovers mid-tool after restart but rejects orphan stops and unknown events0 ms
  • ✓Hermes conversation lifetime › isolates simultaneous profile/conversation identities and rejects malformed ids0 ms
  • ✓Hermes conversation lifetime › bounds retained sessions even if a gateway opens many conversations4 ms
  • ✓Hermes conversations end with their process › closes a conversation whose process is gone, and a late callback cannot reopen it1 ms
  • ✓Hermes conversations end with their process › keeps the conversation while the process runs, or when the probe cannot tell0 ms
  • ✓Hermes conversations end with their process › leaves rows from an observer that reports no pid to the silence TTL0 ms
  • ✓Hermes conversations end with their process › probes a shared gateway process once and closes all of its conversations0 ms
  • ✓Hermes conversations end with their process › reopens a resumed conversation from its new process0 ms
  • ✓Hermes conversations end with their process › keeps a running CLI conversation on screen like OpenClaw, but not a gateway one0 ms
  • ✓Hermes conversations end with their process › reads only "no such process" as dead27 ms
bridge/src/__tests__/hook-claude-sessions.test.ts5/5
  • ✓Claude lifecycle roster merge (#367) › keeps concurrent sessions isolated and preserves passive facts across the whole turn6 ms
  • ✓Claude lifecycle roster merge (#367) › retains another parallel tool when one succeeds or fails1 ms
  • ✓Claude lifecycle roster merge (#367) › never creates a phantom row, suppresses ended rows, and reopens only on SessionStart1 ms
  • ✓Claude lifecycle roster merge (#367) › sanitizes and bounds names, ignores unrelated hooks and never changes managed rows1 ms
  • ✓Claude lifecycle roster merge (#367) › leaves permission overlay precedence and its request identity intact1 ms
bridge/src/__tests__/hook-codex-sessions.test.ts16/16
  • ✓HookCodexSessions › registers idle without resetting a turn on duplicate SessionStart4 ms
  • ✓HookCodexSessions › surfaces a Codex the process scan never found4 ms
  • ✓HookCodexSessions › enriches a hook-only Windows row from its id-keyed desktop rollout1 ms
  • ✓HookCodexSessions › yields to the observer instead of doubling the row1 ms
  • ✓HookCodexSessions › matches the observer row in either id form0 ms
  • ✓HookCodexSessions › tracks tool state through a turn1 ms
  • ✓HookCodexSessions › reaps a finished session, and one that died without a stop hook1 ms
  • ✓HookCodexSessions › lets a follow-up prompt revive a finished session0 ms
  • ✓HookCodexSessions › opens a row from a mid-turn hook when the session start predates the daemon0 ms
  • ✓HookCodexSessions › never lets a trailing tool callback resurrect a finished session1 ms
  • ✓HookCodexSessions › keeps the first cwd instead of relabelling on a later hook0 ms
  • ✓HookCodexSessions › ignores non-codex events and payloads with no session id0 ms
  • ✓Codex PermissionRequest / Interrupt hooks › a permission request keeps the row alive as progress; an interrupt is terminal0 ms
  • ✓Codex PermissionRequest / Interrupt hooks › a permission request can open a row the scan never found (the hook proves the session)0 ms
  • ✓Codex PermissionRequest / Interrupt hooks › buildCodexPermissionQuestion names the command, never the whole input1 ms
  • ✓a resumed idle session is not reaped by its previous terminal timestamp0 ms
bridge/src/__tests__/hook-opencode-sessions.test.ts9/9
  • ✓HookOpenCodeSessions › surfaces a session from its hooks with the turn state the process scan cannot see5 ms
  • ✓HookOpenCodeSessions › PERM: permission.asked is a genuine wait with an answerable requestId, cleared by the reply1 ms
  • ✓HookOpenCodeSessions › a permission without an id is not a wait anyone can answer (no PERM)0 ms
  • ✓HookOpenCodeSessions › unrelated tool traffic preserves a wait; terminal stop clears it0 ms
  • ✓HookOpenCodeSessions › the PID row for the same working directory yields to the hook row (one TUI, one creature)1 ms
  • ✓HookOpenCodeSessions › a PID row with no cwd is kept (nothing proves it is the same session)0 ms
  • ✓HookOpenCodeSessions › a hook on an unknown session is ignored unless it opens one0 ms
  • ✓HookOpenCodeSessions › session_end removes the row; silence reaps it after the TTL0 ms
  • ✓replays the shared identity-scoped wait contract2 ms
bridge/src/__tests__/hook-response-source.test.ts5/5
  • ✓resolveStopResponse › inline text wins for every agent and touches no file6 ms
  • ✓resolveStopResponse › a Codex hook's transcript_path is its rollout, read by the Codex reader — never the Claude one3 ms
  • ✓resolveStopResponse › a Codex hook without a path locates the rollout by session id, and carries its recorded failure1 ms
  • ✓resolveStopResponse › a Claude hook's transcript_path goes to the Claude reader0 ms
  • ✓resolveStopResponse › an agent with neither inline text nor an owned reader yields nothing rather than a guess0 ms
bridge/src/__tests__/observed-hook-classify.test.ts19/19
  • ✓classifyObservedHookEvent › passes Claude-mapped events through with claude-code attribution4 ms
  • ✓classifyObservedHookEvent › maps codex_* lifecycle names to their boundary with codex-cli attribution1 ms
  • ✓classifyObservedHookEvent › maps codex notify turn_complete to stop (single completion row per turn)0 ms
  • ✓classifyObservedHookEvent › keeps Codex subagent hooks attributed to Codex0 ms
  • ✓classifyObservedHookEvent › maps opencode_* plugin events to their boundary with opencode attribution0 ms
  • ✓classifyObservedHookEvent › accepts antigravity_* for forward-compatibility (no installer yet)0 ms
  • ✓classifyObservedHookEvent › maps Kiro 2.x and 3.x hook vocabulary without requiring managed launch0 ms
  • ✓classifyObservedHookEvent › leaves unknown or unprefixed names untouched (claude fallback)0 ms
  • ✓Hermes observer attribution › session_start stays Hermes0 ms
  • ✓Hermes observer attribution › user_prompt_submit stays Hermes0 ms
  • ✓Hermes observer attribution › tool_start stays Hermes0 ms
  • ✓Hermes observer attribution › tool_end stays Hermes0 ms
  • ✓Hermes observer attribution › stop stays Hermes0 ms
  • ✓Hermes observer attribution › session_end stays Hermes0 ms
  • ✓classifyObservedHookEvent — Codex PermissionRequest / Interrupt › permission_request keeps its own boundary (a wait, not a turn edge)0 ms
  • ✓classifyObservedHookEvent — Codex PermissionRequest / Interrupt › interrupt is a turn END with no Stop coming, so it rides the stop boundary0 ms
  • ✓keeps OpenCode question_asked attributed to its harness0 ms
  • ✓keeps OpenCode question_replied attributed to its harness0 ms
  • ✓keeps OpenCode question_rejected attributed to its harness0 ms
bridge/src/__tests__/observed-inject.test.ts18/18
  • ✓parseTmuxPanes › maps pane ttys (dev-prefix stripped) to pane ids3 ms
  • ✓parseTmuxPanes › ignores malformed lines0 ms
  • ✓buildItermSelectScript › targets the session by tty and types Down x index then Enter0 ms
  • ✓buildItermSelectScript › index 0 is a bare Enter0 ms
  • ✓buildItermSelectScript › can emit the arrows without the Enter that would act on them0 ms
  • ✓parseProcessTable with tty column › parses tty and treats ?? as none0 ms
  • ✓buildTerminalAppSelectTabScript › selects the tab by tty without activating the app0 ms
  • ✓buildTerminalAppSelectTabScript › returns notfound when no tab matches0 ms
  • ✓buildPostKeysScript › matches by bundle id first, then localized name1 ms
  • ✓buildPostKeysScript › posts to the pid (never activates) and ends with Return0 ms
  • ✓buildPostKeysScript › carries a second Return only when a grouped prompt needs submitting0 ms
  • ✓buildAppButtonPressScript › matches the button by label with prefix tolerance and clicks it0 ms
  • ✓buildAppButtonPressScript › escapes quotes in labels0 ms
  • ✓buildAppKeysScript › raises the app, keys, and restores the previous frontmost app0 ms
  • ✓escapeAppleScript › escapes backslashes and quotes0 ms
  • ✓app-host resolution › extracts the app name from a bundle path0 ms
  • ✓app-host resolution › walks the ancestry to find the hosting app0 ms
  • ✓app-host resolution › returns undefined for a plain terminal session0 ms
bridge/src/__tests__/observed-launch-integration.test.ts1/1
  • ✓real inherited-terminal shell › preserves quoted executable/arguments, cwd and output redirection215 ms
bridge/src/__tests__/observed-launch.test.ts7/7
  • ✓observed terminal launch › preserves shell grammar on darwin5 ms
  • ✓observed terminal launch › preserves shell grammar on linux0 ms
  • ✓observed terminal launch › preserves shell grammar on win320 ms
  • ✓observed terminal launch › preserves shell grammar on win320 ms
  • ✓observed terminal launch › does not silently misroute hooks from a managed terminal1 ms
  • ✓observed terminal launch › preserves signal exit status and removes signal listeners0 ms
  • ✓observed terminal launch › reports a missing shell and cleans up on failure0 ms
bridge/src/__tests__/observed-steering.test.ts50/50
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › holds a prompt-prone Bash call with no matching rules (the genuine-wait case)7 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › never holds when disabled1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › never holds with zero connected clients (nobody can answer)1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › never holds never-prompt tools (Read/Glob/Grep/TodoWrite)1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › never holds MCP tools (per-server trust state is invisible)1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › never holds unknown tools (not in the prompt-prone set)1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › never holds in auto-approving permission modes1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › acceptEdits: never holds edit-family tools, still holds Bash1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › never holds when a user-level allow rule prefix-matches the command1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › never holds when a PROJECT-LOCAL allow rule matches (session "always allow" persists here)1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › never holds on a bare tool-name allow rule1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › never holds a non-Bash tool when any allow rule exists for it (loose spec match)1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › never holds when a deny rule may match (Claude auto-denies without prompting)1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › never holds when ANY settings file is unparseable (rule picture unknown)1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › compound command: every segment must be covered — an uncovered second segment still holds1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › compound command: allow rules plus the built-in read-only set covering every segment suppress the hold1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › space-star allow rules (the form the permission dialog writes) suppress the hold1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › acceptEdits auto-approves the filesystem commands without any rule1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › built-in read-only commands never hold in default mode1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › holds on a strict ask-rule match (Claude will definitely prompt)1 ms
  • ✓shouldHoldPreToolUse — precision guards (never hold auto-approved calls) › allows only one held gate per session (parallel tool calls pass through)1 ms
  • ✓auto-approval learner (session "always allow" is invisible — learn it) › suppresses a signature after undecided release + tool_end with no permission prompt1 ms
  • ✓auto-approval learner (session "always allow" is invisible — learn it) › does NOT suppress when a permission prompt was shown in between (genuine wait)1 ms
  • ✓auto-approval learner (session "always allow" is invisible — learn it) › does NOT learn from device-decided releases1 ms
  • ✓auto-approval learner (session "always allow" is invisible — learn it) › a release that recorded its tool_use_id learns only from ITS OWN PostToolUse (a parallel Bash must not teach it)1 ms
  • ✓auto-approval learner (session "always allow" is invisible — learn it) › signature granularity: Bash uses the first two command tokens1 ms
  • ✓soft STOP › consumeStop is one-shot1 ms
  • ✓soft STOP › clearOnUserPrompt clears stop + directives (user took over)1 ms
  • ✓soft STOP › clearStop drops the flag without consuming semantics1 ms
  • ✓turn-end directive queue › caps the queue and pops exactly one per takeDirective1 ms
  • ✓turn-end directive queue › rejects empty text1 ms
  • ✓turn-end directive queue › a pending STOP outranks directives: queue is discarded, nothing delivered1 ms
  • ✓turn-end directive queue › empty queue always returns undefined (no stop_hook_active loop)1 ms
  • ✓turn-end directive queue › a synthetic Stop leaves the queue intact — nothing is listening on that response1 ms
  • ✓turn-end directive queue › takeDirectiveForStop tolerates a missing payload0 ms
  • ✓turn-end directive queue › steeringSnapshot reflects both flags for device badges1 ms
  • ✓evaluatePermissionRules verdicts › none when no files exist (and the command is not built-in read-only)3 ms
  • ✓evaluatePermissionRules verdicts › built-in read-only commands are allow even with no rules1 ms
  • ✓evaluatePermissionRules verdicts › exact vs prefix Bash specs2 ms
  • ✓evaluatePermissionRules verdicts › deny beats allow1 ms
  • ✓evaluatePermissionRules verdicts › ask rules never match loosely on unparseable specs (no over-holding)1 ms
  • ✓beginAskGate (AskUserQuestion always prompts — no precision guards) › holds without consulting permission rules or the prompt-prone set2 ms
  • ✓beginAskGate (AskUserQuestion always prompts — no precision guards) › holds even where the rule surface is unreadable (the sandbox case)1 ms
  • ✓beginAskGate (AskUserQuestion always prompts — no precision guards) › never holds when disabled or with nobody to answer1 ms
  • ✓beginAskGate (AskUserQuestion always prompts — no precision guards) › keeps the one-gate-per-session invariant in both directions1 ms
  • ✓beginAskGate (AskUserQuestion always prompts — no precision guards) › releases the session so the next question can hold again1 ms
  • ✓beginAskGate (AskUserQuestion always prompts — no precision guards) › an unanswered ask teaches the auto-approval learner nothing1 ms
  • ✓buildAskAnswerReason (the device answer reaching the agent) › states every Q→A pair and forbids re-asking1 ms
  • ✓buildAskAnswerReason (the device answer reaching the agent) › drops groups that were never answered1 ms
  • ✓buildGateQuestion (device-native semantics, no TUI mirroring) › previews the Bash command / file path / url1 ms
bridge/src/__tests__/observed-turn-watchdogs.test.ts11/11
  • ✓ObservedTurnWatchdogs › recovers a dropped Stop and reports the session it belongs to7 ms
  • ✓ObservedTurnWatchdogs › keeps sessions independent — one session's Stop must not disarm another1 ms
  • ✓ObservedTurnWatchdogs › one session's tool traffic does not keep another's watchdog quiet1 ms
  • ✓ObservedTurnWatchdogs › a real Stop pre-empts recovery for that session2 ms
  • ✓ObservedTurnWatchdogs › the synthetic Stop it triggers disarms it, so it fires once per turn1 ms
  • ✓ObservedTurnWatchdogs › never closes a genuine wait (transcript still shows tool_use)1 ms
  • ✓ObservedTurnWatchdogs › only admits a session that has submitted a prompt1 ms
  • ✓ObservedTurnWatchdogs › sweep() drops cold sessions so their poll timers cannot outlive them1 ms
  • ✓ObservedTurnWatchdogs › sweep() leaves a session that is still posting hooks alone2 ms
  • ✓ObservedTurnWatchdogs › caps tracked sessions, evicting the coldest first1 ms
  • ✓ObservedTurnWatchdogs › stop() (daemon teardown) kills every session timer1 ms
bridge/src/__tests__/opencode-adapter-state.test.ts11/11
  • ✓OpenCodeAdapter run-state arming › arms processing on the first message.part.updated and latches once per turn14 ms
  • ✓OpenCodeAdapter run-state arming › emits idle on session.idle and re-arms on the next turn2 ms
  • ✓OpenCodeAdapter run-state arming › auto-tracks the session and arms when no active session was resolved at connect1 ms
  • ✓OpenCodeAdapter run-state arming › arms when an assistant message is still generating (no time.completed)2 ms
  • ✓OpenCodeAdapter run-state arming › does not arm on a completed assistant message alone1 ms
  • ✓OpenCodeAdapter run-state arming › drops parts for a different active session1 ms
  • ✓OpenCodeAdapter timeline turn shape › upserts the user prompt into chat_start when the user message carries text2 ms
  • ✓OpenCodeAdapter timeline turn shape › routes user text parts to the prompt, not the response3 ms
  • ✓OpenCodeAdapter timeline turn shape › emits chat_response XOR chat_end — response turns produce no chat_end1 ms
  • ✓OpenCodeAdapter timeline turn shape › closes response-less (tool-only) turns with chat_end1 ms
  • ✓OpenCodeAdapter timeline turn shape › resets prompt/response state between turns1 ms
bridge/src/__tests__/opencode-client.test.ts13/13
  • ✓OpenCodeClient › health › should call /global/health9 ms
  • ✓OpenCodeClient › listSessions › should call /session with directory and limit1 ms
  • ✓OpenCodeClient › createSession › should POST to /session1 ms
  • ✓OpenCodeClient › sendMessage › should POST to /session/{id}/message with parts1 ms
  • ✓OpenCodeClient › sendMessage › should include model option when provided1 ms
  • ✓OpenCodeClient › abortSession › should POST to /session/{id}/abort1 ms
  • ✓OpenCodeClient › respondPermission › should POST allow response1 ms
  • ✓OpenCodeClient › respondPermission › should POST deny response0 ms
  • ✓OpenCodeClient › HTTP error handling › should throw on non-ok response2 ms
  • ✓OpenCodeClient › disconnect › should prevent reconnection after disconnect0 ms
  • ✓OpenCodeClient › url accessor › should return server URL0 ms
  • ✓OpenCodeClient › url accessor › should strip trailing slash0 ms
  • ✓OpenCodeAdapter event mapping › should export OpenCodeSSEEvent type0 ms
bridge/src/__tests__/opencode-hook.test.ts9/9
  • ✓opencode-hook → telemetry spans › tool part with status=running produces a tool_call span3 ms
  • ✓opencode-hook → telemetry spans › tool part with status=completed produces a tool_result span0 ms
  • ✓opencode-hook → telemetry spans › todowrite completion with all todos completed emits a task_boundary span (todo_complete)0 ms
  • ✓opencode-hook → telemetry spans › todowrite completion with a pending todo does NOT emit a task_boundary0 ms
  • ✓opencode-hook → telemetry spans › todowrite reads todos out of state.output JSON string when input is absent0 ms
  • ✓opencode-hook → telemetry spans › non-tool part returns an empty span list1 ms
  • ✓opencode-hook → telemetry spans › message.updated for user role with prompt emits a turn_start1 ms
  • ✓opencode-hook → telemetry spans › message.updated for assistant role with response emits session_meta + turn_response0 ms
  • ✓opencode-hook → telemetry spans › assistant message with modelID but no response still emits session_meta (early model attribution)0 ms
bridge/src/__tests__/opencode-steering.test.ts6/6
  • ✓opencode steering queue › poll drains queued commands immediately4 ms
  • ✓opencode steering queue › a parked long-poll wakes the moment a command arrives0 ms
  • ✓opencode steering queue › long-poll resolves empty after the wait window1.0 s
  • ✓opencode steering queue › permission_respond commands pass through with id + decision intact0 ms
  • ✓opencode steering queue › caps the queue1 ms
  • ✓opencode steering queue › steerability reflects a live poller1.0 s
bridge/src/__tests__/passive-observer.test.ts45/45
  • ✓passive-observer parsers › expires a silent observed processing state without hiding fresh work3 ms
  • ✓passive-observer parsers › parses ps output without depending on fixed command columns1 ms
  • ✓passive-observer parsers › parses Win32_Process JSON, converting bytes to KB and dropping null command lines0 ms
  • ✓passive-observer parsers › parses the bare object ConvertTo-Json emits for a single row0 ms
  • ✓passive-observer parsers › returns no rows for non-JSON scan output0 ms
  • ✓passive-observer parsers › parses scan output that arrives with a UTF-8 BOM1 ms
  • ✓passive-observer parsers › summarizes Claude transcripts and redacts tool secrets2 ms
  • ✓passive-observer parsers › reads idle after an ESC/interrupt marker aborts a pending tool_use0 ms
  • ✓passive-observer parsers › summarizes Codex rollout metadata, context, and pending tool calls1 ms
  • ✓passive-observer parsers › reads idle after task_complete even when sampling dropped tool outputs0 ms
  • ✓passive-observer parsers › reads idle after turn_aborted with dangling tool calls0 ms
  • ✓passive-observer parsers › clears stale pending calls from a prior turn when a new user message arrives0 ms
  • ✓passive-observer parsers › stays processing through mid-turn thinking gaps and agent messages0 ms
  • ✓passive-observer parsers › maps lsof field output to Codex rollout files by pid0 ms
  • ✓passive-observer parsers › keeps every rollout a desktop app-server pid holds open, deduped0 ms
  • ✓passive-observer parsers › admits Codex Desktop app-server but rejects helper lookalikes1 ms
  • ✓passive-observer parsers › matches Win32 command lines — quoted paths, backslashes, .exe suffix0 ms
  • ✓passive-observer parsers › finds an npm-installed Claude Code, which never puts "claude" in argv[0]0 ms
  • ✓passive-observer parsers › drops Electron child processes that reuse the app binary name1 ms
  • ✓passive-observer parsers › matches Kiro CLI and IDE owners while rejecting Electron helpers1 ms
  • ✓passive-observer parsers › collapses the Kiro launcher and kiro-cli-chat child to one chat process0 ms
  • ✓passive-observer parsers › collapses the real four-deep chat tree, acp child included, to one process0 ms
  • ✓passive-observer parsers › reads Kiro 2.x metadata and transcript turn state2 ms
  • ✓passive-observer parsers › reads a Kiro tool subject out of the nested operations shape0 ms
  • ✓passive-observer parsers › reads documented Kiro ACP session notifications for the v3 fallback0 ms
  • ✓passive-observer parsers › does not complete a Kiro turn after ten minutes of silent tool work1 ms
  • ✓passive-observer parsers › correlates a naturally launched Kiro process with its newest cwd session1 ms
  • ✓passive-observer parsers › marks internal subagent rollouts from session_meta source0 ms
  • ✓passive-observer parsers › reuses unchanged rollout summaries and invalidates changes/removals1 ms
  • ✓passive-observer parsers › surfaces top-level Desktop rollouts and excludes internal subagents1 ms
  • ✓passive-observer parsers › drops stale idle Desktop rollouts while retaining recent and processing chats1 ms
  • ✓passive-observer parsers › keeps an idle Codex CLI rollout because its owning process is the live session1 ms
  • ✓passive-observer parsers › dedupes a hook-observed Desktop conversation without hiding sibling rollouts1 ms
  • ✓passive-observer parsers › counts Codex cached input once when the producer folds it into input_tokens3 ms
  • ✓passive-observer parsers › recognizes standalone Antigravity processes for CLI daemon passive discovery0 ms
  • ✓passive-observer scan resilience › retains the process table when a scan comes back empty3 ms
  • ✓passive-observer scan resilience › retains the last roster when a scan rejects1 ms
  • ✓passive-observer scan resilience › backs the scan cadence off to the cost of the last scan0 ms
  • ✓passive-observer scan resilience › schedules a 200 ms scan with 4800 ms remaining (rejects=false)3 ms
  • ✓passive-observer scan resilience › schedules a 11000 ms scan with 11000 ms remaining (rejects=false)1 ms
  • ✓passive-observer scan resilience › schedules a 30000 ms scan with 30000 ms remaining (rejects=true)1 ms
  • ✓passive-observer scan resilience › schedules a 70000 ms scan with 60000 ms remaining (rejects=false)1 ms
  • ✓foldHeadlessCodexSessions › drops a headless run that descends from an observed session and reports it as that session's child1 ms
  • ✓foldHeadlessCodexSessions › keeps a headless run with no session ancestor as a row, reported with parent null0 ms
  • ✓foldHeadlessCodexSessions › never leaks codexOriginator onto a roster row and leaves TUI/desktop rows alone1 ms
bridge/src/__tests__/permission-resolver.test.ts12/12
  • ✓permission-resolver › register → resolve(allow) ends the held response with allow5 ms
  • ✓permission-resolver › resolve(deny) ends with deny0 ms
  • ✓permission-resolver › times out to an EMPTY pass-through body after timeoutMs (never an explicit "ask")1 ms
  • ✓permission-resolver › resolving an unknown id returns null and is a no-op0 ms
  • ✓permission-resolver › double resolve: second returns null (already resolved)0 ms
  • ✓permission-resolver › abandonPending drops the entry without writing a decision0 ms
  • ✓permission-resolver › sweepStalePending releases entries older than maxAge as pass-through0 ms
  • ✓permission-resolver › re-registering a duplicate requestId releases the stale entry first1 ms
  • ✓permission-resolver › resolving over a dead socket does not throw and still clears the entry1 ms
  • ✓permission-resolver › fires onResolved exactly once on a device decision1 ms
  • ✓permission-resolver › fires onResolved with "pass" on timeout (arms the auto-approval learner)1 ms
  • ✓permission-resolver › drainAllPending releases every held response as pass-through1 ms
bridge/src/__tests__/project-name.test.ts32/32
  • ✓resolveProjectName › returns git toplevel basename from a nested subdir17 ms
  • ✓resolveProjectName › labels real linked worktrees consistently without reading or changing their branch38 ms
  • ✓resolveProjectName › falls back to nearest package.json name when no git7 ms
  • ✓resolveProjectName › skips package.json with empty name and keeps walking7 ms
  • ✓resolveProjectName › falls back to cwd basename when no git and no package.json6 ms
  • ✓resolveProjectName › returns 'unknown' for a basename-less root6 ms
  • ✓resolveProjectName › AGENTDECK_PROJECT_NAME env var wins over everything11 ms
  • ✓resolveProjectName › envOverride option takes precedence over env var1 ms
  • ✓resolveProjectName › preserves scoped package name verbatim5 ms
  • ✓resolveProjectName › git subprocess error (non-repo) does not throw7 ms
  • ✓resolveProjectName › malformed package.json is ignored (walks to parent)6 ms
  • ✓gitToplevelBasename › returns null outside a git worktree5 ms
  • ✓gitToplevelBasename › returns repo basename from nested subdir18 ms
  • ✓resolveProjectNameFromCwdCached (passive-observer resolver) › matches the PTY resolver for a git repo subdir (launch-path parity)16 ms
  • ✓resolveProjectNameFromCwdCached (passive-observer resolver) › detects a .git FILE (worktree/submodule layout)1 ms
  • ✓resolveProjectNameFromCwdCached (passive-observer resolver) › falls back to package.json name, then basename1 ms
  • ✓resolveProjectNameFromCwdCached (passive-observer resolver) › memoizes per cwd (stale after dir changes are acceptable)9 ms
  • ✓nearestPackageJsonName › returns null when no ancestor has package.json1 ms
  • ✓nearestPackageJsonName › returns nearest ancestor name1 ms
  • ✓shared worktree project-label contract › relative linked worktree2 ms
  • ✓shared worktree project-label contract › absolute linked worktree2 ms
  • ✓shared worktree project-label contract › absolute common directory2 ms
  • ✓shared worktree project-label contract › Claude nested worktree3 ms
  • ✓shared worktree project-label contract › same basename retains worktree distinction2 ms
  • ✓shared worktree project-label contract › spaces and Unicode3 ms
  • ✓shared worktree project-label contract › detached worktree2 ms
  • ✓shared worktree project-label contract › submodule is its own project2 ms
  • ✓shared worktree project-label contract › missing common directory1 ms
  • ✓shared worktree project-label contract › missing common HEAD2 ms
  • ✓shared worktree project-label contract › bare common repository2 ms
  • ✓shared worktree project-label contract › malformed marker3 ms
  • ✓shared worktree project-label contract › empty common pointer2 ms
bridge/src/__tests__/remote-sessions.test.ts7/7
  • ✓remote-sessions registry › registers a remote session with display host/port6 ms
  • ✓remote-sessions registry › keeps startedAt stable but refreshes target on re-register1 ms
  • ✓remote-sessions registry › re-register swaps the sender; the old socket cannot remove or overwrite1 ms
  • ✓remote-sessions registry › updates state only for known sessions0 ms
  • ✓remote-sessions registry › renders enriched rows marked alive/managed1 ms
  • ✓remote-sessions registry › normalizes remote weight and preserves it for legacy re-registration1 ms
  • ✓remote-sessions registry › removes a session0 ms
bridge/src/__tests__/session-id-resolve.test.ts4/4
  • ✓resolveSessionIdPrefix › returns exact matches untouched3 ms
  • ✓resolveSessionIdPrefix › restores the ESP32 31-char truncation of an observed id0 ms
  • ✓resolveSessionIdPrefix › leaves ambiguous prefixes unresolved0 ms
  • ✓resolveSessionIdPrefix › leaves unknown ids and empty input untouched1 ms
hooks/src/__tests__/codex-install.test.ts79/79
  • ✓codex-mini-toml: applyManagedBlock › appends fence when absent4 ms
  • ✓codex-mini-toml: applyManagedBlock › replaces existing fence block1 ms
  • ✓codex-mini-toml: applyManagedBlock › apply twice is idempotent1 ms
  • ✓codex-mini-toml: applyManagedBlock › moves Codex hook trust state out of the managed fence1 ms
  • ✓codex-mini-toml: removeManagedBlock › leaves user content0 ms
  • ✓codex-mini-toml: removeManagedBlock › is idempotent without fence0 ms
  • ✓codex-mini-toml: hasTopLevelKeyOutsideFence › detects user notify key0 ms
  • ✓codex-mini-toml: hasTopLevelKeyOutsideFence › ignores notify key inside table0 ms
  • ✓codex-mini-toml: hasTopLevelKeyOutsideFence › ignores notify key inside fence0 ms
  • ✓codex-mini-toml: hasTableOutsideFence › detects user [otel] table0 ms
  • ✓codex-mini-toml: hasTableOutsideFence › detects user [otel.exporter] dotted table0 ms
  • ✓codex-mini-toml: hasTableOutsideFence › detects array-of-table header [[hooks.Stop]]0 ms
  • ✓codex-mini-toml: hasTableOutsideFence › ignores Codex hook trust state outside fence0 ms
  • ✓codex-mini-toml: hasTableOutsideFence › ignores [otel] inside fence0 ms
  • ✓codex-mini-toml: hasTableOutsideFence › ignores [otelfoo] (word boundary)0 ms
  • ✓codex-mini-toml: hasIncompatibleHookTableOutsideFence › allows official lifecycle arrays to merge1 ms
  • ✓codex-mini-toml: hasIncompatibleHookTableOutsideFence › rejects regular hook tables while ignoring trust state0 ms
  • ✓codex-mini-toml: quoted › escapes backslash and double quote0 ms
  • ✓codex-mini-toml: quoted › escapes newline and tab0 ms
  • ✓codex-mini-toml: quoted › passes simple ASCII0 ms
  • ✓codex-install: managedBlockBody › roundtrip preserves user TOML byte-for-byte (load-bearing)33 ms
  • ✓codex-install: managedBlockBody › matches Codex schema (lifecycle hooks + endpoints + notify dummy)6 ms
  • ✓codex-install: managedBlockBody › posts the launcher PID through the real command shell57 ms
  • ✓codex-install: managedBlockBody › uses PowerShell command hooks on Windows30 ms
  • ✓codex-install: managedBlockBody › windows notify sidecar reads payload from $args and posts codex_turn_complete1 ms
  • ✓codex-install: managedBlockBody › falls back instead of emitting an out-of-range OTel endpoint2 ms
  • ✓codex-install: managedBlockBody › omits conflicting optional channels when asked2 ms
  • ✓codex-install: install / uninstall (file I/O) › preserves permissions and refuses dotfile-manager symlinks112 ms
  • ✓codex-install: install / uninstall (file I/O) › reports refused removal and retains both config and sidecar1 ms
  • ✓codex-install: install / uninstall (file I/O) › refreshes pre-PID hooks while retaining user settings and trust records64 ms
  • ✓codex-install: install / uninstall (file I/O) › preserves settings inserted by Codex between installs and on removal (linux)71 ms
  • ✓codex-install: install / uninstall (file I/O) › preserves settings inserted by Codex between installs and on removal (win32)277 ms
  • ✓codex-install: install / uninstall (file I/O) › refuses invalid UTF-8 without rewriting bytes1 ms
  • ✓codex-install: install / uninstall (file I/O) › keeps notify in root scope when migrating an appended fence29 ms
  • ✓codex-install: install / uninstall (file I/O) › creates config with fence when file is absent4 ms
  • ✓codex-install: install / uninstall (file I/O) › preserves user content when installing into existing config3 ms
  • ✓codex-install: install / uninstall (file I/O) › preserves a user disabling hooks in [features]1 ms
  • ✓codex-install: install / uninstall (file I/O) › preserves user lifecycle arrays in the same TOML representation3 ms
  • ✓codex-install: install / uninstall (file I/O) › skips when user has an incompatible regular [hooks] table1 ms
  • ✓codex-install: install / uninstall (file I/O) › omits notify when user has top-level notify3 ms
  • ✓codex-install: install / uninstall (file I/O) › omits OTel when user has [otel] table3 ms
  • ✓codex-install: install / uninstall (file I/O) › refreshes Windows pre-PID commands and sidecar without losing user settings or trust258 ms
  • ✓codex-install: install / uninstall (file I/O) › omits OTel by default on Windows installs18 ms
  • ✓codex-install: install / uninstall (file I/O) › writes the notify sidecar script on Windows and removes it on uninstall212 ms
  • ✓codex-install: install / uninstall (file I/O) › downgrades to lifecycle-only with a warning when the sidecar write fails13 ms
  • ✓codex-install: install / uninstall (file I/O) › skips notify on Windows when user has top-level notify (no sidecar written)13 ms
  • ✓codex-install: install / uninstall (file I/O) › uninstall strips fence and preserves user content18 ms
  • ✓codex-install: install / uninstall (file I/O) › uninstall is idempotent when no config exists1 ms
  • ✓codex-install: install / uninstall (file I/O) › honours AGENTDECK_NO_CODEX_HOOKS=1 opt-out0 ms
  • ✓codex-install: install / uninstall (file I/O) › install is idempotent (same port → no rewrite)30 ms
  • ✓codex-install: install / uninstall (file I/O) › preserves Codex hook trust state across reinstall50 ms
  • ✓shared lossless config cases › 'fresh'61 ms
  • ✓shared lossless config cases › 'enabled'48 ms
  • ✓shared lossless config cases › 'commented'50 ms
  • ✓shared lossless config cases › 'disabled'1 ms
  • ✓shared lossless config cases › 'missing'1 ms
  • ✓shared lossless config cases › 'inline'1 ms
  • ✓shared lossless config cases › 'quoted'0 ms
  • ✓shared lossless config cases › 'dotted'0 ms
  • ✓shared lossless config cases › 'truncated'0 ms
  • ✓shared lossless config cases › 'multiline'48 ms
  • ✓shared lossless config cases › 'crlf'45 ms
  • ✓shared lossless config cases › 'multiline-array'35 ms
  • ✓shared lossless config cases › 'user-notify-otel'34 ms
  • ✓shared lossless config cases › 'root-and-profile'41 ms
  • ✓shared lossless config cases › 'later-root-key'41 ms
  • ✓shared lossless config cases › 'later-enabled-feature'45 ms
  • ✓shared lossless config cases › 'later-disabled-hooks'1 ms
  • ✓shared lossless config cases › 'triple-quotes-comment'45 ms
  • ✓shared lossless config cases › 'literal-fake-fence'41 ms
  • ✓shared lossless config cases › 'unterminated-multiline'1 ms
  • ✓shared lossless config cases › 'later-multiline-root'40 ms
  • ✓shared lossless config cases › 'multiline-crlf'49 ms
  • ✓shared lossless config cases › 'extended-managed-hook'1 ms
  • ✓shared lossless config cases › 'user-hook-in-managed-group'1 ms
  • ✓shared lossless config cases › 'foreign-notify-in-fence'37 ms
  • ✓shared lossless config cases › 'foreign-table-in-fence'43 ms
  • ✓shared lossless config cases › 'owned-windows-hook'45 ms
  • ✓shared lossless config cases › 'foreign-windows-hook'44 ms
hooks/src/__tests__/hermes-install.test.ts3/3
  • ✓explicit Hermes observer installation › preserves profile configuration and refreshes only its own directory6 ms
  • ✓explicit Hermes observer installation › refuses a directory it does not own2 ms
  • ✓explicit Hermes observer installation › runs the Python observer behavior/transport suite1.5 s
hooks/src/__tests__/install.test.ts55/55
  • ✓Hook Installer › buildHookEntry › creates matcher-group format naming the event9 ms
  • ✓Hook Installer › buildHookEntry › uses `*` matcher for tool events and empty matcher for lifecycle events1 ms
  • ✓Hook Installer › buildHookCommand (POSIX) › reads PORT from AGENTDECK_PORT env var first, then daemon.json, then 91202 ms
  • ✓Hook Installer › buildHookCommand (POSIX) › keeps strict port-validation markers in every co-owned hook writer2 ms
  • ✓Hook Installer › buildHookCommand (POSIX) › rejects non-numeric and out-of-range ports before URL construction206 ms
  • ✓Hook Installer › buildHookCommand (POSIX) › accepts a validated daemon.json port42 ms
  • ✓Hook Installer › buildHookCommand (POSIX) › bounds a blocked daemon-file read before the hook can stall248 ms
  • ✓Hook Installer › buildHookCommand (POSIX) › bounds the fire-and-forget POST so a wedged daemon cannot stall session exit1 ms
  • ✓Hook Installer › buildHookCommand (POSIX) › emits newline-separated shell so if/then/for/do keywords are not mis-terminated by `;`1 ms
  • ○Hook Installer › buildHookCommandWin (Windows) › delivers UTF-8 stdin through Git Bash and Windows PowerShell
  • ✓Hook Installer › buildHookCommandWin (Windows) › invokes the hook script by path and passes the event as a parameter1 ms
  • ✓Hook Installer › buildHookCommandWin (Windows) › puts no "$" on the command line, because a POSIX shell parses it first1 ms
  • ✓Hook Installer › buildHookCommandWin (Windows) › stays a single ASCII line0 ms
  • ✓Hook Installer › WINDOWS_HOOK_SCRIPT › keeps the port discovery, validation and UTF-8 handling of the old one-liner1 ms
  • ✓Hook Installer › WINDOWS_HOOK_SCRIPT › omits the macOS App Store sandbox-container fallback paths0 ms
  • ✓Hook Installer › WINDOWS_HOOK_SCRIPT › accepts the underscored daemon events the Kiro hook file uses1 ms
  • ✓Hook Installer › WINDOWS_HOOK_SCRIPT › writes the script idempotently and repairs a tampered copy2 ms
  • ✓Hook Installer › isAgentDeckHookCommand › recognises the Windows -File form, which carries neither legacy marker1 ms
  • ✓Hook Installer › applyHooks › installs hooks to empty settings in matcher-group format6 ms
  • ✓Hook Installer › applyHooks › preserves non-AgentDeck hooks1 ms
  • ✓Hook Installer › applyHooks › replaces old flat-format hooks1 ms
  • ✓Hook Installer › applyHooks › replaces old matcher-format hooks1 ms
  • ✓Hook Installer › applyHooks › is idempotent — running twice produces same result2 ms
  • ✓Hook Installer › applyHooks › preserves existing non-hook settings1 ms
  • ✓Hook Installer › removeHooks › removes all AgentDeck hooks (new format)1 ms
  • ✓Hook Installer › removeHooks › removes old flat-format AgentDeck hooks0 ms
  • ✓Hook Installer › removeHooks › preserves non-AgentDeck hooks0 ms
  • ✓Hook Installer › removeHooks › handles empty settings gracefully0 ms
  • ✓Hook Installer › migrateHooks › migrates old hardcoded port to env var1 ms
  • ✓Hook Installer › migrateHooks › migrates flat format to matcher-group format0 ms
  • ✓Hook Installer › migrateHooks › skips already-migrated hooks (new format)0 ms
  • ✓Hook Installer › migrateHooks › skips non-AgentDeck hooks0 ms
  • ✓Hook Installer › migrateHooks › migrates multiple events at once1 ms
  • ✓Hook Installer › migrateHooks › migrates hardcoded port inside matcher-group0 ms
  • ✓Hook Installer › migrateHooksIfNeeded (file-based) › upgrades old :-9120 fallback hooks to daemon.json-reading format0 ms
  • ✓Hook Installer › migrateHooksIfNeeded (file-based) › self-heals installed hooks that predate strict port validation4 ms
  • ✓migration 12 (Windows inline -Command → script file) › rewrites hooks whose $variables a POSIX shell would eat5 ms
  • ✓migration 12 (Windows inline -Command → script file) › does not duplicate hooks across repeated installs7 ms
  • ✓migration 12 (Windows inline -Command → script file) › removes the script on uninstall4 ms
  • ✓migration 12 (Windows inline -Command → script file) › repairs the script without rewriting current settings3 ms
  • ✓migration 12 (Windows inline -Command → script file) › keeps the bootstrap script byte-identical to the hooks script1 ms
  • ✓steering hook channels (request-response) › PreToolUse and Stop echo the daemon response to stdout; others stay fire-and-forget1 ms
  • ✓steering hook channels (request-response) › migration 5 rewrites legacy Stop hooks to the current platform hook set (request-response on POSIX)1 ms
  • ✓install target (~/.claude/settings.json) › installs into the watched settings.json, never settings.local.json2 ms
  • ✓install target (~/.claude/settings.json) › strips AgentDeck hooks out of the legacy file while keeping the user keys2 ms
  • ✓install target (~/.claude/settings.json) › relocates pre-move installs from the legacy file into settings.json3 ms
  • ✓install target (~/.claude/settings.json) › leaves a settings.json without AgentDeck hooks untouched1 ms
  • ✓install target (~/.claude/settings.json) › uninstalls from both the current and the legacy file3 ms
  • ✓Kiro v3 global hook installer › skips hosts where Kiro has never created its config root3 ms
  • ✓Kiro v3 global hook installer › writes the measured v1 standalone schema with prefixed lifecycle endpoints2 ms
  • ✓Kiro v3 global hook installer › is idempotent, preserves an occupied path, and only removes its own file2 ms
  • ✓Kiro v3 global hook installer › provisions and repairs the Windows script without a Claude installation3 ms
  • ✓migration 7 — unbounded curl self-heal › flags a pre-fix hook set that every earlier migration passes by1 ms
  • ✓migration 7 — unbounded curl self-heal › rebuilds unbounded hooks on migrate and then leaves the file alone3 ms
  • ✓migration 7 — unbounded curl self-heal › does not flag the request-response hooks for lacking a short timeout1 ms
  • ✓uninstall wiring › the uninstall entry point actually calls the Kiro remover0 ms
hooks/src/__tests__/opencode-install.test.ts16/16
  • ✓opencodePluginSource contract › posts the four lifecycle events the daemon pipeline understands2 ms
  • ✓opencodePluginSource contract › carries the payload fields the daemon reads0 ms
  • ✓opencodePluginSource contract › self-disables inside managed agentdeck PTYs (AGENTDECK_PORT marker)0 ms
  • ✓opencodePluginSource contract › resolves the daemon port from the canonical daemon.json candidates0 ms
  • ✓opencodePluginSource contract › bounds every network call (peer silence is a first-class signal)0 ms
  • ✓opencodePluginSource contract › separates user prompt parts from assistant response parts by messageID role0 ms
  • ✓AgentDeckObserver event sequencing › forwards current permission and question request identities41 ms
  • ✓AgentDeckObserver event sequencing › forwards legacy permission replies and header-only questions30 ms
  • ✓AgentDeckObserver event sequencing › keeps posting when every registry read is stuck awaiting OS access862 ms
  • ✓AgentDeckObserver event sequencing › posts one user_prompt_submit per user message, including after the turn settles80 ms
  • ✓AgentDeckObserver event sequencing › still reports a genuine second turn on a new user message85 ms
  • ✓installOpenCodeHooksIfNeeded › writes the plugin file (creating parent dirs) and is idempotent2 ms
  • ✓installOpenCodeHooksIfNeeded › refreshes a stale or edited plugin file2 ms
  • ✓installOpenCodeHooksIfNeeded › honours the AGENTDECK_NO_OPENCODE_HOOKS opt-out1 ms
  • ✓installOpenCodeHooksIfNeeded › uninstall removes the plugin file and is idempotent1 ms
  • ✓installOpenCodeHooksIfNeeded › default path lives under the opencode plugins dir0 ms
hooks/src/__tests__/windows-codex-pid.test.ts0/0
  • ○Windows Codex hook launcher identity › crosses cmd.exe and sends the actual Codex launcher PID with UTF-8 stdin
  • ○Windows Codex hook launcher identity › discovers the registry port through the bounded native HTTP health probe
  • ○Windows Codex hook launcher identity › does not label an unrelated Node launcher as Codex
  • ○Windows Codex hook launcher identity › still delivers the lifecycle event when the process query is unavailable
  • ○Windows Codex hook launcher identity › does not loop or invent a PID from cyclic process evidence
shared/src/__tests__/claude-permission-rules-sync.test.ts4/4
  • ✓generated Claude permission-rules mirror in sync › apple/AgentDeck/Daemon/Session/ClaudePermissionRules.generated.swift matches the SSOT4 ms
  • ✓generated Claude permission-rules mirror in sync › emitter embeds the SSOT constants3 ms
  • ✓generated Claude permission-rules mirror in sync › the Swift suite replays the shared vector file (grep the test wiring)0 ms
  • ✓generated Claude permission-rules mirror in sync › no daemon keeps a private copy of the Bash rule matcher7 ms
shared/src/__tests__/claude-permission-rules.test.ts88/88
  • ✓claude permission vectors (shared with the Swift suite) › is large enough to be a gate3 ms
  • ✓claude permission vectors (shared with the Swift suite) › no rules, default mode: a prompt-prone Bash call is the genuine-wait case2 ms
  • ✓claude permission vectors (shared with the Swift suite) › space-star allow rule (the form the permission dialog writes) suppresses the hold1 ms
  • ✓claude permission vectors (shared with the Swift suite) › legacy colon-star spelling is an alias of the trailing space-star0 ms
  • ✓claude permission vectors (shared with the Swift suite) › legacy colon-star also keeps its raw-prefix reading (npm run test:* ⇒ npm run test:watch) — over-matching is the safe direction0 ms
  • ✓claude permission vectors (shared with the Swift suite) › trailing ' *' also matches the bare command (Bash(ls *) ⇒ ls)0 ms
  • ✓claude permission vectors (shared with the Swift suite) › the space before a trailing * is part of the rule: Bash(ls *) does not cover lsof0 ms
  • ✓claude permission vectors (shared with the Swift suite) › Bash(ls*) has no space, so it does cover lsof0 ms
  • ✓claude permission vectors (shared with the Swift suite) › a wildcard in the middle: Bash(git log * main) covers git log --oneline main0 ms
  • ✓claude permission vectors (shared with the Swift suite) › Bash(git log * main) does not cover git log main (the * needs text in its place)0 ms
  • ✓claude permission vectors (shared with the Swift suite) › Bash(git * main) covers git push origin main0 ms
  • ✓claude permission vectors (shared with the Swift suite) › a leading wildcard: Bash(* --version) covers node --version0 ms
  • ✓claude permission vectors (shared with the Swift suite) › Bash(* --version) does not cover node -v0 ms
  • ✓claude permission vectors (shared with the Swift suite) › two wildcards: Bash(* --help *) covers npm --help x but not the bare npm --help0 ms
  • ✓claude permission vectors (shared with the Swift suite) › exact rule matches only the exact command0 ms
  • ✓claude permission vectors (shared with the Swift suite) › bare tool-name allow rule covers everything0 ms
  • ✓claude permission vectors (shared with the Swift suite) › compound command: every subcommand must be covered — one uncovered segment still prompts0 ms
  • ✓claude permission vectors (shared with the Swift suite) › compound command fully covered by allow rules and the read-only set0 ms
  • ✓claude permission vectors (shared with the Swift suite) › an operator inside quotes does not split the command0 ms
  • ✓claude permission vectors (shared with the Swift suite) › acceptEdits auto-approves mkdir/cp even without a rule0 ms
  • ✓claude permission vectors (shared with the Swift suite) › default mode does NOT auto-approve mkdir/cp0 ms
  • ✓claude permission vectors (shared with the Swift suite) › built-in read-only command never prompts (no rule needed)0 ms
  • ✓claude permission vectors (shared with the Swift suite) › read-only git form never prompts0 ms
  • ✓claude permission vectors (shared with the Swift suite) › git branch with a name argument writes, so it is not read-only0 ms
  • ✓claude permission vectors (shared with the Swift suite) › a write redirect makes a read-only command prompt0 ms
  • ✓claude permission vectors (shared with the Swift suite) › a /dev/null redirect is not a write0 ms
  • ✓claude permission vectors (shared with the Swift suite) › wrappers and safe env assignments are stripped before matching0 ms
  • ✓claude permission vectors (shared with the Swift suite) › an unparseable compound (operator with nothing after it) is matched as one text and prompts0 ms
  • ✓claude permission vectors (shared with the Swift suite) › heredoc is not split; the whole text is matched against the rule0 ms
  • ✓claude permission vectors (shared with the Swift suite) › deny rule: Claude auto-denies without prompting0 ms
  • ✓claude permission vectors (shared with the Swift suite) › ask rule definitely prompts0 ms
  • ✓claude permission vectors (shared with the Swift suite) › unreadable settings: never hold0 ms
  • ✓claude permission vectors (shared with the Swift suite) › auto mode never holds (the classifier decides outside the settings files)0 ms
  • ✓claude permission vectors (shared with the Swift suite) › bypassPermissions never holds0 ms
  • ✓claude permission vectors (shared with the Swift suite) › dontAsk never holds0 ms
  • ✓claude permission vectors (shared with the Swift suite) › plan mode never holds0 ms
  • ✓claude permission vectors (shared with the Swift suite) › absent mode is treated as default0 ms
  • ✓claude permission vectors (shared with the Swift suite) › acceptEdits: edit-family tools never hold0 ms
  • ✓claude permission vectors (shared with the Swift suite) › default: Edit with no rule holds0 ms
  • ✓claude permission vectors (shared with the Swift suite) › non-Bash tool with any allow rule for it: loose match suppresses the hold0 ms
  • ✓claude permission vectors (shared with the Swift suite) › never-prompt tools never hold0 ms
  • ✓claude permission vectors (shared with the Swift suite) › MCP tools never hold (per-server trust state is invisible)0 ms
  • ✓claude permission vectors (shared with the Swift suite) › unknown future tools never hold0 ms
  • ✓claude permission vectors (shared with the Swift suite) › code-point matching: a Korean argument matches through a wildcard0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(npm run build) matches "npm run build"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(npm run build) does not match "npm run build --watch"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(npm run *) matches "npm run build"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(npm run *) matches "npm run test --watch"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(npm run *) matches "npm run"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(npm run *) does not match "npm install"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(git log * main) matches "git log --oneline main"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(git log * main) matches "git log -5 main"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(git log * main) does not match "git log main"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(git log * main) does not match "git push origin main"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(git * main) matches "git merge main"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(git * main) matches "git -c core.fsmonitor=x diff main"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(git * main) does not match "git log"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(* --version) matches "node --version"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(* --version) matches "bash -c 'echo hi' --version"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(* --version) does not match "node -v"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(ls *) matches "ls -la"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(ls *) matches "ls"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(ls *) does not match "lsof"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(ls*) matches "ls -la"1 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(ls*) matches "lsof"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(* --help *) matches "npm --help x"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(* --help *) does not match "npm --help"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(ls:*) matches "ls -la"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(ls:*) matches "ls"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(ls:*) matches "lsof"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(npm run test:*) matches "npm run test:watch"0 ms
  • ✓bashRuleMatches — documented wildcard table › Bash(*) matches "anything at all"0 ms
  • ✓bashRuleMatches — documented wildcard table › globMatch is linear on adversarial input (many wildcards, long text)1 ms
  • ✓splitCompoundCommand › splits on every documented operator1 ms
  • ✓splitCompoundCommand › keeps operators inside quotes0 ms
  • ✓splitCompoundCommand › refuses heredocs, substitutions and unbalanced quotes0 ms
  • ✓splitCompoundCommand › refuses an operator with nothing after it0 ms
  • ✓wrapper stripping and built-in sets › strips env assignments and wrappers0 ms
  • ✓wrapper stripping and built-in sets › recognises the documented read-only set and its git forms1 ms
  • ✓wrapper stripping and built-in sets › recognises the acceptEdits filesystem commands0 ms
  • ✓evaluatePermissionRules › deny outranks allow0 ms
  • ✓evaluatePermissionRules › ask outranks allow0 ms
  • ✓evaluatePermissionRules › allow covers the rest0 ms
  • ✓evaluatePermissionRules › unknown when rules are unreadable0 ms
  • ✓evaluatePermissionRules › none when nothing decides0 ms
  • ✓mode gate, signature, learn window › gates only default/acceptEdits (non-edit) calls0 ms
  • ✓mode gate, signature, learn window › signature is the first two Bash tokens0 ms
  • ✓mode gate, signature, learn window › learn window outlasts a slow tool (was 8 s, which a curl fetch exceeds)0 ms
shared/src/__tests__/observed-agent-rules.test.ts5/5
  • ✓observed session ids › strips every advertised prefix, Kiro included4 ms
  • ✓observed session ids › keeps the regex and the list from disagreeing about which agents exist1 ms
  • ✓observed tool_exec suppression › covers every observed agent whose tool rows are a firehose0 ms
  • ✓generated mirrors › match this source1 ms
  • ✓generated mirrors › leaves no hand-written copy of the list anywhere in the app sources161 ms
shared/src/__tests__/subagent-activity.test.ts5/5
  • ✓deriveSubagentActivity › pairs summarized starts and completions under their parent session4 ms
  • ✓deriveSubagentActivity › uses team completion rows only as a recent completion pulse0 ms
  • ✓deriveSubagentActivity › ignores ordinary tools and expires orphaned starts0 ms
  • ✓deriveSubagentActivity › treats a dispatch older than the parent's own turn close as drained (lost stop rows)0 ms
  • ✓deriveSubagentActivity › sorts history rows before pairing them0 ms

11 files

Pass

Managed sessions & terminal parsing

Does agentdeck claude|codex read the terminal correctly and route commands to the right PTY?

Passed
407
Failed
0
Skipped
0
Time
1.3 s
11 test files
bridge/src/__tests__/adapter.test.ts95/95
  • ✓createAdapter factory › creates ClaudeCodeAdapter for "claude-code"3 ms
  • ✓createAdapter factory › creates OpenClawAdapter for "openclaw"1 ms
  • ✓createAdapter factory › passes gatewayUrl to OpenClawAdapter0 ms
  • ✓createAdapter factory › creates CodexCliAdapter for "codex-cli"0 ms
  • ✓createAdapter factory › throws for unknown agent type1 ms
  • ✓ClaudeCodeAdapter › capabilities › reports all Claude Code capabilities as true1 ms
  • ✓ClaudeCodeAdapter › handleCommand routing › handles respond → returns true0 ms
  • ✓ClaudeCodeAdapter › handleCommand routing › handles switch_mode → returns true0 ms
  • ✓ClaudeCodeAdapter › handleCommand routing › handles interrupt → returns true0 ms
  • ✓ClaudeCodeAdapter › handleCommand routing › handles escape → returns true0 ms
  • ✓ClaudeCodeAdapter › handleCommand routing › defers select_option to bridge → returns false0 ms
  • ✓ClaudeCodeAdapter › handleCommand routing › defers navigate_option to bridge → returns false0 ms
  • ✓ClaudeCodeAdapter › handleCommand routing › defers send_prompt to bridge → returns false0 ms
  • ✓ClaudeCodeAdapter › handleCommand routing › defers voice to bridge → returns false0 ms
  • ✓ClaudeCodeAdapter › handleCommand routing › defers query_usage to bridge → returns false0 ms
  • ✓ClaudeCodeAdapter › switch_mode debounce › debounces rapid switch_mode calls (< 100ms apart)1 ms
  • ✓ClaudeCodeAdapter › lifecycle › isAlive returns false before start0 ms
  • ✓ClaudeCodeAdapter › lifecycle › getTtyPath returns undefined before start0 ms
  • ✓ClaudeCodeAdapter › lifecycle › getProjectName returns null before start0 ms
  • ✓ClaudeCodeAdapter › lifecycle › getHttpServer returns an object0 ms
  • ✓ClaudeCodeAdapter › onRawData callback › can register callback without error0 ms
  • ✓ClaudeCodeAdapter › onDiag handler › can register handler without error0 ms
  • ✓OpenClawAdapter › capabilities › reports OpenClaw capabilities correctly1 ms
  • ✓OpenClawAdapter › handleCommand routing › handles respond → returns true (RPC)1 ms
  • ✓OpenClawAdapter › handleCommand routing › handles select_option → returns true (RPC)0 ms
  • ✓OpenClawAdapter › handleCommand routing › handles navigate_option → returns true (no-op)0 ms
  • ✓OpenClawAdapter › handleCommand routing › handles send_prompt → returns false without session key0 ms
  • ✓OpenClawAdapter › handleCommand routing › handles interrupt → returns true (RPC)0 ms
  • ✓OpenClawAdapter › handleCommand routing › handles escape → returns true (RPC)0 ms
  • ✓OpenClawAdapter › handleCommand routing › defers switch_mode → returns false (not supported)0 ms
  • ✓OpenClawAdapter › handleCommand routing › defers voice to bridge → returns false0 ms
  • ✓OpenClawAdapter › handleCommand routing › defers query_usage to bridge → returns false0 ms
  • ✓OpenClawAdapter › lifecycle › isAlive returns false before start0 ms
  • ✓OpenClawAdapter › lifecycle › getTtyPath returns undefined (no PTY)0 ms
  • ✓OpenClawAdapter › lifecycle › getProjectName returns null before start0 ms
  • ✓OpenClawAdapter › lifecycle › getHttpServer returns an object0 ms
  • ✓OpenClawAdapter › lifecycle › attachTerminal is a no-op0 ms
  • ✓OpenClawAdapter › onRawData callback › can register callback without error0 ms
  • ✓OpenClawAdapter › onDiag handler › can register handler without error0 ms
  • ✓OpenClawAdapter gateway protocol › sends connect request with correct format on connect.challenge5 ms
  • ✓OpenClawAdapter gateway protocol › becomes alive after hello-ok response7 ms
  • ✓OpenClawAdapter gateway protocol › emits spinner_start on chat delta event1 ms
  • ✓OpenClawAdapter gateway protocol › emits idle on chat final event1 ms
  • ✓OpenClawAdapter gateway protocol › emits idle on chat aborted event1 ms
  • ✓OpenClawAdapter gateway protocol › emits idle on chat error event1 ms
  • ✓OpenClawAdapter gateway protocol › emits permission_prompt on exec.approval.requested1 ms
  • ✓OpenClawAdapter gateway protocol › sends exec.approval.resolve on respond after approval request2 ms
  • ✓OpenClawAdapter gateway protocol › sends exec.approval.resolve with deny on respond "n"1 ms
  • ✓OpenClawAdapter gateway protocol › sends chat.send on send_prompt with active session1 ms
  • ✓OpenClawAdapter gateway protocol › sends chat.abort on interrupt with active session and runId1 ms
  • ✓OpenClawAdapter gateway protocol › emits SessionEnd on shutdown event1 ms
  • ✓OpenClawAdapter gateway protocol › clears pendingApprovalId on exec.approval.resolved1 ms
  • ✓CodexCliAdapter › capabilities › reports Codex CLI capabilities correctly1 ms
  • ✓CodexCliAdapter › handleCommand routing › handles respond → returns true0 ms
  • ✓CodexCliAdapter › handleCommand routing › handles interrupt → returns true0 ms
  • ✓CodexCliAdapter › handleCommand routing › handles escape → returns true0 ms
  • ✓CodexCliAdapter › handleCommand routing › does not handle switch_mode → returns false0 ms
  • ✓CodexCliAdapter › handleCommand routing › defers select_option to bridge → returns false0 ms
  • ✓CodexCliAdapter › handleCommand routing › defers navigate_option to bridge → returns false0 ms
  • ✓CodexCliAdapter › handleCommand routing › defers send_prompt to bridge → returns false0 ms
  • ✓CodexCliAdapter › handleCommand routing › defers voice to bridge → returns false0 ms
  • ✓CodexCliAdapter › handleCommand routing › defers query_usage to bridge → returns false0 ms
  • ✓CodexCliAdapter › lifecycle › isAlive returns false before start0 ms
  • ✓CodexCliAdapter › lifecycle › getTtyPath returns undefined before start0 ms
  • ✓CodexCliAdapter › lifecycle › getProjectName returns null before start0 ms
  • ✓CodexCliAdapter › lifecycle › getHttpServer returns an object0 ms
  • ✓CodexCliAdapter › onRawData callback › can register callback without error0 ms
  • ✓CodexCliAdapter › onDiag handler › can register handler without error0 ms
  • ✓CodexCliAdapter start lifecycle › emits SessionStart and connected on start2 ms
  • ✓CodexCliAdapter start lifecycle › feeds PTY data to output parser and emits activity3 ms
  • ✓CodexCliAdapter start lifecycle › exports only terminal UI affordances, not parser lifecycle guesses1 ms
  • ✓MonitorAdapter › capabilities › reports monitor capabilities correctly1 ms
  • ✓MonitorAdapter › handleCommand routing › rejects respond (no PTY)1 ms
  • ✓MonitorAdapter › handleCommand routing › rejects interrupt (no PTY)0 ms
  • ✓MonitorAdapter › handleCommand routing › rejects escape (no PTY)0 ms
  • ✓MonitorAdapter › handleCommand routing › rejects switch_mode (no PTY)0 ms
  • ✓MonitorAdapter › handleCommand routing › rejects select_option (no PTY)0 ms
  • ✓MonitorAdapter › handleCommand routing › rejects send_prompt (no PTY)0 ms
  • ✓MonitorAdapter › handleCommand routing › defers voice to bridge0 ms
  • ✓MonitorAdapter › handleCommand routing › defers query_usage to bridge0 ms
  • ✓MonitorAdapter › lifecycle › isAlive always returns true0 ms
  • ✓MonitorAdapter › lifecycle › getTtyPath returns undefined (no PTY)0 ms
  • ✓MonitorAdapter › lifecycle › getProjectName returns null0 ms
  • ✓MonitorAdapter › lifecycle › getHttpServer returns an object0 ms
  • ✓MonitorAdapter › lifecycle › writeInput is a no-op (no PTY)0 ms
  • ✓MonitorAdapter › lifecycle › attachTerminal is a no-op0 ms
  • ✓MonitorAdapter › lifecycle › onRawData is a no-op1 ms
  • ✓MonitorAdapter › start and event emission › emits connected event on start0 ms
  • ✓MonitorAdapter › start and event emission › exposes hook server for external wiring0 ms
  • ✓ClaudeCodeAdapter start lifecycle › emits SessionStart and connected on start1 ms
  • ✓ClaudeCodeAdapter start lifecycle › uses claude as default command1 ms
  • ✓ClaudeCodeAdapter start lifecycle › feeds PTY data to output parser and emits activity3 ms
  • ✓ClaudeCodeAdapter start lifecycle › exports terminal UI detail without exporting lifecycle or tool guesses1 ms
  • ✓ClaudeCodeAdapter start lifecycle › emits SessionEnd and disconnected on PTY exit1 ms
  • ✓ClaudeCodeAdapter start lifecycle › registers rawData callback without error1 ms
bridge/src/__tests__/codex-output-parser.test.ts29/29
  • ✓CodexOutputParser › idle detection › emits idle on ❯ prompt with source=prompt12 ms
  • ✓CodexOutputParser › idle detection › emits idle on > prompt2 ms
  • ✓CodexOutputParser › idle detection › debounces rapid idle signals1 ms
  • ✓CodexOutputParser › spinner detection › emits spinner_start on braille spinner chars after first idle1 ms
  • ✓CodexOutputParser › spinner detection › does not emit spinner_start before first idle1 ms
  • ✓CodexOutputParser › spinner detection › emits spinner_stop + idle on timeout with source=timeout1 ms
  • ✓CodexOutputParser › spinner detection › emits spinner_stop when idle prompt appears1 ms
  • ✓CodexOutputParser › spinner detection › detects "Thinking" text as processing1 ms
  • ✓CodexOutputParser › permission prompt detection › emits permission_prompt on Allow/Deny pattern3 ms
  • ✓CodexOutputParser › permission prompt detection › emits permission_prompt on y/n pattern1 ms
  • ✓CodexOutputParser › permission prompt detection › stops spinner when approval detected1 ms
  • ✓CodexOutputParser › permission prompt detection › extracts Allow once / Always allow options1 ms
  • ✓CodexOutputParser › permission prompt detection › detects the current Codex command approval prompt as awaiting permission2 ms
  • ✓CodexOutputParser › tool action detection › detects Running: command pattern4 ms
  • ✓CodexOutputParser › tool action detection › detects file operation patterns1 ms
  • ✓CodexOutputParser › tool action detection › detects Editing file pattern1 ms
  • ✓CodexOutputParser › tool action detection › dedups repeated emits of the same (tool, args) within window1 ms
  • ✓CodexOutputParser › tool action detection › emits a different command immediately1 ms
  • ✓CodexOutputParser › tool action detection › re-emits the same command after the dedup window elapses1 ms
  • ✓CodexOutputParser › tool action detection › re-emits the same command after a turn boundary (idle resets dedup)1 ms
  • ✓CodexOutputParser › model info detection › detects gpt model name1 ms
  • ✓CodexOutputParser › model info detection › detects o-series model1 ms
  • ✓CodexOutputParser › model info detection › does not re-emit same model1 ms
  • ✓CodexOutputParser › project name detection › detects working directory3 ms
  • ✓CodexOutputParser › project name detection › detects project from path0 ms
  • ✓CodexOutputParser › project name detection › only detects project name once0 ms
  • ✓CodexOutputParser › buffer management › truncates buffer at 8192 chars2 ms
  • ✓CodexOutputParser › buffer management › handles incomplete ANSI sequences1 ms
  • ✓CodexOutputParser › getProjectName › returns null when no project detected1 ms
bridge/src/__tests__/compact-session-labels.test.ts10/10
  • ✓compact device labels › activity reordering4 ms
  • ✓compact device labels › single0 ms
  • ✓compact device labels › empty fallback0 ms
  • ✓compact device labels › UTF8 bound0 ms
  • ✓compact device labels › duplicate IDs0 ms
  • ✓compact device labels › truncated name collisions0 ms
  • ✓compact device labels › TTGO bounds names before grouping, retaining distinct ordinals0 ms
  • ✓compact device labels › keeps the generated Swift contract synchronized49 ms
  • ✓compact device labels › numbers before the roster cap, preserving raw identity and controls12 ms
  • ✓compact device labels › adds no label bytes when the baseline frame already exceeds the budget6 ms
bridge/src/__tests__/cursor-sync.test.ts15/15
  • ✓Cursor Synchronization › terminal keyboard cursor tracking › emits cursor_update when full option redraw arrives with ❯ at new position21 ms
  • ✓Cursor Synchronization › terminal keyboard cursor tracking › triggers buffer re-parse on small non-❯ chunk during navigable state3 ms
  • ✓Cursor Synchronization › terminal keyboard cursor tracking › does not emit cursor_update when cursor position unchanged1 ms
  • ✓Cursor Synchronization › genuine idle distinction › treats "❯ \n" as genuine idle (clears navigable state)1 ms
  • ✓Cursor Synchronization › genuine idle distinction › does NOT treat "❯ Beta" as idle (cursor on option label)2 ms
  • ✓Cursor Synchronization › genuine idle distinction › does NOT treat "❯ Allow once" as idle (permission cursor move)3 ms
  • ✓Cursor Synchronization › cursor authority in StateMachine › accepts optimistic update immediately2 ms
  • ✓Cursor Synchronization › cursor authority in StateMachine › suppresses stale PTY value within 200ms of optimistic update1 ms
  • ✓Cursor Synchronization › cursor authority in StateMachine › accepts PTY value after 200ms grace period1 ms
  • ✓Cursor Synchronization › cursor authority in StateMachine › always accepts PTY when no recent optimistic update1 ms
  • ✓Cursor Synchronization › cursor authority in StateMachine › resets authority on state transition out of AWAITING1 ms
  • ✓Cursor Synchronization › cursor authority in StateMachine › default source parameter is pty1 ms
  • ✓Cursor Synchronization › select_option proportional delay › delay scales with step count1 ms
  • ✓Cursor Synchronization › option re-emission with cursorIndex › AWAITING_OPTION update triggers state_changed with options1 ms
  • ✓Cursor Synchronization › option re-emission with cursorIndex › updateCursorIndex emits snapshot with new cursor value1 ms
bridge/src/__tests__/focus-relay-samesocket.test.ts10/10
  • ✓SessionFocusRelay — same-socket reverse control (primary) › focuses via the live push socket and sends session_focus_down5 ms
  • ✓SessionFocusRelay — same-socket reverse control (primary) › routes a command down the push socket as session_command_down1 ms
  • ✓SessionFocusRelay — same-socket reverse control (primary) › ignores commands that are not in the routed set1 ms
  • ✓SessionFocusRelay — same-socket reverse control (primary) › relays only RELAYED_EVENTS from the focused session up0 ms
  • ✓SessionFocusRelay — same-socket reverse control (primary) › sends session_unfocus_down on unfocus and clears focus1 ms
  • ✓SessionFocusRelay — same-socket reverse control (primary) › drops focus when the worker push socket closes1 ms
  • ✓SessionFocusRelay — same-socket reverse control (primary) › a stale socket close does not tear down focus held by a newer socket1 ms
  • ✓SessionFocusRelay — same-socket reverse control (primary) › migrateSender is a no-op for unfocused sessions and same-socket re-registers1 ms
  • ✓SessionFocusRelay — same-socket reverse control (primary) › stamps relayed prompt_options with the session identity1 ms
  • ✓SessionFocusRelay — same-socket reverse control (primary) › does nothing when neither a push socket nor a local session exists1 ms
bridge/src/__tests__/output-parser.test.ts220/220
  • ✓OutputParser › basic option parsing › parses clean numbered options with newlines15 ms
  • ✓OutputParser › basic option parsing › detects (recommended) marker2 ms
  • ✓OutputParser › basic option parsing › detects ✔ selected marker1 ms
  • ✓OutputParser › basic option parsing › handles both recommended and selected on different options1 ms
  • ✓OutputParser › basic option parsing › parses full Claude model selector with labels and middle dot1 ms
  • ✓OutputParser › ANSI-stripped options › parses concatenated text with · delimiter1 ms
  • ✓OutputParser › ANSI-stripped options › extracts version numbers from · delimited labels1 ms
  • ✓OutputParser › chunked input debounce › does NOT emit immediately on first chunk1 ms
  • ✓OutputParser › chunked input debounce › batches multiple chunks into single emission1 ms
  • ✓OutputParser › chunked input debounce › resets debounce timer when new chunk with options arrives1 ms
  • ✓OutputParser › chunked input debounce › simulates real /model flow: split across chunks2 ms
  • ✓OutputParser › model ID date rejection › does NOT parse 20251001 from model ID as option number2 ms
  • ✓OutputParser › model ID date rejection › rejects large numbers that look like dates1 ms
  • ✓OutputParser › version number rejection › does NOT parse version "4.6" followed by digit as option1 ms
  • ✓OutputParser › version number rejection › does NOT parse "6)" from "4.6)" as option 61 ms
  • ✓OutputParser › stale buffer overwrite › newer options overwrite older ones with same index1 ms
  • ✓OutputParser › permission prompts › detects Yes/No/Always pattern2 ms
  • ✓OutputParser › permission prompts › keeps ALL real options when a numbered prompt contains "Yes, allow once" (no collapse to 3)2 ms
  • ✓OutputParser › permission prompts › detects (Y)es/(N)o pattern1 ms
  • ✓OutputParser › permission prompts › emits immediately with no debounce1 ms
  • ✓OutputParser › permission prompts › sets yes_no_always prompt type0 ms
  • ✓OutputParser › permission prompts › sets yes_no prompt type1 ms
  • ✓OutputParser › permission prompts › extracts the multi-line question header above yes/no/always options1 ms
  • ✓OutputParser › permission prompts › extracts the question from a box-framed permission prompt1 ms
  • ✓OutputParser › permission prompts › extracts the inline question from the (Y)es/(N)o line0 ms
  • ✓OutputParser › permission prompts › does not mistake box-drawing or footer chrome for a question1 ms
  • ✓OutputParser › permission prompts › suppresses false idle from user prompt echo after permission (interactive cooldown)1 ms
  • ✓OutputParser › permission prompts › allows real idle after interactive cooldown expires1 ms
  • ✓OutputParser › diff prompts › detects (V)iew/(A)pply/(D)eny pattern1 ms
  • ✓OutputParser › diff prompts › detects lowercase (a)pply/(d)eny/(v)iew pattern1 ms
  • ✓OutputParser › diff prompts › emits immediately with no debounce1 ms
  • ✓OutputParser › diff prompts › suppresses false idle from user prompt echo after diff (interactive cooldown)1 ms
  • ✓OutputParser › various option counts › handles 1 option1 ms
  • ✓OutputParser › various option counts › handles 2 options1 ms
  • ✓OutputParser › various option counts › handles 5+ options1 ms
  • ✓OutputParser › various option counts › marks direct-input options as freeform instead of a normal choice1 ms
  • ✓OutputParser › various option counts › handles bullet-style options1 ms
  • ✓OutputParser › option updates › emits new options when data changes after first emission1 ms
  • ✓OutputParser › spinner cancels option timer › spinner cancels pending option debounce1 ms
  • ✓OutputParser › idle vs option debounce › idle prompt is ignored when option debounce is pending1 ms
  • ✓OutputParser › idle vs option debounce › idle prompt fires normally when no option debounce is pending1 ms
  • ✓OutputParser › interactive prompt during spinner › parses a navigable prompt when spinner and prompt share the first chunk1 ms
  • ✓OutputParser › interactive prompt during spinner › keeps a visible prompt active through spinner redraw floods2 ms
  • ✓OutputParser › interactive prompt during spinner › allows real processing immediately after the prompt is selected1 ms
  • ✓OutputParser › interactive prompt during spinner › does not arm prompt lifecycle from stale numbered prose1 ms
  • ✓OutputParser › interactive prompt during spinner › stops spinner when permission prompt arrives1 ms
  • ✓OutputParser › interactive prompt during spinner › stops spinner when diff prompt arrives1 ms
  • ✓OutputParser › interactive prompt during spinner › stops spinner when option prompt arrives1 ms
  • ✓OutputParser › interactive prompt during spinner › stops spinner when idle prompt arrives in small chunk1 ms
  • ✓OutputParser › interactive prompt during spinner › ignores idle prompt in large chunk during spinner (screen redraw)1 ms
  • ✓OutputParser › cleanOptionLabel (via parseOptions) › deduplicates exact CamelCase matches1 ms
  • ✓OutputParser › cleanOptionLabel (via parseOptions) › preserves labels without · delimiter4 ms
  • ✓OutputParser › cleanOptionLabel (via parseOptions) › removes (recommended) from label text1 ms
  • ✓OutputParser › cleanOptionLabel (via parseOptions) › removes ✔ from label text1 ms
  • ✓OutputParser › cleanOptionLabel (via parseOptions) › extracts version from identity before middle dot1 ms
  • ✓OutputParser › metadata events › emits project_name from startup banner1 ms
  • ✓OutputParser › metadata events › emits project_name only once (caches)0 ms
  • ✓OutputParser › metadata events › parses absolute path for project_name0 ms
  • ✓OutputParser › metadata events › seedProjectName disables the scrape (resolver wins over basename)1 ms
  • ✓OutputParser › metadata events › seeded name survives reset(); scrape stays disabled1 ms
  • ✓OutputParser › metadata events › seeding 'unknown' keeps the scrape live as fallback1 ms
  • ✓OutputParser › metadata events › emits model_info with model and plan0 ms
  • ✓OutputParser › metadata events › emits model_info without plan1 ms
  • ✓OutputParser › metadata events › emits model_info only once for same model (caches)0 ms
  • ✓OutputParser › metadata events › re-emits model_info when model changes1 ms
  • ✓OutputParser › metadata events › parses ANSI-stripped model_info1 ms
  • ✓OutputParser › metadata events › detects API billing plan1 ms
  • ✓OutputParser › metadata events › emits status_line with duration and tokens1 ms
  • ✓OutputParser › metadata events › parses zero-minute status line1 ms
  • ✓OutputParser › metadata events › emits tool_action from ⏺ pattern1 ms
  • ✓OutputParser › metadata events › extracts various tool names1 ms
  • ✓OutputParser › user prompt › emits user_prompt after first idle has been seen1 ms
  • ✓OutputParser › user prompt › does NOT emit user_prompt before first idle0 ms
  • ✓OutputParser › user prompt › filters out mode banner text1 ms
  • ✓OutputParser › user prompt › filters out numbered option lines1 ms
  • ✓OutputParser › user prompt › filters out keyboard hint text1 ms
  • ✓OutputParser › user prompt › filters out "esc to interrupt" text1 ms
  • ✓OutputParser › user prompt › filters out autocomplete suggestions1 ms
  • ✓OutputParser › user prompt › filters out box-drawing decorative lines1 ms
  • ✓OutputParser › usage info › parses usage percentage1 ms
  • ✓OutputParser › usage info › parses usage cost1 ms
  • ✓OutputParser › usage info › parses session percentage with hour limit1 ms
  • ✓OutputParser › usage info › parses reset time with timezone1 ms
  • ✓OutputParser › usage info › parses time remaining0 ms
  • ✓OutputParser › spinner lifecycle › emits spinner_start on spinner char (after idle seen)1 ms
  • ✓OutputParser › spinner lifecycle › does NOT emit spinner_start before first idle1 ms
  • ✓OutputParser › spinner lifecycle › emits spinner_stop after debounce (2000ms)1 ms
  • ✓OutputParser › spinner lifecycle › does NOT emit duplicate spinner_start on repeated chars1 ms
  • ✓OutputParser › spinner lifecycle › resets spinner debounce timer on repeated chars1 ms
  • ✓OutputParser › spinner lifecycle › ignores spinner chars in large text blocks (>80 non-ws)1 ms
  • ✓OutputParser › spinner lifecycle › recognizes all spinner characters2 ms
  • ✓OutputParser › idle detection › emits idle after IDLE_DEBOUNCE_MS (300ms)1 ms
  • ✓OutputParser › idle detection › sets seenFirstIdle on first idle prompt1 ms
  • ✓OutputParser › idle detection › cancels idle timer when spinner starts1 ms
  • ✓OutputParser › idle detection › recognizes > as idle prompt char1 ms
  • ✓OutputParser › mode detection › detects plan mode1 ms
  • ✓OutputParser › mode detection › detects accept edits mode0 ms
  • ✓OutputParser › mode detection › detects default mode after Shift+Tab1 ms
  • ✓OutputParser › mode detection › detects default mode via idle prompt after Shift+Tab1 ms
  • ✓OutputParser › mode detection › emits default mode on Shift+Tab timeout (2s)1 ms
  • ✓OutputParser › mode detection › detects ANSI-stripped plan mode text1 ms
  • ✓OutputParser › mode detection › detects ANSI-stripped accept edits text1 ms
  • ✓OutputParser › mode detection › clears pending mode switch when mode banner is detected1 ms
  • ✓OutputParser › reset › clears cached project name and model name1 ms
  • ✓OutputParser › reset › disables spinner detection (clears seenFirstIdle)1 ms
  • ✓OutputParser › reset › allows re-detection after new idle prompt post-reset1 ms
  • ✓OutputParser › reset › allows re-detection of project name after reset1 ms
  • ✓OutputParser › reset › allows re-detection of model info after reset1 ms
  • ✓OutputParser › buffer management › truncates buffer when exceeding 8192 chars1 ms
  • ✓OutputParser › buffer management › keeps the last 4096 chars after truncation1 ms
  • ✓OutputParser › option navigation › cursor movement during option selection does NOT trigger idle1 ms
  • ✓OutputParser › option navigation › cursor movement does NOT trigger user_prompt1 ms
  • ✓OutputParser › option navigation › normal cursor movement between options emits no idle or user_prompt1 ms
  • ✓OutputParser › option navigation › real idle prompt still works after option navigation1 ms
  • ✓OutputParser › option navigation › effort/hint text during navigation does not trigger events1 ms
  • ✓OutputParser › option navigation › IDLE_PROMPT requires space/tab/NBSP after ❯ (not newline)1 ms
  • ✓OutputParser › option navigation › USER_PROMPT requires space/tab after ❯ (not newline)1 ms
  • ✓OutputParser › permission reclassification › reclassifies numbered Yes/No options as permission_prompt1 ms
  • ✓OutputParser › permission reclassification › does NOT reclassify regular options as permission1 ms
  • ✓OutputParser › permission reclassification › does NOT reclassify cursor-selection UI with "Enter to confirm" as permission1 ms
  • ✓OutputParser › permission reclassification › detects cursor-selection UI even when ANSI stripping removes spaces1 ms
  • ✓OutputParser › permission reclassification › includes navigable and cursorIndex in reclassified permission_prompt3 ms
  • ✓OutputParser › permission reclassification › infers shortcuts for reclassified permission options1 ms
  • ✓OutputParser › permission reclassification › infers shortcut "a" for "don't ask again" labels1 ms
  • ✓OutputParser › permission reclassification › infers shortcut "a" for "allow all sessions" labels1 ms
  • ✓OutputParser › ghost text suggestion › detects SGR 90 (bright black) ghost text on prompt line1 ms
  • ✓OutputParser › ghost text suggestion › detects ghost text via Strategy 1 (Try "..." in clean text)1 ms
  • ✓OutputParser › ghost text suggestion › unwraps Try "..." wrapper with smart quotes1 ms
  • ✓OutputParser › ghost text suggestion › unwraps Try "..." wrapper with straight quotes1 ms
  • ✓OutputParser › ghost text suggestion › detects ghost text on first idle (❯ and suggestion in same chunk)1 ms
  • ✓OutputParser › ghost text suggestion › clears suggestion on spinner start1 ms
  • ✓OutputParser › ghost text suggestion › debounces rapid updates1 ms
  • ✓OutputParser › ghost text suggestion › ignores dim (SGR 2) text without ❯ prompt context1 ms
  • ✓OutputParser › ghost text suggestion › detects dim (SGR 2) ghost text on ❯ prompt line1 ms
  • ✓OutputParser › ghost text suggestion › detects ghost text with cursor-forward spacing (Strategy 1)1 ms
  • ✓OutputParser › ghost text suggestion › filters UI chrome fragments2 ms
  • ✓OutputParser › ghost text suggestion › filters box-drawing lines as UI chrome1 ms
  • ✓OutputParser › ghost text suggestion › filters file paths (false positive from screen redraws)1 ms
  • ✓OutputParser › ghost text suggestion › detects 256-color gray ghost text1 ms
  • ✓OutputParser › ghost text suggestion › handles multi-segment ghost text on prompt line1 ms
  • ✓OutputParser › ghost text suggestion › ignores ghost text on non-prompt lines0 ms
  • ✓OutputParser › ghost text suggestion › detects 24-bit RGB gray ghost text1 ms
  • ✓OutputParser › ghost text suggestion › ignores 24-bit RGB non-gray colors (e.g. blue prompt char)1 ms
  • ✓OutputParser › ghost text suggestion › filters out short gray text like prompt char itself1 ms
  • ✓OutputParser › ghost text suggestion › suppresses ghost text detection during spinner (processing)1 ms
  • ✓OutputParser › ghost text suggestion › rejects digit+operator fragments like diff markers "96 +"1 ms
  • ✓OutputParser › ghost text UI chrome filtering › filters out "Tip:" segments on prompt line1 ms
  • ✓OutputParser › ghost text UI chrome filtering › filters out "(ctrl+...)" shortcut hints on prompt line1 ms
  • ✓OutputParser › ghost text UI chrome filtering › filters concatenated UI chrome even in scheduleSuggestion1 ms
  • ✓OutputParser › ghost text UI chrome filtering › keeps ghost text when UI chrome is also present on the line1 ms
  • ✓OutputParser › filters out extended thinking indicators › rejects "(thought for 1s)" as ghost text via isUiChrome1 ms
  • ✓OutputParser › filters out extended thinking indicators › rejects "(thought for 1s)>" via scheduleSuggestion1 ms
  • ✓OutputParser › filters out extended thinking indicators › rejects longer thinking durations like "(thought for 15s)"1 ms
  • ✓OutputParser › filters out extended thinking indicators › rejects "(thought for 1m 30s)" multipart duration0 ms
  • ✓OutputParser › filters out extended thinking indicators › rejects "✻ Cooked for 1m 26s" sparkle indicator1 ms
  • ✓OutputParser › filters out extended thinking indicators › rejects "✻ Cooked for 5s" short duration variant1 ms
  • ✓OutputParser › filters out extended thinking indicators › rejects "Cooked for 10s" without sparkle (cross-chunk)1 ms
  • ✓OutputParser › parenthesized placeholder filtering › rejects "(no content)" via SGR 2 (dim)1 ms
  • ✓OutputParser › parenthesized placeholder filtering › rejects "(no content)" via SGR 90 (bright black)0 ms
  • ✓OutputParser › parenthesized placeholder filtering › rejects "(loading...)"0 ms
  • ✓OutputParser › parenthesized placeholder filtering › rejects "(empty)"0 ms
  • ✓OutputParser › parenthesized placeholder filtering › rejects "(waiting for response)"1 ms
  • ✓OutputParser › parenthesized placeholder filtering › allows "fix the broken (auth) module" — parens in middle1 ms
  • ✓OutputParser › parenthesized placeholder filtering › allows "(optional) refactor the code" — paren prefix with text after1 ms
  • ✓OutputParser › stacked ANSI + cross-chunk ghost text › detects ghost text with stacked ANSI escapes (gray + italic)0 ms
  • ✓OutputParser › stacked ANSI + cross-chunk ghost text › detects ghost text with combined SGR params (2;90 = dim+bright-black)1 ms
  • ✓OutputParser › stacked ANSI + cross-chunk ghost text › detects cross-chunk ghost text (❯ and gray text in separate feeds)1 ms
  • ✓OutputParser › stacked ANSI + cross-chunk ghost text › does NOT cross-chunk detect when chunk contains ⎿ output fence1 ms
  • ✓OutputParser › stacked ANSI + cross-chunk ghost text › does NOT cross-chunk detect when new chunk has \n (different line)1 ms
  • ✓OutputParser › ghost option from stale buffer › excludes stale numbered list items before actual option prompt1 ms
  • ✓OutputParser › ghost option from stale buffer › bypasses chunk size guard when ❯ cursor is present in large chunk1 ms
  • ✓OutputParser › ghost option from stale buffer › a stale markdown "1." line from chat must not overwrite the ❯-marked option1 ms
  • ✓OutputParser › ghost option from stale buffer › still works with scrambled TUI order after backward scan1 ms
  • ✓OutputParser › ghost option from stale buffer › excludes file path fragments from Read() tool in permission prompt1 ms
  • ✓OutputParser › option index ordering › returns options sorted by index even when TUI lines arrive out of order1 ms
  • ✓OutputParser › split ANSI sequence buffering › handles ANSI SGR codes split across chunks1 ms
  • ✓OutputParser › split ANSI sequence buffering › handles bare ESC at end of chunk1 ms
  • ✓OutputParser › split ANSI sequence buffering › does not buffer complete ANSI sequences1 ms
  • ✓OutputParser › large chunk guard for option detection › does NOT detect numbered items in large response chunks as options1 ms
  • ✓OutputParser › large chunk guard for option detection › still detects real options in small TUI chunks1 ms
  • ✓OutputParser › CUP-positioned options › parses options using CUP (\x1b[row;colH) instead of newlines1 ms
  • ✓OutputParser › CUP-positioned options › parses options using CUD (\x1b[B) for vertical movement1 ms
  • ✓OutputParser › trailing TUI chrome stripping › strips "Esc to cancel" from last option label1 ms
  • ✓OutputParser › trailing TUI chrome stripping › strips "Enter to confirm" from last option label1 ms
  • ✓OutputParser › trailing TUI chrome stripping › does NOT strip "Esc" when it appears within legitimate label text1 ms
  • ✓OutputParser › CJK suggestion detection › accepts ghost text containing CJK characters2 ms
  • ✓OutputParser › CJK suggestion detection › accepts ghost text that is purely CJK (no ASCII words)1 ms
  • ✓OutputParser › AskUserQuestion with separators and descriptions › handles AskUserQuestion with separator and descriptions1 ms
  • ✓OutputParser › AskUserQuestion with separators and descriptions › handles options starting from non-zero index (buffer truncation)1 ms
  • ✓OutputParser › AskUserQuestion tall prompt with oversized separator › keeps leading options a/b when a giant box-drawing rule contaminates a later option1 ms
  • ✓OutputParser › hierarchical CC prompt shapes › captures 5-option plan approval with long labels1 ms
  • ✓OutputParser › hierarchical CC prompt shapes › captures OpenClaw scope selection (numbered list)1 ms
  • ✓OutputParser › hierarchical CC prompt shapes › captures OpenClaw token-action step (short numbered list)3 ms
  • ✓OutputParser › permission scroll does not trigger idle (Bug 1) › scroll chunk with ❯ option text does NOT cause idle when navigable1 ms
  • ✓OutputParser › permission scroll does not trigger idle (Bug 1) › /model combined chunk: confirmation + idle prompt clears options1 ms
  • ✓OutputParser › permission scroll does not trigger idle (Bug 1) › /model separate chunks: ANSI reposition timer does not block idle1 ms
  • ✓OutputParser › permission scroll does not trigger idle (Bug 1) › cursor move chunk with option text does NOT falsely trigger idle1 ms
  • ✓OutputParser › permission scroll does not trigger idle (Bug 1) › genuine idle prompt exits navigable state and emits idle1 ms
  • ✓OutputParser › TUI cursor-overwrite label correction (Bug 2) › fixes contaminated permission option label using correction line1 ms
  • ✓OutputParser › TUI cursor-overwrite label correction (Bug 2) › leaves labels unchanged when no correction line is present1 ms
  • ✓OutputParser › genuine idle detection (semantic) › "❯ \n" is genuine idle — only prompt character, no label text1 ms
  • ✓OutputParser › genuine idle detection (semantic) › "❯ Beta" is NOT idle — has label text after prompt char1 ms
  • ✓OutputParser › genuine idle detection (semantic) › "❯ A" is NOT idle — single-char label still counts1 ms
  • ✓OutputParser › genuine idle detection (semantic) › ">" also treated as idle prompt character1 ms
  • ✓OutputParser › effort level parsing › detects "High effort" selection pattern0 ms
  • ✓OutputParser › effort level parsing › detects "Medium effort" selection pattern0 ms
  • ✓OutputParser › effort level parsing › detects "Low effort" selection pattern0 ms
  • ✓OutputParser › effort level parsing › detects "with high effort" confirmation line0 ms
  • ✓OutputParser › effort level parsing › detects effort in model info line0 ms
  • ✓OutputParser › effort level parsing › caches effort level — no re-emit on same value1 ms
  • ✓OutputParser › effort level parsing › emits on effort level change1 ms
  • ✓OutputParser › effort level parsing › resets effort level on reset()1 ms
  • ✓OutputParser › effort level parsing › does not match "effort" in unrelated context1 ms
  • ✓OutputParser › effort level parsing › does not match effort inside numbered option lines1 ms
  • ✓OutputParser › effort level parsing › getter returns current effort level1 ms
  • ✓OutputParser › effort level parsing › detects "Max effort" (Opus 4.7 variant)1 ms
  • ✓OutputParser › effort level parsing › detects "xhigh effort" (Opus 4.7 variant)0 ms
  • ✓OutputParser › effort level parsing › detects "default effort" (per-model default variant)1 ms
  • ✓OutputParser › effort level parsing › detects "fast effort" (Opus 4.6 variant)0 ms
  • ✓OutputParser › effort level parsing › detects effort in /model confirmation line with max0 ms
bridge/src/__tests__/pty-manager-launch-contract.test.ts5/5
  • ✓PtyManager.spawn launch contract › runs the command through a LOGIN shell on POSIX7 ms
  • ✓PtyManager.spawn launch contract › falls back to /bin/bash when SHELL is unset2 ms
  • ✓PtyManager.spawn launch contract › uses cmd.exe switches on win32, not POSIX ones3 ms
  • ✓PtyManager.spawn launch contract › falls back to cmd.exe when COMSPEC is unset1 ms
  • ✓PtyManager.spawn launch contract › hands the command to the shell verbatim — no escaping, no re-quoting1 ms
bridge/src/__tests__/pty-manager-spawn-helper.test.ts5/5
  • ✓ensureNodePtySpawnHelperExecutable › repairs the 0644 mode shipped by node-pty 1.1.0 on macOS5 ms
  • ✓ensureNodePtySpawnHelperExecutable › leaves an already executable helper unchanged1 ms
  • ✓ensureNodePtySpawnHelperExecutable › repairs a source-built helper when no prebuild is present1 ms
  • ✓ensureNodePtySpawnHelperExecutable › is a no-op outside macOS1 ms
  • ✓ensureNodePtySpawnHelperExecutable › does not chmod a symlink in place of the package helper1 ms
bridge/src/__tests__/pty-manager-stdin.test.ts2/2
  • ✓PtyManager.attachTerminal › preserves multi-byte characters split across stdin chunk boundaries4 ms
  • ✓PtyManager.attachTerminal › forwards single-byte input unchanged1 ms
bridge/src/__tests__/remote-reverse-e2e.test.ts5/5
  • ✓same-socket reverse control (worker ↔ daemon, real sockets) › registers over a single outbound socket with explicit remoteAttach intent4 ms
  • ✓same-socket reverse control (worker ↔ daemon, real sockets) › ignores down-frames addressed to a foreign session303 ms
  • ✓same-socket reverse control (worker ↔ daemon, real sockets) › emits the focus snapshot up when the daemon focuses (session_focus_down)53 ms
  • ✓same-socket reverse control (worker ↔ daemon, real sockets) › applies a command sent down the same socket (session_command_down)51 ms
  • ✓same-socket reverse control (worker ↔ daemon, real sockets) › forwards live events up only while focused352 ms
bridge/src/__tests__/session-push-channel.test.ts11/11
  • ✓session push channel (real handler, fake sockets) › registers an ssh -L (loopback) worker as remote via the explicit remoteAttach flag9 ms
  • ✓session push channel (real handler, fake sockets) › carries the session weight into the remote sessions list1 ms
  • ✓session push channel (real handler, fake sockets) › does NOT register a plain local worker (loopback, no flag) — but still acks1 ms
  • ✓session push channel (real handler, fake sockets) › still registers a flag-less worker from a non-local IP (pre-flag back-compat)1 ms
  • ✓session push channel (real handler, fake sockets) › accepts the exact npm 1.1 managed-worker register/state shape3 ms
  • ✓session push channel (real handler, fake sockets) › worker reconnect: new socket takes over, old close cannot kill the new registration or focus1 ms
  • ✓session push channel (real handler, fake sockets) › session_event_up: only the registered sender may speak for a session1 ms
  • ✓session push channel (real handler, fake sockets) › session_push_state overlap: a pre-reconnect socket can change neither the remote registry nor the effective push-state cache1 ms
  • ✓session push channel (real handler, fake sockets) › session_push_state from a never-registered stranger socket is fully rejected1 ms
  • ✓session push channel (real handler, fake sockets) › session_push_state with no remote registration keeps the plain unguarded local path1 ms
  • ✓session push channel (real handler, fake sockets) › leaves non-push frames unconsumed0 ms

26 files

Pass

State machine & hub protocol

Do agent states transition correctly and reach every client in the documented wire shape?

Passed
523
Failed
0
Skipped
0
Time
2.8 s
26 test files
bridge/src/__tests__/awaiting-overlay.test.ts40/40
  • ✓awaiting-overlay › set then get returns the trimmed question5 ms
  • ✓awaiting-overlay › carries an optional requestId (actionable PreToolUse gate)0 ms
  • ✓awaiting-overlay › preserves a structured AskUserQuestion as a display-only option lifecycle2 ms
  • ✓awaiting-overlay › rejects malformed AskUserQuestion payloads instead of surfacing a dead prompt1 ms
  • ✓awaiting-overlay › does not let a generic permission Notification overwrite AskUserQuestion1 ms
  • ✓awaiting-overlay › returns undefined for an unknown session0 ms
  • ✓awaiting-overlay › survives a long away-wait, then expires past the generous backstop1 ms
  • ✓awaiting-overlay › re-setting refreshes the TTL (a follow-up prompt keeps the overlay alive)0 ms
  • ✓awaiting-overlay › clear removes the entry and reports whether one existed0 ms
  • ✓awaiting-overlay › caps question length at 120 chars0 ms
  • ✓awaiting-overlay › looksLikePermissionMessage › matches genuine permission prompts1 ms
  • ✓awaiting-overlay › looksLikePermissionMessage › rejects the idle ping, non-permission status text, and empty messages0 ms
  • ✓awaiting-overlay › isPermissionNotification › uses notification_type when present — only permission_prompt awaits1 ms
  • ✓awaiting-overlay › isPermissionNotification › falls back to the message regex only when notification_type is absent0 ms
  • ✓awaiting-overlay › shouldGatePreToolUse › gates in default / unknown modes (Claude may prompt)0 ms
  • ✓awaiting-overlay › shouldGatePreToolUse › never gates when Claude will not prompt or execute1 ms
  • ✓awaiting-overlay › shouldGatePreToolUse › acceptEdits auto-approves edits but still gates Bash0 ms
  • ✓awaiting-overlay › applyAwaitingOverlayToObserved › flips a matching observed session to awaiting_permission with the question1 ms
  • ✓awaiting-overlay › applyAwaitingOverlayToObserved › matches the uuid after stripping the observed:claude: / observed:codex: prefix0 ms
  • ✓awaiting-overlay › applyAwaitingOverlayToObserved › leaves sessions untouched when no overlay exists0 ms
  • ✓awaiting-overlay › applyAwaitingOverlayToObserved › propagates the requestId for actionable gates0 ms
  • ✓awaiting-overlay › applyAwaitingOverlayToObserved › renders AskUserQuestion as awaiting_option with real choices and no actionable requestId1 ms
  • ✓awaiting-overlay › transcript-recency supersession (ESC-dismissed prompt) › drops a display-only overlay once the transcript moves past it, and purges the entry1 ms
  • ✓awaiting-overlay › transcript-recency supersession (ESC-dismissed prompt) › keeps the overlay while the transcript stays frozen (genuine wait)0 ms
  • ✓awaiting-overlay › transcript-recency supersession (ESC-dismissed prompt) › holds within the jitter margin (a write at ~setAt is not a resolution)0 ms
  • ✓awaiting-overlay › transcript-recency supersession (ESC-dismissed prompt) › keeps the overlay when the session carries no transcript recency0 ms
  • ✓awaiting-overlay › transcript-recency supersession (ESC-dismissed prompt) › never drops a held device-approval gate on recency (its tool is blocked, resolve via onResolved)0 ms
  • ✓awaiting-overlay › transcript-recency supersession (ESC-dismissed prompt) › never drops AskUserQuestion on transcript mtime movement during a genuine wait0 ms
  • ✓awaiting-overlay › AskUserQuestion tool lifecycle › ignores unrelated or stale tool completions1 ms
  • ✓awaiting-overlay › AskUserQuestion tool lifecycle › clears only the matching PostToolUse/PostToolUseFailure id0 ms
  • ✓awaiting-overlay › multi-group AskUserQuestion progression › presents the first group and keeps the rest for later1 ms
  • ✓awaiting-overlay › multi-group AskUserQuestion progression › advances one group per answer and projects its question/options1 ms
  • ✓awaiting-overlay › multi-group AskUserQuestion progression › reports completion on the last group and keeps every Q→A pair1 ms
  • ✓awaiting-overlay › multi-group AskUserQuestion progression › surfaces group position to devices only when there is more than one1 ms
  • ✓awaiting-overlay › multi-group AskUserQuestion progression › skips malformed groups instead of presenting a dead prompt1 ms
  • ✓awaiting-overlay › multi-group AskUserQuestion progression › ignores advancement when no option prompt is pending2 ms
  • ✓awaiting-overlay › multi-group AskUserQuestion progression › clears every group on the matching PostToolUse0 ms
  • ✓awaiting-overlay › display-only notification flow (daemon restore contract) › permission_prompt notification → awaiting with NO requestId; next hook clears1 ms
  • ✓awaiting-overlay › display-only notification flow (daemon restore contract) › idle ping / auto-approved tool never sets the overlay (the 2026-06-27 false-attention bug)0 ms
  • ✓sticky overlays (Codex PermissionRequest) › a sticky display-only overlay survives transcript activity newer than itself0 ms
bridge/src/__tests__/bridge-core-sessions.test.ts4/4
  • ✓BridgeCore sessions_list › broadcastSessionsList enriches sessions before broadcast25 ms
  • ✓BridgeCore sessions_list › delivers the final hook state within a broadcast window and shares it with feed snapshots9 ms
  • ✓BridgeCore sessions_list › folds same-project Codex App chats in the canonical snapshot2 ms
  • ✓BridgeCore sessions_list › sendInitialState sends enriched sessions_list to the connecting client55 ms
bridge/src/__tests__/bridge-core.test.ts31/31
  • ✓BridgeCore Orchestration › retires old disk quota after a failed first poll and never restamps cache hits20 ms
  • ✓BridgeCore Orchestration › expires z.ai on a focused relay without rebuilding usage or extending capture time5 ms
  • ✓BridgeCore Orchestration › buildStateEvent › builds state_update with IDLE state2 ms
  • ✓BridgeCore Orchestration › buildStateEvent › includes cached ollamaStatus and gatewayAvailable2 ms
  • ✓BridgeCore Orchestration › buildStateEvent › computes promptType for permission options1 ms
  • ✓BridgeCore Orchestration › buildStateEvent › computes promptType yes_no_always for 3+ permission options1 ms
  • ✓BridgeCore Orchestration › usage management › updateApiUsage caches and broadcasts18 ms
  • ✓BridgeCore Orchestration › usage management › buildUsage includes API usage data2 ms
  • ✓BridgeCore Orchestration › usage management › buildUsage works without API data1 ms
  • ✓BridgeCore Orchestration › usage management › inferredBillingType propagates to StateMachine1 ms
  • ✓BridgeCore Orchestration › usage management › stale fetch results › a not-fresh result keeps the numbers but marks them stale1 ms
  • ✓BridgeCore Orchestration › usage management › stale fetch results › a not-fresh result does NOT advance lastApiFetchTime1 ms
  • ✓BridgeCore Orchestration › usage management › stale fetch results › a fresh result clears staleness again1 ms
  • ✓BridgeCore Orchestration › usage management › stale fetch results › a null result (no numbers at all) marks an existing cache stale1 ms
  • ✓BridgeCore Orchestration › usage management › stale fetch results › retires quota and the subscription in both frame types before a timer runs, then recovers2 ms
  • ✓BridgeCore Orchestration › usage management › stale fetch results › keeps API cost usage live without a subscription cache1 ms
  • ✓BridgeCore Orchestration › usage management › stale fetch results › reports freshness back to the caller1 ms
  • ✓BridgeCore Orchestration › sendInitialState › sends state_update, usage_update, connection, display_state on connect9 ms
  • ✓BridgeCore Orchestration › sendInitialState › includes timeline_history when entries exist5 ms
  • ✓BridgeCore Orchestration › sendInitialState › gives a dashboard the latest readable rows, as the Swift daemon does117 ms
  • ✓BridgeCore Orchestration › sendInitialState › keeps an untagged board on the board frame limit from its first connect17 ms
  • ✓BridgeCore Orchestration › sendInitialState › filters unreadable rows before the byte cap, not after5 ms
  • ✓BridgeCore Orchestration › state change broadcast › state_changed emits to WS clients when wired by caller3 ms
  • ✓BridgeCore Orchestration › wireTimeline › timeline entries broadcast as timeline_event53 ms
  • ✓BridgeCore Orchestration › hasClients guard › wsServer.getClientCount reflects connected clients253 ms
  • ✓BridgeCore Orchestration › hasClients guard › external client count provider extends hasClients1 ms
  • ✓BridgeCore Orchestration › voice assistant state › updateVoiceAssistantState caches and triggers state broadcast53 ms
  • ✓BridgeCore Orchestration › voice assistant state › disabled voice assistant state is not included in event1 ms
  • ✓BridgeCore Orchestration › session registry › registerSession writes to sessions.json2 ms
  • ✓BridgeCore Orchestration › session registry › deregisterSession removes from sessions.json2 ms
  • ✓BridgeCore Orchestration › broadcast coordination › broadcast sends to WS and SSE callback55 ms
bridge/src/__tests__/default-settings-drift.test.ts5/5
  • ✓config/default-settings.json › parses as an object4 ms
  • ✓config/default-settings.json › carries only keys a loader actually reads1 ms
  • ✓config/default-settings.json › states the MLX defaults that shared/src/llm-settings.ts applies0 ms
  • ✓config/default-settings.json › states the APME defaults that loadApmeConfig() applies1 ms
  • ✓config/default-settings.json › does not resurrect the removed whisper / autoTune keys1 ms
bridge/src/__tests__/docs-wire-contract.test.ts11/11
  • ✓documented LAN boundary matches the gate › daemon.md quotes exactly the fields buildPublicHealth emits4 ms
  • ✓documented LAN boundary matches the gate › the documented payload carries no credential, inventory or state2 ms
  • ✓documented LAN boundary matches the gate › GET /health is the only route an unauthorized peer reaches1 ms
  • ✓auth and wire specs stay visible in the published rail › docs/wire-compatibility.md is not in the collapsed group1 ms
  • ✓auth and wire specs stay visible in the published rail › docs/protocol.md is not in the collapsed group1 ms
  • ✓auth and wire specs stay visible in the published rail › docs/gateway-protocol.md is not in the collapsed group1 ms
  • ✓auth and wire specs stay visible in the published rail › docs/daemon.md is not in the collapsed group1 ms
  • ✓auth and wire specs stay visible in the published rail › groups them with the other surface specs1 ms
  • ✓wire compatibility contract stays bound to its subjects › is cataloged in the design system1 ms
  • ✓wire compatibility contract stays bound to its subjects › names helpers that still exist, so the id rules stay actionable1 ms
  • ✓wire compatibility contract stays bound to its subjects › quotes the retain-on-absent merge shape the rules depend on0 ms
bridge/src/__tests__/server-integration.test.ts19/19
  • ✓Server Integration › SessionStart hook → IDLE state broadcast100 ms
  • ✓Server Integration › PreToolUse responds 200 with an EMPTY body (managed sessions must not gate)19 ms
  • ✓Server Integration › non-PreToolUse hooks still ack with { received: true }9 ms
  • ✓Server Integration › full session lifecycle: SessionStart → UserPromptSubmit → Stop13 ms
  • ✓Server Integration › PreToolUse → PostToolUse cycle broadcasts tool info12 ms
  • ✓Server Integration › SessionEnd → DISCONNECTED9 ms
  • ✓Server Integration › GET /health returns valid JSON6 ms
  • ✓Server Integration › GET /usage returns data when getter is set5 ms
  • ✓Server Integration › GET /usage returns null when no getter5 ms
  • ✓Server Integration › GET /devices returns empty when no getter5 ms
  • ✓Server Integration › POST to unknown route returns 4045 ms
  • ✓Server Integration › hook endpoint responds immediately7 ms
  • ✓Server Integration › SSE client receives state_update events66 ms
  • ✓Server Integration › broadcasts to multiple WS clients10 ms
  • ✓Server Integration › WS command from client triggers callback105 ms
  • ✓Server Integration › rapid hook events do not corrupt state238 ms
  • ✓Server Integration › token counts accumulate through hook events11 ms
  • ✓Server Integration › client_register {clientType:"tui"} appears in getTuiClients until the WS closes412 ms
  • ✓Server Integration › malformed tui client_register falls back to a "terminal" identity106 ms
bridge/src/__tests__/session-aggregator.test.ts8/8
  • ✓session-aggregator › uses ownState for the current session without fetching /health5 ms
  • ✓session-aggregator › fetches sibling /health and merges state and modelName3 ms
  • ✓session-aggregator › marks the session dead when sibling /health fails with no cache1 ms
  • ✓session-aggregator › returns cached state when sibling /health fails after a previous success1 ms
  • ✓session-aggregator › clearSiblingStateCache removes cached entry0 ms
  • ✓session-aggregator › sweeps cached state for sessions that drop out of the active set1 ms
  • ✓session-aggregator › buildEnrichedSessionsList includes own session and excludes daemon9 ms
  • ✓session-aggregator › buildEnrichedSessionsList returns own session in single-session mode without /health calls1 ms
bridge/src/__tests__/session-deck-awaiting.test.ts4/4
  • ✓buildSessionDeck detail-view awaiting › renders REAL options (navigable → select_option), not hardcoded Yes/No/Always12 ms
  • ✓buildSessionDeck detail-view awaiting › non-navigable inline prompt → respond with the option shortcut1 ms
  • ✓buildSessionDeck detail-view awaiting › observed session (requestId, no options): no fabricated Allow/Deny — only PTY sessions expose real choices4 ms
  • ✓buildSessionDeck detail-view awaiting › awaiting but not remotely answerable (no options, no requestId): no fake action commands1 ms
bridge/src/__tests__/session-order-store.test.ts23/23
  • ✓SessionOrderStore identity › keys pins on the bare id — both observed id forms address the same pin5 ms
  • ✓SessionOrderStore identity › weight 0 clears instead of pinning (0 is the default band)3 ms
  • ✓SessionOrderStore identity › clamps out-of-range weights to the documented cross-platform range2 ms
  • ✓SessionOrderStore persistence and lifecycle › persists atomically and a fresh store re-loads it2 ms
  • ✓SessionOrderStore persistence and lifecycle › a corrupt or missing file loads as an empty store, never a throw1 ms
  • ✓SessionOrderStore persistence and lifecycle › drops pins whose session has been unseen past the TTL, keeps seen ones2 ms
  • ✓SessionOrderStore persistence and lifecycle › enforces the pin cap, evicting the least-recently-seen first138 ms
  • ✓SessionOrderStore persistence and lifecycle › noteSeen is throttled per pin — a roster every few seconds does not churn the file1 ms
  • ✓SessionOrderStore.applyTo precedence › pins an observed row that has no weight of its own1 ms
  • ✓SessionOrderStore.applyTo precedence › never overrides a weight the row already carries (managed/remote contract)1 ms
  • ✓SessionOrderStore.applyTo precedence › leaves managed and remote rows untouched even when their id is pinned1 ms
  • ✓SessionOrderStore.applyTo precedence › returns the identical object for unpinned rows0 ms
  • ✓resolveSessionOrderTarget › an exact roster id resolves to itself1 ms
  • ✓resolveSessionOrderTarget › a unique prefix (e.g. a device-truncated 31-char echo) resolves0 ms
  • ✓resolveSessionOrderTarget › an ambiguous prefix is refused with the candidates0 ms
  • ✓resolveSessionOrderTarget › an id that is not live passes through in bare form (pin waits for the id)0 ms
  • ✓resolveSessionOrderTarget › cross-form: an observed-form reference resolves against a BARE-id roster (Swift shape)0 ms
  • ✓resolveSessionOrderTarget › cross-form: a bare reference resolves against an observed-form roster, ambiguity still named0 ms
  • ✓parseSessionOrderWeight › accepts in-range integers (numbers and numeric strings from curl users)0 ms
  • ✓parseSessionOrderWeight › rejects anything the wire must never see0 ms
  • ✓pinned observed sessions through the shared deck pipeline › a pin reorders observed rows via the exact fold+sort every surface runs1 ms
  • ✓pinned observed sessions through the shared deck pipeline › distinct pins keep same-project Codex tabs from folding together2 ms
  • ✓pinned observed sessions through the shared deck pipeline › unpinned same-project Codex rows still fold exactly as before1 ms
bridge/src/__tests__/state-machine.test.ts80/80
  • ✓StateMachine › basic transitions › starts in DISCONNECTED6 ms
  • ✓StateMachine › basic transitions › SessionStart → IDLE1 ms
  • ✓StateMachine › basic transitions › UserPromptSubmit → PROCESSING1 ms
  • ✓StateMachine › basic transitions › Stop → IDLE from PROCESSING1 ms
  • ✓StateMachine › basic transitions › SessionEnd → DISCONNECTED from any state1 ms
  • ✓StateMachine › permission flow › permission_prompt → AWAITING_PERMISSION1 ms
  • ✓StateMachine › permission flow › user respond → PROCESSING from AWAITING_PERMISSION1 ms
  • ✓StateMachine › terminal UI authority boundary › accepts a real interaction affordance1 ms
  • ✓StateMachine › terminal UI authority boundary › ignores terminal lifecycle guesses1 ms
  • ✓StateMachine › option flow › option_prompt → AWAITING_OPTION1 ms
  • ✓StateMachine › option flow › select_option → PROCESSING from AWAITING_OPTION1 ms
  • ✓StateMachine › option flow › option_prompt carries the question into the snapshot1 ms
  • ✓StateMachine › diff flow › diff_prompt → AWAITING_DIFF1 ms
  • ✓StateMachine › diff flow › respond → PROCESSING from AWAITING_DIFF1 ms
  • ✓StateMachine › interrupt › interrupt → IDLE from PROCESSING1 ms
  • ✓StateMachine › interrupt › interrupt → IDLE from AWAITING_PERMISSION1 ms
  • ✓StateMachine › strict transitions › allows IDLE → AWAITING_PERMISSION (prompt without spinner)1 ms
  • ✓StateMachine › strict transitions › blocks invalid transition: DISCONNECTED → PROCESSING0 ms
  • ✓StateMachine › strict transitions › allows wildcard session_end from any state1 ms
  • ✓StateMachine › stuck timeout › PROCESSING for >5min → auto-recovery to IDLE2 ms
  • ✓StateMachine › stuck timeout › AWAITING_PERMISSION persists indefinitely — a genuine wait is never auto-recovered1 ms
  • ✓StateMachine › stuck timeout › AWAITING_OPTION persists indefinitely (no wall-clock recovery)1 ms
  • ✓StateMachine › stuck timeout › AWAITING_DIFF persists indefinitely (no wall-clock recovery)1 ms
  • ✓StateMachine › stuck timeout › AWAITING leaves only on a real signal — parser spinner (OpenCode/OpenClaw adapters) → PROCESSING1 ms
  • ✓StateMachine › stuck timeout › timer resets on state change before timeout1 ms
  • ✓StateMachine › stuck timeout › no timeout in IDLE state0 ms
  • ✓StateMachine › hook exits from awaiting states › Stop from AWAITING_PERMISSION → IDLE with prompt fields cleared1 ms
  • ✓StateMachine › hook exits from awaiting states › Stop from AWAITING_OPTION → IDLE (keyboard-answered AskUserQuestion ends the turn)1 ms
  • ✓StateMachine › hook exits from awaiting states › Stop from AWAITING_DIFF → IDLE1 ms
  • ✓StateMachine › hook exits from awaiting states › UserPromptSubmit from AWAITING_PERMISSION → PROCESSING (new turn dismisses the prompt)1 ms
  • ✓StateMachine › hook exits from awaiting states › PostToolUse after the grace period dismisses AWAITING_PERMISSION → PROCESSING1 ms
  • ✓StateMachine › hook exits from awaiting states › PreToolUse after the grace period dismisses AWAITING_OPTION → PROCESSING1 ms
  • ✓StateMachine › hook exits from awaiting states › tool activity within the grace period keeps a fresh prompt awaiting5 ms
  • ✓StateMachine › hook exits from awaiting states › PreToolUse from IDLE recovers a dropped user_prompt_submit → PROCESSING1 ms
  • ✓StateMachine › hook exits from awaiting states › codex_turn_complete from AWAITING_OPTION → IDLE (notify closes a keyboard-answered prompt)1 ms
  • ✓StateMachine › hook exits from awaiting states › codex_tool_end after the grace period dismisses AWAITING_PERMISSION → PROCESSING0 ms
  • ✓StateMachine › hook exits from awaiting states › a parallel sibling finishing does not dismiss the prompt while the gated tool is in flight1 ms
  • ✓StateMachine › hook exits from awaiting states › a straggler PostToolUse landing after Stop does not reopen a finished session1 ms
  • ✓StateMachine › hook exits from awaiting states › a re-detected prompt refreshes the dismissal grace1 ms
  • ✓StateMachine › hook exits from awaiting states › the daemon hub machine never dismisses a held prompt on tool activity1 ms
  • ✓StateMachine › hook exits from awaiting states › the daemon hub machine reopens from IDLE and DISCONNECTED on a tool start1 ms
  • ✓StateMachine › hook exits from awaiting states › a straggler tool END does not reopen a finished hub session1 ms
  • ✓StateMachine › hook exits from awaiting states › tool activity re-arms the PROCESSING backstop (toolActivityRecovery=true)2 ms
  • ✓StateMachine › hook exits from awaiting states › tool activity re-arms the PROCESSING backstop (toolActivityRecovery=false)1 ms
  • ✓StateMachine › parser events › spinner_start → PROCESSING from IDLE0 ms
  • ✓StateMachine › parser events › spinner_stop → IDLE from PROCESSING1 ms
  • ✓StateMachine › parser events › idle → IDLE from PROCESSING1 ms
  • ✓StateMachine › parser events › mode_change updates permission mode2 ms
  • ✓StateMachine › snapshot › emits state_changed on transitions1 ms
  • ✓StateMachine › snapshot › includes tool info in snapshot0 ms
  • ✓StateMachine › snapshot › clears tool info on PostToolUse1 ms
  • ✓StateMachine › snapshot › includes project name and model1 ms
  • ✓StateMachine › billingType detection › defaults to unknown1 ms
  • ✓StateMachine › billingType detection › detects subscription from "Claude Max" plan1 ms
  • ✓StateMachine › billingType detection › detects subscription from "Max" (case-insensitive)1 ms
  • ✓StateMachine › billingType detection › detects api from "api.anthropic.com"1 ms
  • ✓StateMachine › billingType detection › detects api (case-insensitive)1 ms
  • ✓StateMachine › billingType detection › stays unknown for unrecognized plan0 ms
  • ✓StateMachine › billingType detection › stays unknown when plan is absent0 ms
  • ✓StateMachine › billingType detection › persists billingType across subsequent model_info without plan0 ms
  • ✓StateMachine › billingType detection › emits state_changed when billingType is set1 ms
  • ✓StateMachine › spinner_start recovery from awaiting states › spinner_start transitions from AWAITING_OPTION to PROCESSING0 ms
  • ✓StateMachine › spinner_start recovery from awaiting states › spinner_start transitions from AWAITING_PERMISSION to PROCESSING1 ms
  • ✓StateMachine › spinner_start recovery from awaiting states › spinner_start transitions from AWAITING_DIFF to PROCESSING0 ms
  • ✓StateMachine › spinner_start recovery from awaiting states › clears options and navigable state on spinner_start from AWAITING_OPTION2 ms
  • ✓StateMachine › spinner_start recovery from awaiting states › stores navigable and cursorIndex from permission_prompt1 ms
  • ✓StateMachine › spinner_start recovery from awaiting states › cursor index tracking via updateCursorIndex1 ms
  • ✓StateMachine › cursor authority (optimistic vs pty) › optimistic source updates cursor immediately1 ms
  • ✓StateMachine › cursor authority (optimistic vs pty) › suppresses stale PTY within 200ms of optimistic1 ms
  • ✓StateMachine › cursor authority (optimistic vs pty) › accepts PTY after 200ms grace period1 ms
  • ✓StateMachine › cursor authority (optimistic vs pty) › emits state_changed on optimistic update1 ms
  • ✓StateMachine › cursor authority (optimistic vs pty) › does NOT emit state_changed when stale PTY is suppressed1 ms
  • ✓StateMachine › codex_* hook events › codex_session_start → IDLE1 ms
  • ✓StateMachine › codex_* hook events › codex_user_prompt_submit → PROCESSING1 ms
  • ✓StateMachine › codex_* hook events › codex_tool_start sets currentTool from tool_name1 ms
  • ✓StateMachine › codex_* hook events › codex_tool_end clears currentTool1 ms
  • ✓StateMachine › codex_* hook events › codex_stop → IDLE from PROCESSING1 ms
  • ✓StateMachine › codex_* hook events › codex_turn_complete closes PROCESSING when codex_stop is absent0 ms
  • ✓StateMachine › codex_* hook events › codex_turn_complete remains idempotent after codex_stop1 ms
  • ✓StateMachine › codex_* hook events › full codex lifecycle preserves state transitions1 ms
bridge/src/__tests__/surface-protocol-runtime.test.ts15/15
  • ✓Surface Protocol HTTP identity › keeps a headerless request in legacy baseline mode3 ms
  • ✓Surface Protocol HTTP identity › accepts Pocket Daily and intersects away unknown capabilities3 ms
  • ✓Surface Protocol HTTP identity › fails closed on partial identity, wrong major/profile, and missing route capability1 ms
  • ✓Surface Protocol HTTP identity › rejects cross-product, cross-board, and cross-channel identities1 ms
  • ✓Surface Protocol HTTP identity › requires repeated query tuple members to match instead of overriding headers2 ms
  • ✓Surface Protocol HTTP identity › activates the same projection from headers without removing the legacy query1 ms
  • ✓Surface Protocol HTTP identity › keeps legacy partial OTA resumable and grants 206 only by capability1 ms
  • ✓Surface Protocol WebSocket negotiation › keeps Claude tool history opt-in for dashboard clients1 ms
  • ✓Surface Protocol WebSocket negotiation › selects portable-reader/v1 and returns only the capability intersection1 ms
  • ✓Surface Protocol WebSocket negotiation › keeps the checked welcome fixture derived from runtime negotiation0 ms
  • ✓Surface Protocol WebSocket negotiation › does not negotiate inbox.ws before the runtime contract exists0 ms
  • ✓Surface Protocol WebSocket negotiation › negotiates licensed learning-pack delivery for portable readers0 ms
  • ✓Surface Protocol WebSocket negotiation › negotiates automatic font-pack delivery for portable readers0 ms
  • ✓Surface Protocol WebSocket negotiation › bounds portable WebSocket traffic to welcome and liveness events1 ms
  • ✓Surface Protocol WebSocket negotiation › rejects unknown majors, profiles, and unregistered portable products1 ms
bridge/src/__tests__/tier3-integration.test.ts16/16
  • ✓mDNS crash recovery › invalidateMdnsInstance does not throw when no instance exists3 ms
  • ✓mDNS crash recovery › isNonFatalMdnsError matches recoverable mDNS multicast failures1 ms
  • ✓mDNS crash recovery › isNonFatalMdnsError does NOT match unrelated fatal errors0 ms
  • ✓Display sleep/wake broadcast › DisplayMonitor initial state is ON0 ms
  • ✓Display sleep/wake broadcast › parses macOS lock/session presence from ioreg output1 ms
  • ✓Display sleep/wake broadcast › treats an absent ScreenIsLocked key as UNLOCKED when IOConsoleUsers is visible0 ms
  • ✓Display sleep/wake broadcast › display_state event has correct shape0 ms
  • ✓Display sleep/wake broadcast › display_state broadcasts to WS clients78 ms
  • ✓Display sleep/wake broadcast › display_state broadcasts to multiple clients57 ms
  • ✓Display sleep/wake broadcast › SSE also receives display_state89 ms
  • ✓Voice transcription endpoint › returns 503 when no voice manager is set20 ms
  • ✓Voice transcription endpoint › returns 400 for empty audio data7 ms
  • ✓Voice transcription endpoint › returns transcription from voice manager5 ms
  • ✓Voice transcription endpoint › returns 500 when voice manager throws6 ms
  • ✓WsServer broadcast hooks (serial relay) › onBroadcast hook receives all broadcast events2 ms
  • ✓WsServer broadcast hooks (serial relay) › broadcast hook errors do not crash server2 ms
bridge/src/__tests__/ws-server-broadcast-actionability.test.ts10/10
  • ✓broadcastActionability (pure classifier) › flags a state_update in an AWAITING_* state as actionable3 ms
  • ✓broadcastActionability (pure classifier) › flags a state_update in IDLE/PROCESSING/DISCONNECTED as cosmetic0 ms
  • ✓broadcastActionability (pure classifier) › flags a sessions_list carrying any awaiting session as actionable0 ms
  • ✓broadcastActionability (pure classifier) › flags a sessions_list with no awaiting session as cosmetic0 ms
  • ✓broadcastActionability (pure classifier) › flags an empty sessions_list as cosmetic, not actionable0 ms
  • ✓broadcastActionability (pure classifier) › answers n/a for event types with no session-state field0 ms
  • ✓WsServer.broadcast() emits the actionability line › fires one debug() call per broadcast naming type, client count, and actionability1 ms
  • ✓WsServer.broadcast() emits the actionability line › reports cosmetic for an idle state_update on the same push site0 ms
  • ✓WsServer.broadcast() emits the actionability line › counts attempts without clients or logging and returns isolated snapshots1 ms
  • ✓WsServer.broadcast() emits the actionability line › starts a new measurement identity and zero counts for a new server4 ms
shared/src/__tests__/action-fold.test.ts25/25
  • ✓agentCoordinationSummary › counts dispatch and messaging tools separately4 ms
  • ✓agentCoordinationSummary › todo bookkeeping tools are NOT dispatch0 ms
  • ✓agentCoordinationSummary › null for a plain single-agent task0 ms
  • ✓agentCoordinationSummary › OpenCode's lowercase task dispatch counts0 ms
  • ✓foldToolName › passes plain tool names through0 ms
  • ✓foldToolName › keeps only the tool segment of MCP names0 ms
  • ✓foldActionCounts › sorts by count desc and shows counts0 ms
  • ✓foldActionCounts › collapses beyond the cap into +N0 ms
  • ✓foldActionCounts › merges MCP variants that fold to the same short name0 ms
  • ✓foldActionCounts › is deterministic on ties (name order)0 ms
  • ✓foldActionCounts › singular file label0 ms
  • ✓foldActionCounts › returns null when there is nothing to say0 ms
  • ✓shared vector file parity (shared/action-fold-vectors.json) › has enough vectors to be a gate0 ms
  • ✓shared vector file parity (shared/action-fold-vectors.json) › count-desc ordering with a files suffix0 ms
  • ✓shared vector file parity (shared/action-fold-vectors.json) › MCP server prefix folded away0 ms
  • ✓shared vector file parity (shared/action-fold-vectors.json) › folded names merge their counts0 ms
  • ✓shared vector file parity (shared/action-fold-vectors.json) › tie-break is UTF-16 code-unit order (uppercase before lowercase), never locale order0 ms
  • ✓shared vector file parity (shared/action-fold-vectors.json) › collapses past ACTION_FOLD_MAX_TOOLS to +N0 ms
  • ✓shared vector file parity (shared/action-fold-vectors.json) › files-only line0 ms
  • ✓shared vector file parity (shared/action-fold-vectors.json) › singular file0 ms
  • ✓shared vector file parity (shared/action-fold-vectors.json) › nothing to say folds to null, never an empty string0 ms
  • ✓shared vector file parity (shared/action-fold-vectors.json) › zero counts and empty names are filtered out0 ms
  • ✓shared vector file parity (shared/action-fold-vectors.json) › dispatch + messaging counted from raw names0 ms
  • ✓shared vector file parity (shared/action-fold-vectors.json) › all dispatch spellings count; TaskCreate-style bookkeeping names do not exist here0 ms
  • ✓shared vector file parity (shared/action-fold-vectors.json) › todo bookkeeping is not dispatch — coordination stays null0 ms
shared/src/__tests__/format-utils.test.ts67/67
  • ✓formatDurationSec › formats sub-minute spans as seconds2 ms
  • ✓formatDurationSec › formats minute spans, dropping a zero-second remainder0 ms
  • ✓formatDurationSec › formats hour spans, dropping a zero-minute remainder0 ms
  • ✓formatDurationSec › clamps negatives to zero0 ms
  • ✓formatAntigravityPlanShort › shortens "Google AI Pro" to "AGY Pro"0 ms
  • ✓formatAntigravityPlanShort › shortens "Google AI Ultra" to "AGY Ultra"0 ms
  • ✓formatAntigravityPlanShort › strips an "Antigravity " prefix0 ms
  • ✓formatAntigravityPlanShort › returns undefined for blank/absent input0 ms
  • ✓formatAntigravityPlanShort › is idempotent on already-shortened values0 ms
  • ✓formatAntigravityPlanShort › collapses a bare "Google AI" to "AGY"0 ms
  • ✓adjustUsagePercent › returns undefined when percent is null0 ms
  • ✓adjustUsagePercent › returns undefined when percent is undefined0 ms
  • ✓adjustUsagePercent › returns percent unchanged when resetsAt is null0 ms
  • ✓adjustUsagePercent › returns percent unchanged when resetsAt is undefined0 ms
  • ✓adjustUsagePercent › returns percent unchanged when resetsAt is in the future0 ms
  • ✓adjustUsagePercent › returns 0 when resetsAt is in the past (window expired)0 ms
  • ✓adjustUsagePercent › returns 0 when resetsAt equals now (edge case)0 ms
  • ✓adjustUsagePercent › handles invalid date string gracefully (returns percent)0 ms
  • ✓adjustUsagePercent › handles empty string resetsAt (returns percent)0 ms
  • ✓adjustUsagePercent › returns percent unchanged when resetsAt is far in the past (>1h)0 ms
  • ✓adjustUsagePercent › still returns 0 just after the 1h threshold boundary0 ms
  • ✓isCodexWindowStale › returns false when resetsAt is undefined0 ms
  • ✓isCodexWindowStale › returns false for an invalid date string0 ms
  • ✓isCodexWindowStale › returns false when resetsAt is in the future0 ms
  • ✓isCodexWindowStale › returns false when resetsAt is in the past but within the grace window0 ms
  • ✓isCodexWindowStale › returns true when resetsAt is past beyond the grace window0 ms
  • ✓isCodexWindowStale › honors a custom grace0 ms
  • ✓codex snapshot freshness › codexSnapshotAgeMs › returns undefined for missing / unparseable stamps0 ms
  • ✓codex snapshot freshness › codexSnapshotAgeMs › measures age against the supplied clock0 ms
  • ✓codex snapshot freshness › codexSnapshotAgeMs › clamps a future stamp to zero rather than going negative0 ms
  • ✓codex snapshot freshness › isCodexSnapshotAged › is false when there is nothing to judge0 ms
  • ✓codex snapshot freshness › isCodexSnapshotAged › is false for a snapshot inside the threshold0 ms
  • ✓codex snapshot freshness › isCodexSnapshotAged › is false exactly at the threshold, true past it0 ms
  • ✓codex snapshot freshness › isCodexSnapshotAged › honors a custom max age0 ms
  • ✓codex snapshot freshness › formatSnapshotAge › rounds DOWN so the label never overstates freshness0 ms
  • ✓codex snapshot freshness › formatSnapshotAge › returns undefined when there is nothing to say0 ms
  • ✓codex snapshot freshness › codexUsageFootnote › says nothing for a live window with a fresh snapshot0 ms
  • ✓codex snapshot freshness › codexUsageFootnote › THE REGRESSION: a 4h-old snapshot behind a 6-day-out weekly reset is aged1 ms
  • ✓codex snapshot freshness › codexUsageFootnote › an ended window outranks age — "stale", not an age0 ms
  • ✓codex snapshot freshness › codexUsageFootnote › says nothing when there is no window at all0 ms
  • ✓codex snapshot freshness › codexUsageFootnote › says nothing for a live window with no capture stamp (legacy producer)0 ms
  • ✓formatResetTime › returns "now" when resetsAt is in the past0 ms
  • ✓formatResetTime › returns undefined for null input0 ms
  • ✓formatResetTime › returns minutes-only for < 1h remaining0 ms
  • ✓formatResetTime › returns hours and minutes for < 24h remaining0 ms
  • ✓formatResetTime › returns days and hours for >= 24h remaining0 ms
  • ✓formatResetTime › omits minutes when exactly on the hour0 ms
  • ✓formatResetTime › passes through pre-formatted strings (no T)0 ms
  • ✓codexSnapshotMatchesAccountPlan › voids a snapshot minted under a plan the account no longer holds0 ms
  • ✓codexSnapshotMatchesAccountPlan › keeps a matching snapshot, free included — free is a plan, not a synonym for void0 ms
  • ✓codexSnapshotMatchesAccountPlan › compares the same normalized form the display table is keyed by0 ms
  • ✓codexSnapshotMatchesAccountPlan › treats an unknown tier on either side as no information, never as licence to void0 ms
  • ✓isModelScopedCodexLimit › treats a NAMED limit as scoped to one model, never as the account0 ms
  • ✓isModelScopedCodexLimit › treats the unnamed families as the account, credit plans included0 ms
  • ✓isModelScopedCodexLimit › is an allow-list of the unnamed, so a future scoped family is excluded by default0 ms
  • ✓codexSnapshotOutranks › prefers a plan-matching snapshot over a NEWER mismatched one0 ms
  • ✓codexSnapshotOutranks › falls back to recency within the same match class0 ms
  • ✓codexSnapshotOutranks › keeps the incumbent on an exact tie — both scanners walk in priority order0 ms
  • ✓codexSnapshotOutranks › orders an unstamped snapshot below a stamped one of the same class0 ms
  • ✓codexSnapshotOutranks › degrades to pure recency when the account tier is unknown0 ms
  • ✓isCodexFreePlan › recognises the free tier regardless of case or padding0 ms
  • ✓isCodexFreePlan › is false for every paid tier and for an unknown one0 ms
  • ✓scopedLimitClaimsUsageKey › claims a key when a scoped cap is present2 ms
  • ✓scopedLimitClaimsUsageKey › claims nothing when there is no cap0 ms
  • ✓codexWindowsBeside › retains all Codex windows beside the scoped cap0 ms
  • ✓codexWindowsBeside › leaves Codex untouched when no cap claims a key0 ms
  • ✓codexWindowsBeside › returns empty when Codex reports nothing0 ms
shared/src/__tests__/no-side-effects.test.ts4/4
  • ✓shared is side-effect free › finds source files to check3 ms
  • ✓shared is side-effect free › has no top-level executable statements12 ms
  • ✓shared is side-effect free › performs no import-time I/O, timers, or global mutation10 ms
  • ✓shared is side-effect free › declares sideEffects: false so bundlers actually act on this0 ms
shared/src/__tests__/protocol-contract.test.ts25/25
  • ✓Protocol Contract — StateUpdateEvent › minimal state_update has required fields5 ms
  • ✓Protocol Contract — StateUpdateEvent › full state_update has all optional fields as correct types2 ms
  • ✓Protocol Contract — StateUpdateEvent › state_update serializes to valid JSON0 ms
  • ✓Protocol Contract — StateUpdateEvent › state enum values are lowercase strings1 ms
  • ✓Protocol Contract — StateUpdateEvent › promptType values match known set0 ms
  • ✓Protocol Contract — UsageEvent › has all required numeric fields0 ms
  • ✓Protocol Contract — UsageEvent › rate limit fields are present when available2 ms
  • ✓Protocol Contract — UsageEvent › tokenStatus matches known values0 ms
  • ✓Protocol Contract — SessionsListEvent › sessions have required fields1 ms
  • ✓Protocol Contract — SessionsListEvent › agentType is optional string0 ms
  • ✓Protocol Contract — ButtonStateEvent › buttons have required fields1 ms
  • ✓Protocol Contract — EncoderStateEvent › encoders have required fields1 ms
  • ✓Protocol Contract — TimelineEventMsg › entry has required fields0 ms
  • ✓Protocol Contract — ConnectionEvent › has required fields0 ms
  • ✓Protocol Contract — DisplayStateEvent › has boolean displayOn0 ms
  • ✓Protocol Contract — PluginCommand shapes › respond command has value0 ms
  • ✓Protocol Contract — PluginCommand shapes › select_option has numeric index0 ms
  • ✓Protocol Contract — PluginCommand shapes › navigate_option has direction0 ms
  • ✓Protocol Contract — PluginCommand shapes › send_prompt has text0 ms
  • ✓Protocol Contract — PluginCommand shapes › utility command has action0 ms
  • ✓Protocol Contract — PluginCommand shapes › clear_session_focus is fieldless1 ms
  • ✓Protocol Contract — BridgeEvent discriminated union › all event types in union have type field2 ms
  • ✓Protocol Contract — BridgeEvent discriminated union › SERIAL_FORWARDED_EVENTS covers expected types12 ms
  • ✓Protocol Contract — Backward Compatibility › state_update can be parsed with only required fields (old client)1 ms
  • ✓Protocol Contract — Backward Compatibility › usage_update required fields are sufficient for display0 ms
shared/src/__tests__/protocol-generated-sync.test.ts12/12
  • ✓generated protocol artifacts in sync › generated/protocol/bridge-event-schema.json matches the SSOT4 ms
  • ✓generated protocol artifacts in sync › generated/protocol/plugin-command-schema.json matches the SSOT0 ms
  • ✓generated protocol artifacts in sync › generated/protocol/gateway-frame-schema.json matches the SSOT1 ms
  • ✓generated protocol artifacts in sync › generated/protocol/BridgeEvent.swift matches the SSOT2 ms
  • ✓generated protocol artifacts in sync › generated/protocol/PluginCommand.swift matches the SSOT0 ms
  • ✓generated protocol artifacts in sync › generated/protocol/GatewayFrame.swift matches the SSOT1 ms
  • ✓generated protocol artifacts in sync › generated/protocol/BridgeEvent.kt matches the SSOT1 ms
  • ✓generated protocol artifacts in sync › generated/protocol/PluginCommand.kt matches the SSOT0 ms
  • ✓generated protocol artifacts in sync › generated/protocol/GatewayFrame.kt matches the SSOT1 ms
  • ✓generated protocol artifacts in sync › generated/protocol/AgentCommand.swift matches the SSOT0 ms
  • ✓generated protocol artifacts in sync › generated/protocol/AgentCommand.kt matches the SSOT0 ms
  • ✓generated protocol artifacts in sync › shared/src/command-builders.ts matches the SSOT0 ms
shared/src/__tests__/question-summary.test.ts8/8
  • ✓summarizeQuestionForKey › keeps the verb and the object, dropping the middle4 ms
  • ✓summarizeQuestionForKey › takes the object of the VERB it kept, not of a later pipeline stage0 ms
  • ✓summarizeQuestionForKey › drops the verb before it drops the object when both will not fit0 ms
  • ✓summarizeQuestionForKey › surfaces the target of a destructive command — the case a head cut hid0 ms
  • ✓summarizeQuestionForKey › does not mistake a flag or a quoted range for the object0 ms
  • ✓summarizeQuestionForKey › returns a fitting string untouched — the elision must buy something0 ms
  • ✓summarizeQuestionForKey › falls back to a plain head cut when there is no path-like object0 ms
  • ✓summarizeQuestionForKey › is total on empty and degenerate input0 ms
shared/src/__tests__/session-deck-usage.test.ts27/27
  • ✓buildSessionDeck list-view usage tiles › pins 5H/7D to the bottom strip and wires them to query_usage18 ms
  • ✓buildSessionDeck list-view usage tiles › leaves the upper grid and the clock-side keys entirely to sessions1 ms
  • ✓buildSessionDeck list-view usage tiles › fills the strip from the right, freeing the leftmost key for a session1 ms
  • ✓buildSessionDeck list-view usage tiles › does not reserve usage slots when showUsage is off (regression: full grid)1 ms
  • ✓buildSessionDeck list-view usage tiles › frees the usage slots (no residue) when usage is unknown1 ms
  • ✓buildSessionDeck list-view usage tiles › frees only the Claude slots when Codex-only usage is present (partial)1 ms
  • ✓buildSessionDeck list-view usage tiles › fits sessions into the 13 non-usage keys without paging1 ms
  • ✓buildSessionDeck list-view usage tiles › paginates when sessions exceed capacity, NEXT→ sits on the last free key1 ms
  • ✓buildSessionDeck list-view usage tiles › page 2 keeps the usage tiles pinned and shows the remainder1 ms
  • ✓buildSessionDeck list-view usage tiles › shows usage even with zero sessions1 ms
  • ✓buildSessionDeck list-view usage tiles › compacts the Codex pair so Plus 5H and 7D both fit the 3-key strip1 ms
  • ✓buildSessionDeck list-view usage tiles › renders only the Codex window whose datum exists1 ms
  • ✓buildSessionDeck list-view usage tiles › labels the Codex weekly window "7D" when it arrives in the primary slot (secondary null)1 ms
  • ✓buildSessionDeck list-view usage tiles › shows a credits tile when Codex reports a credit-based plan (null windows)1 ms
  • ✓buildSessionDeck list-view usage tiles › renders ∞ for an unlimited-credits Codex plan1 ms
  • ✓buildSessionDeck list-view usage tiles › replaces the Codex windows with one LUNA tile while account quota is exhausted2 ms
  • ✓buildSessionDeck list-view usage tiles › shows the purchased-credit balance in place of an exhausted Codex window2 ms
  • ✓buildSessionDeck list-view usage tiles › keeps credits and the Luna reserve as separate keys, yielding the reserve when full1 ms
  • ✓buildSessionDeck list-view usage tiles › falls back to trailing keys on a tiny deck where the strip is not placed0 ms
  • ✓buildSessionDeck list-view usage tiles › never starves the only key on a 1-key deck (usage yields to the session)0 ms
  • ✓buildSessionDeck scoped cap within the fixed usage strip › always pairs weekly and scoped readings, even with a spare key1 ms
  • ✓buildSessionDeck scoped cap within the fixed usage strip › pairs the cap with 7D and leaves 5H a whole gauge2 ms
  • ✓buildSessionDeck scoped cap within the fixed usage strip › fits five readings in three keys: 5H | 7D+cap | Codex pair1 ms
  • ✓buildSessionDeck scoped cap within the fixed usage strip › seats the cap identically whether or not it is binding1 ms
  • ✓buildSessionDeck scoped cap within the fixed usage strip › keeps an INACTIVE cap without displacing a live Codex window1 ms
  • ✓buildSessionDeck scoped cap within the fixed usage strip › draws no tile for a window the API did not report — but still draws a real 0%1 ms
  • ✓buildSessionDeck scoped cap within the fixed usage strip › shows nothing but Claude when neither Codex nor a scoped cap exists0 ms
shared/src/__tests__/session-slot-anim.test.ts12/12
  • ✓session slot animation cycle › returns to its starting phase after one cycle (processing, active=false)5 ms
  • ✓session slot animation cycle › returns to its starting phase after one cycle (processing, active=true)4 ms
  • ✓session slot animation cycle › returns to its starting phase after one cycle (awaiting_permission, active=false)1 ms
  • ✓session slot animation cycle › returns to its starting phase after one cycle (awaiting_permission, active=true)1 ms
  • ✓session slot animation cycle › returns to its starting phase after one cycle (idle, active=false)0 ms
  • ✓session slot animation cycle › returns to its starting phase after one cycle (idle, active=true)1 ms
  • ✓session slot animation cycle › animates the processing border without an explicit start frame0 ms
  • ✓session slot animation cycle › keeps a supplied start frame as a phase offset, not a freeze0 ms
  • ✓session slot animation cycle › leaves an unfocused idle tile static — it must not be encoded as a loop0 ms
  • ✓tile identity is byte-stable › renders the same quiet slot identically every time0 ms
  • ✓tile identity is byte-stable › renders the same empty slot identically every time0 ms
  • ✓tile identity is byte-stable › emits no random-looking ids from any session-slot renderer1 ms
shared/src/__tests__/session-state-presentation.test.ts9/9
  • ✓session state presentation (DESIGN.md §2.7) › gives each wire state one tone: calm idle, cyan work, amber asks, grey offline4 ms
  • ✓session state presentation (DESIGN.md §2.7) › treats a missing state as offline and an unknown one as a live quiet session1 ms
  • ✓session state presentation (DESIGN.md §2.7) › binds colours to the Session token group, never to literals3 ms
  • ✓session state presentation (DESIGN.md §2.7) › keeps work visually distinct from brand hues and from the idle grey1 ms
  • ✓session state presentation (DESIGN.md §2.7) › stays legible as small text on water, dark popups and paper1 ms
  • ✓session state presentation (DESIGN.md §2.7) › lets only the awaiting tone animate0 ms
  • ✓session state presentation (DESIGN.md §2.7) › keeps one vocabulary that fits every surface budget2 ms
  • ✓session state presentation (DESIGN.md §2.7) › draws agent brand hues from the Brand tokens1 ms
  • ✓session state presentation (DESIGN.md §2.7) › generates the Swift, Kotlin and ESP32 mirrors from the source3 ms
shared/src/__tests__/session-utils.test.ts49/49
  • ✓OpenClaw visibility SSOT › isOpenClawSessionActive is true only when gatewayConnected3 ms
  • ✓OpenClaw visibility SSOT › hasOpenClawSession detects an emitted openclaw session0 ms
  • ✓agentTypeRank › ranks known agents stably, with Kiro after the existing agent set1 ms
  • ✓naturalLabelCompare › orders numeric chunks naturally (Agent 2 before Agent 10)13 ms
  • ✓naturalLabelCompare › treats undefined as empty string0 ms
  • ✓sortSessions › places openclaw before claude-code regardless of project1 ms
  • ✓sortSessions › sorts by project name within the same agent type0 ms
  • ✓sortSessions › sorts numbered project names naturally (#2 before #10)0 ms
  • ✓sortSessions › breaks ties on startedAt ascending (oldest first)0 ms
  • ✓sortSessions › falls back to id natural-compare when startedAt ties to the same ms0 ms
  • ✓sortSessions › does not mutate the input array0 ms
  • ✓sessionWeight › returns the weight when it is a finite number (incl. negatives and 0)0 ms
  • ✓sessionWeight › collapses missing / null / non-finite weights to 00 ms
  • ✓sessionWeight › accepts both documented bounds unchanged0 ms
  • ✓sessionWeight › clamps out-of-range values to the documented bounds0 ms
  • ✓sessionWeight › integer-truncates fractional values so all platform mirrors agree0 ms
  • ✓sanitizeWeightForWire › drops non-numbers and non-finite values to undefined (wire omits the key)0 ms
  • ✓sanitizeWeightForWire › passes in-range integers and clamps/truncates the rest0 ms
  • ✓sortSessions weight comparator safety › orders opposite-sign extreme in-range weights without overflow tricks0 ms
  • ✓sortSessions weight override › orders by weight ascending: negatives, then unweighted/0, then positives0 ms
  • ✓sortSessions weight override › treats an unweighted session the same as weight 00 ms
  • ✓sortSessions weight override › weight beats agentType — a weighted claude-code sorts before an unweighted openclaw0 ms
  • ✓sortSessions weight override › within the same weight, existing ordering (agentType → project → startedAt → id) still applies0 ms
  • ✓sortSessions weight override › maps terminal tab order onto the deck: tab N (weight N) lands in slot N0 ms
  • ✓sortSessions weight override › does not change ordering when no session has a weight (backward compatible)0 ms
  • ✓foldCodexSessionsForDisplay › keeps awaiting_permission visible with its own identity and controls beside a processing sibling3 ms
  • ✓foldCodexSessionsForDisplay › keeps awaiting_option visible with its own identity and controls beside a processing sibling1 ms
  • ✓foldCodexSessionsForDisplay › keeps awaiting_diff visible with its own identity and controls beside a processing sibling0 ms
  • ✓foldCodexSessionsForDisplay › folds Codex CLI and Codex App separately even with the same project2 ms
  • ✓foldCodexSessionsForDisplay › never folds same-project Codex sessions pinned to distinct weights0 ms
  • ✓foldCodexSessionsForDisplay › keeps distinct-weight sessions unfolded across state changes1 ms
  • ✓foldCodexSessionsForDisplay › still folds same-project Codex sessions when both are unweighted or share a weight1 ms
  • ✓assignDisplayNames › passes single sessions through without #N suffix0 ms
  • ✓assignDisplayNames › adds #1/#2 suffixes for duplicate (project, agentType) tuples in input order0 ms
  • ✓assignDisplayNames › numbers same project across different agentTypes independently0 ms
  • ✓assignDisplayNames › produces the same #N assignment as a deterministic sort + display pipeline0 ms
  • ✓normalizeProjectForGrouping › strips path and trailing #N suffix0 ms
  • ✓normalizeProjectForGrouping › handles empty/undefined0 ms
  • ✓projectGroupKey › exact match groups0 ms
  • ✓projectGroupKey › long multi-token stems fuse0 ms
  • ✓projectGroupKey › delimiter extension fuses ("foo…" + "foo…-1")0 ms
  • ✓projectGroupKey › short sibling projects stay separate0 ms
  • ✓projectGroupKey › stems with fewer than 2 delimiters stay separate0 ms
  • ✓groupSessionsByProject › clusters same-stem worktrees and keeps singletons flat1 ms
  • ✓groupSessionsByProject › groups duplicate sessions of the same project (#N suffixed)0 ms
  • ✓groupSessionsByProject › preserves input order within groups0 ms
  • ✓rawSessionId / sameSession › strips every observed agent prefix0 ms
  • ✓rawSessionId / sameSession › leaves managed and already-bare ids alone0 ms
  • ✓rawSessionId / sameSession › sameSession bridges the two keying forms3 ms
shared/src/__tests__/session-weight-rules.test.ts6/6
  • ✓session weight range invariants › range is symmetric around the neutral band and fits every platform integer4 ms
  • ✓session weight range invariants › sessionWeight clamps exactly to the SSOT bounds0 ms
  • ✓generated mirrors in sync › apple/AgentDeck/Model/SessionWeightRules.generated.swift matches the SSOT0 ms
  • ✓generated mirrors in sync › android/app/src/main/kotlin/dev/agentdeck/net/SessionWeightRules.generated.kt matches the SSOT0 ms
  • ✓generated mirrors in sync › emitters embed the SSOT literals (sanity on the emitters themselves)1 ms
  • ✓self-contained hand-port copies › D200HLayoutModel.swift carries the same weight-range literals1 ms
shared/src/__tests__/state-transitions.test.ts8/8
  • ✓generated mirrors in sync › apple/AgentDeck/Daemon/Core/StateTransitions.generated.swift matches the SSOT4 ms
  • ✓generated mirrors in sync › every daemon-only generated file carries the macOS guard1 ms
  • ✓generated mirrors in sync › emits a Swift case name for every state, with a rawValue only where they differ1 ms
  • ✓generated mirrors in sync › carries each transition note into the mirror2 ms
  • ✓transition table invariants › is deterministic — no two rows match the same (from, trigger, source)2 ms
  • ✓transition table invariants › leaves AWAITING_* without a wall-clock backstop1 ms
  • ✓transition table invariants › gives every AWAITING_* state a hook-driven exit1 ms
  • ✓transition table invariants › every row targets a real state and a known source6 ms
shared/src/__tests__/text-sanitize.test.ts5/5
  • ✓stripUnsafeText › strips ANSI CSI sequences but keeps the visible text2 ms
  • ✓stripUnsafeText › strips OSC sequences and bare ESC forms0 ms
  • ✓stripUnsafeText › strips XML-invalid control characters but keeps tab/newline0 ms
  • ✓stripUnsafeText › strips lone surrogate halves but keeps full emoji0 ms
  • ✓escSvgText › entity-escapes after stripping0 ms

50 files

Pass

APME evaluation

Are turns and tasks segmented, judged and scored correctly, and does the recommender rank models honestly?

Passed
617
Failed
0
Skipped
2
Time
6.6 s
50 test files
bridge/src/__tests__/apme-abandoned-run-reaper.test.ts12/12
  • ✓abandoned APME run reaper › finds runs with real work that the empty-shell reaper cannot see37 ms
  • ✓abandoned APME run reaper › measures staleness from last activity, not started_at13 ms
  • ✓abandoned APME run reaper › counts steps and sample events as activity, not just turns15 ms
  • ✓abandoned APME run reaper › closes turns, tasks and the run at the last activity, not now16 ms
  • ✓abandoned APME run reaper › is idempotent and drops out of the candidate list once reaped22 ms
  • ✓abandoned APME run reaper › leaves reaped runs to the normal eval queue14 ms
  • ✓abandoned APME run reaper › reports a run the collector still owns as live15 ms
  • ✓what the reaper says it found › a task whose last turn CLOSED is an idle_gap boundary, not an orphan17 ms
  • ✓what the reaper says it found › a task still holding an OPEN turn is orphaned, and that turn is closed as run_close13 ms
  • ✓what the reaper says it found › classifies each open task on its own turns, in one run14 ms
  • ✓reaped tasks keep their run's category and old verdicts are re-read on evidence › a reaped task inherits the run category it never resolved in memory19 ms
  • ✓reaped tasks keep their run's category and old verdicts are re-read on evidence › reclassifyReapedTasks moves only orphaned tasks whose every turn closed with a known signal15 ms
bridge/src/__tests__/apme-activity-history.test.ts5/5
  • ✓APME unified activity history › uses the same deterministic identity contract as the Swift mirror4 ms
  • ✓APME unified activity history › collapses exact duplicates and keeps the richer fields without summing them20 ms
  • ✓APME unified activity history › stitches a handover fragment only when native session/task and time agree1 ms
  • ✓APME unified activity history › builds one glanceable per-agent total after dedup1 ms
  • ✓APME unified activity history › reports agent work time instead of user think time between turns1 ms
bridge/src/__tests__/apme-background-plan.test.ts2/2
  • ✓APME background queues avoid payload table scans › orders the closed-run queue from a covering index and still excludes open/empty runs32 ms
  • ✓APME background queues avoid payload table scans › reads only pending nonempty responses, ordered without sorting the full turns table17 ms
bridge/src/__tests__/apme-category-e2e.test.ts3/3
  • ✓APME category-aware turn evaluation (E2E) › conversation turn: prompt → classify → judge → turn category + composite persisted42 ms
  • ✓APME category-aware turn evaluation (E2E) › research turn: rule-based classifier picks research, judge uses research rubric axes24 ms
  • ✓APME category-aware turn evaluation (E2E) › daemon backfill pass: turns with response but no outcome get committed + null composite20 ms
bridge/src/__tests__/apme-classifier.test.ts26/26
  • ✓classify() › planning — plan mode used3 ms
  • ✓classify() › planning — short session, no file changes0 ms
  • ✓classify() › research — web searches dominant0 ms
  • ✓classify() › research — grep/glob dominant, no file changes0 ms
  • ✓classify() › coding — edit+write dominant with file changes0 ms
  • ✓classify() › debugging — tests + edits + bash0 ms
  • ✓classify() › refactoring — many edits, no new files0 ms
  • ✓classify() › review — mostly reads, minimal edits0 ms
  • ✓classify() › ops — bash dominant, few edits0 ms
  • ✓classify() › conversation — very short, no tools0 ms
  • ✓classify() › multi_agent — multiple Agent delegations0 ms
  • ✓classify() › unknown — no clear pattern0 ms
  • ✓classifyWithLlm() › uses the configured MLX pin without probing a stale download catalog41 ms
  • ✓classifyWithLlm() › prefers MLX when it answers with a valid label, never reaching Foundation Models3 ms
  • ✓classifyWithLlm() › falls through to Foundation Models when MLX is unreachable — the no-MLX-server Mac2 ms
  • ✓classifyWithLlm() › never includes a paid backend in the classifier try-order (SSOT invariant)2 ms
  • ✓classifyWithLlm() › never calls a paid backend even when the eval judge is configured to `api` (mutation check)2 ms
  • ✓classifyWithLlm() › falls back to the rule-based category when every backend answers with an out-of-vocabulary label2 ms
  • ✓classifyWithLlm() › falls through to the next backend when one answers but is unreachable, then to rules1 ms
  • ✓classifyWithLlm() › classification calls use the shared max_tokens cap and never the judge budget2 ms
  • ✓computeSignals() › counts tool calls and identifies dominant tool31 ms
  • ✓computeSignals() › detects plan mode from step payload10 ms
  • ✓computeSignals() › detects web searches10 ms
  • ✓classifyRun() › returns signals and category for a coding run13 ms
  • ✓classifyRun() › returns unknown for empty runs14 ms
  • ✓task_category schema migration › runs table has task_signals, task_category, task_category_source columns17 ms
bridge/src/__tests__/apme-claude-turn-completion.test.ts5/5
  • ✓claudeTurnCompletionSince › locates the transcript by the cwd slug first, then by scanning every project4 ms
  • ✓claudeTurnCompletionSince › an end_turn newer than the turn closes it as a recovered Stop with the final text2 ms
  • ✓claudeTurnCompletionSince › a tail still mid-turn claims nothing1 ms
  • ✓claudeTurnCompletionSince › an end_turn older than the turn belongs to a previous turn1 ms
  • ✓claudeTurnCompletionSince › a client abort is aborted, and no transcript is no evidence1 ms
bridge/src/__tests__/apme-collector.test.ts28/28
  • ✓ApmeCollector › preserves task-scoped relation identity through the collector and sample API29 ms
  • ✓ApmeCollector › openRun → ingestHook → closeRun persists a run with steps24 ms
  • ✓ApmeCollector › updateUsage and updateModel reflect in the run row10 ms
  • ✓ApmeCollector › listRuns filters by agent and orders by started_at desc18 ms
  • ✓ApmeCollector › default rubric v1 is seeded on init14 ms
  • ✓ApmeCollector › insertEval + listEvalsForRun round-trip9 ms
  • ✓ApmeCollector › excludes _empty runs from the unevaluated run queue10 ms
  • ✓ApmeCollector › scorecard view aggregates runs per model10 ms
  • ✓ApmeCollector › multi-turn cycle captures prompts and responses (wireAgentApme path)25 ms
  • ✓ApmeCollector › setLastClosedTurnResponse fills missing response on closed turn10 ms
  • ✓ApmeCollector › setTurnResponse tags efficiency_json.response_kind (text / tool_only / empty)12 ms
  • ✓ApmeCollector › no-op gracefully when store is disabled10 ms
  • ✓ApmeCollector › ignores an echoed duplicate turn_start (same prompt on a fresh empty turn)13 ms
  • ✓ApmeCollector › treats a same-prompt re-send after a response or tool use as a new turn13 ms
  • ✓ApmeCollector › an agent_error span records the failure reason in the trajectory without closing the task11 ms
  • ✓ApmeCollector › drops an agent_error span with no open task instead of inventing one9 ms
  • ✓ApmeCollector › accumulates PER-MESSAGE usage instead of stamping the last message as the total17 ms
  • ✓ApmeCollector › attributes model and provider to each turn without reassigning earlier turns13 ms
  • ✓ApmeCollector › closes the turn on an explicit turn_end and records which signal closed it10 ms
  • ✓ApmeCollector › treats a whitespace-differing prompt echo as the same turn, not a Stop loss9 ms
  • ✓ApmeCollector › never lets cost count messages the token total did not11 ms
  • ✓ApmeCollector › does not mistake an unpriced provider zero for a known-free model10 ms
  • ✓ApmeCollector › keeps a prior numeric cost but makes the aggregate unknown when a later call is unpriced12 ms
  • ✓next-prompt close reads the agent record › codex: a turn open at the next prompt takes its reply and end time from the rollout10 ms
  • ✓next-prompt close reads the agent record › codex: a turn the rollout says failed is aborted — no Stop was ever owed13 ms
  • ✓next-prompt close reads the agent record › codex: no record of the turn ending is still an unrecovered Stop loss10 ms
  • ✓a reply that arrives after the run closed › still lands on the session's last turn11 ms
  • ✓a reply that arrives after the run closed › never reaches a turn that closed long ago12 ms
bridge/src/__tests__/apme-dashboard-html.test.ts6/6
  • ✓apmeDashboardHtml — data-quality affordances › explains an un-scored completed run instead of showing a bare header3 ms
  • ✓apmeDashboardHtml — data-quality affordances › surfaces manual reviews as a distinct, layer-filtered section0 ms
  • ✓apmeDashboardHtml — data-quality affordances › each aggregate tab has a graceful empty state (no NaN / blank grid)0 ms
  • ✓apmeDashboardHtml — data-quality affordances › active sessions are flagged as pending, not silently score-less0 ms
  • ✓apmeDashboardHtml — data-quality affordances › opens on the simple deduplicated activity view0 ms
  • ✓bundled Swift-daemon dashboard copy › matches apmeDashboardHtml() byte-for-byte (pnpm generate-apme-dashboard)2 ms
bridge/src/__tests__/apme-http.test.ts14/14
  • ✓APME HTTP routes › returns 503 when APME is not initialized82 ms
  • ✓APME HTTP routes › GET /apme/runs lists recent runs with evals and overall score51 ms
  • ✓APME HTTP routes › GET /apme/runs filters by agent17 ms
  • ✓APME HTTP routes › GET /apme/activity returns the stable, deduplicated activity projection27 ms
  • ✓APME HTTP routes › GET /apme/run/:id returns run detail with steps and evals37 ms
  • ✓APME HTTP routes › GET /apme/run/:id returns 404 for unknown runs19 ms
  • ✓APME HTTP routes › GET /apme/run/:id includes per-task rollup with attached evals42 ms
  • ✓APME HTTP routes › GET /apme/tasks/:id returns task detail with evals + turns37 ms
  • ✓APME HTTP routes › closeTaskExternal with manual signal records outcome + boundary_signal=manual16 ms
  • ✓APME HTTP routes › GET /apme/scorecard returns model aggregates15 ms
  • ✓APME HTTP routes › GET /apme/rubric/current returns seeded rubric v111 ms
  • ✓APME HTTP routes › POST /apme/vibe records feedback and rejects unknown runs19 ms
  • ✓APME HTTP routes › POST /apme/recommend returns candidate list20 ms
  • ✓APME HTTP routes › unknown /apme/* path returns 40415 ms
bridge/src/__tests__/apme-idle-gap-and-title.test.ts7/7
  • ✓task title at promotion › onTaskOpened carries the title derived from the first prompt41 ms
  • ✓task title at promotion › a prompt that derives no title promotes with title=null (Task N fallback stays with the consumer)12 ms
  • ✓generic idle-gap boundary › closes the active task with idle_gap after the gap elapses post-Stop16 ms
  • ✓generic idle-gap boundary › a new prompt within the gap disarms the timer13 ms
  • ✓generic idle-gap boundary › the next prompt after an idle_gap close opens a NEW task in the same run15 ms
  • ✓generic idle-gap boundary › work board views: filter, row flag and badges share one definition25 ms
  • ✓generic idle-gap boundary › session_end within the gap wins — no double close13 ms
bridge/src/__tests__/apme-judge-detect.test.ts7/7
  • ✓detectLocalJudgeProviders › drops embedding-only Ollama models11 ms
  • ✓detectLocalJudgeProviders › reports no provider when every Ollama model is an embedder1 ms
  • ✓detectLocalJudgeProviders › keeps models from servers that report no capabilities at all1 ms
  • ✓detectLocalJudgeProviders › still detects OpenAI-compatible servers via /v1/models1 ms
  • ✓detectLocalJudgeProviders › returns nothing when no local server answers1 ms
  • ✓MLX discovery uses residency rather than downloads › offers only the resident model when another download appears first32 ms
  • ✓MLX discovery uses residency rather than downloads › does not offer an ambiguous catalog-only MLX server1 ms
bridge/src/__tests__/apme-judge-health.test.ts21/21
  • ✓judgeCoverage › excludes declined rows — refusing to grade an empty task is the judge working4 ms
  • ✓judgeCoverage › counts waiting AND aged-out against coverage — both are ungraded work0 ms
  • ✓judgeCoverage › reports null rather than 0% when there is nothing to adjudicate0 ms
  • ✓ApmeStore.judgeHealth › sorts each task into exactly one bucket33 ms
  • ✓ApmeStore.judgeHealth › splits waiting from aged out AT the drain cutoff, not merely near it15 ms
  • ✓ApmeStore.judgeHealth › counts a scored task with no summary as judged13 ms
  • ✓ApmeStore.judgeHealth › excludes judged and declined tasks from the aged-out total15 ms
  • ✓ApmeStore.judgeHealth › groups days in LOCAL time, matching the window the CLI snaps18 ms
  • ✓ApmeStore.judgeHealth › reports the aged-out total for the whole store, not the window17 ms
  • ✓ApmeStore.judgeHealth › never counts a declined task as aged out, however old it is15 ms
  • ✓ApmeStore.judgeHealth › does not re-offer a task that already has a verdict but no summary15 ms
  • ✓ApmeStore.judgeHealth › reports no rows rather than throwing when nothing closed in the window14 ms
  • ✓ApmeStore.judgeLatency › reports percentiles a single backfilled outlier cannot move19 ms
  • ✓ApmeStore.judgeLatency › places p50 and p90 by nearest rank across sizes22 ms
  • ✓ApmeStore.judgeLatency › measures task_judge rows only, not every eval layer13 ms
  • ✓ApmeStore.judgeLatency › measures the FIRST verdict when a task carries several13 ms
  • ✓ApmeStore.judgeLatency › counts a verdict that predates the close instead of dropping it silently15 ms
  • ✓ApmeStore.judgeLatency › reports nulls rather than NaN when nothing has been judged12 ms
  • ✓judge-health presentation › never rounds coverage up to 100% while anything is unjudged0 ms
  • ✓judge-health presentation › floors the demoted unit so no band is skipped0 ms
  • ✓judge-health presentation › snaps day-or-longer windows to a local midnight and leaves shorter ones alone0 ms
bridge/src/__tests__/apme-judge-json-mode.test.ts8/8
  • ✓judge JSON mode › asks the MLX server for a JSON object instead of hoping for one36 ms
  • ✓judge JSON mode › asks an OpenAI-compatible server too, alongside the existing fields1 ms
  • ✓judge JSON mode › pays the same per-request ladder on every call — nothing converges, nothing is remembered5 ms
  • ✓judge JSON mode › surfaces its own status after 3 attempts, and the next call starts fresh with both fields4 ms
  • ✓judge JSON mode › one endpoint refusing the field never touches another — there is no shared memory to leak4 ms
  • ✓judge JSON mode › still compacts a context-overflow 400 and keeps JSON mode on2 ms
  • ✓judge JSON mode › recovers when a penalty refusal is followed by a context overflow3 ms
  • ✓judge JSON mode › does not read an auth or rate-limit failure as a rejected field1 ms
bridge/src/__tests__/apme-judge-probe.test.ts19/19
  • ✓probeJudgeBackend — MLX › returns unavailable when /v1/models is unreachable35 ms
  • ✓probeJudgeBackend — MLX › returns unavailable when /v1/models advertises no chat-capable model4 ms
  • ✓probeJudgeBackend — MLX › returns unavailable when chat ping fails (model not loaded)2 ms
  • ✓probeJudgeBackend — MLX › returns ready when catalog AND chat ping both succeed1 ms
  • ✓probeJudgeBackend — OpenClaw › returns unavailable when /health is unreachable4 ms
  • ✓probeJudgeBackend — OpenClaw › returns unavailable when /health is up but /models is not (gateway not initialised)1 ms
  • ✓probeJudgeBackend — OpenClaw › returns unavailable when configured model is not in /models catalog1 ms
  • ✓probeJudgeBackend — OpenClaw › returns ready when /health + /models both up and model matches1 ms
  • ✓probeJudgeBackend — Anthropic API (opt-in backend) › returns unavailable with setup guidance when no credential is present1 ms
  • ✓callJudgeWithMeta — effective backend labelling across fallback › FM happy path labels eval as foundationModels1 ms
  • ✓callJudgeWithMeta — effective backend labelling across fallback › FM→MLX fallback (fallbackToMlx=true) labels eval as mlx, not foundationModels2 ms
  • ✓callJudgeWithMeta — effective backend labelling across fallback › FM failure without fallbackToMlx propagates the error (no silent label drift)2 ms
  • ✓sanitizeForMlx — runtime fallback hygiene › strips FM endpoint and model when forcing to MLX1 ms
  • ✓sanitizeForMlx — runtime fallback hygiene › strips OpenClaw endpoint and model when forcing to MLX0 ms
  • ✓sanitizeForMlx — runtime fallback hygiene › returns the same cfg shape when already-clean MLX is passed0 ms
  • ✓sanitizeForMlx — runtime fallback hygiene › clears a custom MLX endpoint when source backend is MLX but model is overridden0 ms
  • ✓probeJudgeBackend — Foundation Models › returns unavailable when Swift daemon FM endpoint cannot be resolved40 ms
  • ✓probeJudgeBackend — Foundation Models › returns unavailable when FM endpoint responds with error body1 ms
  • ✓probeJudgeBackend — Foundation Models › returns ready when FM endpoint responds with text1 ms
bridge/src/__tests__/apme-judge-repetition-penalty.test.ts20/20
  • ✓MLX judge repetition penalty › sends the measured default when the user set nothing38 ms
  • ✓MLX judge repetition penalty › honours an explicit value1 ms
  • ✓MLX judge repetition penalty › omits the field entirely when set to 12 ms
  • ✓repetitionPenalty settings validation › drops 0.53 ms
  • ✓repetitionPenalty settings validation › drops 02 ms
  • ✓repetitionPenalty settings validation › drops -12 ms
  • ✓repetitionPenalty settings validation › drops 34 ms
  • ✓repetitionPenalty settings validation › drops high1 ms
  • ✓repetitionPenalty settings validation › drops null1 ms
  • ✓repetitionPenalty settings validation › keeps 11 ms
  • ✓repetitionPenalty settings validation › keeps 1.052 ms
  • ✓repetitionPenalty settings validation › keeps 1.52 ms
  • ✓repetitionPenalty settings validation › keeps 22 ms
  • ✓loadApmeConfig baseline › leaves repetitionPenalty unset by default so the leg default applies0 ms
  • ✓the retry ladder allows every diagnosis › recovers from penalty refusal, then JSON-mode refusal, then overflow7 ms
  • ✓the retry ladder allows every diagnosis › stops when the prompt is already at the compaction target instead of resending it3 ms
  • ✓openai-compatible leg is deliberately excluded › never sends the penalty, not even the default1 ms
  • ✓openai-compatible leg is deliberately excluded › does not send it even when the user set one explicitly2 ms
  • ✓a server that refuses repetition_penalty › drops the penalty first and keeps JSON mode2 ms
  • ✓a server that refuses repetition_penalty › costs one extra request on every call — nothing is remembered between calls7 ms
bridge/src/__tests__/apme-manual-review.test.ts2/2
  • ✓manual_review eval roundtrip › persists a manual_review eval on a task and reads it back with the layer flag intact45 ms
  • ✓manual_review eval roundtrip › manual_review evals do not contaminate the automatic-layer filters15 ms
bridge/src/__tests__/apme-observed-turn-response.test.ts7/7
  • ✓observed turn response archiving › archives the reply on the turn and as an assistant_message event38 ms
  • ✓observed turn response archiving › tags a tool-only turn so the judge skips it instead of scoring silence13 ms
  • ✓observed turn response archiving › keeps each turn's reply on its own turn across a multi-turn task18 ms
  • ✓observed turn response archiving › lands a late reply on the last closed turn without overwriting one10 ms
  • ✓observed turn response archiving › keeps the full OpenClaw final in one assistant slot (projection-first)14 ms
  • ✓observed turn response archiving › keeps the full OpenClaw final in one assistant slot (final-first)12 ms
  • ✓observed turn response archiving › retains a session-message projection when no chat final is observable10 ms
bridge/src/__tests__/apme-openai-model-safety.test.ts1/1
  • ✓an unconfigured OpenAI-compatible judge never loads the first of multiple downloads42 ms
bridge/src/__tests__/apme-pareto.test.ts5/5
  • ✓computePareto › keeps non-dominated points and drops dominated ones6 ms
  • ✓computePareto › sorts the frontier cheapest → most expensive1 ms
  • ✓computePareto › ignores models below minSamples0 ms
  • ✓ApmeRecommender on the Pareto frontier › recommends only frontier models, cheapest-first under a tight budget40 ms
  • ✓ApmeRecommender on the Pareto frontier › returns null for an unpriced model and excludes it from a dollar budget15 ms
bridge/src/__tests__/apme-prune.test.ts12/12
  • ✓APME retention — payload pruning (#302) › previewPrune (dry run) reports rows/bytes without changing anything35 ms
  • ✓APME retention — payload pruning (#302) › applyPrune replaces payloads only for rows strictly older than the cutoff21 ms
  • ✓APME retention — payload pruning (#302) › never touches a row whose age is unknown (ts=0)17 ms
  • ✓APME retention — payload pruning (#302) › never touches sample_events kinds other than tool, even when old15 ms
  • ✓APME retention — payload pruning (#302) › never deletes rows and never touches runs/tasks/turns/evals17 ms
  • ✓APME retention — payload pruning (#302) › is idempotent: a second apply over the same window reclaims nothing further13 ms
  • ✓APME retention — payload pruning (#302) › classifier.computeSignals treats a pruned Bash step as no signal, not a crash16 ms
  • ✓APME retention — payload pruning (#302) › a pruned tool call is read as "no content" by the judge trajectory renderer11 ms
  • ✓APME retention — payload pruning (#302) › the trajectory-churn scorer does not count two pruned calls as a duplicate12 ms
  • ✓APME retention — payload pruning (#302) › buildPrunedPayload / isPrunedPayload round-trip, and reject real content16 ms
  • ✓APME retention — payload pruning (#302) › vacuum() shrinks the on-disk file immediately, without requiring close() first33 ms
  • ✓APME retention — payload pruning (#302) › checkVacuumSpace gates on 1.1x the file size, using the injected probe10 ms
bridge/src/__tests__/apme-recommend-ordering.test.ts3/3
  • ✓cost-per-quality ordering › ranks unknown cost behind known-free and paid candidates4 ms
  • ✓cost-per-quality ordering › lets a known-free zero beat a real price0 ms
  • ✓cost-per-quality ordering › compares three-way so two unpriced keys never produce NaN1 ms
bridge/src/__tests__/apme-rehydrate.test.ts13/13
  • ✓collector rehydration after a restart › the next prompt continues the SAME run and task instead of opening a fresh run50 ms
  • ✓collector rehydration after a restart › re-arms the idle-gap timer for the REMAINDER of the gap, and closes the task as idle_gap21 ms
  • ✓collector rehydration after a restart › a gap that fully elapsed during the downtime closes on the next tick17 ms
  • ✓collector rehydration after a restart › an open turn is resumed with its tool counters recovered, and its late Stop closes it as `stop`18 ms
  • ✓collector rehydration after a restart › a codex turn whose rollout shows task_complete is closed as synthetic_stop with the reply18 ms
  • ✓collector rehydration after a restart › a codex turn with no completion in its rollout stays open — no evidence is not a verdict13 ms
  • ✓collector rehydration after a restart › a claude turn is never probed against a codex rollout13 ms
  • ✓collector rehydration after a restart › two open runs under one session resolve to the newest; the older stays for the reaper18 ms
  • ✓collector rehydration after a restart › a claude turn open at startup that its transcript says ended is closed at the transcript time, with its reply32 ms
  • ✓collector rehydration after a restart › a claude turn whose transcript tail is still mid-turn stays open20 ms
  • ✓collector rehydration after a restart › a codex turn whose rollout records a failure closes as aborted, not as a recovered Stop16 ms
  • ✓collector rehydration after a restart › releaseRun drops the adopted mapping so a later hook opens a fresh run10 ms
  • ✓collector rehydration after a restart › a run opened live before rehydrate is not displaced by a stale store row11 ms
bridge/src/__tests__/apme-rubric-migration.test.ts3/3
  • ✓general rubric missed-axis migration › fresh install seeds the clarified prompt directly36 ms
  • ✓general rubric missed-axis migration › upgrades a byte-identical legacy prompt on reopen, appending with parentVer23 ms
  • ✓general rubric missed-axis migration › leaves a user-edited general rubric alone18 ms
bridge/src/__tests__/apme-runner.test.ts50/50
  • ✓detectLanguage › recognizes TypeScript from package.json5 ms
  • ✓detectLanguage › recognizes Swift from .xcodeproj1 ms
  • ✓detectLanguage › recognizes Kotlin from build.gradle.kts1 ms
  • ✓detectLanguage › returns null for unknown paths0 ms
  • ✓parseJudgeJson › extracts scores and reasoning from a clean JSON blob2 ms
  • ✓parseJudgeJson › rescales 0-10 axis to 0-10 ms
  • ✓parseJudgeJson › tolerates prose wrapping and code fences0 ms
  • ✓parseJudgeJson › repairs trailing commas outside strings from local judge output2 ms
  • ✓parseJudgeJson › repairs a missing opening quote on an object key without changing string evidence1 ms
  • ✓parseJudgeJson › reads the verdict when the model keeps talking past it, braces included1 ms
  • ✓parseJudgeJson › does not let a brace inside a string close the object early0 ms
  • ✓parseJudgeJson › refuses to guess which verdict is the verdict: an unstripped thinking block0 ms
  • ✓parseJudgeJson › refuses to guess which verdict is the verdict: a draft above the final answer0 ms
  • ✓parseJudgeJson › ignores a non-verdict object beside the verdict1 ms
  • ✓parseJudgeJson › stamps the API judge with the model it will actually call1 ms
  • ✓parseJudgeJson › returns null when there is no JSON at all0 ms
  • ✓parseJudgeJson › returns null when overall is missing0 ms
  • ✓effectiveJudgeModelTag › uses cfg.model for MLX when no pin is set2 ms
  • ✓effectiveJudgeModelTag › uses llm.mlx pin over cfg.model for MLX backend1 ms
  • ✓effectiveJudgeModelTag › ignores llm.mlx pin for non-MLX backends1 ms
  • ✓effectiveJudgeModelTag › foundationModels uses the Swift-parity judgeModelLabel1 ms
  • ✓callJudge foundationModels routing › returns FM text on ok response38 ms
  • ✓callJudge foundationModels routing › throws when FM reports unavailable and fallback is disabled35 ms
  • ✓callJudge foundationModels routing › falls back to MLX when fallbackToMlx is true55 ms
  • ✓callJudge foundationModels routing › caches auto-resolved FM endpoint failure and routes subsequent calls straight to MLX28 ms
  • ✓callJudge foundationModels routing › compacts and retries once when MLX reports an exact context overflow24 ms
  • ✓shouldJudge gating › never runs when sampleRate is 00 ms
  • ✓shouldJudge gating › skips clear passes when onlyWhenDisagreement is true0 ms
  • ✓shouldJudge gating › runs on failures when onlyWhenDisagreement is true0 ms
  • ✓shouldJudge gating › runs for clear passes when onlyWhenDisagreement is false0 ms
  • ✓ApmeRunner duplicate guard › does not enqueue the same run while it is already queued or running17 ms
  • ✓ApmeRunner.runOne › records deterministic failures and invokes judge with rubric prompt47 ms
  • ✓ApmeRunner.runOne › skips layer 2 on a clean pass when onlyWhenDisagreement is true21 ms
  • ✓ApmeRunner.runOne › does not notify onResult for no-op run evals11 ms
  • ✓ApmeRunner.runOne › enqueueTurn skips judge for tool_only and empty turns35 ms
  • ✓ApmeRunner.runOne › swallows judge errors without inserting partial rows27 ms
  • ✓runDeterministic (end-to-end spawn) › runs sh-based commands against the project path and reports pass81 ms
  • ✓runDeterministic (end-to-end spawn) › captures exit code 1 as tests_pass=066 ms
  • ✓runDeterministic (end-to-end spawn) › skips entirely when the worktree has no changes54 ms
  • ✓onTaskEvaluated › fires after enqueueTask resolves and carries the composite score + outcome25 ms
  • ✓onTaskEvaluated › derives outcome=fail for low composite scores24 ms
  • ✓onTaskEvaluated › preserves a manually-set outcome — judge does not overwrite `abandoned`24 ms
  • ✓buildJudgePrompt › includes rubric prompt + task + context fields1 ms
  • ✓buildJudgePrompt › collects the diff with user diff drivers disabled (--no-ext-diff)70 ms
  • ✓judgeBackendSupported › darwin supports every backend1 ms
  • ✓judgeBackendSupported › darwin-only backends (foundationModels, mlx) are unsupported elsewhere0 ms
  • ✓judgeBackendSupported › portable backends stay supported off-darwin0 ms
  • ✓judge failure backoff › a failed judge blocks immediate re-enqueue and retries after the backoff expires20 ms
  • ✓judge failure backoff › a successful judge clears the failure state and persists evals29 ms
  • ✓judge failure backoff › parks the run after JUDGE_RETRY_MAX_ATTEMPTS failures until restart21 ms
bridge/src/__tests__/apme-sample.test.ts7/7
  • ✓ApmeStore sample events › round-trips typed trajectory events into a SessionSample35 ms
  • ✓ApmeStore sample events › dedups identical (task, dedupKey) at storage time14 ms
  • ✓ApmeStore sample events › resolves a pending tool event in place (one row, not two)13 ms
  • ✓ApmeStore sample events › recomputeSampleCost sums ModelEvents into the task header16 ms
  • ✓ApmeStore sample events › local models price at $0; unknown models fall back to $0 but flag unpriced14 ms
  • ✓ApmeCollector → SessionSample (Phase 1 dual-write) › builds a typed trajectory (user → tool pending→resolved → assistant) and cost25 ms
  • ✓ApmeCollector → SessionSample (Phase 1 dual-write) › fires onSampleEvent once per inserted event (no dup double-emit)16 ms
bridge/src/__tests__/apme-scorers.test.ts6/6
  • ✓TrajectoryQualityScorer › penalizes consecutive identical tool calls (churn)3 ms
  • ✓TrajectoryQualityScorer › penalizes tool errors0 ms
  • ✓TrajectoryQualityScorer › does not apply with fewer than 2 tools0 ms
  • ✓ToolEfficiencyScorer › is the success rate of resolved tools0 ms
  • ✓runSampleScorers › runs only applicable scorers and tags layer/scorer1 ms
  • ✓runSampleScorers › returns nothing for a toolless conversation sample0 ms
bridge/src/__tests__/apme-serving-integration.test.ts59/59
  • ✓Ollama-compatible task judge persistence and recovery › leaves no score after http failure and persists on recovery166 ms
  • ✓Ollama-compatible task judge persistence and recovery › leaves no score after json failure and persists on recovery129 ms
  • ✓Ollama-compatible task judge persistence and recovery › leaves no score after timeout failure and persists on recovery127 ms
  • ✓Ollama-compatible task judge persistence and recovery › leaves no score after length failure and persists on recovery117 ms
  • ✓Ollama-compatible task judge persistence and recovery › leaves no score after empty failure and persists on recovery118 ms
  • ✓Ollama-compatible task judge persistence and recovery › coalesces duplicate in-flight task requests117 ms
  • ✓incomplete judge response › mlx rejects a response cut mid-object5 ms
  • ✓incomplete judge response › openai rejects a response cut mid-object1 ms
  • ✓incomplete judge response › mlx rejects a cut that landed after the object closed1 ms
  • ✓incomplete judge response › openai rejects a cut that landed after the object closed0 ms
  • ✓incomplete judge response › mlx rejects choices that is not an array1 ms
  • ✓incomplete judge response › openai rejects choices that is not an array0 ms
  • ✓mlx shared response contract › 'complete'1 ms
  • ✓mlx shared response contract › 'legacy without finish reason'1 ms
  • ✓mlx shared response contract › 'complete JSON at output limit'1 ms
  • ✓mlx shared response contract › 'truncated JSON'1 ms
  • ✓mlx shared response contract › 'empty'1 ms
  • ✓mlx shared response contract › 'whitespace'1 ms
  • ✓mlx shared response contract › 'missing content'1 ms
  • ✓mlx shared response contract › 'non-string content'1 ms
  • ✓mlx shared response contract › 'no choices'1 ms
  • ✓mlx shared response contract › 'choices as an object map'1 ms
  • ✓mlx shared response contract › 'choices missing'1 ms
  • ✓mlx shared response contract › 'closed object at the output limit, th…'1 ms
  • ✓mlx shared response contract › 'brace inside a string does not close …'1 ms
  • ✓mlx shared response contract › 'closed object at the output limit tha…'1 ms
  • ✓mlx shared response contract › 'a closed scratchpad above a verdict t…'1 ms
  • ✓mlx shared response contract › 'a scratchpad above the real verdict'1 ms
  • ✓openai shared response contract › 'complete'0 ms
  • ✓openai shared response contract › 'legacy without finish reason'0 ms
  • ✓openai shared response contract › 'complete JSON at output limit'0 ms
  • ✓openai shared response contract › 'truncated JSON'0 ms
  • ✓openai shared response contract › 'empty'0 ms
  • ✓openai shared response contract › 'whitespace'0 ms
  • ✓openai shared response contract › 'missing content'1 ms
  • ✓openai shared response contract › 'non-string content'1 ms
  • ✓openai shared response contract › 'no choices'1 ms
  • ✓openai shared response contract › 'choices as an object map'0 ms
  • ✓openai shared response contract › 'choices missing'0 ms
  • ✓openai shared response contract › 'closed object at the output limit, th…'0 ms
  • ✓openai shared response contract › 'brace inside a string does not close …'0 ms
  • ✓openai shared response contract › 'closed object at the output limit tha…'0 ms
  • ✓openai shared response contract › 'a closed scratchpad above a verdict t…'0 ms
  • ✓openai shared response contract › 'a scratchpad above the real verdict'0 ms
  • ✓Anthropic API response contract › 'complete'0 ms
  • ✓Anthropic API response contract › 'thinking block before the verdict is …'0 ms
  • ✓Anthropic API response contract › 'cut after the object closed'0 ms
  • ✓Anthropic API response contract › 'cut mid-object'0 ms
  • ✓Anthropic API response contract › 'refusal'0 ms
  • ✓Anthropic API response contract › 'no text blocks'0 ms
  • ✓Anthropic API response contract › 'empty content array'0 ms
  • ✓Anthropic API response contract › 'content is not an array'0 ms
  • ✓Anthropic API response contract › 'content missing entirely'0 ms
  • ✓optional OpenAI-compatible reasoning control › sends only the requested effort: undefined1 ms
  • ✓optional OpenAI-compatible reasoning control › sends only the requested effort: none0 ms
  • ✓optional OpenAI-compatible reasoning control › sends only the requested effort: high0 ms
  • ✓task judge backlog drain › skips the parked head instead of spending the tick on it114 ms
  • ✓task judge backlog drain › still feeds one task per tick12 ms
  • ✓task judge backlog drain › feeds nothing when every candidate is parked, and says so once113 ms
bridge/src/__tests__/apme-settings.test.ts20/20
  • ✓loadApmeConfig — defaults + merge behaviour › validates optional reasoning effort none3 ms
  • ✓loadApmeConfig — defaults + merge behaviour › validates optional reasoning effort high0 ms
  • ✓loadApmeConfig — defaults + merge behaviour › validates optional reasoning effort unsupported0 ms
  • ✓loadApmeConfig — defaults + merge behaviour › validates optional reasoning effort null0 ms
  • ✓loadApmeConfig — defaults + merge behaviour › returns full defaults when settings.json does not exist1 ms
  • ✓loadApmeConfig — defaults + merge behaviour › returns full defaults when settings.json has no apme section1 ms
  • ✓loadApmeConfig — defaults + merge behaviour › returns defaults on malformed JSON (must not throw)1 ms
  • ✓loadApmeConfig — defaults + merge behaviour › merges user-set fields on top of defaults1 ms
  • ✓loadApmeConfig — defaults + merge behaviour › keeps the Foundation Models fallback for the default backend only1 ms
  • ✓loadApmeConfig — defaults + merge behaviour › restores the default chain when judge.backend is an unknown string1 ms
  • ✓loadApmeConfig — defaults + merge behaviour › clamps sampleRate to [0, 1]1 ms
  • ✓loadApmeConfig — defaults + merge behaviour › falls back to mlx when judge.backend is unknown string0 ms
  • ✓loadApmeConfig — defaults + merge behaviour › honours judge.backend="api" (opt-in Anthropic API judge) without downgrading1 ms
  • ✓loadApmeConfig — defaults + merge behaviour › also wipes endpoint/model when falling back from unknown backend → "mlx"1 ms
  • ✓loadApmeConfig — defaults + merge behaviour › preserves user-set endpoint/model when keeping their chosen backend1 ms
  • ✓loadApmeConfig — defaults + merge behaviour › respects explicit enabled=false (opt-out)1 ms
  • ✓loadApmeConfig — defaults + merge behaviour › clamps deterministic.timeoutSec to [5, 1800]1 ms
  • ✓loadApmeConfig — DEFAULT_APME_CONFIG sanity › default backend is local MLX with on-device Foundation Models fallback0 ms
  • ✓loadApmeConfig — DEFAULT_APME_CONFIG sanity › default sampleRate is 1.0 (judge every closed run; cost is local)0 ms
  • ✓loadApmeConfig — DEFAULT_APME_CONFIG sanity › default enabled is true (zero-config activation)0 ms
bridge/src/__tests__/apme-stop-delivery.test.ts22/22
  • ✓turns.end_source › a real Stop closes the turn and tags it `stop`26 ms
  • ✓turns.end_source › a watchdog-injected Stop is tagged `synthetic_stop`, not `stop`14 ms
  • ✓turns.end_source › a turn still open when the next prompt arrives is tagged `next_prompt`13 ms
  • ✓turns.end_source › a duplicate/late Stop cannot re-attribute an already-closed turn10 ms
  • ✓turns.end_source › `ended_at` is stamped at the Stop, not at the following prompt10 ms
  • ✓turns.end_source › a reaped turn lands in `runClose`, not `sessionEnd`, and stays out of the loss ratio12 ms
  • ✓turns.end_source › closing the run tags the open turn `session_end`, `/clear` tags it `clear`19 ms
  • ✓turns.end_source › a cancel is tagged `interrupted`, even though it arrives as a synthetic Stop12 ms
  • ✓turns.end_source › a client abort is tagged `aborted`, even though it arrives as a synthetic Stop11 ms
  • ✓turns.end_source › a cancel outranks an abort when both flags somehow arrive9 ms
  • ✓turns.end_source › a displaced turn whose transcript shows a cancel is `interrupted`, not `next_prompt`12 ms
  • ✓turns.end_source › a displaced turn whose transcript shows a client abort is `aborted`13 ms
  • ✓turns.end_source › a displaced turn the assistant never answered is `superseded`, not a lost Stop10 ms
  • ✓turns.end_source › a displaced turn that called tools is never `superseded`, whatever the transcript says10 ms
  • ✓turns.end_source › no evidence in the window still means `next_prompt` — absence is never a verdict11 ms
  • ✓turns.end_source › never reads a transcript for a non-Claude agent or a pathless hook17 ms
  • ✓turns.end_source › an unreadable transcript falls back to `next_prompt` rather than guessing15 ms
  • ✓turns.end_source › the response still lands on the turn when the Stop closes it first15 ms
  • ✓ApmeStore.stopDelivery › buckets each closing signal and keeps open turns in the denominator12 ms
  • ✓ApmeStore.stopDelivery › counts cancels in their own column, outside every loss bucket10 ms
  • ✓ApmeStore.stopDelivery › reports pre-instrument rows separately instead of guessing a bucket11 ms
  • ✓ApmeStore.stopDelivery › windows on started_at and can filter to one agent11 ms
bridge/src/__tests__/apme-subagent-sample.test.ts2/2
  • ✓subagent census → SessionSample producer › records lifecycle evidence, preserves it through model updates, and exposes rollup + graph nodes51 ms
  • ✓subagent census → SessionSample producer › refuses to guess a parent task edge when no task is active22 ms
bridge/src/__tests__/apme-task-boundary.test.ts16/16
  • ✓ApmeCollector task boundaries › first UserPromptSubmit opens a task and attaches the turn31 ms
  • ✓ApmeCollector task boundaries › TodoWrite all-completed is a soft hint — does NOT close the task22 ms
  • ✓ApmeCollector task boundaries › TodoWrite all-completed does not split — later turns stay in one task23 ms
  • ✓ApmeCollector task boundaries › TodoWrite with partial completion does NOT close the task15 ms
  • ✓ApmeCollector task boundaries › next UserPromptSubmit after an explicit boundary opens a new task22 ms
  • ✓ApmeCollector task boundaries › splitRun closes the active task with boundary=clear25 ms
  • ✓ApmeCollector task boundaries › closeRun closes the active task with boundary=session_end27 ms
  • ✓ApmeCollector task boundaries › onTaskClosed fires on an explicit boundary with the task metadata21 ms
  • ✓ApmeCollector task boundaries › onTaskOpened is DEFERRED: fires on the second turn, not the first20 ms
  • ✓ApmeCollector task boundaries › onTaskOpened also promotes on a TodoWrite plan within the first turn14 ms
  • ✓ApmeCollector task boundaries › groups follow-up prompts under one header; single-turn stays headerless28 ms
  • ✓ApmeCollector task boundaries › onTaskClosed payload includes session, agent, project, and timing14 ms
  • ✓ApmeCollector task boundaries › empty task (no turns between two boundaries) is dropped24 ms
  • ✓ApmeRunner task eval › enqueueTask invokes judge with turns and persists summary + scores35 ms
  • ✓ApmeRunner task eval › skips task eval when all turns are tool_only / empty33 ms
  • ✓ApmeRunner task judge parking › a task parked after two failures is offered again once the park expires41 ms
bridge/src/__tests__/apme-task-gradeability.test.ts26/26
  • ✓taskGradeability › a task with no agent reply and no real tool trajectory is not gradeable4 ms
  • ✓taskGradeability › a replyless task whose tool trajectory is the agent's work is gradeable1 ms
  • ✓taskGradeability › a task whose every turn was ended by the client is not gradeable, whatever text the notice carried0 ms
  • ✓taskGradeability › an aborted turn beside a real one does not block the judge, and its notice is not the reply0 ms
  • ✓taskGradeability › a single tool-less exchange short enough to be a greeting is trivial1 ms
  • ✓taskGradeability › honours the collector's response_kind tag over raw text0 ms
  • ✓taskGradeability › reads the reason back off notes_json and ignores judge notes1 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › has enough vectors to be a gate0 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › an empty task has no agent work0 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › a thin replyless trajectory is not enough evidence0 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › three tool calls are independently gradeable work0 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › a file write is independently gradeable work0 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › an abort notice and aborted tools are not the agent's work0 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › an aborted turn is ignored beside a thin real turn0 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › a real turn beside an aborted one remains gradeable0 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › a short tool-less greeting is not a task0 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › a substantial answer to a short question is gradeable0 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › the shortest measured real instruction clears the prompt cap0 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › even a short exchange is work when it used a tool0 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › the collector response-kind tag outranks leftover raw text0 ms
  • ✓shared vector file parity (shared/task-gradeability-vectors.json) › length parity uses JavaScript UTF-16 code units0 ms
  • ✓runner declines what it cannot grade › does not call the judge for a task with no reply, and stamps the reason on the row40 ms
  • ✓runner declines what it cannot grade › judges a task that has a reply, inheriting the run category when the task row has none23 ms
  • ✓judge backlog drain › offers closed unjudged tasks inside the window, never declined ones16 ms
  • ✓judge backlog drain › a task whose judge keeps failing is offered twice per process, then left alone18 ms
  • ✓retractUngradeableVerdicts › withdraws verdicts reached without the agent's work and keeps the rest18 ms
bridge/src/__tests__/apme-task-milestone.test.ts2/2
  • ✓TodoWrite-all-completed → onTaskMilestone › fires once per (task, turn) with attribution; not on partial todos40 ms
  • ✓opencodeIdleGapTaskBoundary › builds a task_boundary span with the idle_gap signal1 ms
bridge/src/__tests__/apme-task-page-and-graph.test.ts17/17
  • ✓task page › carries the run context a bare task row lacks25 ms
  • ✓task page › counts turns whose reply was never archived14 ms
  • ✓task page › pages without losing the unfiltered total12 ms
  • ✓task page › filters by native session, agent, project and open/closed state17 ms
  • ✓task page › hides bookkeeping shells the collector tagged _empty10 ms
  • ✓task page › offers facets from the whole store, not the current page10 ms
  • ✓tool payload path extraction › reads a path from the tool input, wherever the shape puts it0 ms
  • ✓tool payload path extraction › returns null rather than inventing a path0 ms
  • ✓tool payload path extraction › keys a file on its path relative to the project root0 ms
  • ✓tool payload path extraction › falls back to the path tail without claiming two paths are one file0 ms
  • ✓graph projection › projects the containment spine and the derived entity hubs14 ms
  • ✓graph projection › connects two tasks that share only a file — the cross-link no ancestor gives13 ms
  • ✓graph projection › collapses repeated calls into one weighted edge12 ms
  • ✓graph projection › draws the /clear continuation edge only when both runs are in the slice15 ms
  • ✓graph projection › prunes one-off hubs but never containment nodes11 ms
  • ✓graph projection › reports what it left out so a partial slice never reads as complete17 ms
  • ✓graph projection › attaches trajectory to turns when asked, and to the task when not11 ms
bridge/src/__tests__/apme-telemetry-envelope.test.ts33/33
  • ✓claudeHookToSpans › emits turn_start with prompt for UserPromptSubmit4 ms
  • ✓claudeHookToSpans › detects /clear and emits a task_boundary span (not turn_start)1 ms
  • ✓claudeHookToSpans › drops Claude task notification payloads from UserPromptSubmit1 ms
  • ✓claudeHookToSpans › emits tool_call for PreToolUse with gen_ai.tool.name1 ms
  • ✓claudeHookToSpans › emits tool_result for PostToolUse0 ms
  • ✓claudeHookToSpans › falls through to raw_step for unknown events (Stop, SessionEnd, …)0 ms
  • ✓claudeHookToSpans › handles legacy { prompt: ... } shape on UserPromptSubmit0 ms
  • ✓timelineEntryToSpans › translates chat_start to turn_start with detail as prompt0 ms
  • ✓timelineEntryToSpans › translates chat_response to turn_response0 ms
  • ✓timelineEntryToSpans › translates chat_end with response detail to turn_response with fallback flag0 ms
  • ✓timelineEntryToSpans › drops chat_response / chat_end with empty body0 ms
  • ✓timelineEntryToSpans › translates tool_request with first-token tool name0 ms
  • ✓timelineEntryToSpans › codex_user_prompt_submit → turn_start span6 ms
  • ✓timelineEntryToSpans › codex_user_prompt_submit with /clear → task_boundary span0 ms
  • ✓timelineEntryToSpans › codex_tool_start → tool_call span with tool name0 ms
  • ✓timelineEntryToSpans › codex_tool_end → tool_result span0 ms
  • ✓timelineEntryToSpans › codex_stop / codex_session_start / codex_turn_complete → raw_step1 ms
  • ✓timelineEntryToSpans › translates tool_exec the same as tool_request (legacy + Codex paths)0 ms
  • ✓timelineEntryToSpans › translates tool_resolved to a tool_result span0 ms
  • ✓ApmeCollector.ingestSpan dispatch › turn_start span opens a turn and records the prompt35 ms
  • ✓ApmeCollector.ingestSpan dispatch › turn_response span persists the response on the active turn22 ms
  • ✓ApmeCollector.ingestSpan dispatch › tool_call span increments tool_calls on the active turn29 ms
  • ✓ApmeCollector.ingestSpan dispatch › chat_end fallback does not clobber a prior closed turn response32 ms
  • ✓ApmeCollector.ingestSpan dispatch › Codex timeline path opens turn and counts tools (timelineEntryToSpans)21 ms
  • ✓ApmeCollector.ingestSpan dispatch › task_boundary span with signal=clear splits the run25 ms
  • ✓ApmeCollector.ingestSpan dispatch › task_boundary span with signal=manual closes the active task25 ms
  • ✓ApmeCollector.ingestSpan dispatch › task_boundary span with signal=idle_gap closes the active task25 ms
  • ✓ApmeCollector.ingestSpan dispatch › task_boundary span with signal=todo_complete is a soft hint — does NOT close the task28 ms
  • ✓ApmeCollector.ingestSpan dispatch › task_boundary span with an unknown signal is dropped (no task close, no throw)25 ms
  • ✓ApmeCollector.ingestSpan dispatch › raw_step span inserts a steps row without lifecycle effects27 ms
  • ✓ApmeCollector.ingestSpan dispatch › session_meta span updates the model id on the run18 ms
  • ✓ApmeCollector.ingestSpan dispatch › timeline adapter feeds the same dispatch path correctly25 ms
  • ✓eval-schema constants › exports a stable schema version string0 ms
bridge/src/__tests__/apme-turn-identity.test.ts3/3
  • ✓updateModel stamps the turn, not just the run › the active turn carries the model and its normalized provider40 ms
  • ✓updateModel stamps the turn, not just the run › a model learned at session_end, after the last turn closed, lands on that turn10 ms
  • ✓readModelFromTranscript › skips the <synthetic> model Claude Code stamps on client abort notices1 ms
bridge/src/__tests__/apme-work-board-plan.test.ts2/2
  • ✓Work board page query plan › serves the per-task tool count from a (task_id, kind) covering index, never from the kind index23 ms
  • ✓Work board page query plan › keeps the prune index — the fix is an additional index, not the removal of the #302 one10 ms
bridge/src/__tests__/fm-helper-arch.test.ts10/10
  • ✓machoMatchesArch › accepts a thin binary matching the requested arch3 ms
  • ✓machoMatchesArch › rejects a thin binary built for the other CPU0 ms
  • ✓machoMatchesArch › accepts a fat binary that carries a native slice0 ms
  • ✓machoMatchesArch › rejects a fat binary with no native slice0 ms
  • ✓machoMatchesArch › rejects non-Mach-O and truncated input0 ms
  • ✓machoMatchesArch › rejects a truncated fat header instead of over-reading0 ms
  • ✓machoMatchesArch › rejects unknown architectures outright0 ms
  • ✓npm tarball determinism › never ships a baked helper binary1 ms
  • ✓npm tarball determinism › has no prepack hook baking machine-dependent artifacts0 ms
  • ✓npm tarball determinism › ships the helper source and build script for on-demand compilation0 ms
bridge/src/__tests__/fm-helper-build.test.ts5/5
  • ✓fm-helper build invariants › declares a non-empty NSMicrophoneUsageDescription2 ms
  • ✓fm-helper build invariants › declares a non-empty NSSpeechRecognitionUsageDescription0 ms
  • ✓fm-helper build invariants › embeds the plist into the __TEXT segment at link time0 ms
  • ✓fm-helper build invariants › ad-hoc signs with the identifier the plist declares0 ms
  • ✓fm-helper build invariants › exposes instruction-less eval sessions and the native output limit1 ms
  • ○fm-helper build invariants › ships a binary carrying the usage strings
  • ○fm-helper build invariants › produces a signed binary macOS can prompt for
bridge/src/__tests__/fm-helper-speech-worker.test.ts3/3
  • ✓speech worker isolation and recovery › synthesizes while a model request is still blocked8 ms
  • ✓speech worker isolation and recovery › kills only a stuck speech worker and ignores its late exit after replacement6 ms
  • ✓speech worker isolation and recovery › stops and rejects both workers on daemon shutdown1 ms
bridge/src/__tests__/ollama-residency.test.ts9/9
  • ✓separates installed models from verified zero resident models without changing legacy fields39 ms
  • ✓CPU-only and embedding models in ps are resident even at zero GPU bytes1 ms
  • ✓failed or malformed ps never means unloaded: null1 ms
  • ✓failed or malformed ps never means unloaded: {}1 ms
  • ✓failed or malformed ps never means unloaded: {"models":[{"unknown":true}]}1 ms
  • ✓a failed catalog does not hide CPU residency or claim an installed inventory1 ms
  • ✓MLX only explicit resident metadata establishes residency1 ms
  • ✓MLX only explicit resident metadata establishes residency1 ms
  • ✓MLX only explicit resident metadata establishes residency1 ms
bridge/src/__tests__/review-runner.test.ts15/15
  • ✓renderReviewHtml › is self-contained (no external asset URLs) and shows risk + findings3 ms
  • ✓renderReviewHtml › escapes HTML in judge-provided text (no injection)0 ms
  • ✓renderReviewHtml › empty findings renders a calm "nothing worth flagging" note, not an empty grid0 ms
  • ✓judgeTierNote — honest capability labelling › flags on-device models as basic screening0 ms
  • ✓judgeTierNote — honest capability labelling › flags local MLX depth as model-size dependent0 ms
  • ✓judgeTierNote — honest capability labelling › does not append a caveat for frontier backends0 ms
  • ✓renderJudgeGuidanceHtml — no-judge setup flow › ranks API → OpenRouter → OpenClaw → local → Apple, and states the local model minimum1 ms
  • ✓renderJudgeGuidanceHtml — no-judge setup flow › reassures that not using REVIEW is a supported choice (no background nagging)0 ms
  • ✓renderJudgeGuidanceHtml — no-judge setup flow › surfaces the current backend + reason so the user knows what failed0 ms
  • ✓openAIChatUrl — endpoint normalization (Ollama / OpenRouter / LM Studio) › bare host → /v1/chat/completions0 ms
  • ✓openAIChatUrl — endpoint normalization (Ollama / OpenRouter / LM Studio) › base with /v1 → appends chat/completions0 ms
  • ✓openAIChatUrl — endpoint normalization (Ollama / OpenRouter / LM Studio) › already-full URL is preserved0 ms
  • ✓openAIChatUrl — endpoint normalization (Ollama / OpenRouter / LM Studio) › trailing slash tolerated0 ms
  • ✓renderJudgeGuidanceHtml — detected local providers › surfaces detected servers with a ready-to-paste openai config0 ms
  • ✓renderJudgeGuidanceHtml — detected local providers › omits the detected block when nothing is running0 ms
scripts/__tests__/apme-serving-evidence.test.ts12/12
  • ✓private replay evidence boundaries › preserves interrupted attempts instead of silently rerunning them6 ms
  • ✓private replay evidence boundaries › requires an explicit fixture root0 ms
  • ✓private replay evidence boundaries › rejects unsafe profile/case "../live"0 ms
  • ✓private replay evidence boundaries › rejects unsafe profile/case "/tmp/escape"0 ms
  • ✓private replay evidence boundaries › rejects unsafe profile/case ".."0 ms
  • ✓private replay evidence boundaries › rejects unsafe profile/case "."0 ms
  • ✓private replay evidence boundaries › rejects unsafe profile/case "settings"0 ms
  • ✓private replay evidence boundaries › rejects unsafe profile/case "a/b"0 ms
  • ✓private replay evidence boundaries › rejects unsafe profile/case "a\\b"0 ms
  • ✓private replay evidence boundaries › rejects unsafe profile/case ""0 ms
  • ✓private replay evidence boundaries › rejects live data directories, descendants and symlink aliases2 ms
  • ✓private replay evidence boundaries › rejects output symlinks and accepts an isolated profile1 ms
shared/src/__tests__/apme-classifier-rules-sync.test.ts6/6
  • ✓generated mirror in sync › apple/AgentDeck/Daemon/Apme/ApmeClassifierRules.generated.swift matches the SSOT5 ms
  • ✓generated mirror in sync › the Swift mirror carries every label the TS union declares1 ms
  • ✓generated mirror in sync › neither side ever offers a paid backend in the classifier try-order2 ms
  • ✓generated mirror in sync › `rules` is always the last entry — it is a give-up, not a network call0 ms
  • ✓generated mirror in sync › the output cap stays far below the eval judge budget (a different call, a different purpose)0 ms
  • ✓generated mirror in sync › normalizeClassifierLabel and the Swift mirror agree on exact + partial match1 ms
shared/src/__tests__/apme-display-rules-sync.test.ts7/7
  • ✓generated APME display-rule mirrors in sync › apple/AgentDeck/Daemon/Apme/TaskTitleRules.generated.swift matches the SSOT3 ms
  • ✓generated APME display-rule mirrors in sync › apple/AgentDeck/Daemon/Apme/ActionFoldRules.generated.swift matches the SSOT1 ms
  • ✓generated APME display-rule mirrors in sync › apple/AgentDeck/Daemon/Apme/TaskGradeabilityRules.generated.swift matches the SSOT0 ms
  • ✓generated APME display-rule mirrors in sync › emitters embed the SSOT constants (sanity on the emitters themselves)1 ms
  • ✓generated APME display-rule mirrors in sync › the Swift suite replays all shared vector files (grep the test wiring)1 ms
  • ✓Swift daemon still mirrors the hand-pinned APME constants › idle-gap boundary matches AGENT_IDLE_GAP_MS (Swift keeps seconds)3 ms
  • ✓Swift daemon still mirrors the hand-pinned APME constants › attention window and red band match the eval-schema SSOT1 ms
shared/src/__tests__/llm-settings.test.ts16/16
  • ✓llm-settings › returns defaults when settings.json is missing5 ms
  • ✓llm-settings › reads llm.mlx pin from settings.json1 ms
  • ✓llm-settings › treats "qwen3-30b" and "default" as placeholders (unpinned)1 ms
  • ✓llm-settings › falls back to apme.judge.model when llm.mlx is absent1 ms
  • ✓llm-settings › strips /chat/completions suffix from legacy endpoints1 ms
  • ✓llm-settings › prefers llm.mlx over apme.judge when both set0 ms
  • ✓llm-settings › resolveMlxModel: refuses to invent a model2 ms
  • ✓llm-settings › pickMlxModel requires a singleton catalog and matching pin1 ms
  • ✓llm-settings › never inherits openai credentials routing into MLX1 ms
  • ✓llm-settings › never inherits api credentials routing into MLX0 ms
  • ✓llm-settings › never inherits foundationModels credentials routing into MLX0 ms
  • ✓llm-settings › never inherits openclaw credentials routing into MLX0 ms
  • ✓llm-settings › an explicit default MLX endpoint is not overridden by a legacy custom endpoint0 ms
  • ✓llm-settings › does not redirect an invalid explicit endpoint to a running loopback server1 ms
  • ✓llm-settings › mlxChatUrl reflects endpoint setting0 ms
  • ✓llm-settings › caches result for TTL window1 ms
shared/src/__tests__/mlx-safety.test.ts19/19
  • ✓MLX operational safety › download catalog cannot pick a model3 ms
  • ✓MLX operational safety › pin does not establish residency in a multi-model catalog0 ms
  • ✓MLX operational safety › resident model outranks catalog order0 ms
  • ✓MLX operational safety › pin cannot switch resident model0 ms
  • ✓MLX operational safety › explicitly unloaded is not catalog-only0 ms
  • ✓MLX operational safety › legacy single-model server0 ms
  • ✓MLX operational safety › legacy mismatch0 ms
  • ✓MLX operational safety › no hardcoded model fallback0 ms
  • ✓MLX operational safety › duplicate names are one model0 ms
  • ✓MLX operational safety › vision-only model is not silently replaced0 ms
  • ✓MLX operational safety › keeps the generated Swift policy synchronized0 ms
  • ✓MLX operational safety › canonicalizes base, v1 and chat URLs to one admission key1 ms
  • ✓MLX operational safety › refuses a pinned model mismatch before any inference30 ms
  • ✓MLX operational safety › does not interpret healthy as recovered after OOM1 ms
  • ✓MLX operational safety › refuses external in-flight requests and queued generation0 ms
  • ✓MLX operational safety › holds admission until response body completion, including aliases3 ms
  • ✓MLX operational safety › quarantines timed out generation across callers without sending another POST1 ms
  • ✓MLX operational safety › allows field-rejection retries but quarantines 5002 ms
  • ✓MLX operational safety › catalog-only multi-model servers never receive an auto-selected model1 ms
shared/src/__tests__/mlx-server-guard.test.ts1/1
  • ✓operator server guard rejects replacement and exits on allocator failure97 ms
shared/src/__tests__/model-provider.test.ts9/9
  • ✓modelProvider › names the provider behind every id shape the wire actually carries4 ms
  • ✓modelProvider › answers unknown rather than guessing — the polarity is the point1 ms
  • ✓normalizeModelProvider › normalizes the provider field before falling back to the model id1 ms
  • ✓offHarnessProvider › badges the real case: Claude Code answering from z.ai0 ms
  • ✓offHarnessProvider › says nothing when the harness is where it belongs0 ms
  • ✓offHarnessProvider › never turns two unknowns into a claim0 ms
  • ✓offHarnessProvider › leaves multi-provider harnesses alone1 ms
  • ✓generated mirrors › are in sync with this source3 ms
  • ✓generated mirrors › carry every provider, so no mirror can silently drop one2 ms
shared/src/__tests__/pricing.test.ts11/11
  • ✓isLocalModel › accepts the judge tag the runner emits for Foundation Models2 ms
  • ✓isLocalModel › accepts the bare Foundation Models spellings0 ms
  • ✓isLocalModel › accepts raw ids as a local server advertises them0 ms
  • ✓isLocalModel › accepts the explicit local prefixes0 ms
  • ✓isLocalModel › does not claim hosted models are local0 ms
  • ✓local models cost nothing and say so › prices local judge tags at $00 ms
  • ✓local models cost nothing and say so › reports them as priced, not as unpriced0 ms
  • ✓local models cost nothing and say so › groups them under the local provider0 ms
  • ✓normalizeModelId › strips hosted provider prefixes0 ms
  • ✓normalizeModelId › keeps the org segment of a local id0 ms
  • ✓normalizeModelId › still prices a hosted model after normalization0 ms

20 files

Pass

OpenClaw Gateway

Does the Gateway proxy stay subscribed, attribute runs and approvals, and report its health truthfully?

Passed
279
Failed
0
Skipped
0
Time
591 ms
20 test files
bridge/src/__tests__/gateway-health.test.ts3/3
  • ✓resolveGatewayHealth (shared vectors) › reads every measured payload shape the same way the Swift daemon does4 ms
  • ✓resolveGatewayHealth (shared vectors) › never claims an error from a frame that did not say1 ms
  • ✓resolveGatewayHealth (shared vectors) › keeps the caller on its previous value when the frame is unreadable0 ms
bridge/src/__tests__/gateway-parity-fixtures.test.ts56/56
  • ✓Gateway parity fixtures › fixture set is non-empty3 ms
  • ✓Gateway parity fixtures › 'auth-pairing-required-error.json' carries a valid frame discriminator1 ms
  • ✓Gateway parity fixtures › 'chat-delta.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'chat-final.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'connect-challenge.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'connect-hello-ok-device-token.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'connect-ok.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'exec-approval-requested.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'health-event.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'logs-tail-response.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'models-list-response.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'plugin-approval-removed.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'plugin-approval-requested.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'plugin-approval-resolved.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'rpc-error.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'session-message-assistant-text.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'session-message-assistant-toolcall.js…' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'session-message-user.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'session-tool-result.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'session-tool-start.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'sessions-changed.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'tick.json' carries a valid frame discriminator0 ms
  • ✓Gateway parity fixtures › 'auth-pairing-required-error.json' conforms to its frame shape1 ms
  • ✓Gateway parity fixtures › 'chat-delta.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'chat-final.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'connect-challenge.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'connect-hello-ok-device-token.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'connect-ok.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'exec-approval-requested.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'health-event.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'logs-tail-response.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'models-list-response.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'plugin-approval-removed.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'plugin-approval-requested.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'plugin-approval-resolved.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'rpc-error.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'session-message-assistant-text.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'session-message-assistant-toolcall.js…' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'session-message-user.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'session-tool-result.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'session-tool-start.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'sessions-changed.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › 'tick.json' conforms to its frame shape0 ms
  • ✓Gateway parity fixtures › the chat frame is assistant-only — it carries no prompt and no tools2 ms
  • ✓Gateway parity fixtures › chat final still yields a turn_response once the adapter fills `response`3 ms
  • ✓Gateway parity fixtures › session.message(user) yields the turn_start nothing else can provide1 ms
  • ✓Gateway parity fixtures › a session.message is stamped from the message, not from delivery time0 ms
  • ✓Gateway parity fixtures › session.message(assistant text) yields per-turn model/provider/usage + response1 ms
  • ✓Gateway parity fixtures › toolCall blocks in an assistant message are not counted as tools0 ms
  • ✓Gateway parity fixtures › session.tool start/result pair into tool_call / tool_result on one call id1 ms
  • ✓Gateway parity fixtures › the session.tool facts live under `data`, never at the top level2 ms
  • ✓Gateway parity fixtures › an unrecognized tool phase stays pending rather than inventing a completion0 ms
  • ✓Gateway parity fixtures › exec.approval.requested parses into an answerable prompt1 ms
  • ✓Gateway parity fixtures › plugin.approval.requested parses into an answerable prompt, title as the headline1 ms
  • ✓Gateway parity fixtures › plugin.approval.resolved reports the decision, sharing execApprovalAllows's polarity0 ms
  • ✓Gateway parity fixtures › plugin.approval.removed carries only an id — no decision, never a resolve0 ms
bridge/src/__tests__/gateway-probe-verdict.test.ts6/6
  • ✓checkGatewayHealth verdict › reports healthy only when doctor actually exited 014 ms
  • ✓checkGatewayHealth verdict › reports broken on a real non-zero exit1 ms
  • ✓checkGatewayHealth verdict › reports unknown, not healthy, when openclaw is not on PATH1 ms
  • ✓checkGatewayHealth verdict › reports unknown, not broken, when doctor is killed at the timeout1 ms
  • ✓checkGatewayHealth verdict › reports unknown when the spawn fails some other way1 ms
  • ✓checkGatewayHealth verdict › never reports an error alongside an unknown verdict2 ms
bridge/src/__tests__/openclaw-adapter-catalog.test.ts7/7
  • ✓OpenClawAdapter catalog fetch › reads models.list over the socket first and emits the joined keys plus the default model7 ms
  • ✓OpenClawAdapter catalog fetch › a Gateway that advertised its methods without models.list is not asked; the CLI answers instead1 ms
  • ✓OpenClawAdapter catalog fetch › a fetch that started before the link dropped emits nothing, even if it resolves after a reconnect1 ms
  • ✓OpenClawAdapter catalog fetch › a stale FAILURE schedules no retry on the new link either0 ms
  • ✓OpenClawAdapter catalog fetch › shutdown mid-fetch: a CLI fallback that resolves after shutdown emits nothing1 ms
  • ✓OpenClawAdapter catalog fetch › climbs the retry ladder while both sources fail, then emits and resets on the first success3 ms
  • ✓OpenClawAdapter catalog fetch › stops retrying once the adapter is no longer alive1 ms
bridge/src/__tests__/openclaw-approval-flow.test.ts26/26
  • ✓the user can see what they are approving › puts the real command on the timeline row and the prompt8 ms
  • ✓the user can see what they are approving › exposes the prompt for the session row1 ms
  • ✓the user can actually resolve it › sends a decision the Gateway accepts — never the bare "allow"3 ms
  • ✓the user can actually resolve it › maps each option index to its own decision1 ms
  • ✓the user can actually resolve it › resolves a shortcut `respond` press too0 ms
  • ✓the user can actually resolve it › drops a press whose question echo names a different approval2 ms
  • ✓the user can actually resolve it › accepts a truncated echo — small surfaces cut the question0 ms
  • ✓the user can actually resolve it › keeps the prompt on screen when the resolve fails1 ms
  • ✓the prompt goes away when the approval does › labels a real allow as approved (not denied)1 ms
  • ✓the prompt goes away when the approval does › a deny ends the turn rather than resuming it0 ms
  • ✓the prompt goes away when the approval does › closes the approval when the run is cancelled under it2 ms
  • ✓the prompt goes away when the approval does › closes the approval when the turn errors out1 ms
  • ✓the prompt goes away when the approval does › adopts an approval that was already waiting when the adapter connected1 ms
  • ✓the prompt goes away when the approval does › adopts the OLDEST waiting approval — that is the one blocking the agent0 ms
  • ✓the prompt goes away when the approval does › a resolution for a different approval does not clear the pending one0 ms
  • ✓a non-decision is not a denial › a real deny stays denied0 ms
  • ✓a non-decision is not a denial › every abandonment reason lands in `abandoned`, and names itself in raw1 ms
  • ✓a non-decision is not a denial › names WHICH OpenClaw session asked, falling back to the tracked one0 ms
  • ✓a non-decision is not a denial › does not overwrite a sessionKey the request carried itself0 ms
  • ✓an approval the Gateway has already dropped comes off the deck › abandons the prompt when the resolve says the approval is gone1 ms
  • ✓an approval the Gateway has already dropped comes off the deck › keeps the prompt when the resolve merely failed to get through1 ms
  • ✓an approval the Gateway has already dropped comes off the deck › abandons a prompt the Gateway no longer lists1 ms
  • ✓an approval the Gateway has already dropped comes off the deck › keeps a prompt that is still pending0 ms
  • ✓an approval the Gateway has already dropped comes off the deck › reads a boxed { approvals: [...] } answer too0 ms
  • ✓an approval the Gateway has already dropped comes off the deck › a failed or unreadable list answer changes nothing1 ms
  • ✓an approval the Gateway has already dropped comes off the deck › does not ask a socket that is not open0 ms
bridge/src/__tests__/openclaw-chat-error.test.ts6/6
  • ✓OpenClaw chat error → timeline row › keeps the provider sentence as the row label and puts the turn context in detail9 ms
  • ✓OpenClaw chat error → timeline row › flags an errored gateway-initiated turn automated, like every other close row1 ms
  • ✓OpenClaw chat error → timeline row › describes a message-less error frame instead of writing the word "unknown"0 ms
  • ✓OpenClaw chat error → APME lifecycle › records the failure and re-arms the idle-gap timer so the task can still close2 ms
  • ✓OpenClaw chat error → APME lifecycle › opens no run for a key whose frames record nothing3 ms
  • ✓OpenClaw chat error → APME lifecycle › scopes the run — and the idle-gap timer — per Gateway session key2 ms
bridge/src/__tests__/openclaw-empty-run.test.ts5/5
  • ✓OpenClaw APME run opening › an assistant message alone opens no run for its session key10 ms
  • ✓OpenClaw APME run opening › the user message that starts the turn is what opens it, and later assistant messages then land5 ms
  • ✓OpenClaw APME run opening › a tool span opens a run — it inserts a step row with no turn open1 ms
  • ✓OpenClaw APME run opening › the connection-scoped fallback run is opened by the same rule, not at connect1 ms
  • ✓ApmeCollector.spanCanOpenRun › is true exactly for the kinds whose ingestSpan branch reaches ingestHook1 ms
bridge/src/__tests__/openclaw-gateway-stability.test.ts10/10
  • ✓GatewayFlapTracker › is quiet below the threshold and trips exactly at it2 ms
  • ✓GatewayFlapTracker › forgets flaps older than the window, so one deliberate restart an hour never accumulates0 ms
  • ✓GatewayFlapTracker › matches the measured storm: eleven disconnects inside ninety minutes trip within the first four0 ms
  • ✓diagnoseGatewayLog › returns null on a clean tail — a storm without a reason is reported as exactly that1 ms
  • ✓diagnoseGatewayLog › prefers a structural cause over the routine restart it produced, whichever is newer0 ms
  • ✓diagnoseGatewayLog › ranks restart-loop above migration above config above SIGTERM0 ms
  • ✓diagnoseGatewayLog › carries the most recent line of the winning kind0 ms
  • ✓formatGatewayInstability › fits a timeline row and names the cause0 ms
  • ✓formatGatewayInstability › says so when the log had no reason0 ms
  • ✓formatGatewayInstability › describes every kind0 ms
bridge/src/__tests__/openclaw-gateway-wedge.test.ts3/3
  • ✓OpenClaw adapter without autoReconnect › reports exit when the Gateway closes the socket before the handshake31 ms
  • ✓OpenClaw adapter without autoReconnect › does not report exit when it reconnects by itself154 ms
  • ✓BridgeCore Gateway probe › calls onAvailable on every tick the port stays open, not only on the rising edge64 ms
bridge/src/__tests__/openclaw-hook.test.ts12/12
  • ✓openclaw-hook → telemetry spans › OPENCLAW_IDLE_GAP_MS is conservative (60–180 s) so multi-turn collab stays together2 ms
  • ✓openclaw-hook → telemetry spans › chat.send produces exactly one turn_start span carrying the prompt text1 ms
  • ✓openclaw-hook → telemetry spans › chat.final with a response + tools yields turn_response + per-tool tool_result spans1 ms
  • ✓openclaw-hook → telemetry spans › chat.delta emits no spans — deltas are streaming chunks, not eval signals1 ms
  • ✓openclaw-hook → telemetry spans › chat.aborted emits a manual task_boundary so the user gesture closes the task immediately0 ms
  • ✓openclaw-hook → telemetry spans › chat.error emits an agent_error span — and NOT a task_boundary, the agent may retry0 ms
  • ✓openclaw-hook → telemetry spans › idle-gap task_boundary span carries boundary_signal=idle_gap0 ms
  • ✓openclaw-hook → telemetry spans › agent_error carries the turn context the Gateway frame omits0 ms
  • ✓openclaw-hook → telemetry spans › agent_error falls back to a describable label when the frame carries no message0 ms
  • ✓openclaw-hook → telemetry spans › omits detail entirely when it would only repeat the label0 ms
  • ✓openclaw-hook → telemetry spans › label is byte-equal to the projected row raw, so the two never render twice0 ms
  • ✓openclaw-hook → telemetry spans › all emitted spans propagate traceId for run correlation0 ms
bridge/src/__tests__/openclaw-model-catalog.test.ts8/8
  • ✓catalogFromModelsList › reads the parity fixture the way the Swift adapter does4 ms
  • ✓catalogFromModelsList › joins provider/id into the CLI key form, then falls back through id and provider/name; title/id for the name1 ms
  • ✓catalogFromModelsList › a missing or unavailable model is not available; non-string tags are ignored0 ms
  • ✓catalogFromModelsList › answers null for a payload without a usable list — not an empty catalog0 ms
  • ✓explicitDefaultModelName › an explicit payload key wins and resolves to the display name0 ms
  • ✓explicitDefaultModelName › otherwise only a default tag counts — never catalog order0 ms
  • ✓parseModelRole › maps tags like the CLI parser and the Swift adapter0 ms
  • ✓catalogRetryDelayMs › climbs the ladder and then holds at the last rung forever0 ms
bridge/src/__tests__/openclaw-personal-activity.test.ts7/7
  • ✓personal voice activity on the real adapter › accepts a string assistant body and ignores malformed blocks without losing the answer18 ms
  • ✓personal voice activity on the real adapter › shows work before any delta and closes a final-only response2 ms
  • ✓personal voice activity on the real adapter › does not let a cron final hide the voice model thinking interval1 ms
  • ✓personal voice activity on the real adapter › keeps a second voice request visible when the first finishes1 ms
  • ✓personal voice activity on the real adapter › clears activity on refusal and on the voice deadline6 ms
  • ✓personal voice activity on the real adapter › preserves a permission prompt while voice work is pending5 ms
  • ✓personal voice activity on the real adapter › handles a final that arrives before the acknowledgement1 ms
bridge/src/__tests__/openclaw-plugin-approval-flow.test.ts34/34
  • ✓the user can see what they are approving › puts the plugin title on the timeline row and the prompt, labeled [Plugin]9 ms
  • ✓the user can see what they are approving › exposes the prompt for the session row1 ms
  • ✓the user can actually resolve it › sends a decision to plugin.approval.resolve, never exec.approval.resolve2 ms
  • ✓the user can actually resolve it › maps each option index to its own decision0 ms
  • ✓the user can actually resolve it › resolves a shortcut `respond` press too0 ms
  • ✓the user can actually resolve it › drops a press whose question echo names a different approval1 ms
  • ✓the user can actually resolve it › accepts the echo a device that renders the question actually sends — the [Plugin]-prefixed form1 ms
  • ✓the user can actually resolve it › accepts a truncated prefixed echo — small surfaces cut the question0 ms
  • ✓the user can actually resolve it › keeps the prompt on screen when the resolve fails2 ms
  • ✓the prompt goes away when the approval does › labels a real allow as approved (plugin)1 ms
  • ✓the prompt goes away when the approval does › a deny does not resume0 ms
  • ✓the prompt goes away when the approval does › a resolution for a different approval does not clear the pending one0 ms
  • ✓the prompt goes away when the approval does › adopts a plugin approval that was already waiting when the adapter connected1 ms
  • ✓the prompt goes away when the approval does › adopts BOTH kinds independently on connect — neither adoption clobbers the other1 ms
  • ✓exec and plugin approvals coexist without dropping either › presents the OLDER of the two — the plugin one, when it arrived first0 ms
  • ✓exec and plugin approvals coexist without dropping either › resolving the shown one surfaces the queued one automatically — nothing is lost0 ms
  • ✓exec and plugin approvals coexist without dropping either › resolving the shown one keeps the row in awaiting_permission for the survivor1 ms
  • ✓exec and plugin approvals coexist without dropping either › an expiring approval does not idle the row while the other kind is live1 ms
  • ✓exec and plugin approvals coexist without dropping either › with nothing left pending, a resolution still settles the activity state0 ms
  • ✓exec and plugin approvals coexist without dropping either › a second plugin approval closes the one it supersedes instead of dropping it0 ms
  • ✓exec and plugin approvals coexist without dropping either › a failing exec catch-up does not skip the plugin catch-up0 ms
  • ✓exec and plugin approvals coexist without dropping either › a press routes to the ACTIVE prompt's own resolve method, never the other kind's0 ms
  • ✓plugin.approval.removed — real wire protocol, no .d.ts declaration › abandons the pending prompt named by the removed id0 ms
  • ✓plugin.approval.removed — real wire protocol, no .d.ts declaration › never resolves — removed carries no decision, so it cannot approve/deny1 ms
  • ✓plugin.approval.removed — real wire protocol, no .d.ts declaration › ignores a removed for a different id0 ms
  • ✓plugin.approval.removed — real wire protocol, no .d.ts declaration › makes no claim about an unreadable payload0 ms
  • ✓turn-scoping asymmetry from exec › a turn abort does NOT abandon a pending plugin approval1 ms
  • ✓turn-scoping asymmetry from exec › a turn error does NOT abandon a pending plugin approval1 ms
  • ✓turn-scoping asymmetry from exec › gateway disconnect DOES abandon a pending plugin approval0 ms
  • ✓an approval the Gateway has already dropped comes off the deck › abandons the prompt when the resolve says the approval is gone3 ms
  • ✓an approval the Gateway has already dropped comes off the deck › keeps the prompt when the resolve merely failed to get through1 ms
  • ✓an approval the Gateway has already dropped comes off the deck › abandons a prompt the Gateway no longer lists0 ms
  • ✓an approval the Gateway has already dropped comes off the deck › keeps a prompt that is still pending0 ms
  • ✓an approval the Gateway has already dropped comes off the deck › a failed or unreadable list answer changes nothing1 ms
bridge/src/__tests__/openclaw-session.test.ts15/15
  • ✓injectOpenClawSession › does NOT inject when the Gateway is reachable but not authenticated (regression: phantom trace)8 ms
  • ✓injectOpenClawSession › injects a minimal session when authenticated (CLI bridge shape)1 ms
  • ✓injectOpenClawSession › carries daemon-hub extras (state/projectName/modelName/controlMode) when provided0 ms
  • ✓injectOpenClawSession › carries the pending approval so decks render real, pressable options2 ms
  • ✓injectOpenClawSession › leaves the prompt fields off when nothing is pending0 ms
  • ✓injectOpenClawSession › is idempotent — does not duplicate an already-present openclaw session1 ms
  • ✓injectOpenClawSession › returns the original array reference when not injecting (no needless copy)0 ms
  • ✓injectOpenClawSession — the approval carries its context › puts the reason approval was demanded at the head of questionDetail1 ms
  • ✓injectOpenClawSession — the approval carries its context › names WHICH OpenClaw session asked0 ms
  • ✓injectOpenClawSession — the approval carries its context › leaves the question itself untouched — detail is additive0 ms
  • ✓injectOpenClawSession — the approval carries its context › emits no questionDetail when the Gateway sent nothing to say0 ms
  • ✓injectOpenClawSession — the approval carries its context › carries nothing when there is no approval at all0 ms
  • ✓injectOpenClawSession — plugin approvals are labeled distinctly from exec › prefixes the plugin question with [Plugin], never the exec one1 ms
  • ✓injectOpenClawSession — plugin approvals are labeled distinctly from exec › still carries options and liveAnswerable for a plugin approval1 ms
  • ✓injectOpenClawSession — plugin approvals are labeled distinctly from exec › carries the plugin approval detail too0 ms
scripts/__tests__/gateway-setup-status.test.ts2/2
  • ✓Gateway setup status › keeps Apple and Android generated decisions in sync53 ms
  • ✓Gateway setup status › does not request setup for transport progress or timeouts1 ms
shared/src/__tests__/gateway-live-activity.test.ts8/8
  • ✓Gateway live activity › replays captured 2026.9.6 frames: work starts before tools, one prompt/tool/answer5 ms
  • ✓Gateway live activity › keeps parallel runs busy, ignores lastRunId, and handles explicit settled snapshot0 ms
  • ✓Gateway live activity › deduplicates tool streams, silences successful polling, retains errors and late results1 ms
  • ✓Gateway live activity › folds every tool call of a run into one row, updated in place3 ms
  • ✓Gateway live activity › labels a folded row by its calls0 ms
  • ✓Gateway live activity › retains dispatched prompt and attributes a pending user message to its run1 ms
  • ✓Gateway live activity › does not treat unknown events or historical assistant messages as work0 ms
  • ✓Gateway live activity › keeps the generated Swift constants and reducer template in sync1 ms
shared/src/__tests__/openclaw-approval-rules-sync.test.ts5/5
  • ✓generated mirror in sync › apple/AgentDeck/Daemon/Gateway/OpenClawApprovalRules.generated.swift matches the SSOT3 ms
  • ✓generated mirror in sync › the Swift mirror carries every decision the TS union declares1 ms
  • ✓generated mirror in sync › neither side ever offers the plain "allow" the Gateway rejects1 ms
  • ✓generated mirror in sync › both suites replay the same "is this approval gone?" vectors1 ms
  • ✓generated mirror in sync › the Swift mirror reads the nested request, not flat fields0 ms
shared/src/__tests__/openclaw-approval.test.ts36/36
  • ✓parseExecApprovalRequest › reads the command out of the nested request3 ms
  • ✓parseExecApprovalRequest › never renders `ask` as if it were the question0 ms
  • ✓parseExecApprovalRequest › carries cwd and warning as supporting detail0 ms
  • ✓parseExecApprovalRequest › offers exactly the decisions the request allows1 ms
  • ✓parseExecApprovalRequest › drops allow-always when the policy withholds it0 ms
  • ✓parseExecApprovalRequest › honors unavailableDecisions on top of the allowed set0 ms
  • ✓parseExecApprovalRequest › always keeps a deny — a prompt you can only accept is not a prompt0 ms
  • ✓parseExecApprovalRequest › still produces an answerable prompt when no command was reported0 ms
  • ✓parseExecApprovalRequest › falls back to flat fields if a Gateway ever inlines them0 ms
  • ✓parseExecApprovalRequest › falls back to argv when only that is present0 ms
  • ✓parseExecApprovalRequest › returns null only when there is no usable id0 ms
  • ✓parseExecApprovalRequest › carries the expiry so a stale prompt can be swept0 ms
  • ✓answering › maps an option index to the decision that option carries0 ms
  • ✓answering › refuses an out-of-range index instead of guessing0 ms
  • ✓answering › accepts shortcuts, decision names, and the y/n/a spellings0 ms
  • ✓answering › never turns an unrecognized press into an approval0 ms
  • ✓answering › refuses "always" when the request forbids allow-always0 ms
  • ✓answering › classifies which decisions let the command run0 ms
  • ✓parseExecApprovalRequest — detail is most-decisive-first › leads with WHY approval was demanded, not with the cwd0 ms
  • ✓parseExecApprovalRequest — detail is most-decisive-first › still carries every part — this is a reorder, not a drop1 ms
  • ✓parseExecApprovalRequest — detail is most-decisive-first › degrades to whatever the Gateway did send0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › covers both directions0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › legacy message — the one this fleet actually produced (2026-09-09 23:40:37)0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › legacy message — older gateway spelling0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › structured reason under INVALID_REQUEST0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › structured code on its own0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › already resolved — another operator answered first0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › already resolved by message alone0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › ambiguous id prefix — the approval EXISTS, the id was short0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › transport failure — we do not know anything about the approval0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › RPC timeout — silence is not an answer0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › a decision the request forbids — the approval is still pending0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › empty error object claims nothing0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › details that is not an object cannot carry a reason0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › reads a real Error with the fields the adapter attaches0 ms
  • ✓isApprovalGoneError — vectors shared with the Swift suite › claims nothing about what it cannot read0 ms
shared/src/__tests__/openclaw-plugin-approval-rules-sync.test.ts6/6
  • ✓generated mirror in sync › apple/AgentDeck/Daemon/Gateway/OpenClawPluginApprovalRules.generated.swift matches the SSOT3 ms
  • ✓generated mirror in sync › the default severity mirrors PLUGIN_APPROVAL_DEFAULT_SEVERITY, not a re-guessed literal1 ms
  • ✓generated mirror in sync › reuses ExecApprovalDecision rather than redeclaring the decision vocabulary0 ms
  • ✓generated mirror in sync › the Swift mirror reads the nested request, not flat fields0 ms
  • ✓generated mirror in sync › the Swift mirror has no unavailableDecisions subtraction — that axis does not exist on this surface0 ms
  • ✓generated mirror in sync › carries a defensive reader for plugin.approval.removed, undeclared in any .d.ts0 ms
shared/src/__tests__/openclaw-plugin-approval.test.ts24/24
  • ✓parsePluginApprovalRequest › reads the title as the headline, not a generic fallback4 ms
  • ✓parsePluginApprovalRequest › carries the description as the lead supporting line0 ms
  • ✓parsePluginApprovalRequest › defaults severity to "warning" when the Gateway omitted it — matching buildPluginApprovalRequestMessage0 ms
  • ✓parsePluginApprovalRequest › honors an explicit severity0 ms
  • ✓parsePluginApprovalRequest › offers exactly the decisions the request allows2 ms
  • ✓parsePluginApprovalRequest › narrows to an explicit allowedDecisions list — no unavailableDecisions subtraction exists on this surface0 ms
  • ✓parsePluginApprovalRequest › always keeps a deny — a prompt you can only accept is not a prompt0 ms
  • ✓parsePluginApprovalRequest › never offers the plain "allow" the Gateway rejects, even if the Gateway sent it1 ms
  • ✓parsePluginApprovalRequest › falls back to the full default set when allowedDecisions is empty/absent0 ms
  • ✓parsePluginApprovalRequest › still produces an answerable prompt when no title/description was reported1 ms
  • ✓parsePluginApprovalRequest › falls back to flat fields if a Gateway ever inlines them0 ms
  • ✓parsePluginApprovalRequest › returns null only when there is no usable id1 ms
  • ✓parsePluginApprovalRequest › carries the expiry so a stale prompt can be swept0 ms
  • ✓parsePluginApprovalRequest › carries which session asked0 ms
  • ✓parsePluginApprovalRequest › summarizes scope as a display-only supporting line0 ms
  • ✓answering › maps an option index to the decision that option carries0 ms
  • ✓answering › refuses an out-of-range index instead of guessing0 ms
  • ✓answering › accepts shortcuts, decision names, and the y/n/a spellings0 ms
  • ✓answering › never turns an unrecognized press into an approval0 ms
  • ✓answering › refuses "always" when the request forbids allow-always0 ms
  • ✓answering › classifies which decisions let the plugin action proceed0 ms
  • ✓parsePluginApprovalRemoved › reads the id out of the {id} payload the embedded broker emits0 ms
  • ✓parsePluginApprovalRemoved › makes no claim about a payload it cannot read0 ms
  • ✓isApprovalGoneError — shared with the exec module › is the same predicate exec uses0 ms

63 files

Pass

Devices & display surfaces

Do ESP32, Pixoo, Timebox, iDotMatrix, ADB and mDNS paths discover, flash and render correctly?

Passed
722
Failed
0
Skipped
2
Time
9.5 s
63 test files
bridge/src/__tests__/adb-async.test.ts6/6
  • ✓adb probe stays off the event loop (#327) › adb-reverse.ts never spawns synchronously3 ms
  • ✓adb probe stays off the event loop (#327) › the adb module activates without a synchronous probe0 ms
  • ✓parseAdbDeviceLines › collects every ready device; usbOnly is the only network filter1 ms
  • ✓parseAdbDeviceLines › usbOnly drops network transports but keeps the emulator0 ms
  • ✓parseAdbDeviceLines › tolerates a header-only or empty listing0 ms
  • ✓cached adb device count serves request handlers › reads zero before any refresh and the cached list after one1 ms
bridge/src/__tests__/adb-transport.test.ts4/4
  • ✓isNetworkAdbTransport › flags adb-over-TCP serials (adb connect <ip>:<port>)3 ms
  • ✓isNetworkAdbTransport › flags wireless-debugging mDNS instance names0 ms
  • ✓isNetworkAdbTransport › passes USB serials through0 ms
  • ✓isNetworkAdbTransport › does not flag the local emulator0 ms
bridge/src/__tests__/ble-sync-circuit-breaker.test.ts3/3
  • ✓SIGABRT circuit breaker › halts after the threshold of consecutive SIGABRT kills, naming the device2 ms
  • ✓SIGABRT circuit breaker › keeps reporting the halt on further exits instead of silently re-arming0 ms
  • ✓SIGABRT circuit breaker › resets on any non-SIGABRT exit so a powered-off panel never trips it1 ms
bridge/src/__tests__/ble-sync-squelch.test.ts5/5
  • ✓createSyncCycleSquelch › logs the first two occurrences of a cycle, then suppresses identical repeats9 ms
  • ✓createSyncCycleSquelch › emits an hourly summary with the repeat count while suppressing2 ms
  • ✓createSyncCycleSquelch › flushes and logs immediately when a different exit appears1 ms
  • ✓createSyncCycleSquelch › treats 1x vs 2x captures of the same retry error as the same cycle1 ms
  • ✓createSyncCycleSquelch › treats an exit after a long healthy run as a fresh incident1 ms
bridge/src/__tests__/ble-sync-status.test.ts14/14
  • ✓parseStatusLine › parses an AGENTDECK_STATUS line3 ms
  • ✓parseStatusLine › rejects a non-status line and malformed JSON0 ms
  • ✓createBleLinkTracker › reports "sync not started" before anything is spawned0 ms
  • ✓createBleLinkTracker › reports no device when nothing is configured0 ms
  • ✓createBleLinkTracker › goes connected on a streaming status line — the regression this fixes0 ms
  • ✓createBleLinkTracker › distinguishes host-display pause from a dead link0 ms
  • ✓createBleLinkTracker › keeps hasFrame sticky across a disconnect and surfaces the error0 ms
  • ✓createBleLinkTracker › clears a stale error once the client recovers0 ms
  • ✓createBleLinkTracker › reports the backoff window after an exit, then falls back to connecting2 ms
  • ✓createBleLinkTracker › surfaces a missing BLE runtime instead of an unexplained disconnect0 ms
  • ✓createBleLinkTracker › a respawn clears the connected flag until the child reports again0 ms
  • ✓mergeBleLinkSnapshots › is connected only when every panel is0 ms
  • ✓mergeBleLinkSnapshots › reports no-device state when there are no trackers yet0 ms
  • ✓mergeBleLinkSnapshots › takes the newest push time across panels0 ms
bridge/src/__tests__/calendar.test.ts16/16
  • ✓parseCalendarSettings › accepts a single url or a list, rejects everything else3 ms
  • ✓ICS parsing › unfolds continuation lines0 ms
  • ✓ICS parsing › parses date, local, and UTC instants1 ms
  • ✓ICS parsing › parses VEVENTs with summary escaping and TZID params2 ms
  • ✓occursOn (recurrence) › non-recurring: literal date only0 ms
  • ✓occursOn (recurrence) › DAILY with INTERVAL and UNTIL1 ms
  • ✓occursOn (recurrence) › WEEKLY honors BYDAY0 ms
  • ✓occursOn (recurrence) › DAILY COUNT exhausts0 ms
  • ✓occursOn (recurrence) › MONTHLY and YEARLY match the start day0 ms
  • ✓occursOn (recurrence) › EXDATE removes an instance0 ms
  • ✓buildTodayEvents › keeps only what is left of today, all-day first, capped and sorted9 ms
  • ✓buildTodayEvents › an in-progress event survives the past filter via its end time0 ms
  • ✓buildTodayEvents › RECURRENCE-ID override replaces the master instance0 ms
  • ✓buildTodayEvents › trims titles to the UTF-8 byte budget0 ms
  • ✓CalendarProvider › fetches, caches, and re-filters cached parses against the current clock30 ms
  • ✓CalendarProvider › resolves undefined when unconfigured or on a cold failure1 ms
bridge/src/__tests__/card-feed.test.ts50/50
  • ✓classifySessionCard › permission gate → live with the long-poll TTL3 ms
  • ✓classifySessionCard › awaiting prompt without requestId → live with the awaiting backstop TTL0 ms
  • ✓classifySessionCard › idle/processing sessions → info without expiry1 ms
  • ✓buildCardFeed › derives one card per session with cardId session:<id>1 ms
  • ✓buildCardFeed › module cards (M7) follow the session projections and carry no session body1 ms
  • ✓buildCardFeed › builds a module-only feed for an offline-first Pocket reader0 ms
  • ✓buildCardFeed › pull cadence hint: idle roster → idle interval, active roster → active interval1 ms
  • ✓pull OTA Feed bootstrap › replaces a full deck with a cache-preserving OTA bootstrap envelope1 ms
  • ✓pull OTA Feed bootstrap › leaves ordinary and already-conditional feeds untouched0 ms
  • ✓applyOutboxDecisions › dismiss is always acknowledged, never dispatched1 ms
  • ✓applyOutboxDecisions › permission_decision applies only while the gate is still pending2 ms
  • ✓applyOutboxDecisions › permission_decision without requestId/decision is rejected0 ms
  • ✓applyOutboxDecisions › select_option routes session-scoped when the session is still awaiting1 ms
  • ✓applyOutboxDecisions › select_option expires when the prompt question changed0 ms
  • ✓applyOutboxDecisions › select_option expires when the session is no longer awaiting0 ms
  • ✓applyOutboxDecisions › unknown session → unknown_card0 ms
  • ✓applyOutboxDecisions › respond routes through session_command with the value0 ms
  • ✓applyOutboxDecisions › send_prompt queues via session_command for any alive session0 ms
  • ✓applyOutboxDecisions › malformed decisions are rejected without dispatch, order preserved0 ms
  • ✓applyOutboxDecisions › empty/absent decisions array → ok with no results0 ms
  • ✓applyOutboxDecisions › deduplicates a response-loss retry even when its computed age changed1 ms
  • ✓FeedPullTracker › normalizes v4-mapped and loopback client addresses0 ms
  • ✓FeedPullTracker › first pull carries no interval; the second measures it against the advertised cadence1 ms
  • ✓FeedPullTracker › a gap far off the advertised cadence is not counted as honoured0 ms
  • ✓FeedPullTracker › compares against the cadence advertised on the PREVIOUS pull, not the current one0 ms
  • ✓FeedPullTracker › learns the board from an outbox push and keeps it for later anonymous pulls0 ms
  • ✓FeedPullTracker › retains non-secret Surface product identity for card-feed diagnostics1 ms
  • ✓FeedPullTracker › tracks clients independently and reports the median observed interval1 ms
  • ✓FeedPullTracker › bounds its history ring, newest first0 ms
  • ✓FeedPullTracker › formats the pull line with the drift verdict0 ms
  • ✓conditional pull (deckSig) › stamps a deckSig and short-circuits to unchanged on a matching echo0 ms
  • ✓conditional pull (deckSig) › a ticking elapsedSec does not defeat the signature0 ms
  • ✓conditional pull (deckSig) › an idle roster stays signature-stable across a long clock advance (the idle-night case)0 ms
  • ✓conditional pull (deckSig) › a real content change still breaks the match even when elapsedSec moves0 ms
  • ✓conditional pull (deckSig) › expiresAt churn does not defeat the signature (live cards re-stamp every build)0 ms
  • ✓conditional pull (deckSig) › content change breaks the match1 ms
  • ✓conditional pull (deckSig) › glance is covered by the signature0 ms
  • ✓glance builders › trimUtf8Bytes trims on rune boundaries by byte budget0 ms
  • ✓glance builders › buildGlanceUsage derives Claude and Codex rows with integer percents1 ms
  • ✓glance builders › buildGlanceUsage emits no row for a provider with no numbers0 ms
  • ✓glance builders › buildGlanceUsage adds the z.ai row after Codex and fits all three providers1 ms
  • ✓glance builders › buildGlanceUsage keeps the list compact when only z.ai is live0 ms
  • ✓glance builders › buildGlanceWrapup ranks attention first and folds overflow0 ms
  • ✓glance builders › buildGlance returns undefined when there is nothing to say0 ms
  • ✓glance builders › projects portable-reader weather to exactly the fields firmware renders1 ms
  • ✓glance builders › projects a compact five-day outlook only when the reader negotiated it1 ms
  • ✓pull telemetry › keeps omitted telemetry absent rather than inventing a zero-percent battery0 ms
  • ✓pull telemetry › parsePullTelemetry keeps in-range values and drops garbage0 ms
  • ✓pull telemetry › tracker records telemetry and unchanged counts per client0 ms
  • ✓pull telemetry › formats unchanged pulls and telemetry into the log line0 ms
bridge/src/__tests__/card-modules.test.ts17/17
  • ✓module card ids › round-trips module:<id>:<key>, including keys that contain colons7 ms
  • ✓module card ids › rejects session card ids and malformed ones0 ms
  • ✓sealModuleCard — the ≤4-choice rule as a clamp › binds at most three choices, because slot 1 is the device Later2 ms
  • ✓sealModuleCard — the ≤4-choice rule as a clamp › drops choices missing an id or a label rather than shipping a blank button0 ms
  • ✓sealModuleCard — the ≤4-choice rule as a clamp › caps context lines and omits empty collections entirely0 ms
  • ✓sealModuleCard — the ≤4-choice rule as a clamp › trims text to a UTF-8 byte budget on a code-point boundary1 ms
  • ✓sealModuleCard — the ≤4-choice rule as a clamp › bounds stable choice ids for fixed firmware outbox buffers0 ms
  • ✓sealModuleCard — the ≤4-choice rule as a clamp › stamps the card id and keeps the class/expiry the module asked for0 ms
  • ✓buildModuleCards › a module that throws is skipped, the rest of the feed survives1 ms
  • ✓THREAD module › produces nothing when there is no live session0 ms
  • ✓THREAD module › leads with how many threads need the user1 ms
  • ✓THREAD module › checkpoints an idle session with its last milestone0 ms
  • ✓THREAD module › says what did not fit instead of implying the list is complete0 ms
  • ✓card_choice routing › routes a choice to the authoring module with the card key3 ms
  • ✓card_choice routing › is terminal — never retryable — for unknown, read-only, or id-less cards0 ms
  • ✓card_choice routing › a throwing module reports error rather than taking the batch down0 ms
  • ✓card_choice routing › reaches the module through the outbox path, without touching session state1 ms
bridge/src/__tests__/dashboard-provider-vectors.test.ts13/13
  • ✓dashboard provider vectors (shared with the Swift suite) › the frozen pre-mechanism baseline is exactly the vocabulary minus z.ai4 ms
  • ✓dashboard provider vectors (shared with the Swift suite) › legacy save (no seen key) + z.ai confirmed → z.ai joins exactly once1 ms
  • ✓dashboard provider vectors (shared with the Swift suite) › the join is once — a second read with z.ai still confirmed adds nothing0 ms
  • ✓dashboard provider vectors (shared with the Swift suite) › a deliberate later hide wins — z.ai stays hidden despite being confirmed0 ms
  • ✓dashboard provider vectors (shared with the Swift suite) › pre-mechanism absence is a deliberate choice: legacy save without claude never auto-adds claude0 ms
  • ✓dashboard provider vectors (shared with the Swift suite) › unconfirmed providers never join — confirmation is the gate, not vocabulary membership0 ms
  • ✓dashboard provider vectors (shared with the Swift suite) › initialize seeds the discovered set; a later-confirmed z.ai joins0 ms
  • ✓dashboard provider vectors (shared with the Swift suite) › initialize cannot overwrite a saved choice (initialize-once unchanged)0 ms
  • ✓dashboard provider vectors (shared with the Swift suite) › after initialize, a confirmed z.ai joins the saved list once0 ms
  • ✓dashboard provider vectors (shared with the Swift suite) › a MANUAL save saw the full menu — nothing auto-joins afterwards, not even a newly confirmed provider0 ms
  • ✓dashboard provider vectors (shared with the Swift suite) › an explicit empty list is a saved choice, not uninitialized0 ms
  • ✓dashboard provider vectors (shared with the Swift suite) › the empty saved choice still admits a never-offered confirmed provider0 ms
  • ✓dashboard provider vectors (shared with the Swift suite) › null providers with no update stays null — initialize-once waits for a client0 ms
bridge/src/__tests__/dashboard-providers.test.ts4/4
  • ✓stable dashboard provider membership › initializes once and preserves the list through a cold read and absent telemetry9 ms
  • ✓stable dashboard provider membership › explicit hiding survives re-registration and preserves unrelated settings2 ms
  • ✓stable dashboard provider membership › rejects invalid membership without overwriting saved choices2 ms
  • ✓stable dashboard provider membership › deduplicates and keeps stable order1 ms
bridge/src/__tests__/desk-awareness.test.ts5/5
  • ✓desk awareness › prioritizes real waiting over recent results and ignores dead sessions4 ms
  • ✓desk awareness › does not mistake idle, missing sessions, or abandoned work for completion1 ms
  • ✓desk awareness › retains explicit results for 90 seconds, rejects future timestamps0 ms
  • ✓desk awareness › keeps rendering deterministic and states distinct at 11 pixels12 ms
  • ✓desk awareness › keeps rendering deterministic and states distinct at 32 pixels57 ms
bridge/src/__tests__/device-discovery.test.ts2/2
  • ✓Pixoo discovery — config-reply classification › accepts a real GetAllConf reply3 ms
  • ✓Pixoo discovery — config-reply classification › rejects a non-Pixoo HTTP/JSON response1 ms
bridge/src/__tests__/device-photo.test.ts10/10
  • ✓DevicePhotoCollector › assembles frames into a JPEG file and reports the target session6 ms
  • ✓DevicePhotoCollector › ignores binary frames with no open capture0 ms
  • ✓DevicePhotoCollector › keeps captures separate per connection1 ms
  • ✓DevicePhotoCollector › returns null for a cancelled or empty capture0 ms
  • ✓DevicePhotoCollector › drops a capture whose byte count disagrees with the board claim0 ms
  • ✓DevicePhotoCollector › drops an oversized capture instead of writing a truncated JPEG6 ms
  • ✓DevicePhotoCollector › reaps captures whose photo_end never arrived1 ms
  • ✓DevicePhotoCollector › abandon drops the open capture for a dead socket0 ms
  • ✓prunePhotoDir › keeps only the newest N photos3 ms
  • ✓prunePhotoDir › is silent on a missing directory1 ms
bridge/src/__tests__/display-dim.test.ts3/3
  • ✓display dim settings › defaults to enabled full-off when settings are missing5 ms
  • ✓display dim settings › normalizes minimum-brightness settings0 ms
  • ✓display dim settings › embeds the resolved dim instruction in display_state events1 ms
bridge/src/__tests__/dot-matrix-glyphs.test.ts19/19
  • ✓canonical dot-matrix agent masks › ships every official agent mark at Pixoo/iDotMatrix and TC001 resolutions11 ms
  • ✓canonical dot-matrix agent masks › preserves the official negative-space features instead of filled approximations1 ms
  • ✓Timebox Mini Agent Beacon › does not replace the agent beacon with an error when quota is exhausted10 ms
  • ✓Timebox Mini Agent Beacon › renders octopus from a non-empty official 9×9 mask1 ms
  • ✓Timebox Mini Agent Beacon › renders jellyfish from a non-empty official 9×9 mask5 ms
  • ✓Timebox Mini Agent Beacon › renders opencode from a non-empty official 9×9 mask0 ms
  • ✓Timebox Mini Agent Beacon › renders crayfish from a non-empty official 9×9 mask0 ms
  • ✓Timebox Mini Agent Beacon › renders antigravity from a non-empty official 9×9 mask1 ms
  • ✓Timebox Mini Agent Beacon › renders kiro from a non-empty official 9×9 mask0 ms
  • ✓Timebox Mini Agent Beacon › keeps OpenCode hollow and OpenClaw teal-eyed at physical resolution1 ms
  • ✓Timebox Mini Agent Beacon › keeps identity fixed and moves only the perimeter rail while processing5 ms
  • ✓Timebox Mini Agent Beacon › renders a device-native standby tide when no agent exists0 ms
  • ✓Timebox Mini Agent Beacon › uses a continuous dim status frame instead of isolated floating pixels1 ms
  • ✓iDotMatrix native 32×32 stage › keeps a vivid official mark and four source-keyed telemetry rails5 ms
  • ✓Pixoo64 provider usage HUD › renders Claude and Codex as matching percentage-plus-reset bands21 ms
  • ✓Pixoo64 provider usage HUD › uses the empty Codex primary zone for the subscription date12 ms
  • ✓Pixoo adaptive push policy › separates state latency from stable animation cadence0 ms
  • ✓Pixoo adaptive push policy › uses safe moving single frames for active Pixoo states0 ms
  • ✓Pixoo adaptive push policy › keeps the safe active cadence at 2.5 seconds0 ms
bridge/src/__tests__/esp32-flash-lease.test.ts11/11
  • ✓clampLeaseSeconds › clamps to the documented window5 ms
  • ✓clampLeaseSeconds › never lets junk become an unbounded suspension1 ms
  • ✓readLease › reports an active lease2 ms
  • ✓readLease › an expired lease reads as NO lease — expiry is enforced here, not by a timer1 ms
  • ✓readLease › is evaluated against the caller's clock, not the write time1 ms
  • ✓readLease › fails OPEN on a corrupt lease1 ms
  • ✓readLease › fails open on a lease with no usable expiry1 ms
  • ✓readLease › is absent when no lease was ever written1 ms
  • ✓lease persistence › is a FILE, so it outlives the process that wrote it2 ms
  • ✓lease persistence › clearLease is idempotent1 ms
  • ✓lease persistence › honours AGENTDECK_DATA_DIR0 ms
bridge/src/__tests__/esp32-flash-transport.test.ts8/8
  • ✓optional dependency › reports absence rather than throwing MODULE_NOT_FOUND2 ms
  • ✓optional dependency › lists no candidates when serialport is missing — never a crash1 ms
  • ✓optional dependency › open() names the missing module instead of failing obscurely2 ms
  • ✓port enumeration › keeps USB serial candidates and prefers the callout node1 ms
  • ✓port enumeration › drops Bluetooth and debug nodes0 ms
  • ✓port enumeration › accepts the Linux node names too0 ms
  • ✓setSignals › writes BOTH lines on every call, whichever one was named1 ms
  • ✓setSignals › is a no-op on a closed port rather than an error1 ms
bridge/src/__tests__/esp32-flash.test.ts25/25
  • ✓resolveFlashBoard › accepts the canonical id, the pio env, and every alias4 ms
  • ✓resolveFlashBoard › offers boards the BROWSER does not — USB is the only path for some0 ms
  • ✓resolveFlashBoard › names the alternatives when a target is unknown2 ms
  • ✓firmwareVersionFromCheckout › reads FIRMWARE_VERSION out of config.h0 ms
  • ✓firmwareVersionFromCheckout › returns undefined rather than guessing when the constant is absent0 ms
  • ✓classifyHolders › separates AgentDeck processes from everyone else2 ms
  • ✓classifyHolders › treats an unknown holder as foreign1 ms
  • ✓pickFirmwareTag › prefers the checkout version when that release is published0 ms
  • ✓pickFirmwareTag › falls back to the newest release in the bump-before-tag window0 ms
  • ✓pickFirmwareTag › falls back when there is no checkout at all (installed from npm)0 ms
  • ✓pickFirmwareTag › refuses rather than inventing a tag0 ms
  • ✓pickFirmwareTag › reports WHICH rule it used0 ms
  • ✓parseLsofHolders › pairs each command with its pid and ignores the fd lines0 ms
  • ✓parseLsofHolders › handles several holders0 ms
  • ✓parseLsofHolders › reads empty output as no holders0 ms
  • ✓SERIAL_PROBE_UNAVAILABLE › names TC001, whose CH340 TX is broken in hardware0 ms
  • ✓SERIAL_PROBE_UNAVAILABLE › does not excuse any other board0 ms
  • ✓resolveFirmware › serves a cache hit without touching the network6 ms
  • ✓resolveFirmware › RE-VERIFIES the cache, because a cache key proves provenance and not integrity3 ms
  • ✓resolveFirmware › refuses a manifest whose hash does not describe its file2 ms
  • ✓resolveFirmware › takes the tag from a checkout config.h when none is given1 ms
  • ✓resolveFirmware › an explicit --firmware bypasses the manifest and SAYS it is unverified1 ms
  • ✓resolveFirmware › explains a release that publishes no merged image for the board1 ms
  • ✓resolveFirmware › --offline fails rather than reaching the network for a tag1 ms
  • ✓resolveFirmware › respects AGENTDECK_DATA_DIR for the cache1 ms
bridge/src/__tests__/esp32-ota-stage-key.test.ts13/13
  • ✓pull-OTA staging › resolves an IP target to the board the advert is keyed on8 ms
  • ✓pull-OTA staging › keeps an unknown target verbatim rather than inventing a board1 ms
  • ✓pull-OTA staging › adverts an IP-targeted stage to the board that pulls the feed3 ms
  • ✓pull-OTA staging › reports pullSeen=false when no feed pull has ever been observed1 ms
  • ✓pull-OTA staging › reports pullSeen=true once that board has actually pulled the feed2 ms
  • ✓pull-OTA staging › resolves a target through the feed tracker when the board has slept out of the WS roster1 ms
  • ✓pull-OTA staging › isolates X3 and X4 stages by the complete product tuple3 ms
  • ✓pull-OTA staging › acknowledges an SD-installed staged image by its embedded client version2 ms
  • ✓pull-OTA staging › refreshes a rebuilt-in-place image before accepting a version acknowledgement3 ms
  • ✓pull-OTA staging › rejects cross-product and cross-channel stages before persisting2 ms
  • ✓pull-OTA staging › serves pull OTA as bounded, contiguous resume segments3 ms
  • ✓pull-OTA staging › honors the bounded cooperative Surface binary segment limit1 ms
  • ✓pull-OTA staging › redirects dual-homed OTA traffic to the device-side Wi-Fi interface once1 ms
bridge/src/__tests__/esp32-serial-node.test.ts94/94
  • ✓SERIAL_FORWARDED_EVENTS › includes all display events3 ms
  • ✓SERIAL_FORWARDED_EVENTS › includes timeline events (unique to serial)0 ms
  • ✓SERIAL_FORWARDED_EVENTS › does NOT include plugin-only events0 ms
  • ✓SERIAL_FORWARDED_EVENTS › required serial events are present0 ms
  • ✓ESP32 port detection patterns › matches CH340 ports (86 Box)1 ms
  • ✓ESP32 port detection patterns › matches native USB JTAG ports (IPS 3.5", Round AMOLED)0 ms
  • ✓ESP32 port detection patterns › matches Linux serial ports0 ms
  • ✓ESP32 port detection patterns › excludes Bluetooth and WLAN0 ms
  • ✓ESP32 port detection patterns › excludes non-ESP32 ports0 ms
  • ✓handleSerialLine (source) › parses device_info message and updates deviceInfo2 ms
  • ✓handleSerialLine (source) › carries the board-reported WiFi RSSI and drops implausible values2 ms
  • ✓handleSerialLine (source) › skips debug lines (non-JSON)2 ms
  • ✓handleSerialLine (source) › retains numeric e-ink completion events without painted content4 ms
  • ✓handleSerialLine (source) › retains hosted software-reset evidence without logging subsequent private lines0 ms
  • ✓handleSerialLine (source) › records allowed voice stages under normal serial ownership0 ms
  • ✓handleSerialLine (source) › does not misclassify the normal I2C register probe as a panic1 ms
  • ✓handleSerialLine (source) › retains a content-free voice milestone: [WakeVoice] detected score=2420 ms
  • ✓handleSerialLine (source) › retains a content-free voice milestone: [WakeVoice] capture end samples=42912 queued=10 ms
  • ✓handleSerialLine (source) › retains a content-free voice milestone: [Voice] HTTP upload attempt 1: 85824 bytes, internal heap 84 KB0 ms
  • ✓handleSerialLine (source) › retains a content-free voice milestone: [Voice] HTTP upload 85824 bytes -> 200 (attempt 1)0 ms
  • ✓handleSerialLine (source) › retains a content-free voice milestone: [Voice] HTTP upload 85824 bytes -> -2 (attempt 1)0 ms
  • ✓handleSerialLine (source) › retains a content-free voice milestone: [VoicePerf] uploadMs=198 minInternalKB=59 pressureWaits=20 ms
  • ✓handleSerialLine (source) › retains a content-free voice milestone: [Speaker] played 28132/28132 bytes (0.9s), 0 frames dropped0 ms
  • ✓handleSerialLine (source) › retains a content-free voice milestone: [VoiceFeedback] state=waiting captureElapsedMs=00 ms
  • ✓handleSerialLine (source) › retains a content-free voice milestone: [VoiceEndpoint] complete elapsedMs=2422 quietMs=1217 rms=99 threshold=2091 ms
  • ✓handleSerialLine (source) › does not log unapproved voice content: [Voice] upload peer=192.168.68.100:91200 ms
  • ✓handleSerialLine (source) › does not log unapproved voice content: [WakeVoice] capture wake target=private-session noise=88 threshold=2090 ms
  • ✓handleSerialLine (source) › does not log unapproved voice content: [VoiceFeedback] state=waiting captureElapsedMs=0 secret text0 ms
  • ✓handleSerialLine (source) › does not log unapproved voice content: [Voice] transcript: private speech0 ms
  • ✓handleSerialLine (source) › does not log unapproved voice content: {"type":"voice_result","text":"private speech"}0 ms
  • ✓handleSerialLine (source) › does not log unapproved voice content: [Voice] xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx0 ms
  • ✓handleSerialLine (source) › opens a panic capture window on crash markers and captures the dump1 ms
  • ✓handleSerialLine (source) › closes the panic window after expiry and caps captured lines0 ms
  • ✓handleSerialLine (source) › recovers from malformed JSON1 ms
  • ✓handleSerialLine (source) › ignores JSON without type field0 ms
  • ✓handleSerialLine (source) › does not mark deviceInfo present for non-identifying messages1 ms
  • ✓budgetTimelineEntries › keeps the newest entries under the byte budget, in chronological order2 ms
  • ✓budgetTimelineEntries › passes small histories through untouched1 ms
  • ✓serial I/O error classification › treats nonblocking backpressure as retryable0 ms
  • ✓serial I/O error classification › does not retry terminal serial errors0 ms
  • ✓prepareForSerial (source) › strips agentCapabilities from state_update1 ms
  • ✓prepareForSerial (source) › strips billingType and remoteUrl from state_update0 ms
  • ✓prepareForSerial (source) › preserves essential state_update fields1 ms
  • ✓prepareForSerial (source) › strips bulky state_update fields before device_info arrives1 ms
  • ✓prepareForSerial (source) › trims sessions_list to firmware fields and string sizes1 ms
  • ✓prepareForSerial (source) › forwards per-session D1 mosaic fields (tool/elapsed/awaiting prompt) and trims oversized strings2 ms
  • ✓prepareForSerial (source) › round-robins sessions_list by agent type so non-Claude agents survive the cap2 ms
  • ✓prepareForSerial (source) › roundRobinByAgentType keeps every present agent type and is a no-op under the cap1 ms
  • ✓prepareForSerial (source) › strips legacy usage fields from usage_update1 ms
  • ✓prepareForSerial (source) › preserves Codex window freshness for firmware-side hiding1 ms
  • ✓prepareForSerial (source) › forwards z.ai windowMinutes so serial boards label windows by length, not "P"/"S"0 ms
  • ✓prepareForSerial (source) › passes through other events unchanged0 ms
  • ✓prepareForSerial (source) › caps timeline raw by UTF-8 BYTES on a code-point boundary (한글-safe)1 ms
  • ✓prepareForSerial (source) › forwards daemon-computed lastEvent* fields byte-capped, omitted when absent1 ms
  • ✓prepareForSerial (source) › caps timeline_history to the firmware ring (64) AND the line-buffer byte budget1 ms
  • ✓WiFi provision protocol › provision message has all required fields0 ms
  • ✓WiFi provision protocol › auto-provisions first-setup boards and refreshes IPS10 endpoint while online0 ms
  • ✓WiFi provision protocol › re-arms the pairing token on boards the WiFi gate skips0 ms
  • ✓WiFi provision protocol › costs one write per board per token, not one per device_info frame0 ms
  • ✓WiFi provision protocol › never writes a credential to an unidentified or empty peer0 ms
  • ✓serial buffer management › line splitting handles multiple lines in one chunk0 ms
  • ✓serial buffer management › handles partial message across chunks1 ms
  • ✓serial buffer management › truncates buffer when exceeding 8KB limit0 ms
  • ✓foreign port detection › flags a port that opened but never sent valid JSON0 ms
  • ✓foreign port detection › does NOT flag a connection that ever read a valid AgentDeck message0 ms
  • ✓foreign port detection › does NOT flag a connection that already identified its board0 ms
  • ✓foreign port denylist threshold › denylists a port only after repeated failed probes1 ms
  • ✓foreign port denylist threshold › treats an elapsed cooldown as no longer denylisted0 ms
  • ✓half-open identified CDC recovery › flags an identified CDC port that has never read since (re)connect0 ms
  • ✓half-open identified CDC recovery › does NOT flag while the read pipe is alive0 ms
  • ✓half-open identified CDC recovery › does NOT flag inside the grace window after connect0 ms
  • ✓half-open identified CDC recovery › does NOT flag an unidentified CDC port — that is the foreign path0 ms
  • ✓half-open identified CDC recovery › does NOT flag a UART (non-CDC) port — handled by the read-timeout branch0 ms
  • ✓serial keepalive ack contract › contains the quoted "keepalive" substring the firmware strstr-matches0 ms
  • ✓serial keepalive ack contract › is the exact frame the deployed firmware acks0 ms
  • ✓half-open identified UART recovery › flags an identified UART whose RX died mid-connection0 ms
  • ✓half-open identified UART recovery › does NOT flag while reads are current0 ms
  • ✓half-open identified UART recovery › does NOT flag below the silence timeout (one lost device_info is not death)0 ms
  • ✓half-open identified UART recovery › does NOT flag a never-read UART — that is the initial-read/foreign path0 ms
  • ✓half-open identified UART recovery › does NOT flag an unidentified UART0 ms
  • ✓half-open identified UART recovery › does NOT flag a CDC port — handled by isHalfOpenIdentifiedCdc/CDC clauses0 ms
  • ✓device-info re-identify gating › retries a brand-new (unseeded) connection that has not yet identified0 ms
  • ✓device-info re-identify gating › retries a CACHE-SEEDED connection (stale deviceInfo, not yet fresh) — the trmnl_75 bug0 ms
  • ✓device-info re-identify gating › STOPS once a live device_info has landed on this connection0 ms
  • ✓device-info re-identify gating › stops after the request budget is exhausted0 ms
  • ✓device-info re-identify gating › paces requests — no retry inside the retry interval0 ms
  • ✓device-info re-identify gating › does not retry a disconnected connection0 ms
  • ✓serial/WiFi display-path dedup › does not suppress WiFi for a cache-seeded CDC port with no inbound traffic0 ms
  • ✓serial/WiFi display-path dedup › suppresses WiFi only while a live device_info connection has recent inbound traffic0 ms
  • ✓serialOpenFailureBackoffMs › ramps 10→20→40→80→160→300s and reaches the 5-min block at the 6th failure1 ms
  • ✓serialOpenFailureBackoffMs › never exceeds the 5-min block for any transient failCount6 ms
  • ✓serialOpenFailureBackoffMs › uses the flat 5-min block for EACCES (permanent) failures0 ms
  • ✓sanitizeRssiDbm › keeps a plausible dBm reading, rounded0 ms
  • ✓sanitizeRssiDbm › treats absent, zero, positive and non-numeric values as no reading1 ms
bridge/src/__tests__/esp32-suspend-sweep.test.ts7/7
  • ✓sweepAndSuspendDaemons › suspends every answering daemon and reports the ports to resume5 ms
  • ✓sweepAndSuspendDaemons › the incident shape: timeout against an AgentDeck listener REFUSES, not "nothing to suspend"1 ms
  • ✓sweepAndSuspendDaemons › ECONNREFUSED is the one silence that means absent — proceeds1 ms
  • ✓sweepAndSuspendDaemons › silence from a NON-AgentDeck listener is not a serial threat0 ms
  • ✓sweepAndSuspendDaemons › a daemon that answers HTTP 404 refuses only when /health says it is a daemon1 ms
  • ✓sweepAndSuspendDaemons › an unobservable listener (lsof failed) is treated as absent, not refused0 ms
  • ✓scanTcpListener (live loopback) › names its listener and observes its release114 ms
bridge/src/__tests__/esp32-transport-dedup.test.ts7/7
  • ✓isWifiTransportRedundant › dedups when the same board id is live on serial (both report matching IP)3 ms
  • ✓isWifiTransportRedundant › dedups by board id alone when serial reports no IP (radio parked / pre-DHCP)0 ms
  • ✓isWifiTransportRedundant › dedups when the WiFi side has no IP but the board id matches serial0 ms
  • ✓isWifiTransportRedundant › does NOT dedup a WiFi-only board with no serial presence (86box: USB power-only)0 ms
  • ✓isWifiTransportRedundant › does NOT dedup two distinct physical units of the same model on different IPs0 ms
  • ✓isWifiTransportRedundant › never dedups an unresolved/unknown board identity0 ms
  • ✓isWifiTransportRedundant › returns false when nothing is live on serial (WiFi is the only path)0 ms
bridge/src/__tests__/esp32-wifi-ota-reconnect.test.ts3/3
  • ✓WiFi OTA reconnect-follow › completes a transfer across a mid-flight WS disconnect by resending on the reconnected socket59 ms
  • ✓WiFi OTA reconnect-follow › fails fast when the board drops mid-OTA and never reconnects454 ms
  • ✓WiFi OTA reconnect-follow › a clean transfer (no disconnect) completes with zero reconnect-resends1 ms
bridge/src/__tests__/esp32-wifi-ota-serial-wake.test.ts4/4
  • ✓WiFi OTA to a board parked on USB serial › releases only that board, waits for it on WiFi, transfers, then reopens the port511 ms
  • ✓WiFi OTA to a board parked on USB serial › reports that the board never joined WiFi and reopens its port503 ms
  • ✓WiFi OTA to a board parked on USB serial › leaves serial alone when the board is already live on WiFi2 ms
  • ✓WiFi OTA to a board parked on USB serial › keeps the original error when the board is on neither transport1 ms
bridge/src/__tests__/font-pack.test.ts4/4
  • ✓Surface font pack › loads the bundled licensed cpfont and verifies both digests33 ms
  • ✓Surface font pack › rejects a payload changed after the manifest was authored26 ms
  • ✓Surface font pack › keeps the Node and signed Apple resources byte-identical25 ms
  • ✓Surface font pack › serves only the exact id and advertised version25 ms
bridge/src/__tests__/glance-frame.test.ts11/11
  • ✓wmoIconKey › maps WMO code groups to icon keys2 ms
  • ✓renderGlanceFrameSvg › lays out the content and escapes markup2 ms
  • ✓renderGlanceFrameSvg › renders an honest empty state when there is nothing to show0 ms
  • ✓renderGlanceFrameSvg › ellipsizes long work lines before the hard column clip0 ms
  • ✓packMono › packs MSB-first with 1 = white0 ms
  • ✓packMono › pads row tails to byte boundaries0 ms
  • ✓packMono › dithers mid-gray into a stable ordered pattern0 ms
  • ✓renderGlanceFrame › produces the exact framebuffer size with a stable sig for stable input179 ms
  • ✓renderGlanceFrame › rotates X3 into physical space with the device GfxRenderer Portrait mapping1 ms
  • ✓renderGlanceFrame › sig ignores the clock stamp but tracks content — a clock tick must not void the conditional178 ms
  • ✓renderGlanceFrame › landscape preset fills the X4 framebuffer51 ms
bridge/src/__tests__/idotmatrix-settings.test.ts3/3
  • ✓iDotMatrix extra name prefixes › keeps the configured prefixes4 ms
  • ✓iDotMatrix extra name prefixes › drops blanks and non-strings instead of passing them to the scanner0 ms
  • ✓iDotMatrix extra name prefixes › treats a missing or malformed key as no extra prefixes0 ms
bridge/src/__tests__/ips10-collaboration.test.ts8/8
  • ✓IPS10 additive collaboration census › preserves explicit zero and a parent idle with active children5 ms
  • ✓IPS10 additive collaboration census › leaves every other board and unidentified connection unchanged1 ms
  • ✓IPS10 additive collaboration census › does not invent a census from absent or malformed evidence0 ms
  • ✓IPS10 additive collaboration census › drops only the enhancement when the conservative frame budget is exceeded2 ms
  • ✓IPS10 stable card roster › keeps every awaiting session, fills with the newest, and orders by id8 ms
  • ✓IPS10 stable card roster › adds the coordination census and the roster total for IPS10 only1 ms
  • ✓IPS10 quota transport fidelity › preserves a missing 5h slot, actual window lengths, Luna (fleet-wide), and all subscription dates1 ms
  • ✓IPS10 quota transport fidelity › forwards the purchased-credit balance so boards can show it once a window is exhausted0 ms
bridge/src/__tests__/ips10-roster.test.ts3/3
  • ✓IPS10 rotating roster › visits all 1000 sessions, retains attention, and never grows the frame902 ms
  • ✓IPS10 rotating roster › rotates real projected IDs and excludes dead sessions16 ms
  • ✓IPS10 rotating roster › keeps the generated Swift kernel synchronized59 ms
  • ○IPS10 rotating roster › keeps the generated Swift kernel behaviorally equivalent
bridge/src/__tests__/learning-pack.test.ts5/5
  • ✓Surface learning pack › loads the bundled licensed PDLP and verifies both digest layers8 ms
  • ✓Surface learning pack › rejects a payload changed after the manifest was authored4 ms
  • ✓Surface learning pack › rejects an unapproved content licence before serving bytes1 ms
  • ✓Surface learning pack › keeps the Node package and signed Apple bundle resources byte-identical4.0 s
  • ✓Surface learning pack › serves only the exact id and advertised version2 ms
bridge/src/__tests__/matrix-expression.test.ts11/11
  • ✓expressive BLE matrices › keeps numeric information on the default world, with no creature carousel46 ms
  • ✓expressive BLE matrices › earns a six-second entrance only for a new live session, independent of polls and order1 ms
  • ✓expressive BLE matrices › never hides waiting or errors behind a new creature or a response5 ms
  • ✓expressive BLE matrices › holds a reply on stage briefly, retains its count for 90s, and distinguishes unknown and idle2 ms
  • ✓expressive BLE matrices › keeps the asked agent listening until its reply, and a task close is not a conversation5 ms
  • ✓expressive BLE matrices › rotates only the live attention owners and clears attention immediately1 ms
  • ✓expressive BLE matrices › does not keep a historical conversation over a new turn, closed turn or missing roster1 ms
  • ✓expressive BLE matrices › upserts one session without retracting another result at the same timestamp2 ms
  • ✓expressive BLE matrices › unknown agent identities are neutral, and face expressions survive 4-bit packing2 ms
  • ✓expressive BLE matrices › routes live Node endpoint frames through the same event state (preview does not replay entrances)84 ms
  • ✓expressive BLE matrices › has no generated policy or pixel drift390 ms
  • ○expressive BLE matrices › executes the Swift engine and matches Node state and every output pixel
bridge/src/__tests__/mdns-discover.test.ts5/5
  • ✓discoverDaemons › returns a reachable daemon, preferring the /health pairingToken over TXT20 ms
  • ✓discoverDaemons › filters out daemons that do not advertise sameSocketControl (e.g. Swift App Store)11 ms
  • ✓discoverDaemons › drops link-local and loopback addresses11 ms
  • ✓discoverDaemons › excludes candidates whose /health is not a daemon11 ms
  • ✓discoverDaemons › falls back to the TXT ip when no usable addresses are present11 ms
bridge/src/__tests__/mdns-hostname.test.ts4/4
  • ✓mDNS service hostname › never publishes A/AAAA records under the macOS system hostname10 ms
  • ✓mDNS service hostname › identifies which daemon it is without naming the account2 ms
  • ✓mDNS service hostname › never puts a pairing token in the multicast TXT record (issue #145)1 ms
  • ✓mDNS service hostname › keeps the service hostname stable across recovery re-publishes2 ms
bridge/src/__tests__/mdns-routable-records.test.ts7/7
  • ✓isRoutableIpv4 › rejects the addresses no LAN peer can reach2 ms
  • ✓isRoutableIpv4 › keeps ordinary LAN addresses, including private ranges0 ms
  • ✓advertisedIpv4Addresses › drops link-local while keeping every real interface address1 ms
  • ✓published A records › filters the unroutable A record out of what is announced6 ms
  • ✓published A records › leaves non-A records untouched1 ms
  • ✓published A records › republishes when an address appears on a non-default interface3 ms
  • ✓published A records › does not republish for a link-local address flapping1 ms
bridge/src/__tests__/peripheral-mapping.test.ts10/10
  • ✓parsePeripheralMappings › keeps valid entries and drops malformed ones5 ms
  • ✓parsePeripheralMappings › returns empty when the block is absent or not an array0 ms
  • ✓resolvePeripheralAction › matches a tag case-insensitively and honors the project filter1 ms
  • ✓resolvePeripheralAction › without a project filter targets the session that needs a human0 ms
  • ✓resolvePeripheralAction › matches IR codes too0 ms
  • ✓resolvePeripheralAction › returns undefined for an unmapped tag or an empty roster0 ms
  • ✓resolvePeripheralAction › does not cross kinds (an nfc uid never matches an ir event)0 ms
  • ✓commandForAction › approve prefers a held gate and falls back to the live prompt0 ms
  • ✓commandForAction › deny mirrors approve0 ms
  • ✓commandForAction › maps focus / review / stop to existing daemon commands0 ms
bridge/src/__tests__/pixoo-autodiscover-default.test.ts5/5
  • ✓pixooAutoDiscoverFrom › is OFF when the setting is absent3 ms
  • ✓pixooAutoDiscoverFrom › is OFF for anything that is not an explicit true1 ms
  • ✓pixooAutoDiscoverFrom › is ON only when explicitly opted in0 ms
  • ✓discoverPixoo cloud opt-out › queries the Divoom cloud by default1 ms
  • ✓discoverPixoo cloud opt-out › never touches the cloud when cloud is false2 ms
bridge/src/__tests__/pixoo-creature-sync.test.ts19/19
  • ✓pixoo creature sync — Codex folding › draws one cloud per project, not one per codex thread12 ms
  • ✓pixoo creature sync — Codex folding › keeps separate projects as separate clouds0 ms
  • ✓pixoo creature sync — Codex folding › surfaces the busiest state in a folded group0 ms
  • ✓pixoo creature sync — Codex folding › does NOT fold Claude Code — several octopuses on one project is deliberate0 ms
  • ✓pixoo creature sync — Codex folding › ignores dead sessions0 ms
  • ✓pixoo creature sync — per-type bands › clusters each agent type into its own X band1 ms
  • ✓pixoo creature sync — per-type bands › stratifies types vertically so a mixed tank stays readable0 ms
  • ✓pixoo creature sync — per-type bands › shrinks creatures as a band fills up0 ms
  • ✓pixoo creature sync — the `_primary` fallback › synthesizes a creature before the first sessions list arrives1 ms
  • ✓pixoo creature sync — the `_primary` fallback › draws an empty tank once an empty list is known0 ms
  • ✓pixoo creature sync — the `_primary` fallback › does not synthesize a creature for a non-creature agent0 ms
  • ✓pixoo creature sync — the `_primary` fallback › does not stamp the stateEvent onto a real session0 ms
  • ✓pixoo creature sync — unknown agent types › draws no creature for an agent type it has never heard of0 ms
  • ✓pixoo creature sync — unknown agent types › does not let an unknown agent become a Claude octopus alongside real ones0 ms
  • ✓pixoo creature sync — unknown agent types › gives every known agent type its own creature1 ms
  • ✓pixoo creature sync — Usage HUD safe area floor › keeps normal ground depth (0.80) when no usage HUD is active0 ms
  • ✓pixoo creature sync — Usage HUD safe area floor › clamps idle creatures to 0.72 when a single-provider HUD is active0 ms
  • ✓pixoo creature sync — Usage HUD safe area floor › clamps idle creatures to 0.65 when a dual-provider HUD is active0 ms
  • ✓pixoo creature sync — Usage HUD safe area floor › does not alter upper/mid-water processing creatures when HUD is active0 ms
bridge/src/__tests__/pixoo-font.test.ts1/1
  • ✓Pixoo 3×5 font › keeps N diagonal and visibly distinct from M5 ms
bridge/src/__tests__/pixoo-sprites.test.ts19/19
  • ✓getOctopusPaletteForSession › keeps the first additional session near the original terracotta tone4 ms
  • ✓getOctopusPaletteForSession › darkens later sessions while preserving channel ordering1 ms
  • ✓getOctopusPaletteForSession › clamps very large session indices to the darkest supported tone0 ms
  • ✓creatureCellSize › is monotonic non-decreasing across a zoom sweep (cols=24, 32px)18 ms
  • ✓creatureCellSize › is monotonic non-decreasing across a zoom sweep (cols=14, 32px)6 ms
  • ✓creatureCellSize › is monotonic non-decreasing across a zoom sweep (cols=12, 32px)9 ms
  • ✓creatureCellSize › is monotonic non-decreasing across a zoom sweep (cols=8, 32px)11 ms
  • ✓creatureCellSize › is monotonic non-decreasing across a zoom sweep (cols=24, 64px)8 ms
  • ✓creatureCellSize › is monotonic non-decreasing across a zoom sweep (cols=14, 64px)13 ms
  • ✓creatureCellSize › is monotonic non-decreasing across a zoom sweep (cols=12, 64px)7 ms
  • ✓creatureCellSize › is monotonic non-decreasing across a zoom sweep (cols=8, 64px)7 ms
  • ✓creatureCellSize › is stable for sub-0.25 zoom jitter around a value (no flicker)0 ms
  • ✓creatureCellSize › never returns less than 10 ms
  • ✓Codex cloud low-detail grids › keeps the MD cloud as one rounded body instead of separated dangling lobes1 ms
  • ✓Codex cloud low-detail grids › keeps the LOD cloud rounded for iDotMatrix-wide shots0 ms
  • ✓quantizeCameraPixels › snaps the center onto the device-pixel grid0 ms
  • ✓quantizeCameraPixels › is idempotent0 ms
  • ✓quantizeCameraPixels › preserves zoom and width0 ms
  • ✓quantizeCameraPixels › defaults width to 64 and tolerates degenerate scale0 ms
bridge/src/__tests__/pixoo-usage-rails.test.ts9/9
  • ✓iDotMatrix 32×32 telemetry rails › draws GLM credits independently and retracts stale or removed windows8 ms
  • ✓iDotMatrix 32×32 telemetry rails › spends no row on a provider that reports nothing1 ms
  • ✓iDotMatrix 32×32 telemetry rails › reads a windowless free-tier Codex block as no Codex rows1 ms
  • ✓iDotMatrix 32×32 telemetry rails › still fills all four rows when both providers report both windows1 ms
  • ✓iDotMatrix 32×32 telemetry rails › gives the panel back entirely when no provider reports usage1 ms
  • ✓iDotMatrix 32×32 telemetry rails › keeps a stale (ended) Codex window off the rails, not on a blank row0 ms
  • ✓Pixoo64 usage HUD bands › shows all three providers simultaneously and reserves a band for a seven-day-only Claude sample12 ms
  • ✓Pixoo64 usage HUD bands › drops the Codex band for a windowless free-tier block, and the lone Claude band slides down15 ms
  • ✓Pixoo64 usage HUD bands › draws no HUD at all when neither provider reports usage4 ms
bridge/src/__tests__/pocket-autonomy.test.ts9/9
  • ✓AutonomousPocketEngine › cold-start explores a preference quest and the strongest available signal7 ms
  • ✓AutonomousPocketEngine › learns from choices and stops repeating the cold-start survey2 ms
  • ✓AutonomousPocketEngine › treats a retried outbox answer as idempotent1 ms
  • ✓AutonomousPocketEngine › treats Later as a neutral read-state and does not return the same card1 ms
  • ✓AutonomousPocketEngine › lets a read-only pulse be marked Done through the reserved Later action1 ms
  • ✓AutonomousPocketEngine › persists only aggregate learning state and opaque fingerprints2 ms
  • ✓AutonomousPocketEngine › counts delivered cards with no response as weak negative evidence after a day1 ms
  • ✓AutonomousPocketEngine › routes only stable Pocket choices1 ms
  • ✓parsePocketAutonomyConfig › defaults on absent settings and clamps explicit bounds0 ms
bridge/src/__tests__/python-ble-runtime.test.ts9/9
  • ✓optional Python BLE runtime › resolves packaged scripts from the bridge package and keeps the venv in the data directory3 ms
  • ✓optional Python BLE runtime › reports missing npm assets instead of silently disabling BLE1 ms
  • ✓optional Python BLE runtime › creates and marks a persistent environment on first explicit setup6 ms
  • ✓optional Python BLE runtime › accepts a compatible legacy checkout venv without modifying it3 ms
  • ✓bridge npm BLE assets › publishes every Python client and the dependency manifest1 ms
  • ✓Rosetta interpreter guard (darwin/arm64) › refuses an x86_64-only legacy venv on an arm64 Mac with a rebuild hint3 ms
  • ✓Rosetta interpreter guard (darwin/arm64) › accepts a universal binary carrying an arm64 slice, and anything on non-arm64 hosts3 ms
  • ✓Rosetta interpreter guard (darwin/arm64) › makes no claim about an unrecognized binary (scripts, ELF, truncated files)2 ms
  • ✓Rosetta interpreter guard (darwin/arm64) › rebuilds a Rosetta venv with the native Homebrew interpreter probed first3 ms
bridge/src/__tests__/timebox-ble.test.ts14/14
  • ✓Timebox device identity › uses the BLE address as the device id2 ms
  • ✓micro layout (Timebox 11×11) › returns a size²·3 RGB buffer2 ms
  • ✓micro layout (Timebox 11×11) › draws a bright dominant creature for a processing session2 ms
  • ✓micro layout (Timebox 11×11) › degrades parent-linked subagents to fixed perimeter companions1 ms
  • ✓micro layout (Timebox 11×11) › shows the device-native standby tide when no session exists1 ms
  • ✓micro layout (Timebox 11×11) › provides a static dark farewell badge for a stopped daemon1 ms
  • ✓micro layout (Timebox 11×11) › does not mistake a live empty roster for daemon offline1 ms
  • ✓micro layout (Timebox 11×11) › keeps gateway errors on a red perimeter rail without tinting identity0 ms
  • ✓micro layout (Timebox 11×11) › maps claude-code to its shaded official mark and product color1 ms
  • ✓micro layout (Timebox 11×11) › maps codex-cli to its shaded official mark and product color1 ms
  • ✓micro layout (Timebox 11×11) › maps opencode to its shaded official mark and product color0 ms
  • ✓micro layout (Timebox 11×11) › maps openclaw to its shaded official mark and product color0 ms
  • ✓micro layout (Timebox 11×11) › preserves OpenCode negative space and OpenClaw teal eyes1 ms
  • ✓micro layout (Timebox 11×11) › renders the generated Antigravity mark as a multicolor open arc1 ms
bridge/src/__tests__/weather-config.test.ts6/6
  • ✓weather CLI configuration › rounds coordinates to the shared coarse-location boundary3 ms
  • ✓weather CLI configuration › rejects malformed and out-of-range coordinates2 ms
  • ✓weather CLI configuration › validates IANA time zones24 ms
  • ✓weather CLI configuration › uses the host time zone and does not retain a stale place label1 ms
  • ✓weather CLI configuration › retains only an explicit custom-provider boundary when changing location1 ms
  • ✓weather CLI configuration › renders configured and unconfigured status without exposing a custom API key1 ms
bridge/src/__tests__/weather.test.ts14/14
  • ✓parseWeatherSettings › accepts lat/lon + optional place3 ms
  • ✓parseWeatherSettings › rejects missing, malformed, or out-of-range config0 ms
  • ✓parseWeatherSettings › keeps an explicit paid/custom Open-Meteo boundary opt-in1 ms
  • ✓wmoSummary › maps representative WMO codes to short words1 ms
  • ✓findTodayRainWindow › finds the first remaining contiguous window ≥ the floor with its peak1 ms
  • ✓findTodayRainWindow › single qualifying hour → no endHm0 ms
  • ✓findTodayRainWindow › no qualifying hour today (tomorrow does not count) → undefined0 ms
  • ✓toGlanceWeather › rounds temps, maps codes, and carries tomorrow + rain window1 ms
  • ✓MET Norway normalization › builds seven-day offline data and precipitation cues without inventing probability62 ms
  • ✓WeatherProvider › unconfigured → undefined without fetching1 ms
  • ✓WeatherProvider › fetches once, then serves from cache inside the window1 ms
  • ✓WeatherProvider › serves the stale cache on fetch failure, up to the stale-serve bound1 ms
  • ✓WeatherProvider › failure with no cache → undefined (never throws)0 ms
  • ✓WeatherProvider › keeps a future-bearing seven-day snapshot through a long provider outage19 ms
scripts/__tests__/dashboard-providers-sync.test.ts1/1
  • ✓native validators and provider menus follow the daemon vocabulary37 ms
scripts/__tests__/fetch-flash-firmware.test.ts8/8
  • ✓firmwareVersionFromConfig › reads the constant3 ms
  • ✓firmwareVersionFromConfig › returns undefined rather than a guess0 ms
  • ✓resolveTag › an explicit --tag wins outright, without probing anything1 ms
  • ✓resolveTag › prefers the checkout version when that release exists0 ms
  • ✓resolveTag › falls back to the newest release when the checkout version has no tag yet0 ms
  • ✓resolveTag › falls back when the checkout has no version at all (installed, not cloned)0 ms
  • ✓resolveTag › refuses rather than deploying an unnamed firmware2 ms
  • ✓resolveTag › reports WHICH rule it used — a silently chosen tag is unauditable0 ms
scripts/__tests__/firmware-manifest.test.ts9/9
  • ✓firmware manifest › describes every board in the SSOT6 ms
  • ✓firmware manifest › points every consumer at one file and one offset3 ms
  • ✓firmware manifest › computes hashes from the artifacts, not from the SSOT3 ms
  • ✓firmware manifest › fails the release when a board ships nothing3 ms
  • ✓firmware manifest › carries the web-flash evidence, not just a flag2 ms
  • ✓release notes tail › tells people to write the merged image at 0x0, not the app at 0x1000010 ms
  • ✓release notes tail › lists every board that built3 ms
  • ✓release notes tail › marks a board browser-flashable only when the SSOT says so2 ms
  • ✓merged image self-check › parses the fields esptool image-info prints0 ms
scripts/__tests__/hermes-mermaid-2d.test.ts3/3
  • ✓Hermes 2D mermaid generator › has every generated output in sync with the generator133 ms
  • ✓Hermes 2D mermaid generator › keeps the official Nous girl paths as the head, unmodified1 ms
  • ✓Hermes 2D mermaid generator › places a tail below the bust instead of a floating head1 ms
scripts/__tests__/preview-mirror-sync.test.ts1/1
  • ✓preview mirror pins › every SYNC-HASH pin matches its origin file251 ms
scripts/__tests__/surface-protocol-manifest.test.ts6/6
  • ✓Surface Protocol integration manifest › validates schemas/surface-protocol/v1/fixtures/pocket-daily-reader.json9 ms
  • ✓Surface Protocol integration manifest › validates schemas/surface-protocol/v1/fixtures/bitfocus-companion.json1 ms
  • ✓Surface Protocol integration manifest › keeps product identity separate from board and update channel1 ms
  • ✓Surface Protocol integration manifest › rejects duplicate capabilities instead of hiding a malformed declaration1 ms
  • ✓Surface Protocol integration manifest › requires evidence for Verified Compatible and Official claims2 ms
  • ✓Surface Protocol integration manifest › does not allow an independently owned project to claim Official1 ms
scripts/__tests__/surface-protocol-portable-reader.test.ts15/15
  • ✓Surface Protocol portable-reader/v1 Card Feed › validates the weather-seven-day fixture7 ms
  • ✓Surface Protocol portable-reader/v1 Card Feed › validates the unchanged fixture0 ms
  • ✓Surface Protocol portable-reader/v1 Card Feed › validates the unchanged-learning-pack fixture0 ms
  • ✓Surface Protocol portable-reader/v1 Card Feed › carries a full seven-day future horizon with provider attribution and bounded cues3 ms
  • ✓Surface Protocol portable-reader/v1 Card Feed › rejects an eighth forecast day0 ms
  • ✓Surface Protocol portable-reader/v1 Card Feed › rejects a cue without an expiry boundary0 ms
  • ✓Surface Protocol portable-reader/v1 Card Feed › rejects conditional responses that try to replace cached content2 ms
  • ✓Surface Protocol portable-reader/v1 Card Feed › requires the complete OTA tuple even on an unchanged feed0 ms
  • ✓Surface Protocol portable-reader/v1 Card Feed › bounds the licensed learning-pack advert on an unchanged feed1 ms
  • ✓Surface Protocol portable-reader/v1 Card Feed › allows an additive optional weather field for tolerant v1 clients0 ms
  • ✓Surface Protocol portable-reader/v1 Outbox › validates the bounded request and ordered terminal response fixtures2 ms
  • ✓Surface Protocol portable-reader/v1 Outbox › requires action correlation fields without forbidding additive metadata0 ms
  • ✓Surface Protocol portable-reader/v1 Outbox › bounds a wake-cycle batch and recognizes only v1 terminal statuses1 ms
  • ✓Surface Protocol negotiation fixtures › validates the bounded portable welcome without advertising Inbox1 ms
  • ✓Surface Protocol negotiation fixtures › keeps public fixtures free of credentials and authorization material1 ms
scripts/__tests__/web-flasher-flash-path.test.ts26/26
  • ✓md5 (browser flasher) › matches node:crypto for empty4 ms
  • ✓md5 (browser flasher) › matches node:crypto for abc0 ms
  • ✓md5 (browser flasher) › matches node:crypto for sentence0 ms
  • ✓md5 (browser flasher) › matches node:crypto for 55 bytes0 ms
  • ✓md5 (browser flasher) › matches node:crypto for 56 bytes0 ms
  • ✓md5 (browser flasher) › matches node:crypto for 64 bytes0 ms
  • ✓md5 (browser flasher) › matches node:crypto for 119 bytes0 ms
  • ✓md5 (browser flasher) › matches node:crypto for 120 bytes0 ms
  • ✓md5 (browser flasher) › matches node:crypto for 600 KB86 ms
  • ✓esp32PreflightVerdict › passes when chip and flash both agree2 ms
  • ✓esp32PreflightVerdict › refuses an S3 image on a classic ESP32 — the brick this guard exists for1 ms
  • ✓esp32PreflightVerdict › refuses a 16MB-header image on an 8MB part0 ms
  • ✓esp32PreflightVerdict › ALLOWS a smaller declaration than the part reports — TRMNL 7.5" is 16MB physical, 8MB declared0 ms
  • ✓esp32PreflightVerdict › treats an unreadable flash size as its own answer, not as a failure0 ms
  • ✓esp32PreflightVerdict › refuses when nothing answered at all0 ms
  • ✓esp32PreflightVerdict › surface polarity › browser refuses a board it does not offer0 ms
  • ✓esp32PreflightVerdict › surface polarity › CLI writes the same board — USB is its only update path0 ms
  • ✓esp32PreflightVerdict › surface polarity › the CLI still applies the hardware guards to an unoffered board0 ms
  • ✓esp32PreflightVerdict › image geometry › passes when the image agrees with the board spec2 ms
  • ✓esp32PreflightVerdict › image geometry › refuses a 16MB image for a board this build calls 4MB, even on an 8MB part0 ms
  • ✓esp32PreflightVerdict › image geometry › refuses the CONSERVATIVE direction too — this is not the directional size rule0 ms
  • ✓esp32PreflightVerdict › image geometry › is the ONLY size check when the flash id is unreadable1 ms
  • ✓esp32PreflightVerdict › image geometry › refuses an image built for a different chip family0 ms
  • ✓esp32PreflightVerdict › image geometry › omitting it does not weaken the other guards0 ms
  • ✓esp32PreflightVerdict › the erase refusal is still reachable — stubless boards exist, on the CLI0 ms
  • ✓esp32PreflightVerdict › every board the browser offers accepts its own declared identity1 ms
shared/src/__tests__/collaboration-presentation.test.ts2/2
  • ✓preserves parent state while distinguishing outstanding work from idle3 ms
  • ✓keeps firmware and macOS on the same census labels and phase rule1 ms
shared/src/__tests__/creature-layout.test.ts30/30
  • ✓creature-layout bands › returns nothing for a count of zero or less3 ms
  • ✓creature-layout bands › 'hermes' keeps every slot inside its X band4 ms
  • ✓creature-layout bands › 'octopus' keeps every slot inside its X band3 ms
  • ✓creature-layout bands › 'cloud' keeps every slot inside its X band3 ms
  • ✓creature-layout bands › 'opencode' keeps every slot inside its X band2 ms
  • ✓creature-layout bands › 'antigravity' keeps every slot inside its X band2 ms
  • ✓creature-layout bands › 'hermes' emits exactly `count` slots2 ms
  • ✓creature-layout bands › 'octopus' emits exactly `count` slots1 ms
  • ✓creature-layout bands › 'cloud' emits exactly `count` slots1 ms
  • ✓creature-layout bands › 'opencode' emits exactly `count` slots1 ms
  • ✓creature-layout bands › 'antigravity' emits exactly `count` slots1 ms
  • ✓creature-layout bands › 'hermes' stays on one row up to its limit, then wraps0 ms
  • ✓creature-layout bands › 'octopus' stays on one row up to its limit, then wraps0 ms
  • ✓creature-layout bands › 'cloud' stays on one row up to its limit, then wraps0 ms
  • ✓creature-layout bands › 'opencode' stays on one row up to its limit, then wraps0 ms
  • ✓creature-layout bands › 'antigravity' stays on one row up to its limit, then wraps0 ms
  • ✓creature-layout bands › anchors each band at a distinct X center, so types read as clusters0 ms
  • ✓crowd shrink › never grows the scale as a band fills up1 ms
  • ✓crowd shrink › honors the 0.40 hard floor even when a band is packed3 ms
  • ✓crowd shrink › shrinks a crowded row enough to cap neighbor overlap at ~half a body0 ms
  • ✓crowd shrink › gives a lone creature the band-native full scale0 ms
  • ✓crowd shrink › centers a lone creature in its band0 ms
  • ✓spreadFloorResidents (shared vectors) › already apart: nobody moves0 ms
  • ✓spreadFloorResidents (shared vectors) › two overlapping idle octopuses (live 2026-09-27) push apart, the left one stays0 ms
  • ✓spreadFloorResidents (shared vectors) › input order is preserved in the output0 ms
  • ✓spreadFloorResidents (shared vectors) › a crowd at the right edge is pulled back left of the crayfish line0 ms
  • ✓spreadFloorResidents (shared vectors) › a band too small spaces them evenly0 ms
  • ✓spreadFloorResidents (shared vectors) › left edge clamp0 ms
  • ✓spreadFloorResidents (shared vectors) › empty0 ms
  • ✓spreadFloorResidents (shared vectors) › keeps floor residents inside the band left of the crayfish clear line0 ms
shared/src/__tests__/d200h-free-space.test.ts8/8
  • ✓usage free-space expansion › uncompacts into keys the roster leaves free — six windows, six keys8 ms
  • ✓usage free-space expansion › keeps every session key — expansion only takes keys the roster cannot use11 ms
  • ✓usage free-space expansion › keeps all providers and the cap in three keys (Claude weekly: undefined)3 ms
  • ✓usage free-space expansion › keeps all providers and the cap in three keys (Claude weekly: 64)2 ms
  • ✓usage free-space expansion › no spare (roster fills the grid) — usage stays the compacted strip3 ms
  • ✓folded z.ai key (5H + MCP on one key, press cycles the view) › folds z.ai before Claude and makes that key cycle2 ms
  • ✓folded z.ai key (5H + MCP on one key, press cycles the view) › shows only the selected z.ai reading in 5H and MCP modes3 ms
  • ✓folded z.ai key (5H + MCP on one key, press cycles the view) › keeps one z.ai window per key, with no cycle, when there is room1 ms
shared/src/__tests__/d200h-layout.test.ts33/33
  • ✓usage tiles — usageKnown tri-state › renders Luna Reserve as a large moon tile with remaining percentage4 ms
  • ✓usage tiles — usageKnown tri-state › renders a percent when the quota is known1 ms
  • ✓usage tiles — usageKnown tri-state › renders a muted "—" instead of a confident 0% when unknown0 ms
  • ✓usage tiles — usageKnown tri-state › defaults to known (percent) when the flag is omitted0 ms
  • ✓usage tiles — usageKnown tri-state › wide slot shows "—" for both columns when unknown1 ms
  • ✓usage tiles — usageKnown tri-state › wide slot shows percents when known0 ms
  • ✓usage tiles — usageKnown tri-state › compact pair preserves both window labels and values in one key1 ms
  • ✓usage tiles — usageKnown tri-state › parseState infers usageKnown=false when no percent fields are present0 ms
  • ✓usage tiles — usageKnown tri-state › parseState infers usageKnown=true when a percent is present0 ms
  • ✓usage tiles — usageKnown tri-state › parseState honors an explicit usageKnown=false even with a coerced 0 percent0 ms
  • ✓buildSessionDeck — daemon offline › renders one deck-spanning OFFLINE card for a disconnected daemon8 ms
  • ✓buildSessionDeck — daemon offline › makes EVERY key launch the companion app while offline3 ms
  • ✓buildSessionDeck — daemon offline › does not show OFFLINE / launch when the daemon is connected2 ms
  • ✓buildSessionDeck — daemon offline › shows the session list (not OFFLINE) when state is disconnected but sessions exist3 ms
  • ✓buildSessionDeck — selected session isolation › does not render another agent model while selected-session focus is pending2 ms
  • ✓buildSessionDeck — per-model scoped caps › surfaces the worst ACTIVE scoped cap with the critical ramp3 ms
  • ✓buildSessionDeck — per-model scoped caps › renders an INACTIVE scoped cap muted (informational cyan), never the ramp1 ms
  • ✓buildSessionDeck — per-model scoped caps › parseState carries scopedLimits through0 ms
  • ✓usage tiles — Codex snapshot freshness › prints the countdown, undimmed, for a fresh snapshot1 ms
  • ✓usage tiles — Codex snapshot freshness › THE REGRESSION: an aged snapshot keeps its % but is dimmed and dated1 ms
  • ✓usage tiles — Codex snapshot freshness › an ended window still reads "stale", not an age1 ms
  • ✓usage tiles — Codex snapshot freshness › a legacy producer (no capturedAt) renders exactly as before1 ms
  • ✓usage tiles — scoped caps and the three-key strip budget › emits at most ONE scoped tile, however many caps arrive1 ms
  • ✓usage tiles — scoped caps and the three-key strip budget › compacts provider pairs so scoped and Codex limits all remain visible2 ms
  • ✓usage tiles — scoped caps and the three-key strip budget › sanitizes and truncates the API label before it reaches SVG text1 ms
  • ✓usage tiles — scoped caps and the three-key strip budget › renders no scoped tile when there are none1 ms
  • ✓usage tiles — z.ai provider windows › renders a lone z.ai plan with a text identity and a length-derived 30D label5 ms
  • ✓usage tiles — z.ai provider windows › compacts all three providers into pair tiles — six windows, nothing dropped1 ms
  • ✓usage tiles — z.ai provider windows › emits no z.ai tiles for a windowless (retired or payg) block2 ms
  • ✓VOICE tile — hold-to-talk contract › tells the user to hold, not to tap1 ms
  • ✓VOICE tile — hold-to-talk contract › reads as listening while the daemon captures4 ms
  • ✓VOICE tile — hold-to-talk contract › still declares the wire commands the layout is responsible for2 ms
  • ✓VOICE tile — hold-to-talk contract › goes inert only while transcribing0 ms
shared/src/__tests__/d200h-observed.test.ts14/14
  • ✓D200H observed session detail › idle: REVIEW + VOICE are the actionable cells — no fake prompts, inert STOP7 ms
  • ✓D200H observed session detail › idle while recording: the VOICE tile flips to a stop toggle1 ms
  • ✓D200H observed session detail › processing: soft STOP is the only action; no queued task or REVIEW mid-turn2 ms
  • ✓D200H observed session detail › processing: last review verdict stays visible as an inert badge0 ms
  • ✓D200H observed session detail › processing + stopRequested: STOP replaced by STOPPING tile, no queue presets1 ms
  • ✓D200H observed session detail › awaiting + requestId: exactly ALLOW/DENY permission_decision cells1 ms
  • ✓D200H observed session detail › awaiting WITHOUT requestId: answer-in-terminal only, never fabricated Allow/Deny1 ms
  • ✓D200H observed session detail › AskUserQuestion options are visible but inert without a live-answer host1 ms
  • ✓D200H observed session detail › liveAnswerable turns the same options into select_option presses1 ms
  • ✓D200H observed session detail › codex observed idle: REVIEW only — steering stays inert (notify-only hooks)3 ms
  • ✓D200H observed session detail › codex observed processing: nothing actionable — REVIEW waits for turn end1 ms
  • ✓D200H observed session detail › opencode observed idle: inject-now presets via session_command (plugin queue)1 ms
  • ✓D200H observed session detail › opencode observed permission gate: ALLOW/DENY carry the ocperm requestId1 ms
  • ✓D200H observed session detail › managed sessions keep the original idle quick-actions (regression)1 ms
shared/src/__tests__/d200h-openclaw-approval.test.ts8/8
  • ✓D200H — OpenClaw exec approval › renders the real command and its decisions, not the global idle state6 ms
  • ✓D200H — OpenClaw exec approval › every decision is a pressable select_option carrying its own index2 ms
  • ✓D200H — OpenClaw exec approval › keeps the relayed event authoritative when IT is the one with the prompt1 ms
  • ✓D200H — OpenClaw exec approval › an idle gateway row still reads as idle1 ms
  • ✓D200H — the approval says what it is › renders the command on the hero cell, not only in the press echo1 ms
  • ✓D200H — the approval says what it is › renders WHY approval was demanded — the head of questionDetail1 ms
  • ✓D200H — the approval says what it is › still echoes the question on a press (the answer path is unchanged)0 ms
  • ✓D200H — the approval says what it is › an idle row draws no prompt text — this is not a permanent header0 ms
shared/src/__tests__/esp32-boards.test.ts15/15
  • ✓ESP32 board SSOT › agrees with platformio.ini, the spec sheet, cli.ts and docs/esp32.md7 ms
  • ✓ESP32 board SSOT › keeps the release matrix generated, not hand-maintained1 ms
  • ✓ESP32 board SSOT › keeps the Swift Surface product allow-list on the board SSOT3 ms
  • ✓ESP32 board SSOT › never claims a browser-flashable board without evidence2 ms
  • ✓ESP32 board SSOT › puts every board on esptool's bootloader-offset table1 ms
  • ✓ESP32 board SSOT › resolves every documented alias to exactly one board1 ms
  • ✓ESP32 board SSOT › keeps ids unique0 ms
  • ✓esp32ChipFamilyOf › classifies ESP32-S3 (QFN56) (revision v0.2)0 ms
  • ✓esp32ChipFamilyOf › classifies ESP32-D0WD (revision 1)0 ms
  • ✓esp32ChipFamilyOf › classifies ESP32-D0WDQ6 (revision 1)0 ms
  • ✓esp32ChipFamilyOf › does not let the classic case swallow the variants0 ms
  • ✓flash guards › treats an all-ones or all-zeros flash id as no answer0 ms
  • ✓flash guards › is directional: under-declaring flash is safe, over-declaring is not0 ms
  • ✓flash guards › resolves a legacy wire id a deployed board still reports0 ms
  • ✓flash guards › returns unknown rather than a verdict when the size could not be read0 ms
shared/src/__tests__/idotmatrix-identity.test.ts12/12
  • ✓BLE UUID normalization › expands the short advertisement forms onto the Bluetooth base UUID3 ms
  • ✓BLE UUID normalization › left-pads a short form that dropped its leading zeroes0 ms
  • ✓BLE UUID normalization › leaves an unrelated 128-bit UUID alone0 ms
  • ✓iDotMatrix discovery predicate › matches the iDotMatrix and iPixel name families case-insensitively0 ms
  • ✓iDotMatrix discovery predicate › does not match unrelated peripherals0 ms
  • ✓iDotMatrix discovery predicate › accepts a user-configured prefix for the next rebrand0 ms
  • ✓iDotMatrix discovery predicate › identifies an unnamed peripheral by its advertised service UUID0 ms
  • ✓iDotMatrix discovery predicate › still identifies a named panel that keeps the service out of its advertisement0 ms
  • ✓iDotMatrix discovery predicate › rejects a peripheral with neither signal0 ms
  • ✓generated mirrors in sync › apple/AgentDeck/Daemon/Modules/IDotMatrixIdentity.generated.swift matches the SSOT0 ms
  • ✓generated mirrors in sync › bridge/src/idotmatrix/identity_generated.py matches the SSOT0 ms
  • ✓generated mirrors in sync › emitters embed the SSOT literals (sanity on the emitters themselves)1 ms
shared/src/__tests__/mdns-identity.test.ts7/7
  • ✓sanitizeMdnsLabel › keeps a hostname greppable and collision-free7 ms
  • ✓sanitizeMdnsLabel › bounds the label so the whole instance name stays inside DNS-SD limits1 ms
  • ✓mdnsUserTag › is stable, short, and never the account name1 ms
  • ✓mdnsUserTag › separates two accounts on one machine1 ms
  • ✓buildMdnsInstanceName › names host, user and port — the three ways two daemons can differ0 ms
  • ✓buildMdnsInstanceName › degrades to the old shape rather than emitting a malformed name0 ms
  • ✓generated Swift mirror › matches this source1 ms
shared/src/__tests__/terrarium-rules.test.ts5/5
  • ✓terrarium rules invariants › floor-rest clear anchor stays left of the crayfish claws3 ms
  • ✓terrarium rules invariants › rest strips sit above the crayfish, below mid-water0 ms
  • ✓generated mirrors in sync › apple/AgentDeck/Terrarium/TerrariumRules.generated.swift matches the SSOT1 ms
  • ✓generated mirrors in sync › android/app/src/main/kotlin/dev/agentdeck/terrarium/TerrariumRules.generated.kt matches the SSOT1 ms
  • ✓generated mirrors in sync › esp32/src/ui/terrarium/terrarium_rules_generated.h matches the SSOT0 ms

6 files

Pass

Voice

Are push-to-talk, transcription and spoken replies routed to the right session and device?

Passed
111
Failed
0
Skipped
0
Time
176 ms
6 test files
bridge/src/__tests__/device-transcription.test.ts20/20
  • ✓device speech backend › preserves Apple as the default5 ms
  • ✓device speech backend › runs only the explicitly configured local executable with bounded argument-based IO1 ms
  • ✓device speech backend › refuses missing executable/model configuration1 ms
  • ✓device speech backend › propagates local errors and rejects empty speech without falling back1 ms
  • ✓warm local speech server › uploads locally with a deadline and never follows redirects29 ms
  • ✓warm local speech server › refuses https://127.0.0.1/inference before sending audio1 ms
  • ✓warm local speech server › refuses http://example.com/inference before sending audio0 ms
  • ✓warm local speech server › refuses http://127.0.0.1/other before sending audio0 ms
  • ✓warm local speech server › refuses http://user@127.0.0.1/inference before sending audio0 ms
  • ✓warm local speech server › falls back to configured CLI on unreachable1 ms
  • ✓warm local speech server › falls back to configured CLI on timeout0 ms
  • ✓warm local speech server › falls back to configured CLI on bad response1 ms
  • ✓warm local speech server › does not retry genuinely empty speech1 ms
  • ✓speech-presence gate › rejects silence before recognition or any CLI fallback2 ms
  • ✓speech-presence gate › passes the unchanged recording after positive VAD evidence2 ms
  • ✓speech-presence gate › fails closed for an invalid VAD response: 1 ms
  • ✓speech-presence gate › fails closed for an invalid VAD response: model missing0 ms
  • ✓speech-presence gate › fails closed for an invalid VAD response: Detected 1 speech segments:0 ms
  • ✓speech-presence gate › fails closed for an invalid VAD response: Detected 1 speech segments: Speech segment 0: start = 10.00, end = 1.000 ms
  • ✓speech-presence gate › does not bypass a failed or incomplete VAD setup1 ms
bridge/src/__tests__/device-voice-reply.test.ts35/35
  • ✓speakableReply › replaces fenced code with a spoken marker instead of reciting it3 ms
  • ✓speakableReply › strips markdown scaffolding and URLs that read as noise0 ms
  • ✓speakableReply › returns empty when nothing speakable survives0 ms
  • ✓speakableReply › cuts at a sentence boundary when the cap lands mid-sentence0 ms
  • ✓speakableReply › hard-caps when no sentence boundary is near the cap0 ms
  • ✓pcmFromWav › walks chunks rather than assuming the data starts at byte 441 ms
  • ✓pcmFromWav › rejects non-RIFF and truncated input0 ms
  • ✓pcmFromWav › clamps a data chunk that claims more bytes than the file holds0 ms
  • ✓pcmFrames › splits into fixed frames with a short tail0 ms
  • ✓DeviceVoiceReplyRouter › only arms boards that advertise an amplifier2 ms
  • ✓DeviceVoiceReplyRouter › does not arm without a session id0 ms
  • ✓DeviceVoiceReplyRouter › matches only the session that was dictated to0 ms
  • ✓DeviceVoiceReplyRouter › expires an arming once the window closes, so a stale answer stays silent0 ms
  • ✓DeviceVoiceReplyRouter › keeps an arming across a transport blip, and refuses to stream while down1 ms
  • ✓DeviceVoiceReplyRouter › streams begin, paced PCM frames, then end1 ms
  • ✓DeviceVoiceReplyRouter › consumes the arming so one dictation yields one spoken reply0 ms
  • ✓DeviceVoiceReplyRouter › stops mid-stream when the board disconnects and sends no end frame0 ms
  • ✓DeviceVoiceReplyRouter › refuses a WAV it cannot parse rather than streaming garbage0 ms
  • ✓DeviceVoiceReplyRouter › disarm removes a target without streaming0 ms
  • ✓session-id keying across the two forms › matches a bare-uuid completion against a prefixed arming0 ms
  • ✓session-id keying across the two forms › matches in the other direction too0 ms
  • ✓session-id keying across the two forms › normalizes the claude prefix0 ms
  • ✓session-id keying across the two forms › normalizes the codex prefix0 ms
  • ✓session-id keying across the two forms › normalizes the codex-app prefix0 ms
  • ✓session-id keying across the two forms › normalizes the opencode prefix0 ms
  • ✓session-id keying across the two forms › normalizes the antigravity prefix0 ms
  • ✓session-id keying across the two forms › still keeps different sessions apart0 ms
  • ✓long-running turns › keeps an arming alive while the session is still working0 ms
  • ✓long-running turns › still expires a session that went quiet0 ms
  • ✓long-running turns › refresh accepts either id form and ignores an empty list0 ms
  • ✓following the board across transports › streams to the board's current transport when the armed one is gone0 ms
  • ✓following the board across transports › gives up when the board is not reachable at all0 ms
  • ✓following the board across transports › prefers the armed transport while it is still open0 ms
  • ✓HTTP reply target after synthesis › moves a pull notification to the live connection after reconnect0 ms
  • ✓HTTP reply target after synthesis › upgrades a metadata-only connection to a pull-capable transport0 ms
bridge/src/__tests__/device-voice.test.ts18/18
  • ✓dropSilentInterleave › drops a digitally silent odd slot (ips10 duplex shape)3 ms
  • ✓dropSilentInterleave › drops a digitally silent even slot (flipped alignment)0 ms
  • ✓dropSilentInterleave › passes true mono audio through untouched0 ms
  • ✓dropSilentInterleave › still collapses when the stuffed slot carries rare glitch spikes1 ms
  • ✓dropSilentInterleave › passes silence and tiny buffers through untouched0 ms
  • ✓normalizePcm › boosts a quiet capture toward the target peak0 ms
  • ✓normalizePcm › caps the gain so near-silence does not become full-scale noise0 ms
  • ✓normalizePcm › leaves already-loud audio untouched0 ms
  • ✓normalizePcm › leaves pure silence untouched0 ms
  • ✓normalizePcm › clamps instead of wrapping at the int16 edge1 ms
  • ✓buildWav › wraps PCM in a canonical 16-bit mono header1 ms
  • ✓DeviceVoiceCollector › assembles frames into a WAV and reports the target session3 ms
  • ✓DeviceVoiceCollector › ignores binary frames with no open utterance0 ms
  • ✓DeviceVoiceCollector › keeps utterances separate per connection1 ms
  • ✓DeviceVoiceCollector › returns null for a cancelled or empty utterance0 ms
  • ✓DeviceVoiceCollector › caps a runaway utterance instead of buffering without bound53 ms
  • ✓DeviceVoiceCollector › reports device-side and in-flight PCM loss before transcription4 ms
  • ✓DeviceVoiceCollector › abandons and sweeps stale utterances0 ms
bridge/src/__tests__/personal-voice-turn.test.ts15/15
  • ✓personal voice turn › passes an explicit voice-only thinking override without changing the session key8 ms
  • ✓personal voice turn › allows a dedicated voice conversation on the same personal agent2 ms
  • ✓personal voice turn › removes the local recognizer spelling of the wake word0 ms
  • ✓personal voice turn › routes to the personal conversation and ignores other runs/cron replies1 ms
  • ✓personal voice turn › retains a completion that precedes the send acknowledgement1 ms
  • ✓personal voice turn › uses only matching delta text when final has no text0 ms
  • ✓personal voice turn › cleans listeners on refusal and never substitutes a different conversation3 ms
  • ✓personal voice turn › bounds peer silence and handles aborted requests6 ms
  • ✓personal speech readiness › waits for a closed explicit summary and speaks it before the final details1 ms
  • ✓personal speech readiness › does not treat an acknowledgement, an unfinished paragraph or a code block as a ready summary1 ms
  • ✓personal speech readiness › keeps full-reply consumers waiting for the final1 ms
  • ✓personal speech readiness › recovers an empty final only from a late snapshot of the same run1 ms
  • ✓personal speech readiness › rejects an unrecoverable empty final promptly instead of silently succeeding1 ms
  • ✓personal speech readiness › preserves a final received before acknowledgement even when a late delta follows0 ms
  • ✓personal speech readiness › does not hide an abort that arrives before acknowledgement1 ms
shared/src/__tests__/voice-ptt.test.ts8/8
  • ✓Stream Deck VOICE hold lifecycle › maps key-down plus a real hold to begin then end4 ms
  • ✓Stream Deck VOICE hold lifecycle › cancels a key-clack tap shorter than the minimum hold0 ms
  • ✓Stream Deck VOICE hold lifecycle › cancels a held key that disappears during a page or profile switch0 ms
  • ✓Stream Deck VOICE hold lifecycle › ignores lifecycle events from another action instance0 ms
  • ✓Stream Deck VOICE hold lifecycle › maps terminal PTT actions to daemon wire commands0 ms
  • ✓cancelActive — releases that can never arrive › cancels the open hold without knowing which key owns it0 ms
  • ✓cancelActive — releases that can never arrive › is a no-op when nothing is held, so a stray socket close sends nothing0 ms
  • ✓cancelActive — releases that can never arrive › does not fire twice for one hold0 ms
shared/src/__tests__/voice-reply-digest.test.ts15/15
  • ✓spokenDigest parity cases › first sentence only, not the whole paragraph5 ms
  • ✓spokenDigest parity cases › explicit summary label wins over the opening sentence0 ms
  • ✓spokenDigest parity cases › bare summary label takes the line after it0 ms
  • ✓spokenDigest parity cases › a heading-only first line is skipped for the prose under it0 ms
  • ✓spokenDigest parity cases › code fences never get recited0 ms
  • ✓spokenDigest parity cases › a version number does not end the sentence0 ms
  • ✓spokenDigest parity cases › nothing speakable stays empty0 ms
  • ✓spokenDigest › reads one sentence out of a long answer, not the whole thing1 ms
  • ✓spokenDigest › keeps a summary line even when it is the last thing in the reply0 ms
  • ✓spokenDigest › does not treat prose that merely opens with a label word as a summary1 ms
  • ✓spokenDigest › cuts an over-long single sentence on a word boundary1 ms
  • ✓spokenDigest › honours full: true for callers that opted back in0 ms
  • ✓spokenDigest › stays empty when there is nothing to say0 ms
  • ✓speakableReply › keeps the whole readable answer1 ms
  • ✓speakableReply › caps at the monologue ceiling0 ms

33 files

Pass

Stream Deck & Ulanzi plugins

Do the deck plugins connect, recover, lay out keys and encoders, and dispatch host controls per platform?

Passed
380
Failed
0
Skipped
0
Time
8.4 s
33 test files
plugin-ulanzi/src/__tests__/anim-schedule.test.ts11/11
  • ✓D200H animation schedule › spans exactly one animation cycle3 ms
  • ✓D200H animation schedule › steps evenly and monotonically through the cycle2 ms
  • ✓D200H animation schedule › keeps the dash step small enough to read as motion, not as a jump0 ms
  • ✓D200H animation schedule › paces the loop at the Stream Deck tick so both surfaces share a tempo0 ms
  • ✓D200H animation schedule › gives each key its own phase without breaking the loop3 ms
  • ✓D200H animation schedule › is deterministic per key so a tile keeps encoding to the same bytes0 ms
  • ✓D200H animation schedule › identifies a rotated loop by its OWN first frame, not by deck 02 ms
  • ✓D200H animation schedule › starts each key at the frame its phase offset names2 ms
  • ✓D200H animation schedule › separates neighbouring keys by a visible fraction of the cycle0 ms
  • ✓D200H animation schedule › spreads a full deck across the cycle instead of clustering0 ms
  • ✓D200H animation schedule › probes a frame that a moving tile cannot match by accident1 ms
plugin-ulanzi/src/__tests__/daemon-client.test.ts2/2
  • ✓does not open a socket after a pending discovery has been stopped3 ms
  • ✓registers the Ulanzi surface after health discovery and bounds the handshake4 ms
plugin-ulanzi/src/__tests__/daemon-discovery.test.ts13/13
  • ✓Ulanzi daemon discovery across process namespaces › finds the canonical hub without a Windows registry6 ms
  • ✓Ulanzi daemon discovery across process namespaces › uses a registry port even when its PID belongs to Linux0 ms
  • ✓Ulanzi daemon discovery across process namespaces › falls back after a stale registry and ignores invalid port values1 ms
  • ✓Ulanzi daemon discovery across process namespaces › does not escape an explicit development data directory1 ms
  • ✓bounded loopback health probe › accepts only a healthy, locally accessible daemon27 ms
  • ✓bounded loopback health probe › rejects an invalid, incomplete, or unauthorized health response 03 ms
  • ✓bounded loopback health probe › rejects an invalid, incomplete, or unauthorized health response 12 ms
  • ✓bounded loopback health probe › rejects an invalid, incomplete, or unauthorized health response 22 ms
  • ✓bounded loopback health probe › rejects an invalid, incomplete, or unauthorized health response 32 ms
  • ✓bounded loopback health probe › rejects an invalid, incomplete, or unauthorized health response 41 ms
  • ✓bounded loopback health probe › rejects an invalid, incomplete, or unauthorized health response 52 ms
  • ✓bounded loopback health probe › rejects an invalid, incomplete, or unauthorized health response 62 ms
  • ✓bounded loopback health probe › rejects HTTP errors and bounds silent peers28 ms
plugin-ulanzi/src/__tests__/deck-signature.test.ts11/11
  • ✓D200H deckSignature — every render-affecting change must be visible › is stable when nothing changed3 ms
  • ✓D200H deckSignature — every render-affecting change must be visible › changes when a multi-question prompt advances to the next question0 ms
  • ✓D200H deckSignature — every render-affecting change must be visible › changes when only the question text moves0 ms
  • ✓D200H deckSignature — every render-affecting change must be visible › changes when only the option labels move0 ms
  • ✓D200H deckSignature — every render-affecting change must be visible › changes when only the group index moves0 ms
  • ✓D200H deckSignature — every render-affecting change must be visible › changes when answerability flips (dead-button regression)0 ms
  • ✓D200H deckSignature — every render-affecting change must be visible › changes when the prompt shape flips to multi-select0 ms
  • ✓D200H deckSignature — every render-affecting change must be visible › changes on review badge transitions0 ms
  • ✓D200H deckSignature — every render-affecting change must be visible › changes on usage-only updates (quota gauges ride a separate event)2 ms
  • ✓local weekly preference and quota-only repaint › invalidates for every weekly view and changed key placement0 ms
  • ✓local weekly preference and quota-only repaint › redraws Fable-only percent, binding and reset changes1 ms
plugin-ulanzi/src/__tests__/device-support.test.ts2/2
  • ✓Ulanzi device support contract › offers the existing 14-key action on both D200H and D200X4 ms
  • ✓Ulanzi device support contract › does not claim D200X encoder support before an encoder action exists1 ms
plugin-ulanzi/src/__tests__/manifest-localization.test.ts6/6
  • ✓ulanzi manifest ↔ localization alignment › declares exactly one dynamic action3 ms
  • ✓ulanzi manifest ↔ localization alignment › en.json repeats the manifest text verbatim0 ms
  • ✓ulanzi manifest ↔ localization alignment › zh_CN.json Actions is index-aligned with the manifest1 ms
  • ✓ulanzi manifest ↔ localization alignment › zh_HK.json Actions is index-aligned with the manifest0 ms
  • ✓ulanzi manifest ↔ localization alignment › ja_JP.json Actions is index-aligned with the manifest0 ms
  • ✓ulanzi manifest ↔ localization alignment › ko_KR.json Actions is index-aligned with the manifest0 ms
plugin-ulanzi/src/__tests__/property-inspector.test.ts10/10
  • ✓ulanzi property inspector › is declared by the manifest and present on disk3 ms
  • ✓ulanzi property inspector › loads the SDK Property Inspector libraries that the package ships1 ms
  • ✓ulanzi property inspector › opens the H5 tutorial through the SDK, at a path the package contains1 ms
  • ✓ulanzi property inspector › is self-contained — no remote asset can leave either surface half-drawn2 ms
  • ✓ulanzi property inspector › keeps English in the markup, not only in the language files0 ms
  • ✓ulanzi property inspector › resolves every data-localize key against every shipped language16 ms
  • ✓ulanzi property inspector › resolves every runtime string lookup against every shipped language12 ms
  • ✓ulanzi property inspector › keeps every language key-aligned with canonical en16 ms
  • ✓ulanzi property inspector › preserves the {port} placeholder the script substitutes2 ms
  • ✓ulanzi property inspector › reads the daemon only through routes that carry no secret0 ms
plugin-ulanzi/src/__tests__/reconnect-supervisor.test.ts8/8
  • ✓ReconnectSupervisor › connects once on start and reports the first open without firing onReconnect5 ms
  • ✓ReconnectSupervisor › reconnects with backoff after a drop and fires onReconnect on re-open1 ms
  • ✓ReconnectSupervisor › ignores the spurious close emitted while a connect is in flight0 ms
  • ✓ReconnectSupervisor › retries via connect-timeout when an attempt neither opens nor closes0 ms
  • ✓ReconnectSupervisor › escalates the backoff ladder across consecutive failed attempts0 ms
  • ✓ReconnectSupervisor › forces a fresh connect on wake even when the socket looked open (half-open)1 ms
  • ✓ReconnectSupervisor › reconnects immediately on wake while disconnected (backoff reset)0 ms
  • ✓ReconnectSupervisor › stop() halts the watchdog so wake no longer reconnects0 ms
plugin-ulanzi/src/__tests__/state-store.test.ts7/7
  • ✓D200H StateStore session isolation › keeps daemon transport separate from an empty disconnected aggregate state6 ms
  • ✓D200H StateStore session isolation › does not use the global OpenClaw model while Claude focus is pending1 ms
  • ✓D200H StateStore session isolation › ignores unscoped and foreign prompt options for the selected session0 ms
  • ✓D200H StateStore session isolation › applies prompt options only to their correlated session1 ms
  • ✓D200H StateStore — observed rows are not shadowed by the focus reply › keeps the live question and options when the focus reply lands1 ms
  • ✓D200H StateStore — observed rows are not shadowed by the focus reply › lets the roster row win even over a snapshot stored before the row arrived0 ms
  • ✓D200H StateStore — observed rows are not shadowed by the focus reply › still lets a managed session's live state override its roster row0 ms
plugin/src/__tests__/bridge-client.test.ts7/7
  • ✓BridgeClient — port provider › counts a real pong as activity and clears a stale connection129 ms
  • ✓BridgeClient — port provider › retries an in-flight connection without leaving the old generation stuck55 ms
  • ✓BridgeClient — port provider › does not reconnect after an explicit disconnect1.3 s
  • ✓BridgeClient — port provider › bounds a silent handshake and advances to another daemon candidate1.3 s
  • ✓BridgeClient — port provider › skips connect when provider returns null51 ms
  • ✓BridgeClient — port provider › connects once provider returns a live port1.3 s
  • ✓BridgeClient — port provider › rebinds to a new port when provider value changes2.1 s
plugin/src/__tests__/connection-integration.test.ts6/6
  • ✓ConnectionManager Integration — Real WebSocket › client connects and receives broadcast events236 ms
  • ✓ConnectionManager Integration — Real WebSocket › multiple clients receive same broadcast208 ms
  • ✓ConnectionManager Integration — Real WebSocket › client sends command to server154 ms
  • ✓ConnectionManager Integration — Real WebSocket › handles rapid connect/disconnect without crashes413 ms
  • ✓ConnectionManager Integration — Real WebSocket › server detects client disconnect253 ms
  • ✓ConnectionManager Integration — Real WebSocket › bridge → gateway priority: second server takes over on disconnect409 ms
plugin/src/__tests__/connection-manager.test.ts26/26
  • ✓ConnectionManager › starts disconnected3 ms
  • ✓ConnectionManager › start() begins bridge connection to daemon1 ms
  • ✓ConnectionManager › start() installs a port provider so daemon.json is re-read every attempt0 ms
  • ✓ConnectionManager › emits connected when bridge connects1 ms
  • ✓ConnectionManager › emits disconnected when bridge disconnects1 ms
  • ✓ConnectionManager › forwards state_update from bridge1 ms
  • ✓ConnectionManager › send() delegates to bridge2 ms
  • ✓ConnectionManager › send() drops command when not connected0 ms
  • ✓ConnectionManager › getBridgePort returns bridge port0 ms
  • ✓ConnectionManager › getConnectionSnapshot exposes connection and discovery state0 ms
  • ✓ConnectionManager › retryNow probes daemon discovery and reconnects immediately when disconnected1 ms
  • ✓ConnectionManager › switchToOpenClaw() › sends switch_agent command to bridge1 ms
  • ✓ConnectionManager › switchToClaude() › sends switch_agent command to bridge0 ms
  • ✓ConnectionManager › findDaemonPort() candidate fallthrough › tries the socket when a daemon PID probe is permission denied1 ms
  • ✓ConnectionManager › findDaemonPort() candidate fallthrough › skips a daemon whose PID is confirmed absent0 ms
  • ✓ConnectionManager › findDaemonPort() candidate fallthrough › rejects malformed daemon identity {"port":9120,"pid":0} before probing0 ms
  • ✓ConnectionManager › findDaemonPort() candidate fallthrough › rejects malformed daemon identity {"port":9120,"pid":-1} before probing0 ms
  • ✓ConnectionManager › findDaemonPort() candidate fallthrough › rejects malformed daemon identity {"port":9120,"pid":"1234"} before probing0 ms
  • ✓ConnectionManager › findDaemonPort() candidate fallthrough › rejects malformed daemon identity {"port":65536,"pid":1234} before probing0 ms
  • ✓ConnectionManager › findDaemonPort() candidate fallthrough › rejects malformed daemon identity {"port":0,"pid":1234} before probing0 ms
  • ✓ConnectionManager › findDaemonPort() candidate fallthrough › rejects malformed daemon identity {"port":"9120","pid":1234} before probing0 ms
  • ✓ConnectionManager › findDaemonPort() candidate fallthrough › prefers the CLI daemon when it is healthy0 ms
  • ✓ConnectionManager › findDaemonPort() candidate fallthrough › falls through to the Swift daemon after the CLI port fails0 ms
  • ✓ConnectionManager › findDaemonPort() candidate fallthrough › falls through after an initial handshake failure without a prior connection1 ms
  • ✓ConnectionManager › findDaemonPort() candidate fallthrough › reports the daemon as found even when every candidate has failed1 ms
  • ✓ConnectionManager › findDaemonPort() candidate fallthrough › reports missing when no daemon.json exists0 ms
plugin/src/__tests__/connection-status-pi.test.ts11/11
  • ✓buildConnectionStatusPayload — snapshot -> PI payload mapping › never probed yet: unknown status, no path, no port, no probe time4 ms
  • ✓buildConnectionStatusPayload — snapshot -> PI payload mapping › daemon.json not found: missing status, plain-words path, still no port1 ms
  • ✓buildConnectionStatusPayload — snapshot -> PI payload mapping › daemon found but not yet connected: shows the DISCOVERED port and resolved path0 ms
  • ✓buildConnectionStatusPayload — snapshot -> PI payload mapping › connected: shows the LIVE bridge port, not the (possibly stale) discovery candidate0 ms
  • ✓buildConnectionStatusPayload — snapshot -> PI payload mapping › carries lastRetryAt through untouched, including null0 ms
  • ✓buildConnectionStatusPayload — snapshot -> PI payload mapping › a zero/negative port never renders as a real port0 ms
  • ✓inbound PI message recognition › isRetryNowMessage matches only the retryNow event1 ms
  • ✓inbound PI message recognition › isRequestConnectionStatusMessage matches only the requestConnectionStatus event1 ms
  • ✓retry round-trip: PI message -> ConnectionManager.retryNow() -> payload › before any daemon.json exists: payload reports "not found"1 ms
  • ✓retry round-trip: PI message -> ConnectionManager.retryNow() -> payload › a retryNow message from the PI drives discovery, and the rebuilt payload reflects the new answer1 ms
  • ✓retry round-trip: PI message -> ConnectionManager.retryNow() -> payload › a non-retry message must not be treated as a retry0 ms
plugin/src/__tests__/deck-usage-modes.test.ts8/8
  • ✓weekly key display modes › cycles three views on the same SD key without moving or changing another deck6 ms
  • ✓weekly key display modes › retains the preference across quota disappearance, stale snapshots and return1 ms
  • ✓weekly key display modes › D200H shares the weekly key with 0 sessions and cycles locally5 ms
  • ✓weekly key display modes › D200H shares the weekly key with 13 sessions and cycles locally10 ms
  • ✓weekly key display modes › D200H keeps a scoped-only reading when weekly data is absent1 ms
  • ✓SD+ independent subscription pages › offers both Claude overviews on either dial without hiding 7D-only selection2 ms
  • ✓SD+ independent subscription pages › restores duplicate manual choices and leaves the peer alone after every selection1 ms
  • ✓SD+ independent subscription pages › makes Antigravity a confirmed plan page, never a credit or percentage gauge1 ms
plugin/src/__tests__/device-profile.test.ts3/3
  • ✓Stream Deck device profiles › maps every documented Elgato DeviceType without conflating families5 ms
  • ✓Stream Deck device profiles › uses the larger text profile only for original 72px keys0 ms
  • ✓Stream Deck device profiles › does not coerce missing or malformed device types to Stream Deck1 ms
plugin/src/__tests__/encoder-offline-policy.test.ts3/3
  • ✓renderOfflineTouchStrip — all-or-nothing 800px slice contract › every slice is a 200x100 canvas carrying the shared banner content5 ms
  • ✓renderOfflineTouchStrip — all-or-nothing 800px slice contract › slices 0..3 offset the same banner by -index*200 (E0..E3 align into one strip)1 ms
  • ✓renderOfflineTouchStrip — all-or-nothing 800px slice contract › uses a per-slice gradient id so the four panels are independent SVGs1 ms
plugin/src/__tests__/focused-detail-state.test.ts5/5
  • ✓FocusedDetailState › does not carry an OpenClaw GLM model into a focused Claude session4 ms
  • ✓FocusedDetailState › drops late options from another session and unscoped legacy options1 ms
  • ✓FocusedDetailState › accepts options correlated to the selected session0 ms
  • ✓FocusedDetailState › keeps an observed session's live prompt when the focus reply lands0 ms
  • ✓FocusedDetailState › still lets a managed session's live state replace its detail snapshot0 ms
plugin/src/__tests__/launcher-target.test.ts16/16
  • ✓launcher: runTarget fallback chain › opens a desktop app for an app: target5 ms
  • ✓launcher: runTarget fallback chain › opens a browser tab for a url: target1 ms
  • ✓launcher: runTarget fallback chain › stops at the first step that succeeds0 ms
  • ✓launcher: runTarget fallback chain › falls through to the URL when the app is not installed1 ms
  • ✓launcher: runTarget fallback chain › rejects when every step fails, so the dial can showAlert2 ms
  • ✓launcher: runTarget fallback chain › rejects an unrecognized scheme rather than guessing1 ms
  • ✓launcher: runTarget fallback chain › rejects an empty target0 ms
  • ✓launcher: runTarget fallback chain › tolerates whitespace around chain steps0 ms
  • ✓launcher: shipped defaults › every default target parses into runnable steps1 ms
  • ✓launcher: shipped defaults › falls back to a URL for both agents that have a desktop app0 ms
  • ✓launcher: entry list and rolling › lists every agent in a stable order1 ms
  • ✓launcher: entry list and rolling › honours a per-agent override from the Property Inspector0 ms
  • ✓launcher: entry list and rolling › ignores a blank override rather than launching nothing0 ms
  • ✓launcher: entry list and rolling › wraps the rolling index in both directions0 ms
  • ✓launcher: entry list and rolling › never returns a negative or out-of-range index1 ms
  • ✓launcher: entry list and rolling › returns 0 for an empty list instead of NaN0 ms
plugin/src/__tests__/manifest-platform.test.ts2/2
  • ✓Stream Deck Marketplace platform contract › ships installable entries for both maintained desktop platforms4 ms
  • ✓Stream Deck Marketplace platform contract › mirrors the plugin package version as X.Y.Z.01 ms
plugin/src/__tests__/per-action-view-state.test.ts4/4
  • ✓PerActionViewState › rotates one action without changing another action3 ms
  • ✓PerActionViewState › restores a persisted view for each action0 ms
  • ✓PerActionViewState › falls back when a dynamic scoped view disappears0 ms
  • ✓PerActionViewState › wraps in both directions0 ms
plugin/src/__tests__/renderer-snapshots.test.ts40/40
  • ✓utility-renderer snapshots › renderUtilityGeneric with icon5 ms
  • ✓utility-renderer snapshots › renderUtilityGeneric muted (text-only value)1 ms
  • ✓launcher-renderer snapshots › renderLauncher first entry0 ms
  • ✓launcher-renderer snapshots › renderLauncher last entry0 ms
  • ✓launcher-renderer snapshots › renderLauncherEmpty0 ms
  • ✓usage-dial-renderer snapshots › renderUsageSession1 ms
  • ✓usage-gauge snapshots › renderUsageGauge Claude 5h (green ramp, low usage)2 ms
  • ✓usage-gauge snapshots › renderUsageGauge Claude 7d0 ms
  • ✓usage-gauge snapshots › renderUsageGauge Codex 5h uses the blue brand logo + green ramp1 ms
  • ✓usage-gauge snapshots › renderUsageGauge Codex 7d0 ms
  • ✓usage-gauge snapshots › renderUsageGauge critical (>=90 used → red ramp)0 ms
  • ✓usage-gauge snapshots › renderUsageGauge full-bleed fill (no narrow 60px tank rect)0 ms
  • ✓usage-gauge snapshots › renderUsageGauge unknown draws a dim tile + dash, no fill0 ms
  • ✓usage-encoder level-fill (SD+ 200x100) snapshots › both-view Claude shows both windows + terracotta brand logo1 ms
  • ✓usage-encoder level-fill (SD+ 200x100) snapshots › both-view Codex uses the blue brand logo + severity ramp1 ms
  • ✓usage-encoder level-fill (SD+ 200x100) snapshots › single 5h view enlarges the 5H window across the LCD0 ms
  • ✓usage-encoder level-fill (SD+ 200x100) snapshots › single 7d view enlarges the 7D window across the LCD0 ms
  • ✓usage-encoder level-fill (SD+ 200x100) snapshots › note suppresses the gauges (No Codex usage)0 ms
  • ✓usage-encoder level-fill (SD+ 200x100) snapshots › Waiting note before the first usage payload0 ms
  • ✓usage-encoder level-fill (SD+ 200x100) snapshots › a single unknown window draws a dash panel, the other a real fill0 ms
  • ✓session-slot-renderer snapshots › disconnected hero is icon-rich1 ms
  • ✓session-slot-renderer snapshots › disconnected non-center slot is empty0 ms
  • ✓session-slot-renderer snapshots › disconnected cluster quadrant tl0 ms
  • ✓session-slot-renderer snapshots › disconnected cluster quadrant tr0 ms
  • ✓session-slot-renderer snapshots › disconnected cluster quadrant bl0 ms
  • ✓session-slot-renderer snapshots › disconnected cluster quadrant br0 ms
  • ✓session-slot-renderer snapshots › connected no-session card is icon-rich0 ms
  • ✓session-slot-renderer snapshots › active idle session uses orbiting focus border1 ms
  • ✓session-slot-renderer snapshots › stale session dims the render and shows a STALE badge1 ms
  • ✓session-slot-renderer snapshots › wraps names with larger typography on original Stream Deck keys1 ms
  • ✓display-tile snapshots › renderStatusReadout MODEL is flat (no raised bezel, no glyph)1 ms
  • ✓display-tile snapshots › renderStatusReadout READY (label only)0 ms
  • ✓display-tile snapshots › renderStatusReadout AWAITING2 ms
  • ✓display-tile snapshots › renderStatusReadout HUB READY (no-session hub state)0 ms
  • ✓display-tile snapshots › renderSessionReadout keeps name/model/state, flat & non-interactive1 ms
  • ✓display-tile snapshots › renderSessionReadout openclaw hides model, shows STANDBY1 ms
  • ✓agent-logos snapshots › claude-code watermark1 ms
  • ✓agent-logos snapshots › openclaw watermark1 ms
  • ✓agent-logos snapshots › antigravity icon uses the full-color mark1 ms
  • ✓agent-logos snapshots › CLAUDE_LOGO_PATH is defined1 ms
plugin/src/__tests__/session-slot-manager.test.ts61/61
  • ✓SessionSlotManager detail layout › re-points detail focus onto the codex fold representative when the focused thread is absorbed7 ms
  • ✓SessionSlotManager detail layout › exits detail view when the focused session is gone with no fold successor0 ms
  • ✓SessionSlotManager detail layout › folds codex companion threads by project before slot assignment2 ms
  • ✓SessionSlotManager detail layout › renders connected no-session list as status cards instead of text-only empty buttons1 ms
  • ✓SessionSlotManager detail layout › renders the OpenClaw exec approval from the row when the relay says idle2 ms
  • ✓SessionSlotManager detail layout › shows an OpenClaw approval as display-only when it is not deliverable0 ms
  • ✓SessionSlotManager detail layout › puts processing tool info before OpenClaw presets1 ms
  • ✓SessionSlotManager detail layout › keeps a processing status tile even before tool metadata arrives0 ms
  • ✓SessionSlotManager detail layout › aliases the model name on detail MODEL surfaces (status card + OpenClaw preset)2 ms
  • ✓SessionSlotManager detail layout › renders the MODEL tile exactly once in Claude PROCESSING detail (no duplicate)1 ms
  • ✓SessionSlotManager detail layout › does not duplicate MODEL on OpenClaw idle (preset + status card)1 ms
  • ✓SessionSlotManager detail layout › does not render a READY/idle tile while a Claude session is PROCESSING1 ms
  • ✓SessionSlotManager detail layout › keeps observed Claude PROCESSING free of queued task buttons1 ms
  • ✓SessionSlotManager detail layout › observed AskUserQuestion: options are inert without liveAnswerable, pressable with it1 ms
  • ✓SessionSlotManager detail layout › renders no MODEL tile for observed Codex PROCESSING on every Stream Deck layout1 ms
  • ✓SessionSlotManager detail layout › never renders MODEL as a key for any observed session state2 ms
  • ✓SessionSlotManager detail layout › does not render a STANDBY/idle tile while an OpenClaw session is PROCESSING1 ms
  • ✓SessionSlotManager detail layout › uses actual parser options and reserves MORE only when awaiting overflow exists1 ms
  • ✓SessionSlotManager detail layout › resets option paging when the question changes, not just when awaiting ends1 ms
  • ✓SessionSlotManager detail layout › makes observed options pressable exactly when the daemon says they are answerable1 ms
  • ✓SD+ keypad relocation (Phase 2) › AWAITING permission renders selectable option buttons + ESC on the SD+ keypad0 ms
  • ✓SD+ keypad relocation (Phase 2) › shows a SUGGESTED quick-send button leading the IDLE detail content on SD+0 ms
  • ✓SD+ keypad relocation (Phase 2) › does NOT add a SUGGESTED button on classic Stream Deck (behavior unchanged)1 ms
  • ✓SD+ keypad relocation (Phase 2) › drops the SUGGESTED button when the session leaves IDLE0 ms
  • ✓SessionSlotManager list-view usage tiles › pins Claude 5H/7D to the last two keys of a classic Stream Deck (no Codex)8 ms
  • ✓SessionSlotManager list-view usage tiles › reserves 4 keys (Claude 5h/7d + Codex 5h/7d), left-aligned, when both agents report quota1 ms
  • ✓SessionSlotManager list-view usage tiles › hides Codex tiles when only Claude reports quota (reserve 2, not 4)1 ms
  • ✓SessionSlotManager list-view usage tiles › hides Claude tiles when only Codex reports quota (reserve 2 Codex tiles)1 ms
  • ✓SessionSlotManager list-view usage tiles › replaces the Codex 5h/7d keys with one LUNA gauge while account quota is exhausted2 ms
  • ✓SessionSlotManager list-view usage tiles › seats the purchased-credit balance ahead of the Luna reserve once a Codex window is exhausted2 ms
  • ✓SessionSlotManager list-view usage tiles › does NOT reserve usage on Stream Deck+ (encoder carries usage)1 ms
  • ✓SessionSlotManager list-view usage tiles › does NOT reserve usage on the Stream Deck + XL (6 dials carry usage)2 ms
  • ✓SessionSlotManager list-view usage tiles › pins usage to the last keys of the Stream Deck XL (no encoder, 32 keys)0 ms
  • ✓SessionSlotManager list-view usage tiles › isPlusFamily covers both dial-bearing families but not the plain XL0 ms
  • ✓SessionSlotManager list-view usage tiles › reserves NO usage keys when no quota was fed (hide-if-absent)0 ms
  • ✓SessionSlotManager list-view usage tiles › drops Claude tiles when its quota goes stale (hide-if-absent on stale)0 ms
  • ✓SessionSlotManager list-view usage tiles › fits 13 sessions on a classic deck without paging (15 keys − 2 usage)1 ms
  • ✓SessionSlotManager list-view usage tiles › paginates over capacity: NEXT→ at slot 12, usage at 13/141 ms
  • ✓SessionSlotManager list-view usage tiles › repositions NEXT→ ahead of a 4-key usage block when paginating1 ms
  • ✓SessionSlotManager list-view usage tiles › pressing a usage tile resolves to refresh-usage0 ms
  • ✓SessionSlotManager list-view usage tiles › shows usage tiles even with zero sessions0 ms
  • ✓VOICE hold-to-talk key › renders VOICE after the CC idle presets and maps its press to voice-ptt-begin1 ms
  • ✓VOICE hold-to-talk key › keeps localAction on materialized CC presets so REVIEW presses dispatch0 ms
  • ✓VOICE hold-to-talk key › restyles the VOICE key from daemon voice_state transitions0 ms
  • ✓VOICE hold-to-talk key › offers VOICE on observed Claude idle next to REVIEW (terminal-injection delivery)0 ms
  • ✓SessionSlotManager detail cache ownership › does not carry the previous session model into the next detail view0 ms
  • ✓SessionSlotManager detail cache ownership › seeds the detail readout from the session row rather than blanking it0 ms
  • ✓SessionSlotManager detail cache ownership › drops the cached model when re-entering a session that has none0 ms
  • ✓SessionSlotManager scoped cap vs the Codex usage keys › uses the entire Classic bottom row for Claude, weekly Codex and z.ai without MORE1 ms
  • ✓SessionSlotManager scoped cap vs the Codex usage keys › folds z.ai onto one key instead of paging when that makes the row fit2 ms
  • ✓SessionSlotManager scoped cap vs the Codex usage keys › keeps one z.ai window per key when the row has room1 ms
  • ✓SessionSlotManager scoped cap vs the Codex usage keys › pages only when the row still overflows after folding, and recovers when the usage set shrinks1 ms
  • ✓SessionSlotManager scoped cap vs the Codex usage keys › shares the weekly key with Fable even when there is spare capacity1 ms
  • ✓SessionSlotManager scoped cap vs the Codex usage keys › shows Claude 5H, 7D, active Fable, and Codex weekly together on 3 reserved keys1 ms
  • ✓SessionSlotManager scoped cap vs the Codex usage keys › seats five readings canonically across the whole bottom row2 ms
  • ✓SessionSlotManager scoped cap vs the Codex usage keys › keeps both Codex windows beside active Fable across four keys1 ms
  • ✓SessionSlotManager scoped cap vs the Codex usage keys › seats an inactive cap where the active one sat — ramp changes, position does not2 ms
  • ✓SessionSlotManager scoped cap vs the Codex usage keys › reserves no key for a Claude window the API did not report1 ms
  • ✓SessionSlotManager scoped cap vs the Codex usage keys › drops the scoped cap along with the Claude gauges when usage goes stale1 ms
  • ✓SessionSlotManager scoped cap vs the Codex usage keys › shows Claude 5H, 7D, Fable, and a lone 30-day Codex window together across 3 reserved keys1 ms
  • ✓SessionSlotManager scoped cap vs the Codex usage keys › shows the scoped cap for a Codex-less, Claude-only user too1 ms
plugin/src/__tests__/streamdeck-profiles-sync.test.ts6/6
  • ✓bundled Stream Deck profiles in sync › agentdeck-sd.streamDeckProfile matches the SSOT5 ms
  • ✓bundled Stream Deck profiles in sync › agentdeck-sdmini.streamDeckProfile matches the SSOT1 ms
  • ✓bundled Stream Deck profiles in sync › agentdeck-sdplus.streamDeckProfile matches the SSOT1 ms
  • ✓bundled Stream Deck profiles in sync › agentdeck-sdxl.streamDeckProfile matches the SSOT2 ms
  • ✓bundled Stream Deck profiles in sync › agentdeck-sdplusxl.streamDeckProfile matches the SSOT2 ms
  • ✓bundled Stream Deck profiles in sync › covers exactly the five shipped device families1 ms
plugin/src/__tests__/system-dispatch.test.ts6/6
  • ✓system: selectBackend platform mapping › maps win32 to the windows backend3 ms
  • ✓system: selectBackend platform mapping › maps darwin to the mac backend0 ms
  • ✓system: selectBackend platform mapping › falls back to the mac backend on platforms the Stream Deck app does not exist on0 ms
  • ✓system: setOutputVolume debounce › coalesces rapid rotation — only the final value commits8 ms
  • ✓system: setOutputVolume debounce › commits again after the debounce window settles1 ms
  • ✓system: setOutputVolume debounce › swallows backend failures — a bad tick must not become an unhandled rejection1 ms
plugin/src/__tests__/system-win32-open.test.ts5/5
  • ✓win32: openUrl via rundll32 › passes the URL as a single argv to rundll32, never through cmd5 ms
  • ✓win32: openUrl via rundll32 › rejects when rundll32 fails so callers can fall back2 ms
  • ✓win32: openApp via Start Apps lookup › passes an exact Start-menu name through the environment, not PowerShell source2 ms
  • ✓win32: openApp via Start Apps lookup › rejects a missing Start-menu app so the |url: fallback chain can take over1 ms
  • ✓win32: openAgentDeckAppOrGitHub › goes straight to the project page — there is no Windows desktop app1 ms
plugin/src/__tests__/system-win32-volume.test.ts14/14
  • ✓win32 volume coprocess › spawns powershell with -EncodedCommand carrying the ReadLine server script12 ms
  • ✓win32 volume coprocess › waits for READY before writing, then GET resolves from an OK line3 ms
  • ✓win32 volume coprocess › SET and MUTE use the wire format and parse the post-state echo1 ms
  • ✓win32 volume coprocess › an ERR line rejects only that request; the child keeps serving3 ms
  • ✓win32 volume coprocess › serializes: the second request is not written until the first resolves1 ms
  • ✓win32 volume coprocess › coalesces queued SETs while the child hangs — only the final value is written2 ms
  • ✓win32 volume coprocess › kills a hung child on the first-request timeout and respawns lazily3 ms
  • ✓win32 volume coprocess › breaker: three never-served children (incl. hung-killed-by-timeout) flip unsupported3 ms
  • ✓win32 volume coprocess › a served child dying is NOT a strike, and resets the count6 ms
  • ✓win32 volume coprocess › a spawn 'error' without 'exit' (ENOENT) still rejects and counts a strike2 ms
  • ✓win32 volume coprocess › a zombie child's late buffered stdout cannot resolve the new child's request1 ms
  • ✓win32 volume coprocess › a child exit rejects the in-flight and all queued requests1 ms
  • ✓win32 volume coprocess › a stream 'error' event is absorbed — an unlistened stream error would crash the process1 ms
  • ✓win32 volume coprocess › dispose writes EXIT and kills the child1 ms
plugin/src/__tests__/usage-encoder.test.ts43/43
  • ✓buildClaudeUsageEncoder › maps the top-level 5h/7d quota to two known tanks5 ms
  • ✓buildClaudeUsageEncoder › shows "Waiting…" before the first payload0 ms
  • ✓buildClaudeUsageEncoder › shows "No usage data" when data arrived but the quota is absent0 ms
  • ✓buildClaudeUsageEncoder › treats stale data as unknown (suppresses the tanks)0 ms
  • ✓buildCodexUsageEncoder › maps codexRateLimits primary→5h, secondary→7d1 ms
  • ✓buildCodexUsageEncoder › falls back to a muted note when Codex reports no rate limits1 ms
  • ✓buildCodexUsageEncoder › shows "Waiting…" before the first payload0 ms
  • ✓buildCodexUsageEncoder › renders only the windows Codex actually reports (partial)1 ms
  • ✓buildCodexUsageEncoder — single live window › shows the subscription tier and its next renewal1 ms
  • ✓buildCodexUsageEncoder — single live window › leaves the countdown to the gauge instead of restating it on the card1 ms
  • ✓buildCodexUsageEncoder — single live window › picks the ChatGPT subscription by name, never by position0 ms
  • ✓buildCodexUsageEncoder — single live window › drops the renewal line when the subscription has no boundary0 ms
  • ✓buildCodexUsageEncoder — single live window › falls back to the bare plan tier when no subscription is reported0 ms
  • ✓buildCodexUsageEncoder — single live window › falls back to the absolute reset instant when even the tier is unknown0 ms
  • ✓buildCodexUsageEncoder — single live window › adds no card at all when the stale window leaves nothing knowable0 ms
  • ✓buildCodexUsageEncoder — single live window › adds no side card while both windows are live0 ms
  • ✓buildCodexUsageEncoder — snapshot freshness › says nothing extra for a fresh snapshot0 ms
  • ✓buildCodexUsageEncoder — snapshot freshness › THE REGRESSION: an aged snapshot is dated, and the percent survives0 ms
  • ✓buildCodexUsageEncoder — snapshot freshness › renders the age into the LCD even though the side card owns the reset slot1 ms
  • ✓buildCodexUsageEncoder — snapshot freshness › a legacy producer (no capturedAt) is unchanged0 ms
  • ✓renderUsageEncoderBoth — single live window › keeps the lone weekly gauge in its canonical right column, side card on the left1 ms
  • ✓renderUsageEncoderBoth — single live window › keeps the gauge printing its own countdown, like E2 does0 ms
  • ✓renderUsageEncoderBoth — single live window › keeps a lone 5h gauge in the left column instead0 ms
  • ✓renderUsageEncoderBoth — single live window › renders the side card smaller than the gauge percentage0 ms
  • ✓renderUsageEncoderBoth — single live window › still renders two gauges when both windows are live0 ms
  • ✓renderUsageEncoderBoth — single live window › keeps the Codex identity while showing Luna in the wide SD+ layout1 ms
  • ✓Codex purchased credits on the SD+ encoder › replaces the exhausted gauge with the remaining balance1 ms
  • ✓Codex purchased credits on the SD+ encoder › shows credits and the Luna reserve side by side in the wide view, credits alone when zoomed1 ms
  • ✓Codex purchased credits on the SD+ encoder › shows nothing new while the plan still has room or the balance is zero0 ms
  • ✓availableUsageViews › drops the zoom stop for a window the provider no longer reports1 ms
  • ✓availableUsageViews › keeps every stop while both windows are live0 ms
  • ✓availableUsageViews › leaves only the gauge and session stops when no window exists0 ms
  • ✓buildZaiUsageEncoder › maps the wire block to tanks; the MCP window labels by quantity1 ms
  • ✓buildZaiUsageEncoder › windowless blocks render as notes, never as gauges0 ms
  • ✓provider pages and the auto selection › restores manual selections across plugin restarts and retains automatic mode1 ms
  • ✓provider pages and the auto selection › lets either dial claim every provider and allows duplicate providers without moving the peer1 ms
  • ✓provider pages and the auto selection › keeps manual choices through data loss and restores them after a restart1 ms
  • ✓provider pages and the auto selection › does not replace a pinned Claude page with Codex when Claude usage turns stale0 ms
  • ✓provider pages and the auto selection › lists every provider that currently has a page0 ms
  • ✓provider pages and the auto selection › auto-picks activity without considering the other dial0 ms
  • ✓provider pages and the auto selection › releases finished and removed sessions from auto-provider ranking0 ms
  • ✓provider pages and the auto selection › buildProviderUsageEncoder dispatches per provider0 ms
  • ✓provider pages and the auto selection › modelProviderToUsageProvider maps only the three usage providers0 ms
plugin/src/__tests__/usage-scoped.test.ts13/13
  • ✓sanitizeScopedLabel › drops control chars / newlines and collapses whitespace3 ms
  • ✓sanitizeScopedLabel › is null/undefined safe0 ms
  • ✓splitResetTwoLine › splits a two-unit countdown at the first boundary2 ms
  • ✓splitResetTwoLine › leaves a single-unit string on one line2 ms
  • ✓pickWorstScopedLimit › returns the worst-sorted [0] (store keeps active-first, percent-desc)0 ms
  • ✓pickWorstScopedLimit › returns undefined when the snapshot is stale (a stale number reads as live)0 ms
  • ✓pickWorstScopedLimit › returns undefined when there are no scoped limits0 ms
  • ✓scoped gauge treatment — active vs inactive › an ACTIVE cap at 98% wears the critical red ramp1 ms
  • ✓scoped gauge treatment — active vs inactive › an INACTIVE cap at 98% wears the calm informational cyan, NOT red1 ms
  • ✓scoped gauge treatment — active vs inactive › the triple view mutes an inactive worst-scoped panel (no red from a 99% inactive cap)1 ms
  • ✓scoped gauge treatment — active vs inactive › renders the unknown tile when there is no scoped cap0 ms
  • ✓scoped label on the compact gauge › sanitizes, uppercases and truncates the model label0 ms
  • ✓scoped store merge — omitted key clears › an update that omits scopedLimits clears a prior set (no phantom latch)2 ms
plugin/src/__tests__/usage-severity.test.ts7/7
  • ✓identical quota severity on Stream Deck and Ulanzi › 69% #52D988 uses the shared color on both keypads4 ms
  • ✓identical quota severity on Stream Deck and Ulanzi › 70% #FFA93D uses the shared color on both keypads1 ms
  • ✓identical quota severity on Stream Deck and Ulanzi › 82% #FFA93D uses the shared color on both keypads1 ms
  • ✓identical quota severity on Stream Deck and Ulanzi › 89% #FFA93D uses the shared color on both keypads0 ms
  • ✓identical quota severity on Stream Deck and Ulanzi › 90% #FF6B6B uses the shared color on both keypads0 ms
  • ✓identical quota severity on Stream Deck and Ulanzi › 100% #FF6B6B uses the shared color on both keypads0 ms
  • ✓identical quota severity on Stream Deck and Ulanzi › colors 18% remaining from 82% consumed on both keypads and the encoder1 ms
scripts/__tests__/plugin-deployment.test.ts3/3
  • ✓plugin deployment identity › distinguishes Marketplace directories, missing installs, wrong and correct source links5 ms
  • ✓plugin deployment identity › keeps startup identity when disk bytes are rebuilt3 ms
  • ✓plugin deployment identity › requires matching bytes, path, fresh startup and a live process1 ms
shared/src/__tests__/brand-assets-contract.test.ts13/13
  • ✓canonical agent brand assets › keeps the Claude compatibility export on the Claude Code robot mark3 ms
  • ✓canonical agent brand assets › claudecode.svg is the geometry used by shared renderers1 ms
  • ✓canonical agent brand assets › codex.svg is the geometry used by shared renderers0 ms
  • ✓canonical agent brand assets › openclaw.svg is the geometry used by shared renderers0 ms
  • ✓canonical agent brand assets › opencode.svg is the geometry used by shared renderers0 ms
  • ✓canonical agent brand assets › antigravity.svg is the geometry used by shared renderers0 ms
  • ✓canonical agent brand assets › kiro.svg is the geometry used by shared renderers0 ms
  • ✓canonical agent brand assets › zai.svg is the geometry used by shared renderers1 ms
  • ✓canonical agent brand assets › mirrors every canonical path on Android and Apple vector surfaces2 ms
  • ✓canonical agent brand assets › mirrors the z.ai provider mark on its two vector consumers1 ms
  • ✓canonical agent brand assets › generates constrained-device masks directly from canonical SVG files1 ms
  • ✓canonical agent brand assets › records the upstream source and trademark holder for every agent mark1 ms
  • ✓canonical agent brand assets › does not retain alternate logo-source dumps1 ms
shared/src/__tests__/hermes-brand.test.ts1/1
  • ✓Hermes upstream brand fidelity › preserves all upstream paths, including face negative space4 ms
shared/src/__tests__/svg-renderers/model-alias.test.ts7/7
  • ✓aliasModelName › shortens claude family-major-minor4 ms
  • ✓aliasModelName › drops trailing date suffix on claude releases0 ms
  • ✓aliasModelName › passes gpt and unknown strings through unchanged0 ms
  • ✓formatModelEffort › returns aliased model when no effort to show0 ms
  • ✓formatModelEffort › appends non-default effort when it fits0 ms
  • ✓formatModelEffort › truncates aliased model name to fit budget with effort suffix0 ms
  • ✓formatModelEffort › returns empty string for missing model0 ms

4 files

Pass

Terminal dashboard

Does the TUI dashboard render sessions and the terrarium stably?

Passed
75
Failed
0
Skipped
0
Time
132 ms
4 test files
bridge/src/__tests__/tui-dashboard.test.ts16/16
  • ✓TUI dashboard models › renders Codex Plus 5h and 7d limits in the wide layout9 ms
  • ✓TUI dashboard models › renders Codex Plus 5h and 7d limits in the standard layout1 ms
  • ✓TUI dashboard models › renders Codex Plus 5h and 7d limits in the narrow layout1 ms
  • ✓TUI dashboard models › renders a Codex Pro weekly-only primary window without a phantom 5h row in the wide layout1 ms
  • ✓TUI dashboard models › renders a Codex Pro weekly-only primary window without a phantom 5h row in the standard layout1 ms
  • ✓TUI dashboard models › renders a Codex Pro weekly-only primary window without a phantom 5h row in the narrow layout1 ms
  • ✓TUI dashboard models › stores modelCatalog from state_update1 ms
  • ✓TUI dashboard models › renders OAuth catalog and Ollama models in wide layout1 ms
  • ✓TUI dashboard models › renders disconnected OAuth state1 ms
  • ✓TUI dashboard models › renders downstream module health compactly2 ms
  • ✓TUI dashboard models › renders Stream Deck, WiFi-only ESP32, and TUI dashboard downstream rows1 ms
  • ✓TUI dashboard models › shows current session summary and control hints2 ms
  • ✓TUI dashboard models › renders agent list secondary line as model dash compact state2 ms
  • ✓TUI dashboard models › renders sibling models in session bridge mode and omits uptime label2 ms
  • ✓TUI dashboard models › renders help overlay when helpVisible is on1 ms
  • ✓TUI dashboard models › shows numbered session badges19 ms
bridge/src/__tests__/tui-hud-entries.test.ts16/16
  • ✓buildHudEntries — primary anchoring › promotes the matching-port sibling to primary instead of appending a duplicate19 ms
  • ✓buildHudEntries — primary anchoring › patches the anchored sibling with the primary's live fields (matches macOS / Android)1 ms
  • ✓buildHudEntries — primary anchoring › uses the primary's projectName for the #N suffix grouping after anchoring1 ms
  • ✓buildHudEntries — primary anchoring › preserves the deterministic #N order when primary anchors a sibling slot2 ms
  • ✓buildHudEntries — primary anchoring › appends a synthetic primary when no sibling shares its agentType0 ms
  • ✓buildHudEntries — primary anchoring › skips the synthetic primary when a sibling shares agentType but no port match (duplicate guard)1 ms
  • ✓buildHudEntries — primary anchoring › never appends primary when agentType is daemon or openclaw0 ms
  • ✓buildHudEntries — virtual OpenClaw › inserts a virtual OpenClaw row when gateway is available and sessions has none1 ms
  • ✓buildHudEntries — virtual OpenClaw › does not insert a virtual row when sessions already contains an openclaw entry0 ms
  • ✓buildHudEntries — hotkey eligibility (sibling-only) › primary and virtual rows do not consume hotkey slots0 ms
  • ✓formatTaskEvalSuffix — task_end badge for each outcome class › renders ✓ for success0 ms
  • ✓formatTaskEvalSuffix — task_end badge for each outcome class › renders ✗ for fail1 ms
  • ✓formatTaskEvalSuffix — task_end badge for each outcome class › renders △ for partial0 ms
  • ✓formatTaskEvalSuffix — task_end badge for each outcome class › renders ⊘ for abandoned (manual cancel)0 ms
  • ✓formatTaskEvalSuffix — task_end badge for each outcome class › renders ? as score placeholder when judge has not produced a number yet0 ms
  • ✓formatTaskEvalSuffix — task_end badge for each outcome class › returns empty string while the eval is still pending (outcome undefined)0 ms
bridge/src/__tests__/tui-renderer-snapshots.test.ts29/29
  • ✓blockGauge snapshots › 0% — all empty, green7 ms
  • ✓blockGauge snapshots › 50% — half filled, green1 ms
  • ✓blockGauge snapshots › 75% — yellow threshold0 ms
  • ✓blockGauge snapshots › 95% — red threshold0 ms
  • ✓blockGauge snapshots › 100% — all filled, red1 ms
  • ✓blockGauge snapshots › renders negative as unknown with an empty neutral gauge0 ms
  • ✓blockGauge snapshots › clamps above 1000 ms
  • ✓resetTimeStr › returns empty for undefined1 ms
  • ✓resetTimeStr › returns ↻now for past time2 ms
  • ✓resetTimeStr › formats minutes1 ms
  • ✓resetTimeStr › formats hours and minutes0 ms
  • ✓resetTimeStr › formats days and hours0 ms
  • ✓formatUptime › seconds0 ms
  • ✓formatUptime › minutes0 ms
  • ✓formatUptime › hours and minutes0 ms
  • ✓formatTokens › below 1000 unchanged0 ms
  • ✓formatTokens › 1k-10k with decimal0 ms
  • ✓formatTokens › 10k+ rounded0 ms
  • ✓activityDensityBar › empty timestamps — all dim1 ms
  • ✓activityDensityBar › recent burst — bright right side1 ms
  • ✓activityDensityBar › spread timestamps — distributed density0 ms
  • ✓getLayout › wide for 120+ cols0 ms
  • ✓getLayout › standard for 80-119 cols0 ms
  • ✓getLayout › narrow for <80 cols0 ms
  • ✓shouldShowTerrarium › true for adequate size0 ms
  • ✓shouldShowTerrarium › false for too narrow0 ms
  • ✓shouldShowTerrarium › false for too short0 ms
  • ✓spinner › returns braille characters at different frames2 ms
  • ✓spinner › spinner cycles through 10 frames0 ms
bridge/src/__tests__/tui-terrarium-snapshots.test.ts14/14
  • ✓TUI terrarium snapshots › initTerrarium creates context with expected structure5 ms
  • ✓TUI terrarium snapshots › setOctopi configures octopus instances1 ms
  • ✓TUI terrarium snapshots › draws an octopus for Claude and for nobody else1 ms
  • ✓TUI terrarium snapshots › setCrayfish configures crayfish state1 ms
  • ✓TUI terrarium snapshots › setJellyfish configures jellyfish instances1 ms
  • ✓TUI terrarium snapshots › renderTerrariumFrame empty terrarium (small)4 ms
  • ✓TUI terrarium snapshots › renderTerrariumFrame empty terrarium (large)1 ms
  • ✓TUI terrarium snapshots › renderTerrariumFrame with idle octopus2 ms
  • ✓TUI terrarium snapshots › renderTerrariumFrame with processing octopus2 ms
  • ✓TUI terrarium snapshots › renderTerrariumFrame with routing crayfish3 ms
  • ✓TUI terrarium snapshots › renderTerrariumFrame with sick crayfish3 ms
  • ✓TUI terrarium snapshots › renderTerrariumFrame with OpenCode hollow ring2 ms
  • ✓TUI terrarium snapshots › renderTerrariumFrame too small returns empty6 ms
  • ✓TUI terrarium snapshots › updateTerrarium advances bubble positions1 ms

7 files

Pass

Release & repository policy

Do versions, release notes, runtime support and palette ratchets hold?

Passed
48
Failed
0
Skipped
0
Time
175 ms
7 test files
scripts/__tests__/build-health-report.test.ts12/12
  • ✓Build Health page — design rules on the generated output › keeps every colour literal inside :root (R1, R2)5 ms
  • ✓Build Health page — design rules on the generated output › declares its tokens with the canonical design/tokens.css values2 ms
  • ✓Build Health page — design rules on the generated output › uses only the two type faces (R3)2 ms
  • ✓Build Health page — design rules on the generated output › keeps radii on the design scale0 ms
  • ✓Build Health page — design rules on the generated output › animates nothing (only amber awaiting may pulse, and a report has no awaiting state)0 ms
  • ✓Build Health page — design rules on the generated output › shares the Pages chrome: the canonical GNB with Test Report active, and the page grammar1 ms
  • ✓Build Health page — what it states › reports a failing run as failing, with the escaped failure1 ms
  • ✓Build Health page — what it states › never shows a suite that did not run as passing1 ms
  • ✓Build Health page — what it states › renders every gate and every test domain from the verification catalog3 ms
  • ✓Build Health page — what it states › calls only a branch-protection-required gate a merge blocker1 ms
  • ✓Build Health page — what it states › quotes the coverage floor from vitest.config.ts and flags a metric under it0 ms
  • ✓Build Health page — what it states › leaks no Python or JS placeholders0 ms
scripts/__tests__/native-palette.test.ts2/2
  • ✓native Dashboard palette ratchet › adds no raw colour literal to a Swift, Kotlin or ESP32 Dashboard file39 ms
  • ✓native Dashboard palette ratchet › flags a file that gains a literal and a new file that starts with one1 ms
scripts/__tests__/node-runtime-support.test.ts6/6
  • ✓Node runtime support contract › package.json advertises only the prebuild-verified maintained lines3 ms
  • ✓Node runtime support contract › bridge/package.json advertises only the prebuild-verified maintained lines0 ms
  • ✓Node runtime support contract › hooks/package.json advertises only the prebuild-verified maintained lines0 ms
  • ✓Node runtime support contract › shared/package.json advertises only the prebuild-verified maintained lines0 ms
  • ✓Node runtime support contract › setup/package.json advertises only the prebuild-verified maintained lines0 ms
  • ✓Node runtime support contract › makes the workspace engine contract an install-time failure0 ms
scripts/__tests__/npm-registry-visibility.test.ts2/2
  • ✓npm registry visibility › retries until a newly published version becomes visible6 ms
  • ✓npm registry visibility › fails after the bounded attempt count1 ms
scripts/__tests__/release-notes.test.ts12/12
  • ✓release notes lookup › npm at its declared version has an entry4 ms
  • ✓release notes lookup › streamdeck at its declared version has an entry1 ms
  • ✓release notes lookup › ulanzi at its declared version has an entry1 ms
  • ✓release notes lookup › android at its declared version has an entry1 ms
  • ✓release notes lookup › esp32 at its declared version has an entry1 ms
  • ✓release notes lookup › apple at its declared version has an entry1 ms
  • ✓release notes lookup › matches a channel named anywhere in a multi-channel heading1 ms
  • ✓release notes lookup › does not let a prefix version match a longer one0 ms
  • ✓release notes lookup › never falls back to a bare version heading1 ms
  • ✓release notes lookup › stops a section at the next heading0 ms
  • ✓release notes lookup › never matches the Unreleased section, and never spills it into a real entry1 ms
  • ✓release notes lookup › rejects anything that is not a delivery tag2 ms
scripts/__tests__/verification-catalog.test.ts9/9
  • ✓verification catalog › accounts for every GitHub workflow as a gate or a stated non-gate4 ms
  • ✓verification catalog › names only files that exist0 ms
  • ✓verification catalog › states, for every gate, what it proves and what it does not2 ms
  • ✓verification catalog › calls a gate required only when it is a failing-CI check, and dates that claim0 ms
  • ✓verification catalog › refers only to package scripts that exist0 ms
  • ✓verification catalog › puts every tracked TypeScript test file in a domain15 ms
  • ✓verification catalog › has no domain pattern that matches nothing57 ms
  • ✓verification catalog › keeps the scenario matrix pointing at test files that exist1 ms
  • ✓verification catalog › keeps every scenario name pattern present in its TypeScript test file4 ms
scripts/__tests__/version-policy.test.ts5/5
  • ✓cross-target version compatibility › ignores minor and patch values and ordering within the same major line2 ms
  • ✓cross-target version compatibility › rejects different major lines0 ms
  • ✓cross-target version compatibility › requires strict numeric X.Y.Z versions1 ms
  • ✓release tag identity › matches a release tag against that target, not the root patch1 ms
  • ✓release tag identity › can resolve every maintained target source1 ms

Native and hardware

Platforms

Suites outside the TypeScript toolchain. Apple and hardware results are recorded by their own workflows and the lab.

Android

Pass

JUnit + Robolectric on the JVM (ubuntu), 42 suites.

42 suites
MainActivityOverrideRecoveryTest5/5
  • ✓a timed-out startup read is distinguishable from genuine defaults2 ms
  • ✓recreate when the user changes the panel override1 ms
  • ✓expired startup read recovers a persisted show-anyway choice
  • ✓no recreate when storage matches what the instance was built from
  • ✓pressing show anyway takes effect through the same comparison
MainActivityScreenPolicyTest6/6
  • ✓LCD releases keep-screen-on for host full-off sleep1 ms
  • ✓LCD releases keep-screen-on when keep-awake is disabled
  • ✓LCD releases keep-screen-on for legacy sleep event without dim config
  • ✓LCD stays on for minimum-brightness dim mode1 ms
  • ✓LCD stays on when host disables downstream dimming
  • ✓e-ink stays on during host full-off sleep
data.DashboardOrientationTest3/3
  • ✓defaults keep e-ink fixed and tablets auto-rotating
  • ✓legacy unspecified preference still counts as auto
  • ✓manual rotate toggles fixed modes from auto using current posture
data.DashboardTypeTest2/2
  • ✓readerNeverSelectsAnUnsupportedNativeRenderer8 ms
  • ✓oldAndFutureSettingsKeepTheExistingDashboard
net.AndroidDashboardRegistrationTest3/3
  • ✓repeated identity does not duplicate registration churn30 ms
  • ✓disconnected change is retried when the socket opens1 ms
  • ✓connected profile change publishes new wire kind without reconnect8 ms
net.AutoConnectRulesTest6/6
  • ✓the canonical port is preferred but not required11 ms
  • ✓the first loopback re-probe waits, and later ones back off
  • ✓the backoff is capped, and never inverts or overflows1 ms
  • ✓only the daemon hub is dialable
  • ✓no daemon means no dial1 ms
  • ✓a negative miss count is treated as the first probe
net.BridgeDiscoveryTest2/2
  • ✓ws url uses primary host and preserves pairing token11 ms
  • ✓fallback url is absent without a fallback host
net.GatewaySetupStatusTest2/2
  • ✓wireAuthenticationFailuresReachTheGuidance59 ms
  • ✓reachabilityAndTransportProgressDoNotRequestApproval
net.PairingCodeClientTest14/14
  • ✓an unexpected status is reported rather than guessed at4.3 s
  • ✓normalize accepts a bare code43 ms
  • ✓a missing attempt count degrades to unknown, never to zero44 ms
  • ✓410 and 429 mean the window is gone, not that the code was wrong41 ms
  • ✓normalize rejects anything that is not the code length46 ms
  • ✓constants match the daemon's35 ms
  • ✓200 without a token is a daemon fault, not a pairing40 ms
  • ✓normalize accepts the separators the code was displayed with32 ms
  • ✓format groups the code the way the CLI prints it31 ms
  • ✓401 mismatch is a retryable wrong code, and carries the budget32 ms
  • ✓200 with a token pairs, using the endpoint we actually reached32 ms
  • ✓401 no-window sends the user to the Mac instead of retyping35 ms
  • ✓400 malformed is retryable and spends none of the budget33 ms
  • ✓401 with no body at all is treated as no window31 ms
net.PairingCredentialTest27/27
  • ✓a live connection to another endpoint is left alone1 ms
  • ✓an endpoint that closed us 4001 is not redialled inside the holdoff1 ms
  • ✓with nothing refused the last error stands
  • ✓a tokenless url persists when nothing better is stored1 ms
  • ✓a refused endpoint is dialled again once we hold a credential for it
  • ✓paired endpoint inherits its stored credential
  • ✓token extraction treats empty as absent
  • ✓default port matches an explicit default port1 ms
  • ✓a refusal is remembered per endpoint, not globally
  • ✓the advice a camera-less reader is given is one it can actually take
  • ✓an explicitly credentialed discovery wins
  • ✓pairing is still offered to a device riding the USB tunnel
  • ✓different host or port are different daemons1 ms
  • ✓a tokenless url for a different daemon still persists
  • ✓pairing is not offered to a device already on the LAN
  • ✓a refusal outranks whatever the last attempt reported
  • ✓the USB attempt is not preempted while it is still in flight1 ms
  • ✓the LAN endpoint is dialled once nothing else is in flight1 ms
  • ✓unparseable inputs never compare equal1 ms
  • ✓credential is not part of a daemon's identity
  • ✓a tokenless url may not displace a stored credential for the same daemon2 ms
  • ✓localhost and blank are never persisted
  • ✓every refused spelling of one daemon stays refused1 ms
  • ✓a credentialed url always persists
  • ✓no stored credential leaves the discovered url untouched1 ms
  • ✓a refusal expires on read, because approval mints no token
  • ✓a different daemon never inherits another endpoint's credential
net.ProtocolTest37/37
  • ✓parse state_update with all permission modes80 ms
  • ✓parse connection connected with sessionId53 ms
  • ✓parse sessions_list34 ms
  • ✓PluginCommands selectOption generates valid JSON29 ms
  • ✓parse timeline_event with fractional timestamps54 ms
  • ✓parse button_state38 ms
  • ✓PluginCommands utility with and without value27 ms
  • ✓parse invalid json returns null35 ms
  • ✓parse timeline_history22 ms
  • ✓parse timeline_event upsert28 ms
  • ✓parse state_update with processing state and tool info19 ms
  • ✓PluginCommands respond escapes special characters31 ms
  • ✓parse display_state with dim instruction27 ms
  • ✓stale Claude quota retires scopes and subscriptions while Codex survives78 ms
  • ✓PluginCommands respond generates valid JSON25 ms
  • ✓parse state_update with permission options29 ms
  • ✓parse connection disconnected19 ms
  • ✓parse missing type returns null24 ms
  • ✓parse deck_slot_map29 ms
  • ✓parse usage_update with extra usage23 ms
  • ✓parse state_update with ollama status23 ms
  • ✓PluginCommands navigateOption20 ms
  • ✓parse state_update with agent capabilities22 ms
  • ✓released model fields decode when additive residency metadata is present20 ms
  • ✓parse unknown type returns null20 ms
  • ✓parse user_prompt23 ms
  • ✓BridgeTimelineEntry converts to TimelineEntry28 ms
  • ✓parse state_update with model catalog23 ms
  • ✓a real sessions_list frame from the Swift daemon parses26 ms
  • ✓parse state_update ignores unknown fields19 ms
  • ✓parse state_update with idle state19 ms
  • ✓parse display_state sleep and wake18 ms
  • ✓parse voice_state20 ms
  • ✓parse encoder_state27 ms
  • ✓PluginCommands interrupt and escape21 ms
  • ✓parse usage_update with rate limits21 ms
  • ✓parse timeline_event19 ms
service.PanelPolicyTransitionManagerTest3/3
  • ✓installing the active panel policy is a no-op1 ms
  • ✓eink to lcd restores old effects before applying and re-evaluating
  • ✓lcd to eink uses the same coherent transition
state.SessionMetricsTest8/8
  • ✓reset clears all metrics5 ms
  • ✓onMessageReceived updates lastMessageAt
  • ✓onConnected sets connectedSince
  • ✓onDisconnected clears connectedSince
  • ✓clean reconnect after disconnect does not increment
  • ✓initial state has no connection
  • ✓reconnect increments reconnectCount when still connected
  • ✓onMessageReceived increments count1 ms
state.TimelineDisplayScenarioTest13/13
  • ✓synthetic response-less turn is hidden completely3 ms
  • ✓multi-agent dashboard timeline projects meaningful session rows4 ms
  • ✓progress chat_response and progress chat_end are hidden
  • ✓chat_end is hidden when chat_start already represents a response-less turn
  • ✓codex tool entries are suppressed from device timeline
  • ✓synthetic chat_start is suppressed once completion arrives1 ms
  • ✓task notification chat_start is suppressed1 ms
  • ✓chat_end is hidden when chat_response already represents the same turn1 ms
  • ✓answered turn is not folded
  • ✓task_end boundary blocks fold1 ms
  • ✓still-open queued prompts are not folded
  • ✓queued prompt folds into next turn
  • ✓same timestamp summaries stay separate by agent and project
state.TimelineStoreTest64/64
  • ✓addEntry drops openclaw placeholder with status-only detail2 ms
  • ✓timelineDisplayGroups hides model_call after model_response in same run1 ms
  • ✓deriveSubagentActivity ignores ordinary tools and expires orphaned starts2 ms
  • ✓addEntry allows same type+summary after 5s1 ms
  • ✓addEntries filters otel noise from bulk replay2 ms
  • ✓TimelineSessionFilter label prefers project then friendly agent
  • ✓clear empties the store
  • ✓addEntry keeps coalesced codex subagent lifecycle
  • ✓upsertEntry updates existing entry within 1s tolerance
  • ✓addEntry drops opencode tool_exec so a tool-heavy turn does not flood
  • ✓upsertEntry without taskId falls back to ts-window match
  • ✓timelineDisplayGroups keeps in-flight chat_start until completion
  • ✓groupConsecutive merges a meaningful chat_start with its response into one turn
  • ✓addEntry drops antigravity tool_exec forward-compat
  • ✓deriveSubagentActivity treats a dispatch older than the parent turn close as drained2 ms
  • ✓groupConsecutive empty list returns empty1 ms
  • ✓addEntry drops openclaw placeholder tool rows
  • ✓timelineLifecycleBounds pairs response with prior start
  • ✓upsertEntry adds new entry if no match
  • ✓addEntry stores entry
  • ✓matchesTimelineFilter keeps sessionless OpenClaw rows under gateway1 ms
  • ✓matchesTimelineFilter falls back to project plus agent for legacy sessionless rows
  • ✓groupConsecutive does not merge tool_request across sessions
  • ✓groupConsecutive groups same summary within 60s
  • ✓matchesTimelineFilter canonicalizes observed session prefixes
  • ✓timelineDisplayGroups keeps independent sessions visible1 ms
  • ✓updateLastOfType modifies the last matching entry
  • ✓upsertEntry refuses to insert otel noise via add fallback
  • ✓addEntries merges and deduplicates
  • ✓addEntry drops openclaw placeholder with arbitrary future status1 ms
  • ✓upsertEntry preserves existing detail if new detail is null
  • ✓deriveSubagentActivity pairs starts and completions per parent
  • ✓addEntry keeps real openclaw tool rows
  • ✓groupConsecutive splits tool_request after 10s gap
  • ✓addEntries sorts by timestamp
  • ✓upsertEntry preserves existing summaryKind when new entry omits it
  • ✓groupConsecutive does not merge chat_end across projects1 ms
  • ✓matchesTimelineFilter with sessionId ignores project fallback
  • ✓upsertEntry preserves timeline attribution
  • ✓turn merge looks past interleaved rows from other sessions
  • ✓addEntry allows different type within 5s
  • ✓addEntry drops openclaw placeholder with failed status
  • ✓groupConsecutive single entry
  • ✓addEntry deduplicates within 5s window
  • ✓upsertEntry propagates summaryKind progression heuristic to llm1 ms
  • ✓timelineDisplayGroups collapses synthetic chat_start once response arrives
  • ✓groupConsecutive splits different types but folds the turn completion
  • ✓addEntry allows different summary within 5s
  • ✓groupConsecutive groups chat_end by type only within 60s
  • ✓addEntry drops codex otel low-signal tool noise
  • ✓addEntry caps at MAX_ENTRIES9 ms
  • ✓groupConsecutive does not merge distinct run ids on same session
  • ✓groupConsecutive folds a whole chat_start-response-end turn into one row
  • ✓timelineDisplayGroups keeps in-flight start when later completion has distinct run id
  • ✓addEntry drops codex tool_exec from normal codex session
  • ✓addEntry keeps openclaw placeholder summary when detail has content
  • ✓groupConsecutive requires same summary for other types
  • ✓addEntry normalizes openclaw cron model_call prompt dump
  • ✓matchesTimelineFilter matches by sessionId
  • ✓updateLastOfType no-op if type not found1 ms
  • ✓turn merge never crosses a newer same-session chat_start
  • ✓groupConsecutive groups tool_request within 10s
  • ✓addEntry drops codex tool_exec even when raw is meaningful
  • ✓upsertEntry merges task_end by taskId beyond the 1s tolerance window
state.TimelineTaskHierarchyTest44/44
  • ✓OpenClaw NO_REPLY polling responses are hidden from display projection1 ms
  • ✓chat_start with a later same-session completion stops rotating1 ms
  • ✓mismatched taskId on task_end does not close it1 ms
  • ✓chat_start older than the age cap stops rotating
  • ✓summary is redundant with detail for standalone response prefix truncation6 ms
  • ✓OpenClaw automated polling chat starts are hidden from display projection
  • ✓stripMarkdownInline drops markers including table syntax1 ms
  • ✓interrupted reaper closure pair leaves no visible row1 ms
  • ✓summary is not redundant for merged prompt-response turn2 ms
  • ✓detail redundancy fires when prefix matches summary1 ms
  • ✓detail redundancy does not fire when content is genuinely new
  • ✓same project two sessions are not the same context anymore
  • ✓judge summary outranks a meaningful own title1 ms
  • ✓an error closes a turn so the spinner stops beside its explanation
  • ✓closed task_start does not rotate
  • ✓non task_start entries are never in flight1 ms
  • ✓fresh chat_start rotates via icon-key running
  • ✓task_start whose task_end (same taskId) appeared is finished
  • ✓static rows do not rotate
  • ✓a closed task returns to its own task glyph1 ms
  • ✓session_end task boundary is hidden from display projection
  • ✓an error in a different session leaves the turn spinning
  • ✓stripMarkdownInline preserves non-table pipes
  • ✓task_start older than the staleness cap is no longer in flight
  • ✓a rotating row draws the running glyph, not its own
  • ✓task_start without matching task_end is in flight1 ms
  • ✓chat_start superseded by a newer same-session prompt stops rotating
  • ✓idle_gap task boundary is hidden from display projection
  • ✓task entries never group with each other
  • ✓iconKey resolves tool_request status to success error awaiting
  • ✓taskId is the strongest grouping key
  • ✓OpenClaw LINE notification failures stay visible1 ms
  • ✓promote informative lead skips generic outcome paragraph1 ms
  • ✓other sessions completions do not stop a running chat_start
  • ✓iconKey resolves to Task for task entries
  • ✓judged closure makes a bare header visible with the summary as title
  • ✓eval_result and task_end never rotate
  • ✓stripMarkdownInline handles single-cell table row
  • ✓markdown parser parity with shared - basics2 ms
  • ✓task_start without taskId is never considered in flight
  • ✓orphan task_start rotates via in-flight predicate
  • ✓markdown parser code fence is verbatim1 ms
  • ✓eink glyphs are constant 4-char width
  • ✓closed task renders as a single header row, never a standalone task_end
terrarium.AquariumResidentOverlayTest3/3
  • ✓all six kinds render a filled working badge and keep activity when labels hide3.1 s
  • ✓a yielding WORKING tag leaves no opaque badge over the resident behind it41 ms
  • ✓state changes clear work cue immediately even with frozen phase and selection93 ms
terrarium.AquariumResidentsTest6/6
  • ✓Kiro gets its own model while unknown types never borrow another mark24 ms
  • ✓Hermes swims as its own mermaid resident in its own band38 ms
  • ✓exported templates contain only their original character hierarchy59 ms
  • ✓Claude arm meshes exclude the fixed torso edges25 ms
  • ✓Antigravity packages a sampled rainbow and texture coordinates651 ms
  • ✓crowds remain bounded but focus and waiting sessions stay reachable20 ms
terrarium.CreatureFloorSpacingTest1/1
  • ✓every vector matches the TypeScript source24 ms
terrarium.CreatureLayoutTest3/3
  • ✓no row overlaps more than ~50 percent across realistic counts4 ms
  • ✓low counts keep full size and do not shrink
  • ✓floor-rest clear anchor stays left of the crayfish claws
terrarium.CreatureNameTagLayerTest2/2
  • ✓separate tags are both drawn and the queue is emptied57 ms
  • ✓an idle tag colliding with a working tag is dropped, the working tag is drawn29 ms
terrarium.ResidentLabelLayoutTest6/6
  • ✓a sparse tank keeps every tag whole and translucent20 ms
  • ✓every shared vector matches (Swift replays the same file)19 ms
  • ✓a dense tank collapses idle tags but never working, awaiting or focused ones19 ms
  • ✓an idle tag colliding with a higher-priority tag drops out and priority draws last23 ms
  • ✓two colliding working tags both stay, the lower one yields23 ms
  • ✓a tag over another resident's body yields so the body shows through20 ms
terrarium.TerrariumStateTest13/13
  • ✓claude processing does not bleed into OpenClaw crayfish on aggregate view22 ms
  • ✓primary Kiro session routes to the canonical vector-mark creature17 ms
  • ✓a focused Codex session keeps its cloud when the list contains itself17 ms
  • ✓Kiro IDE sibling does not fall back to an octopus20 ms
  • ✓emitted OpenClaw session shows OpenClaw at rest18 ms
  • ✓gateway error without an emitted session does not spawn a creature18 ms
  • ✓gateway error with a live session surfaces sick OpenClaw22 ms
  • ✓OpenClaw processing routes its own crayfish17 ms
  • ✓reachable gateway without an emitted session hides OpenClaw and workers16 ms
  • ✓a focused session keeps its creature when the list contains itself17 ms
  • ✓a second session of the same agent does not suppress the focused one19 ms
  • ✓stuck gatewayConnected without an emitted session still hides OpenClaw17 ms
  • ✓daemon aggregate keeps OpenClaw crayfish calm while claude works26 ms
terrarium.renderer.EinkAnimationTimingTest9/9
  • ✓LCD uses vsync and physical e-ink uses fast partial cadence5 ms
  • ✓state changes and repeated laps preserve continuous bounded motion160 ms
  • ✓fish cross the aquarium rather than orbiting in place1 ms
  • ✓reader habitat is monotonic native grayscale with dark tones reserved for agents2 ms
  • ✓routine vendor modes never carry full refresh or blocking flags2 ms
  • ✓fish advance on every 60Hz frame without coarse simulation stalls
  • ✓animation frame advance is elapsed-time based and bounded
  • ✓fish trajectory is identical across display cadences3 ms
  • ✓fish simulation scales movement for partial color frames
ui.common.ConnectionSetupGuideTest1/1
  • ✓disconnected guidance states the host prerequisite and both setup paths1 ms
ui.eink.EinkAttentionPanelTest4/4
  • ✓non-focused awaiting session hides unavailable live prompt fields6 ms
  • ✓primary awaiting session is surfaced when not represented by siblings
  • ✓featured attention prefers focused awaiting session5 ms
  • ✓observed AskUserQuestion choices are visible but not actionable
ui.eink.EinkPaperBoardTest6/6
  • ✓a judged task speaks for its turns and abandoned work is not done17 ms
  • ✓recent holds finished agent work, never a prompt still waiting
  • ✓the same finished work is listed once1 ms
  • ✓headline skips bare headings and machine output
  • ✓same project and agent twice are numbered2 ms
  • ✓sessions rank by what they ask of the reader2 ms
ui.eink.EinkUsageColumnsTest3/3
  • ✓a narrow zone shrinks the label before dropping the bar, never the figures
  • ✓a Crema-width zone keeps every column inside it
  • ✓a wide zone caps the bar instead of stretching it across the page
ui.eink.EinkUsageGroupsTest7/7
  • ✓a plan attaches to its own provider, with a readable until20 ms
  • ✓providers keep one order whatever arrives first1 ms
  • ✓a lapsed plan asks for renewal1 ms
  • ✓one provider is one group with its own windows
  • ✓a plan without metered windows is a group of its own
  • ✓the Swift daemon's plan fields give the same plan line as a subscription row1 ms
  • ✓no subscription draws no zone
ui.eink.SessionDisplayOrderingTest11/11
  • ✓naturalLabelCompare orders Agent 2 before Agent 101 ms
  • ✓compareSessionsForDisplay breaks ties on startedAt ascending (oldest first)
  • ✓compareSessionsForDisplay maps terminal tab order onto the deck1 ms
  • ✓compareSessionsForDisplay sorts openclaw before claude-code regardless of project
  • ✓compareSessionsForDisplay tie-breaks on natural id when startedAt is identical1 ms
  • ✓agentTypeRank places openclaw first
  • ✓sessionWeight accepts the documented bounds and clamps beyond them
  • ✓compareSessionsForDisplay is stable across re-sorts of any input order1 ms
  • ✓compareSessionsForDisplay orders opposite-sign extreme weights without Int overflow
  • ✓compareSessionsForDisplay orders by weight ascending before everything else1 ms
  • ✓sessionWeight collapses null to 0 and preserves finite values
ui.monitor.MonitorLayoutScaleTest9/9
  • ✓isTablet is true exactly for the classes with room for side rails2 ms
  • ✓eink bands grow with the panel and portrait is never shorter than landscape1 ms
  • ✓side rails cannot overlap at the narrowest width of their size class7 ms
  • ✓rail fractions leave room for the terrarium between them1 ms
  • ✓size class boundaries match the device profile
  • ✓every size class resolves to an eink scale
  • ✓single rail phone mode restores readable type without tablet insets
  • ✓every size class resolves to a monitor scale
  • ✓larger classes never render denser than smaller ones
ui.monitor.OpenClawDisplayLinesTest4/4
  • ✓empty when no default tagged36 ms
  • ✓empty catalog yields empty list
  • ✓keeps only default model when present
  • ✓empty when default is unavailable
ui.monitor.SubscriptionLineTest15/15
  • ✓inline plan preserves credits and absent date
  • ✓past subscription date does not claim cancellation1 ms
  • ✓subscriptionTrailing flags expired for past dates
  • ✓inline date is distinct from quota reset
  • ✓malformed until renders renewal needed1 ms
  • ✓future ISO8601 with offset renders date suffix
  • ✓parseUntilInstant accepts ISO8601
  • ✓bare date in past renders renewal needed
  • ✓future ISO8601 with fractional seconds renders date suffix1 ms
  • ✓subscriptionTrailing returns date for future
  • ✓parseUntilInstant accepts blank as null
  • ✓past until renders renewal needed
  • ✓bare date string parses as UTC midnight
  • ✓subscriptionTrailing returns null for blank or null1 ms
  • ✓null until renders name only
ui.monitor.TimelineDayBreakTest6/6
  • ✓index 0 anchors an older buffer but stays silent on today40 ms
  • ✓separator lands on the first row of the new day1 ms
  • ✓out-of-range index yields no separator
  • ✓all-today buffer gets no separator at all
  • ✓a day older than this year carries the year
  • ✓same instant reads as a different day across zones
update.ApkUpdatesTest3/3
  • ✓onlyOfficialBoundedStableAndroidAssetsAreAccepted42 ms
  • ✓signer set falls back to legacy signatures when signingInfo is empty24 ms
  • ✓versionsAreNumericAndStableOnly22 ms
util.ChatGPTPlanTest3/3
  • ✓capitalises a tier this build predates rather than dropping it
  • ✓names the known tiers
  • ✓spells a multi-word tier the same way every other surface does
util.CreditBalanceVectorsTest2/2
  • ✓creditsActive matches every shared vector24 ms
  • ✓formatCreditBalance matches every shared vector35 ms
util.DeviceProfileHolderTest1/1
  • ✓listeners receive genuine changes but not repeated installation30 ms
util.DeviceProfileTest44/44
  • ✓onyx mono model is not color eink4 ms
  • ✓real property values are eink signals
  • ✓hisense eink reader is detected by model1 ms
  • ✓reader with a generic manufacturer is detected from the brand in its model
  • ✓screen below the floor is unsupported1 ms
  • ✓oneplus x is not an eink device despite its onyx codename
  • ✓false-like property values are not eink signals
  • ✓hisense lcd phone is not an eink device
  • ✓verified hisense eink families retain model detection2 ms
  • ✓generic models named leaf or poke are not eink devices
  • ✓onyx note air 3 c is color eink
  • ✓short label prefers a named vendor and otherwise names the form factor1 ms
  • ✓crema stays eink and does not require system fixed rotation1 ms
  • ✓color signal alone establishes eink evidence
  • ✓onyx boox stays eink
  • ✓eink override wins over a negative detection2 ms
  • ✓compact screens are supported but flagged as tight
  • ✓kobo stays eink
  • ✓auto override is not reported as overridden1 ms
  • ✓size class boundaries
  • ✓color signal upgrades an otherwise mono verdict
  • ✓hisense lcd television model numbers do not match short eink families
  • ✓build characteristics eink is detected
  • ✓false-like color property values remain rejected
  • ✓television renders with a no-touch caveat
  • ✓moaan pantone is color eink and requires system fixed rotation
  • ✓wire kind keeps the two value vocabulary the swift topology maps
  • ✓display name does not repeat an embedded brand1 ms
  • ✓eink override keeps a color panel in color
  • ✓automotive renders with its own caveat
  • ✓a touchless tablet is limited even without a tv ui mode
  • ✓panel override round trips through storage
  • ✓hisense q5 is a reflective lcd not an eink panel1 ms
  • ✓huawei nova is not an eink device
  • ✓describe names the panel size and evidence1 ms
  • ✓build string only match is flagged as unverified
  • ✓redmi note is not an eink device
  • ✓display name degrades gracefully when build fields are empty
  • ✓unlisted reader with eink system property is detected
  • ✓watch is unsupported regardless of size
  • ✓aggregate property check ignores false-like values
  • ✓lcd override wins over a positive detection
  • ✓phone sized lcd is handheld and tablet sized lcd is tablet
  • ✓unlisted reader with vendor eink api is detected
util.SessionGroupingTest7/7
  • ✓short siblings stay separate1 ms
  • ✓groups duplicate #N sessions of the same project1 ms
  • ✓delimiter extension fuses
  • ✓exact match groups
  • ✓long multi-token stems fuse
  • ✓normalize strips path and trailing #N suffix
  • ✓clusters same-stem worktrees and keeps singletons flat2 ms
util.TimeFormatUtilsTest40/40
  • ✓gaugeBar clamps below 01 ms
  • ✓formatCount thousands show K1 ms
  • ✓an expired Luna reserve restores the regular windows1 ms
  • ✓formatBytes kilobytes
  • ✓codexLimitRows swaps exhausted windows for the credit balance1 ms
  • ✓formatDurationCompact seconds
  • ✓codex footnote ended window outranks age
  • ✓formatDurationCompact exact minutes no seconds1 ms
  • ✓formatDurationCompact sub-second
  • ✓codexCreditBalance reads the wire balance, hasCredits false is zero
  • ✓formatCount int overload works
  • ✓gaugeBar clamps above 100
  • ✓credits show only while a live window is exhausted and a balance remains1 ms
  • ✓formatBytes small values1 ms
  • ✓a zero balance leaves the exhausted windows alone
  • ✓formatCount small numbers unchanged
  • ✓a reported Luna reserve alone keeps the regular windows1 ms
  • ✓credits report the latest exhausted reset
  • ✓gaugeBar 50 percent is half filled1 ms
  • ✓gaugeBar 0 percent is all empty
  • ✓formatBytes megabytes
  • ✓codexLimitRows shows credits and the Luna reserve together
  • ✓formatResetTime returns original on parse failure
  • ✓codexLimitRows dates an aged reading of a still-live weekly window1 ms
  • ✓gaugeBar custom width
  • ✓codex footnote fires only past the threshold
  • ✓codexLimitRows swaps exhausted windows for the Luna reserve read as left1 ms
  • ✓codex snapshot threshold matches the TypeScript SSOT
  • ✓windowLabel maps minutes to compact day-hour-minute labels
  • ✓formatBytes gigabytes
  • ✓codexLimitRows maps primary and secondary windows with agent tag1 ms
  • ✓codexLimitRows skips windows with null usedPercent
  • ✓formatUptime zero returns 0 colon 00
  • ✓codexLimitRows returns empty when limits null
  • ✓formatCount millions show M
  • ✓codex snapshot age ignores missing and malformed stamps
  • ✓formatDurationCompact minutes1 ms
  • ✓codexLimitRows leaves a legacy producer without capturedAt unchanged
  • ✓gaugeBar 100 percent is all filled
  • ✓codex snapshot age label rounds down1 ms
util.UsageSeverityTest1/1
  • ✓consumedAndRemainingUseTheSameBoundaryAndPalette1 ms

Apple (XCTest)

Not run

Runs in the Apple Tests workflow (macos-26) on apple/** changes; not re-run here

ESP32 Robot Framework

Not run

Physical hardware suite; run in the maintainer's lab, not on GitHub-hosted runners

User flows

Scenarios

Each flow maps to specific tests by file and case name (scripts/scenario-matrix.json). A dash means no test at that level — listed with the known gaps.

ScenarioPriorityUnitIntegrationPlatformE2EKnown gaps
Session LifecycleStart session -> processing -> idle -> stop. Core state machine transitions that drive all UI. critical 4/4 — 4 passed2/2 — 2 passed1/2 — 1 passed, 1 not executed here1/1 — 1 passed
  • No end-to-end test with a real Claude process (the daemon E2E posts documented hook payloads)
  • No Android UI test for state transitions
Permission FlowAgent requests permission -> user sees options -> approves/denies -> state transitions back. critical 3/3 — 3 passed2/2 — 2 passed1/1 — 1 passed1/1 — 1 passed
  • No multi-option navigation end-to-end test
  • No encoder dial interaction test for option scrolling
Multi-agent MonitoringDaemon hub orchestrates multiple session bridges, unified dashboard for all agents. high 3/3 — 3 passed4/4 — 4 passed——
  • No daemon + session bridge coordinated test
  • No multi-agent state aggregation test
Device ConnectionmDNS discovery -> auth token validation -> WebSocket/serial state sync with dashboard clients. high 3/3 — 3 passed3/3 — 3 passed1/1 — 1 passed—
  • No real mDNS discovery test
  • No ADB reverse tunnel test
  • No auth token rejection test
Timeline AggregationEvents from multiple sessions -> daemon relay -> dedup -> unified timeline for all clients. high 2/2 — 2 passed1/1 — 1 passed1/2 — 1 passed, 1 not executed here1/1 — 1 passed
  • No daemon-to-client relay end-to-end test
  • No LLM summary enrichment test
Voice Recording & TranscriptionAudio capture -> whisper transcription -> text delivery to Claude PTY or clipboard. medium —1/1 — 1 passed——
  • No audio recording unit test
  • No recording -> transcription -> paste end-to-end
  • No Android voice recording test
  • No silence detection unit test
Encoder & Button InteractionDial rotate -> option scroll -> push select. Button press -> action trigger. Label rendering. medium 4/4 — 4 passed———
  • No encoder rotate/push integration test
  • No wide canvas takeover test
  • No Android Deck gesture test
Usage Tracking & Rate LimitsAPI usage fetch -> rate limit gauge -> sibling relay (429 prevention) -> dashboard display. medium 1/1 — 1 passed2/2 — 2 passed2/2 — 2 passed—
  • No real API rate limit response test
  • No usage_update WS relay end-to-end
Daemon Singleton GuardPID check -> port probe -> prevent double daemon. Auto-fallback on port conflict. medium 1/1 — 1 passed1/1 — 1 passed—1/1 — 1 passed
  • No port conflict auto-fallback test
  • No LaunchAgent KeepAlive loop prevention test
Hook Installation & MigrationInstall Claude Code hooks -> migrate v2.0 flat format to v2.1 matcher groups -> port injection. medium 1/1 — 1 passed——1/1 — 1 passed
  • No real settings.local.json file test
  • No hook execution verification test

Vitest --coverage

Coverage

Line, statement, function and branch coverage of bridge, shared, plugin and hooks. The floors are read from vitest.config.ts and enforced on every pull request.

Lines

60.4%

floor 56% · above

Statements

59.4%

floor 55% · above

Functions

60.7%

floor 57% · above

Branches

56.3%

floor 52% · above

By package

bridge

57.8%

15,952 / 27,619 lines · 221 files

hooks

95.0%

499 / 525 lines · 5 files

plugin

54.2%

1,229 / 2,267 lines · 36 files

plugin-ulanzi

89.0%

65 / 73 lines · 1 file

shared

81.7%

2,573 / 3,151 lines · 62 files

325 source files, least covered first
FileLinesStmtsFuncsBranch
bridge/src/apme/types.ts 0%0% 0%0%
bridge/src/check-deps.ts 0%0% 0%0%
bridge/src/daemon.ts 0%0% 0%0%
bridge/src/diag-analyzer.ts 0%0% 0%0%
bridge/src/esptool-js-bundle.d.ts 0%0% 0%0%
bridge/src/esptool-js-node.ts 0%0% 0%0%
bridge/src/event-journal.ts 0%0% 0%0%
bridge/src/idotmatrix/idotmatrix-discover.ts 0%0% 0%0%
bridge/src/index.ts 0%0% 0%0%
bridge/src/log-stream.ts 0%0% 0%100%
bridge/src/pty-ringbuffer.ts 0%0% 0%0%
bridge/src/terminal-status.ts 0%0% 0%0%
bridge/src/timebox/timebox-discover.ts 0%0% 0%0%
bridge/src/tui/screen.ts 0%0% 0%0%
bridge/src/types.ts 0%0% 0%0%
bridge/src/types/picovoice.d.ts 0%0% 0%0%
bridge/src/utility-proxy.ts 0%0% 0%0%
bridge/src/voice.ts 0%0% 0%0%
plugin/src/actions/iterm-dial.ts 0%0% 0%0%
plugin/src/actions/launcher-dial.ts 0%0% 0%0%
plugin/src/actions/option-dial.ts 0%0% 0%0%
plugin/src/actions/session-slot-button.ts 0%0% 0%0%
plugin/src/actions/usage-limit-button.ts 0%0% 0%0%
plugin/src/actions/utility-dial.ts 0%0% 0%0%
plugin/src/agent-link.ts 0%0% 0%0%
plugin/src/display-dim.ts 0%0% 0%0%
plugin/src/encoder-registry.ts 0%0% 0%100%
plugin/src/plugin.ts 0%0% 0%0%
plugin/src/renderers/agent-logos.ts 0%0% 0%0%
plugin/src/renderers/button-renderer.ts 0%0% 0%100%
plugin/src/renderers/session-slot-renderer.ts 0%0% 0%0%
plugin/src/renderers/slot-svg.ts 0%0% 0%0%
plugin/src/system/darwin.ts 0%0% 0%0%
plugin/src/system/types.ts 0%0% 0%0%
shared/src/index.ts 0%0% 0%0%
shared/src/sample.ts 0%0% 0%0%
shared/src/svg-renderers/index.ts 0%0% 0%0%
shared/src/telemetry-envelope.ts 0%0% 0%0%
shared/src/timeline-label.ts 0%0% 0%0%
bridge/src/wake-word.ts 2%2% 0%0%
shared/src/net-utils.ts 2%2% 0%0%
bridge/src/voice-assistant.ts 2%2% 0%0%
shared/src/command-builders.ts 4%4% 0%100%
bridge/src/apme/hw-sampler.ts 4%6% 20%4%
shared/src/gateway-health.ts 5%4% 0%0%
bridge/src/tts.ts 5%4% 0%0%
bridge/src/antigravity-local.ts 5%6% 18%3%
shared/src/matrix-expression.ts 5%4% 0%0%
bridge/src/apme/index.ts 5%5% 9%0%
bridge/src/modules/index.ts 6%6% 33%0%
bridge/src/tui/dashboard.ts 6%5% 3%11%
bridge/src/timebox/timebox-daemon-sync.ts 7%6% 0%0%
bridge/src/wifi-config.ts 7%6% 0%0%
bridge/src/apme/outcome.ts 8%8% 8%2%
bridge/src/modules/idotmatrix-module.ts 8%6% 0%0%
bridge/src/modules/timebox-module.ts 8%6% 0%0%
bridge/src/modules/pixoo-module.ts 8%6% 0%0%
shared/src/apme-graph.ts 9%9% 0%100%
bridge/src/daemon-server.ts 10%10% 12%9%
bridge/src/pixoo/pixoo-client.ts 10%10% 0%0%
bridge/src/timebox/timebox-settings.ts 10%8% 8%6%
bridge/src/cli.ts 10%10% 14%8%
bridge/src/pixoo/pixoo-settings.ts 12%11% 10%0%
bridge/src/pixoo/pixoo-bridge.ts 14%14% 10%8%
bridge/src/idotmatrix/idotmatrix-settings.ts 15%18% 17%33%
bridge/src/idotmatrix/idotmatrix-daemon-sync.ts 16%14% 0%0%
bridge/src/version-check.ts 17%18% 23%16%
bridge/src/modules/serial-module.ts 18%17% 0%0%
bridge/src/review-runner.ts 18%17% 21%22%
bridge/src/session-transcript-timeline.ts 23%24% 14%17%
bridge/src/modules/adb-module.ts 24%21% 0%0%
bridge/src/display-monitor.ts 28%27% 28%30%
plugin/src/runtime-identity.ts 29%29% 50%100%
bridge/src/observed-inject.ts 30%31% 50%18%
bridge/src/pixoo/pixoo-sprites.ts 30%28% 42%15%
bridge/src/adb-reverse.ts 38%38% 43%53%
bridge/src/logger.ts 38%38% 62%21%
bridge/src/esp32-serial.ts 39%39% 34%45%
shared/src/eval-schema.ts 40%40% 0%0%
bridge/src/esp32-flash.ts 41%40% 42%35%
bridge/src/modules/broadcast-module.ts 43%43% 25%100%
bridge/src/modules/mdns-module.ts 43%43% 25%100%
bridge/src/usage-tracker.ts 44%44% 23%55%
bridge/src/opencode-client.ts 44%40% 57%38%
shared/src/voice-paths.ts 45%42% 0%0%
bridge/src/model-catalog.ts 47%40% 40%19%
shared/src/pairing-code.ts 47%44% 60%35%
bridge/src/session-activity.ts 49%44% 58%42%
bridge/src/ble-sync-spawn.ts 49%48% 47%39%
bridge/src/esp32-flash-transport.ts 51%53% 55%49%
bridge/src/pixoo/pixoo-camera.ts 51%51% 50%37%
bridge/src/adapters/opencode-adapter.ts 53%49% 41%38%
bridge/src/windows-service.ts 54%53% 50%78%
bridge/src/bridge-core.ts 55%54% 38%52%
bridge/src/hook-server.ts 56%54% 54%32%
bridge/src/foundation-models-helper.ts 58%59% 54%45%
bridge/src/codex-auth.ts 60%61% 40%41%
shared/src/claude-weekly-view.ts 60%71% 33%47%
bridge/src/claude-usage-recovery.ts 61%62% 47%70%
bridge/src/session-focus-relay.ts 62%62% 56%61%
plugin/src/system/index.ts 62%65% 50%100%
bridge/src/kiro-session.ts 63%60% 48%61%
bridge/src/apme/antigravity-transcript.ts 63%63% 33%74%
bridge/src/apme/activity-history.ts 65%59% 73%58%
bridge/src/ws-server.ts 65%63% 58%56%
shared/src/format-utils.ts 66%66% 73%66%
plugin/src/log.ts 67%59% 33%50%
plugin/src/system/win32.ts 67%63% 62%80%
plugin/src/bridge-client.ts 67%62% 75%41%
bridge/src/agent-cli-diagnostics.ts 68%68% 60%44%
shared/src/svg-renderers/agent-logos.ts 70%70% 79%53%
bridge/src/mlx-probe.ts 70%64% 50%70%
bridge/src/adapters/index.ts 71%71% 100%67%
bridge/src/openclaw-gateway-stability.ts 72%74% 70%80%
bridge/src/adapters/claude-code.ts 72%72% 50%40%
bridge/src/apme/http.ts 73%71% 74%64%
bridge/src/pixoo/pixoo-font.ts 73%72% 50%67%
bridge/src/pty-manager.ts 73%74% 76%66%
bridge/src/tui/renderer.ts 73%71% 95%54%
bridge/src/tui/terrarium.ts 74%72% 78%56%
bridge/src/daemon-supervisor.ts 75%73% 85%66%
bridge/src/session-registry.ts 75%72% 59%75%
bridge/src/apme/payload-prune.ts 75%75% 57%89%
bridge/src/passive-observer.ts 75%71% 79%65%
bridge/src/tui/ansi.ts 75%74% 84%49%
bridge/src/linux-service.ts 76%73% 72%100%
shared/src/timeline-summarizer.ts 76%73% 57%63%
shared/src/svg-renderers/session-slot-renderer.ts 78%77% 80%67%
bridge/src/font-pack.ts 79%79% 100%90%
bridge/src/apme/recommend.ts 79%77% 73%56%
bridge/src/adapters/openclaw.ts 79%76% 74%69%
bridge/src/pixoo/pixoo-renderer.ts 79%77% 82%66%
bridge/src/gateway-probe.ts 79%66% 38%76%
bridge/src/python-ble-runtime.ts 80%78% 88%73%
plugin/src/renderers/display-tile.ts 80%77% 100%53%
shared/src/d200h-layout.ts 81%81% 89%78%
bridge/src/learning-pack.ts 81%81% 100%85%
bridge/src/adapters/monitor.ts 81%81% 87%60%
bridge/src/pixoo/pixoo-discover.ts 82%74% 75%63%
bridge/src/adapters/pty-adapter.ts 82%82% 65%81%
bridge/src/kiro-diagnostics.ts 84%85% 87%64%
bridge/src/session-timeline-relay.ts 84%80% 71%62%
bridge/src/apme/adapters/codex-hook.ts 85%79% 100%60%
bridge/src/apme/runner.ts 85%81% 79%71%
bridge/src/state-machine.ts 85%85% 87%69%
shared/src/timeline.ts 85%78% 100%74%
shared/src/usage-severity.ts 86%89% 75%90%
bridge/src/kiro-timeline-feed.ts 86%84% 86%65%
bridge/src/broadcast.ts 87%79% 56%56%
bridge/src/openclaw-timeline-feed.ts 87%83% 90%57%
bridge/src/apme/sample-to-timeline.ts 87%82% 100%65%
bridge/src/usage-api.ts 87%83% 89%80%
bridge/src/device-photo.ts 88%88% 87%87%
bridge/src/mdns.ts 88%87% 94%77%
bridge/src/glance-frame.ts 88%89% 84%79%
plugin/src/session-slot-manager.ts 89%88% 92%80%
plugin-ulanzi/src/reconnect-supervisor.ts 89%85% 75%68%
bridge/src/apme/store.ts 90%77% 95%74%
plugin/src/focused-detail-state.ts 90%91% 86%76%
bridge/src/apme/classifier.ts 91%85% 92%81%
shared/src/apme-classifier-rules.ts 91%92% 67%67%
shared/src/openclaw-plugin-approval.ts 91%91% 94%87%
bridge/src/apme/adapters/openclaw-hook.ts 91%88% 92%83%
plugin/src/utility-modes/usage.ts 91%87% 86%85%
bridge/src/pocket-autonomy.ts 91%86% 89%66%
bridge/src/apme/collector.ts 91%87% 91%77%
hooks/src/install.ts 91%89% 94%90%
bridge/src/surface-protocol.ts 91%88% 85%82%
bridge/src/device-voice.ts 92%89% 83%74%
bridge/src/adapters/codex-cli.ts 92%92% 88%100%
bridge/src/daemon-ws-client.ts 92%86% 89%75%
bridge/src/session-aggregator.ts 92%91% 100%75%
shared/src/timeline-icons.ts 92%89% 83%82%
bridge/src/network-posture.ts 92%93% 100%88%
bridge/src/ollama-probe.ts 92%94% 100%92%
plugin/src/actions/per-action-view-state.ts 92%86% 80%70%
bridge/src/zai-usage.ts 92%89% 93%77%
hooks/src/opencode-install.ts 93%93% 100%82%
bridge/src/mdns-discover.ts 93%90% 100%74%
bridge/src/session-order-store.ts 94%91% 77%89%
bridge/src/codex-output-parser.ts 94%90% 95%84%
shared/src/esp32-boards.ts 94%93% 91%85%
bridge/src/output-parser.ts 94%90% 100%84%
bridge/src/apme/adapters/timeline.ts 94%95% 100%76%
bridge/src/pixoo/micro-glyphs.ts 94%92% 100%85%
bridge/src/openclaw-transcript-timeline.ts 94%86% 100%77%
bridge/src/opencode-steering.ts 94%95% 100%87%
bridge/src/peripheral-mapping.ts 94%96% 100%95%
hooks/src/codex-install.ts 95%92% 96%86%
bridge/src/hook-opencode-sessions.ts 95%92% 100%91%
bridge/src/auth.ts 95%92% 100%88%
bridge/src/apme/classify-turn.ts 95%91% 100%80%
bridge/src/fallback-task-timeline.ts 95%95% 83%82%
bridge/src/device-voice-reply.ts 95%89% 70%84%
bridge/src/apme/adapters/codex-turn-manager.ts 95%90% 100%79%
bridge/src/claude-permission-rules.ts 95%96% 71%81%
shared/src/claude-permission-rules.ts 95%90% 100%87%
bridge/src/claude-turn-watchdog.ts 95%93% 88%91%
bridge/src/apme/pareto.ts 95%96% 100%81%
bridge/src/weather.ts 95%89% 93%75%
bridge/src/subagent-timeline.ts 95%94% 93%85%
plugin/src/connection-manager.ts 96%94% 83%91%
bridge/src/claude-hook-timeline.ts 96%93% 100%82%
bridge/src/codex-rate-limits-live.ts 96%95% 94%90%
bridge/src/codex-otel.ts 96%89% 100%84%
bridge/src/kiro-transcript-timeline.ts 96%91% 100%83%
shared/src/pricing.ts 96%91% 88%77%
bridge/src/ask-gate.ts 96%97% 100%97%
bridge/src/daemon-takeover.ts 96%96% 100%85%
plugin/src/renderers/usage-gauge.ts 96%94% 93%78%
bridge/src/codex-exec-children.ts 96%91% 90%85%
bridge/src/pixoo/matrix-art.ts 96%95% 100%93%
bridge/src/pairing-window.ts 96%95% 88%89%
bridge/src/codex-rate-limits.ts 97%93% 100%86%
bridge/src/daemon-build-identity.ts 97%90% 100%84%
shared/src/mlx-safety.ts 97%92% 100%89%
bridge/src/pairing-knocks.ts 97%94% 90%88%
bridge/src/native-dependency-diagnostics.ts 97%92% 75%55%
bridge/src/codex-rollout-response.ts 97%92% 91%90%
bridge/src/apme/claude-transcript-reader.ts 97%92% 100%88%
bridge/src/calendar.ts 97%95% 96%84%
bridge/src/observed-steering.ts 97%93% 96%86%
bridge/src/session-push-channel.ts 97%97% 50%82%
bridge/src/utils/project-name.ts 97%93% 100%81%
bridge/src/coordination-evidence.ts 97%93% 97%79%
shared/src/session-utils.ts 97%97% 100%89%
plugin/src/system/win32-volume.ts 97%94% 92%81%
bridge/src/apme/graph.ts 97%93% 100%82%
bridge/src/observed-turn-watchdogs.ts 97%96% 100%75%
bridge/src/awaiting-overlay.ts 98%92% 100%84%
bridge/src/hook-codex-sessions.ts 98%91% 91%89%
bridge/src/permission-resolver.ts 98%94% 91%70%
bridge/src/apme/adapters/opencode-hook.ts 98%98% 100%84%
shared/src/subagent-activity.ts 98%94% 86%88%
bridge/src/card-feed.ts 98%91% 100%81%
bridge/src/timeline-store.ts 98%95% 100%85%
bridge/src/timeline-summarizer.ts 98%91% 80%58%
shared/src/creature-layout.ts 98%96% 93%86%
bridge/src/connection-diagnostics.ts 99%95% 96%82%
bridge/src/apme/adapters/claude-hook.ts 100%100% 100%77%
bridge/src/apme/dashboard-html.ts 100%100% 100%100%
bridge/src/apme/judge-detect.ts 100%98% 100%86%
bridge/src/apme/scorers/index.ts 100%98% 100%79%
bridge/src/apme/settings.ts 100%98% 100%90%
bridge/src/apme/task-gradeability.ts 100%100% 100%96%
bridge/src/ble-sync-status.ts 100%100% 100%91%
bridge/src/board-timeline-entry.ts 100%83% 100%80%
bridge/src/card-modules.ts 100%100% 100%90%
bridge/src/claude-interrupt-marker.ts 100%95% 100%92%
bridge/src/codex-ambient-hooks.ts 100%100% 100%100%
bridge/src/codex-hook-silence.ts 100%94% 100%79%
bridge/src/compact-session-labels.ts 100%100% 100%100%
bridge/src/daemon-launcher.ts 100%100% 100%100%
bridge/src/daemon-listen.ts 100%100% 100%100%
bridge/src/daemon-port.ts 100%100% 100%89%
bridge/src/daemon-settings.ts 100%95% 100%75%
bridge/src/daemon-socket-drain.ts 100%81% 100%50%
bridge/src/dashboard-providers.ts 100%100% 100%100%
bridge/src/deferred-reply-read.ts 100%100% 100%100%
bridge/src/device-transcription.ts 100%96% 100%92%
bridge/src/display-dim.ts 100%95% 100%69%
bridge/src/esp32-flash-lease.ts 100%100% 100%94%
bridge/src/event-loop-telemetry.ts 100%100% 100%75%
bridge/src/hermes-sessions.ts 100%97% 100%97%
bridge/src/hook-claude-sessions.ts 100%95% 100%91%
bridge/src/hook-response-source.ts 100%100% 100%100%
bridge/src/http-auth-gate.ts 100%100% 100%93%
bridge/src/hub-state-identity.ts 100%100% 100%97%
bridge/src/ips10-roster.ts 100%79% 75%67%
bridge/src/legacy-rearm-ledger.ts 100%100% 100%100%
bridge/src/modules/types.ts 100%100% 100%100%
bridge/src/observed-launch.ts 100%100% 50%90%
bridge/src/openclaw-model-catalog.ts 100%100% 100%100%
bridge/src/openclaw-session.ts 100%100% 100%100%
bridge/src/personal-voice-turn.ts 100%96% 100%89%
bridge/src/pixoo/desk-awareness.ts 100%100% 100%100%
bridge/src/pixoo/hermes-mermaid.generated.ts 100%100% 100%100%
bridge/src/pixoo/official-dot-glyphs.generated.ts 100%100% 100%100%
bridge/src/relayed-usage.ts 100%100% 100%95%
bridge/src/remote-sessions.ts 100%100% 100%100%
bridge/src/session-id-resolve.ts 100%100% 100%93%
bridge/src/tui/gauge.ts 100%96% 100%92%
bridge/src/usage-event.ts 100%100% 100%91%
bridge/src/utils/redact-secrets.ts 100%100% 100%50%
bridge/src/weather-config.ts 100%95% 100%86%
hooks/src/codex-mini-toml.ts 100%96% 100%94%
hooks/src/hermes-install.ts 100%100% 100%86%
plugin/src/connection-status-pi.ts 100%100% 100%95%
plugin/src/device-profile.ts 100%100% 100%100%
plugin/src/launch-targets.ts 100%100% 100%93%
plugin/src/renderers/launcher-renderer.ts 100%100% 100%50%
plugin/src/renderers/usage-dial-renderer.ts 100%90% 100%47%
plugin/src/renderers/utility-renderer.ts 100%100% 100%75%
plugin/src/utility-modes/usage-dial-view.ts 100%92% 100%77%
shared/src/action-fold.ts 100%100% 100%95%
shared/src/adapter.ts 100%100% 100%100%
shared/src/collaboration-presentation.ts 100%100% 100%100%
shared/src/connection-status.ts 100%100% 100%100%
shared/src/daemon-parity.ts 100%100% 100%90%
shared/src/design-tokens.ts 100%100% 100%100%
shared/src/gateway-live-activity.ts 100%99% 100%87%
shared/src/gateway-protocol.ts 100%100% 100%100%
shared/src/idotmatrix-identity.ts 100%100% 100%94%
shared/src/llm-settings.ts 100%100% 100%98%
shared/src/mdns-identity.ts 100%100% 100%100%
shared/src/model-provider.ts 100%100% 100%93%
shared/src/openclaw-approval.ts 100%99% 100%96%
shared/src/paper-palette.ts 100%100% 100%100%
shared/src/protocol.ts 100%100% 100%100%
shared/src/session-state-presentation.ts 100%100% 100%91%
shared/src/state-colors.ts 100%100% 100%75%
shared/src/states.ts 100%100% 100%100%
shared/src/svg-renderers/hermes-brand.ts 100%100% 100%100%
shared/src/svg-renderers/hermes-mermaid-2d.generated.ts 100%100% 100%100%
shared/src/svg-renderers/text-utils.ts 100%91% 100%69%
shared/src/svg-renderers/usage-reserve-marks.ts 100%100% 100%100%
shared/src/task-title.ts 100%95% 100%92%
shared/src/terrarium-rules.ts 100%100% 100%100%
shared/src/timeline-markdown.ts 100%99% 100%92%
shared/src/timeline-task-display.ts 100%100% 100%96%
shared/src/usage-presentation.ts 100%100% 100%98%
shared/src/voice-ptt.ts 100%100% 100%100%
shared/src/voice-reply-digest.ts 100%97% 100%90%
shared/src/zai-pair-view.ts 100%100% 100%100%
shared/src/zai-quota.ts 100%97% 100%96%

Trend

History

Totals from the last runs of this page on master.

Tests

5,591

last 50 runs on master

Pass rate

99.8%

last 50 runs on master

Line coverage

60.4%

last 50 runs on master