feat(isms): internal audit programme, plan and report — clause 9.2 (CS-724) - #3468
Conversation
…S-724) Adds the ninth ISMS document type: Compliance > ISMS > Internal Audit. - Programme paragraph (editable, seeded with a default) rendered verbatim into the generated document - Audit instances with server-generated IA-YYYY-NN references, templated scope/criteria, auditor pulled from ISMS > Roles > Internal Auditor, planned dates, status, and a conclusion verdict + narrative - Controls Tested table seeded with the default 15-row sample set (management-system clauses + high-impact Annex A controls); result + notes recorded per row - Findings with server-generated F-NN references, type/owner/due date/status/closure evidence, optional link back to a control row - Three sign-off slots per audit, frozen into the published version snapshot and rendered as the document's sign-off table - Submit gate: at least one audit, and a conclusion verdict on every completed audit (enforced server-side, mirrored in the client)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The Vercel next build enforces the design-system unions that the local tsc pass missed: Stack gap has no '5', TableCell omits className, and Select.onValueChange passes string | null.
There was a problem hiding this comment.
cubic analysis
8 issues found and verified against the latest diff
Confidence score: 2/5
apps/api/src/isms/isms-registers.controller.tsremaps audit/finding mutations toevidence:update, which can grant evidence editors unintended write access and prevents proper custom-role scoping for audit/finding resources. This is the riskiest regression because it affects authorization boundaries — restore dedicated RBAC resources for audit/finding actions before merging.apps/api/src/isms/registers/register-registry.tsaccepts arbitrary auditor text, andapps/app/src/app/(app)/[orgId]/documents/isms/[type]/page.tsxdeduplicates same-name auditors while storing only names, so records can be assigned to the wrong or non-holder person. That creates concrete data integrity and traceability issues in audit ownership — enforce Internal Auditor holder validation and persist/select by member ID before merging.apps/api/src/isms/isms-audit.service.tsderivesIA-YYYY-NNfrom remaining rows, so deleting the latest/only audit can cause reference reuse and duplicate-looking identifiers. Merging as-is risks confusing audit history and downstream references — persist a monotonic per-year sequence (or equivalent) before merge.apps/api/src/isms/documents/generate.tsintroduces a new regeneration/approval dispatch path withoutrunDerivationcoverage, and related wording logic is split across API/UI (apps/api/src/isms/documents/internal-audit-defaults.ts), increasing the chance of silent generation or narrative drift regressions. Add focused derivation tests and consolidate/shared verdict text mapping to de-risk release.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/api/src/isms/isms-audit.service.ts">
<violation number="1" location="apps/api/src/isms/isms-audit.service.ts:162">
P2: Deleting the only or highest audit for a year causes the next audit to reuse its `IA-YYYY-NN` reference because the sequence is derived solely from remaining rows. Persist the per-document/year sequence (or otherwise retain consumed references) if references must remain non-reusable as this service documents.</violation>
</file>
<file name="apps/app/src/app/(app)/[orgId]/documents/isms/components/AuditFindingFields.tsx">
<violation number="1" location="apps/app/src/app/(app)/[orgId]/documents/isms/components/AuditFindingFields.tsx:76">
P3: According to linked Linear issue CS-724, each finding type's plain-English explanation is specified as a tooltip; rendering it permanently below the selector changes the requested interaction and adds repeated vertical content to every finding form. Exposing this description through the design-system tooltip on the type control would match the requirement.</violation>
<violation number="2" location="apps/app/src/app/(app)/[orgId]/documents/isms/components/AuditFindingFields.tsx:91">
P2: According to linked Linear issue CS-724, the Clause or control field should pre-fill from the linked Controls Tested row; in edit mode, `AuditFindingRow` does not provide `onRelatedControlPicked`, so selecting a related control leaves this field unchanged. Passing the same prefill callback from the edit form (or moving the synchronization into this shared component) would keep linked findings consistent.</violation>
</file>
<file name="apps/api/src/isms/documents/generate.ts">
<violation number="1" location="apps/api/src/isms/documents/generate.ts:265">
P3: Internal Audit regeneration/approval now depends on this new dispatch path, but it has no `runDerivation` coverage. A later dispatch or narrative-preservation regression could make Clause 9.2 generation fail without a focused test detecting it; a small seed-and-preserve test would make this behavior explicit.</violation>
</file>
<file name="apps/app/src/app/(app)/[orgId]/documents/isms/[type]/page.tsx">
<violation number="1" location="apps/app/src/app/(app)/[orgId]/documents/isms/[type]/page.tsx:194">
P3: Audits cannot distinguish two Internal Auditor holders who share the same display name because this deduplication removes one option and the form stores only the name string. Preserving a member ID in the option/value (or disambiguating labels with a stable secondary identifier) would keep the dropdown selection unambiguous.</violation>
</file>
<file name="apps/api/src/isms/documents/internal-audit-defaults.ts">
<violation number="1" location="apps/api/src/isms/documents/internal-audit-defaults.ts:27">
P3: The API adds a second conclusion-verdict map and sentence builder while the same mapping/output already exists in the app, so UI and exported-document wording can drift when a verdict or copy changes. A shared typed constant/helper would keep both surfaces consistent.</violation>
</file>
<file name="apps/api/src/isms/registers/register-registry.ts">
<violation number="1" location="apps/api/src/isms/registers/register-registry.ts:201">
P2: According to linked Linear issue CS-724, Auditor must be pulled from the ISMS Roles > Internal Auditor assigned holder. This API accepts arbitrary auditor text, so a direct caller can save an auditor who is not the configured holder and the generated Clause 9.2 document will report the wrong person; resolving the current role assignment (or validating a submitted member/reference against it) would keep the audit tied to Roles.</violation>
</file>
<file name="apps/api/src/isms/isms-registers.controller.ts">
<violation number="1" location="apps/api/src/isms/isms-registers.controller.ts:202">
P1: Audit and finding mutations now inherit `evidence:update` rather than their dedicated RBAC resources, so custom roles cannot be scoped correctly and evidence editors may gain unintended audit/finding write access. Resolving permissions per register and operation (for example `audit:update` for audit rows and `finding:create|update|delete` for findings) would preserve the repository's resource-level RBAC model.</violation>
</file>
Linked issue analysis
Linked issue: CS-724: [Feature] Area- Internal Audit
| Status | Acceptance criteria | Notes |
|---|---|---|
| ✅ | Programme paragraph seeded with auditor-defensible default, editable, and rendered verbatim into the generated document | Default programme text function, derivation and seeding on document create, narrative schema, client ProgrammeCard and export mapping are present and tested. |
| ✅ | Audits register with server-generated IA-YYYY-NN reference and fields (scope, criteria defaults, auditor, planned dates, status, conclusion template) | DB migration, Prisma model, service for audits, client audit form fields and default-scope/criteria constants are implemented and covered by tests. |
| ✅ | Controls Tested table seeded with the 15 default rows (idempotent by controlKey); editable rows and result dropdown exist | Seed definitions and seed-if-missing function exist, CRUD service for audit controls added, UI components for the controls table/rows and constants for result enums included. |
| ✅ | Result dropdown saves and Notes editable; rows can be added/edited/deleted (seeded rows deletable) | Control service implements create/update/delete, client components support inline editing and immediate saves; tests and register wiring present. |
| ✅ | Findings inline below Controls Tested with server-generated F-NN, types, optional related-control link, owner/due/status/closure fields | DB model and migration for findings, finding service CRUD, client finding forms/UI, export mapping and tests are present. |
| ✅ | Sign-off: three slots (Auditor / SPO / Top Management) stored on audit and frozen into the version snapshot for export | Sign-off fields added to DB model, client sign-off card exists, export code includes audits and sign-off in exported rows, and version/export wiring includes internal audit extras. |
| ✅ | Submit gate: at least one audit required and completed audits must have a conclusion verdict (server and client validation) | Client validation messages mirror server rules; server lifecycle test blocks submission for no audits; document-level assertion and validation functions are implemented. |
| ✅ | Generated document export mirrors reference DOCX (Programme · Audit plan · Controls Tested · Findings · Conclusion · Sign-off) | Export builders, mapping of controls/findings/sign-off, tests for export data and PDF/DOCX rendering verification are included. |
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
- scope audit/control/finding lookups to the internal-audit document type - seed the programme paragraph only when the draft narrative is empty, so a concurrent setup call can never overwrite an edit - trim required text fields (scope, criteria, control reference) so whitespace-only values are rejected - marking a control row non-conformity/observation now opens the findings form pre-filled with the linked row (per the ticket flow), and the edit form pre-fills clause text like the add form - preserve unsaved sign-off input across sibling register refreshes - gate the control-row Save button on validity - document the audit register fields in the OpenAPI body schema - cover the internal-audit derivation path in generate.spec
There was a problem hiding this comment.
All reported issues were addressed across 15 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
- seed the programme via a conditional update that matches only a NULL draft narrative, removing the read-then-write race entirely (and with it the duplicated empty-narrative predicate) - key the prefilled linked-finding form by row and type, so re-marking the same control with a different result re-initializes the form
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
…rate
The programme seed's conditional update now matches NULL or {} — the
same empty definition generateNarrative uses — so the two seed paths
can never drift while still never overwriting a populated draft.
Closed corrective actions now show how they were evidenced in the generated document — appended to the description cell, keeping the reference document's seven-column findings table.
# [3.106.0](v3.105.0...v3.106.0) (2026-07-22) ### Bug Fixes * **auth:** attribute API-key mutations to the key's creator, not the org owner ([#3472](#3472)) ([206ed96](206ed96)), closes [hi#risk](https://github.com/hi/issues/risk) * **deps:** bump adm-zip 0.5.18 -> 0.6.0 in apps/api (Dependabot [#88](https://github.com/trycompai/comp/issues/88)/[#89](https://github.com/trycompai/comp/issues/89)) ([#3462](#3462)) ([300f2a1](300f2a1)), closes [#3451](#3451) * **deps:** override tar to ^7.5.19 to clear node-tar Dependabot alerts ([#94](https://github.com/trycompai/comp/issues/94)-[#104](https://github.com/trycompai/comp/issues/104)) ([#3466](#3466)) ([8ab5709](8ab5709)) * **deps:** patch engine.io ([#93](#93)) and body-parser ([#92](#92)) Dependabot alerts ([#3464](#3464)) ([94c33b1](94c33b1)) * **isms:** harden internal-audit validation and edge cases from deploy review ([#3473](#3473)) ([c6c7379](c6c7379)) * **policies:** create draft version on policy regenerate instead of overwriting published ([#3471](#3471)) ([ff31dbd](ff31dbd)) * **policies:** delete detached PDF objects when regenerating a draft ([#3474](#3474)) ([ecd1bd0](ecd1bd0)) * **policies:** rename CreateVersionDto to avoid swagger collision with automations ([#3469](#3469)) ([2d5290a](2d5290a)) ### Features * **isms:** internal audit programme, plan and report — clause 9.2 (CS-724) ([#3468](#3468)) ([42e5ebd](42e5ebd)), closes [hi#impact](https://github.com/hi/issues/impact)
|
🎉 This PR is included in version 3.106.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This is an automated pull request to merge tofik/cs-724-feature-area-internal-audit into dev.
It was created by the [Auto Pull Request] action.
Summary by cubic
Adds the Internal Audit (ISO 27001 9.2) as a new ISMS document with programme, audits, controls, findings, sign‑off, and export (now including owner names and closure evidence), plus safety and UX fixes aligning with
@trycompai/design-system; delivers CS-724.New Features
internal_auditdocument at Compliance > ISMS > Internal Audit; programme paragraph seeded atomically only when empty and editable.IA-YYYY-NNrefs, templated scope/criteria, auditor from Roles, planned dates, status, and conclusion verdict + notes.F-NNrefs, type/owner/due/status/closure evidence, optional link to a control; three sign‑off slots captured and frozen in versions.Bug Fixes
internal_audit; atomic programme seed via conditional update matching NULL or {} to avoid race and align with generate; trimmed required text fields.Written for commit 36c2473. Summary will update on new commits.