Skip to content

Add 100/100 hybrid app handoff gate - #504

Merged
100yenadmin merged 2 commits into
mainfrom
codex/worldos-handoff-gate-100
Jun 1, 2026
Merged

Add 100/100 hybrid app handoff gate#504
100yenadmin merged 2 commits into
mainfrom
codex/worldos-handoff-gate-100

Conversation

@100yenadmin

@100yenadmin 100yenadmin commented Jun 1, 2026

Copy link
Copy Markdown
Member

Summary

  • add qa/app_handoff_gate.py, a single hybrid gate that scores web scripted smoke, built dist/WorldOS.app scripted smoke, and a short built-app Codex-provider playtest on the same clean SHA
  • normalize evidence manifest.json with review_entrypoint pointers for command, repo/commit, provider/gate kind, screenshots, app-status/session-surface snapshots, moves, traces, console/network/action logs, art status, and failure bucket
  • add a dependency-light same-port hook probe for launcher/table/settings/modal/toast/chrome driving hooks
  • harden evidence recovery so run-dir app-status/session-surface snapshots can recover a dead live exporter URL without creating false evidence gaps
  • document that handoff_score: 100 is an implementation-velocity handoff signal, not a release verdict; full non-partial RRI remains separate

Local proof

Evidence root: /Volumes/LEXAR/Codex/worldos-agent-grade-app-testability/handoff-local-20260601T063508Z-c3d37ae/

handoff.json result:

  • status: passed
  • handoff_score: 100
  • commit_sha: c3d37ae
  • dirty: false
  • release_verdict: false

Gate results on the same SHA:

  • web scripted smoke: passed, 5 beats, no evidence gaps
  • built-app scripted smoke: passed, 5 beats, private art present, active actor/actions, accepted moves, narration advanced, hook probe passed, no evidence gaps
  • built-app Codex playtest: passed, 1 move, provider trace present, provider trace failed_or_error_count: 0, no evidence gaps

Evidence manifests:

  • web scripted: manifest ok, 0 gaps, screenshots/app-status/session-surface/moves/provider trace/console/network/action logs present
  • built-app scripted: manifest ok, 0 gaps, screenshots/app-status/session-surface/moves/provider trace/console/network/action logs present
  • built-app Codex: manifest ok, 0 gaps, screenshots/app-status/session-surface/moves/provider trace/console/network/action logs present

Tests

  • bash -n qa/ui_playtest_app.sh scripts/play_scripted_dm.sh
  • python3 -m py_compile qa/app_handoff_gate.py qa/app_smoke_scripted.py qa/export_app_evidence.py qa/app_failure_buckets.py
  • node --check qa/app_handoff_hooks.js
  • python3 -m pytest qa/test_app_handoff_gate.py qa/test_export_app_evidence.py -q -> 13 passed
  • python3 -m pytest qa/test_app_failure_buckets.py qa/test_app_smoke_scripted.py qa/test_export_app_evidence.py qa/test_release_readiness.py qa/test_app_handoff_gate.py qa/test_macos_app_static.py qa/test_ui_playtest_app_buckets.py viewer/tests/test_openworlds_static.py -q -> 88 passed, 6 subtests passed

Follow-ups

This does not run or replace the full five-persona RRI.

Summary by CodeRabbit

  • New Features

    • Automated hybrid handoff run that captures richer playtest evidence and a normalized review entrypoint
    • Optional native app provider selection for playtests
  • Documentation

    • Expanded handoff/gate docs with a new “100/100 Handoff Gate” section clarifying required sub-checks and scoring intent
  • Tests

    • Added/extended tests for handoff gates, timeouts, evidence indexing, provider traces, and hook probe reporting
  • Chores

    • Improved QA infrastructure for deterministic handoffs and clearer readiness signals

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a hybrid QA handoff gate: a Python orchestrator runs deterministic web/native smokes and a short Codex playtest on the same clean SHA, captures indexed evidence via an enhanced exporter, validates hooks with a Node probe, and writes handoff.json plus enriched bundle manifests (review_entrypoint, handoff_gate).

Changes

Handoff Gate Implementation & Tests

Layer / File(s) Summary
Handoff Gate Contract & Documentation
docs/AGENT_GRADE_APP_TESTABILITY.md, qa/SCORECARD.md
review_entrypoint manifest contract and the "100/100 Handoff Gate" checklist documented; SCORECARD notes hybrid gate semantics (not a release verdict).
Hook Probe Validation Script
qa/app_handoff_hooks.js
New Node CLI that fetches /app-status and openworlds/*.jsx, counts required data-worldos-testid/testId occurrences and source markers, and emits a structured JSON hook-probe report.
Provider Selection for Native Playtest
qa/ui_playtest_app.sh, qa/test_macos_app_static.py
Adds WOS_APP_SELECTED_PROVIDER env var handling (allowlist: `codex
Evidence Export Enhancement with Review Metadata
qa/export_app_evidence.py
Adds repo snapshot helpers, expands RUN_DIR_PATTERNS, builds evidence index, supports snapshot fallback recovery, and constructs handoff_gate and review_entrypoint objects; extends CLI for gate/provider/command/verdict/timestamps.
Orchestrator bootstrap & helpers
qa/app_handoff_gate.py
Bootstraps repo root, timestamp/JSON helpers, git introspection, run/log helpers, and failure normalization.
App-status validation & summarization
qa/app_handoff_gate.py
Implements validate_app_status, provider_trace_summary, summarize_hook_probe, and evidence_gap_count.
GateResult & finalize_handoff
qa/app_handoff_gate.py
Defines GateResult dataclass and finalize_handoff to compute overall handoff verdict, score, and blocking reasons.
Native run copy / cleanup & evidence export
qa/app_handoff_gate.py, qa/export_app_evidence.py
Copy native run artifacts, cleanup processes, invoke exporter, persist failure manifests, run hook probe, and store probe output.
Drive moves & gate runners
qa/app_handoff_gate.py
Drive /move beats, capture per-beat evidence, enforce Codex trace checks, and implement run_web_scripted and run_native_provider_gate (scripted/Codex).
CLI and main
qa/app_handoff_gate.py
Argument parsing, run selection, output directories, final handoff.json writing, and exit code policy.
Tests for Handoff Gate & Export
qa/test_app_handoff_gate.py, qa/test_export_app_evidence.py
Unit tests for repo SHA, run_logged timeout, finalize_handoff scoring, validate_app_status, provider trace parsing, hook-probe summarization, and expanded exporter manifest assertions including review_entrypoint and handoff_gate.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as Handoff Gate CLI
  participant WebGate as Web Scripted Gate
  participant NativeGate as Native Provider Gate
  participant Hooks as app_handoff_hooks.js
  participant Exporter as export_app_evidence.py
  participant Finalize as finalize_handoff

  CLI->>WebGate: run_web_scripted(args)
  WebGate->>Exporter: export evidence (manifest + files)
  CLI->>NativeGate: run_native_provider_gate(provider)
  NativeGate->>Hooks: run_hook_probe(base_url)
  Hooks-->>NativeGate: hook report (ok/missing, details)
  NativeGate->>Exporter: export evidence (play-state, manifest)
  CLI->>Finalize: finalize_handoff(run_id, gates, expected_sha)
  Finalize-->>CLI: write handoff.json (status, handoff_score, blockers)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

"I hopped through logs and code today,
Collected snaps and traces on my way,
A tiny gate with tidy paws,
Bundled proof and careful laws,
Wired handoff: ready—hip hooray!" 🐰✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description is comprehensive and covers the main changes, but is missing the required CLA checkbox and validation sections from the template. Add the Licensing/CLA checklist and Validation section to match the repository's required description template structure.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add 100/100 hybrid app handoff gate' clearly and specifically summarizes the main change: adding a hybrid handoff gate that scores at 100/100.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
qa/test_export_app_evidence.py (1)

151-157: 💤 Low value

Consider defensive assertions for review_entrypoint file categories.

The assertIn checks assume review["files"][category] keys exist. If the exporter omits a category, the test raises KeyError instead of a clear assertion failure. Adding explicit existence checks would improve test readability on failure.

♻️ Optional defensive assertion pattern
 review = payload["review_entrypoint"]
 self.assertEqual(review["schema"], "worldos.app-evidence-review-entrypoint.v1")
 self.assertEqual(review["failure_bucket"], "")
+self.assertIn("screenshots", review["files"])
+self.assertIn("app_status_snapshots", review["files"])
+self.assertIn("session_surface_snapshots", review["files"])
+self.assertIn("moves", review["files"])
+self.assertIn("provider_trace", review["files"])
+self.assertIn("network_logs", review["files"])
+self.assertIn("action_logs", review["files"])
 self.assertIn("run-dir/screenshots/beat-001.png", review["files"]["screenshots"])
 self.assertIn("app-status.json", review["files"]["app_status_snapshots"])
 self.assertIn("session-surface.json", review["files"]["session_surface_snapshots"])
 self.assertIn("local-files/moves.jsonl", review["files"]["moves"])
 self.assertIn("run-dir/scripted-provider/summary.json", review["files"]["provider_trace"])
 self.assertIn("run-dir/network.ndjson", review["files"]["network_logs"])
 self.assertIn("run-dir/actions.ndjson", review["files"]["action_logs"])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@qa/test_export_app_evidence.py` around lines 151 - 157, The test assumes keys
exist under review["files"] which can raise KeyError; update the assertions in
qa/test_export_app_evidence.py to first assert the presence of each category key
(e.g., "screenshots", "app_status_snapshots", "session_surface_snapshots",
"moves", "provider_trace", "network_logs", "action_logs") in review["files"] and
then use the existing assertIn checks to verify specific file entries like
"run-dir/screenshots/beat-001.png" in review["files"]["screenshots"]; this makes
failures show clear assertion errors instead of KeyError and points directly to
the missing category or file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@qa/app_handoff_gate.py`:
- Around line 79-87: The run_logged function can raise subprocess.TimeoutExpired
and currently propagates, so wrap the subprocess.run call in a try/except that
catches subprocess.TimeoutExpired (referencing run_logged and the proc variable)
and in the except block write a clear timeout message and the partial output to
the same log file, write a synthetic exit marker like "[timeout]" or "[exit
124]" and return a non-zero code (e.g., 124) instead of re-raising; ensure the
log_path.open context is used for these writes so the log is flushed and the
function returns normally so downstream code (like writing handoff.json) still
runs.
- Around line 389-391: The code currently ignores the result of
subprocess.run(cmd, cwd=ROOT, ...) and then treats read_json(out /
"manifest.json") as if export succeeded; update the block so you capture the
CompletedProcess (e.g., result = subprocess.run(...)), check result.returncode
and on non‑zero either raise an exception or return an error status (include
result.stdout/stderr in the log/error), and only call read_json if the process
succeeded and the manifest file exists; also validate that read_json returns a
valid dict (raise or propagate an error if parsing fails) so the gate does not
silently pass when the exporter failed (refer to variables cmd, ROOT, out, and
function read_json).
- Around line 552-575: The exported manifest currently uses the initial
smoke_json verdict before later checks; move the export_evidence call (and
assignment to gate.evidence_manifest) so it occurs after validate_app_status and
evidence_gap_count (i.e., after the failure checks and after
gate.fail/gate.pass_ decisions), or alternatively re-run export_evidence with
the final verdict right before returning; update references to export_evidence,
gate.evidence_manifest, validate_app_status, evidence_gap_count, gate.fail, and
gate.pass_ to ensure manifest_path/manifest reflect the final gate outcome.

---

Nitpick comments:
In `@qa/test_export_app_evidence.py`:
- Around line 151-157: The test assumes keys exist under review["files"] which
can raise KeyError; update the assertions in qa/test_export_app_evidence.py to
first assert the presence of each category key (e.g., "screenshots",
"app_status_snapshots", "session_surface_snapshots", "moves", "provider_trace",
"network_logs", "action_logs") in review["files"] and then use the existing
assertIn checks to verify specific file entries like
"run-dir/screenshots/beat-001.png" in review["files"]["screenshots"]; this makes
failures show clear assertion errors instead of KeyError and points directly to
the missing category or file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: eb5793c5-d200-49b5-856f-3c91e9d3e7fd

📥 Commits

Reviewing files that changed from the base of the PR and between ec96832 and abf4f60.

📒 Files selected for processing (9)
  • docs/AGENT_GRADE_APP_TESTABILITY.md
  • qa/SCORECARD.md
  • qa/app_handoff_gate.py
  • qa/app_handoff_hooks.js
  • qa/export_app_evidence.py
  • qa/test_app_handoff_gate.py
  • qa/test_export_app_evidence.py
  • qa/test_macos_app_static.py
  • qa/ui_playtest_app.sh

Comment thread qa/app_handoff_gate.py
Comment thread qa/app_handoff_gate.py Outdated
Comment thread qa/app_handoff_gate.py
@100yenadmin
100yenadmin force-pushed the codex/worldos-handoff-gate-100 branch from abf4f60 to a4f00aa Compare June 1, 2026 06:24

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
qa/export_app_evidence.py (2)

678-688: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Allow session-surface snapshot fallback after a live fetch error.

When the live /session-surface call fails, this code records sources["session_surface"] = {"ok": False}. The later fallback only runs when the key is absent, so a valid session-surface.final.json already copied from run-dir is ignored and handoff_gate.session_surface_ok stays false.

Suggested fix
-    if "session_surface" not in sources:
+    if not (sources.get("session_surface") or {}).get("ok"):
         _surface, session_surface_snapshot = first_bundle_json(bundle, ("session-surface.final.json", "session-surface*.json"))
         if session_surface_snapshot:
             sources["session_surface_snapshot"] = {"path": session_surface_snapshot, "ok": True}

Also applies to: 707-710

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@qa/export_app_evidence.py` around lines 678 - 688, The error handler that
catches (OSError, urllib.error.URLError, ValueError) should not force
sources["session_surface"]["ok"] = False because later logic only falls back
when the key is absent; instead, in the except block update gaps and set
sources["session_surface"]["url"] = surface_url but do not overwrite an existing
sources["session_surface"]["ok"] value (remove setting ok=False or only set
ok=False if the key did not previously exist). Apply the same change to the
other similar block around lines 707-710 so handoff_gate.session_surface_ok can
become true when a pre-existing session-surface.final.json was copied from
run-dir.

655-666: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear the hard-fail exit code once the bundled app-status snapshot recovers the export.

After the live /app-status fetch fails, exit_code stays 1 even when first_bundle_json() finds a valid app-status.final.json. qa/app_handoff_gate.py Line 406 treats any non-zero exporter exit as fatal, so recovered native bundles still get downgraded to an exporter failure.

Suggested fix
     if not app_status:
         app_status, app_status_snapshot = first_bundle_json(bundle, ("app-status.final.json", "app-status*.json"))
         if app_status_snapshot:
             sources["app_status_snapshot"] = {"path": app_status_snapshot, "ok": True}
+            exit_code = 0

Also applies to: 703-706

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@qa/export_app_evidence.py` around lines 655 - 666, When a live /app-status
fetch fails we currently set exit_code = 1 and never clear it even if
first_bundle_json() later finds a valid bundled app-status.final.json; update
the recovery path (the code that calls first_bundle_json() and sets
sources["app_status"]) to reset exit_code back to 0 when a bundled snapshot is
accepted (i.e., when you set sources["app_status"]["ok"] = True or otherwise
mark the source recovered). Specifically, after the code path that records the
bundled app-status (the place that uses first_bundle_json() to populate
sources["app_status"]), explicitly set exit_code = 0 so recovered bundles do not
leave a hard-fail exit; make the same change for the other identical block that
sets sources["app_status"] in the second location noted.
♻️ Duplicate comments (1)
qa/app_handoff_gate.py (1)

396-430: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Persist the synthesized failure manifest before returning.

On timeout, non-zero exit, or missing/invalid manifest.json, this returns an in-memory failure payload but does not write it to out/manifest.json. gate.evidence_manifest then points at a file that may not exist, and the later native check at Line 741 can reread {} and miss the exporter failure.

Suggested fix
     manifest_path = out / "manifest.json"
+
+    def persist_failure(reason: str) -> tuple[str, dict[str, Any]]:
+        payload = {
+            "schema": "worldos.app-evidence.v1",
+            "evidence_gaps": [{"source": "export_app_evidence", "kind": "manifest", "path": str(manifest_path), "reason": reason}],
+            "failure": {"failure_bucket": "no_provider", "failure_detail": reason},
+            "handoff_gate": {"ok": False, "blocking_reasons": [reason]},
+        }
+        json_dump(manifest_path, payload)
+        return str(manifest_path), payload
+
     try:
         proc = subprocess.run(cmd, cwd=ROOT, text=True, capture_output=True, check=False, timeout=60)
     except subprocess.TimeoutExpired as exc:
-        reason = f"export_app_evidence timed out after {exc.timeout}s"
-        return str(manifest_path), {
-            "schema": "worldos.app-evidence.v1",
-            "evidence_gaps": [{"source": "export_app_evidence", "kind": "manifest", "path": str(manifest_path), "reason": reason}],
-            "failure": {"failure_bucket": "no_provider", "failure_detail": reason},
-            "handoff_gate": {"ok": False, "blocking_reasons": [reason]},
-        }
+        return persist_failure(f"export_app_evidence timed out after {exc.timeout}s")
     if proc.returncode != 0:
-        reason = f"export_app_evidence exited {proc.returncode}: {(proc.stderr or proc.stdout)[-1000:]}"
-        return str(manifest_path), {
-            "schema": "worldos.app-evidence.v1",
-            "evidence_gaps": [{"source": "export_app_evidence", "kind": "manifest", "path": str(manifest_path), "reason": reason}],
-            "failure": {"failure_bucket": "no_provider", "failure_detail": reason},
-            "handoff_gate": {"ok": False, "blocking_reasons": [reason]},
-        }
+        return persist_failure(f"export_app_evidence exited {proc.returncode}: {(proc.stderr or proc.stdout)[-1000:]}")
     if not manifest_path.exists():
-        reason = "export_app_evidence did not write manifest.json"
-        return str(manifest_path), {
-            "schema": "worldos.app-evidence.v1",
-            "evidence_gaps": [{"source": "export_app_evidence", "kind": "manifest", "path": str(manifest_path), "reason": reason}],
-            "failure": {"failure_bucket": "no_provider", "failure_detail": reason},
-            "handoff_gate": {"ok": False, "blocking_reasons": [reason]},
-        }
+        return persist_failure("export_app_evidence did not write manifest.json")
     manifest = read_json(manifest_path)
     if not manifest:
-        reason = "export_app_evidence wrote invalid or empty manifest.json"
-        return str(manifest_path), {
-            "schema": "worldos.app-evidence.v1",
-            "evidence_gaps": [{"source": "export_app_evidence", "kind": "manifest", "path": str(manifest_path), "reason": reason}],
-            "failure": {"failure_bucket": "no_provider", "failure_detail": reason},
-            "handoff_gate": {"ok": False, "blocking_reasons": [reason]},
-        }
+        return persist_failure("export_app_evidence wrote invalid or empty manifest.json")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@qa/app_handoff_gate.py` around lines 396 - 430, When export_app_evidence
fails (the subprocess.run block, the proc.returncode != 0 branch, or when
manifest_path doesn't exist or read_json(manifest_path) returns falsy), persist
the failure payload to the same manifest_path before returning so
gate.evidence_manifest points to a real file; construct the same dict you return
(schema/evidence_gaps/failure/handoff_gate), ensure manifest_path.parent exists,
write the JSON atomically (e.g., write to a temp file then rename) and flush,
then return str(manifest_path) and the dict; update the branches that set reason
(the TimeoutExpired except, the non-zero return code branch, the missing file
branch, and the invalid manifest branch) to perform this write using the
manifest_path and the local reason variable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@qa/app_handoff_gate.py`:
- Around line 556-560: The Codex gate currently only fails when
trace.failed_or_error_count > 0 but provider_trace_summary can report
failed_or_error_count==0 while the trace is missing; update the check after
calling provider_trace_summary (the trace variable returned by
provider_trace_summary) to also fail when the trace is absent/missing by testing
its existence flag (e.g. trace.get("trace_exists") or equivalent key returned by
provider_trace_summary) in the conditional that returns the Codex failure (the
branch that currently checks provider == "codex" and failed_or_error_count);
modify that conditional to fail if the trace is missing OR failed_or_error_count
> 0 and keep the same return tuple and payload fields (screenshots,
evidence_gaps, provider_trace).

---

Outside diff comments:
In `@qa/export_app_evidence.py`:
- Around line 678-688: The error handler that catches (OSError,
urllib.error.URLError, ValueError) should not force
sources["session_surface"]["ok"] = False because later logic only falls back
when the key is absent; instead, in the except block update gaps and set
sources["session_surface"]["url"] = surface_url but do not overwrite an existing
sources["session_surface"]["ok"] value (remove setting ok=False or only set
ok=False if the key did not previously exist). Apply the same change to the
other similar block around lines 707-710 so handoff_gate.session_surface_ok can
become true when a pre-existing session-surface.final.json was copied from
run-dir.
- Around line 655-666: When a live /app-status fetch fails we currently set
exit_code = 1 and never clear it even if first_bundle_json() later finds a valid
bundled app-status.final.json; update the recovery path (the code that calls
first_bundle_json() and sets sources["app_status"]) to reset exit_code back to 0
when a bundled snapshot is accepted (i.e., when you set
sources["app_status"]["ok"] = True or otherwise mark the source recovered).
Specifically, after the code path that records the bundled app-status (the place
that uses first_bundle_json() to populate sources["app_status"]), explicitly set
exit_code = 0 so recovered bundles do not leave a hard-fail exit; make the same
change for the other identical block that sets sources["app_status"] in the
second location noted.

---

Duplicate comments:
In `@qa/app_handoff_gate.py`:
- Around line 396-430: When export_app_evidence fails (the subprocess.run block,
the proc.returncode != 0 branch, or when manifest_path doesn't exist or
read_json(manifest_path) returns falsy), persist the failure payload to the same
manifest_path before returning so gate.evidence_manifest points to a real file;
construct the same dict you return (schema/evidence_gaps/failure/handoff_gate),
ensure manifest_path.parent exists, write the JSON atomically (e.g., write to a
temp file then rename) and flush, then return str(manifest_path) and the dict;
update the branches that set reason (the TimeoutExpired except, the non-zero
return code branch, the missing file branch, and the invalid manifest branch) to
perform this write using the manifest_path and the local reason variable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fab6b424-3b59-4f16-b1db-a4c55769e55d

📥 Commits

Reviewing files that changed from the base of the PR and between abf4f60 and a4f00aa.

📒 Files selected for processing (9)
  • docs/AGENT_GRADE_APP_TESTABILITY.md
  • qa/SCORECARD.md
  • qa/app_handoff_gate.py
  • qa/app_handoff_hooks.js
  • qa/export_app_evidence.py
  • qa/test_app_handoff_gate.py
  • qa/test_export_app_evidence.py
  • qa/test_macos_app_static.py
  • qa/ui_playtest_app.sh
✅ Files skipped from review due to trivial changes (1)
  • docs/AGENT_GRADE_APP_TESTABILITY.md
🚧 Files skipped from review as they are similar to previous changes (6)
  • qa/SCORECARD.md
  • qa/ui_playtest_app.sh
  • qa/test_app_handoff_gate.py
  • qa/test_macos_app_static.py
  • qa/app_handoff_hooks.js
  • qa/test_export_app_evidence.py

Comment thread qa/app_handoff_gate.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
qa/app_handoff_gate.py (1)

728-729: 💤 Low value

Avoid redundant manifest re-read.

The manifest variable from line 715 is still in scope and contains the same data that would be read from disk. Re-reading adds I/O overhead and could theoretically get different data if something modified the file.

Suggested simplification
-    if gate.status == "passed" and evidence_gap_count(read_json(Path(gate.evidence_manifest))):
+    if gate.status == "passed" and evidence_gap_count(manifest):
         gate.fail("no_provider", "native evidence manifest has gaps")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@qa/app_handoff_gate.py` around lines 728 - 729, Replace the redundant disk
re-read by using the in-scope manifest variable: when checking if gate.status ==
"passed" and calling evidence_gap_count, pass the already-loaded manifest (the
variable manifest from earlier) instead of re-reading
Path(gate.evidence_manifest); update the conditional to call
evidence_gap_count(manifest) and keep the gate.fail("no_provider", "native
evidence manifest has gaps") behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@qa/app_handoff_gate.py`:
- Around line 728-729: Replace the redundant disk re-read by using the in-scope
manifest variable: when checking if gate.status == "passed" and calling
evidence_gap_count, pass the already-loaded manifest (the variable manifest from
earlier) instead of re-reading Path(gate.evidence_manifest); update the
conditional to call evidence_gap_count(manifest) and keep the
gate.fail("no_provider", "native evidence manifest has gaps") behavior
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9e78462b-8f89-45ed-b4b3-6702b2e2480c

📥 Commits

Reviewing files that changed from the base of the PR and between a4f00aa and c3d37ae.

📒 Files selected for processing (4)
  • qa/app_handoff_gate.py
  • qa/export_app_evidence.py
  • qa/test_app_handoff_gate.py
  • qa/test_export_app_evidence.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • qa/test_export_app_evidence.py
  • qa/export_app_evidence.py

@100yenadmin
100yenadmin merged commit 4a0efe1 into main Jun 1, 2026
9 checks passed
@100yenadmin
100yenadmin deleted the codex/worldos-handoff-gate-100 branch June 1, 2026 08:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant