Skip to content

feat(macos): add beta distribution lane - #150

Closed
100yenadmin wants to merge 8 commits into
mainfrom
macos/beta-distribution-sparkle
Closed

feat(macos): add beta distribution lane#150
100yenadmin wants to merge 8 commits into
mainfrom
macos/beta-distribution-sparkle

Conversation

@100yenadmin

@100yenadmin 100yenadmin commented May 26, 2026

Copy link
Copy Markdown
Member

Summary

Adds the local beta distribution lane for the OpenWorlds-first ClawDnD macOS app.

  • Adds Sparkle 2.9.2 to the SwiftPM macOS app.
  • Adds native UpdaterService plus OpenWorlds bridge requests for updaterStatus, checkForUpdates, and windowCommand.
  • Packages viewer/openworlds/ into ClawDnD.app/Contents/Resources/openworlds and launches the repo-backed Python viewer with CLAWDND_OPENWORLDS_DIR pointing at bundled UI assets.
  • Serves the local Sparkle appcast and ZIP payload through the viewer loopback route, not raw file://, so Sparkle can check local beta updates safely.
  • Converts OpenWorlds traffic lights into bridge-backed native close/minimize/zoom controls and adds an AppKit drag strip over the title region.
  • Adds script/package_macos_beta.sh to build, sign, zip, DMG, appcast, checksums, release notes, and validation output under /Volumes/LEXAR/Codex/clawdnd-beta-channel.
  • Updates the OpenWorlds native app roadmap and surface map with the beta channel, signing/update boundaries, and display/read/write surface ownership.

State Authority

This keeps the app in the supervisor/read-surface lane:

  • The .app can update the Swift shell and bundled OpenWorlds UI assets.
  • The Python viewer, engine, state directories, play-state, QA state, and campaign truth remain repo/state backed.
  • Browser actions still go through existing viewer/player intent lanes; this PR does not move game-state writes into the app.

Local Beta Output

Generated locally by:

./script/package_macos_beta.sh --version 0.3.0 --build 2026052601 --channel local-beta

Expected local channel:

/Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app
/Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD-0.3.0-beta.1.zip
/Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD-0.3.0-beta.1.dmg
/Volumes/LEXAR/Codex/clawdnd-beta-channel/appcast.xml
/Volumes/LEXAR/Codex/clawdnd-beta-channel/RELEASE_NOTES.md
/Volumes/LEXAR/Codex/clawdnd-beta-channel/CHECKSUMS.txt
/Volumes/LEXAR/Codex/clawdnd-beta-channel/validation-report.md

Runtime Sparkle feed in the packaged app:

http://127.0.0.1:<viewer-port>/appcast.xml

The packaged app stores the local channel path in Info.plist; the Swift supervisor passes CLAWDND_BETA_CHANNEL_DIR to the viewer, and the viewer rewrites appcast URLs to the live loopback port.

Adversarial Review Fixes

After CodeRabbit approval, four read-only adversarial slice agents reviewed the PR. Verified findings fixed in commit 3b85fca:

  • P1: update checks could be enabled when no local beta appcast was actually served. Fixed by tracking feedAvailable, only overriding Sparkle to loopback when localBetaChannelPath exists, and failing closed before invoking Sparkle.
  • P1: packaging paths could escape the intended beta channel via non-canonical BETA_OUTPUT_DIR or slash-bearing release tokens. Fixed by canonicalizing OUTPUT_ROOT, validating version/build/channel/prerelease tokens, and asserting generated artifacts stay under the resolved channel root.
  • P2: appcast rewriting trusted the request Host header. Fixed by deriving appcast artifact URLs from the bound loopback server port and adding a spoofed-Host regression test.
  • P2: windowCommand could target the wrong macOS window if another window was key. Fixed by passing the source WKWebView window through the native bridge and applying close/minimize/zoom only to that window.
  • P3: docs/help implied an arbitrary BETA_OUTPUT_DIR; updated docs/help to state the Lexar-backed local beta constraint.

Review clearances included: no private Sparkle key committed, Sparkle framework/resources are bundled, appcast targets ZIP not DMG, notarization is correctly deferred to #151, and state authority remains outside the app bundle.

Validation

Ran from /Volumes/LEXAR/repos/ClawDnD-macos-beta-distribution:

bash -n script/package_macos_beta.sh script/build_and_run.sh
python3 -m unittest viewer.tests.test_openworlds_static -q
python3 -m py_compile viewer/server.py
swift build --package-path macos/ClawDnDApp
python3 scripts/license_check.py
git diff --check
./script/package_macos_beta.sh --version 0.3.0 --build 2026052601 --channel local-beta
codesign --verify --deep --strict /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app
plutil -lint /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app/Contents/Info.plist
hdiutil verify /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD-0.3.0-beta.1.dmg

Focused negative checks:

./script/package_macos_beta.sh --version 'x/../../escape' --build 2026052601 --channel local-beta
BETA_OUTPUT_DIR='/Volumes/LEXAR/Codex/../../../Users/lume/clawdnd-beta-escape' ./script/package_macos_beta.sh --version 0.3.0 --build 2026052601 --channel local-beta

Results:

  • OpenWorlds static route tests: 19 passing.
  • Swift/Sparkle build: passing.
  • Package script and local beta generation: passing.
  • Invalid release token and escaping output root: rejected before packaging.
  • codesign --verify --deep --strict: passing.
  • Info.plist lint: passing.
  • DMG checksum verification: passing.
  • License check: passing.
  • spctl: rejected as Unnotarized Developer ID, expected because notarization is tracked separately in [macos][release] Add notarized public-beta distribution lane #151.

Current External Gate

GitHub Actions are failing before repo code runs because GitHub checkout/action download is returning external auth/codeload errors (403, Your account is suspended, and setup-uv codeload download failures). Local focused validation is clean; these CI failures should be retried after the GitHub account/action-fetch issue clears.

