================================================================================ Gate 3a — Dead Code Node: W13_gate_dead_code Ticket: #3248 (oms-ui-ux-and-functional) Branch: feat/3248-oms-ui-ux-and-functional Repo: cross-repo-orchestration/oms-web (Next.js 14 App Router / Tailwind) Result: PASS — 0 items removed ================================================================================ SUMMARY ------- 55 entries in sides.web.feature_files were scanned across the 7 in-scope dead-code categories. No provably dead code was found, so nothing was removed. Five items are REPORTED (not deleted) under the node instruction "report what you are unsure about instead of deleting it" and the gate's Step 2 "report, don't delete" rules: one redundant export modifier, two design tokens carried over from #3247 with zero current call sites, one component variant reachable only from tests, one unused barrel file, and the two pre-existing orphaned schema column groups this gate has flagged on every prior ticket. The tree is byte-for-byte unchanged by this gate apart from this evidence file (`git diff --stat` empty; `git status` shows only untracked gate-evidence files from W11/W12 and this one). FILES SCANNED (55, verbatim from sides.web.feature_files) --------------------------------------------------------- All 55 paths were confirmed to exist on disk (0 missing). app/admin/items/[id]/edit/page.tsx app/admin/uom/[id]/edit/page.tsx app/admin/items/[id]/page.tsx app/admin/uom/[id]/page.tsx app/admin/items/new/page.tsx app/admin/uom/new/page.tsx app/admin/items/page.tsx app/admin/uom/page.tsx app/admin/projects/[id]/edit/page.tsx app/admin/users/[id]/edit/page.tsx app/admin/projects/[id]/page.tsx app/admin/users/[id]/page.tsx app/admin/projects/new/page.tsx app/admin/users/new/page.tsx app/admin/projects/page.tsx app/admin/users/page.tsx app/admin/suppliers/[id]/edit/page.tsx app/expense-summary/[project]/page.tsx app/admin/suppliers/[id]/page.tsx app/expense-summary/page.tsx app/admin/suppliers/new/page.tsx app/expenses/ExpenseRowActions.tsx app/admin/suppliers/page.tsx app/expenses/[id]/edit/page.tsx app/expenses/new/page.tsx app/expenses/page.tsx app/orders/OrderRowActions.tsx app/orders/[id]/page.tsx app/orders/new/page.tsx app/orders/page.tsx components/forms/ExpenseForm.tsx components/forms/ItemForm.tsx components/forms/OrderForm.tsx components/forms/OrderFulfillmentForm.tsx components/forms/ProjectForm.tsx components/forms/SupplierForm.tsx components/forms/UomForm.tsx components/forms/UserForm.tsx components/ui/DataTable.tsx components/ui/FilterBar.tsx components/ui/ListToolbar.tsx components/ui/QuickCreateModal.tsx components/ui/RowActions.tsx components/ui/StatusBadge.tsx components/ui/fields/SelectField.tsx components/ui/fields/TextField.tsx components/ui/icons/SortIcon.tsx tailwind.config.ts docs/design/oms-screens-standalone.html docs/design/users-screens-standalone.html docs/specs/ticket-3248-design.md tests/admin-items-page.test.tsx tests/admin-users-page.test.tsx tests/expenses-page.test.tsx tests/item-form.test.tsx tests/quick-create-modal.test.tsx The three docs/ entries (2 design HTML bundles + 1 spec markdown) are reference inputs, not compiled sources — no export/import/route/schema surface, so categories 1-2 and 4-6 do not apply to them. They were checked for category 3 and 7 only (see CHECK 3 / CHECK 7). ================================================================================ CHECK 1 — Unused imports / unused local declarations ================================================================================ Method: compiler-authoritative, not grep. $ npx tsc --noEmit --noUnusedLocals --noUnusedParameters \ --incremental false -p tsconfig.json (no output) EXIT=0 Detector sanity-check (proving the flags were live and not silently satisfied by a stale incremental cache) — a throwaway unused const was appended to lib/params.ts and the same command re-run: lib/params.ts(67,7): error TS6133: '__deadcheck' is declared but its value is never read. Reverted immediately via `git checkout lib/params.ts`; `git diff --stat lib/params.ts` confirms the file is unmodified. tsconfig.json's `include` is `**/*.ts` / `**/*.tsx`, so this pass covers app/, components/, lib/, tests/, middleware.ts and prisma/ — a strict superset of the 52 compiled feature files. RESULT: 0 unused imports, 0 unused locals, 0 unused parameters. ================================================================================ CHECK 2 — Exported symbols never imported elsewhere ================================================================================ Every top-level `export` in the 52 compiled feature files was enumerated, then cross-referenced against app/, components/, lib/, tests/, middleware.ts and prisma/. Tests were included deliberately — the gate forbids deleting anything referenced only by a test. symbol refs referencing files ---------------------- ---- --------------------------------------------- ExpenseRowActions 8 self, app/expenses/page.tsx, test OrderRowActions 6 self, app/orders/page.tsx, test ExpenseForm 37 7 files ItemForm 46 14 files OrderForm 28 7 files OrderFulfillmentForm 27 6 files ProjectForm 39 10 files SupplierForm 44 15 files UomForm 38 10 files UserForm 35 8 files Column (type) 22 11 files DataTable 30 13 files FilterField (type) 6 self, ListToolbar.tsx, test FilterBar 17 5 files ListToolbar 28 12 files QuickCreateModal 25 12 files RowActions 35 14 files StatusBadge 25 10 files SelectField 13 self, Listbox.tsx, FilterBar.tsx, test TextField 32 8 files SortIcon 9 self, DataTable.tsx, test StatusBadgeVariant (type) 2 self only <-- see REPORTED #1 Framework-contract exports (Next.js resolves these by convention, never by import, so a zero-importer count is expected and correct): - `export default` in all 30 page.tsx files - `export const metadata` in 26 page.tsx files - `export async function generateMetadata` in app/expense-summary/[project]/ page.tsx and app/orders/[id]/page.tsx - `export default config` in tailwind.config.ts (read by tailwindcss/postcss) RESULT: 0 dead exports removed. 1 redundant export modifier reported. ================================================================================ CHECK 3 — Commented-out code blocks (>3 lines) ================================================================================ Searched app/, components/, lib/ for runs of `//`-prefixed lines opening with a code token (const|let|var|function|return|import|export|if|<|{|}): lib/actions/expenses.ts:31 "// constraint requires exists by construction..." lib/permissions/ui.ts:15 "// import this file — it pulls in Prisma..." lib/auth/password.ts:5 "// constraint so the same code works in both..." All three are prose sentences that happen to begin with a keyword-like word; none is commented-out code. None is in a feature file. Separately checked, and NOT counted as commented-out code: - prisma/schema.prisma lines 112-119 — the 8-line comment block replacing the dropped `Item.modelNo` field. This is a deliberate tombstone recording the W03_schema_review C1 tier ruling (and explicitly warning that Uom.symbol / Supplier.supplierNo are gated on a pg_dump confirmation that has NOT landed). It is documentation of a removal, not dormant code awaiting re-enablement. - docs/design/*.html — reference design bundles; HTML comments in a vendored design artifact are not this repo's source. RESULT: 0 commented-out blocks. ================================================================================ CHECK 4 — Dead barrel (index.ts) re-exports ================================================================================ The repo has exactly one barrel: lib/permissions/index.ts (not in feature_files). export * from "./registry"; -> lib/permissions/registry.ts EXISTS export * from "./evaluate"; -> lib/permissions/evaluate.ts EXISTS export * from "./guard"; -> lib/permissions/guard.ts EXISTS The gate defines a dead barrel entry as a re-export "pointing at deleted/renamed files". All three targets exist, so there is no dead entry to remove. REPORTED #4 (unchanged from the #3247 run): the barrel file itself has zero importers — every consumer imports the submodule directly. Deleting the whole file is a public-API decision about a module this ticket did not touch. RESULT: 0 barrel entries removed. ================================================================================ CHECK 5 — Orphaned App Router segments ================================================================================ All 33 route files under app/ were enumerated, then every route in feature_files was matched against static hrefs, template-literal hrefs, redirect()/ router.push() targets, and the permission-filtered nav table in lib/nav.ts. route family literals found in source ------------------ -------------------------------------------------------- /admin/items "/admin/items", "/admin/items/new", `/admin/items/${id}`, `/admin/items/${id}/edit`, `/admin/items?${p.toString()}` /admin/projects same five shapes /admin/suppliers same five shapes /admin/uom same five shapes /admin/users same five shapes /expense-summary "/expense-summary", `/expense-summary/${r.projectId}`, `/expense-summary/${routeParams.project}?...` /expenses "/expenses", "/expenses/new", `/expenses/${id}/edit` /orders "/orders", "/orders/new", `/orders/${id}`, `/orders/${orderId}`, `/orders/${r.id}` Every one of the 30 feature-file route segments — including all five newly-added `[id]` view pages and all five `[id]/edit` pages per master, plus app/orders/[id]/page.tsx — is reached by at least one link or nav entry. Side observation (NOT a dead-code finding, no action taken): lib/actions/ expenses.ts:98 calls `revalidatePath("/expenses/${id}")`, but there is no app/expenses/[id]/page.tsx — Expenses has an edit route only, as ExpenseRowActions.tsx's own comment states. A revalidatePath against a non-existent path is a harmless no-op, and lib/actions/expenses.ts is not in feature_files. Recorded here so it isn't lost, not fixed by this gate. RESULT: 0 orphaned route segments. ================================================================================ CHECK 6 — Unused Prisma schema models / fields ================================================================================ Scoped first to what THIS ticket changed (git diff c9e1fbf..HEAD -- prisma/): prisma/schema.prisma 10 +/- migrations/20260830060000_remove_item_modelno/ 19 + prisma/seed.ts 4 +/- This ticket ADDS no schema field. Its only schema change is a REMOVAL — `Item.modelNo` (ticket #3248 req 14) — i.e. this ticket already deleted the one piece of orphaned schema in its own scope, with a migration, before this gate ran. Verified there is no stranded reference to it: $ grep -rn modelNo app components lib tests prisma tests/schemas.test.ts:100-103 regression test asserting itemSchema no longer carries modelNo (intentional, keep) migrations/20260825145455_oms/migration.sql:53 original CREATE (history) migrations/20260830060000_remove_item_modelno/* this ticket's DROP + its documented rollback note No app/ or components/ reference remains, and `tsc --noEmit` is clean — a leftover `item.modelNo` read would be a compile error, not a silent survivor. Earlier-branch fields (User.mobile, Attachment.updatedAt @default, Role @@unique([id, userType]), User's composite role FK, the two User indexes) were re-verified as still fully wired by lib/queries/users.ts, lib/validation/ schemas.ts, components/forms/UserForm.tsx and app/admin/users/page.tsx. REPORTED #5 (pre-existing, NOT added or touched by this ticket, NOT removed): Expense.approvedBy (String?) Expense.approvedAt (DateTime?) Expense.approvedById (String?) Expense.approvedByUser (relation) Zero references outside prisma/schema.prisma, the migration SQL, and prior gate-evidence write-ups. Not removed, on two independent grounds: (a) the gate's Step 2 states outright that deleting a schema field is a migration decision this gate does not make unilaterally; and (b) the schema documents the intent — the legacy free-text approver kept alongside the newer approvedById FK until a follow-up migration backfills a human-decided name->user mapping. Consistent with how this ticket's own author handled the analogous case: the modelNo tombstone comment explicitly refuses to drop Uom.symbol / Supplier.supplierNo until a pg_dump of every populated environment is confirmed. This repo's convention is that an unread column is evidence to gather, not licence to drop. Per the gate's PASS rule, a reported-but-unremoved schema field does not block PASS. RESULT: 0 schema fields removed by this gate; 4 reported. ================================================================================ CHECK 7 — TODO / FIXME markers (report, do not remove) ================================================================================ $ grep -rnE 'TODO|FIXME|XXX|HACK' --include='*.ts' --include='*.tsx' \ --include='*.css' app components lib tests (none) RESULT: 0 incomplete-implementation markers. ================================================================================ CHECK 8 — Duplicate components / orphaned component chains / dead styles ================================================================================ Importer fan-in was computed for all 41 non-route .tsx files, counting src/ and tests/ referrers separately so a component alive only in tests would stand out: src=1 ExpenseRowActions, OrderRowActions, SortIcon, ItemRowActions, ProjectRowActions, SupplierRowActions, UomRowActions, UserRowActions src=2 LogoutButton, DeleteConfirmModal, RepeaterTable, SelectField, Sidebar, ConfirmModal src=3+ everything else (max: PageHeader at 29) Minimum src fan-in across the whole component tree = 1. No component has zero src referrers, so nothing is alive only in tests and nothing is transitively dead. Each src=1 case was traced to a live route: ExpenseRowActions -> app/expenses/page.tsx LIVE OrderRowActions -> app/orders/page.tsx LIVE SortIcon -> DataTable -> 8 list pages LIVE *RowActions (5) -> their own master list page LIVE Duplicate definitions: $ find app components -name '*.tsx' | xargs -n1 basename | sort | uniq -d page.tsx (App Router convention — one per segment, not a duplicate) $ duplicated exported function names across files generateMetadata (Next.js convention export, one per dynamic segment) No component is defined twice. No near-duplicate pair: SelectField wraps Listbox rather than re-implementing it, and FilterBar composes Listbox + DatePicker rather than forking them. app/globals.css — all 10 component classes are referenced from TSX: .btn 28, .btn-primary 12, .btn-secondary 16, .btn-danger 1, .field-input 12, .field-control 2, .field-label 15, .field-error 21, .card 8, .link 18 No dead CSS rules. tailwind.config.ts design tokens — every token this ticket ADDED is wired: text.subtle 6 uses (filter labels, order-detail info labels) text.helper 1 use ExpenseForm.tsx:170 attachment empty state border.dashed 1 use ExpenseForm.tsx:170 (`border-border-dashed`; no collision with Tailwind's built-in `border-dashed` style utility, which the same className also uses, correctly) status.success 3 uses (changed value, not a new key) status.successSubtle 1 use StatusBadge.tsx:22 PILL_CLASSES.success Two tokens inherited from #3247 have zero uses — see REPORTED #2. REPORTED #3 / carried forward: EXPENSE_TYPE_OPTIONS and MAX_UPLOAD_BYTES are still duplicated verbatim across live files (app/expense-summary/[project]/ page.tsx:11 + components/forms/ExpenseForm.tsx:36; ExpenseForm.tsx:46 + OrderFulfillmentForm.tsx:37). Both copies of each are USED, so neither is dead code — de-duplicating means creating a new shared module, which is a DRY refactor and explicitly outside this gate's whitelist. RESULT: 0 duplicate components removed, 0 dead style rules. ================================================================================ REPORTED — NOT REMOVED (5 items) ================================================================================ 1. components/ui/StatusBadge.tsx:45 — `export type StatusBadgeVariant` Imported by nothing outside the file, but genuinely referenced in-file at line 65 as StatusBadge's `variant?:` prop type. Only the `export` keyword is redundant. The symbol is not dead, and dropping `export` would narrow the public prop-type surface of an exported component for zero dead-code benefit. UNSURE -> reported. (Same finding as the #3247 run; line moved 41 -> 45.) 2. tailwind.config.ts — `accent.subtleHover` (#f0e8dc) and `accent.selected` (#e2cdb0): zero references anywhere in app/, components/, lib/ or tests/, as `accent-subtleHover`/`accent-selected` classes or as raw hex. NOT removed, deliberately: - Both were added by ticket #3247 from its design spec's unmapped-token list, for surfaces that do not exist yet (a selected tab-pill segment; a secondary/Cancel hover fill — .btn-secondary currently hovers to accent.subtle, not accent.subtleHover). This is the design-token analogue of the gate's own "a schema field may be intentionally added ahead of the code that will use it next ticket" carve-out. - An unused theme key is inert: Tailwind's JIT emits classes by scanning `content`, so an unreferenced token generates no CSS. There is no bundle-size or runtime cost to recover by deleting it. - Deleting a measured design token is a design-system decision, and both predate this ticket. UNSURE -> reported, per the node instruction. Flagging for whoever owns the token set: either wire them or retire them in a dedicated pass. 3. StatusBadge's `"pill"` variant (the default branch + the PILL_CLASSES table at StatusBadge.tsx:19-27) has no application call site — all 8 in-app `` usages pass variant="plain" (5 masters + Users), variant="dot" (Orders list + OrderFulfillmentForm) or variant="text" (Expenses). It is exercised only by tests/StatusBadge.test.tsx, which renders the default. NOT removed: the gate states outright "Do NOT remove anything referenced only by test files", and it is also the documented fallback for any future caller not covered by #3247's finding F1. Consequence worth noting: status.successSubtle (added this ticket) is likewise reachable only through that pill path today. 4. lib/permissions/index.ts — barrel file with zero importers. All three re-export targets exist, so this is NOT a "dead barrel entry" as the gate defines it, and the file is not in feature_files. Reported only. 5. Expense.approvedBy / approvedAt / approvedById / approvedByUser — orphaned columns. See CHECK 6. Removal is a migration; out of this gate's authority. ================================================================================ REMOVED (0 items) ================================================================================ Nothing was removed. No finding in any of the 7 categories was provably dead. Worth stating plainly, since a zero-removal result can read as a gate that didn't look: this ticket's own author had already performed the one real deletion in scope — Item.modelNo, dropped with a migration, a rollback note, a regression test, and the dependent `required` attribute in ItemForm.tsx corrected. CHECK 6 verified that removal is complete and leaves no stranded reference, rather than re-finding it. ================================================================================ POST-GATE VERIFICATION ================================================================================ $ git diff --stat (empty — no tracked file modified) $ git status --short only untracked docs/gate-evidence/ W11/W12 files from the preceding gates, plus this file $ npx tsc --noEmit -p tsconfig.json EXIT=0 $ npx next lint ✔ No ESLint warnings or errors $ npx vitest run Test Files 86 passed (86) Tests 630 passed (630) GATE STATUS: PASS ================================================================================