Skip to content

Changelog - Dev - 08.27.2026 โ€‹

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog.


Not on dev yet. Every entry below is on feat/admin-and-merchant-portals behind draft pull request #976, which covers both August 24 and August 27. The only thing that reached dev across those two days is #952, already recorded in dev-08.24.2026-v0.1.0.md. This file is written now so one entry covers every lane; it describes work that is complete on the branch and awaiting review, not work that has shipped.


[Unreleased] - 2026-08-27 โ€‹

Added โ€‹

  • The admin Dashboard becomes a reading page with a status rail: The landing page was an eleven-card grid of links where every card read "Available", which told nobody anything. It is now a dominant reading column (what Lantern is, the launch plan stage parsed from the launch docs rather than restated, and the six most recent release days) beside a fixed 240px rail of seven live counts. Both documents are read at runtime through the docs API, behind the same admin token every other portal call carries, after review found the first cut had bundled them into a publicly served build (see Security below). Every count comes from a call the owning screen already makes, so no endpoint was added. Open moderation cases were deliberately left off, because counting them means two unbounded listeners on a landing page. (#976) enhancement

  • One grammar for an empty value, across both portals: 63 replacements across 30 files give "we have this and are not showing it" and "this is genuinely empty" two distinct components and two distinct characters, with the screen-reader phrase the glyph itself cannot carry. The banned long dash, which had been serving in both roles and was the largest group, is gone from that duty. The rule already existed on a private constant in packages/shared/ads/delivery.js where nothing could import it, which is why every call site had been re-deciding it by eye; it now lives in a shared module. (#976) enhancement

  • An Errors tab that reads Cloud Logging: The admin portal had no way to see service errors without leaving it. The new tab surfaced a Cloud Scheduler job that had been returning 404 for days, which was then deleted against lantern-app-dev only, on explicit permission and with its definition backed up first. (#976) enhancement

  • A Firestore source in the Query Console, and an Assistant grounded in the real schema: The console is its own Analytics surface with a structured Firestore builder beside BigQuery, executed server side against a field allowlist. Firestore has no query language, so a SQL box was never an option; the rejected alternative, streaming Firestore into BigQuery, was turned down for keeping a second live copy of user documents outside Firestore. The Assistant now reads the real BigQuery schema instead of inventing table names. (#961) enhancement

  • A gate that fails when an allowlisted Firestore field is not written by anything: npm run lint:allowlist. Measured rather than asserted: 6 of 8 known-bad entries caught, 0 false positives across 42 good ones. A more precise version was built, measured, and rejected for failing on four genuinely written fields, and the rejected approach is recorded in the script so nobody re-attempts it blind. (#961) devops

  • A gate that checks a document's stated rules against its own templates: A rule written in prose while the template beside it demonstrates something else is a forked rule, and the template is the copy an agent follows. Wired into npm run validate as "Docs Format Invariants". (#954) devops

  • A guard that refuses any write outside the project-manager lane: Built on 2026-08-24 after a PM session produced six commits nobody asked for, with a documented override door that has to be opened explicitly and by name. (#957) devops

  • A session-start check that says which PM lane a session is in: The guard's inversion means an undeclared project-manager session is unguarded, so the session now says so out loud instead of being silent about it. (#975) devops

Changed โ€‹

  • The admin sidebar is rearranged and Billing becomes Financials: System Health moves into Development, Development sits directly above Settings, and the Costs tab leaves System Health. Cost is not a health signal: the Four Golden Signals, USE and RED all measure whether the system is working now, while cost is a different audience on a different clock. Rather than moving one tab, Billing absorbed the last cost surfaces and became a single complete money view. (#976) enhancement

  • The PM lane guard now judges only a declared project manager: It classified by launch route, so every agent dispatched through any other route, including an Agent-tool subagent, read as the project manager and was blocked; two dispatched builders were stopped inside fifteen minutes. There is exactly one PM session a day and its launch is deliberate, so declaring THAT is reliable in a way that enumerating every builder route is not. (#975) bug

  • The draft pull request opens early, everywhere: Three places taught opening the pull request at the end of the work, two of them runnable blocks a newcomer copies rather than reads, which cannot be followed alongside the autopilot loop that opens a draft first and reviews third. Nothing about when a pull request is marked ready or merged moved, and rule 12 stands untouched. (#976) documentation

  • verified-done gains the agreement-is-not-correctness rule: A guard that compares two copies proves they match, never that either is right, and a check that covers half a decision must say which half. Seven instances of that pattern landed in a single day, in seven different costumes. (#976) documentation

Fixed โ€‹

  • Five visible merchant portal defects, measured before and after: A sub-metric icon breaking its row, Recent Offers titles wrapping to three and four lines, a 370px flat-zero chart where an empty state belongs, broken click-through-rate text, and roughly 420px of empty sidebar nav (407.9px to 40px). All confirmed in a running browser, all scoped to apps/admin/src/merchant so nothing leaked onto the admin portal through the shared stylesheet. (#976) bug

  • Uptime monitoring checked hosts that do not exist: Three of four checks could never pass. Two probed hostnames that do not resolve, and the third probed an HTTP health endpoint on the Discord bot, which is a gateway client with no HTTP listener. A check that can never pass teaches everyone to ignore the tab. Nine real endpoints now, with the cold-start bar set from measurement. (#976) bug

  • Profile completeness counted fields the encryption work had removed: The dashboard counts read plaintext fields that no longer exist, so they were structurally zero rather than reporting a real number. (#881) bug

  • The em dash gate stops failing on the evidence instead of the source: The failing file was a Playwright capture of what the admin portal renders, and the portal really was rendering the banned character as its empty-value placeholder. Three sessions in a row edited the capture, one of them by escaping the characters so the linter could not see them, which was reverted. Fixed at the source: six placeholders across the admin portal became the hyphen that eighteen sites already used, and generated capture output is now out of the gate's scope by rule rather than by edit. (#976) bug

  • lint:admin-zones after the financials rename: The ratchet keys on file path, so renaming a directory made eleven grandfathered violations look new. They were remapped rather than re-baselined, because a blind re-snapshot would also have swallowed a twelfth violation that was genuinely written that day. (#976) devops

  • Five review findings across the new Dashboard, Financials and System Health: The Financials Costs tab painted "Failed to load billing metrics" on every open, because it branched on the error alone while both the data and the error are null on first paint; the loading gate had existed on System Health and was lost in the move. The Billing to Financials rename left /admin/billing with no route, so the catch-all swallowed old bookmarks into the Dashboard; it now redirects, the way the sibling bigquery/console rename already did. Two Dashboard rail counts read .length off a list the server had cut at 100 and rendered it as a total; a saturated queue now reads "100+". The API cold-start allowance was unreachable, a 12s slow bar sitting behind a 10s abort, so anything between the two was recorded as a timeout and the 12s was dead code; the abort was raised rather than the bar lowered, since lowering it reintroduces the false alarm the bar was raised to fix. And the Server Errors 24h card stated a capped number as a fact while the 7 day card beside it admitted its cap; every figure now carries its own marker, and the 24h one is computed rather than copied, because a page ordered newest first still holds every error newer than its oldest entry. (#976) bug

  • The Discord notification and the AI changelog died on large payloads: A squash merge whose commit message exceeded the pipe buffer produced a SIGPIPE that killed the notification, and the same shape sat inside an if condition in the changelog workflow, where the skip check therefore read false. That workflow had been running on pushes it should have skipped, which on a private repository is money as well as correctness. (#956, #966) bug

  • The em dash guard now also scans Bash git and gh commands: A heredoc feeding a commit message or a pull request body is caught before it lands, not at pull request time. (#967) devops

  • The PM lane guard resolved paths against the main checkout: docs/projects was unreachable from any worktree, so a session in one could not write its own record. It now measures from the checkout it is standing in. (#974) bug

  • The docs build ran out of heap partway through a merge: Node heap size raised for the docs build step. (#976) devops

  • Two long dashes no static gate could see: They rendered from a gitignored generated file, which the ratchet never scans because it lists tracked files. Found by a browser and fixed at the tracked generator. Sixteen more were rewritten in strings a user reads on screen; the count came from the linter's own per-character classifier rather than a file-level grep, which had over-reported by three. (#976) bug

  • The admin test suite passed 490 tests and exited 1: Nine unhandled rejections escaped outside the assertions, so a green count read as a green run. A mock proxy threw on property access, synchronously, while Promise.allSettled was still building its argument array, and evaluation short-circuited at the second of four calls, so two further gaps were never logged. All four were fixed rather than the two the evidence named. (#976) bug

Security โ€‹

  • The admin build stopped shipping internal documents as public static assets: The new Dashboard reading column pulled its two sources in at BUILD time, the launch plan via ?raw and every changelog via a lazy glob. Admin deploys to Cloudflare Pages, which serves /assets/* as static files before any JavaScript authentication runs, so the built bundle published the launch stage table, the December 2026 and Summer 2027 targets, the business-plan and ideation-matrix document links, and 66 changelog chunks to anyone who could guess an asset path. Found and confirmed against the build rather than the source. Both sources now come from the docs API at runtime, which already serves the same files behind a role gate; the column itself is unchanged. Re-verified against a fresh build: zero hits for the document URLs, the stage-table prose and any changelog text, and total assets down from 92 to 26. (#976) security

  • The Assistant stopped sending merchant Auth UIDs to a third-party API: An allowlist entry exposed Firebase Auth UIDs in data leaving to the Claude API. Pre-existing on dev rather than introduced this week. Closed alongside seven allowlisted field names that no writer produces, verified by counting live documents rather than by reading code: venues.city returned 0 documents of 300 where addressCity returns 112. (#961) security

  • The Firestore console shipped every document id past its own allowlist: projectDoc opened the document key before the allowlist ran, so the key escaped the field-by-field review the response header promises. The key now goes through the same review as every field, rather than being filtered afterwards. (#970) security

  • A recovered schema fetch stayed broken, and a held shortcut ran unbounded queries: One cold-start 503 killed the Firestore source until reload, and holding the run shortcut fired queries past a disabled button. Measured at 65 and 87 concurrent 500-row requests on two runs; one after the fix. (#971) bug

Documentation โ€‹

  • The launch plan stops disagreeing with its own issues: Eight stale rows reconciled with evidence, the scheduled-lanterns row given its own issue instead of an unrelated one, and the privacy gap closed. ALPHA.md had cited only a tracker for "Privacy architecture in place" while two open Prototype blockers gated the same thing and were invisible from the Alpha page. (#976) documentation

  • A design survey of three surfaces, with options rather than verdicts: 30 measured defects across the app, merchant and admin surfaces, captured from a real browser. The admin Dashboard deliberately got three replacement shapes rather than a recommendation, because a landing page is a product fork and not a defect. (#976) documentation

  • Announcements, alerts and merchant dashboard parity scoped and captured: Announcements folds into the Dashboard, alerts become a Dashboard rail with a backend behind it, and the merchant report creator gets its own project record. (#976) documentation

  • An observability origin in the fireside collection: borrowed from Kalman's control theory. (#976) documentation

Built with VitePress