Follow-Up

  • Rebase only if required by merge queue/reviewer, since main is moving.
  • Keep this branch unsquashed until final merge so the review-fix trail remains easy to inspect.
  • Manual visual smoke remains useful for titlebar controls and drag strip behavior after the next installed-app handoff.
  • Notarization remains a later release-trust task in [macos][release] Add notarized public-beta distribution lane #151.

Refs #134
Refs #136
Refs #151

Summary by CodeRabbit

  • New Features

    • Sparkle-backed automatic updates with updater status, last-check info, and a "Check for Updates" action in the UI.
    • Interactive macOS-style window controls (close, minimize, zoom) in the embedded viewer.
    • Viewer can use bundled OpenWorlds UI assets and serve local beta release artifacts via a local beta channel.
  • Documentation

    • Roadmap and app surface docs updated with updater distribution details and expanded native bridge API.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a17d1b44-44f6-42d3-8a00-d1d52be3893d

📥 Commits

Reviewing files that changed from the base of the PR and between 58d1f31 and 3b85fca.

📒 Files selected for processing (11)
  • docs/OPENWORLDS_NATIVE_APP_ROADMAP.md
  • macos/ClawDnDApp/Sources/ClawDnDApp/Models/LocalEndpoint.swift
  • macos/ClawDnDApp/Sources/ClawDnDApp/Services/AppProcessService.swift
  • macos/ClawDnDApp/Sources/ClawDnDApp/Services/RepositoryLocator.swift
  • macos/ClawDnDApp/Sources/ClawDnDApp/Services/UpdaterService.swift
  • macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift
  • macos/ClawDnDApp/Sources/ClawDnDApp/Views/WebView.swift
  • script/package_macos_beta.sh
  • viewer/openworlds/screen-settings.jsx
  • viewer/server.py
  • viewer/tests/test_openworlds_static.py
📜 Recent review details
🧰 Additional context used
🧬 Code graph analysis (3)
macos/ClawDnDApp/Sources/ClawDnDApp/Views/WebView.swift (1)
viewer/openworlds/native-bridge.js (1)
  • request (9-14)
macos/ClawDnDApp/Sources/ClawDnDApp/Services/RepositoryLocator.swift (1)
macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift (1)
  • defaultOpenWorldsRepoPath (380-380)
macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift (1)
macos/ClawDnDApp/Sources/ClawDnDApp/Views/WebView.swift (2)
  • success (32-34)
  • failure (36-38)
🪛 Ruff (0.15.14)
viewer/server.py

[warning] 2869-2870: try-except within a loop incurs performance overhead

(PERF203)

🪛 SwiftLint (0.63.2)
macos/ClawDnDApp/Sources/ClawDnDApp/Services/UpdaterService.swift

[Warning] 5-5: Classes should have an explicit deinit method

(required_deinit)

🔇 Additional comments (15)
macos/ClawDnDApp/Sources/ClawDnDApp/Models/LocalEndpoint.swift (1)

31-33: LGTM!

macos/ClawDnDApp/Sources/ClawDnDApp/Services/UpdaterService.swift (1)

1-116: LGTM!

macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift (1)

1-660: LGTM!

macos/ClawDnDApp/Sources/ClawDnDApp/Views/WebView.swift (1)

1-237: LGTM!

script/package_macos_beta.sh (1)

1-426: LGTM!

macos/ClawDnDApp/Sources/ClawDnDApp/Services/AppProcessService.swift (1)

13-14: LGTM!

Also applies to: 27-28, 86-87, 172-173, 370-394

viewer/server.py (1)

40-40: LGTM!

Also applies to: 54-54, 77-83, 2828-2873, 2998-3009

viewer/tests/test_openworlds_static.py (1)

29-29: LGTM!

Also applies to: 33-33, 55-58, 61-76, 114-174

macos/ClawDnDApp/Sources/ClawDnDApp/Services/RepositoryLocator.swift (1)

17-18: LGTM!

Also applies to: 93-94

viewer/openworlds/screen-settings.jsx (1)

316-316: LGTM!

docs/OPENWORLDS_NATIVE_APP_ROADMAP.md (5)

74-77: LGTM!


82-83: LGTM!


89-95: LGTM!


118-120: LGTM!


97-99: ⚡ Quick win

