# Architecture Hardening Backlog

**Date:** 2026-03-30
**Purpose:** Keep the remaining cleanup work for the current quality/security scope in a single prioritized list, so small hardening slices can be completed before larger redesign work.

## Recently Completed

- SSE audience model narrowed to canonical `user:*`, `permission:*`, and `resource:*` scopes only
- CI architecture guardrail added for SSE audience scoping
- import boundaries hardened for server dispo workbooks and browser spreadsheet uploads
- AI and SMTP runtime diagnostics sanitized before they reach logs or admin-facing error messages
- transitive audit hotspots for `flatted` and `picomatch` pinned through root `pnpm.overrides`
- `apps/web` export paths migrated from direct `xlsx` usage to a shared `exceljs` workbook export helper
- `packages/application` workbook reading and `packages/engine` XLSX export serialization migrated from `xlsx` to `exceljs`
- `pnpm audit --audit-level=high` no longer reports high-severity dependency findings
- `apps/web` now has focused Vitest coverage for browser spreadsheet parsing and skill-matrix workbook parsing
- cron routes, Redis helpers, reminder scheduling, webhook dispatching, and SSE fallback paths now use structured logger calls instead of raw `console.*`
- `packages/api` now has focused Vitest coverage for reminder scheduler and webhook dispatcher logging failures
- `apps/web` typecheck is now decoupled from generated `.next-e2e` artifacts via a dedicated `tsconfig.typecheck.json`
- comment entity support is now centralized across shared constants, API registry policy, assistant tool metadata, and the web comment target API without pretending a second consumer exists
- `resource` is now onboarded as the second real comment entity, reusing the same ownership and staff-visibility rules as the resource detail route
- comment mention autocomplete now uses a dedicated entity-scoped API route instead of inheriting the narrower `user.listAssignable` audience
- runtime secret handling is now environment-first end to end: admin updates no longer persist new operational secrets, runtime status is surfaced explicitly, and legacy database secret copies can be cleared through a dedicated cleanup path
- production auth runtime config now fails fast when `AUTH_SECRET`/`NEXTAUTH_SECRET` is missing or left on a known development placeholder, and local compose no longer hardcodes that secret
- `apps/web` system settings UI is now decomposed into section components with shared secret/runtime helpers, bringing all files in that slice back under the file-size guardrail
- the first API-side `assistant-tools` extraction is in place: settings, system-role config, webhooks, audit log access, and shoring ratio now live in a dedicated domain module with shared assistant-tool types
- the advanced timeline assistant toolset now lives in its own domain module, keeping the high-risk read/mutation pairings out of the monolithic router without changing the assistant contract
- the adjacent allocation planning assistant helpers now live in their own domain module, covering allocation listing, budget status, and the core allocation create/cancel/status mutations without changing the assistant contract
- the neighboring vacation and holiday assistant helpers now live in their own domain module, covering vacation-balance reads, regional/resource holiday inspection, and holiday calendar admin mutations without changing the assistant contract
- the adjacent roles, skill-search, and lightweight analytics assistant helpers now live in their own domain module, covering role CRUD plus `search_by_skill`, `get_statistics`, and `get_chargeability` without changing the assistant contract
- the neighboring client and org-unit admin mutations now live in their own domain module, keeping more CRUD wiring out of the monolithic router without changing the assistant contract
- the adjacent chargeability/computation read helpers now live in their own domain module, keeping the advanced financial transparency read models out of the monolithic router without changing the assistant contract
- the neighboring country and metro-city admin mutations now live in their own domain module, keeping more settings-side CRUD wiring out of the monolithic router without changing the assistant contract
- the adjacent management-level, utilization, calculation-rule, effort-rule, and experience-multiplier read helpers now live in their own domain module, further shrinking the monolithic assistant router without changing the assistant contract
- the remaining assistant user-admin helper cluster now lives in its own domain module, covering admin listing, user lifecycle mutations, permission overrides, resource linking, and MFA overrides without changing the assistant contract
- the authenticated user self-service assistant helpers now live in their own domain module, covering assignable users, dashboard preferences, favorites, column preferences, and MFA self-service without changing the assistant contract
- the embedded notification, task, reminder, and broadcast assistant helpers now live in their own domain module, keeping the collaboration workflow wiring out of the monolithic router without changing the assistant contract
- the estimate read and mutation helpers now live in their own domain module, keeping estimate lifecycle orchestration out of the monolithic assistant router without changing the assistant contract
- the project search, lifecycle, and cover-art helpers now live in their own domain module, keeping project orchestration out of the monolithic assistant router without changing the assistant contract
- the demand, staffing-suggestion, capacity, and resource-availability assistant helpers now live in their own domain module, keeping staffing orchestration out of the monolithic assistant router without changing the assistant contract
- the resource search, detail, and lifecycle assistant helpers now live in their own domain module, keeping resource CRUD orchestration out of the monolithic assistant router without changing the assistant contract
- the blueprint and rate-card read helpers now live in their own domain module, keeping reference-data and pricing lookups out of the monolithic assistant router without changing the assistant contract
- the dashboard detail, insight summary, anomaly detection, and dynamic report read helpers now live in their own domain module, keeping controller-side analytics reads out of the monolithic assistant router without changing the assistant contract
- the scenario simulation, project narrative, and rate lookup assistant helpers now live in their own domain module, keeping the remaining controller-side scenario/AI analytics wiring out of the monolithic assistant router without changing the assistant contract
- the comment listing and comment mutation assistant helpers now live in their own domain module, keeping collaboration-side comment flows out of the monolithic assistant router without changing the assistant contract
- the audit-history assistant helpers now live in their own domain module, keeping controller-side change-history reads out of the monolithic assistant router without changing the assistant contract
- the import/export and staged Dispo assistant helpers now live in their own domain module, keeping file-bound export/import and batch-staging orchestration out of the monolithic assistant router without changing the assistant contract
- the remaining estimate search, planning lookup, self-service timeline read, and navigation assistant helpers now live in their own domain module, keeping another mixed read-only cluster out of the monolithic assistant router without changing the assistant contract
- the country listing and country detail assistant helpers now live in their own domain module, keeping the remaining geo/readmodel lookups out of the monolithic assistant router without changing the assistant contract
- the remaining vacation workflow and entitlement assistant helpers now live in their own domain module, leaving `packages/api/src/router/assistant-tools.ts` as an aggregator/composition layer instead of the last mixed monolithic execution block
- API and auth rate limiting now prefer shared Redis-backed counters when `REDIS_URL` is configured, while retaining an in-memory fallback for local/degraded operation with focused regression coverage
- production delivery is now consolidated on a single image-based compose path with automatic image publication on `main`, deploy-time readiness gating, and architecture guardrails that prevent host-side app builds from creeping back in

## Next Up

Pin the next structural cleanup on the API side:
continue splitting `packages/api/src/router/assistant-tools.ts` into domain-oriented tool modules without changing the public tool contract.
That extraction work is now effectively complete for the current assistant-tool scope; the next structural cleanup should target oversized source routers or shared helper boundaries rather than another inline assistant-tool cluster.

## Remaining Major Themes

The small hardening slices are effectively exhausted.
The remaining work is now structural rather than another quick batch:

1. oversized router decomposition
2. performance hotspot reduction

## Working Rule

For the next batches, prefer work in this order:

1. remove or isolate known-risk runtime dependencies
2. add guardrails and tests around already-hardened code
3. only then expand architecture surface area
