Skip to content

Doc drift

The numbered files under Deployment are normative. They were originally mirrored into all three application repos; today no repo carries a copy — each repo's own deploy/RUNBOOK.md supersedes them and ends with a deviation table, and the runbook wins. Only the files under docs/main/ are still copied into the repos.

Facts that CR round 3 (2026-09-10) made false in these files — the wizard's step list, the dropped work-window fields, the round-3 feature flags — were corrected in place on that date, because a doc describing behaviour the code no longer has is worse than no doc.

Facts that CR round 4 (2026-09-11) made false were corrected in place the same way, on that date, across this site: the two Area flags in the backend feature-flag table (PERMIT_AREA_REQUIRED, AREA_VISIBILITY_SCOPED — deleted with Area; PPE_REQUIRED added), the contractor verification steps that pasted a map link, and the safety verification step that expected ENTRANTS_STILL_INSIDE on closure. The removed concepts are named as removed on each page rather than silently dropped. The round-4 summary lives on Current state.

A second round-4 pass (2026-09-12, wayfinder 114) corrected the site again for everything that landed after the first pass: 098's contractor and safety closure halves (the closure checklist is gone, request/approve/close-directly is real), 101's safety half (a visit can start from history), 108 step 3 (risk-map attention markers), 109 (the socket service — useSocket.ts is live, not vestigial), 110's safety and inspector menu halves, and 112's safety half (the Report tab exists in both apps). See Current state for the fuller table.

An owner-filed-fixes pass (2026-09-13) corrected the site for a batch outside any formal CR round — the owner tested the deployed app directly and filed issues, each grilled individually. Only two changed something this site already described: ticket 101's history-start time bound is reversed (any prior scan now qualifies, no 12-hour/work-window cap — data-model.md, permit-lifecycle.md, CONTEXT.md's PermitScan/SCAN_WINDOW_EXPIRED entries), and preWorkChecklist is a genuinely new cross-repo term (CONTEXT.md, data-model.md's ER diagram). Everything else in that batch — inspector UX cleanup, risk-map sizing/legend/pulse, a clickable review-queue notification, full-permit print/export in both frontends, an audit-label gap, two height-overflow fixes — was new-only or a straightforward bug fix, nothing here to correct. All of it is on dev across all three repos (api PR #13, contractor PR #7, safety/inspector PR #8) as of this writing, not yet merged to main — treat this page's account of it as the intended state once merged, not yet the deployed one.

A second owner-filed-fixes pass (2026-09-13) covered a new batch, same outside-any-CR-round process as the first: the owner exercised the deployed app again and filed issues, each grilled individually rather than run through wayfinder. Three are genuine bugs, found but not yet fixed as of this writing — treat them as known-bad until a later pass records them corrected: the safety app's permit-list CSV export (usePermitsCsv.ts) never prepends a UTF-8 BOM, so Excel opening the file guesses a legacy codepage and renders Thai text as mojibake (the contractor app's own CSV export, src/utils/Csv.ts, already does this correctly and is the fix's reference); the permit Report tab's entrant timeline (usePermitReport.ts's entrantEvents filter, both frontends) only recognises ENTRANT_CHECKED_IN/ENTRANT_CHECKED_OUT, so a worker marked not available with a reason never appears there — it already appears correctly in the generic Audit Trail tab, only the Report tab's narrower filter drops it; and the inspector's mid-visit flow (InspectorVisitPage.vue) only checks for an expired scan session (SCAN_WINDOW_EXPIRED) in its initial startVisit() call, not in any of the actions taken once a visit is already under way, so a session that expires mid-visit fails without the "scan again" message the initial check already has.

The rest of the batch is new work, not a correction, so this page says nothing about it until it ships: a permit-expiry warning and an in-app extend/request-close shortcut for the contractor when a permit's work window is about to or has already passed with the permit still open; a real notification bell (rather than today's single-page banner and toast) that distinguishes a close-request or emergency/incident notification by type; and, on the safety review-detail page, a page-level alert card surfacing Hot Work fire-watch/closure status outside its own tab. None of the three exists in the app yet — this page will describe them once they land, the same way the Print / Export PDF button and pre-work checklist above are marked "on dev, not yet merged" rather than written as if already shipped. The permitStatus filter on /safety/permits was also reported in this batch but could not be reproduced against either frontend or backend code (both are correctly wired and covered by passing tests) — it needs a repro from the owner before anyone touches it, and this page makes no claim about it either way.

Not corrected, and still asserting the old model: the files under docs/main/ (the dev-handoff set copied into the three repos). They are partly updated — closure is correct in some places — but still describe the area routes, the permit coordinate and, in places, the foreman's closure. They are governed by CONTEXT.md's copy rule and by ticket 092, which exists because the repo copies have diverged from the root — so they are left for that ticket rather than edited here.

What follows is the older list of places where the deployment files no longer match production, so you can decide what to correct deliberately.

FileSaysReality
10-DEPLOYMENT-OVERVIEW.md §6VM env lives at /opt/swp/.env/opt/esw/.env — see the compose header and deploy.yml
10-DEPLOYMENT-OVERVIEW.md §5CORS_ORIGIN is exactly app.<domain> + safety.<domain>Four origins, including both *.pages.dev — see Current state
10-DEPLOYMENT-OVERVIEW.md §6Secrets table lists VM_HOST, VM_SSH_KEY, GHCR_TOKENdeploy.yml also consumes secrets.VM_USER
docker-compose.prod.yml comment"tuned for the 1 GB Oracle E2.1.Micro"The host is a DigitalOcean droplet
12- / 13-…-deployment.mdDeploy step uses cloudflare/wrangler-actionBoth workflows now call bunx [email protected] pages deploy directly — the action swallowed wrangler's stderr and hid two failures in a row
cloudflare/tunnel.mdWas a four-line scratch file containing a live tunnel tokenRewritten as a real page with the token removed

On that tunnel token

The file was never committed and the umbrella repo has no remote configured, so the token did not leave the machine. Rotate it anyway if you are unsure — it costs one dashboard click, and the file has now been overwritten so a future git add . will not capture it.

Not drift, but worth knowing

  • 10-DEPLOYMENT-OVERVIEW.md §7 says there is no staging environment and no horizontal scaling of api. Both still hold. prisma migrate deploy runs on container start, so a second replica introduces a migration race — read 11-backend-deployment.md §9 first.
  • §7 also says there are no push notifications. No longer true as of wayfinder 109 (2026-09-11): both frontends now open a live WebSocket (GET /api/v1/realtime) carrying badge counts and notification events, with GET /notifications / GET /v1/badges polling kept only as the fallback when the socket is down. See Applications — "Dead scaffolding" no longer applies to useSocket.ts.