You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Focus: end-to-end polish, type safety, validation, and component boundaries. Every item below touches both the frontend and the backend (or a Python service) so you keep sharpening full-stack delivery.
1. Build and roll out a reusable AsyncBoundary component
The frontend currently renders ad-hoc CircularProgress spinners, blank screens, or raw error text in pages such as ModelInventory and EvalsDashboard. Create a single <AsyncBoundary> component under Clients/src/presentation/components/AsyncBoundary that accepts isLoading, error, isEmpty, and children props and consistently renders a skeleton loader, an error alert with a retry button, and the existing EmptyIllustration when there is no data. Use the loading and empty-state patterns already documented in the StyleGuide so the UX feels uniform across the product.
On the backend, audit the endpoints that feed those two pages and make sure they return the project’s standard { message, data } envelope and use the STATUS_CODE helper for errors. Replace any silent .catch(console.error) patterns in the corresponding repositories so failures actually surface to the UI. The goal is a contract where the frontend knows exactly what an error or empty response looks like and the backend never swallows exceptions.
Acceptance:ModelInventory and EvalsDashboard use AsyncBoundary; all their direct API calls return standardized envelopes; no silent failures remain; npm run typecheck stays green.
2. Add server-side HTML sanitization for rich-text content
User-generated rich text in policies, evidence hub entries, intake forms, and notes is currently persisted mostly as-is. Only the policy importer uses any sanitization, which leaves other features open to stored XSS. Build a shared sanitizeUserHtml() utility in Servers/utils/sanitization.utils.ts using sanitize-html (or DOMPurify on the server) with a strict allowlist of tags and attributes, and wire it into the create/update paths of the four highest-risk content types.
On the frontend, add a small rendering wrapper for rich-text fields that enforces the same allowlist and adds a sandbox attribute where appropriate. This gives defense in depth: even if someone bypasses the server, the client still refuses to execute injected scripts. Coordinate with Inna on the UI so warning states are shown when disallowed tags are stripped.
Acceptance: Utility is unit-tested with a representative set of malicious and benign inputs; policies, evidence, intake forms, and notes use it on save; frontend renderer uses the allowlist; no raw HTML reaches the database on happy paths.
3. Migrate direct localStorage access to a typed storage service
Raw localStorage.getItem/setItem/removeItem calls are scattered across roughly 40 frontend files, including authentication, dashboard filters, charts, the user guide, and the command palette. Build on the previous sprint’s StorageService idea (or create it if it is not ready) with typed keys, JSON parsing safety, namespaced verifywise_* keys, and SSR fallbacks. Migrate the highest-traffic ten callers first, such as customAxios, Dashboard, Tasks, EvalsDashboard, SettingsPage/Preferences, SettingsPage/Profile, StartHere, and AIGateway/SpendDashboard.
On the backend, expose a small GET /api/users/preferences endpoint that returns the server-side copy of preferences (theme, date format, language) so the storage service can hydrate defaults without another round trip when the token payload is not enough. Keep the source of truth on the server while using localStorage only for transient UI state like snooze timestamps and temporary filters.
Acceptance: At least ten direct localStorage callers migrated; no localStorage exceptions in sandboxed contexts; full unit-test coverage for the service; backend preference endpoint returns the current user’s saved preferences.
4. Standardize the API error-response envelope across controllers
Many controllers still return return res.status(500).json({ message: ... }), raw objects, or nested shapes that differ from STATUS_CODE[500](...). Sweep Servers/controllers/ and align every error response to the project’s STATUS_CODE helper so the frontend can rely on a uniform { message, data } envelope. Pay special attention to auth.middleware.ts, rateLimit.middleware.ts, and the transaction catch blocks in dataset.ctrl.ts, datasetBulkUpload.ctrl.ts, and modelInventory.ctrl.ts.
On the frontend, update customAxios response interceptors to consume that envelope and surface translated error messages via the alert/snackbar system. Remove ad-hoc .catch(console.error) calls in repositories such as entity.repository.ts, file.repository.ts, and trainingregistar.repository.ts so users see a toast instead of a silent failure.
Acceptance: A sample of 15 controllers shows zero raw res.json({ message }) error returns; middleware uses STATUS_CODE; frontend toasts appear on 4xx/5xx; repositories no longer log raw errors to the console.
5. Add optimistic updates and targeted cache invalidation for high-frequency mutations
Task status toggles, risk score changes, file tagging, and policy edits currently wait for the server before updating the UI, which makes the app feel sluggish. Add optimistic updates using TanStack Query’s onMutate + setQueryData, followed by invalidateQueries for related keys. For example, updating a task should immediately reflect in the tasks table, the dashboard counters, and the deadline-warning banner; if the mutation fails, roll back the UI and show the standardized error toast.
On the backend, make sure the mutation endpoints return the updated entity (not just a success message) so the optimistic patch can be replaced with authoritative data without a second fetch. Add a lightweight useTaskMutations, useRiskMutations, and useFileTagMutations hook family in Clients/src/application/hooks to keep the pattern consistent.
Acceptance: Demo video or automated test shows instant UI feedback after a mutation; eventual consistency on failure via rollback; related query keys are invalidated correctly; no full-page refetches for single-row updates.
6. Introduce Pydantic request schemas on EvalServer routes
Several EvalServer endpoints accept config_data: dict = Body(...) or payload: dict = Body(...), which means invalid fields silently pass through and fail later inside controllers. Define Pydantic request schemas for /evaluate, /scorers, /models, /experiments, and /metrics and return a proper 422 when validation fails. Convert the imperative verify_internal_key(request) check into a FastAPI Depends() callable and apply it at the router level so no route can forget it.
On the Node side, add a small OpenAPI contract check that compares the EvalServer schemas to the generated swagger.generated.yaml or the internal client used by Servers/controllers/evaluationLlmApiKey.ctrl.ts. This prevents drift between the Python service and the Node backend when payloads evolve.
Acceptance: Each listed EvalServer route rejects malformed payloads with 422; internal-key dependency is applied at router level; Node client types are updated and typecheck passes.
7. Refactor PolicyEditorPage into sub-components and migrate hardcoded theme tokens
PolicyEditorPage.tsx is ~88 KB and contains 72 hardcoded color literals, making it hard to maintain and impossible to theme. Extract a PolicyHeader, PolicyToolbar, PolicyContentEditor, PolicyMetadataSidebar, and PolicyReviewPanel as co-located sub-components, keeping each under 200 lines where feasible. Replace raw hex/rgb values with references to the MUI theme palette or a shared theme.tokens.ts file.
On the backend, the policy save endpoint currently returns a generic success message. Update it to return the saved policy object including the new updated_at and status fields so the editor can reflect the save immediately without a refetch. Also add a server-side sanitization call (coordinate with task First login & sign in & forgot password pages #2) before the policy body is persisted.
Acceptance: Page file shrinks by at least 30%; hardcoded color count drops to zero; sub-components render and save correctly; backend returns updated entity; no visual regressions in StyleGuide comparison.
8. Add a cross-tenant isolation test harness
There are currently no tests that verify one organization cannot read another’s data. Build a reusable cross-tenant test matrix under Servers/tests/integration/tenant-isolation/ that creates two organizations, two users, and shared entity types (projects, files, risks, tasks), then asserts that User A cannot see Organization B’s records. Start with GET /api/projects, GET /api/files, GET /api/risks, and GET /api/tasks.
On the frontend, add a complementary MSW scenario in Clients/src/test/mocks/handlers.ts that returns a 403 when a request includes an organization ID different from the authenticated user’s organization, and verify that the UI shows the standardized error boundary/toast instead of leaking data.
Acceptance: Test matrix runs under npm run test:integration; at least four entity types covered; frontend MSW scenario exists; CI fails if tenant isolation breaks.
9. Build an AI Advisor tool-registry roadmap tracker
The AI Implementation Plan calls for 263 tools, but the registry in Servers/advisor/aiActions/registry.ts only exposes a small subset. Create a new read-only page at Clients/src/presentation/pages/AIAdvisorRoadmap that visualizes implemented vs. planned tools by domain and phase. The page should read from a new GET /api/advisor/tools/roadmap endpoint that returns the current registry plus a static roadmap manifest derived from AI Implementation Plan.md.
On the backend, implement the endpoint in Servers/controllers/advisor.ctrl.ts and make sure it respects role-based access (Admin/Editor/Reviewer/Auditor can read, no write tools exposed). Add controller tests for the endpoint and keep the response shape versioned so the frontend can render progress bars and filters.
Acceptance: Page renders a filterable grid of tool cards with status badges; backend endpoint is tested; data is role-isolated; no write-tool implementation details are leaked.
10. Add route-level input validation to project and risk routes
project.route.ts and risks.route.ts parse req.body and req.params manually, which leads to inconsistent 400 responses and missing validation. Introduce express-validator chains (or Zod middleware) for create, update, and path parameters, and add a shared validation-error handler that returns STATUS_CODE[400] with a clear field-level message array.
On the frontend, update the project and risk forms to disable submission until the client-side validation passes and to display inline field errors returned by the backend. Use the existing Field component improvements and ensure screen readers announce server errors.
Acceptance: Both routes reject malformed payloads with a consistent 400 shape; frontend forms display server errors inline; existing happy-path tests still pass; no manual parseInt without radix remains in these routes.
Aryaman — Full-Stack Developer 2nd Task List
1. Build and roll out a reusable
AsyncBoundarycomponentThe frontend currently renders ad-hoc
CircularProgressspinners, blank screens, or raw error text in pages such asModelInventoryandEvalsDashboard. Create a single<AsyncBoundary>component underClients/src/presentation/components/AsyncBoundarythat acceptsisLoading,error,isEmpty, andchildrenprops and consistently renders a skeleton loader, an error alert with a retry button, and the existingEmptyIllustrationwhen there is no data. Use the loading and empty-state patterns already documented in the StyleGuide so the UX feels uniform across the product.On the backend, audit the endpoints that feed those two pages and make sure they return the project’s standard
{ message, data }envelope and use theSTATUS_CODEhelper for errors. Replace any silent.catch(console.error)patterns in the corresponding repositories so failures actually surface to the UI. The goal is a contract where the frontend knows exactly what an error or empty response looks like and the backend never swallows exceptions.Acceptance:
ModelInventoryandEvalsDashboarduseAsyncBoundary; all their direct API calls return standardized envelopes; no silent failures remain;npm run typecheckstays green.2. Add server-side HTML sanitization for rich-text content
User-generated rich text in policies, evidence hub entries, intake forms, and notes is currently persisted mostly as-is. Only the policy importer uses any sanitization, which leaves other features open to stored XSS. Build a shared
sanitizeUserHtml()utility inServers/utils/sanitization.utils.tsusingsanitize-html(or DOMPurify on the server) with a strict allowlist of tags and attributes, and wire it into the create/update paths of the four highest-risk content types.On the frontend, add a small rendering wrapper for rich-text fields that enforces the same allowlist and adds a
sandboxattribute where appropriate. This gives defense in depth: even if someone bypasses the server, the client still refuses to execute injected scripts. Coordinate with Inna on the UI so warning states are shown when disallowed tags are stripped.Acceptance: Utility is unit-tested with a representative set of malicious and benign inputs; policies, evidence, intake forms, and notes use it on save; frontend renderer uses the allowlist; no raw HTML reaches the database on happy paths.
3. Migrate direct
localStorageaccess to a typed storage serviceRaw
localStorage.getItem/setItem/removeItemcalls are scattered across roughly 40 frontend files, including authentication, dashboard filters, charts, the user guide, and the command palette. Build on the previous sprint’sStorageServiceidea (or create it if it is not ready) with typed keys, JSON parsing safety, namespacedverifywise_*keys, and SSR fallbacks. Migrate the highest-traffic ten callers first, such ascustomAxios,Dashboard,Tasks,EvalsDashboard,SettingsPage/Preferences,SettingsPage/Profile,StartHere, andAIGateway/SpendDashboard.On the backend, expose a small
GET /api/users/preferencesendpoint that returns the server-side copy of preferences (theme, date format, language) so the storage service can hydrate defaults without another round trip when the token payload is not enough. Keep the source of truth on the server while usinglocalStorageonly for transient UI state like snooze timestamps and temporary filters.Acceptance: At least ten direct
localStoragecallers migrated; nolocalStorageexceptions in sandboxed contexts; full unit-test coverage for the service; backend preference endpoint returns the current user’s saved preferences.4. Standardize the API error-response envelope across controllers
Many controllers still return
return res.status(500).json({ message: ... }), raw objects, or nested shapes that differ fromSTATUS_CODE[500](...). SweepServers/controllers/and align every error response to the project’sSTATUS_CODEhelper so the frontend can rely on a uniform{ message, data }envelope. Pay special attention toauth.middleware.ts,rateLimit.middleware.ts, and the transaction catch blocks indataset.ctrl.ts,datasetBulkUpload.ctrl.ts, andmodelInventory.ctrl.ts.On the frontend, update
customAxiosresponse interceptors to consume that envelope and surface translated error messages via the alert/snackbar system. Remove ad-hoc.catch(console.error)calls in repositories such asentity.repository.ts,file.repository.ts, andtrainingregistar.repository.tsso users see a toast instead of a silent failure.Acceptance: A sample of 15 controllers shows zero raw
res.json({ message })error returns; middleware usesSTATUS_CODE; frontend toasts appear on 4xx/5xx; repositories no longer log raw errors to the console.5. Add optimistic updates and targeted cache invalidation for high-frequency mutations
Task status toggles, risk score changes, file tagging, and policy edits currently wait for the server before updating the UI, which makes the app feel sluggish. Add optimistic updates using TanStack Query’s
onMutate+setQueryData, followed byinvalidateQueriesfor related keys. For example, updating a task should immediately reflect in the tasks table, the dashboard counters, and the deadline-warning banner; if the mutation fails, roll back the UI and show the standardized error toast.On the backend, make sure the mutation endpoints return the updated entity (not just a success message) so the optimistic patch can be replaced with authoritative data without a second fetch. Add a lightweight
useTaskMutations,useRiskMutations, anduseFileTagMutationshook family inClients/src/application/hooksto keep the pattern consistent.Acceptance: Demo video or automated test shows instant UI feedback after a mutation; eventual consistency on failure via rollback; related query keys are invalidated correctly; no full-page refetches for single-row updates.
6. Introduce Pydantic request schemas on EvalServer routes
Several EvalServer endpoints accept
config_data: dict = Body(...)orpayload: dict = Body(...), which means invalid fields silently pass through and fail later inside controllers. Define Pydantic request schemas for/evaluate,/scorers,/models,/experiments, and/metricsand return a proper422when validation fails. Convert the imperativeverify_internal_key(request)check into a FastAPIDepends()callable and apply it at the router level so no route can forget it.On the Node side, add a small OpenAPI contract check that compares the EvalServer schemas to the generated
swagger.generated.yamlor the internal client used byServers/controllers/evaluationLlmApiKey.ctrl.ts. This prevents drift between the Python service and the Node backend when payloads evolve.Acceptance: Each listed EvalServer route rejects malformed payloads with
422; internal-key dependency is applied at router level; Node client types are updated and typecheck passes.7. Refactor
PolicyEditorPageinto sub-components and migrate hardcoded theme tokensPolicyEditorPage.tsxis ~88 KB and contains 72 hardcoded color literals, making it hard to maintain and impossible to theme. Extract aPolicyHeader,PolicyToolbar,PolicyContentEditor,PolicyMetadataSidebar, andPolicyReviewPanelas co-located sub-components, keeping each under 200 lines where feasible. Replace raw hex/rgb values with references to the MUI theme palette or a sharedtheme.tokens.tsfile.On the backend, the policy save endpoint currently returns a generic success message. Update it to return the saved policy object including the new
updated_atandstatusfields so the editor can reflect the save immediately without a refetch. Also add a server-side sanitization call (coordinate with task First login & sign in & forgot password pages #2) before the policy body is persisted.Acceptance: Page file shrinks by at least 30%; hardcoded color count drops to zero; sub-components render and save correctly; backend returns updated entity; no visual regressions in StyleGuide comparison.
8. Add a cross-tenant isolation test harness
There are currently no tests that verify one organization cannot read another’s data. Build a reusable cross-tenant test matrix under
Servers/tests/integration/tenant-isolation/that creates two organizations, two users, and shared entity types (projects, files, risks, tasks), then asserts that User A cannot see Organization B’s records. Start withGET /api/projects,GET /api/files,GET /api/risks, andGET /api/tasks.On the frontend, add a complementary MSW scenario in
Clients/src/test/mocks/handlers.tsthat returns a403when a request includes an organization ID different from the authenticated user’s organization, and verify that the UI shows the standardized error boundary/toast instead of leaking data.Acceptance: Test matrix runs under
npm run test:integration; at least four entity types covered; frontend MSW scenario exists; CI fails if tenant isolation breaks.9. Build an AI Advisor tool-registry roadmap tracker
The AI Implementation Plan calls for 263 tools, but the registry in
Servers/advisor/aiActions/registry.tsonly exposes a small subset. Create a new read-only page atClients/src/presentation/pages/AIAdvisorRoadmapthat visualizes implemented vs. planned tools by domain and phase. The page should read from a newGET /api/advisor/tools/roadmapendpoint that returns the current registry plus a static roadmap manifest derived fromAI Implementation Plan.md.On the backend, implement the endpoint in
Servers/controllers/advisor.ctrl.tsand make sure it respects role-based access (Admin/Editor/Reviewer/Auditor can read, no write tools exposed). Add controller tests for the endpoint and keep the response shape versioned so the frontend can render progress bars and filters.Acceptance: Page renders a filterable grid of tool cards with status badges; backend endpoint is tested; data is role-isolated; no write-tool implementation details are leaked.
10. Add route-level input validation to project and risk routes
project.route.tsandrisks.route.tsparsereq.bodyandreq.paramsmanually, which leads to inconsistent 400 responses and missing validation. Introduceexpress-validatorchains (or Zod middleware) for create, update, and path parameters, and add a shared validation-error handler that returnsSTATUS_CODE[400]with a clear field-level message array.On the frontend, update the project and risk forms to disable submission until the client-side validation passes and to display inline field errors returned by the backend. Use the existing
Fieldcomponent improvements and ensure screen readers announce server errors.Acceptance: Both routes reject malformed payloads with a consistent 400 shape; frontend forms display server errors inline; existing happy-path tests still pass; no manual
parseIntwithout radix remains in these routes.