Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ enum ActionOutcomeHumanRenderer {
case .refreshTarget:
"refresh the target before retrying"
case .updateRuntime:
"update the runtime before retrying"
"verify the selected runtime before retrying"
case .reconnectSession:
"reconnect the Bridge session before retrying"
case .recoverSideEffect:
Expand Down
7 changes: 7 additions & 0 deletions Apps/CLI/Tests/CoreCLITests/ResultEnvelopeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ struct ResultEnvelopeTests {
"⛔ Click refused before dispatch; reconnect the Bridge session before retrying")
}

@Test func `runtime refusal asks for verification without assuming an outdated build`() {
let outcome = DesktopActionOutcome.refused(route: .bridge, reason: .runtimeIncompatible)
#expect(ActionOutcomeHumanRenderer.statusLine(for: outcome, operation: "Capture") ==
"⛔ Capture refused before dispatch; verify the selected runtime before retrying")
#expect(outcome.refusalReason == .runtimeIncompatible)
}

@Test func `Space switch human detail never claims unverified dispatch completed`() {
let dispatched = DesktopActionOutcome.dispatchedUnverified(
delivery: .init(mechanism: .nativeFramework, mode: .foreground),
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
- Fix host-routed screen observations with Accessibility elements by validating their semantic owner separately from the screen raster target. #715, #710.
- Fix application name and bundle resolution being blocked by reaped processes lingering in LaunchServices; require repeated native absence while retaining refusal for uncertain or changing process identities. #709.

- Clarify observation evidence failures and runtime refusal guidance so same-build verification errors do not imply that an update will fix them. #710.

## 4.3.4 - 2026-09-11

**Highlights:** Restore provider-compatible MCP tools and frontmost daemon captures.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ extension PeekabooBridgeServer {
}
throw PeekabooBridgeErrorEnvelope(
code: .internalError,
message: "The desktop observation provider returned response evidence that did not match " +
"the requested \(mismatch).")
message: "The desktop observation provider returned inconsistent response evidence: \(mismatch).")
}

func windowMutationResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,35 @@ import Testing
@Suite(.serialized)
@MainActor
struct PeekabooBridgeDesktopObservationResultTests {
@Test
func `observation refusal treats unexpected evidence as a diagnostic rather than a target`() throws {
let original = Self.readOnlyFixtureResult()
let application = try Self.fixture().result.capture.metadata.applicationInfo
let observation = DesktopObservationResult(
target: original.target,
capture: .init(
imageData: Data(),
metadata: .init(
size: original.capture.metadata.size,
mode: .screen,
applicationInfo: application,
displayInfo: original.capture.metadata.displayInfo,
diagnostics: original.capture.metadata.diagnostics)),
elements: original.elements)

let error = #expect(throws: PeekabooBridgeErrorEnvelope.self) {
try PeekabooBridgeRequestContext.$usesAttestedOperationResultSemantics.withValue(true) {
try PeekabooBridgeServer.validateAttestedObservationBinding(
Self.readOnlyRequest,
result: observation,
requireContentDigest: false)
}
}
let expected = "The desktop observation provider returned inconsistent response evidence: " +
"unexpected application evidence."
#expect(error?.message == expected)
}

@Test
func `signed observation authenticates exact file bytes and rejects same-size replacement`() async throws {
let fixture = try Self.fixture()
Expand Down
2 changes: 2 additions & 0 deletions docs/commands/see.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ peekaboo see --app "Google Chrome" --json --path /tmp/chrome-see.png \

## Troubleshooting tips

- An inconsistent-response-evidence refusal means the returned capture could not be verified against the request. Check the specific evidence named in the error. Use `--verbose` to identify the selected runtime and Bridge socket before inspecting that host; a current client and host can still hit a runtime bug, so an update is not assumed to resolve every verification refusal.

Observation verification and MCP image reloads limit each artifact to 256 MiB. Reads retain one regular-file
descriptor and reject files that grow or change during the read. Ordinary symlink paths remain supported;
oversized or replaced artifacts fail before their content is published.
Expand Down