Skip to content

fix(risks): cs-727 deploy-review fixes (acceptance race, approval drift baseline, error surfacing)#3497

Merged
tofikwest merged 2 commits into
mainfrom
tofik/cs-727-deploy-review-fixes
Jul 24, 2026
Merged

fix(risks): cs-727 deploy-review fixes (acceptance race, approval drift baseline, error surfacing)#3497
tofikwest merged 2 commits into
mainfrom
tofik/cs-727-deploy-review-fixes

Conversation

@github-actions

@github-actions github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #3493 (CS-727): fixes the cubic findings raised on the main→release deploy PR #3496. 13 of 16 findings fixed here; 3 rebutted on their threads (cascade-delete of acceptance history — settled on #3493, published RTP snapshots + AuditLog preserve the trail; the submit-gate TOCTOU — would require serializing every Risk/Vendor write against ISMS submits for an advisory gate; openapi.json regeneration — four-PR precedent, spec pipeline reads main).

Correctness

  • Acceptance race: createForRisk/createForVendor now run in a transaction that takes a FOR UPDATE row-lock on the subject, so a concurrent residual PATCH serializes with the read→freeze→insert sequence — a fresh acceptance can no longer be recorded against an already-superseded rating (and can never be born stale).
  • Approval drift baseline: approve() used to collect the platform snapshot before its transaction while the published rows were loaded inside it — a concurrent edit in that window made a just-approved document immediately show "may be out of date". collectPlatformData gained an optional transaction client and the approval flow collects the baseline under the document lock, in the same transaction as the frozen rows.

UX / client

  • useAcceptances surfaces ApiResponse.error (the useApiSWR fetcher never throws on API failures, so swr.error alone missed forbidden/failed responses); the acceptance card now renders an explicit "Couldn't load the acceptance history" + Retry state instead of misreporting a failed load as "No acceptance recorded yet".
  • The acceptance dialog only preselects the owner when they are actually in the selectable member list (a deactivated owner previously submitted and failed server-side).
  • The RTP page keeps "Submit for approval" blocked while readiness data is loading or failed, instead of letting the dialog complete into the server-side completeness error.

Documents / docs

  • The methodology's matrix intro misstated the bands (it attributed the raw-product ranges to the normalized score). It now states both: score bands (Very low 1-2 … Very high 9-10) and their raw-product equivalents (1-5 … 21-25). Same correction in the in-app matrix preview caption.
  • Acceptance response schemas document authType/authenticatedUser (which the controllers actually return) and a 500 response, matching the sibling risk schemas.
  • Comment fixes: the PDF column-sizing note says 10 columns; the defaults header attributes the treatment-option names to ISO 27001 (not 27002).

Structure

  • The RTP readiness queries moved out of the oversized isms.service.ts into documents/risk-treatment-readiness.ts; the service keeps only the thin submit-gate and page-payload wrappers.

Verification

  • 664 API tests + 214 app tests green (new regression test asserts the create path row-locks and writes through one transaction; the approve spec asserts the baseline is collected in-tx).
  • Both typechecks clean apart from the documented pre-existing sets; bun run --filter '@trycompai/app' build green.

- acceptance create runs in a transaction with a FOR UPDATE row-lock on
  the subject, so a concurrent residual edit can no longer produce an
  acceptance frozen at a superseded rating
- approve() collects the drift baseline INSIDE the approval transaction
  (collectPlatformData gained an optional client), so a just-approved
  document can't immediately read as stale
- useAcceptances surfaces ApiResponse.error (apiClient never throws);
  the acceptance card renders an error + retry state instead of
  misreporting a failed load as "no acceptance recorded"
- acceptance dialog only preselects the owner when they are selectable
- RTP submit stays blocked while readiness is loading or failed
- methodology matrix intro states score bands AND raw-product bands
  (the previous wording misattributed raw ranges to the score)
- readiness queries extracted to risk-treatment-readiness.ts
- acceptance response schemas document authType/authenticatedUser + 500
- comment fixes: 10-column table note, ISO 27001 attribution
@linear

linear Bot commented Jul 24, 2026

Copy link
Copy Markdown

CS-727

@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app Ready Ready Preview, Comment Jul 24, 2026 4:21am
comp-framework-editor Ready Ready Preview, Comment Jul 24, 2026 4:21am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
portal Skipped Skipped Jul 24, 2026 4:21am

Request Review

@tofikwest tofikwest changed the title [dev] [tofikwest] tofik/cs-727-deploy-review-fixes fix(risks): cs-727 deploy-review fixes (acceptance race, approval drift baseline, error surfacing) Jul 24, 2026

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

cubic analysis

2 issues found across 15 files

