Stop the app from lying: dormant flags hide all mock, plus live cloud→local sync and a mobile cockpit fix
The founder caught the specialized screens (Marketing/Customer/founder-ops) presenting hardcoded sample data as if it were his real pipeline. We didn't delete anything — we routed every mock surface through a single dormant feature-flag registry (hide, don't destroy), backed up the seed rows, kept the real action-SDK engine untouched, made cloud→local sync re-render without a refresh, and fixed the phone cockpit's garbled render.
What the founder saw
Opening 알리다 (Marketing) showed "30 content rows / 10 leads / 27 approvals" — a dense pipeline he never created. Same for 고객 (Customer), and a whole "Invoice reissue" workflow screen wired to invoice-system · payment-processor · crm. His question was blunt: "이런 화면도 다 사기인거냐?" (is this all fake?).
It was. The data came from hardcoded constants:
DEFAULT_MARKETING_CONTACT_LEDGER_CSV/DEFAULT_MARKETING_CONTENT_CALENDAR_CSV(10 + 30 rows)DEFAULT_CUSTOMER_ACCOUNT_LEDGER_CSV/DEFAULT_CUSTOMER_WORK_LEDGER_CSV(5 + 6 rows)MOCK_CONNECTORSin@ddalkkak/action-sdk(invoice-system, payment-processor,founder_workflow_mock…)
Verified by grep: executeAction (the real executor) is called 2 times in the whole app, while MOCK_CONNECTORS is referenced 18 times — almost entirely to display fake capabilities. The screens looked operational but did nothing.
Real vs fake — the line we drew
The mock data is garbage to surface, but the action-sdk engine around it is not: 32 functions for an untrusted-content prompt-injection envelope, an approval-gate / risk-permission model, provider-neutral execution plans + receipts, and manifest validation. Crucially, verificationEvidence (which the Mission Control merge gate actually uses) imports from the same package. Deleting it would have broken a real, load-bearing system.
So the decision (the founder's: "숨기는 애들은 미사용 플래그 걸어"): hide, don't delete; flag what's hidden so future-me never wonders if it's in use.
The dormant registry
apps/desktop/src/featureFlags.ts is the single source of truth for what's parked:
export const DORMANT = {
marketingMockActions: { enabled: false, since: "2026-06-20", why: "…가짜 — 채널 연결 0" },
customerMockWorkflows: { enabled: false, since: "2026-06-20", why: "founder_workflow_mock 기반" },
founderOpsMockScreens: { enabled: false, since: "2026-06-20", why: "mock 데모 화면" },
seedSampleLedgers: { enabled: false, since: "2026-06-20", why: "박힌 가짜 기록(백업됨)" },
} as const satisfies Record<string, DormantFeature>;Every render path for those surfaces is now isEnabled(...) && (...). Every parked constant/block carries an @dormant featureFlags:<key> comment, so grep -rn "@dormant" lists everything dormant at a glance. Flip a flag to true to revive. The seed rows were copied verbatim to docs/archive/removed-fake-seed-data-2026-06-20.md before anything changed — nothing was physically deleted.
The render tests now assert the inverse of what they used to: the mock sections must NOT appear by default (expect(html).not.toContain("Campaign actions"), etc.). 330/330 vitest green = the engine and merge gate are intact.
Two more fixes in the same pass
cloud → local was data-only. CloudSync pulls remote checklist/widget changes into localStorage every 5s, but ZonePage read its checkbox state once at mount, so a cloud edit didn't show until you navigated away and back. sync.ts now counts rows it actually applied and dispatches a dalkkak:remote-data event only when something changed; ZonePage re-reads on that event. No manual refresh.
The phone cockpit rendered as a field of dots and "??". Two causes: (1) the same tmux session attached on phone and Mac at different sizes makes tmux fill the dead window region with ·; (2) the phone's terminal font has no emoji glyph (👀 → "??"), worsened by no unicode-width addon. Fix in apps/terminal-cockpit/static/index.html: load the unicode11 addon (term.unicode.activeVersion = "11"), add "Apple Color Emoji" to the font stack, and on session-switch run tmux set -g window-size latest + setw -g aggressive-resize on before attach so the window follows whichever client is active. The HTML is include_str!-embedded, so the cockpit binary was rebuilt.
Pattern
When a surface looks like it works, separate the engine (keep — it may be real and load-bearing) from the data/connectors it operates on (the fake part). Don't delete in anger; route everything questionable through one flag registry with a written reason and a grep-able tag, back up the raw data, and let the tests assert the mock is gone. "Reversible and labeled" beats "deleted and forgotten."