Docs match the script for output paths; only wording around overrides/separators needs tightening.

  • Output path rules aren’t actually a contradiction: script/package_macos_beta.sh constrains BETA_OUTPUT_DIR/OUTPUT_ROOT to “resolve under /Volumes/LEXAR/Codex” (fail unless "$OUTPUT_ROOT" == /Volumes/LEXAR/Codex/*), so contributors can override the subfolder but not escape that subtree.
  • The doc claim that the script “rejects release identifiers containing path separators” (lines 97-99) isn’t shown in the inspected code yet—please confirm the --prerelease/--channel validation explicitly rejects / (or other path separator characters) rather than just using an allowed-character regex.

📝 Walkthrough

Walkthrough

Adds Sparkle-based update support and UpdaterService, bundles OpenWorlds UI into the macOS app and injects CLAWDND_OPENWORLDS_DIR/CLAWDND_BETA_CHANNEL_DIR into child processes, expands the native bridge with updater/window commands and UI controls, and adds packaging, signing, tests, and docs.

Changes

Sparkle Updates and Native Bridge Expansion

Layer / File(s) Summary
Sparkle dependency and UpdaterService
macos/ClawDnDApp/Package.swift, macos/ClawDnDApp/Package.resolved, macos/ClawDnDApp/SparklePublicKey.txt, macos/ClawDnDApp/Sources/ClawDnDApp/Services/UpdaterService.swift, macos/ClawDnDApp/Sources/ClawDnDApp/Models/LocalEndpoint.swift
Adds Sparkle SPM pin and resolved lock, repo-stored Sparkle public key, LocalEndpoint.appcastURL, and a new UpdaterService exposing statusPayload, checkForUpdates(), setFeedURL(_:available:), and delegate handling.
App wiring and native bridge
macos/ClawDnDApp/Sources/ClawDnDApp/App/ClawDnDApp.swift, macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift, macos/ClawDnDApp/Sources/ClawDnDApp/Views/WebView.swift
Injects UpdaterService into SwiftUI environment; RootView adds native handlers (updaterStatus, checkForUpdates, windowCommand, openFallbackDashboard), repo-path resolution utilities, performWindowCommand, drag-strip UI, and updates WebView handler signature to include NSWindow?.
Bundled OpenWorlds detection & injection
macos/ClawDnDApp/Sources/ClawDnDApp/Services/AppProcessService.swift, viewer/server.py, viewer/tests/test_openworlds_static.py
AppProcessService publishes openWorldsAssetsPath and localBetaChannelPath, validates OpenWorlds-capable repos before launching viewer/provider, and injects CLAWDND_OPENWORLDS_DIR/CLAWDND_BETA_CHANNEL_DIR into child environments; viewer/server.py resolves overrides and rewrites appcast URLs; tests exercise bundled override and beta-channel protections.
Repository locator & build bundle helpers
macos/ClawDnDApp/Sources/ClawDnDApp/Services/RepositoryLocator.swift, script/build_and_run.sh
Refactors repo discovery to prefer OpenWorlds-enabled repositories and adds candidate enumeration; local build script now prepares Contents/Frameworks/Contents/Resources, copies Sparkle/openworlds resources, and adds rpath handling.
Frontend UI: window controls & updater UI
viewer/openworlds/chrome.jsx, viewer/openworlds/screen-settings.jsx, viewer/openworlds/styles.css
TitleBar renders interactive traffic-light buttons when native bridge is ready (sending windowCommand), screen-settings shows updater status and a gated Check button, and CSS adds disabled/focus-visible rules.
Beta packaging and release automation
script/package_macos_beta.sh
New end-to-end packaging script: builds SwiftPM product, stages signed .app, emits Info.plist with channel/feed/key, copies Sparkle and resources, adjusts rpath, codesigns nested framework and app, generates appcast.xml with generate_appcast (Ed25519), creates ZIP/DMG, computes checksums, and writes release notes and a validation report.
Docs: roadmap and surface map
docs/OPENWORLDS_NATIVE_APP_ROADMAP.md, docs/OPENWORLDS_APP_SURFACE_MAP.md
Documents concrete Sparkle local-beta lane, key locations (private vs repo public key), bundling goal for viewer/openworlds/, and expanded native bridge request types and payload shapes; adds OpenWorlds app surface map.

Sequence Diagram

sequenceDiagram
  participant UI as RootView
  participant Updater as UpdaterService
  participant Sparkle as SPUStandardUpdaterController
  UI->>Updater: request checkForUpdates()
  Updater->>Updater: validate config, set lastCheckRequestedAt
  Updater->>Sparkle: checkForUpdates(nil)
  Sparkle-->>Updater: callbacks update lastError/status
  Updater-->>UI: return statusPayload
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(macos): add beta distribution lane' accurately summarizes the main feature added—a macOS beta distribution mechanism with Sparkle integration, packaging automation, and native bridge support.
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

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • LINEAR integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

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: 5

🤖 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 `@docs/OPENWORLDS_NATIVE_APP_ROADMAP.md`:
- Around line 72-75: The docs currently reference a hardcoded beta channel
volume path which limits portability; update the markdown around the listed app
bundle and update feed to note this is the maintainer's local setup and add
guidance on how contributors can override it (e.g., set an environment variable
like BETA_OUTPUT_DIR, pass a script parameter, or change a config file) and
include a short example note line such as "(Local setup: maintainers use their
own volume path; contributors can modify the script or set BETA_OUTPUT_DIR)" so
others know how to configure their own beta channel.
- Around line 131-133: Add payload/response schema docs for the new bridge
request types: document `updaterStatus` (no request payload, returns status
object with fields like canCheck, isChecking, updateAvailable, etc.),
`checkForUpdates` (no payload, triggers update check and returns updated
status), and `windowCommand` (request payload `{ command: "close" | "minimize" |
"zoom" }`, no return payload). Insert this short schema block after the listed
items and before `openFallbackDashboard` so the README clearly shows expected
request/response shapes for `updaterStatus`, `checkForUpdates`, and
`windowCommand`.

In `@macos/ClawDnDApp/Sources/ClawDnDApp/Services/UpdaterService.swift`:
- Around line 12-18: The current checks in UpdaterService.swift only test for
presence of keys via Bundle.main.object(forInfoDictionaryKey: "SUFeedURL") and
"SUPublicEDKey" but allow empty strings; update these checks to cast to String,
trim whitespace (use .trimmingCharacters(in: .whitespacesAndNewlines)) and
verify .isEmpty == false before initializing updaterController; if either
SUFeedURL or SUPublicEDKey is missing or empty set updaterController = nil and
set lastError to a clear message (e.g., "Sparkle feed URL is missing or
empty..." / "Sparkle public key is missing or empty...") to match existing
behavior.

In `@script/package_macos_beta.sh`:
- Line 306: The artifact stem currently hardcodes "beta.1" in the variable
artifact_stem; make the pre-release suffix configurable by adding a PRERELEASE
parameter/flag (or environment variable) and using it when constructing
artifact_stem (e.g., read PRERELEASE with a default of "beta.1" and set
artifact_stem to "${APP_NAME}-${version}-${PRERELEASE}"); update any help/usage
text to document the new --prerelease/PRERELEASE option and validate/normalize
the value (non-empty) before building.

In `@viewer/openworlds/screen-settings.jsx`:
- Around line 307-311: The "Check for Updates" button can be clicked when the
native bridge isn't available; update the enablement logic so the button is
disabled unless the bridge exists. In the BrassButton instance that uses
onClick={() => nativeAction("checkForUpdates")} and disabled={!bridgeReady ||
!updater.canCheckForUpdates}, add a hasBridge() guard (or wrap the onClick to
early-return if !hasBridge()) so the button's disabled state includes
!hasBridge(), ensuring nativeAction("checkForUpdates") is never invoked when the
bridge is absent.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: a2584c9c-52fb-43b0-b7fe-5f2e6dd1a1bb

📥 Commits

Reviewing files that changed from the base of the PR and between a5316b1 and a5c227f.

📒 Files selected for processing (14)
  • docs/OPENWORLDS_NATIVE_APP_ROADMAP.md
  • macos/ClawDnDApp/Package.resolved
  • macos/ClawDnDApp/Package.swift
  • macos/ClawDnDApp/Sources/ClawDnDApp/App/ClawDnDApp.swift
  • macos/ClawDnDApp/Sources/ClawDnDApp/Services/AppProcessService.swift
  • macos/ClawDnDApp/Sources/ClawDnDApp/Services/UpdaterService.swift
  • macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift
  • macos/ClawDnDApp/SparklePublicKey.txt
  • script/package_macos_beta.sh
  • viewer/openworlds/chrome.jsx
  • viewer/openworlds/screen-settings.jsx
  • viewer/openworlds/styles.css
  • viewer/server.py
  • viewer/tests/test_openworlds_static.py
📜 Review details
🧰 Additional context used
🧬 Code graph analysis (2)
viewer/openworlds/screen-settings.jsx (1)
viewer/openworlds/screen-character.jsx (1)
  • StatLine (445-457)
macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift (1)
viewer/openworlds/native-bridge.js (1)
  • request (9-14)
🪛 LanguageTool
docs/OPENWORLDS_NATIVE_APP_ROADMAP.md

[uncategorized] ~83-~83: The operating system from Apple is written “macOS”.
Context: ... The repo stores only the public key in macos/ClawDnDApp/SparklePublicKey.txt. Impl...

(MAC_OS)

🪛 Shellcheck (0.11.0)
script/package_macos_beta.sh

[info] 221-221: Expressions don't expand in single quotes, use double quotes for that.

(SC2016)


[info] 222-222: Expressions don't expand in single quotes, use double quotes for that.

(SC2016)


[info] 223-223: Expressions don't expand in single quotes, use double quotes for that.

(SC2016)


[info] 224-224: Expressions don't expand in single quotes, use double quotes for that.

(SC2016)


[info] 225-225: Expressions don't expand in single quotes, use double quotes for that.

(SC2016)


[info] 226-226: Expressions don't expand in single quotes, use double quotes for that.

(SC2016)


[info] 227-227: Expressions don't expand in single quotes, use double quotes for that.

(SC2016)

🪛 SwiftLint (0.63.2)
macos/ClawDnDApp/Sources/ClawDnDApp/Services/UpdaterService.swift

[Warning] 5-5: Classes should have an explicit deinit method

(required_deinit)

macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift

[Warning] 415-415: Classes should have an explicit deinit method

(required_deinit)

🔇 Additional comments (20)
docs/OPENWORLDS_NATIVE_APP_ROADMAP.md (2)

63-67: LGTM!


96-98: LGTM!

macos/ClawDnDApp/Sources/ClawDnDApp/Services/AppProcessService.swift (1)

13-13: LGTM!

Also applies to: 26-27, 79-80, 158-167, 178-179, 339-354

viewer/server.py (1)

52-53: LGTM!

Also applies to: 65-73, 2799-2810

viewer/tests/test_openworlds_static.py (1)

28-32: LGTM!

Also applies to: 49-53, 92-103

viewer/openworlds/chrome.jsx (1)

369-412: LGTM!

viewer/openworlds/screen-settings.jsx (1)

224-224: LGTM!

Also applies to: 289-306, 312-318

viewer/openworlds/styles.css (1)

200-220: LGTM!

script/package_macos_beta.sh (7)

1-73: LGTM!


75-114: LGTM!


116-164: LGTM!


166-180: LGTM!


182-234: LGTM!

Note: Shellcheck SC2016 warnings on lines 221-227 are false positives—the single-quoted backticks are intentional markdown formatting in the output, not shell variable expansion.


236-293: LGTM!


294-357: LGTM!

macos/ClawDnDApp/Package.swift (1)

13-22: LGTM!

macos/ClawDnDApp/Package.resolved (1)

1-14: LGTM!

macos/ClawDnDApp/SparklePublicKey.txt (1)

1-1: LGTM!

macos/ClawDnDApp/Sources/ClawDnDApp/App/ClawDnDApp.swift (1)

10-10: LGTM!

Also applies to: 17-17, 24-24

macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift (1)

8-8: LGTM!

Also applies to: 57-61, 180-186, 269-270, 276-294, 407-421

Comment thread docs/OPENWORLDS_NATIVE_APP_ROADMAP.md Outdated
Comment thread docs/OPENWORLDS_NATIVE_APP_ROADMAP.md
Comment thread macos/ClawDnDApp/Sources/ClawDnDApp/Services/UpdaterService.swift Outdated
Comment thread script/package_macos_beta.sh Outdated
Comment thread viewer/openworlds/screen-settings.jsx
@100yenadmin

Copy link
Copy Markdown
Member Author

Beta handoff update after Sparkle crash validation:

  • Fixed the runtime crash reported as Library not loaded: @rpath/Sparkle.framework/Versions/B/Sparkle. The packaged app now embeds Contents/Frameworks/Sparkle.framework, and the executable has @executable_path/../Frameworks in its rpaths.
  • Verified the loose beta-channel app launches from /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app and remains running.
  • Verified the ZIP payload contains Sparkle, has the correct rpath, and passes codesign --verify --deep --strict after extraction.
  • Verified hdiutil verify passes for ClawDnD-0.3.0-beta.1.dmg; this agent environment returned Permission denied when trying to mount the DMG, so manual mount smoke remains the one artifact-level check to do from Finder.
  • Regenerated artifacts under /Volumes/LEXAR/Codex/clawdnd-beta-channel.
  • Updated local handoff at /Volumes/LEXAR/Codex/clawdnd-macos-beta-handoff-2026-05-26/HANDOFF.md.

Focused validation already run from /Volumes/LEXAR/repos/ClawDnD-macos-beta-distribution:

bash -n script/build_and_run.sh script/package_macos_beta.sh
python3 -m py_compile viewer/server.py
python3 -m unittest viewer.tests.test_openworlds_static -q
swift build --package-path macos/ClawDnDApp
./script/package_macos_beta.sh --version 0.3.0 --build 2026052601 --channel local-beta
codesign --verify --deep --strict /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app
plutil -lint /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app/Contents/Info.plist
hdiutil verify /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD-0.3.0-beta.1.dmg
python3 scripts/license_check.py
git diff --check

Known remaining before taking out of draft: manual visual smoke in the actual app window, CodeRabbit reviewDecision refresh on the latest head, and notarization follow-up tracked separately in #151.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown

@100yenadmin: Acknowledged — solid validation pass. The Sparkle rpath fix (@executable_path/../Frameworks + embedded Contents/Frameworks/Sparkle.framework) is the right call and the codesign/hdiutil/plutil checks confirm the artifact is well-formed. Kicking off a review against the current head now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@100yenadmin

Copy link
Copy Markdown
Member Author

Additional runtime fix pushed in 58d1f3151f138af97ba311d7e9c6b50e0eb2217d after owner smoke found the app still stuck on launch.

Root cause:

  • Sparkle crash was fixed, but the installed app then launched without the dev-only CLAWDND_REPO_ROOT environment variable.
  • Auto-detection picked /Volumes/LEXAR/repos/ClawDnD, which is a valid ClawDnD checkout but does not currently have the /openworlds viewer routes. The app process stayed alive, while /openworlds/ returned 404.

Fix:

  • RepositoryLocator now scans Lexar ClawDnD worktrees and prefers an OpenWorlds-capable checkout.
  • RootView resolves/persists a compatible repo before starting viewer/provider paths.
  • AppProcessService rejects stale checkouts without OpenWorlds viewer route support with a clear supervisor error.

Validation after rebuild/regeneration:

swift build --package-path macos/ClawDnDApp
python3 -m unittest viewer.tests.test_openworlds_static -q
./script/package_macos_beta.sh --version 0.3.0 --build 2026052601 --channel local-beta
codesign --verify --deep --strict /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app
plutil -lint /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app/Contents/Info.plist
hdiutil verify /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD-0.3.0-beta.1.dmg
python3 scripts/license_check.py
git diff --check

Runtime smoke:

  • Launched /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app from Finder-style open -n.
  • Verified the app-started viewer runs from /Volumes/LEXAR/repos/ClawDnD-macos-beta-distribution.
  • Verified GET /openworlds/ and GET /openworlds/config.json both return 200.
  • Captured local screenshot at /Volumes/LEXAR/Codex/clawdnd-beta-channel/smoke/openworlds-app-smoke-front.png showing OpenWorlds rendered inside the native ClawDnD window with Wired Native.

Remaining manual checks are now limited to traffic-light behavior, drag strip behavior, and the Settings/Sparkle panel interaction.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Get the diff for the new commit
git show 58d1f3151f138af97ba311d7e9c6b50e0eb2217d --stat

echo "---DIFF---"
git show 58d1f3151f138af97ba311d7e9c6b50e0eb2217d

Length of output: 12755



@100yenadmin: Good progress — the root cause was correctly identified and the fix is logically sound. A few issues worth addressing before removing draft status:


🔴 supportsOpenWorldsViewer — fragile capability probe

Reading and string-matching viewer/server.py source to infer a route capability is brittle. Any refactor that renames the env var or route string will silently break detection in a packaged app without a clear error.

Better: test for a static sentinel file that only exists in OpenWorlds-capable checkouts:

static func supportsOpenWorldsViewer(_ url: URL) -> Bool {
    // viewer/openworlds/config.json is only present in OpenWorlds-capable checkouts
    let configURL = url
        .appendingPathComponent("viewer/openworlds/config.json")
    return FileManager.default.fileExists(atPath: configURL.path)
}

🟠 Hard-coded dev-machine path in candidateURLs()

/Volumes/LEXAR/repos/ClawDnD is Eva's machine-specific path. It will never resolve on any other machine and will be dead weight in every shipped binary. The /Volumes/LEXAR/repos directory scan already covers this case — the explicit line adds no value and embeds personal machine state into source.

// Remove this line:
append(URL(fileURLWithPath: "/Volumes/LEXAR/repos/ClawDnD"))

🟡 supportsOpenWorldsViewer checked before looksLikeRepo in defaultOpenWorldsRepoPath()

This means server.py is read via disk I/O for every candidate URL even if it doesn't pass the basic repo check. Reorder:

static func defaultOpenWorldsRepoPath() -> String? {
    for candidate in candidateURLs()
        where looksLikeRepo(candidate) && supportsOpenWorldsViewer(candidate) {
        return candidate.standardizedFileURL.path
    }
    return nil
}

🟡 resolvedRepoPath(persist:) — state mutation as a side effect

Mutating repoPath (@AppStorage) inside a function that reads like a pure resolver is surprising. Consider renaming or splitting:

// Either make the mutation explicit at the call site:
if let resolved = RepositoryLocator.openWorldsRepoPath(repoPath)
    ?? RepositoryLocator.defaultOpenWorldsRepoPath() {
    if resolved != repoPath { repoPath = resolved }
    return resolved
}

// Or keep the helper but rename to make mutation obvious:
private func resolveAndPersistRepoPath() -> String? { ... }

✅ What looks good

  • The symmetric supportsOpenWorldsViewer guard in both startViewer and startProviderSession paths in AppProcessService is correct.
  • requireRepoPath() throwing ProviderError.configuration with a clear user message is the right approach.
  • The append deduplication helper in candidateURLs() correctly prevents duplicates when the Lexar scan also returns the explicit candidate.
  • All validation commands pass; the smoke confirms /openworlds/ returning 200.
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

Caution

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

⚠️ Outside diff range comments (1)
macos/ClawDnDApp/Sources/ClawDnDApp/Services/UpdaterService.swift (1)

32-48: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Minor inconsistency: publicKeyConfigured doesn't trim whitespace.

Line 41 checks (info["SUPublicEDKey"] as? String)?.isEmpty == false without trimming, while init() trims whitespace before validating. A key containing only whitespace would show publicKeyConfigured: true in the payload despite failing initialization.

Not a runtime bug (controller would be nil, status would be "unavailable"), but the payload could be misleading during debugging.

Proposed fix for consistency
-            "publicKeyConfigured": (info["SUPublicEDKey"] as? String)?.isEmpty == false,
+            "publicKeyConfigured": ((info["SUPublicEDKey"] as? String)?
+                .trimmingCharacters(in: .whitespacesAndNewlines))?.isEmpty == false,
🤖 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 `@macos/ClawDnDApp/Sources/ClawDnDApp/Services/UpdaterService.swift` around
lines 32 - 48, In statusPayload, publicKeyConfigured currently checks
(info["SUPublicEDKey"] as? String)?.isEmpty == false which doesn't trim
whitespace; change it to mirror the init() validation by trimming the
SUPublicEDKey string (e.g., calling .trimmingCharacters(in:
.whitespacesAndNewlines)) and then testing isEmpty == false so keys of only
whitespace report false; update the publicKeyConfigured expression in the
statusPayload computed property accordingly.
🤖 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.

Outside diff comments:
In `@macos/ClawDnDApp/Sources/ClawDnDApp/Services/UpdaterService.swift`:
- Around line 32-48: In statusPayload, publicKeyConfigured currently checks
(info["SUPublicEDKey"] as? String)?.isEmpty == false which doesn't trim
whitespace; change it to mirror the init() validation by trimming the
SUPublicEDKey string (e.g., calling .trimmingCharacters(in:
.whitespacesAndNewlines)) and then testing isEmpty == false so keys of only
whitespace report false; update the publicKeyConfigured expression in the
statusPayload computed property accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 84a7b037-25af-45cc-85cd-fa2089aaa0b2

📥 Commits

Reviewing files that changed from the base of the PR and between a5c227f and 58d1f31.

📒 Files selected for processing (9)
  • docs/OPENWORLDS_APP_SURFACE_MAP.md
  • docs/OPENWORLDS_NATIVE_APP_ROADMAP.md
  • macos/ClawDnDApp/Sources/ClawDnDApp/Services/AppProcessService.swift
  • macos/ClawDnDApp/Sources/ClawDnDApp/Services/RepositoryLocator.swift
  • macos/ClawDnDApp/Sources/ClawDnDApp/Services/UpdaterService.swift
  • macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift
  • script/build_and_run.sh
  • script/package_macos_beta.sh
  • viewer/openworlds/screen-settings.jsx
📜 Review details
🧰 Additional context used
🧬 Code graph analysis (3)
script/build_and_run.sh (1)
script/package_macos_beta.sh (1)
  • copy_sparkle_framework (345-345)
macos/ClawDnDApp/Sources/ClawDnDApp/Services/RepositoryLocator.swift (2)
macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift (1)
  • defaultRepoPath (11-11)
macos/ClawDnDApp/Sources/ClawDnDApp/Services/AppProcessService.swift (1)
  • providerStatuses (52-54)
macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift (2)
macos/ClawDnDApp/Sources/ClawDnDApp/Services/AppProcessService.swift (1)
  • providerStatuses (52-54)
macos/ClawDnDApp/Sources/ClawDnDApp/Services/CampaignStore.swift (1)
  • reload (11-32)
🪛 LanguageTool
docs/OPENWORLDS_NATIVE_APP_ROADMAP.md

[style] ~154-~154: Consider using the typographical ellipsis character here instead.
Context: ...erStatus", "payload": {} } ``` Returns `{ "updater": { "version": "0.3.0", "build": "2026052601", "feedURL": "...", "channel": "local-beta", "canCheckFor...

(ELLIPSIS)


[style] ~168-~168: Consider using the typographical ellipsis character here instead.
Context: ..., or zoom; the native reply includes { "command": "...", "performed": true }`. Native replies...

(ELLIPSIS)

🔍 Remote MCP

The DeepWiki tool is unavailable for this private repository. However, based on the comprehensive context provided in your PR summary, I can identify the key factual information already gathered that's relevant for review:

Summary of Additional Relevant Context Found

Sparkle Framework Integration

The PR integrates Sparkle 2.9.2 pinned in both Package.resolved and Package.swift. The implementation requires:

  • SUFeedURL and SUPublicEDKey configured in Info.plist (UpdaterService validates these at initialization)
  • The public key stored in macos/ClawDnDApp/SparklePublicKey.txt
  • Framework bundled at Contents/Frameworks/Sparkle.framework with proper runtime search path (@executable_path/../Frameworks) set via install_name_tool

OpenWorlds Asset Bundling & Environment Variable

  • Bundled assets are placed at ClawDnD.app/Contents/Resources/openworlds
  • Launched via CLAWDND_OPENWORLDS_DIR environment variable (injected by AppProcessService)
  • Detection validates presence of openworlds/index.html in Bundle.main
  • Python viewer (viewer/server.py) resolves assets dynamically based on CLAWDND_OPENWORLDS_DIR, defaulting to viewer/openworlds if unset

State Authority Boundaries

Per the PR objectives:

  • App controls: Swift shell updates, bundled OpenWorlds UI assets
  • Repo/state-backed: Python viewer, engine, state directories, play-state, QA state, campaign truth
  • No change in scope: Browser actions and game-state writes remain in existing lanes (not moved to app)

Native Bridge Extension

New request types added via RootView:

  • updaterStatus → returns UpdaterService.statusPayload
  • checkForUpdates → triggers UpdaterService.checkForUpdates()
  • windowCommand → validates command (close/minimize/zoom), locates active window, executes operation

Repository Detection Enhancement

RepositoryLocator.supportsOpenWorldsViewer(_:) now validates repo capability by checking viewer/server.py for OpenWorlds marker strings before allowing asset bundling.

Validation Artifacts

The packaging script (script/package_macos_beta.sh) produces:

  • Signed .app, .zip, .dmg outputs
  • appcast.xml (generated via Sparkle's generate_appcast with Ed25519 private key)
  • RELEASE_NOTES.md, CHECKSUMS.txt, validation report
  • All outputs staged to /Volumes/LEXAR/Codex/clawdnd-beta-channel
🔇 Additional comments (34)
docs/OPENWORLDS_APP_SURFACE_MAP.md (1)

1-74: LGTM!

docs/OPENWORLDS_NATIVE_APP_ROADMAP.md (2)

71-75: LGTM!

Also applies to: 85-92


148-169: LGTM!

macos/ClawDnDApp/Sources/ClawDnDApp/Services/UpdaterService.swift (2)

12-22: LGTM!


51-62: LGTM!

macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift (6)

57-61: LGTM!

Also applies to: 78-82, 94-96


185-194: LGTM!

Also applies to: 200-212


214-234: LGTM!

Also applies to: 236-249, 251-283


285-303: LGTM!


332-344: LGTM!

Also applies to: 346-369


442-456: LGTM!

viewer/openworlds/screen-settings.jsx (1)

228-228: LGTM!

Also applies to: 308-314

macos/ClawDnDApp/Sources/ClawDnDApp/Services/AppProcessService.swift (6)

13-13: LGTM!

Also applies to: 26-26


65-69: LGTM!


84-85: LGTM!


130-134: LGTM!


168-169: LGTM!

Also applies to: 176-176, 188-188


349-364: LGTM!

macos/ClawDnDApp/Sources/ClawDnDApp/Services/RepositoryLocator.swift (5)

4-14: LGTM!


16-23: LGTM!


25-39: LGTM!


41-86: LGTM!


95-102: LGTM!

script/build_and_run.sh (5)

16-17: LGTM!

Also applies to: 35-35


38-40: LGTM!


75-84: LGTM!


86-90: LGTM!


92-96: LGTM!

script/package_macos_beta.sh (6)

14-14: LGTM!

Also applies to: 21-21


25-42: LGTM!


91-97: LGTM!


252-252: LGTM!

Also applies to: 271-275


290-295: LGTM!


328-328: LGTM!

Also applies to: 346-346

@linear-code
linear-code Bot marked this pull request as ready for review May 26, 2026 11:23
@100yenadmin

Copy link
Copy Markdown
Member Author

Current-head cleanup pushed in 3bc24b346d6a697d4e6c70e3681628d0c2553a12.

Addressed the follow-up review items:

  • Replaced source-string capability probing with a static OpenWorlds sentinel: viewer/openworlds/index.html. There is no static viewer/openworlds/config.json in this prototype; /openworlds/config.json is served dynamically by the viewer.
  • Removed the exact /Volumes/LEXAR/repos/ClawDnD candidate from the shipped binary; the general /Volumes/LEXAR/repos scan still covers the maintainer machine without embedding that specific checkout path.
  • Reordered OpenWorlds repo discovery to check looksLikeRepo before supportsOpenWorldsViewer.
  • Split read-only repo resolution from the mutating persist step via resolveAndPersistRepoPath().

Focused validation after the cleanup:

swift build --package-path macos/ClawDnDApp
python3 -m unittest viewer.tests.test_openworlds_static -q
python3 -m py_compile viewer/server.py
bash -n script/build_and_run.sh script/package_macos_beta.sh
./script/package_macos_beta.sh --version 0.3.0 --build 2026052601 --channel local-beta
codesign --verify --deep --strict /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app
plutil -lint /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app/Contents/Info.plist
hdiutil verify /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD-0.3.0-beta.1.dmg
python3 scripts/license_check.py
git diff --check

Installed-app smoke after regenerating the beta artifact:

  • Relaunched /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app.
  • Verified the app-started viewer cwd is /Volumes/LEXAR/repos/ClawDnD-macos-beta-distribution.
  • Verified GET /openworlds/ and GET /openworlds/config.json both return 200.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

git show 3bc24b346d6a697d4e6c70e3681628d0c2553a12 --stat
echo "---DIFF---"
git show 3bc24b346d6a697d4e6c70e3681628d0c2553a12

Length of output: 639


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Check current branch and latest commits
git log --oneline -10
echo "---"
# Look at the key files changed in the cleanup
cat macos/ClawDnDApp/Sources/ClawDnDApp/Services/RepositoryLocator.swift
echo "---ROOTVIEW---"
grep -n "resolveAndPersistRepoPath\|resolvedRepoPath\|requireRepoPath" macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift

Length of output: 5700


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Read the resolvedRepoPath / resolveAndPersistRepoPath helpers in full
awk '/private func requireRepoPath/,/^    private func [a-z]/' \
    macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift | head -60

Length of output: 346


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Get lines from requireRepoPath onwards to end of file
grep -n "private func requireRepoPath\|private func resolveAndPersistRepoPath\|private func resolvedRepoPath" \
    macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift

echo "---TAIL---"
# Show the last 60 lines where these helpers likely live
tail -80 macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift

Length of output: 3271


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Extract from line 346 onwards, about 40 lines
sed -n '346,390p' macos/ClawDnDApp/Sources/ClawDnDApp/Views/RootView.swift

Length of output: 1722



@100yenadmin: All four items verified from the current HEAD:

  • supportsOpenWorldsViewer — ✅ Now a clean FileManager.fileExists check on viewer/openworlds/index.html. No source-string fragility.
  • Hard-coded Lexar path — ✅ Removed; dynamic /Volumes/LEXAR/repos scan covers it without embedding the specific checkout path.
  • looksLikeRepo ordering — ✅ defaultOpenWorldsRepoPath() correctly gates supportsOpenWorldsViewer behind looksLikeRepo.
  • Mutation split — ✅ resolvedRepoPath() is pure-read; resolveAndPersistRepoPath() owns the @AppStorage write; requireRepoPath() throws with a clear user-facing message.

One minor observation (not a blocker): resolvedRepoPath() silently falls through to discovery when the user's stored repoPath is a valid ClawDnD checkout but lacks viewer/openworlds/index.html. The path will be transparently swapped to the discovered OpenWorlds-capable checkout — probably the right call, but users who explicitly set a path in Settings may find it overwritten without indication. Something to revisit if that surfaces as a UX issue.

Triggering review on the current head.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@100yenadmin

Copy link
Copy Markdown
Member Author

Update on commit 9931040:

Fixed the Sparkle local-beta blocker found during packaged-app smoke. Sparkle 2 rejects raw file:// appcast/download URLs, so the app now stores the local beta channel path in Info.plist, passes CLAWDND_BETA_CHANNEL_DIR to the repo-backed viewer, and serves /appcast.xml plus ClawDnD-* release artifacts over the same loopback host/port as OpenWorlds. The appcast is rewritten to the actual viewer port before Sparkle sees it.

Also fixed the packaged-app blank-window regression by keeping the titled window style bit while hiding native title/buttons; this preserves WKWebView rendering while leaving OpenWorlds as the visible app chrome.

Focused validation from /Volumes/LEXAR/repos/ClawDnD-macos-beta-distribution:

  • python3 -m unittest viewer.tests.test_openworlds_static -q -> 18 tests passing
  • python3 -m py_compile viewer/server.py
  • swift build --package-path macos/ClawDnDApp
  • bash -n script/build_and_run.sh script/package_macos_beta.sh
  • ./script/package_macos_beta.sh --version 0.3.0 --build 2026052601 --channel local-beta
  • codesign --verify --deep --strict /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app
  • plutil -lint /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app/Contents/Info.plist
  • hdiutil verify /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD-0.3.0-beta.1.dmg
  • python3 scripts/license_check.py
  • git diff --check

Manual packaged-app smoke:

  • Launched /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app
  • Confirmed OpenWorlds renders inside the native app, not Safari
  • Confirmed visible OpenWorlds window controls are bridge-backed accessibility buttons
  • Confirmed Settings feed is http://127.0.0.1:8769/appcast.xml
  • Clicked Check for Updates; Sparkle returned You're up to date! ClawDnD 0.3.0 is currently the newest version available.; update error remained none
  • Confirmed GET /appcast.xml rewrites release notes and ZIP URLs to the live viewer port, and GET /ClawDnD-0.3.0-beta.1.zip returns the packaged ZIP payload

Current GitHub status note: CodeRabbit is green/approved on the latest head. GitHub Actions are failing before repo code runs: Ubuntu CI cannot download astral-sh/setup-uv@v5 from codeload, and checkout/license/macOS jobs fail with GitHub returning 403 / Your account is suspended. I reran the failed workflows once and got the same external auth/codeload failure.

@100yenadmin

Copy link
Copy Markdown
Member Author

Adversarial follow-up pushed in 3b85fca.

What changed after the last approved head:

  • Fixed appcast URL rewriting so a spoofed Host header cannot make local beta update URLs point off-loopback. Added a regression test for Host: updates.example.test.
  • Fixed Sparkle update availability so the Settings update action fails closed when no local beta appcast is actually being served.
  • Fixed windowCommand targeting so close/minimize/zoom applies to the WKWebView source window, not whichever app window is currently key.
  • Hardened script/package_macos_beta.sh path handling: canonicalized BETA_OUTPUT_DIR, rejected release identifiers with path separators, asserted generated artifacts remain under the resolved beta channel root, and aligned docs/help with the Lexar local-beta constraint.
  • Regenerated /Volumes/LEXAR/Codex/clawdnd-beta-channel from the new commit.

Validation from /Volumes/LEXAR/repos/ClawDnD-macos-beta-distribution:

bash -n script/package_macos_beta.sh script/build_and_run.sh
python3 -m unittest viewer.tests.test_openworlds_static -q
python3 -m py_compile viewer/server.py
swift build --package-path macos/ClawDnDApp
python3 scripts/license_check.py
git diff --check
./script/package_macos_beta.sh --version 0.3.0 --build 2026052601 --channel local-beta
codesign --verify --deep --strict /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app
plutil -lint /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD.app/Contents/Info.plist
hdiutil verify /Volumes/LEXAR/Codex/clawdnd-beta-channel/ClawDnD-0.3.0-beta.1.dmg

Results: 19 viewer tests passing, Swift build passing, license check passing, code signing verify passing, Info.plist lint passing, DMG verification passing. Negative path checks also reject slash-bearing versions and escaped beta output roots before packaging.

Known external gate: GitHub Actions are still failing before repo code runs because checkout/action downloads are returning GitHub 403/codeload/account errors. I am leaving the branch unsquashed for auditability; it can be rebased or squashed later once the external CI/merge gate is healthy.

@100yenadmin

Copy link
Copy Markdown
Member Author

macOS Sparkle beta-distribution lane — deferred (see #134) and hard-conflicted by the ClawDnD→WorldOS rename. Closing the stale PR; re-port fresh when the beta lane is prioritized.

@100yenadmin 100yenadmin closed this Jun 5, 2026
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