Skip to content

Controls Hub#3744

Draft
MuhammadKhalilzadeh wants to merge 71 commits intodevelopfrom
mo-335-april-16-controls-hub
Draft

Controls Hub#3744
MuhammadKhalilzadeh wants to merge 71 commits intodevelopfrom
mo-335-april-16-controls-hub

Conversation

@MuhammadKhalilzadeh
Copy link
Copy Markdown
Collaborator

Controls Hub

Descriptions will be added here soon

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

  • I deployed the code locally.
  • I have performed a self-review of my code.
  • I have included the issue # in the PR.
  • I have labelled the PR correctly.
  • 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.

MuhammadKhalilzadeh and others added 30 commits April 16, 2026 10:10
25 curated master controls with mappings across EU AI Act, ISO 42001,
ISO 27001, and NIST AI RMF — covers risk, governance, access, data,
oversight, incident response, supplier, and change management overlaps.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Tenant-scoped CRUD helpers for master_controls and mappings plus a
bulk-update helper. Returns list rows enriched with owner/reviewer/
approver names and per-framework mapping counts for the hub matrix.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
getAllMasterControls, getMasterControlById, createMasterControl,
updateMasterControl, deleteMasterControl. Uses domain-model validation,
logProcessing/Success/Failure telemetry, and rolls back transactions on
failure. Demo rows are read-only (BusinessLogicException surfaces as 403).
Change-history recording is wired in T-016.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
GET    /api/master-controls/:id/mappings        list mappings
POST   /api/master-controls/:id/mappings        add mapping (validated)
DELETE /api/master-controls/mappings/:mappingId remove mapping

Uses ON CONFLICT DO NOTHING to make POST idempotent and surfaces duplicate
attempts as 200 with {duplicate: true}. Demo master controls reject mapping
mutations with 403.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
On master control update, propagateMasterControlUpdate iterates mappings
and, via a per-entity-type adapter registry, fans the diff out to every
tenant row that instantiates the mapped struct requirement. Runs inside
the caller's transaction so the master + fan-out stay atomic.

- Translates master status vocabulary to each framework's enum
- Handles implementation_details vs implementation_description column drift
- Surfaces skips (no adapter, no translatable fields) as explicit results
- Exposes previewPropagation for the preview modal (read-only)

Co-Authored-By: Claude Opus 4.6 <[email protected]>
updateMasterControl now computes a diff against the pre-update snapshot
and, for any propagatable field that actually changed, fans the update
out to mapped framework rows inside the same transaction. The response
shape becomes { master, propagation } so the client can render the
propagation preview modal after save.

Also adds POST /:id/propagation-preview — read-only endpoint powering
the Propagation Preview modal before the user confirms a save.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Wires the generic changeHistory.base.utils helpers into the master
control controller:
- recordEntityCreation in createMasterControl
- trackEntityChanges + recordMultipleFieldChanges in updateMasterControl
  (tracked BEFORE mutation so diffs compare against the persisted snapshot)
- recordEntityDeletion in deleteMasterControl
- recordEntityChange(..., "mapping", ...) for addMasterControlMapping and
  deleteMasterControlMapping, using the "framework:entity_type:entity_id"
  tuple as the value so the audit log captures which mapping was
  attached/detached

All history writes share the request transaction so audit entries roll
back atomically with the data change.
…points

Documents all nine Controls Hub endpoints under a new "Master Controls"
tag: CRUD on the master control itself, mappings CRUD, and the
propagation preview. Captures request/response shape, enums for status
and framework_entity_type, 403 semantics for demo rows, 502 for
propagation failure, and 200/201 duality for idempotent POST on mappings.
Covers happy paths, validation failures, multi-tenant isolation
(every utils call receives req.organizationId), demo-row rejection
(403s on update/delete and mapping mutations), the propagation
decision tree (skipped when nothing trackable changed, fanned out
only for diffed fields), PropagationError → 502 mapping, and the
idempotent 200-with-duplicate response from ON CONFLICT DO NOTHING.
30 tests covering all nine controller exports.
Registers the Controls Hub under the dashboard ProtectedRoute tree with
a Suspense fallback. Ships a minimal page shell as the lazy target so
the route compiles cleanly; the real page content lands in T-023.
Adds a "Controls hub" link in the ASSURANCE group pointing to
/controls-hub, with the lucide Library icon to signal the "reusable
library of controls" concept that unifies the four framework silos.
Thin axios wrappers for the nine Controls Hub endpoints: getAll/getById/
create/update/delete plus mappings CRUD and the read-only propagation
preview. Payload and response types mirror the server contract and are
re-exported for use in hooks and components.
Ships useMasterControls (list), useMasterControl (detail),
useMasterControlMappings (mappings tab), useMasterControlMutations
(create/update/delete + add/remove mapping with scoped invalidation),
and usePropagationPreview. All keys live under the "masterControls"
namespace so the drawer and page can invalidate selectively. List and
detail queries hydrate responses into MasterControlModel instances.
…pty/error states

