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
9 changes: 8 additions & 1 deletion benchmark/citrix_ica_hdx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,10 @@ observation must return both challenges and a signed `observed_at` timestamp.
The harness accepts the observation challenge once and rejects a stale, future,
or replayed observation. A signed independent collector also binds its current
OS observations of the runner, oracle, and collector principals and executable
digests to the configuration, trial, session, and transport.
digests to the configuration, trial, session, transport, and exact display.
The display observation includes the viewport, DPI, scale, window mode, and a
monitor-topology digest. A resize or monitor change outside the configured
trial condition refuses before dispatch.

Before each dispatch, a separately authenticated read-only oracle must report a
signed `REFUTED` baseline for the exact trial, entity, and effect. The collector
Expand All @@ -193,6 +196,10 @@ and the fixed condition contract. It emits `VERIFIED` only for a healthy trial
whose complete oracle checks pass. A trial with `passed: false` is always
`HALTED` or `HALTED_UNCERTAIN`; it is never `VERIFIED`.

The runner receipt must also report zero retries and zero model calls. The
terminal report includes explicit counts for every condition, verified and halt
outcomes, silent incorrect success, over-halt, retries, and model calls.

A commit timeout has the fixed result `HALTED_UNCERTAIN`. The runner reports
uncertain delivery, zero retries, and required independent reconciliation. The
oracle must independently confirm or refute the effect before the counted trial
Expand Down
7 changes: 5 additions & 2 deletions benchmark/citrix_ica_hdx/fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@

from PIL import Image, ImageDraw, ImageFilter, ImageOps

from openadapt_flow.backends.remote_display import RemoteDisplayError, WindowInfo
from openadapt_flow.backends.remote_display import (
RemoteInputRefused,
WindowInfo,
)

