Desktop core E2E harness, tiered testing, and release blessing gate#9168
Conversation
Introduces the tiered desktop-core-harness dispatcher, YAML flow lint against registered bridge actions, CORE_E2E.md, and a Linux self-check job so desktop PRs get a fast static confidence gate. Verification: ./desktop/macos/scripts/desktop-core-harness.sh --self-check Co-authored-by: Cursor <cursoragent@cursor.com>
Adds capture_test_transcript plus conversation/memories/tasks snapshot actions and last_assistant_text in main_chat_snapshot for deterministic harness assertions. Verification: desktop-flow-lint.py (action names registered in bridge source) Co-authored-by: Cursor <cursoragent@cursor.com>
Adds bless-release.sh for agent-local T2 blessing, requires blessedSha metadata in check-desktop-release-promotion.py with typed override escape hatch, and extends the promotion workflow/policy enforcement. Verification: python3 .github/scripts/test_check_desktop_release_promotion.py Co-authored-by: Cursor <cursoragent@cursor.com>
Parses bless keys from GitHub release KEY_VALUE metadata, fixes parseVersion for two-part macOS tags, and adds a Blessed column plus deployable banner on the releases dashboard. Verification: parseVersion regex accepts v11.0+11000-macos Co-authored-by: Cursor <cursoragent@cursor.com>
open_ask_omi returns focused timing fields, not askOmiOpen in action detail; state.expect still covers askOmiOpen on step 3. Co-authored-by: Cursor <cursoragent@cursor.com>
Wire capture_test_transcript through TranscriptionStorage with real session IDs, assert conversation_count_increased in the flow, strengthen chat marker checks, and document hermetic vs Omi Dev auth bootstrap in CORE_E2E.md. Verified: swift build, desktop-core-harness --self-check. Co-authored-by: Cursor <cursoragent@cursor.com>
The manifest pass-check used `raise (X if cond else None)`, which raised TypeError on success; background desktop-run-local and wait for the worktree automation bridge; use make desktop-run-local (alice) instead of omi-auth-seed; discover --port via dev-instance.sh; consolidate KEY_VALUE edits into release-keyvalue.py; add cleanup trap. Verified: bash -n scripts/bless-release.sh; release-keyvalue.py self-test (pass manifest exit 0, fail exit 1, old pattern TypeError on pass). Co-authored-by: Cursor <cursoragent@cursor.com>
Split T0 CI to desktop-only static checks (--skip-backend-contracts) so the job no longer duplicates backend pytest; enforce offline dev-stack via config digest + health probes; derive flow lists from tier metadata; record provider_mode in manifests. Verified: bash -n; --self-check (8 pytest contracts); --self-check --skip-backend-contracts; workflow YAML parse; tier-1 metadata = harness-smoke + navigation; probe exits 0 on healthy offline stack and 2 on non-offline digest. Co-authored-by: Cursor <cursoragent@cursor.com>
memories_snapshot and tasks_snapshot now await real API/store loads and return falsifiable load_state/count-valid fields; flows assert those instead of vacuous ok/refreshed. floating-bar-functional waits for chat idle and asserts the stub marker echo via main_chat_snapshot (shared ChatProvider). Document automationStopCaptureTestSession sync with stopTranscription(). Verified: xcrun swift build -c debug --package-path Desktop (pass); python3 scripts/desktop-flow-lint.py (pass, 22 flows). Live omi-harness runs blocked by concurrent ./run.sh lock contention; manual binary swap breaks Sparkle rpaths so full bundle rebuild needed for live flow runs. Co-authored-by: Cursor <cursoragent@cursor.com>
Local profile no longer crashes on Auth.auth() or signs out when pi-mono requests a token refresh against the Auth emulator; skip RealtimeHub warm-up and Omi Dev auth seed for desktop-run-local. Replace mapfile (bash 4+) in desktop-core-harness.sh for macOS default bash. Verified: omi-core-e2e boots signed-in as alice on bridge :47956; tier-2 harness runs 9/13 flows pass (capture-lifecycle, chat*, floating-bar still fail). Co-authored-by: Cursor <cursoragent@cursor.com>
Terminate omi-bless-* app and run.sh process tree on bless cleanup instead of only killing the launch subshell; require harness sentinel + alive owned PIDs before T2 accepts a dev stack; require provider_mode=offline in check-manifest and fail update-blessed when KEY_VALUE block is missing. Verified: bash -n bless-release.sh desktop-core-harness.sh; release-keyvalue.py self-test; probe fixtures (stale PIDs, foreign/absent sentinel → exit 1); cleanup process-tree simulation OK. Co-authored-by: Cursor <cursoragent@cursor.com>
Dedupe repeated markers in one message and add a regression test so hermetic chat assertions see a single stub echo per turn. Verified: cargo test in Backend-Rust (301 passed). Co-authored-by: Cursor <cursoragent@cursor.com>
Clear kernel main_chat surface state and UI messages before each chat flow so stub marker assertions are not polluted by prior turns in the same session. Verified: chat-hermetic + chat flows pass on bridge port 47956. Co-authored-by: Cursor <cursoragent@cursor.com>
Report askOmiOpen for any open AI conversation (including response view), reset floating-bar kernel/chat on open_ask_omi reset, and use floating-bar snapshot/wait actions so assertions target the correct provider. Verified: floating-bar-functional flow pass on bridge port 47956. Co-authored-by: Cursor <cursoragent@cursor.com>
Await segment persistence in the automation inject seam and guarantee finishedAt is at least one second after startedAt so from-segments uploads pass backend validation. Verified: capture-lifecycle flow pass on bridge port 47956. Co-authored-by: Cursor <cursoragent@cursor.com>
…gers. Restart unhealthy harness services before dev-up skips them, verify the Typesense Docker container in probe_dev_stack, and retry stack probes after dev-up so tier-2 dispatch passes reliably. Verified: PROVIDER_MODE=offline make dev-up; ./scripts/desktop-core-harness.sh --tier 2 --bundle omi-core-e2e --port 47956 --keep-stack (manifest passed=true, provider_mode=offline); release-keyvalue.py self-test; desktop-core-harness --self-check. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a9cc72e705
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| pub fn stub_chat_completions_response(req: &ChatCompletionRequest) -> Response { | ||
| let user_text = extract_latest_user_text(req); | ||
| let lines = fixture_lines(&user_text, DEFAULT_FIXTURE); |
There was a problem hiding this comment.
Honor non-stream chat completion requests in the stub
When OMI_LLM_STUB=1, this stub always returns text/event-stream, even when req.stream is false. Non-stream desktop callers such as AgentDelegationResolver.swift and AgentPill.swift send "stream": false and parse a JSON choices[0].message.content response, so the offline/T3 harness paths that exercise those callers will get HTTP 200 with an SSE body and fail with an unexpected response shape. Return an OpenAI-compatible JSON completion for non-stream requests.
Useful? React with 👍 / 👎.
| fi | ||
| if [[ "$TIER" -eq 3 ]]; then | ||
| set +e | ||
| OMI_AUTOMATION_PORT="$PORT" "$SCRIPT_DIR/agent-continuity-gauntlet.sh" --bundle-id "com.omi.${BUNDLE#omi-}" |
There was a problem hiding this comment.
Use the actual named-bundle id for T3 gauntlet
For any required omi-* bundle, the app-config logic derives the bundle id as com.omi.<full slug> (for example omi-core-e2e becomes com.omi.omi-core-e2e). Stripping the omi- prefix here makes the T3 gauntlet target com.omi.core-e2e, which will not match the running named bundle, so tier-3 harness runs fail to connect to the app even though the bridge is healthy.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
22 issues found across 56 files
Confidence score: 2/5
desktop/macos/Desktop/Sources/DesktopAutomationBridge.swift(reset_main_chat) can erase a signed-in user’s main chat when automation bridge is enabled on a production bundle, which is a direct user-data loss risk if merged as-is — add the same non-prod gating used by adjacent harness-only actions before allowing reset.desktop/macos/Desktop/Sources/AppState/AppState+Transcription.swiftallowsalready_recordingto proceed throughinject/stop, so harness capture can interfere with a real in-progress recording and tear it down unexpectedly — track harness-owned sessions (or hard-stop on preexisting recordings) before merge.- Release promotion integrity is currently weak across
desktop/macos/scripts/release-keyvalue.pyand.github/workflows/desktop_promote_prod.yml: blessing can accept non-T2 manifests, malformed metadata blocks can appear successful, andblessed_override=truecan bypassblessedShacommit mismatch checks — tighten these validations so only correctly tiered, well-formed metadata tied to the target commit can be promoted. desktop/macos/scripts/desktop-flow-lint.pychecks for a non-existentdo:key, so typed flows can silently include legacydosteps and pass lint, reducing confidence in flow correctness — fix the key detection todoand re-run lint on typed flows before merging.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="desktop/macos/Backend-Rust/src/routes/chat_completions.rs">
<violation number="1" location="desktop/macos/Backend-Rust/src/routes/chat_completions.rs:616">
P2: Stub always returns SSE even for non-streaming requests — `req.stream` is ignored. A client sending `stream: false` or omitting `stream` gets `Content-Type: text/event-stream` with SSE line events instead of the expected JSON `ChatCompletionResponse`. Consider checking `req.stream` in the stub: when `false`, return a single-chunk JSON response matching the real handler's non-streaming shape.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…delity. Gate harness-only chat/capture actions on non-prod bundles, reject bless manifests without tier-2 offline evidence, fix promotion override semantics, and tighten admin blessed parsing. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
4 issues found across 19 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="desktop/macos/Backend-Rust/src/routes/chat_completions.rs">
<violation number="1" location="desktop/macos/Backend-Rust/src/routes/chat_completions.rs:616">
P2: Stub always returns SSE even for non-streaming requests — `req.stream` is ignored. A client sending `stream: false` or omitting `stream` gets `Content-Type: text/event-stream` with SSE line events instead of the expected JSON `ChatCompletionResponse`. Consider checking `req.stream` in the stub: when `false`, return a single-chunk JSON response matching the real handler's non-streaming shape.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Resolve DesktopAutomationBridge conflict by keeping both the PR's hermetic capture/snapshot actions and main's ptt_start/ptt_stop hooks. Co-authored-by: Cursor <cursoragent@cursor.com>
…down. - Parse Gemini stub markers from latest user content only; assert in test - Propagate harness chat-reset deletion failures to automation bridge - Clear capture-test state after finalize errors so retries can recover - Share release metadata parsing; restore override_unblessed blessedSha warning - Fix flow-lint typed-flow do-key detection; add promotion gate to lint CI Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
1 issue found across 10 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/lint.yml">
<violation number="1" location=".github/workflows/lint.yml:92">
P3: Two assertions in `test_unblessed_release_fails` lack failure context, making CI failures harder to debug. If the check script's output format or exit code changes, the failed assertion produces only `AssertionError` without showing what the actual stdout/stderr contained. The other two test functions already include `result.stderr` in their assertions for this reason.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| @@ -89,6 +89,9 @@ jobs: | |||
| - name: Check desktop prod promotion policy | |||
There was a problem hiding this comment.
P3: Two assertions in test_unblessed_release_fails lack failure context, making CI failures harder to debug. If the check script's output format or exit code changes, the failed assertion produces only AssertionError without showing what the actual stdout/stderr contained. The other two test functions already include result.stderr in their assertions for this reason.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/lint.yml, line 92:
<comment>Two assertions in `test_unblessed_release_fails` lack failure context, making CI failures harder to debug. If the check script's output format or exit code changes, the failed assertion produces only `AssertionError` without showing what the actual stdout/stderr contained. The other two test functions already include `result.stderr` in their assertions for this reason.</comment>
<file context>
@@ -89,6 +89,9 @@ jobs:
- name: Check desktop prod promotion policy
run: python3 .github/scripts/check-desktop-prod-promotion-policy.py
+ - name: Test desktop release promotion gate
+ run: python3 .github/scripts/test_check_desktop_release_promotion.py
+
</file context>
Restore DesktopLocalProfile visibility for harness-gated code paths, replace Response::builder unwraps with response_or_500, and add stderr context to unblessed release promotion test assertions. Co-authored-by: Cursor <cursoragent@cursor.com>
Map changed AuthService, agent runtime, floating bar, and transcription files to tier-2 harness flows, restore check-e2e-flow-coverage.py, and align lint.yml with main's strict coverage gate. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
1 issue found across 6 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="desktop/macos/Backend-Rust/src/routes/chat_completions.rs">
<violation number="1" location="desktop/macos/Backend-Rust/src/routes/chat_completions.rs:616">
P2: Stub always returns SSE even for non-streaming requests — `req.stream` is ignored. A client sending `stream: false` or omitting `stream` gets `Content-Type: text/event-stream` with SSE line events instead of the expected JSON `ChatCompletionResponse`. Consider checking `req.stream` in the stub: when `false`, return a single-chunk JSON response matching the real handler's non-streaming shape.</violation>
</file>
<file name=".github/workflows/lint.yml">
<violation number="1" location=".github/workflows/lint.yml:92">
P3: Two assertions in `test_unblessed_release_fails` lack failure context, making CI failures harder to debug. If the check script's output format or exit code changes, the failed assertion produces only `AssertionError` without showing what the actual stdout/stderr contained. The other two test functions already include `result.stderr` in their assertions for this reason.</violation>
</file>
<file name="desktop/macos/scripts/check-e2e-flow-coverage.py">
<violation number="1" location="desktop/macos/scripts/check-e2e-flow-coverage.py:181">
P2: The harness recommendation in `check-e2e-flow-coverage.py` prints a command that uses the old `omi-harness` runner and the old app name `omi-e2e`, but this PR introduces the `desktop-core-harness.sh` tiered harness and the `omi-core-e2e` bundle name. Developers relying on the coverage report will get stale or incorrect invocation instructions that don't align with the new desktop core E2E contract. Consider updating `harness_command` to generate a command that matches the new harness interface (e.g., `desktop-core-harness.sh` with `--bundle omi-core-e2e` or the appropriate tier/port).</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
@cursor address merge conflict |
|
Merge conflict resolved and pushed. Conflict:
Also fixed the stale harness recommendation in Verification: Commit: |
Resolve harness-smoke.yaml conflict: keep tier: 1 and AuthService.swift coverage from the desktop core E2E harness branch. Update check-e2e-flow-coverage.py to recommend desktop-core-harness.sh with omi-core-e2e bundle instead of the legacy omi-harness command. Co-authored-by: David Zhang <Git-on-my-level@users.noreply.github.com>
Move the macOS Swift build/test job out of lint.yml into desktop-swift-ci.yml so PR checks no longer show two unrelated jobs under "Lint Check". Update AgentRuntimeProcessTests source-inspection assertions for the merged main auth refresh guard (forceRefreshToken + DesktopLocalProfile). Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
2 issues found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/desktop-swift-ci.yml">
<violation number="1" location=".github/workflows/desktop-swift-ci.yml:25">
P2: For push events to `main`, the diff base is hard-coded to `HEAD~1` in both the path gate and the e2e coverage check. On a multi-commit push, this only looks at the last commit, so desktop changes in earlier pushed commits can be missed and the Swift build, tests, and e2e coverage validation will be skipped.
Use `github.event.before` as the diff base for push events so the full pushed range is evaluated.</violation>
<violation number="2" location=".github/workflows/desktop-swift-ci.yml:63">
P2: The CI build only validates `arm64-apple-macosx`, while the Codemagic release pipeline builds both `arm64` and `x86_64` and merges them into a universal binary. This means x86_64-specific compile or link failures won't be caught in CI and will only surface during the release build, which has historically caused issues (see the "Burned a build in May 2026" comment in the release pipeline). Consider adding an x86_64 build step or using the same build matrix the release pipeline uses.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| - name: Build desktop Swift app | ||
| if: steps.changed.outputs.should_run == 'true' | ||
| working-directory: desktop/macos | ||
| run: xcrun swift build -c release --package-path Desktop --triple arm64-apple-macosx |
There was a problem hiding this comment.
P2: The CI build only validates arm64-apple-macosx, while the Codemagic release pipeline builds both arm64 and x86_64 and merges them into a universal binary. This means x86_64-specific compile or link failures won't be caught in CI and will only surface during the release build, which has historically caused issues (see the "Burned a build in May 2026" comment in the release pipeline). Consider adding an x86_64 build step or using the same build matrix the release pipeline uses.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/desktop-swift-ci.yml, line 63:
<comment>The CI build only validates `arm64-apple-macosx`, while the Codemagic release pipeline builds both `arm64` and `x86_64` and merges them into a universal binary. This means x86_64-specific compile or link failures won't be caught in CI and will only surface during the release build, which has historically caused issues (see the "Burned a build in May 2026" comment in the release pipeline). Consider adding an x86_64 build step or using the same build matrix the release pipeline uses.</comment>
<file context>
@@ -0,0 +1,68 @@
+ - name: Build desktop Swift app
+ if: steps.changed.outputs.should_run == 'true'
+ working-directory: desktop/macos
+ run: xcrun swift build -c release --package-path Desktop --triple arm64-apple-macosx
+
+ - name: Test desktop Swift app
</file context>
| if [ "${{ github.event_name }}" = "pull_request" ]; then | ||
| DIFF_BASE="origin/${{ github.base_ref }}" | ||
| else | ||
| DIFF_BASE="HEAD~1" |
There was a problem hiding this comment.
P2: For push events to main, the diff base is hard-coded to HEAD~1 in both the path gate and the e2e coverage check. On a multi-commit push, this only looks at the last commit, so desktop changes in earlier pushed commits can be missed and the Swift build, tests, and e2e coverage validation will be skipped.
Use github.event.before as the diff base for push events so the full pushed range is evaluated.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/desktop-swift-ci.yml, line 25:
<comment>For push events to `main`, the diff base is hard-coded to `HEAD~1` in both the path gate and the e2e coverage check. On a multi-commit push, this only looks at the last commit, so desktop changes in earlier pushed commits can be missed and the Swift build, tests, and e2e coverage validation will be skipped.
Use `github.event.before` as the diff base for push events so the full pushed range is evaluated.</comment>
<file context>
@@ -0,0 +1,68 @@
+ if [ "${{ github.event_name }}" = "pull_request" ]; then
+ DIFF_BASE="origin/${{ github.base_ref }}"
+ else
+ DIFF_BASE="HEAD~1"
+ fi
+ FILES=$(git diff --name-only --diff-filter=ACMRD "$DIFF_BASE"...HEAD)
</file context>


Summary
desktop-core-harness.sh,CORE_E2E.md, flow lint, T0 CI self-check) so agents can validate real app behavior via the automation bridge — not just compile/unit tests.PROVIDER_MODE=offline, RustOMI_LLM_STUB, capture/chat/memories/tasks bridge seams, and falsifiable YAML flow assertions (12 flows + spatial overlay verified green locally).bless-release.shruns T2 against a rebuild tag, writesblessedmetadata to GitHub release KEY_VALUE blocks; prod promotion (desktop_promote_prod) is gated unless--override-unblessed+ confirmation string; admin Releases page shows blessing status.Test plan
./scripts/desktop-core-harness.sh --self-check --skip-backend-contracts(T0)PROVIDER_MODE=offline make dev-up+make desktop-run-local DESKTOP_APP_NAME=omi-core-e2e DESKTOP_USER=alice./scripts/desktop-core-harness.sh --tier 2 --bundle omi-core-e2e --port <automation-port> --keep-stack→passed: true,provider_mode: offlinepython3 scripts/release-keyvalue.py check-manifestaccepts good manifest, rejects missingprovider_mode: offlinepython3 .github/scripts/test_check_desktop_release_promotion.py(bless gate unit tests)Notes
dev-upnow restarts Typesense when the container dies but manifest PID lingers.override_unblessed=trueandoverride_confirm=I-ACCEPT-UNBLESSED-PROD-RISK.Made with Cursor