Adds loading skeletons, error alert with retry, empty-state CTA, and a
placeholder ControlsMatrix stub that the full T-024 table replaces.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
…pagination

Sortable title/status/owner/due-date columns plus per-framework mapping
counts (EU AI Act, ISO 42001, ISO 27001, NIST AI RMF), status chips, and
MUI pagination.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
…igned states

Flags due dates in the past with red "Overdue" text (skipping completed
rows), italicises unassigned owners, and truncates long titles with a
tooltip fallback.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Applies the same patch to N master controls with per-row transactions so
one row's failure does not roll back the others. Each successful row
triggers propagation to its mapped framework entities; per-row errors
(validation, demo, propagation, not-found) are returned in the response.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Builds a dependency-free CSV with RFC 4180 quoting: id, title, status,
risk review, owner/reviewer/approver names, due date, description,
implementation details, total and per-framework mapping counts, and a
semicolon-joined list of mapped entity codes. Sends with text/csv and
a date-stamped attachment filename.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Imports the 25 curated master controls and resolves each seed code to a
struct row where the struct table exposes a stable identifier (ISO 42001
subclauses, ISO 27001 subclauses + annex controls, NIST subcategories).
Codes on struct tables without stable identifiers (EU AI Act Articles,
ISO 42001 Annex A.*) are reported back as skipped so the UI can tell the
user to add those mappings manually.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
MuhammadKhalilzadeh and others added 12 commits April 16, 2026 20:17
…ance

MasterControlModel and MasterControlFrameworkMappingModel were defined
with sequelize-typescript decorators but never added to the Sequelize
instance's models array, so every code path that instantiated them
(seed loader, create controller, seed service) failed at runtime with
"Model not initialized". Registering them in database/db.ts fixes the
POST /master-controls/seed-recommended endpoint and unblocks the
"Import recommended mappings" flow.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
The "New master control" CTA was a no-op placeholder. Ship a minimal
create dialog that collects title/status/description (the two required
fields plus one optional) and, on success, opens the drawer on the new
master's id so the user can fill in owner/reviewer/due date/etc via the
Details tab.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
`updateMasterControl` and `bulkUpdateMasterControls` were repacking
`req.body` into an object with every allowed column spelled out. For a
single-field patch that meant `{title: undefined, status: undefined,
...}` — and `updateMasterControlQuery` treats every key present on the
object as one to write, coercing `undefined ?? null` into a literal
NULL in the SET clause. The UPDATE then failed with
"null value in column 'title' violates not-null constraint".

Pass `updateData`/`patch` through directly so the utils' allow-list
sees only the keys the client actually sent.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
GET /api/master-controls/framework-catalog returns the full list of
struct rows for every framework_entity_type so the Mappings tab picker
can show real requirement titles instead of raw numeric ids.

Struct tables are framework metadata (not tenant-scoped), so no
organization filter is needed on the joins. Entries are normalised to
{ id, code, title, description } where `code` is synthesised for
frameworks without a stable code column (EU AI Act controls, ISO 42001
Annex categories).
…hook

Thin apiServices wrapper that fetches /master-controls/framework-catalog,
normalises the response envelope, and exposes a FrameworkCatalog type
keyed by FrameworkEntityType. useFrameworkCatalog wraps the call in a
React Query with a 1-hour staleTime — struct rows only change when
migrations ship, so aggressive caching is safe and keeps the Mappings
tab picker snappy.
…ngs tab

Replaces the raw numeric entity-id field with an MUI Autocomplete backed
by the framework catalog hook. Users pick a framework, choose the entity
type, then search by code or title — the dropdown shows real struct
rows (e.g. "Clause 6.1.2 — Risk management process") and the selected
row's struct id is what gets persisted.

Changing framework or entity type clears the previous selection so a
mismatched pair can never be submitted. Catalog load errors surface
inline; empty entity-type lists show a helpful placeholder. Results are
capped at 200 to keep the popper performant on the larger struct tables.
file_entity_links has no FK back to master_controls, so rows keyed on
entity_type='master_control' aren't removed by cascade when the master
is deleted. Add explicit DELETE inside the same transaction as the
master + mappings cleanup. Only the association row goes away — the
underlying file in `files` is preserved in case it's attached elsewhere.

The generic /api/files/attach-bulk, /files/entity/:framework/:type/:id,
and /files/detach endpoints already accept arbitrary framework/entity
type strings, so no new routes are needed for master control evidence.
Replaces the read-only signpost tab with a working upload/list/remove
flow. Files are posted to /file-manager (source='evidence') then linked
via /files/attach-bulk with framework_type='master_control' and
entity_type='master_control'. The list reads back through
GET /files/entity/master_control/master_control/:id and the trash icon
calls DELETE /files/detach.

Uploads can be multi-select; each picked file is uploaded in sequence so
the apiServices queue stays predictable. File-manager ids are resolved
from both the {data:{id}} and flat shapes to tolerate both historical
response envelopes. Errors surface inline via the mutation's onError.
- Replace monospace field chips with themed pill chips (primary accent
  background, border in theme.palette.border.light).
