Skip to main content

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:

ThingWhy gone
firms / firm licenses / Entity VaultD11 — the tenant is the legal entity
Admin Workflow Studio + workflow_* tablesD8 — 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 singletonsWork-tab rework — services derived; scheduling is per inspections row
Stripe (Complied→tenant plans and tenant→client payment links)D9 + owner ruling
HQ subscription / plan pagesOwner ruling
Per-agency mirror tables (dob_violations, …)All feeds write public_events
Native mobile / IndexedDB offline field queueOPEN-6: online-only
Custom domainsD16: 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")

CapabilityRoute / 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 decisionsproject_order_decisions
Document slotsdocument_orders
Derived services / tracksservicesForTracks, Work tab
Collaboration invite pickercollaboration_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.

CapabilityState
Agency ingestsync-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 digestFunctions behave. Webhook HMAC is a simplified SHA-256, not Resend/Svix.
SMSLog stub only
Platform operators + impersonationTables + RPCs. No HQ console, no impersonate UI
XRF reuse (domain/src/field/xrf/reuse.ts)Tested logic. No UI
Instrument / lab-partner rosterTables + RLS. No Settings UI (licenses and rate cards do have one)
Final floor-plan uploaduploadFloorPlanFinal in src/data/floorPlans.ts. No button
resolve_unlinked_eventRPC exists. No quarantine dashboard
Non-XRF field reportsgenerate-paint-chip-report, generate-dust-wipe-report, generate-abatement-report, generate-cocno frontend invoker
Lab resultsNo entry UI — clearance cannot be marked passed in-app
Units createsrc/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_client flag (every doc on the client's projects is visible).
  • No client messaging / work orders.
  • Filing package has no contents view and no download (listFilingPackageDocuments has 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

FeedIn orchestrator?
HPD violations / complaints / litigationYes
DOB violationsYes
ECB violationsYes
OATH hearingsYes
DOF liensYes
NYC 311Yes
Building-facts / PLUTO seeding beyond the seed migrationYes — Layer 1 (fetchBuildingIdentities), run against prod (1,092,017 buildings rows).
DOB permits, ACRIS deeds, FDNY, DEP, DSNY-as-feedNo
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)

  1. 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.
  2. Confirm post-Rung-6 judgment calls: warn-only DESIGN-C candidates, tenant-only document RLS (no collaborator), portal-chrome-only client_branding, no shared_with_client.
  3. 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_events for real BINs.

B. Hosting and identity

  1. Cloudflare Pagesdone. Live, auto-deploys on push to main.
  2. Production Supabase project (D1: fresh schema) — deployed to and holding data (1,092,017 buildings rows, 2,150,615 public_events rows). Still confirm the real prod project ref (supabase/.temp/linked-project.json) before every supabase db push / functions deploy. Apply supabase/migrations/ in order. Never copy the archived 215-migration chain. Map migrations (20260827100000, 20260827110000) are local-only until pushed.
  3. Seed SecureEnv and Abated (migration already seeds branding for those slugs) plus real Auth users / profiles / client_users. npm run seed:dev covers licenses locally.
  4. Subdomain routing (secureenv.complied.com, abated.complied.com) — not implemented in the SPA. Today one origin, tenant from the logged-in profile.
  5. Env: VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY, Mapbox token for / and /map, never service-role in the browser.
  6. Edge functions deploy via Supabase CLI. Set verify_jwt = false only on the cron/webhook functions that already declare it (process-email-outbox, process-notification-digest, handle-email-*, and any ingest invoke pattern you keep).
  7. Storage buckets documents and branding (declared in config + migrations).

C. Jobs that must be scheduled or the system is idle

JobFunctionSuggested cadence (from old world / REQUIREMENTS)
Agency ingestsync-orchestratorScheduled — nightly pg_cron (sync-orchestrator-nightly-delta, 08:10 UTC), installed 2026-09-03; old scheduled-sync was the umbrella
Email drainprocess-email-outboxFrequent poll (minutes)
Digestprocess-notification-digestDaily, honor digest_time_of_day

Without email drain and digest scheduled, email_outbox sits at queued.

D. Email production hardening

  1. RESEND_API_KEY, verified sending domain matching tenant_branding.sender_email.
  2. Replace handle-email-suppression HMAC with Resend's real Svix signature (timestamp + tolerance). Flagged in PHASE-8-NOTES (retired — see ../history/README.md).
  3. Wire Resend bounce/complaint → suppression table.
  4. Optional: admin UI to retry failed outbox rows.

E. Proof, not hope

  1. npm run build and npm run test green (already the commit gate).
  2. cd domain && npm test (375 tests as of 2026-09-02).
  3. cd db-tests && npm test against a local instance — cross-tenant leakage is a v1 deliverable. Do not supabase db reset without asking; the local DB holds real ingested datasets.
  4. Browser pass of: login both tenants, violation-origin XRF project through Decide → Work → File → Money, portal document signed URL, branded PDF (pdftotext letterhead). Do not treat "phase walked to closed" as a gate proof — the dropdown always works.
  5. One real orchestrator run against NYC Open Data into a staging project; inspect unlinked_events and sync_health_summary. Empty public_events means untested, not finished (REQUIREMENTS §10). Fix the HPD watermark before trusting HPD-lead map pins.
  6. Confirm RLS: USING (true) only on reference tables; every SECURITY DEFINER has REVOKE EXECUTE from anon.

F. Known sharp edges to not discover in prod

  • service_role bypasses 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_id in the object key). Do not change the key layout without updating 20260823091200_documents_bucket_client_portal_rls.sql.
  • current_tenant_id() comes from the JWT / profile. Spoofing tenant_id on 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 .ts extensions on domain relative imports.
  • Lint still reports debt in archive/ and some supabase/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."