Skip to content

SOA as document: replace export with publish workflow#1022

Merged
SachaProbo merged 1 commit intomainfrom
SachaProbo/soa-as-document
Apr 16, 2026
Merged

SOA as document: replace export with publish workflow#1022
SachaProbo merged 1 commit intomainfrom
SachaProbo/soa-as-document

Conversation

@SachaProbo
Copy link
Copy Markdown
Contributor

@SachaProbo SachaProbo commented Apr 10, 2026

Statements of Applicability are no longer exported as one-off PDFs. Instead, each SOA owns a persistent document that accumulates versions over time, following the same publish/approve lifecycle as authored documents.

Publishing without approvers publishes immediately; publishing with approvers creates a draft pending approval via the existing quorum system. SOAs can also store default approvers that are pre-populated in the publish dialog.

The SOA is removed from the snapshot system — applicability statements are now queried directly (snapshot_id IS NULL) rather than through snapshot copies.

A standalone migration script (cmd/migrate-soa-snapshots-to-documents) converts existing SOA snapshots into documents with proper ProseMirror content, preserving version history and approval decisions.


Summary by cubic

Replaces one-off SOA PDF exports with persistent, generated documents that follow the publish/approve workflow. SOAs now version like other documents and can be published immediately or sent for approval; snapshots and export are removed.

  • New Features

    • SOA is a STATEMENT_OF_APPLICABILITY document with GENERATED write mode; generated versions aren’t editable.
    • Document engine: added DocumentWriteMode (default AUTHORED) and DocumentVersionOrientation; GeneratedDocumentService renders SOA content from templates/statement_of_applicability.json.tmpl and supports landscape output.
    • Console: new SOA Publish dialog; removed export and snapshot routes; document list adds type and write‑mode filters; description/details pages block edits when writeMode=GENERATED.
    • GraphQL/MCP: added enums/fields for document type (STATEMENT_OF_APPLICABILITY), write mode, and orientation; document queries (incl. control/measure/risk) support write‑mode filtering; SOA exposes its document; electronic signatures include SOA type.
    • Policies: replaced SOA export permission with publish permission.
    • CLI/n8n/E2E: probo soa publish; probo document list --write-mode; n8n adds statementOfApplicability resource (create/get/list/update/delete/publish) and document listing supports SOA type; new SOA publish E2E tests.
  • Migration

    • Removed SOA from snapshots and related routes/filters; queries no longer use snapshot IDs.
    • cmd/migrate-soa-snapshots-to-documents converts existing SOA snapshots into documents with preserved versions and approvals.

Written for commit c635492. Summary will update on new commits.

@SachaProbo SachaProbo marked this pull request as draft April 10, 2026 16:40
@SachaProbo SachaProbo force-pushed the SachaProbo/soa-as-document branch from a7b699b to 396a25a Compare April 10, 2026 16:43
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 issues found across 48 files

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="e2e/console/statement_of_applicability_test.go">

<violation number="1" location="e2e/console/statement_of_applicability_test.go:487">
P2: Authorization negative tests should assert FORBIDDEN explicitly, not just any error.</violation>
</file>

<file name="apps/console/src/pages/organizations/statements-of-applicability/dialogs/PublishStatementOfApplicabilityDialog.tsx">

<violation number="1" location="apps/console/src/pages/organizations/statements-of-applicability/dialogs/PublishStatementOfApplicabilityDialog.tsx:108">
P2: Only close/reset the dialog after a confirmed successful publish result; otherwise show an error to avoid silent failure UX.</violation>
</file>

<file name="apps/console/src/pages/organizations/statements-of-applicability/dialogs/CreateStatementOfApplicabilityDialog.tsx">

<violation number="1" location="apps/console/src/pages/organizations/statements-of-applicability/dialogs/CreateStatementOfApplicabilityDialog.tsx:100">
P2: Handle GraphQL errors in `onCompleted` before showing success. Right now the dialog can report success for failed mutation completions.</violation>
</file>

<file name="pkg/coredata/control_obligation.go">

<violation number="1" location="pkg/coredata/control_obligation.go:163">
P1: Prefixing `scope.SQLFragment()` with `o.` breaks `NoScope` (`TRUE` becomes `o.TRUE`) and causes runtime SQL errors.</violation>
</file>

<file name="pkg/coredata/document.go">

<violation number="1" location="pkg/coredata/document.go:38">
P1: Adding `ContentSource` to `Document` without updating all `Document` SELECT projections will break row scanning in some loaders (`LoadByControlID`, `LoadByRiskID`, `LoadByMeasureID`).</violation>
</file>

<file name="pkg/probo/statement_of_applicability_service.go">

<violation number="1" location="pkg/probo/statement_of_applicability_service.go:239">
P2: Validate that each DefaultApproverID belongs to the statement’s organization before merging. Without this check, profiles from other organizations in the tenant can be attached as default approvers.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread pkg/coredata/control_obligation.go Outdated
Comment thread pkg/coredata/document.go Outdated
Comment thread e2e/console/statement_of_applicability_test.go
Comment thread pkg/probo/statement_of_applicability_service.go Outdated
@SachaProbo SachaProbo force-pushed the SachaProbo/soa-as-document branch 11 times, most recently from 4eba208 to 77e3231 Compare April 13, 2026 13:28
@SachaProbo
Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai review please

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented Apr 13, 2026

@cubic-dev-ai review please