- Translate raw snake_case field/entity keys to human labels
  ("implementation_details" -> "Implementation details").
- Add uppercase section headers for "Fields that will propagate" and
  "Affected framework rows" to match the drawer tab structure.
- Swap the non-existent theme.palette.warning.main references for the
  canonical theme.palette.warning.text token.
- Tighten the rows column to a fixed-width cell so long titles don't
  push the count off the edge.
…2001 convention

Bump drawer width to 800, replace border-bottom header seam with Divider,
stabilize header minHeight and truncate long titles so the header does
not resize when switching between controls.
Select layout-only sx props (width/flexGrow/minWidth/maxWidth) are lifted
to the wrapper Stack. Without width the status dropdown collapsed to
intrinsic content, misaligning it against the title/description Fields
above. Pin it to the dialog width so all three inputs share the same
edge.
Mapping-form selects now use the same minWidth/maxWidth/flexGrow pattern
as the Details tab, and the submit button matches the 160px minWidth
used by the Save button — so both tabs render identical controls when
the drawer switches between them.
@MuhammadKhalilzadeh
Copy link
Copy Markdown
Collaborator Author

Screenshot (35) Screenshot (37) Screenshot (42) Screenshot (41) Screenshot (40) Screenshot (39) Screenshot (38) Screenshot (36)

CC: @gorkem-bwl

@MuhammadKhalilzadeh
Copy link
Copy Markdown
Collaborator Author

Screenshot (35) Screenshot (37) Screenshot (42) Screenshot (41) Screenshot (40) Screenshot (39) Screenshot (38) Screenshot (36)
CC: @gorkem-bwl

These are all the current screen shots. This was a big implementation, so far 61 commits, and will be more. Lots of things needs to be enhanced and fixed. These are just attached for the purpose of displaying current status.

@MuhammadKhalilzadeh MuhammadKhalilzadeh self-assigned this Apr 16, 2026
@MuhammadKhalilzadeh MuhammadKhalilzadeh added frontend Frontend related tasks/issues backend Backend related tasks/issues integrations labels Apr 16, 2026
@MuhammadKhalilzadeh MuhammadKhalilzadeh added this to the 2.3 milestone Apr 16, 2026
MuhammadKhalilzadeh and others added 10 commits April 17, 2026 12:17
…" to "Controls"

Industry standard per Drata/Vanta — drop "Hub" suffix from user-facing labels.
URL path and component ids remain stable (/controls-hub).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Replace custom dashed-border EmptyState with the shared EmptyState +
EmptyStateTip pattern used across AgentDiscovery, EvidenceHub, etc.
Keeps the Import/New CTAs and seed-result alerts.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
… framework mappings

New migration adds rationale (TEXT), coverage (full|partial), and
confidence (direct_match|strong_analogy|partial_overlap) columns to
master_control_framework_mappings. Backend model, utils, controller,
seed service, and client domain model all updated. Seed file annotated
with per-mapping confidence scores.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
…ment preview in Mappings tab

Each mapping row now displays:
- Plain-language title as primary label with code in monospace badge
- Coverage badge (Full/Partial) with green/amber coloring
- Confidence badge for non-direct-match seed mappings
- Rationale text in italic when provided
- Collapsible requirement description from framework catalog

Add-mapping form gains rationale textarea and coverage selector.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
…rkCell chips

Chips now show framework_entity_title (truncated to 30 chars) as the
primary label instead of monospace codes. Full tooltip shows code + title.
Partial-coverage mappings use amber background/border for visual
distinction.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
…etail

Replace generic "N rows across M frameworks" summary with a human-readable
description like "This will update status on 3 ISO 42001 sub-clauses,
2 EU AI Act controls." Results are grouped by framework. Each affected
row shows its requirement title (from framework catalog) instead of
just type + id. Skipped mappings are separated into their own section.
Field chips now show the incoming value (→ "In progress").

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Add role="alert" on error alerts in page shell
- Add aria-busy + aria-label on loading skeleton area
- Add role="status" + aria-label on overdue badge in matrix
- Existing a11y already covers: aria-sort on sortable headers,
  keyboard nav (Enter/Space) on rows and headers, aria-label on all
  icon-only buttons, aria-labelledby on modals/drawer, role="region"
  on bulk edit bar, focus-visible outlines

Co-Authored-By: Claude Opus 4.6 <[email protected]>
…nce, bump CLAUDE.md dates

Add mapping metadata columns (rationale, coverage, confidence) to the
data model diagram and API description. Document orphan detection and
guided mapping as future work. Bump Last Updated across all CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
PropagationPreviewModal now imports useFrameworkCatalog from the
useMasterControls hook module, so the existing vi.mock needs the
additional export to prevent test failures.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Backend related tasks/issues frontend Frontend related tasks/issues integrations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant