Skip to content

#1436 W5c U2: native-window T3 playtester palette + runner#1440

Merged
100yenadmin merged 1 commit into
mainfrom
feat/t3-player-harness
Jul 8, 2026
Merged

#1436 W5c U2: native-window T3 playtester palette + runner#1440
100yenadmin merged 1 commit into
mainfrom
feat/t3-player-harness

Conversation

@100yenadmin

Copy link
Copy Markdown
Member

What

Unit 2 of #1436 (W5c) / the T3 gate of #1322: extend the blind-playtester harness to drive the native macOS WorldOSPlayer window (not the browser), ready for the T3 quest-loop gate. Same 9-tool player contract as the browser palette, backed by macOS primitives instead of Playwright, scored by the same qa/ui_playtest_score.py.

Files

  • qa/native_palette/native_input.swift — Swift helper: CGWindowList window-find + CGEvent synthetic click/key/type + a permission probe. CGPreflightScreenCaptureAccess returns a false positive on macOS 14+/15+ (observed on the dev box), so the Screen-Recording gate is empirical (normal-window visibility). Runs interpreted (swift …) and compiles (swiftc …).
  • qa/native_palette/native_palette_server.js — the native palette MCP server. screenshot = screencapture -l <windowid>; click(x,y) maps window-relative screenshot pixels → global points (Retina scale-aware); a11y_tree = a pixels-only stub (the T3 persona plays from screenshots); type/key/wait/report_bug/give_up/finish mirror the browser palette byte-for-byte in artifact shape. Shares the qa/playwright MCP SDK (no second npm install). --selfcheck asserts the 9-tool contract GUI-free.
  • qa/ui_playtest_player.sh — the runner: seed_gfx_combat.pyviewer/server.py (/combat-surface + move sink + chat) → WorldOSPlayer.app launched with the WORLDOS_ENGINE_BASE_URL/WORLDOS_CAMPAIGN_ID launch-contract → the unchanged run_duo DM loop → native-palette player agent → teardown → score. FAILS LOUD with the exact System Settings pane on a missing permission (never a silent skip). --preflight validates app + deps + swift helper + both permissions without a run.
  • qa/native_palette/play_player_native_t3.txt — the pixels-only quest-loop persona brief.
  • qa/test_native_palette.py — contract + runner-permission-discipline + swift-compile + server-boot + scorer-compatibility self-checks. GUI-free; swift/node subtests skip where the toolchain is absent, so CI/fast-gate stay green.
  • qa/UI_PLAYTEST.md — a section documenting the native variant + the permission requirements.

macOS permissions (owner action)

The palette needs Screen Recording (System Settings ▸ Privacy & Security ▸ Screen Recording — for the capture AND for CGWindowList to enumerate the window) and Accessibility (▸ Accessibility — for synthetic input). Missing → the run aborts naming the pane. qa/ui_playtest_player.sh --preflight checks both.

Tests

  • python3 -m pytest -q qa/test_native_palette.py11 passed (incl. swift compile + node MCP selfcheck + scorer-compat on this Mac).
  • qa/fast_gate.sh257 passed (green; additive, no engine touch).
  • qa/ui_playtest_player.sh --preflight → ALL GREEN on the dev box.
  • End-to-end MCP stdio drive (report_bug + type-submit + finish) → qa/ui_playtest_score.py scored the native output unchanged (in_story_turns=1, satisfaction=7 self-reported).

Constraints honored

One live GUI harness at a time; never touches Eva; engine stays the sole writer; DM side unchanged (same run_duo loop). Does NOT run the actual T3 gate — that is the orchestrator's session (a real playtest that may need a permission grant).

Refs #1436 #1322. PRs #1430/#1432/#1435/#1438.

Admin-merge note (#1389): auto-merge armed, no manual merge; admin-merge is acceptable per the relaxed policy once CI is green.

Extend the blind-playtester harness to drive the native macOS WorldOSPlayer
window for the T3 gate (#1322), exposing the SAME 9-tool contract as the
browser palette but backed by macOS primitives instead of Playwright.

- qa/native_palette/native_input.swift: CGWindowList window-find + CGEvent
  synthetic click/key/type + an empirical Screen-Recording/Accessibility perm
  probe (CGPreflight lies on macOS 14+/15+; we gate on normal-window visibility).
- qa/native_palette/native_palette_server.js: MCP server; screenshot=screencapture
  -l <windowid>, click(x,y) window-pixels->global-points (Retina scale-aware),
  a11y_tree=pixels-only stub, report_bug/give_up/finish identical shape so
  qa/ui_playtest_score.py scores a native run UNCHANGED. Shares the qa/playwright
  MCP SDK (no second install). --selfcheck asserts the 9-tool contract GUI-free.
- qa/ui_playtest_player.sh: seed_gfx_combat -> viewer -> WorldOSPlayer.app (env
  launch-contract) -> unchanged run_duo DM loop -> native palette player -> score.
  FAILS LOUD naming the exact System Settings pane on a missing permission;
  --preflight validates app+deps+swift+perms without a run.
- qa/native_palette/play_player_native_t3.txt: the pixels-only quest-loop persona.
- qa/test_native_palette.py: contract + runner-permission + swift-compile +
  server-boot + scorer-compat self-checks (GUI-free; CI/fast-gate green).

Does NOT run the actual T3 gate (orchestrator's session; may need a perm grant).
Refs #1436 #1322.
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 2 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5b129b19-5f18-41bc-97d8-ac0106f3f70e

📥 Commits

Reviewing files that changed from the base of the PR and between 0dde586 and 1ff104d.

📒 Files selected for processing (6)
  • qa/UI_PLAYTEST.md
  • qa/native_palette/native_input.swift
  • qa/native_palette/native_palette_server.js
  • qa/native_palette/play_player_native_t3.txt
  • qa/test_native_palette.py
  • qa/ui_playtest_player.sh
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/t3-player-harness

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

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@evaos-code-review-bot

evaos-code-review-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

evaOS review status: completed

PR: #1440 - #1436 W5c U2: native-window T3 playtester palette + runner
Head: 1ff104d845284c9546ffd458687e4595942947f7
Updated: 2026-07-08T17:53:03.285Z

evaOS review completed for this PR head.

Automation note: agents should wait for this comment to reach completed, stale_head, closed_or_merged_before_review, skipped, or failed before treating evaOS review as settled for this head. provider_deferred means evaOS still intends to retry.

PR URL: #1440

Review URL: #1440 (review)

// there). Resolve normally first (in case this dir grows its own node_modules), then fall back to the
// playwright workspace — no second npm install required.
function requireShared(mod) {
const parts = mod.split("/");
// Used by qa/test_native_palette.py to prove the server boots and registers the 9-tool contract
// without a live player window or a TCC grant.
if (SELFCHECK) {
const registered = Object.keys((server && server._registeredTools) || {});

@evaos-code-review-bot evaos-code-review-bot 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.

Walkthrough

PR: #1440 - #1436 W5c U2: native-window T3 playtester palette + runner
Head: 1ff104d845284c9546ffd458687e4595942947f7 into main. Review event: REQUEST_CHANGES.
Provider: GLM/Z.ai through ZCode (zcode-glm, zcode, model GLM-5.2).

Estimated review effort: 5/5 (~62 min)

Changed Files

File Status Churn Purpose Risk
qa/UI_PLAYTEST.md modified +28/-0 Documentation Low
qa/native_palette/native_input.swift added +188/-0 Changed file Low
qa/native_palette/native_palette_server.js added +494/-0 Changed file Elevated: validated P1 finding
qa/native_palette/play_player_native_t3.txt added +66/-0 Changed file Low
qa/test_native_palette.py added +178/-0 Test coverage Moderate: validated P2 finding
qa/ui_playtest_player.sh added +332/-0 Changed file Elevated: large change

Review Signal

Validated inline findings: 2 (P0: 0, P1: 1, P2: 1, P3: 0).
Dropped findings before posting: 0. High-severity findings: 1.

Risk Taxonomy

  • API compatibility: 1
  • Release regression: 1

Validation and Proof

2 required validation/proof recommendation(s) selected from changed files.

  • required: Unity editor or Play Mode smoke - WorldOS repo profile implies Unity runtime risk. Proof: Unity editor smoke; Play Mode log; scene/prefab screenshot or recording.
  • required: TypeScript/web build or CI proof - Runtime TypeScript/web files or package/config files changed. Proof: npm run build; typecheck; focused Vitest; green GitHub check.
    Proof status: missing - 2 required validation/proof recommendation(s) missing from PR metadata.
    Profile validation hints: Prefer correctness, persistence, CI, release, and regression findings over style-only feedback.
    Profile proof expectations: Look for Unity editor, play-mode, fixture, or focused smoke evidence when runtime behavior changes.

Related Context

Related issues/PRs: #1436, #1322, #1430, #1432, #1435, #1438, #1389.
Suggested labels: bug, docs, tests.
Suggested reviewers: none from current metadata.

Review Settings Preview

  • Profile: assertive
  • Enabled sections: Review summary (inline_review); Walkthrough (inline_review); Changed-files table (walkthrough); Effort estimate (walkthrough); Related issues/PRs (walkthrough); Suggested labels (suggestion_only); Review status comment (sticky_status)
  • Path instructions: Assets/** - Prioritize scene, prefab, save-state, asset-reference, and gameplay regressions.
  • Path instructions: ProjectSettings/** - Treat build, platform, input, graphics, and release behavior changes as high risk.
  • Label suggestions: unity, gameplay, regression-hardening
  • Reviewer suggestions: none
  • Suggestion behavior: suggestions only; labels and reviewers are not auto-applied.
  • Roadmap-only settings: auto-apply labels; auto-request reviewers; required status checks

Pre-merge checklist

  • Inline comments target current RIGHT-side diff lines.
  • No secret-like content survived into posted inline comments.
  • REQUEST_CHANGES is only used when eligible P0/P1 findings survive validation.
  • Required behavior proof is present or not applicable.
  • Labels and reviewers are suggestions only; the bot did not auto-apply them.

// require.resolve({paths}) honors the package's "exports" map (the SDK maps
// "server/mcp.js" -> dist/cjs/...), which a raw path.join would bypass.
for (const root of roots) {
try { return require(require.resolve(mod, { paths: [root] })); } catch (_e2) {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Native runs always score passed=False (scorer requires 'table' screen, palette stamps 'player')

SCREEN_LABEL defaults to "player" (line 74) and is stamped on every action via logAction(). The shared scorer qa/ui_playtest_score.py computes reached_table = "table" in screens and passed = completed_intro_flow and critical==0 and console_errors==0 and satisfaction>=6, where completed_intro_flow requires reached_table. Because no native action ever carries screen=="table", reached_table is always False, so completed_intro_flow and passed are ALWAYS False for any native run — even one where the player fully completed the quest loop and finished with satisfaction 10. The T3 gate's entire purpose is proving a quest loop completes in the native window, but the gate verdict is structurally unreachable here. The code comments honestly justify the "player" label vs the SPA's "table", but never reconcile that this disables the pass gate. Fix one of: (a) have the native palette stamp screen="table" once the player is visibly in the rendered game view (e.g. after a successful in-game action), (b) add a native-aware pass branch to the scorer keyed on surface=="native-player" + in_story_turns>=1 + finish, or (c) translate screen labels in the scorer when meta.surface=="native-player". The added test test_score_reads_native_palette_artifacts deliberately does NOT assert passed/completed_intro_flow, masking this — add an assertion that a completing native run yields passed=True.

Category: Release regression

Why this matters: A release-readiness gate that can never return True for the exact surface it was built for makes every T3 run report failure regardless of evidence, undermining the proof the gate exists to produce and risking false release-blocking or silent acceptance of a broken metric.

Comment thread qa/test_native_palette.py
self.assertEqual(score["satisfaction_source"], "self-reported")
self.assertEqual(score["bug_reports_minor"], 1)
self.assertEqual(score["console_errors"], 0)
self.assertEqual(score["network_failures"], 0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Scorer compat test omits the pass/completed_intro_flow assertions that are the load-bearing contract

test_score_reads_native_palette_artifacts asserts persona, in_story_turns, persona_satisfaction, bug_reports_minor, console_errors, network_failures, and summary.md — but never asserts score['passed'] or score['completed_intro_flow']. Those are the fields the release gate keys on, and (per the screen-label mismatch) they will be False for this exact fixture. Without asserting them, the test proves the scorer PARSES native artifacts but not that it can ever PASS a native run. Add self.assertTrue(score['passed']) and self.assertTrue(score['completed_intro_flow']) (after fixing the screen label), or explicitly document why native runs are expected to be passed=False and how the T3 gate then exits.

Category: API compatibility

Why this matters: A compatibility test that skips the gating fields gives false confidence that the shared scorer works end-to-end for the native surface, hiding the release-regression above until a live run.

@100yenadmin 100yenadmin merged commit 26aa07f into main Jul 8, 2026
36 of 37 checks passed
@100yenadmin 100yenadmin deleted the feat/t3-player-harness branch July 8, 2026 18:24
100yenadmin added a commit that referenced this pull request Jul 8, 2026
…lision-validated clicks (#1446)

* #1441 W5d: grounded reposition + engine-path glide + walk clips + click pre-validation

- PlaceActor -> UpdateActor: reposition now re-grounds feet->FloorY via the SAME BakeMesh
  Measure as SpawnActor (float fix; old path preserved raw Y). First-sight snap, changed-cell
  glide, same-cell no-op (poll never interrupts an in-flight glide).
- GlideTo: cell->cell tween at GlideSpeed following the engine-confirmed lastPath polyline
  (straight-line fallback when absent); rings/AO follow every frame; walk clip during glide
  (own walk/run -> humanoid donor-walk retarget -> glide-only fallback), idle at rest.
- Click pre-validation: reject clicks on impassable/occupied cells with a red ring flash
  (UX pre-filter only; engine stays authoritative — it already rejects illegal cells).
- Reconcile actor heights to named consts (ActorHeightFoe 4.2 / ActorHeightChar 3.2, the
  paint_combat_v1 #1418-calibrated source); flag replay_v1's stale 5.0.
- Parse impassable+lastPath via the runtime Json parser (poll + move-response paths).

Refs #1441 #1436 #1322 (PRs #1438/#1440).

* #1441 W5d: QA evidence — compile-clean, editor sanity capture, build+scp notes

Box validation: 0-error compile (component compiled + attached), editor sanity screenshot
(grounded actors + rings, painterly camp scene), Universal build Succeeded (0 errors), zip
scp'd to ~/worldos-session-notes/w5a-build/WorldOSPlayer.app.zip (overwrite).

---------

Co-authored-by: Eva <arncalso@gmail.com>
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