@SachaProbo I have started the AI code review. It will take a few minutes to complete.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 54 files

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="pkg/coredata/snapshot.go">

<violation number="1" location="pkg/coredata/snapshot.go:166">
P2: `LoadByOrganizationID` now excludes `STATEMENTS_OF_APPLICABILITY`, but `CountByOrganizationID` does not. This creates inconsistent list vs total-count results for snapshot pagination.</violation>
</file>

<file name="pkg/coredata/migrations/20260410T120000Z.sql">

<violation number="1" location="pkg/coredata/migrations/20260410T120000Z.sql:36">
P2: Enforce uniqueness on `document_id` to preserve a one-to-one SOA↔Document relationship for documentId-based lookups.</violation>
</file>

<file name="pkg/server/api/mcp/v1/specification.yaml">

<violation number="1" location="pkg/server/api/mcp/v1/specification.yaml:6152">
P2: The SOA list filter still documents `snapshot_id` semantics even though SOA now links to `document_id`, leaving an inconsistent API contract.</violation>

<violation number="2" location="pkg/server/api/mcp/v1/specification.yaml:6235">
P2: `default_approver_ids` is write-only in the schema, so clients cannot read saved default approvers from SOA responses.</violation>
</file>

<file name="pkg/probo/generated_document_service.go">

<violation number="1" location="pkg/probo/generated_document_service.go:53">
P2: External I/O (`GetFileBase64` to object storage) and pure computation (`BuildStatementOfApplicabilityProseMirrorDocument`) both execute inside the database transaction, holding the DB connection open unnecessarily. Consider restructuring: fetch data (including logo) and build the ProseMirror document *before* the transaction, then pass the result into a shorter write-only transaction for inserts/updates.

(Based on your team's feedback about splitting data fetching from generation and keeping transactions short-lived.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread pkg/coredata/snapshot.go
Comment thread pkg/coredata/migrations/20260410T120000Z.sql Outdated
Comment thread pkg/server/api/mcp/v1/specification.yaml
Comment thread pkg/server/api/mcp/v1/specification.yaml Outdated
Comment thread pkg/probo/generated_document_service.go
@SachaProbo SachaProbo force-pushed the SachaProbo/soa-as-document branch 2 times, most recently from c9d64ed to 671dbc5 Compare April 13, 2026 16:18
@SachaProbo SachaProbo marked this pull request as ready for review April 13, 2026 16:22
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 65 files

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="cmd/migrate-soa-snapshots-to-documents/main.go">

<violation number="1" location="cmd/migrate-soa-snapshots-to-documents/main.go:476">
P1: Custom agent: **Avoid Logging Sensitive Information**

Database password can leak to stderr when DSN parsing fails. `url.Parse` embeds the full URL (including credentials) in its error output, and this error is printed to stderr in `main()`. Redact the DSN or return a generic error instead of wrapping `url.Parse`'s error directly.</violation>
</file>

<file name="pkg/probo/generated_document_service.go">

<violation number="1" location="pkg/probo/generated_document_service.go:429">
P2: Non-applicable controls incorrectly show a "not implemented" justification. The `implemented` column correctly displays "-" for non-applicable controls, but `notImplJustification` doesn't check applicability, so it can still show justification text — contradicting the annex definition on line 603 ("This field is empty … when the control is not applicable").</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread cmd/migrate-soa-snapshots-to-documents/main.go Outdated
Comment thread pkg/probo/generated_document_service.go Outdated
@SachaProbo SachaProbo force-pushed the SachaProbo/soa-as-document branch 3 times, most recently from 05527e1 to a5a080a Compare April 13, 2026 16:46
@SachaProbo SachaProbo requested a review from a team April 13, 2026 16:56
@SachaProbo SachaProbo force-pushed the SachaProbo/soa-as-document branch 4 times, most recently from edcf71f to 7242f2b Compare April 14, 2026 15:38
Comment thread pkg/coredata/migrations/20260410T120000Z.sql Outdated
Comment thread pkg/coredata/migrations/20260410T120000Z.sql Outdated
Comment thread pkg/coredata/snapshot.go
Comment thread pkg/coredata/snapshot.go
Comment thread pkg/server/api/console/v1/v1_resolver.go Outdated
@SachaProbo SachaProbo force-pushed the SachaProbo/soa-as-document branch 13 times, most recently from 7d299e1 to 35a4956 Compare April 15, 2026 20:20
Statements of Applicability are no longer exported as one-off PDFs.
Instead, each SOA owns a persistent document that accumulates versions
over time, following the same publish/approve lifecycle as authored
documents.

Publishing without approvers publishes immediately; publishing with
approvers creates a draft pending approval via the existing quorum
system. SOAs can also store default approvers that are pre-populated in
the publish dialog.

The SOA is removed from the snapshot system — applicability statements
are now queried directly (snapshot_id IS NULL) rather than through
snapshot copies.

A standalone migration script (cmd/migrate-soa-snapshots-to-documents)
converts existing SOA snapshots into documents with proper ProseMirror
content, preserving version history and approval decisions.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
@SachaProbo SachaProbo force-pushed the SachaProbo/soa-as-document branch from 35a4956 to c635492 Compare April 15, 2026 22:01
@SachaProbo SachaProbo merged commit c635492 into main Apr 16, 2026
17 checks passed
@SachaProbo SachaProbo deleted the SachaProbo/soa-as-document branch April 16, 2026 06:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants