Skip to content

Open items — what needs a human

A deliberately short list of everything currently waiting on a person rather than on code. Two categories, and the distinction is the point:

  • Needs a decision — nobody can build it until someone with authority answers a question.
  • Needs an action — the answer is known; someone has to do it somewhere code cannot reach (a DNS record, a credential, a merge).

Anything not on this list is either done or is ordinary work that an implementer can pick up unblocked. The authoritative tracker is docs/wayfinder/ in the workspace repo; this page is the human-facing summary of it.

Last reconciled against the tracker: 2026-09-12 (wayfinder 114, after 098's frontends, 101's safety half, 108 step 3, 109 and 110's safety/inspector halves, and 112's safety half all landed).

Needs an action

Storage credentials are wrong in production

Symptom a user sees: photos cannot be attached to a permit.

MINIO_ACCESS_KEY_ID / MINIO_SECRET_KEY in /opt/esw/.env do not authenticate against the storage the API is pointed at. The endpoint now fails honestly — it answers 503 STORAGE_UNAVAILABLE rather than a misleading 400 — but it still fails. No code change is available for this; it is a deployment credential.

Worth checking before assuming it is a password: in the shipped compose file MinIO's own root credentials are set from these same two variables, and the bucket-creation step would fail loudly if they were wrong. If the bucket exists, they authenticate — and the fault is more likely the API's configured endpoint than the credential. The API points at a public hostname while the container is reachable directly on the internal network, and an S3 signature covers the Host header.

Two API hostnames, to split the app sessions

Signing into the safety app currently signs you out of the contractor app in the same browser profile, because one apex-scoped cookie is one session. The API half of the fix is shipped; the rest is infrastructure:

  1. A DNS record and Cloudflare Tunnel ingress rule for api-safety.e-safework.com, routed to the same container. nginx.conf needs no new vhost — it proxies everything on port 80 to api:3000 regardless of host.
  2. The safety app's VITE_APP_API_URL repository variable pointed at that hostname. It comes from vars.VITE_APP_API_URL, so this is a settings change, not a commit.
  3. On the deployment: unset COOKIE_DOMAIN, set COOKIE_SAMESITE=Lax.

Confirm one thing in a browser first. The change rests on host-only cookies being sent on same-site XHR with SameSite=Lax. That should hold — app. and api. share a registrable domain — but the code previously assumed the opposite, and if the old assumption is right, the alternative (SameSite=None) is one a privacy-mode browser may drop mid-shift. See the cookie layouts.

Convert the facility plan to PNG before uploading

Plan uploads are raster-only: PNG, JPEG and WebP. PDF and HEIC are refused at the route, on purpose — the map draws with a plain <img>, and a bad rasterisation would become a permanent plan on a safety map. Whoever supplies the plan exports it, or photographs the printed copy and uses the four-corner correction in the upload flow.

Name the plans and place the pins — deliberately, and on a chosen day

Round 4 deactivated every existing facility plan and dropped every old permit position, so production has no active plan and no pins. Nothing on the risk map, and no pin required from any contractor, until safety names a plan, activates it and places pins. The third of those acts is also what switches on PERMIT_POSITION_REQUIRED for every contractor's submit — so do it on a day contractors have been told, not as an experiment on production.

Tell the inspectors their notes are now contractor-visible — built, still worth a briefing

Built 2026-09-12 (112's safety half, dfe4e96e): the notice now ships on the inspector's getting-started page and beside the note field on InspectorVisitPage. Kept here as a reminder that the in-app notice is not a substitute for the person-to-person heads-up before the next deploy — an inspector who only reads it after writing a candid note has already lost the thing this was meant to prevent.

Check production for Gas Testing certificates

Round 4 dropped Gas Testing from the certificate types — each type now matches exactly one permit type. Development held no such certificates; production was never checked, because the session that made the change could not reach it. A Gas Testing row would keep working as a record but would satisfy no permit once type enforcement is on. Count them before anyone switches CERT_TYPE_REQUIRED on.

Ship the API and the safety app together

The API's new PPE checklist shape (round 4, ticket 120) refuses the inspector app's old one with PPE_CHECKLIST_EMPTY. Loud rather than silent, but still a field outage for every inspector submitting a visit: the API must not reach production ahead of the safety app.

Back up off-box before any migration-carrying deploy

backup.sh runs nightly, and its own comment notes that both the database dump and the object-store mirror live on the same disk. Before a deploy carrying migrations, take a fresh backup and copy it somewhere else. Prisma has no down-migrations: that dump is the only real undo.

Needs a decision

When to switch certificate-type enforcement on

This is the one with a safety consequence. By default a worker's certificate type decides nothing — the system checks only that they hold some unexpired certificate, so a card of the wrong kind satisfies a hot-work permit. The list of types is now settled and built: exactly three, one per permit type (Hot Work, Confined Space Entry, Working at Heights — Gas Testing was dropped in round 4), offered as a fixed choice on every certificate form. The gate is built at both the submit check and the entrant scan. It stays off until a deployment sets CERT_TYPE_REQUIRED.

What is still needed from a person is a pass over the existing certificate records — including the production Gas Testing count above — and then the decision to switch it on. The moment it is on, a worker whose recorded type does not match is blocked from a permit that works today.

Whether PPE declaration becomes mandatory

Round 4 added a PPE declaration to the contractor's step 5 and made it optional: an empty declaration submits. Setting PPE_REQUIRED makes an empty one a refusal at submit. It is off by default for the same reason as the certificate gate — a rule that can refuse work must be switched on deliberately, once contractors are actually declaring.

What a tabbed detail page should look like — decided and built

Round 4 ruled it (2026-09-11): tabs everywhere, except urgent and notification-related state, which stays a fixed strip above the tabs. That narrows the earlier "every safety-critical section stays outside the tabs" ruling to what is alarming right now — an emergency note, a live overlap, an overdue gas reading — so the normal case is a clean tabbed page. Both apps' detail pages were converted the same day.

Not waiting on anyone

Recorded here so nobody mistakes them for blockers. These are specified and simply not built yet — but note the last column: two of them have no open ticket, because the ticket that described them was closed on the strength of one half.

Everything that was on this table for CR round 4 is now built, except the one item below — checked against each ticket's own resolution, not assumed from the map.

ItemWhat it isTicket
Offline replays keep their real timeA queued check-in/out or gas reading is recorded at sync time, not when it happened126

Closed since the last reconciliation: contractor "request close" and the officer's close-request queue (098, both frontend halves); the safety and inspector menu cut (110, closed to five/three items); contractor reads inspector visits (119's UI, via 112's Report tab); start a visit from history (101's safety half); risk-map attention markers (108 step 3's safety half); the socket service and live badges (109, all three halves); the permit report / print view (112, both frontend halves).

Where the full record lives

  • docs/wayfinder/map.md (rounds 1–2), map-permit-ux-and-inspector.md (round 3) and map-round-4-pins-closure-and-the-inspector-menu.md (round 4) — every decision made, with the reasoning and the rejected alternatives.
  • docs/wayfinder/tickets/ — one file per item, each carrying what it got wrong as well as what it shipped.
  • Current state & blockers — what is live and proven, from the deployment side.