RELEASE16 May 2026 · 9 min read · v7.8.76

v7.8 — Portfolio Truth and a brain that never sleeps

For a year, "Swing Deck" was a tab in your browser. Type localhost:8001. Hope the Python server you started in Terminal was still alive. Don't quit Terminal or it dies. v7.8 ships next month and reframes that completely: Swing Deck is now a standalone Mac app with a brain that runs 24/7. Plus real Portfolio Truth — a broker_state daemon polls E*TRADE every 30 seconds and surfaces broker-vs-dashboard drift in real time. Plus six silent broker bugs caught and pinned. Release window: June 1, 2026, when the GitHub Actions quota refreshes. 16 commits stacked locally; this post is what's in them.

The framing shift

The product up to v7.7 had a structural problem nobody talked about: the "dashboard" and the "engine" were the same process. If you closed the browser tab, you closed your watchlist's view of itself — but if you closed Terminal too (or rebooted, or your laptop went to sleep), you also killed the audit cycle, the alert engine, the scanner, the broker reconciliation. The whole discipline machine.

For a tool whose whole pitch is "discipline that runs while you don't," that gap mattered. Pre-market scanner fires at 7am ET. Earnings drops at 4:30pm. VIX spikes at 9:32am. Macro print at 8:30am. If your "trading app" is a browser tab you closed last night, the alerts that would tell you about any of those never fire. The engine that you bought specifically because you wanted it watching while you weren't — it wasn't watching either.

v7.8 separates the two: the brain runs 24/7 as a system service; the window is something you open when you want to look.

1. Standalone Mac app (PWA install)

The dashboard now ships a proper PWA manifest. Safari menu → File → Add to Dock creates a real standalone app: own Dock icon, no URL bar, no tabs, native macOS traffic lights, system notifications attributed to "Swing Deck" instead of "Safari." Chrome's "Install" works the same way (menu → Cast, Save, Share → Install Swing Deck). Both use the same WKWebView / Blink engines the browser always uses — there's no native binary, just a manifest the browser respects to drop the chrome.

Why this matters more than it sounds: the cognitive friction of "is this a browser tab or an app?" was higher than I realized. Cmd+W behaves like an app now (closes the window, no "this will close N tabs" prompt). Cmd+T does nothing (there are no tabs). Spotlight finds it. Cmd+Tab treats it as its own app. The Dock badge can show pending-confirmation counts. None of that is a feature you'd put on a marketing page, but together they remove a kind of background noise — the constant low-grade reminder that this is "a website I happen to use a lot" rather than "an app I work with."

What we tried first: Tauri. Shipped a full scaffold at tauri-app/, then spent four patch releases chasing browser-API divergences — window.open blocked by WKWebView, <a> navigation routed differently, window.confirm() silently returning false. The fifth divergence (confirm()) would have meant refactoring 28 dashboard buttons that gate on it. We pivoted to Safari Add-to-Dock. The Tauri scaffold stays in the repo as a future option for customer distribution once the dashboard is wrapper-agnostic. Honest scope — what we tried, what worked, what we'll come back to. A separate post is queued on that pivot.

2. 24/7 brain (macOS LaunchAgent)

The real shift. A single command:

./packaging/launchd/install.sh

...writes a user-scope macOS LaunchAgent that runs control_server.py (which auto-spawns the audit watcher and index fetcher as managed subprocesses) 24/7 starting at every login, with auto-respawn on crash. The installer detects your Python (framework 3.14 preferred, then Homebrew, then system), substitutes paths into a checked-in plist template via sed, validates with plutil -lint, then launchctl loads it and smoke-checks that port :8001 is bound within 10 seconds.

The one environment variable that matters: AUTO_SHUTDOWN_ON_CLOSE=false. The dashboard has always had a watchdog that exits the server when no dashboard heartbeat is detected for 5 minutes — that watchdog made sense when "running Swing Deck" meant "I am at my desk looking at it." It actively breaks the 24/7 use case. The LaunchAgent disables it.

The result: alerts fire pre-market at 4am AZ regardless of whether I have a window open. The audit cycle runs every ~2 minutes through overnight. The framework's state is current when I sit down in the morning, not "let me wait while it cold-starts." The viewing surface — the dashboard tab — became optional. The discipline engine is always-on.

Uninstall is the symmetric one-liner: ./packaging/launchd/uninstall.sh. Logs in ~/Library/Logs/SwingDeck/ stay in place as operator trail. The plist sets KeepAlive.Crashed=true but SuccessfulExit=false — respawn on crash, but SIGTERM still stops cleanly so you can launchctl unload when you want to.

3. Portfolio Truth (4 phases)

The companion shift. The dashboard's understanding of broker state used to rely on whatever was in audit_output.json, which itself was a snapshot of what the framework recommended — not necessarily what the user actually held at the broker. v7.8 restructures that completely:

Local-first stays local-first. The broker_state daemon is your machine talking to your broker, never through us. We don't proxy. We don't see your positions. We don't see your fills. The dashboard you run is the same dashboard we ship; the broker integration runs in your Python process on your laptop.

4. Six silent broker bugs caught and pinned

The most uncomfortable kind of bug is the one that fails quietly enough that nobody notices. v7.8 caught six of them — not because they were urgent, but because the user reported a symptom and we traced. A full post on the deepest two of these is queued; here's the summary:

All six now have pin tests + bug-class lessons in the project memory so the next dev (which is also me, three weeks from now) sees the pattern. The pattern in all six is the same: error logs that look like noise until you trace them. The work isn't catching the bug when it screams. It's noticing when the system stops screaming about something it should be screaming about.

What didn't make it

A few things in flight that didn't ship in v7.8 and that the roadmap is honest about:

How to try it

Until the June 1 release window: clone from source.

git clone https://github.com/pinoy81/swing-audit
cd swing-audit
python3 control_server.py

Then, in Safari:

For 24/7 daemons (optional but recommended if you trade pre-market): ./packaging/launchd/install.sh. Uninstall any time via ./packaging/launchd/uninstall.sh.

On June 1, the same is available via the normal .dmg install at swing-deck.com/download.

The pitch

Discipline-as-a-product has a structural problem: you have to be using the product for it to work. v7.8's whole point is to remove the "have to be using" dependency — the engine runs whether you're watching or not. The window is optional. The brain isn't.

Combined with the broker reconciliation work, the picture for May 2026 is: a local-first dashboard whose broker truth is real-time and whose discipline engine is always-on. Same trader, same framework, same local-only data — just no longer dependent on you remembering to start the server every morning.

Claim a Founding Slot — $14.50/mo

Questions, feature requests, or just want to see how the LaunchAgent flow works on your setup before you commit? Reply to contact@swing-deck.com. Real human, usually under 24h.


Disclosure: Swing Deck is built and operated by one person. The product is local-first; positions, broker tokens, and journal entries never leave your machine. AI features use your own API keys (BYOK). We don't proxy your data through our servers. Pricing as of 2026-05-16. Past performance is not indicative of future results. Nothing in this post is investment advice; it's a description of what software does. Source code for the framework + dashboard is available at github.com/pinoy81/swing-audit.