Confidence score: 3/5

  • In apps/api/src/isms/documents/data-source.ts, the RTP stale-check runs in a transaction that can see different snapshots under READ COMMITTED, so a just-approved Risk Treatment Plan may be marked stale immediately and trigger incorrect workflow/state transitions — use a single consistent-read strategy (e.g., stronger isolation or locking/statement structure that guarantees one snapshot for the check).
  • In apps/api/src/risks/schemas/risk-acceptances.responses.ts, the new 500 schema entries are not connected to the controller decorators, so Swagger omits internal-server-error responses and clients won’t see accurate failure contracts — wire [500] into both risk-acceptance decorators to expose the intended API docs.
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/documents/data-source.ts">

<violation number="1" location="apps/api/src/isms/documents/data-source.ts:32">
P2: A just-approved Risk Treatment Plan can be marked stale immediately: this transaction client does not guarantee one consistent read snapshot under the repository's default READ COMMITTED isolation, and the RTP rows are read again later in the same transaction. Configuring a repeatable-read/serializable transaction for approval, or otherwise locking the rendered risk/vendor rows, would keep `riskTreatmentFingerprint` aligned with the frozen RTP.</violation>
</file>

<file name="apps/api/src/risks/schemas/risk-acceptances.responses.ts">

<violation number="1" location="apps/api/src/risks/schemas/risk-acceptances.responses.ts:173">
P3: The new 500 response definitions are unreachable from the controller decorators, so Swagger still omits the documented internal-server-error response. Wiring `[500]` into both risk acceptance decorators (and the vendor equivalents) would make this addition effective.</violation>
</file>

Linked issue analysis

Linked issue: CS-727: [Feature] - Risks module updates

Status Acceptance criteria Notes
Row-lock acceptance creation so a fresh acceptance cannot be recorded against an already-superseded residual rating createForRisk/createForVendor run inside db.$transaction and take a FOR UPDATE row lock before reading and inserting; tests assert the transaction and the FOR UPDATE query.
Collect the approval drift baseline inside the approval transaction so a just-approved document cannot immediately show as stale collectPlatformData accepts an optional transaction client and approve() now calls it inside the document-lock transaction; unit spec asserts client threading.
Surface acceptance-history loading errors in the UI (don’t show a failed load as 'Awaiting acceptance') and offer a Retry useAcceptances now converts ApiResponse.error into an error for callers; ResidualAcceptanceCard renders an explicit error + Retry instead of treating failure as empty history.
Acceptance dialog only preselects the owner when they are actually selectable/active RecordAcceptanceDialog computes a valid default and uses it; prevents silently submitting a deactivated owner.
Keep 'Submit for approval' blocked while RTP readiness data is loading or failed (so the dialog cannot be completed into a server-side completeness error) RiskTreatmentPlanClient sets a blockedReason when readiness is loading or errored, rather than allowing the submit dialog to proceed.
Correct the Risk Assessment Methodology matrix text and preview so score bands and raw-product equivalents are stated consistently Methodology narrative updated to state normalized score calculation and band mappings; preview text updated to match.
Extract RTP readiness queries into a focused loader and use it where readiness is checked (refactor to support tx-client usage) Readiness logic moved to documents/risk-treatment-readiness.ts and isms.service now uses the loader (including tx client) for completeness checks and approval gating.

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread apps/api/src/isms/documents/data-source.ts
Comment thread apps/api/src/risks/risk-acceptances.service.ts Outdated
Comment thread apps/api/src/risks/schemas/risk-acceptances.responses.ts
Comment thread apps/api/src/risks/schemas/risk-acceptances.responses.ts Outdated
- RTP submit gating fails closed: a load/revalidation error blocks
  submission even when stale cached readiness data is present
- approve() runs REPEATABLE READ (single MVCC snapshot), so the drift
  baseline and the frozen rows can never observe different data; one
  retry on a P2034 write conflict re-runs with a fresh snapshot
- acceptance row locks are org-scoped, so a foreign-tenant id can never
  acquire another organization's row lock
- 500 responses wired into all four acceptance endpoint decorators
- authType documents the 'service' value HybridAuthGuard can return

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 7 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

@tofikwest
tofikwest merged commit 7446c89 into main Jul 24, 2026
12 checks passed
@tofikwest
tofikwest deleted the tofik/cs-727-deploy-review-fixes branch July 24, 2026 04:42
claudfuen pushed a commit that referenced this pull request Jul 24, 2026
# [3.108.0](v3.107.0...v3.108.0) (2026-07-24)

### Bug Fixes

* **device-registration:** set source to device_agent when agent adopts intune device ([#3495](#3495)) ([1071066](1071066))
* **risks:** cs-727 deploy-review fixes (acceptance race, approval drift baseline, error surfacing) ([#3497](#3497)) ([7446c89](7446c89))

### Features

* **risks:** risk acceptance events + ISMS 6.1.2 methodology and 6.1.3 treatment plan documents (CS-727) ([#3493](#3493)) ([8c4b358](8c4b358))
@claudfuen

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.108.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants