That much is in the code. What is not in the code is which walls were already
walked into, and that is what this file is for.
**This is a record of *then*, never a description of *now*.** That distinction is
what makes it safe to leave alone as the project changes — a history document
that gets edited to stay current is not a history, it is a second and competing
description of the present.
If you find yourself updating a sentence here because the code changed, the
sentence belongs somewhere else.
## Decisions and their reasons
| When | What was decided | Why, at the time |
| --- | --- | --- |
| 2026-05-11 | Vite + React SPA, explicitly **not** Next.js | The rebuild needed a fast client and a small Express API, not SSR or server components. `docs/planning/REQUIREMENTS.md` states it as a requirement rather than a preference, because it kept getting re-proposed |
| 2026-05-11 | better-sqlite3 rather than a hosted database | Two forms and two tables. A managed database would have been more infrastructure than the product has data |
| 2026-05-12 | Light-first B2B layout with strategic dark navy, evolving the palette rather than replacing it | `docs/design/REDESIGN_REVIEW.md`: the original read as a developer portfolio, and B2B buyers decide on trust in seconds. Rip-and-replace to a light theme would have thrown away the navy brand equity along with the problem |
| 2026-05-13 | Zoho forwarding is fire-and-forget, behind SQLite | SQLite always writes first. A CRM outage must never cost a lead — the local row is the record and Zoho is a best-effort overlay |
| 2026-05-17 | The standalone `/8x8` page was merged into the UCaaS and CCaaS service entries | Issue #32. A partner page nobody navigated to, when the partnership is better evidenced inside the services it actually powers |
| 2026-06-14 | Zoho switched from the REST/OAuth path to **WebToLead** as the default mode | `ZOHO_FORWARDING_MODE=webtolead`. The OAuth path works and is kept as a standby; WebToLead needed no token lifecycle for what is a single lead form |
| 2026-08-01 | Every route is prerendered at build time, and the privacy policy has a server-rendered fallback | Meta's crawler does not run JavaScript. See `docs/architecture/README.md` |
| 2026-08-18 | Adopted the project template; **retired phase-versioning** | See below |
| 2026-08-18 | **`queuenorth.com` is the permanent public origin**, with `qn.isnull.dev` kept as a second ingress to the same container | Asked and answered by `_null` while closing out the template adoption. It settles #212 and it corrected a mistake made earlier the same day: the first draft of `docs/OPERATIONS.md` asserted `queuenorth.com` was *not* this deployment, written from a DNS lookup rather than a request. Both hostnames serve the identical bundle and this server's own `/api/health` shape; `24.41.108.95` is this network's own public IP |
| 2026-08-18 | The Forgejo repository was made **private** | Adoption committed the previously-gitignored internal documents into `docs/`. Those hold the agent narrative and internal findings about the client's own marketing claims (#108, #110), which is not material for a public tree. `null/fruit-fall` was already private and reports to the Command Center normally, so nothing was lost by it |
## What was tried and dropped
### Phase-versioning — the minor number *was* the phase number
`PROJECT.md` and `STRUCTURE.md` both carried the rule: Phase 1 releases are
`0.1.x`, Phase 5 releases are `0.5.x`, and *"do not use unrelated semantic
version bumps during the overhaul"*. Bishop was made responsible for enforcing it
across `package.json`, `PROJECT.md` and `HISTORY.md`.
**It drifted twice, and the second time nobody noticed for two and a half
months.** At adoption `package.json` said `0.8.3` while the last four commit
subjects said *batch 0.9.0* through *batch 0.9.3*, and both `PROJECT.md` and
`STRUCTURE.md` still described the project as being in Phase 5.
The rule was not wrong; it was unenforceable. It asked four files to agree, with
a human check as the only mechanism, and a rule that needs a person to remember
it is a rule that decays. Milestones in the tracker now hold what the phase
number was carrying — a batch is a milestone, and closing it is what marks the
batch shipped — so the version number no longer has to encode anything.
`package.json` was corrected to `0.9.3` on adoption to match the commits.
**What replaced it:** nothing, deliberately. Version numbers are now just version
numbers. `docs/WORK_CYCLE.md` holds what happens at the end of a piece of work.
### The markdown backlog
Work was planned in `FUTURE.md`, with a parallel changelog in `HISTORY.md`, an
agent log in `DEVELOPMENT_LOG.md`, phase checklists in `PROJECT.md` and
`README.md`, and 205 issues in Forgejo. Six records of what was happening, and
the tracker — the one with atomic state and real timestamps — was the one nothing
pointed at.
The specific damage: 205 issues sat in **zero** milestones, so privacyllc.dev
reported the project as *not adopted* rather than as 87% complete, and the
severity labels were spelled `P0 Critical` / `P1 High` / `P2 Medium`, which no
query matches.
The markdown records were retired on 2026-08-18. What was reasoning is now in
`docs/history/BATCH_LEDGER.md`; what was work is in the tracker.