Skip to content

fix(app): the paid Mac app now actually starts the fleet (W5-2 × W5-3 wiring)#235

Merged
AVADSA25 merged 3 commits into
mainfrom
claude/r3-fleet-start
Jul 10, 2026
Merged

fix(app): the paid Mac app now actually starts the fleet (W5-2 × W5-3 wiring)#235
AVADSA25 merged 3 commits into
mainfrom
claude/r3-fleet-start

Conversation

@AVADSA25

Copy link
Copy Markdown
Owner

Audit finding D2: a paying customer's app launched, logged "fleet start deferred to W5-3 (launchd); no services started", and exited 0.

The engineering was never missing. generate_launchagents.py, install_launchagents.sh and first_run.py all existed and were tested — first_run even invoked the installer. Three wires were never connected:

  1. Nothing called first_run. main() now runs it on first launch (sentinel-idempotent), re-bootstraps if launchd dropped the fleet, and fails loudly when no agents load. Source-tree safety preserved: outside a bundle it still starts nothing (PM2 owns the fleet on a dev machine).
  2. build_app.sh never copied first_run.py or launchd/ into the bundle. A buyer's .app contained only {app, codec_app_main.py, python}.
  3. A buyer's Mac has no node. The installer read ecosystem.config.js via node. The build now dumps Resources/services.json at build time; the installer prefers it.

Found while verifying, not by reading: every service's cwd is the build machine's repo path. Shipped verbatim, all 15 services would fail on a buyer's Mac. The build now strips it and fails if any build-machine path survives.

Verified end to end: built a real bundle, then regenerated all 15 LaunchAgents with node removed from PATH → 15 valid plists (plutil -lint OK), each pointing at the bundled interpreter and Resources/app.

23 new tests, each mutation-tested. (The bundling test was rewritten after mutation testing showed the first version passed a deleted cp line.) Full suite: 2497 passed, ruff clean.

🤖 Generated with Claude Code

Mikarina13 and others added 3 commits July 10, 2026 12:52
… open blocker

Records what shipped (license-email 404, dead leak-guard test, pytest firing a
live webhook, client email leak, fictional /codec copy, README/FEATURES lies),
the two audit findings I checked and rejected, and the blockers: AVA-site-v2 has
no remote and deploys by hand, so the client's email + fictional SDK are STILL
LIVE until someone republishes. Also flags a concurrent Claude session
committing in that repo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Payment Link created and verified against the webhook guard; Buy button + honest
terms on the codec page; four money defects fixed and mutation-tested BEFORE the
button went in (unpaid mint, swallowed email failure, refunds never revoking,
year-2 renewal lockout). charge.refunded was not even subscribed on the live
endpoint — now is.

Still owed: one real purchase (needs a card) and a site republish, without which
the button is not visible to anyone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… wiring)

Buyer-journey audit R3, finding D2. A paying customer's app launched, logged
"fleet start deferred to W5-3 (launchd); no services started", and exited 0.

The engineering was never missing. generate_launchagents.py,
install_launchagents.sh and first_run.py all existed, were tested, and first_run
already invoked the installer. Three wires were never connected:

1. NOTHING CALLED first_run. codec_app_main.py:main() logged the deferral and
   returned. main() now runs first-run on first launch (idempotent via the
   ~/.codec/.first_run_complete sentinel) and, on later launches, re-bootstraps
   the fleet if launchd has dropped it. It fails loudly (exit 1) when no agents
   are loaded after setup — silence was how this shipped unnoticed.

   SOURCE-TREE SAFETY IS PRESERVED: outside an .app the app still starts nothing,
   because a developer's fleet runs under PM2 and double-running it is
   destructive. That instinct in W5-2 was right; it just also had to do something
   when it IS the shipped app.

2. build_app.sh NEVER COPIED first_run.py OR launchd/ INTO THE BUNDLE. The .app a
   buyer received contained only {app, codec_app_main.py, python} — so even a
   fixed main() would have had nothing to call. Now bundles first_run.py,
   fetch_models.py, models.json and launchd/.

3. THE BUYER'S MAC HAS NO NODE. install_launchagents.sh read the service list from
   ecosystem.config.js via `node`, which no customer has. build_app.sh now dumps
   the fleet to Resources/services.json at BUILD time (when node is present) and
   the installer prefers it; the ecosystem/node path remains for dev machines and
   errors clearly if node is absent.

Found while verifying, not by reading: every service in ecosystem.config.js
carries cwd = the BUILD MACHINE's repo path. Shipped verbatim, all 15 services
would have failed on a buyer's Mac (that directory doesn't exist there). The
build now strips the repo-root cwd so --workdir substitutes the bundle's
Resources/app, and FAILS THE BUILD if any build-machine path survives into
services.json.

Verified end to end, not asserted:
  * Built a real bundle; Resources now carries first_run.py, fetch_models.py,
    models.json, launchd/, services.json (15 services, 0 dev paths).
  * Regenerated all 15 LaunchAgents in an env with node REMOVED from PATH,
    stdlib python only -> 15 valid plists (plutil -lint OK), every one pointing
    at the bundle's own interpreter and Resources/app.
  * 23 new tests. Each fix is mutation-tested: restoring the "deferred" main(),
    deleting the cp of first_run.py, or dropping the dev-path guard each fails
    its test. (The bundling test was rewritten after mutation testing showed the
    first version passed a deleted cp line — it asserted a substring that also
    appears in a comment.)
  * Full suite: 2497 passed. ruff clean.

Still open before a buyer can install: publish CODEC-Installer.dmg (it is signed,
notarized and stapled, and sits in no release), and repoint CODEC_DMG_URL at that
installer rather than the app-only update DMG.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AVADSA25 AVADSA25 merged commit c60702a into main Jul 10, 2026
1 check passed
AVADSA25 pushed a commit that referenced this pull request Jul 10, 2026
… agreement is the blocker

The paid app now starts the fleet (PR #235); the licence email points at the
installer via a stable latest URL (not the stub update DMG); build-app.sh refuses
to bundle a stub. The only thing between here and a buyer-testable install is the
Apple Developer agreement (notarytool 403 'agreement expired') — Account Holder
must accept it at developer.apple.com. Full 2-step release runbook included.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.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.

2 participants