What stopped, and what production still needs
Feature work through Phase 8 is done and promoted to the repo root; Rungs 1–6, Complied Map,
the violations explorer, the Work-tab rework, and /field landed after that. Cloudflare Pages
hosting is live (auto-deploys on push to main). Schema, functions, and a nightly ingest cron
are now deployed to the real production Supabase project; what remains of Cutover is finishing the
in-progress ingestion backfill and the rest of the gaps below — see docs/history/CHANGE-LOG.md's
2026-08-26 entry.
This file is the tiered gap list between REQUIREMENTS.md / the old live app and what you can
operate today. For architecture, start at OVERVIEW.md. For "can we run a real
project?", start at docs/LIFECYCLE_ASSESSMENT.md.
Deliberately retired (do not rebuild)
These were signed off in REQUIREMENTS / the capability inventory:
| Thing | Why gone |
|---|---|
firms / firm licenses / Entity Vault | D11 — the tenant is the legal entity |
| Admin Workflow Studio + workflow_* tables | D8 — services live in TypeScript |
Phase-transition gate engine (gates.ts, advance-project-status) | Rung 6 — phase is a label; see GATING.md |
project_services + project-level scheduling singletons | Work-tab rework — services derived; scheduling is per inspections row |
| Stripe (Complied→tenant plans and tenant→client payment links) | D9 + owner ruling |
| HQ subscription / plan pages | Owner ruling |
Per-agency mirror tables (dob_violations, …) | All feeds write public_events |
| Native mobile / IndexedDB offline field queue | OPEN-6: online-only |
| Custom domains | D16: subdomain-per-tenant, and even that is not deployed |
Lovable-hosted email (LOVABLE_API_KEY) | Rebuilt on Resend + own tables |
Production blockers (must close before calling this a live replacement)
See docs/LIFECYCLE_ASSESSMENT.md §8 — the single ranked next-fix
list for the whole project, including what used to be tracked here separately.
Built in product (do not relitigate as "not rebuilt")
| Capability | Route / location |
|---|---|
| Complied Map | /map (2026-08-27). Needs loaded public_events + map migrations on the target DB. |
| Violations explorer | /violations |
| Field jobs queue + wizard | /field, /field/:inspectionId |
| Decide / Work / File / Money | /projects/:id |
| Order-path decisions | project_order_decisions |
| Document slots | document_orders |
| Derived services / tracks | servicesForTracks, Work tab |
| Collaboration invite picker | collaboration_tenant_directory() — name picker, not paste-UUID |
Landing page / still hits Mapbox + a public HPD feed for marketing. That is not the authenticated map.
Built in schema/domain, not in product
The rows exist; operators cannot run the workflow without SQL, Settings, or curl.
| Capability | State |
|---|---|
| Agency ingest | sync-orchestrator + 8 feeds + quarantine + sync_runs, plus Layer 1 (buildings identity) and Layer 2 (citywide public_events), built 2026-08-25. Now run against prod: 1,092,017 buildings rows, 2,150,615 public_events rows. Nightly pg_cron delta sync (sync-orchestrator-nightly-delta, 08:10 UTC) installed 2026-09-03, first run not yet observed; manual historical backfill still in progress. No ops UI. |
| Email send + daily digest | Functions behave. Webhook HMAC is a simplified SHA-256, not Resend/Svix. |
| SMS | Log stub only |
| Platform operators + impersonation | Tables + RPCs. No HQ console, no impersonate UI |
XRF reuse (domain/src/field/xrf/reuse.ts) | Tested logic. No UI |
| Instrument / lab-partner roster | Tables + RLS. No Settings UI (licenses and rate cards do have one) |
| Final floor-plan upload | uploadFloorPlanFinal in src/data/floorPlans.ts. No button |
resolve_unlinked_event | RPC exists. No quarantine dashboard |
| Non-XRF field reports | generate-paint-chip-report, generate-dust-wipe-report, generate-abatement-report, generate-coc — no frontend invoker |
| Lab results | No entry UI — clearance cannot be marked passed in-app |
| Units create | src/data/units.ts has no insert |
CARRY capabilities that did not survive the rebuild
REQUIREMENTS §4 (capability parity) still lists these as must-work. They are not in the promoted app.
Obligation engine (largest remaining hole)
There is no obligations table, no evaluate-obligations function, no /obligations page, no unit_rpo_records / compliance windows, no annual-notice ladder, no LL31 needs-test queue, no File-and-Resolve status diff.
The pure functions were ported (rpoNeeds.ts, ll31Status.ts, exemptionMonitoring.ts) but ComplianceProgram.obligationRules / applicability are still empty placeholders. projects.origin = 'obligation' is a string you type at create time — and without a post-create event-link UI, those projects open Decide empty.
Old live jobs this replaces: daily obligation evaluator, refresh-compliance-windows, roll-rpo-audit-year, follow-ups population.
Intelligence extras still missing
No property analyzer, Kanban operations board, deadlines cockpit, or alert-rule engine. Saved views exist on /violations (saved_views + SavedViewsMenu). Staff also see events on a building detail page after they track the building, plus /map.
Field extras
- Photo capture (before/after/safety) — no table, no wizard step. Legacy gated "mark complete" on photos.
floor_plan_revisions— deferred on purpose.
Documents / portal extras
- No per-document
shared_with_clientflag (every doc on the client's projects is visible). - No client messaging / work orders.
- Filing package has no contents view and no download (
listFilingPackageDocumentshas no caller). Assemble still auto-generates missing decided HPD docs before writing the row.
Other old-app surfaces with no rebuild equivalent
Inspector "my day" (partially replaced by /field), sales workspace, communications registry, knowledge center, charlie-chat, RoleSwitcher "preview as", document-template admin, XRF admin settings page, notary roster, insurance policies, floor-plan artist queue as a staff role. Sidebar still shows several of these as dead stubs.
Ingest coverage vs REQUIREMENTS §8.2
| Feed | In orchestrator? |
|---|---|
| HPD violations / complaints / litigation | Yes |
| DOB violations | Yes |
| ECB violations | Yes |
| OATH hearings | Yes |
| DOF liens | Yes |
| NYC 311 | Yes |
| Building-facts / PLUTO seeding beyond the seed migration | Yes — Layer 1 (fetchBuildingIdentities), run against prod (1,092,017 buildings rows). |
| DOB permits, ACRIS deeds, FDNY, DEP, DSNY-as-feed | No |
citywide-lead-nightly / pager (old live crons) | Retired shape — not ported |
Known local-load defects (map's HPD-lead filter can show zero pins even though the map works): HPD success-watermark poison, ECB SAFETY_CAP_OFFSET 550k truncation, one malformed DOB date marking a whole run partial. Details in ../compliance-ingestion-layers.md; ranked in ../LIFECYCLE_ASSESSMENT.md §8 item 9.
Before you call this production
A. Owner / product decisions (blockers if you care about parity)
- Sign off which CARRY gaps stay deferred. Map and violations UI shipped. Obligations, cron, silent $0, non-XRF reports, lab results, and units UI have not.
- Confirm post-Rung-6 judgment calls: warn-only DESIGN-C candidates, tenant-only document RLS (no collaborator), portal-chrome-only
client_branding, noshared_with_client. - Confirm D17: re-derive public data from feeds at cutover; migrate only tenant-authored rows. Owner already noted the old app was never actually live for real users — migration may be a no-op, but ingest still has to fill
buildings+public_eventsfor real BINs.
B. Hosting and identity
Cloudflare Pages— done. Live, auto-deploys on push tomain.- Production Supabase project (D1: fresh schema) — deployed to and holding data (1,092,017
buildingsrows, 2,150,615public_eventsrows). Still confirm the real prod project ref (supabase/.temp/linked-project.json) before everysupabase db push/functions deploy. Applysupabase/migrations/in order. Never copy the archived 215-migration chain. Map migrations (20260827100000,20260827110000) are local-only until pushed. - Seed SecureEnv and Abated (migration already seeds branding for those slugs) plus real Auth users /
profiles/client_users.npm run seed:devcovers licenses locally. - Subdomain routing (
secureenv.complied.com,abated.complied.com) — not implemented in the SPA. Today one origin, tenant from the logged-in profile. - Env:
VITE_SUPABASE_URL,VITE_SUPABASE_ANON_KEY, Mapbox token for/and/map, never service-role in the browser. - Edge functions deploy via Supabase CLI. Set
verify_jwt = falseonly on the cron/webhook functions that already declare it (process-email-outbox,process-notification-digest,handle-email-*, and any ingest invoke pattern you keep). - Storage buckets
documentsandbranding(declared in config + migrations).
C. Jobs that must be scheduled or the system is idle
| Job | Function | Suggested cadence (from old world / REQUIREMENTS) |
|---|---|---|
| Agency ingest | sync-orchestrator | Scheduled — nightly pg_cron (sync-orchestrator-nightly-delta, 08:10 UTC), installed 2026-09-03; old scheduled-sync was the umbrella |
| Email drain | process-email-outbox | Frequent poll (minutes) |
| Digest | process-notification-digest | Daily, honor digest_time_of_day |
Without email drain and digest scheduled, email_outbox sits at queued.
D. Email production hardening
RESEND_API_KEY, verified sending domain matchingtenant_branding.sender_email.- Replace
handle-email-suppressionHMAC with Resend's real Svix signature (timestamp + tolerance). Flagged in PHASE-8-NOTES (retired — see../history/README.md). - Wire Resend bounce/complaint → suppression table.
- Optional: admin UI to retry
failedoutbox rows.
E. Proof, not hope
npm run buildandnpm run testgreen (already the commit gate).cd domain && npm test(375 tests as of 2026-09-02).cd db-tests && npm testagainst a local instance — cross-tenant leakage is a v1 deliverable. Do notsupabase db resetwithout asking; the local DB holds real ingested datasets.- Browser pass of: login both tenants, violation-origin XRF project through Decide → Work → File → Money, portal document signed URL, branded PDF (
pdftotextletterhead). Do not treat "phase walked to closed" as a gate proof — the dropdown always works. - One real orchestrator run against NYC Open Data into a staging project; inspect
unlinked_eventsandsync_health_summary. Emptypublic_eventsmeans untested, not finished (REQUIREMENTS §10). Fix the HPD watermark before trusting HPD-lead map pins. - Confirm RLS:
USING (true)only on reference tables; everySECURITY DEFINERhasREVOKE EXECUTEfromanon.
F. Known sharp edges to not discover in prod
service_rolebypasses RLS but still needs table GRANTs — every phase that forgot this 403'd. New tables need the same grant migration pattern.- Client portal Storage RLS is path-derived (
tenant_id/project_idin the object key). Do not change the key layout without updating20260823091200_documents_bucket_client_portal_rls.sql. current_tenant_id()comes from the JWT / profile. Spoofingtenant_idon insert is blocked by triggers on later tables (sync_tenant_id_from_project/_from_client). Do not add a tenant-owned table without that trigger.- Deno edge functions need
.tsextensions on domain relative imports. - Lint still reports debt in
archive/and somesupabase/functions/— do not treat archive failures as app failures.
Suggested order of work from here
See docs/LIFECYCLE_ASSESSMENT.md §8 — the single ranked list,
covering what used to be tracked here separately.
The rebuild replaced architecture. Map and violations shipped. Cutover is still "schema + cron + real data on the real project," not "build the intelligence UI first."