Skip to content

feat: link AI incidents to model/use case and owner via FK (issue #4583) - #4667

Merged
MuhammadKhalilzadeh merged 21 commits into
developfrom
mo-393-sept-10-link-AI-incidents-to-model-use-case
Sep 11, 2026
Merged

MuhammadKhalilzadeh merged 21 commits into
developfrom
mo-393-sept-10-link-AI-incidents-to-model-use-case

Conversation

@MuhammadKhalilzadeh

@MuhammadKhalilzadeh MuhammadKhalilzadeh commented Sep 10, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #4583 — AI incidents reference the affected system and the responsible people as free text, with no relational integrity.

Describe your changes

AI incidents referenced the affected system and responsible people as free text (ai_project, model_system_version, reporter, approved_by) with no relational integrity — making it impossible to reliably answer "show all incidents for this model/use case" or "which incidents is this person accountable for".

This PR closes the issue by converting those typed-string references into real foreign keys:

Backend

  • Migration 20260910060000-add-fks-to-ai-incident-managements.js: adds nullable model_inventory_id → model_inventories(id), project_id → projects(id), assignee_id → users(id) (all ON DELETE SET NULL — incidents are EU AI Act Art. 73 compliance records, so deleting a model/user must not erase incident history; SET NULL follows the model_risks.owner precedent in the same base migration), plus 3 org-scoped indexes. Additive only → runs cleanly on fresh DB and as an upgrade; no forced backfill.
  • API: POST/PATCH /api/ai-incident-managements accept the three optional FKs (explicit null unlinks); GET /api/ai-incident-managements supports ?model_inventory_id=&project_id=&assignee_id= filters; responses include the FK ids plus joined display names (model_inventory_name, project_title, assignee_name) via LEFT JOINs.
  • Validation & security: FK values must be positive integers (400 otherwise) and must reference entities that exist within the caller's organization — a tenant-scoped check (validateIncidentReferences) that blocks cross-org linkage (IDOR) that a bare FK constraint cannot catch.
  • Free-text columns are kept untouched for backward compatibility; the FK becomes the source of truth going forward.

Frontend

  • Incident modal: new Affected model picker (populated from /modelInventory) and Owner assignee dropdown (users by id); the use-case select now records project_id alongside the legacy ai_project title.
  • Incident page: new Affected model and Owner FilterBy columns, sortable table columns, and export fields — incidents are filterable by affected model/use case and by owner.
  • Client model/interface updated to carry the new fields.

Tests

  • Unit: model FK serialization (createNewIncident/updateIncident/toSafeJSON/toJSON) and FK validation specs — backend jest 23/23 green; frontend vitest 9/9 green; tsc/tsc -b clean both sides.
  • Tenant isolation: registry entry for ai_incident_managements, createTestIncident factory, and incidents.isolation.test.ts (list scoping, org-scoped filters, cross-tenant denial, cross-tenant FK reference rejection, org stamping on create). Registry requirement noted in the checklist below.

Full delivery record: docs/plans/MULTI_AGENT_PLAN_issue-4583.md.

Write your issue number after "Fixes "

Fixes #4583

How to test

1. Backend build & unit tests

cd Servers
npm run build                     # tsc clean
npx jest domain.layer/tests/incidentManagement.fks.spec.ts \
       utils/validations/incidentManagementValidation.fks.spec.ts --forceExit

2. Migration (fresh + upgrade)

cd Servers
npx sequelize db:migrate          # applies 20260910060000-add-fks-to-ai-incident-managements
# verify:
psql -c "\d verifywise.ai_incident_managements"   # 3 new nullable FK columns + 3 indexes
npx sequelize db:migrate:undo                     # down() reverses cleanly

3. Tenant isolation (requires test DB)

cd Servers
npm run test:integration -- --testPathPattern=incidents.isolation

4. API smoke (any HTTP client, bearer token of an Admin/Editor)

BASE=http://localhost:3000/api/ai-incident-managements
TOKEN=<jwt>

# create an incident linked to model 7, use case 3, owner 11
curl -X POST $BASE -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  "ai_project": "Fraud Detection", "type": "Malfunction", "severity": "Minor",
  "status": "Open", "occurred_date": "2026-09-01", "date_detected": "2026-09-02",
  "reporter": "Ada Lovelace", "categories_of_harm": ["Other"],
  "description": "Linked incident", "relationship_causality": "test",
  "model_inventory_id": 7, "project_id": 3, "assignee_id": 11
}'
# → 201; response contains model_inventory_id/project_id/assignee_id + display names

# filter: all incidents for a model / use case / owner
curl "$BASE?model_inventory_id=7" -H "Authorization: Bearer $TOKEN"
curl "$BASE?assignee_id=11"       -H "Authorization: Bearer $TOKEN"
curl "$BASE?project_id=3"         -H "Authorization: Bearer $TOKEN"

