fix(app): the paid Mac app now actually starts the fleet (W5-2 × W5-3 wiring)#235
Merged
Conversation
… 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
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.shandfirst_run.pyall existed and were tested — first_run even invoked the installer. Three wires were never connected: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).build_app.shnever copiedfirst_run.pyorlaunchd/into the bundle. A buyer's.appcontained only{app, codec_app_main.py, python}.node. The installer readecosystem.config.jsvia node. The build now dumpsResources/services.jsonat build time; the installer prefers it.Found while verifying, not by reading: every service's
cwdis 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
noderemoved fromPATH→ 15 valid plists (plutil -lintOK), each pointing at the bundled interpreter andResources/app.23 new tests, each mutation-tested. (The bundling test was rewritten after mutation testing showed the first version passed a deleted
cpline.) Full suite: 2497 passed, ruff clean.🤖 Generated with Claude Code