# -- fixed synthetic geometry (pixel space of the captured frame) --------------
# Window bounds are screen POINTS; the captured frame is pixels. scale 2.0 (a
Expand Down Expand Up @@ -335,7 +338,7 @@ def key(self, keycode: int, *, down: bool, flags: list[str]) -> None:
if self.paste_blocked and "control" in flags:
# Citrix policy disables the clipboard channel: fail LOUD rather
# than let a no-op paste look like a completed action.
raise RemoteDisplayError(
raise RemoteInputRefused(
"clipboard redirection is disabled by session policy; paste "
"was not delivered (refusing to let a no-op look like success)"
)
Expand Down
52 changes: 49 additions & 3 deletions benchmark/citrix_ica_hdx/run_real_acceptance.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey

SCHEMA = "openadapt.citrix-real-acceptance.v3"
REPORT_SCHEMA = "openadapt.citrix-real-acceptance-report.v3"
REPORT_SCHEMA = "openadapt.citrix-real-acceptance-report.v4"
TRUST_ROOT_SCHEMA = "openadapt.citrix-acceptance-trust-roots.v1"
COLLECTOR_SCHEMA = "openadapt.citrix-independent-collector.v2"
SHA256_RE = re.compile(r"[0-9a-f]{64}")
NONCE_RE = re.compile(r"[0-9a-f]{32,128}")
MAX_COLLECTOR_AGE_S = 300
Expand Down Expand Up @@ -664,14 +665,15 @@ def _validate_collector_evidence(
"standin",
"session_id_sha256",
"transport_sha256",
"display",
"observed_at",
"observed_components",
"diagnostic_evidence",
}
if not isinstance(proof, dict) or set(proof) != required:
raise ValueError("collector evidence has incomplete or unknown fields")
expected = {
"schema_version": "openadapt.citrix-independent-collector.v1",
"schema_version": COLLECTOR_SCHEMA,
"campaign_nonce": config["campaign_nonce"],
"config_sha256": config_sha256,
"execution_challenge": execution_challenge,
Expand All @@ -681,6 +683,7 @@ def _validate_collector_evidence(
"standin": False,
"session_id_sha256": config["fingerprints"]["session"]["session_id_sha256"],
"transport_sha256": config["fingerprints"]["ica_hdx"]["transport_sha256"],
"display": config["fingerprints"]["display"],
}
for key, value in expected.items():
if proof[key] != value:
Expand Down Expand Up @@ -716,6 +719,7 @@ def _validate_runner_receipt(
"condition",
"delivery_state",
"retry_count",
"model_calls",
"collector_evidence_sha256",
"reconciliation_required",
}
Expand All @@ -725,8 +729,10 @@ def _validate_runner_receipt(
raise ValueError("session runner receipt schema is invalid")
if receipt["trial_id"] != trial["id"] or receipt["condition"] != trial["condition"]:
raise ValueError("session runner receipt is not bound to the trial")
if receipt["retry_count"] != 0:
if type(receipt["retry_count"]) is not int or receipt["retry_count"] != 0:
raise ValueError("a counted acceptance trial must not retry")
if type(receipt["model_calls"]) is not int or receipt["model_calls"] != 0:
raise ValueError("a counted healthy-path acceptance trial must use zero models")
if receipt["collector_evidence_sha256"] != collector_evidence_sha256:
raise ValueError("session runner receipt is not bound to collector evidence")
required_delivery = {
Expand Down Expand Up @@ -979,6 +985,7 @@ def _write_terminal_report(
*,
require_primary: bool = False,
) -> None:
_refresh_report_metrics(report)
report["journal"] = {
"path": str(journal.path),
"sha256": _sha256(journal.path),
Expand All @@ -993,6 +1000,44 @@ def _write_terminal_report(
raise


def _refresh_report_metrics(report: dict) -> None:
"""Add explicit reliability counts from the retained terminal rows."""

rows = [row for row in report.get("trials", []) if isinstance(row, dict)]
condition_counts = {
condition: sum(row.get("condition") == condition for row in rows)
for condition in EXPECTED_OUTCOMES
}
report["metrics"] = {
"trial_count": len(rows),
"condition_counts": condition_counts,
"verified_outcomes": sum(row.get("outcome") == "VERIFIED" for row in rows),
"halted_outcomes": sum(row.get("outcome") == "HALTED" for row in rows),
"halted_uncertain_outcomes": sum(
row.get("outcome") == "HALTED_UNCERTAIN" for row in rows
),
"silent_incorrect_successes": sum(
row.get("outcome") == "VERIFIED" and row.get("expected") != "VERIFIED"
for row in rows
),
"over_halts": sum(
row.get("expected") == "VERIFIED" and row.get("outcome") != "VERIFIED"
for row in rows
),
"delivery_retries": sum(
row.get("retry_count", 0)
for row in rows
if type(row.get("retry_count", 0)) is int
),
"model_calls": sum(
row["receipt"].get("model_calls", 0)
for row in rows
if isinstance(row.get("receipt"), dict)
and type(row["receipt"].get("model_calls", 0)) is int
),
}


def _recover_reserved_campaign(binding: dict) -> dict:
output = Path(binding["output"])
fallback = Path(binding["terminal_fallback"])
Expand Down Expand Up @@ -1469,6 +1514,7 @@ def main() -> int:
print(f"acceptance refused: {exc}", file=sys.stderr)
return 2
if not args.execute:
_refresh_report_metrics(report)
_atomic_write_json(args.output, report)
if not args.execute:
print("preflight passed; no trial ran")
Expand Down
10 changes: 7 additions & 3 deletions claims.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,10 @@ claims:
- path: tests/test_rdp_backend.py
proves: >-
CI covers the backend/transport contract, framebuffer conversion,
pointer and keyboard delivery, and record-compile-replay conformance.
pointer and keyboard delivery, resize rebaseline between actions,
mid-lease geometry refusal, pre-delivery horizontal-scroll refusal,
typed uncertain transport failures, and record-compile-replay
conformance.
- path: tests/test_rdp_multiapp_campaign_contract.py
proves: >-
Required CI covers the bounded 27-trial FreeRDP campaign contract,
Expand Down Expand Up @@ -707,8 +710,9 @@ claims:
proves: >-
Required CI validates the public real-ICA campaign preflight,
distinct authority keys, executable and oracle attestations, one-use
nonce journal, crash recovery, uncertain dispatch, and fail-closed
report contract without provisioning infrastructure.
nonce journal, crash recovery, signed display and monitor-topology
observation, explicit reliability metrics, uncertain dispatch, and
fail-closed report contract without provisioning infrastructure.
- path: benchmark/citrix_workspace/results.json
kind: artifact
proves: >-
Expand Down
4 changes: 2 additions & 2 deletions docs/PRODUCT_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ and its generated view is [`VERIFICATION.md`](VERIFICATION.md).
| Native macOS desktop actuation | **Scoped acceptance** | Candidate `b1b61a5` completed 3/3 TextEdit replace-and-save trials with exact file-byte effects and refused a two-window ambiguous selector without changing either file. See the [accepted evidence adjudication](../benchmark/macos_native/textedit_counted_3plus1_b1b61a5_20260717.adjudication.json). | Acceptance covers TextEdit on one macOS 15.7.3 Apple Silicon host and active user session. Customer applications require workflow-specific qualification. |
| Native macOS AX structured identity | **Scoped acceptance** | The macOS backend implements the same structured-layer contract as the browser DOM, Windows UIA, and Linux AT-SPI backends: it records a stable AX locator, re-finds the UNIQUE element at replay, refuses ambiguous / truncated / scope-escaping enumeration instead of guessing, and returns structured text under a point. Headless unit CI covers record/locate/refuse; a live-AX TextEdit run produced real evidence ([AX identity adjudication](../benchmark/macos_native/ax_identity_20260720.adjudication.json)); the record鈫抍ompile鈫抮eplay conformance test asserts zero model calls on healthy replay. See [`tests/test_macos_structural.py`](../tests/test_macos_structural.py) and the [capability matrix](../tests/test_backend_capability_matrix.py). | The backend uses gated point-bound physical click after structural resolution rather than claiming AXPress everywhere. AX exposure varies by application; controls without durable AX identity use the visual ladder. |
| Native Linux desktop actuation | **Scoped acceptance** | The required `linux-atspi-x11` job runs a real GTK3 application against AT-SPI inside an isolated Xvfb/session-D-Bus environment: 3 clean exact-file-effect trials, 3 ambiguous-target refusals, and 3 stale-target refusals. Unit CI covers the remaining window, traversal, capture, physical-input, and portal boundaries. | Acceptance is bounded to the in-tree GTK3 workflow and CI image. Each application and environment retains its own qualification. The built-in driver uses X11; Wayland requires a live operator-approved XDG portal session and refuses without one. |
| RDP | **Scoped acceptance** | Candidate `82a658a` completed 3/3 real-network Aardwolf RDP trials into Windows 11, with a guest-tools file oracle, zero failures, zero silent incorrect successes, zero over-halts, and zero model calls. The public multi-window FreeRDP campaign adds a bounded 27-trial contract with independent SQLite, CSV, and Maildir oracles. See the [accepted batch](../benchmark/rdp/ACCEPTED_BATCH_82A658A.md) and [campaign contract](../benchmark/rdp_multiapp/README.md). | The accepted batch covers the tested 1280脳800 transport/input task. The multi-window fixture uses synthetic applications. Target applications, identity/effect rules, session policies, and display conditions are qualified per deployment. |
| Citrix / pixel-only remote display | **Code-qualified** | `--backend citrix` binds an exact Citrix Workspace window, readiness marker, pixel-only ladder, governed run, durable resume, and report; required CI covers those orchestration and refusal contracts. The public real-ICA preflight adds distinct authority keys, executable and oracle attestations, one-use campaign state, crash recovery, and uncertain-dispatch handling. Separately, the retained no-DOM driver qualification passed 3 healthy effect-confirmed trials and 3 drift safe-halts with zero model calls, silent incorrect successes, or false completion, and records `code_readiness_accepted=true`. | The counted stand-in and preflight do not claim live ICA/HDX acceptance. A live result remains bound to the exact Workspace/server/application matrix, customer-approved executable, and independent effect oracle. Deployment-specific recipes, data, and thresholds stay outside the public repository. |
| RDP | **Scoped acceptance** | Candidate `82a658a` completed 3/3 real-network Aardwolf RDP trials into Windows 11, with a guest-tools file oracle, zero failures, zero silent incorrect successes, zero over-halts, and zero model calls. The public multi-window FreeRDP campaign adds a bounded 27-trial contract with independent SQLite, CSV, and Maildir oracles. The backend also rebaselines a changed framebuffer between actions, refuses a change during the exact-frame lease, refuses unsupported horizontal scroll before delivery, and classifies transport failures as uncertain delivery. See the [accepted batch](../benchmark/rdp/ACCEPTED_BATCH_82A658A.md) and [campaign contract](../benchmark/rdp_multiapp/README.md). | The accepted batch covers the tested 1280脳800 transport/input task. The multi-window fixture uses synthetic applications. Target applications, identity/effect rules, session policies, and display conditions are qualified per deployment. A composite multi-monitor session remains deployment-qualified evidence, not part of the accepted 1280脳800 batch. |
| Citrix / pixel-only remote display | **Code-qualified** | `--backend citrix` binds an exact Citrix Workspace window, readiness marker, pixel-only ladder, governed run, durable resume, and report; required CI covers those orchestration and refusal contracts. The window driver recalculates capture scale after a resize or cross-monitor move and refuses DPI or geometry drift during input. The public real-ICA preflight adds distinct authority keys, executable and oracle attestations, a signed display and monitor-topology observation, explicit reliability metrics, one-use campaign state, crash recovery, and uncertain-dispatch handling. Separately, the retained no-DOM driver qualification passed 3 healthy effect-confirmed trials and 3 drift safe-halts with zero model calls, silent incorrect successes, or false completion, and records `code_readiness_accepted=true`. | The counted stand-in and preflight do not claim live ICA/HDX acceptance. A live result remains bound to the exact Workspace/server/application/display matrix, customer-approved executable, and independent effect oracle. Deployment-specific recipes, data, and thresholds stay outside the public repository. |
| Identity verification | **Experimental, armed steps only** | Wrong-entity refusal and adversarial corpora run in CI. | Unarmed clicks have no identity check. Real compiled bundles currently arm only a subset of clicks. |
| System-of-record effect verification | **Experimental** | REST, FHIR, SQL, file, and document verifier contracts catch fault classes that screen-only verification misses. A deployment with multiple reviewed read boundaries selects and preflights the strongest evidence tier before input, retains that binding through durable resume, and never downgrades after an action. | Effects are not generally inferred; both authored effects and a configured verifier are required. A selected verifier that becomes unavailable halts or enters reconciliation. |
| Lint and certification policies | **Beta** | The CLI reports coverage gaps and refuses bundles that violate a selected policy. | Certification is opt-in; `replay` remains the permissive tutorial path. Use fail-closed `run` for a deployment. |
Expand Down
4 changes: 2 additions & 2 deletions docs/VERIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@
| Backing evidence | Kind | Gating / CI stage | Strength | Proves |
|---|---|---|---|---|
| `tests/e2e/test_parallels_rdp_e2e.py` | test | opt-in (OAFLOW_PARALLELS_RDP_E2E) | validating | Opt-in snapshot-safe real RDP qualification harness with exactly three trials, independent guest-tools oracle, failure taxonomy, and cleanup. |
| `tests/test_rdp_backend.py` | test | ci (required PR gate (test)) | supported | CI covers the backend/transport contract, framebuffer conversion, pointer and keyboard delivery, and record-compile-replay conformance. |
| `tests/test_rdp_backend.py` | test | ci (required PR gate (test)) | supported | CI covers the backend/transport contract, framebuffer conversion, pointer and keyboard delivery, resize rebaseline between actions, mid-lease geometry refusal, pre-delivery horizontal-scroll refusal, typed uncertain transport failures, and record-compile-replay conformance. |
| `tests/test_rdp_multiapp_campaign_contract.py` | test | ci (required PR gate (test)) | supported | Required CI covers the bounded 27-trial FreeRDP campaign contract, independent SQLite, CSV, and Maildir oracles, fail-closed visual action preflight, uncertain-delivery handling, and result accounting. |
| `benchmark/rdp_multiapp/README.md` | doc | artifact (doc/benchmark) | roadmap | The public synthetic multi-window campaign defines the workflow, faults, independent persisted surfaces, and acceptance denominator. |
| `benchmark/rdp/results_82a658a_20260718.sanitized.json` | benchmark | artifact (doc/benchmark) | roadmap | Candidate 82a658a passed 3/3 at 51.845, 10.467, and 7.477 seconds, with zero failures, silent incorrect successes, over-halts, or model calls. |
Expand Down Expand Up @@ -342,7 +342,7 @@
| `tests/test_cli_new_commands.py` | test | ci (required PR gate (test)) | supported | Required CI restores the recorded Citrix owner, exact title, and readiness binding through durable approve/resume, and refuses before backend construction when resumed configuration is incomplete. |
| `tests/test_hosted.py` | test | ci (required PR gate (test)) | supported | Required CI binds a completed local Citrix report to the closed `citrix` execution token without copying target owner, title, or readiness values into the hosted summary. |
| `tests/e2e/test_citrix_workspace_standin_e2e.py` | test | opt-in (OAFLOW_CITRIX_STANDIN_E2E) | validating | The dedicated Citrix backend passes three healthy effect-confirmed record->compile->replay trials and three severe-drift safe-halts over a no-DOM surface, with zero model calls, false completion, or silent incorrect success. |
| `tests/test_citrix_real_acceptance.py` | test | ci (required PR gate (test)) | supported | Required CI validates the public real-ICA campaign preflight, distinct authority keys, executable and oracle attestations, one-use nonce journal, crash recovery, uncertain dispatch, and fail-closed report contract without provisioning infrastructure. |
| `tests/test_citrix_real_acceptance.py` | test | ci (required PR gate (test)) | supported | Required CI validates the public real-ICA campaign preflight, distinct authority keys, executable and oracle attestations, one-use nonce journal, crash recovery, signed display and monitor-topology observation, explicit reliability metrics, uncertain dispatch, and fail-closed report contract without provisioning infrastructure. |
| `benchmark/citrix_workspace/results.json` | artifact | artifact (doc/benchmark) | roadmap | The retained six-trial record reports code_readiness_accepted=true and ica_hdx_accepted=false, keeping driver readiness distinct from a counted live ICA/HDX qualification. |
| `docs/desktop/CITRIX_PIXEL.md` | doc | artifact (doc/benchmark) | roadmap | The Citrix driver model, reusable evidence, exact-deployment acceptance contract, independent-effect boundary, and customer-controlled posture. |

Expand Down
Loading