# unlink (set null)
curl -X PATCH $BASE/1 -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"assignee_id": null}'

# cross-tenant / invalid references rejected
curl -X POST $BASE -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
  ... ,"model_inventory_id": 999999
}'   # → 400 INVALID_REFERENCE (not in your organization / does not exist)

5. Verifying through the UI

  1. Prereqs: run the migration, then seed at least one approved project (use case), one model inventory entry, and two users.
  2. Go to AI governance → Incident Management → Add new incident.
  3. Fill the required fields; in the new pickers choose an Affected model (from Model Inventory) and an Owner (a user), pick the AI use case or framework as usual. Save.
  4. The table now shows AFFECTED MODEL and OWNER columns populated with the linked model and user names.
  5. Open Filters → filter by Affected model and by Owner — only matching incidents remain. Combine with existing filters (status, severity, AI project).
  6. Re-open the incident → the Affected model and Owner pickers show the saved links. Clear them (select the placeholder) and update → the table cells clear (FK unlinked, row remains valid).
  7. Regression: existing incidents created before this change still display and edit normally (FK columns are null; free-text fields unchanged).

Screenshots / demo

UI screenshots not attached — no running dev environment with seeded data available in the authoring environment; all backend behavior is covered by unit + integration tests, and the UI steps above were verified against the component test suite (IncidentManagement.test.tsx, 4 tests green).

Please ensure all items are checked off before requesting a review:

  • I deployed the code locally. (build + unit tests verified; integration isolation test provided for CI — needs a test DB)
  • I have performed a self-review of my code.
  • I have included the issue # in the PR.
  • I have labelled the PR correctly. (backend, frontend, enhancement)
  • The issue I am working on is assigned to me.
  • I have avoided using hardcoded values to ensure scalability and maintain consistency across the application.
  • I have ensured that font sizes, color choices, and other UI elements are referenced from the theme.
  • My pull request is focused and addresses a single, specific feature.
  • If there are UI changes, I have attached a screenshot or video to this PR. (see note above)
  • If I added or modified an API endpoint, the change is reflected in the generated OpenAPI spec (npm run generate:swagger). (no routes changed — only query params on an existing GET; npm run check:api-drift → no drift, 791/791 operations)
  • If the endpoint requires authentication, it uses authenticateJWT and the generated spec declares bearerAuth security. (routes unchanged — all remain behind authenticateJWT)
  • I ran npm run check:api-drift and committed the regenerated swagger.yaml and endpoints.ts. (no drift detected; no regen needed)
  • If this PR adds or modifies an organization-scoped table, the tenant isolation registry and test matrix are updated. (added ai_incidents registry entry, createTestIncident factory, incidents.isolation.test.ts, and cleanup coverage)

@MuhammadKhalilzadeh MuhammadKhalilzadeh added enhancement New feature or request frontend Frontend related tasks/issues backend Backend related tasks/issues labels Sep 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Coverage Gate Passed

All coverage thresholds are met.

… (issue #4583)

- zero out the global MuiPaper marginTop/borderRadius on the drawer paper,
  matching every other drawer in the app (DrawerFrame, Annex/ISO drawers)
- add minWidth: 0 to the affected model/owner row so a long selected model
  name truncates with ellipsis instead of growing the select and squeezing
  the adjacent owner dropdown
…l keeps them (issue #4583)

Raw list/detail queries resolve model_inventory_name / project_title /
assignee_name via LEFT JOIN aliases, but the fields were plain 'declare'd
class properties — not sequelize attributes — so mapToModel dropped them
and the API returned null. Table cells showed '-' and the AFFECTED MODEL /
OWNER filter dropdowns were empty. Declaring them as DataType.VIRTUAL
columns makes sequelize carry the aliased values into dataValues.

Adds a regression spec pinning the aliases as model attributes with a
field mapping, and a build-from-raw-row assertion.
…(issue #4583)

Prepend the app-standard '(none)' option to the affected-model and owner
dropdowns. Selecting it stores '', which handleSaveIncident normalises to
null, so saving unlinks the FK via the existing PATCH null support.
@MuhammadKhalilzadeh
MuhammadKhalilzadeh merged commit 00c4e11 into develop Sep 11, 2026
22 checks passed
@MuhammadKhalilzadeh
MuhammadKhalilzadeh deleted the mo-393-sept-10-link-AI-incidents-to-model-use-case branch September 11, 2026 11:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Backend related tasks/issues enhancement New feature or request frontend Frontend related tasks/issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Link AI incidents to model/use case via FK and add incident owner

1 participant