Draft
Conversation
Co-Authored-By: Claude Opus 4.6 <[email protected]>
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]>
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]>
…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.
Collaborator
Author
CC: @gorkem-bwl |
Collaborator
Author
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. |
…" 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]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
















Controls Hub
Descriptions will be added here soon
Please ensure all items are checked off before requesting a review: