Skip to content

feat(macos): W5 player-build launch handoff (#1322)#1430

Merged
100yenadmin merged 1 commit into
mainfrom
feat/w5a-launch-handoff
Jul 8, 2026
Merged

feat(macos): W5 player-build launch handoff (#1322)#1430
100yenadmin merged 1 commit into
mainfrom
feat/w5a-launch-handoff

Conversation

@100yenadmin

Copy link
Copy Markdown
Member

What

Additive Mac-side launch path that hands the active campaign off to the standalone Unity player build, mirroring AppProcessService's existing env-dict handoff idiom. Formalizes docs/roadmap/RENDER-DELIVERY-DECISION.md: the player is launched beside the app; renderer = pure consumer; input = the existing POST /move kinds only. Part 2 of W5a (issue #1322).

The GEX44 box build lane delivers WorldOSPlayer.app in parallel — this PR is built against the documented contract below and a faked .app (injected fileExists) so it lands independent of that artifact.

THE CONTRACT (so the build lane aligns)

The app hands the campaign off via environment variables set on the launched player process (the same mechanism AppProcessService.launchManagedProcess already uses to hand campaign id + roots to the viewer/provider). The player reads these at startup (System.Environment.GetEnvironmentVariable(...)):

Env var Meaning
WORLDOS_ENGINE_BASE_URL engine/viewer origin the player consumes — /combat-surface, /events, POST /move (e.g. http://127.0.0.1:8765)
WORLDOS_CAMPAIGN_ID active campaign to render; omitted when there is no active campaign

Absent → the player shows its own idle / no-campaign state. It NEVER writes engine state and defines NO new endpoint.

Player .app location: new additive playerAppPath preference. Blank → default ~/Applications/WorldOSPlayer.app, then /Applications/WorldOSPlayer.app. None present → .notConfigured no-op (today's behavior byte-identical).

Launch mechanism: NSWorkspace.openApplication(at:configuration:) with configuration.environment carrying the contract and createsNewApplicationInstance = falseidempotent relaunch (an already-running player is activated, not duplicated). The player is an independent LaunchServices process; no termination handler is wired to the viewer/provider, so player exit never touches the engine.

Chose env vars over a plist/config-file beside the .app: mirrors the existing bridge idiom, writes nothing to disk (no stale-config hazard / no cleanup / no reconcile-on-relaunch), and keeps the pure-consumer invariant crisp.

Invariants

  • Zero new engine endpoints; zero new writers (renderer stays a pure consumer).
  • Additive + defaulted: no player installed ⇒ identical to main today.

Tests

  • PlayerLauncherTests (XCTest, hermetic — mocks .app presence + captures the launch invocation/env payload): contract env payload, default-path fallback, configured-over-default, campaign omission, byte-identity .notConfigured no-op, single-invocation seam, idempotent relaunch.
  • swift build warning-clean; qa/fast_gate.sh = 257 passed (engine byte-identical — zero Python touched).
  • XCTest executes in the macOS Swift CI job (this dev host is CommandLineTools-only, no XCTest — documented in the workflow).

Merge

Auto-merge armed; not merging here. Admin-merge per #1389 once macOS Swift CI (build + XCTest) is green.

Closes part 2 of #1322.

Additive Mac-side launch path that hands the active campaign off to the
standalone Unity player build, mirroring AppProcessService's env-dict
handoff idiom (RENDER-DELIVERY-DECISION.md: player launched BESIDE the
app; renderer = pure consumer; input = existing POST /move kinds only).

CONTRACT (env vars set on the launched player process):
  WORLDOS_ENGINE_BASE_URL  engine/viewer origin the player consumes
  WORLDOS_CAMPAIGN_ID      active campaign to render (omitted when none)
Player .app located via the new 'playerAppPath' preference; blank falls
back to ~/Applications then /Applications/WorldOSPlayer.app. No player
installed -> .notConfigured no-op (today's behavior byte-identical).

Launched via NSWorkspace.openApplication with createsNewApplicationInstance
= false (idempotent relaunch) as an INDEPENDENT process, so player exit
never touches the engine (no termination handler wired to viewer/provider).

Zero new engine endpoints, zero new writers. XCTest run in CI (host lacks
XCTest); swift build + qa/fast_gate.sh (257 passed) green locally.
@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.

@100yenadmin 100yenadmin enabled auto-merge (squash) July 8, 2026 16:27
@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: 26 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: f658dede-8741-42e5-98dd-6b6bcda7eb1d

📥 Commits

Reviewing files that changed from the base of the PR and between 741ef60 and 2117ac2.

📒 Files selected for processing (5)
  • macos/WorldOSApp/Sources/WorldOSApp/Services/AppProcessService.swift
  • macos/WorldOSApp/Sources/WorldOSApp/Services/PlayerLauncher.swift
  • macos/WorldOSApp/Sources/WorldOSApp/Views/PlayView.swift
  • macos/WorldOSApp/Sources/WorldOSApp/Views/SettingsView.swift
  • macos/WorldOSApp/Tests/WorldOSAppTests/PlayerLauncherTests.swift
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/w5a-launch-handoff

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

@evaos-code-review-bot

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

Copy link
Copy Markdown

evaOS review status: closed or merged before review

PR: #1430 - feat(macos): W5 player-build launch handoff (#1322)
Head: 2117ac2adf388379c7a03e4fb023b5d9fff5381f
Updated: 2026-07-08T16:31:14.888Z

evaOS review stopped because the PR closed or merged before this queued head could be reviewed.

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: #1430

Details: state=closed

@100yenadmin 100yenadmin merged commit e601561 into main Jul 8, 2026
32 of 34 checks passed
@100yenadmin 100yenadmin deleted the feat/w5a-launch-handoff branch July 8, 2026 16:33
100yenadmin added a commit that referenced this pull request Jul 8, 2026
…guards, config env vars) (#1432)

Adds BuildMacOSPlayer.cs (Tools/WorldOS/Build/macOS Player (Universal) MenuItem) that builds a
Universal (x64+AppleSilicon) macOS standalone player from the current-best combat scene
(M1CombatV1_canonical.unity), sets company/product/bundle id to worldos/WorldOSPlayer, and
registers the scene in EditorBuildSettings (was empty).

Guards 4 legacy editor-only scripts (AnimFrameCapture, IntegrationBuilder, TavernTier1Builder,
SetupPainterlyScene) that referenced UnityEditor/[MenuItem] outside Assets/Editor/ with
#if UNITY_EDITOR, so the player build compiles clean (0 errors).

CombatSurfaceClient now resolves WORLDOS_ENGINE_BASE_URL / WORLDOS_CAMPAIGN_ID from the process
environment before falling back to today's hardcoded defaults (byte-identical when absent), per
the app-host launch contract (NSWorkspace configuration.environment, PR #1430).

Build evidence (report, bundle listing, log tail, structural findings) in
qa/evidence/1322-build/. Built + verified on the GEX44 box; .app delivered to the Mac out of
band (not committed, binary).

Co-authored-by: Eva <arncalso@gmail.com>
100yenadmin added a commit that referenced this pull request Jul 8, 2026
…urfaceClient live (#1435)

- Commit WorldOS/OccluderDepth as a build-included .shader asset (was runtime ShaderUtil
  CreateShaderAsset -> stripped from the player build -> magenta occluder cubes). paint_combat_v1
  prefers the committed shader (runtime fallback keeps the editor capture flow byte-identical).
- CombatSurfaceClient: resolve actors by Actor_<token.id> registry naming (was stale HeroFighter/
  MonsterGoblin lookups); cell<->world mirrors paint_combat_v1 (grid from surface, cell 2.0, row-flip);
  click -> POST /move with the existing kinds only (move_to_cell / on-turn attack), payloads byte-exact
  to qa/drive_gfx_combat.py. Attached to M1CombatV1_canonical via the W5bWireScene migration MenuItem.
- Evidence in qa/evidence/1433/ (build report + shader-inclusion log + before-magenta smoke frame).

Refs #1322, PR #1430, PR #1432.

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