3 Tech Debt
shad0w edited this page 2026-06-22 23:40:08 +05:30

Tech Debt

A running register of known shortcuts, dormant code paths, and design/implementation mismatches that we've consciously accepted for now. The goal is visibility: each item records what, why it matters, and a suggested direction — not a commitment to fix on any timeline.

Add new items at the top of the "Open" list with a short, honest description. Move resolved items to "Resolved" with the commit/PR that closed them.

Open

TD-4 · Crewing recruitment vetting gates only partially enforced

What. The 7-stage pipeline (Implementation Spec §5.1) now enforces C5 (≥1 reference before leaving COMPETENCY_AND_REFERENCES) and a partial C3 (an expired mandatory document for the seat's rank blocks verifyDocuments). Two gaps remain:

  • C3 "missing required document" is not hard-enforced in the pipeline. Seafarer documents are collected on the crew profile after onboarding (Phase 4a), so a candidate usually has none on file mid-pipeline; a presence check would stall the funnel. Presence is enforced post-onboarding in the verification queue (§8.11). Once careers intake (A2) uploads documents pre-onboarding, tighten verifyDocuments to require every mandatory docType to be present.
  • C4 experience check is deferred: Requisition has no min-experience criteria field (see A2 AC1). When it lands, compare the candidate's ExperienceRecord total against it in verifyDocuments and flag a shortfall.

Touch points. App/app/(portal)/crewing/applications/actions.ts (advanceStage, verifyDocuments); App/prisma/schema.prisma (Requisition min-experience, future).

TD-3 · Inventory increments are not reversed when a PO is cancelled

What. Approving a PO with a siteId increments ItemInventory (approvals/[id]/actions.ts). The cancel feature (#53) intentionally does not reverse that increment, so a cancelled approved PO can leave stale stock behind.

Why it matters. Low impact today — inventory writes almost never fire (see TD-1) and the surface is feature-flagged off — but it becomes a correctness bug the moment inventory-on-approval is brought live.

Suggested direction. When inventory goes live, cancelPo should decrement the same ItemInventory quantities for line items with a productId, guarding against double-reversal and negative stock. Tracked as Forgejo issue #55; deferred per the #53 answers.

Touch points. app/(portal)/po/[id]/actions.ts (cancelPo), app/(portal)/approvals/[id]/actions.ts (the increment), lib/feature-flags.ts.


TD-1 · Inventory-on-approval is dormant in production

What. Approving a PO is meant to add its ordered items to the delivery site's stock (ItemInventory, keyed by (productId, siteId)). In production the write almost never fires, and it isn't governed by the inventory feature flag.

Why it matters.

  • POs are raised against a Vessel (cost centre), and PO forms set vesselId, never siteId. The if (po.siteId) guard in approvePo() is therefore almost always false → no inventory is written. Inventory screens render (the flag defaults on) but stay empty.
  • The ItemInventory upsert is not gated by NEXT_PUBLIC_INVENTORY_ENABLED — the flag only hides UI. So "inventory off" would still mutate data if a PO ever did carry a siteId.
  • Line items are counted only when they already have a productId, but products are linked/created at payment, not approval — so hand-typed items would be skipped even with a site.
  • Root cause: inventory is modelled per Site, but POs are per Vessel, and nothing maps a PO to a Site.

Suggested direction. Decide one: (a) give POs a Site (vessel→home-site link or an explicit delivery-site field) and link products at approval; or (b) gate the write on the flag / remove it and set NEXT_PUBLIC_INVENTORY_ENABLED=false in prod to hide the empty screens. Full write-up: Inventory on Approval.

Touch points. app/(portal)/approvals/[id]/actions.ts (the upsert), lib/feature-flags.ts, PurchaseOrder.siteId, payments/actions.ts (syncProductCatalog).


TD-2 · Migrations are not coupled to the build

What. pnpm build runs prisma generate (TypeScript client) but not prisma migrate deploy. Shipping code whose client expects a new column before the DB has it throws P2022 … column does not exist at runtime.

Why it matters. This already caused a production incident (the paymentDate column). The deploy workflow (.forgejo/workflows/deploy.yml) does run migrate deploy, but manual deploys can skip it.

Suggested direction. Treat "apply migrations before the new build serves traffic" as the invariant. Options: fold prisma migrate deploy into the start/release script, or add a pre-deploy check that fails if migrations are pending. Documented in the App README.md.

Touch points. App/package.json scripts, .forgejo/workflows/deploy.yml, App/README.md.

Resolved

None yet.