Skip to content

[comp] Production Deploy#2878

Open
github-actions[bot] wants to merge 23 commits into
releasefrom
main
Open

[comp] Production Deploy#2878
github-actions[bot] wants to merge 23 commits into
releasefrom
main

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented May 20, 2026

This is an automated pull request to release the candidate branch into production, which will trigger a deployment.
It was created by the [Production PR] action.


Summary by cubic

Ships the offboarding checklist with vendor access‑revocation tracking, evidence upload/export, and employment event dates/evidence across People and integrations. Also improves Cloud Security auto‑remediation (required‑param validation, deterministic SLR normalization, universal step‑repair) and enables API key/service token mutations with clear audit attribution; sets Frameworks to default to a Controls view; and fixes evidence export OOMs with streaming.

  • New Features

    • API: Offboarding checklist with template CRUD, per‑member checklist, complete/uncomplete with notes/evidence, pending members, per‑vendor access revocations, and ZIP export (per member or all). Adds employment event evidence endpoints and onboard/offboard date fields/filters; auto‑sets offboardDate on deactivate.
    • Cloud Security: API key/service token callers can mark exceptions/revoke/update AWS scan mode, attributed via an ActingUser resolver with labels like [via API key "…"]. Auto‑remediation adds required‑param checks, universal step‑repair on AWS validation errors, and a plan normalizer to backfill CreateServiceLinkedRole AWSServiceName; prompt includes SLR mapping.
    • UI: Employee Offboarding tab (checklist, access revocations, summary), Overview banner/todos, People list shows onboard/offboard dates, Settings to manage checklist template, Frameworks default to Controls tab with requirement identifiers, and offboarding evidence download via app route. DS compatibility fixes; use accent badge for critical items.
    • Integrations: Set onboardDate from Google Workspace user creationTime and generic employee startDate; DSL accepts startDate.
    • Docs/DB: OpenAPI updated; People queries accept onboard/offboard date filters. Prisma migrations add Member.onboardDate/offboardDate and offboarding checklist/access revocation tables with SetNull FKs to preserve history. Dependency bump: @trycompai/design-system1.1.19.
  • Bug Fixes

    • Evidence export: Prevent OOM by loading headers first and streaming each automation with cursor‑based batching; add deterministic ordering and id tiebreaker; extract mappers and JSON builder.
    • Offboarding exports/seed: Batch export‑all with cursor pagination (50), handle archiver errors (abort on failure), sanitize ZIP paths and prefix dangerous CSV chars, include member ID in folders, and use a transaction with re‑check for default template seeding (narrow catch to Prisma conflicts).
    • Offboarding hardening: Enforce evidenceRequired on completeItem; validate evidence fields together and reject partial payloads; roll back completion/revocation if evidence upload fails; delete orphaned attachments when undoing a revocation; validate member belongs to org; scope lookups by org; count completions only for enabled items; validate date query params; clear file inputs after uploads; validate tab param; show error states in TodosOverview and OffboardingBanner; display “Until {date}”; show “All People” when offboard filter overrides status; add noopener/noreferrer to all window.open calls; disable attach‑evidence buttons while processing.
    • People/integrations: Validate provider date strings before parsing; guard employment‑evidence upload when userId is missing under API key auth; clear offboardDate on reactivation; replace heavy vendor‑revocation queries with lightweight counts.
    • UI: Close MultipleSelector dropdown on blur to prevent it blocking adjacent form controls.
    • App proxy: Forward Authorization header in offboarding export route.
    • Follow‑ups: Prefix checklist evidence filenames with attachment ID to avoid ZIP collisions; wrap vendor sync in try/catch and log failures so revocation state is preserved; prevent non‑expandable checklist rows from toggling; normalize preset date ranges to start of day; hide pending invitations when date filters are active; use a controlled tab value in ToDoOverview and revalidate from the server on error; add missing test mock for undoVendorRevocation.
    • Additional: Guard sync errors in revokeAllVendorAccess and undoVendorRevocation; batch attachment queries when loading member checklist evidence (N+1 → 1); extract fileToBase64 to apps/app/src/lib/file-utils.ts; remove unused TodosOverview component.

Written for commit dbc364c. Summary will update on new commits. Review in cubic

@vercel
Copy link
Copy Markdown

vercel Bot commented May 20, 2026

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

Project Deployment Actions Updated (UTC)
app (staging) Ready Ready Preview, Comment May 21, 2026 3:28pm
comp-framework-editor (staging) Ready Ready Preview, Comment May 21, 2026 3:28pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
portal (staging) Skipped Skipped May 21, 2026 3:28pm

Request Review

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.

20 issues found across 55 files

Confidence score: 2/5

  • Several high-confidence backend issues are concrete and user-impacting (8/10), including unsanitized ZIP entry filenames in apps/api/src/offboarding-checklist/offboarding-export.service.ts and unscoped tenant/member deletes in apps/api/src/offboarding-checklist/access-revocation.service.ts and apps/api/src/people/people.controller.ts.
  • There is meaningful data integrity risk in apps/api/src/offboarding-checklist/offboarding-checklist.service.ts: completeItem can remain completed when evidence upload fails, leaving partial state that should be rolled back.
  • Additional medium issues (6–7/10) in checklist/export flows suggest regression potential rather than just style concerns, such as CSV formula injection handling and pending-offboarding count mismatches in apps/api/src/offboarding-checklist/offboarding-export.service.ts and apps/api/src/offboarding-checklist/offboarding-checklist.service.ts.
  • Pay close attention to apps/api/src/offboarding-checklist/offboarding-export.service.ts, apps/api/src/offboarding-checklist/access-revocation.service.ts, apps/api/src/people/people.controller.ts, apps/api/src/offboarding-checklist/offboarding-checklist.service.ts - security scoping, path/input sanitization, and transactional consistency need tightening before merge.
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/app/src/app/(app)/[orgId]/people/[employeeId]/components/AccessRevocationList.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/AccessRevocationList.tsx:52">
P2: Using a single `processingVendorId` causes incorrect loading/disabled behavior when multiple row actions overlap. Track processing per vendor (or globally lock actions) to avoid prematurely re-enabling controls during in-flight requests.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/people/settings/components/OffboardingChecklistSettings.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/settings/components/OffboardingChecklistSettings.tsx:57">
P2: Rolling back with a full-list snapshot can clobber other in-flight item updates. Revert only the failed item field (or revalidate from server) instead of restoring `data: previous`.</violation>
</file>

<file name="apps/api/src/offboarding-checklist/offboarding-checklist.controller.ts">

<violation number="1" location="apps/api/src/offboarding-checklist/offboarding-checklist.controller.ts:251">
P2: Use a DTO with class-validator rules for `revokeVendorAccess` body instead of an inline type, and enforce all-or-none evidence fields with a 400 response on partial payloads.</violation>
</file>

<file name="apps/api/src/offboarding-checklist/offboarding-checklist.service.ts">

<violation number="1" location="apps/api/src/offboarding-checklist/offboarding-checklist.service.ts:49">
P2: `createTemplateItem` has a race condition for `sortOrder`; concurrent creates can assign the same order value.</violation>
</file>

<file name="apps/app/src/hooks/use-access-revocations.ts">

<violation number="1" location="apps/app/src/hooks/use-access-revocations.ts:49">
P2: Add a file-size check before base64 conversion to avoid loading oversized evidence files into browser memory.</violation>
</file>

<file name="apps/api/src/offboarding-checklist/access-revocation.service.ts">

<violation number="1" location="apps/api/src/offboarding-checklist/access-revocation.service.ts:204">
P2: Avoid calling the full `getAccessRevocations` path in completion sync; it does expensive attachment fetches that are unnecessary for count checks.</violation>
</file>

<file name="apps/app/src/hooks/use-employment-evidence.ts">

<violation number="1" location="apps/app/src/hooks/use-employment-evidence.ts:32">
P2: Add a client-side file size check before `readAsDataURL` to avoid loading oversized files into memory before the server rejects them.</violation>
</file>

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

Re-trigger cubic

Comment thread apps/api/src/offboarding-checklist/offboarding-export.service.ts Outdated
Comment thread apps/api/src/offboarding-checklist/offboarding-export.service.ts Outdated
Comment thread apps/api/src/offboarding-checklist/offboarding-checklist.service.ts
Comment thread apps/api/src/offboarding-checklist/access-revocation.service.ts
Comment thread apps/api/src/people/people.controller.ts Outdated
Comment thread apps/app/src/app/(app)/[orgId]/overview/components/TodosOverview.tsx Outdated
Comment thread apps/api/src/offboarding-checklist/access-revocation.service.ts Outdated
Comment thread apps/api/src/people/people.controller.ts Outdated

const uploadEvidence = useCallback(
(file: File): Promise<AttachmentMetadata> => {
return new Promise((resolve, reject) => {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 20, 2026

Choose a reason for hiding this comment

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

P2: Add a client-side file size check before readAsDataURL to avoid loading oversized files into memory before the server rejects them.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/app/src/hooks/use-employment-evidence.ts, line 32:

<comment>Add a client-side file size check before `readAsDataURL` to avoid loading oversized files into memory before the server rejects them.</comment>

<file context>
@@ -0,0 +1,95 @@
+
+  const uploadEvidence = useCallback(
+    (file: File): Promise<AttachmentMetadata> => {
+      return new Promise((resolve, reject) => {
+        const reader = new FileReader();
+        reader.onload = async () => {
</file context>
Fix with Cubic

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.

Won't fix — file size is validated server-side. Adding a client-side check is a nice-to-have but doesn't prevent any actual issue since the server rejects oversized payloads. The file input accept attribute already limits to expected document types.

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.

Thanks for the clarification—understood that server-side validation handles file size here.

Comment thread apps/app/src/app/(app)/[orgId]/people/all/components/TeamMembersClient.tsx Outdated
* feat(frameworks): show controls as default tab with requirement column

Users expect to see controls first when clicking into a framework.
Adds a Controls tab (default) showing all controls with their mapped
requirement identifiers, keeping Requirements as a secondary tab.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(people): use valid badge variant for critical offboarding items

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Mariano Fuentes <marfuen98@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(people): ds component compatibility fixes for offboarding UI

- remove redundant wrapper divs from PopoverContent (DS already renders
  card chrome)
- remove className from PopoverContent (not accepted by DS)
- use render prop on DropdownMenuTrigger to avoid nested buttons
- use accent variant for Critical badge instead of destructive

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(people): override popover width for date range filter

DS PopoverContent has hardcoded w-72 which clips the 380px filter
content. Use inline style to override width to auto.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Mariano Fuentes <marfuen98@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(evidence-export): load automations one at a time to prevent OOM

The export endpoint loaded all automation runs with full results and logs
into memory simultaneously, causing V8 heap OOM at ~6 GB on large orgs.
Now loads lightweight headers first, then each automation's data individually
so peak memory ≈ one automation instead of all combined.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(evidence-export): paginate runs, extract mappers, eliminate duplication

- Paginate loadFullAutomation in batches of 50 runs to bound JSON.parse
  pressure for enormous automations
- Extract Prisma→normalizer mappers (toAppAutomationRun, toCustomAutomationRun)
  as single source of truth — eliminates duplicated mapping in service
- Rewrite getTaskEvidenceSummary to reuse data loader instead of duplicating
  100 lines of query + mapping code
- Extract buildAutomationJson to evidence-json-builder.ts
- Service: 600 → 411 lines

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(evidence-export): add deterministic tiebreaker to paginated queries

Offset pagination ordered only by createdAt could skip or duplicate rows
when multiple runs share the same timestamp. Adding id as a secondary
sort key makes the ordering deterministic across batches.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(evidence-export): use cursor pagination instead of offset

Offset pagination (skip) can duplicate or miss rows when new automation
runs are inserted during a long export. Cursor-based pagination uses
keyset lookups (WHERE id < cursor) which are stable under concurrent
writes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Mariano Fuentes <marfuen98@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tofikwest and others added 2 commits May 20, 2026 15:34
…callers

A customer (Josh) hit a 401 calling
POST /v1/cloud-security/findings/:id/exception with an X-API-Key:
"Marking an exception requires session authentication." The same
inline check existed on revokeException and updateAwsScanMode. With
MCP integration coming soon we want the full cloud-tests mutation
surface to be API-callable.

The reason the wall existed: AuditLog.userId + FindingException.markedById
are both String/FK to User, and API keys are org-scoped (no per-user
identity). This change introduces a shared ActingUserResolver that
attributes API key / service-token mutations to the org's oldest
owner-role member — same pattern 19+ other services in the codebase
already use for owner lookups. The audit log description prepends
`[via API key "<name>"]` so reviewers see at a glance that it was
automation, not a UI action.

What's preserved:
  - Authentication unchanged (HybridAuthGuard runs as before).
  - RBAC unchanged (PermissionGuard runs as before — caller still
    needs integration:update scope).
  - Cross-tenant isolation preserved (owner lookup scoped to the
    calling org).
  - Session callers: zero behavior change — resolver short-circuits
    to req.userId with NO DB query. Audit description unchanged
    (no [via ...] prefix).
  - Existing API key writes (vendor:create, risk:create, etc.):
    untouched.
  - Service tokens passing x-user-id: unchanged (HybridAuthGuard
    already sets req.userId, resolver short-circuits).

What changes:
  - Service tokens without x-user-id: now succeed with owner-fallback
    attribution (previously hit the inline 401 too).
  - API key callers: now succeed with owner-fallback attribution.
  - Org with no owner-role member: 400 with actionable message
    ("ensure your organization has at least one user with the owner
    role"). Soft failure, never a 500.

Tests:
  - 10 acting-user.service tests (session short-circuit, owner
    fallback, cross-tenant scoping, deterministic oldest-owner pick,
    no-owner soft failure, caller-label formatting)
  - 12 exception.service tests (+2 for callerLabel propagation —
    audit description prefix + metadata field)
  - 5 NEW aws-scan-mode.service tests (idempotent no-op, AWS-only
    enforcement, owner-fallback callerLabel)
  - 9 NEW controller-level tests covering all 3 endpoints × 3
    scenarios (session, API-key+owner, no-owner → 400)
  - 253 cloud-security + auth tests pass (3 pre-existing TLS-env
    failures unrelated — same suites fail on main)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ations

feat(api): unblock cloud-tests mutations for API key + service token callers
…2884)

- prevent CSV formula injection in export by prefixing dangerous chars
- sanitize filenames in ZIP paths to prevent path traversal
- add member ID to export folder names to prevent collisions
- scope revocation lookup by organizationId to prevent cross-tenant access
- rollback completion record if evidence upload fails
- filter completion counts to enabled template items only
- use @ISINT() for sortOrder DTO validation
- validate date query params before building DB filters
- scope attachment deletion to member and event type
- show "Until {date}" when only end date is set in filter
- clear file input in finally block for retry support
- validate tab param against dynamically available tabs
- show error state in TodosOverview on fetch failure

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel vercel Bot temporarily deployed to staging – portal May 20, 2026 22:04 Inactive
@Marfuen
Copy link
Copy Markdown
Contributor

Marfuen commented May 21, 2026

@cubic-dev-ai re-review this

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented May 21, 2026

@cubic-dev-ai re-review this

@Marfuen 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.

16 issues found across 82 files

Confidence score: 2/5

  • There are high-confidence, high-severity data-integrity risks in apps/api/src/offboarding-checklist/access-revocation.service.ts: revokeAllVendorAccess can create cross-tenant revocation rows if memberId is not verified against organizationId before createMany.
  • Auth handling appears brittle in write paths (apps/api/src/offboarding-checklist/offboarding-checklist.controller.ts, apps/api/src/people/people.controller.ts), where missing authContext.userId under API key/service-token flows can cause runtime failures or unattributed uploads.
  • This is scored as high merge risk because the issues are concrete and user-impacting (tenant boundary violations, audit/history integrity concerns, and failing tests in apps/api/src/offboarding-checklist/offboarding-checklist.service.spec.ts due to findUnique vs findFirst mocking mismatch).
  • Pay close attention to apps/api/src/offboarding-checklist/access-revocation.service.ts, apps/api/src/offboarding-checklist/offboarding-checklist.controller.ts, apps/api/src/people/people.controller.ts, packages/db/prisma/schema/offboarding-checklist.prisma - tenant scoping, actor attribution, and audit-history retention need tightening before merge.
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/app/src/app/(app)/[orgId]/people/[employeeId]/components/AccessRevocationList.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/AccessRevocationList.tsx:52">
P2: Using a single `processingVendorId` causes incorrect loading/disabled behavior when multiple row actions overlap. Track processing per vendor (or globally lock actions) to avoid prematurely re-enabling controls during in-flight requests.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/people/settings/components/OffboardingChecklistSettings.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/settings/components/OffboardingChecklistSettings.tsx:57">
P2: Rolling back with a full-list snapshot can clobber other in-flight item updates. Revert only the failed item field (or revalidate from server) instead of restoring `data: previous`.</violation>
</file>

<file name="apps/api/src/offboarding-checklist/offboarding-checklist.service.ts">

<violation number="1" location="apps/api/src/offboarding-checklist/offboarding-checklist.service.ts:49">
P2: `createTemplateItem` has a race condition for `sortOrder`; concurrent creates can assign the same order value.</violation>
</file>

<file name="apps/app/src/hooks/use-access-revocations.ts">

<violation number="1" location="apps/app/src/hooks/use-access-revocations.ts:49">
P2: Add a file-size check before base64 conversion to avoid loading oversized evidence files into browser memory.</violation>
</file>

<file name="apps/app/src/hooks/use-employment-evidence.ts">

<violation number="1" location="apps/app/src/hooks/use-employment-evidence.ts:32">
P2: Add a client-side file size check before `readAsDataURL` to avoid loading oversized files into memory before the server rejects them.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/components/FrameworkControls.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/components/FrameworkControls.tsx:177">
P2: This per-row computation repeatedly scans tasks and re-sorts evidence submissions, causing avoidable O(n×m) render-time work for larger control lists.</violation>
</file>

<file name="apps/api/src/cloud-security/ai-remediation.prompt.ts">

<violation number="1" location="apps/api/src/cloud-security/ai-remediation.prompt.ts:182">
P2: The prompt mixes IAM action syntax with SDK command-class syntax (`iam:CreateServiceLinkedRoleCommand`), which can make generated fix steps use an invalid command name.</violation>
</file>

<file name="apps/api/src/cloud-security/plan-normalizer.ts">

<violation number="1" location="apps/api/src/cloud-security/plan-normalizer.ts:59">
P2: Whitespace-only `AWSServiceName` values are treated as valid, so malformed SLR steps can bypass normalization and still fail at execution time.</violation>
</file>

<file name="apps/api/src/cloud-security/aws-command-executor.ts">

<violation number="1" location="apps/api/src/cloud-security/aws-command-executor.ts:411">
P2: Trailing space in `'invalidparameter '` will miss error messages where "InvalidParameter" is followed by punctuation or appears at the end of the string. Since `'invalidparametervalue'` is already matched by an earlier check (short-circuits via `||`), you can safely use `'invalidparameter'` without the space—or add a word-boundary-style check.</violation>
</file>

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

Re-trigger cubic

Comment thread apps/api/src/people/people.controller.ts
Comment thread packages/db/prisma/schema/offboarding-checklist.prisma Outdated
Comment thread packages/db/prisma/schema/offboarding-checklist.prisma Outdated
Comment thread apps/api/src/offboarding-checklist/offboarding-checklist.controller.ts Outdated
Comment thread apps/api/src/integration-platform/controllers/sync.controller.ts Outdated
return steps.map((step, idx) => {
if (step.command !== SLR_COMMAND) return step;
const existing = step.params?.AWSServiceName;
if (typeof existing === 'string' && existing.length > 0) return step;
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P2: Whitespace-only AWSServiceName values are treated as valid, so malformed SLR steps can bypass normalization and still fail at execution time.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/cloud-security/plan-normalizer.ts, line 59:

<comment>Whitespace-only `AWSServiceName` values are treated as valid, so malformed SLR steps can bypass normalization and still fail at execution time.</comment>

<file context>
@@ -0,0 +1,95 @@
+  return steps.map((step, idx) => {
+    if (step.command !== SLR_COMMAND) return step;
+    const existing = step.params?.AWSServiceName;
+    if (typeof existing === 'string' && existing.length > 0) return step;
+    const inferred = inferServiceLinkedRolePrincipal(steps, idx);
+    if (!inferred) return step;
</file context>
Fix with Cubic

lower.includes('failed to satisfy constraint') ||
lower.includes('member must not be null') ||
lower.includes('invalidparametervalue') ||
lower.includes('invalidparameter ') ||
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P2: Trailing space in 'invalidparameter ' will miss error messages where "InvalidParameter" is followed by punctuation or appears at the end of the string. Since 'invalidparametervalue' is already matched by an earlier check (short-circuits via ||), you can safely use 'invalidparameter' without the space—or add a word-boundary-style check.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/cloud-security/aws-command-executor.ts, line 411:

<comment>Trailing space in `'invalidparameter '` will miss error messages where "InvalidParameter" is followed by punctuation or appears at the end of the string. Since `'invalidparametervalue'` is already matched by an earlier check (short-circuits via `||`), you can safely use `'invalidparameter'` without the space—or add a word-boundary-style check.</comment>

<file context>
@@ -372,6 +390,32 @@ function isValidationError(errName: string, errMsg: string): boolean {
+    lower.includes('failed to satisfy constraint') ||
+    lower.includes('member must not be null') ||
+    lower.includes('invalidparametervalue') ||
+    lower.includes('invalidparameter ') ||
+    lower.includes('invalid parameter') ||
+    lower.includes('must be a valid') ||
</file context>
Suggested change
lower.includes('invalidparameter ') ||
(lower.includes('invalidparameter') && !lower.includes('invalidparametervalue')) ||
Fix with Cubic

Comment thread apps/app/src/app/(app)/[orgId]/overview/components/OffboardingBanner.tsx Outdated
- fix test mock: findUnique → findFirst for undoVendorRevocation
- guard uploadEmploymentEvidence against missing userId in API key auth
- change User onDelete to SetNull on completions/revocations (preserve audit)
- add requireUserId helper to offboarding controller for all mutations
- validate member belongs to org before revocation operations
- validate provider date strings before converting to Date in sync
- reject partial evidence payloads in revokeVendorAccess
- add noopener,noreferrer to all window.open calls
- disable attach evidence button while processing
- handle error state explicitly in OffboardingBanner

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Marfuen
Copy link
Copy Markdown
Contributor

Marfuen commented May 21, 2026

@cubic-dev-ai re-review this

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented May 21, 2026

@cubic-dev-ai re-review this

@Marfuen 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.

12 issues found across 82 files

Confidence score: 3/5

  • There is concrete user-impacting risk: apps/api/src/offboarding-checklist/offboarding-export.service.ts can produce non-unique evidence filenames, which may cause ZIP extraction collisions and evidence loss.
  • apps/api/src/offboarding-checklist/offboarding-checklist.service.spec.ts references an undefined findFirst mock in a success test, which is likely to fail tests and reduce confidence in this change set until fixed.
  • Several medium-severity consistency issues add regression risk (non-atomic revoke flow in apps/api/src/offboarding-checklist/access-revocation.service.ts, stale rollback state in apps/app/src/app/(app)/[orgId]/people/settings/components/OffboardingChecklistSettings.tsx, and incomplete repair equality checks in apps/api/src/cloud-security/aws-command-executor.ts).
  • Pay close attention to apps/api/src/offboarding-checklist/offboarding-export.service.ts, apps/api/src/offboarding-checklist/access-revocation.service.ts, apps/app/src/app/(app)/[orgId]/people/settings/components/OffboardingChecklistSettings.tsx - these areas have the highest chance of user-visible data or state inconsistencies.
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/app/src/app/(app)/[orgId]/people/[employeeId]/components/AccessRevocationList.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/AccessRevocationList.tsx:52">
P2: Using a single `processingVendorId` causes incorrect loading/disabled behavior when multiple row actions overlap. Track processing per vendor (or globally lock actions) to avoid prematurely re-enabling controls during in-flight requests.</violation>
</file>

<file name="apps/api/src/offboarding-checklist/offboarding-checklist.service.ts">

<violation number="1" location="apps/api/src/offboarding-checklist/offboarding-checklist.service.ts:49">
P2: `createTemplateItem` has a race condition for `sortOrder`; concurrent creates can assign the same order value.</violation>
</file>

<file name="apps/app/src/hooks/use-access-revocations.ts">

<violation number="1" location="apps/app/src/hooks/use-access-revocations.ts:49">
P2: Add a file-size check before base64 conversion to avoid loading oversized evidence files into browser memory.</violation>
</file>

<file name="apps/app/src/hooks/use-employment-evidence.ts">

<violation number="1" location="apps/app/src/hooks/use-employment-evidence.ts:32">
P2: Add a client-side file size check before `readAsDataURL` to avoid loading oversized files into memory before the server rejects them.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/components/FrameworkControls.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/components/FrameworkControls.tsx:177">
P2: This per-row computation repeatedly scans tasks and re-sorts evidence submissions, causing avoidable O(n×m) render-time work for larger control lists.</violation>
</file>

<file name="apps/api/src/cloud-security/ai-remediation.prompt.ts">

<violation number="1" location="apps/api/src/cloud-security/ai-remediation.prompt.ts:182">
P2: The prompt mixes IAM action syntax with SDK command-class syntax (`iam:CreateServiceLinkedRoleCommand`), which can make generated fix steps use an invalid command name.</violation>

<violation number="2" location="apps/api/src/cloud-security/ai-remediation.prompt.ts:182">
P2: Use the AWS SDK command class name without the `iam:` prefix to avoid generating invalid `command` values.</violation>
</file>

<file name="apps/api/src/cloud-security/plan-normalizer.ts">

<violation number="1" location="apps/api/src/cloud-security/plan-normalizer.ts:59">
P2: Whitespace-only `AWSServiceName` values are treated as valid, so malformed SLR steps can bypass normalization and still fail at execution time.</violation>
</file>

<file name="apps/api/src/cloud-security/aws-command-executor.ts">

<violation number="1" location="apps/api/src/cloud-security/aws-command-executor.ts:411">
P2: Trailing space in `'invalidparameter '` will miss error messages where "InvalidParameter" is followed by punctuation or appears at the end of the string. Since `'invalidparametervalue'` is already matched by an earlier check (short-circuits via `||`), you can safely use `'invalidparameter'` without the space—or add a word-boundary-style check.</violation>

<violation number="2" location="apps/api/src/cloud-security/aws-command-executor.ts:740">
P2: Repair equality check only compares `params`, ignoring potential `service`/`command` changes from the AI callback. If the repair function fixes the command name (e.g., correcting `PutBucketPolicyCommand` → `PutBucketEncryptionCommand`) but keeps similar params, the fix is silently discarded.

Compare the full step instead of just params.</violation>
</file>

<file name="apps/app/src/hooks/use-offboarding-checklist.ts">

<violation number="1" location="apps/app/src/hooks/use-offboarding-checklist.ts:63">
P2: Add a file-size limit check before base64 conversion to avoid high client memory usage on large evidence uploads.</violation>
</file>

<file name="apps/api/src/people/people.controller.ts">

<violation number="1" location="apps/api/src/people/people.controller.ts:627">
P2: This delete path calls `getAttachments`, which unnecessarily generates signed URLs for every attachment even though only IDs are used for existence checking.</violation>
</file>

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

Re-trigger cubic

Comment thread apps/api/src/offboarding-checklist/offboarding-export.service.ts Outdated
- Service-linked roles (GuardDuty, Config, Inspector, Macie): use CreateServiceLinkedRole — AWS manages them.

## SERVICE-LINKED ROLE AWSServiceName VALUES (MANDATORY)
When emitting iam:CreateServiceLinkedRoleCommand you MUST populate the
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P2: Use the AWS SDK command class name without the iam: prefix to avoid generating invalid command values.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/cloud-security/ai-remediation.prompt.ts, line 182:

<comment>Use the AWS SDK command class name without the `iam:` prefix to avoid generating invalid `command` values.</comment>

<file context>
@@ -178,6 +178,20 @@ A human will ALWAYS review your plan before execution. Be precise and correct.
 - Service-linked roles (GuardDuty, Config, Inspector, Macie): use CreateServiceLinkedRole — AWS manages them.
 
+## SERVICE-LINKED ROLE AWSServiceName VALUES (MANDATORY)
+When emitting iam:CreateServiceLinkedRoleCommand you MUST populate the
+AWSServiceName param. Leaving it null or empty causes AWS to reject the
+call with "Member must not be null". Use exactly these values:
</file context>
Suggested change
When emitting iam:CreateServiceLinkedRoleCommand you MUST populate the
When emitting CreateServiceLinkedRoleCommand you MUST populate the
Fix with Cubic

Comment thread apps/app/src/app/(app)/[orgId]/people/all/components/TeamMembersClient.tsx Outdated
Comment thread apps/api/src/offboarding-checklist/access-revocation.service.ts Outdated
Comment on lines +740 to +741
JSON.stringify(refined.params ?? {}) !==
JSON.stringify(originalStep.params ?? {})
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P2: Repair equality check only compares params, ignoring potential service/command changes from the AI callback. If the repair function fixes the command name (e.g., correcting PutBucketPolicyCommandPutBucketEncryptionCommand) but keeps similar params, the fix is silently discarded.

Compare the full step instead of just params.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/cloud-security/aws-command-executor.ts, line 740:

<comment>Repair equality check only compares `params`, ignoring potential `service`/`command` changes from the AI callback. If the repair function fixes the command name (e.g., correcting `PutBucketPolicyCommand` → `PutBucketEncryptionCommand`) but keeps similar params, the fix is silently discarded.

Compare the full step instead of just params.</comment>

<file context>
@@ -607,22 +687,91 @@ export async function executePlanSteps(params: {
+          });
+          if (
+            refined &&
+            JSON.stringify(refined.params ?? {}) !==
+              JSON.stringify(originalStep.params ?? {})
+          ) {
</file context>
Suggested change
JSON.stringify(refined.params ?? {}) !==
JSON.stringify(originalStep.params ?? {})
JSON.stringify(refined) !==
JSON.stringify(originalStep)
Fix with Cubic

Comment on lines +627 to +631
const attachments = await this.attachmentsService.getAttachments(
organizationId,
memberId,
entityType,
);
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P2: This delete path calls getAttachments, which unnecessarily generates signed URLs for every attachment even though only IDs are used for existence checking.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/people/people.controller.ts, line 627:

<comment>This delete path calls `getAttachments`, which unnecessarily generates signed URLs for every attachment even though only IDs are used for existence checking.</comment>

<file context>
@@ -529,6 +565,77 @@ export class PeopleController {
+  ) {
+    const entityType = this.resolveEventType(eventType);
+    await this.peopleService.findById(memberId, organizationId);
+    const attachments = await this.attachmentsService.getAttachments(
+      organizationId,
+      memberId,
</file context>
Suggested change
const attachments = await this.attachmentsService.getAttachments(
organizationId,
memberId,
entityType,
);
const attachments = await this.attachmentsService.getAttachmentMetadata(
organizationId,
memberId,
entityType,
);
Fix with Cubic

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.

Won't fix — the getAttachments call generates signed URLs as a side effect, but the overhead is minimal (only runs on delete, which is rare). Adding an ID-only query path would require a new method on AttachmentsService for marginal benefit.

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.

Understood—thanks for the context.

* fix(people): address fourth round of cubic review findings

- add findFirst mock to test spec for undoVendorRevocation
- prefix checklist evidence filenames with ID to prevent ZIP collisions
- wrap sync call in try/catch so revocation stands on sync failure
- prevent non-expandable checklist rows from toggling open
- normalize preset date ranges to start of day
- hide pending invitations when date filters are active
- use controlled tab value in ToDoOverview for async data
- revalidate from server on error instead of restoring stale snapshot

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(people): log sync failures instead of swallowing silently

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Marfuen
Copy link
Copy Markdown
Contributor

Marfuen commented May 21, 2026

@cubic-dev-ai re-review this

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented May 21, 2026

@cubic-dev-ai re-review this

@Marfuen 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.

7 issues found across 82 files

Confidence score: 2/5

  • There is a high-impact multi-tenant integrity risk in packages/db/prisma/schema/offboarding-checklist.prisma: missing DB-level tenant scoping on related foreign keys can permit cross-organization references.
  • apps/api/src/offboarding-checklist/access-revocation.service.ts has two medium-high issues where sync failures in revokeAllVendorAccess/undoVendorRevocation can return errors after DB state already changed, creating confusing partial-success behavior for users.
  • apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/AccessRevocationList.tsx uses a single processingVendorId, which can produce racey row-action UI state when requests overlap; there is also a performance concern from N+1 attachment loading in apps/api/src/offboarding-checklist/offboarding-checklist.service.ts.
  • Pay close attention to packages/db/prisma/schema/offboarding-checklist.prisma, apps/api/src/offboarding-checklist/access-revocation.service.ts, and apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/AccessRevocationList.tsx - tenant isolation, consistency after sync errors, and concurrent action handling are the key merge risks.
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/app/src/app/(app)/[orgId]/people/[employeeId]/components/AccessRevocationList.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/AccessRevocationList.tsx:52">
P2: Using a single `processingVendorId` causes incorrect loading/disabled behavior when multiple row actions overlap. Track processing per vendor (or globally lock actions) to avoid prematurely re-enabling controls during in-flight requests.</violation>
</file>

<file name="apps/api/src/offboarding-checklist/offboarding-checklist.service.ts">

<violation number="1" location="apps/api/src/offboarding-checklist/offboarding-checklist.service.ts:49">
P2: `createTemplateItem` has a race condition for `sortOrder`; concurrent creates can assign the same order value.</violation>
</file>

<file name="apps/app/src/hooks/use-access-revocations.ts">

<violation number="1" location="apps/app/src/hooks/use-access-revocations.ts:49">
P2: Add a file-size check before base64 conversion to avoid loading oversized evidence files into browser memory.</violation>
</file>

<file name="apps/app/src/hooks/use-employment-evidence.ts">

<violation number="1" location="apps/app/src/hooks/use-employment-evidence.ts:32">
P2: Add a client-side file size check before `readAsDataURL` to avoid loading oversized files into memory before the server rejects them.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/components/FrameworkControls.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/components/FrameworkControls.tsx:177">
P2: This per-row computation repeatedly scans tasks and re-sorts evidence submissions, causing avoidable O(n×m) render-time work for larger control lists.</violation>
</file>

<file name="apps/api/src/cloud-security/ai-remediation.prompt.ts">

<violation number="1" location="apps/api/src/cloud-security/ai-remediation.prompt.ts:182">
P2: The prompt mixes IAM action syntax with SDK command-class syntax (`iam:CreateServiceLinkedRoleCommand`), which can make generated fix steps use an invalid command name.</violation>

<violation number="2" location="apps/api/src/cloud-security/ai-remediation.prompt.ts:182">
P2: Use the AWS SDK command class name without the `iam:` prefix to avoid generating invalid `command` values.</violation>
</file>

<file name="apps/api/src/cloud-security/plan-normalizer.ts">

<violation number="1" location="apps/api/src/cloud-security/plan-normalizer.ts:59">
P2: Whitespace-only `AWSServiceName` values are treated as valid, so malformed SLR steps can bypass normalization and still fail at execution time.</violation>
</file>

<file name="apps/api/src/cloud-security/aws-command-executor.ts">

<violation number="1" location="apps/api/src/cloud-security/aws-command-executor.ts:411">
P2: Trailing space in `'invalidparameter '` will miss error messages where "InvalidParameter" is followed by punctuation or appears at the end of the string. Since `'invalidparametervalue'` is already matched by an earlier check (short-circuits via `||`), you can safely use `'invalidparameter'` without the space—or add a word-boundary-style check.</violation>

<violation number="2" location="apps/api/src/cloud-security/aws-command-executor.ts:740">
P2: Repair equality check only compares `params`, ignoring potential `service`/`command` changes from the AI callback. If the repair function fixes the command name (e.g., correcting `PutBucketPolicyCommand` → `PutBucketEncryptionCommand`) but keeps similar params, the fix is silently discarded.

Compare the full step instead of just params.</violation>
</file>

<file name="apps/app/src/hooks/use-offboarding-checklist.ts">

<violation number="1" location="apps/app/src/hooks/use-offboarding-checklist.ts:63">
P2: Add a file-size limit check before base64 conversion to avoid high client memory usage on large evidence uploads.</violation>
</file>

<file name="apps/api/src/people/people.controller.ts">

<violation number="1" location="apps/api/src/people/people.controller.ts:627">
P2: This delete path calls `getAttachments`, which unnecessarily generates signed URLs for every attachment even though only IDs are used for existence checking.</violation>
</file>

You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread packages/db/prisma/schema/offboarding-checklist.prisma
Comment thread apps/api/src/offboarding-checklist/access-revocation.service.ts Outdated
Comment thread apps/api/src/offboarding-checklist/access-revocation.service.ts Outdated
Comment thread apps/app/src/app/(app)/[orgId]/overview/components/TodosOverview.tsx Outdated
Comment thread apps/api/src/offboarding-checklist/offboarding-checklist.service.ts
Comment thread apps/app/src/hooks/use-offboarding-checklist.ts Outdated
- guard sync errors in revokeAllVendorAccess and undoVendorRevocation
- remove unused TodosOverview component (dead code)
- batch attachment queries in getMemberChecklist (N+1 → 1 query)
- extract fileToBase64 into shared lib/file-utils.ts

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Marfuen
Copy link
Copy Markdown
Contributor

Marfuen commented May 21, 2026

@cubic-dev-ai re-review this

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented May 21, 2026

@cubic-dev-ai re-review this

@Marfuen 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.

14 issues found across 82 files

Confidence score: 2/5

  • High-confidence, high-severity test breakages are likely in apps/api/src/offboarding-checklist/offboarding-checklist.service.spec.ts (missing mockDb.$transaction, attachment, member, and getPresignedDownloadUrl mocks), which makes CI failures very likely until fixed.
  • There are concrete user-impact risks in export/data paths: apps/api/src/offboarding-checklist/offboarding-export.service.ts can swallow archiver errors (possible incomplete ZIP reported as success), and apps/api/src/tasks/evidence-export/evidence-data-loader.ts may still cause memory spikes due to unbounded header loading.
  • A few medium-severity correctness gaps remain (race handling in apps/api/src/offboarding-checklist/access-revocation.service.ts, partial-evidence validation in apps/api/src/offboarding-checklist/dto/complete-checklist-item.dto.ts, and prompt command-format inconsistency in apps/api/src/cloud-security/ai-remediation.prompt.ts), so this doesn’t look like a safe merge yet.
  • Pay close attention to apps/api/src/offboarding-checklist/offboarding-checklist.service.spec.ts, apps/api/src/offboarding-checklist/offboarding-export.service.ts, and apps/api/src/tasks/evidence-export/evidence-data-loader.ts - likely test failures and export reliability/performance regressions need resolution before merge.
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/cloud-security/ai-remediation.prompt.ts">

<violation number="1" location="apps/api/src/cloud-security/ai-remediation.prompt.ts:182">
P2: The added instruction mixes IAM action syntax into the command name (`iam:CreateServiceLinkedRoleCommand`), which conflicts with the prompt’s required SDK class-name format and can produce invalid command output.</violation>
</file>

<file name="apps/api/src/offboarding-checklist/dto/complete-checklist-item.dto.ts">

<violation number="1" location="apps/api/src/offboarding-checklist/dto/complete-checklist-item.dto.ts:14">
P2: Evidence fields are validated independently, so partial evidence payloads can pass and then be silently ignored. Enforce an all-or-none constraint for `fileName`, `fileType`, and `fileData`.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/OffboardingSummaryCard.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/OffboardingSummaryCard.tsx:22">
P2: `Days since` can render negative values when `offboardDate` is in the future. Clamp or otherwise handle future dates to avoid incorrect elapsed-day output.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/components/FrameworkControls.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/components/FrameworkControls.tsx:33">
P2: This component substantially duplicates the existing requirement controls table logic; extract shared table logic/components to avoid maintenance drift.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/OffboardingChecklistItem.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/OffboardingChecklistItem.tsx:377">
P2: The evidence dropzone is not disabled while `isProcessing` is true, so users can still click/drop files that are then silently ignored by the upload guard.</violation>
</file>

<file name="apps/api/src/tasks/evidence-export/evidence-data-loader.ts">

<violation number="1" location="apps/api/src/tasks/evidence-export/evidence-data-loader.ts:130">
P1: `getAutomationHeaders` fetches all run headers in one shot, which can still cause large memory spikes for high-volume tasks. Use paginated/grouped queries instead of unbounded `findMany` here.</violation>
</file>

<file name="apps/api/src/offboarding-checklist/offboarding-export.service.ts">

<violation number="1" location="apps/api/src/offboarding-checklist/offboarding-export.service.ts:34">
P1: Archiver errors are swallowed instead of being propagated, which can return incomplete ZIP exports as apparent success.</violation>

<violation number="2" location="apps/api/src/offboarding-checklist/offboarding-export.service.ts:221">
P2: Apply CSV formula-injection prefixing before quote-escaping in `escapeCsvField` to follow the repository’s required sanitization order.

(Based on your team's feedback about CSV formula-injection protection ordering.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/api/src/tasks/evidence-export/evidence-export.service.spec.ts">

<violation number="1" location="apps/api/src/tasks/evidence-export/evidence-export.service.spec.ts:558">
P2: Test does not verify absence of the bulk `integrationCheckRun.findMany` call (without `checkId`). The test name says "instead of all runs at once" but only checks that per-automation calls happen; a regression that adds back the bulk load while keeping per-automation calls would still pass, silently reintroducing the OOM.</violation>
</file>

<file name="apps/api/src/offboarding-checklist/access-revocation.service.ts">

<violation number="1" location="apps/api/src/offboarding-checklist/access-revocation.service.ts:38">
P2: Avoid per-vendor attachment fetches in the loop; this introduces N+1 DB/signing work and slows large revocation lists.</violation>

<violation number="2" location="apps/api/src/offboarding-checklist/access-revocation.service.ts:105">
P2: Handle unique-constraint races when creating revocations so duplicate requests return a 400 instead of a server error.</violation>
</file>

<file name="apps/api/src/offboarding-checklist/offboarding-checklist.service.spec.ts">

<violation number="1" location="apps/api/src/offboarding-checklist/offboarding-checklist.service.spec.ts:3">
P0: Missing `$transaction` mock in `mockDb`: `seedDefaultsIfNeeded()` calls `db.$transaction(...)` but `mockDb.$transaction` is undefined. The `getTemplate` seeding test will fail.</violation>

<violation number="2" location="apps/api/src/offboarding-checklist/offboarding-checklist.service.spec.ts:3">
P0: Missing `member` mock in `mockDb`: `AccessRevocationService.revokeVendorAccess()` calls `db.member.findFirst(...)` but `mockDb` does not define a `member` property. All three `revokeVendorAccess` tests will throw a TypeError.</violation>

<violation number="3" location="apps/api/src/offboarding-checklist/offboarding-checklist.service.spec.ts:149">
P0: Missing `attachment` mock in `mockDb` and missing `getPresignedDownloadUrl` mock: `getMemberChecklist()` uses `db.attachment.findMany(...)` and `attachmentsService.getPresignedDownloadUrl(...)` but neither is mocked. The `getAttachments` mock on line 424 is dead code since the service now uses `db.attachment.findMany` directly.</violation>
</file>

You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

@@ -0,0 +1,545 @@
import { BadRequestException, NotFoundException } from '@nestjs/common';

const mockDb = {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P0: Missing $transaction mock in mockDb: seedDefaultsIfNeeded() calls db.$transaction(...) but mockDb.$transaction is undefined. The getTemplate seeding test will fail.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/offboarding-checklist/offboarding-checklist.service.spec.ts, line 3:

<comment>Missing `$transaction` mock in `mockDb`: `seedDefaultsIfNeeded()` calls `db.$transaction(...)` but `mockDb.$transaction` is undefined. The `getTemplate` seeding test will fail.</comment>

<file context>
@@ -0,0 +1,545 @@
+import { BadRequestException, NotFoundException } from '@nestjs/common';
+
+const mockDb = {
+  offboardingChecklistTemplate: {
+    findMany: jest.fn(),
</file context>
Fix with Cubic

completedBy: { id: 'usr_1', name: 'Test User' },
},
]);
mockAttachmentsService.getAttachments.mockResolvedValue([
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P0: Missing attachment mock in mockDb and missing getPresignedDownloadUrl mock: getMemberChecklist() uses db.attachment.findMany(...) and attachmentsService.getPresignedDownloadUrl(...) but neither is mocked. The getAttachments mock on line 424 is dead code since the service now uses db.attachment.findMany directly.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/offboarding-checklist/offboarding-checklist.service.spec.ts, line 149:

<comment>Missing `attachment` mock in `mockDb` and missing `getPresignedDownloadUrl` mock: `getMemberChecklist()` uses `db.attachment.findMany(...)` and `attachmentsService.getPresignedDownloadUrl(...)` but neither is mocked. The `getAttachments` mock on line 424 is dead code since the service now uses `db.attachment.findMany` directly.</comment>

<file context>
@@ -0,0 +1,545 @@
+          completedBy: { id: 'usr_1', name: 'Test User' },
+        },
+      ]);
+      mockAttachmentsService.getAttachments.mockResolvedValue([
+        { id: 'att_1', name: 'evidence.pdf' },
+      ]);
</file context>
Fix with Cubic

@@ -0,0 +1,545 @@
import { BadRequestException, NotFoundException } from '@nestjs/common';

const mockDb = {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P0: Missing member mock in mockDb: AccessRevocationService.revokeVendorAccess() calls db.member.findFirst(...) but mockDb does not define a member property. All three revokeVendorAccess tests will throw a TypeError.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/offboarding-checklist/offboarding-checklist.service.spec.ts, line 3:

<comment>Missing `member` mock in `mockDb`: `AccessRevocationService.revokeVendorAccess()` calls `db.member.findFirst(...)` but `mockDb` does not define a `member` property. All three `revokeVendorAccess` tests will throw a TypeError.</comment>

<file context>
@@ -0,0 +1,545 @@
+import { BadRequestException, NotFoundException } from '@nestjs/common';
+
+const mockDb = {
+  offboardingChecklistTemplate: {
+    findMany: jest.fn(),
</file context>
Fix with Cubic

}

const [appRuns, customRuns] = await Promise.all([
db.integrationCheckRun.findMany({
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P1: getAutomationHeaders fetches all run headers in one shot, which can still cause large memory spikes for high-volume tasks. Use paginated/grouped queries instead of unbounded findMany here.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/tasks/evidence-export/evidence-data-loader.ts, line 130:

<comment>`getAutomationHeaders` fetches all run headers in one shot, which can still cause large memory spikes for high-volume tasks. Use paginated/grouped queries instead of unbounded `findMany` here.</comment>

<file context>
@@ -0,0 +1,385 @@
+  }
+
+  const [appRuns, customRuns] = await Promise.all([
+    db.integrationCheckRun.findMany({
+      where: { taskId },
+      select: {
</file context>
Fix with Cubic

output: NodeJS.WritableStream;
}) {
const archive = archiver('zip', { zlib: { level: 9 } });
archive.on('error', () => { archive.abort(); });
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P1: Archiver errors are swallowed instead of being propagated, which can return incomplete ZIP exports as apparent success.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/offboarding-checklist/offboarding-export.service.ts, line 34:

<comment>Archiver errors are swallowed instead of being propagated, which can return incomplete ZIP exports as apparent success.</comment>

<file context>
@@ -0,0 +1,226 @@
+    output: NodeJS.WritableStream;
+  }) {
+    const archive = archiver('zip', { zlib: { level: 9 } });
+    archive.on('error', () => { archive.abort(); });
+    archive.pipe(output);
+
</file context>
Fix with Cubic

onDrop={onFileDrop}
onDragOver={(e) => e.preventDefault()}
onClick={() => dropzoneInputRef.current?.click()}
className="flex cursor-pointer flex-col items-center gap-2 rounded-md border-2 border-dashed border-muted-foreground/25 px-4 py-6 text-center transition hover:border-muted-foreground/50 hover:bg-muted/25"
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P2: The evidence dropzone is not disabled while isProcessing is true, so users can still click/drop files that are then silently ignored by the upload guard.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/OffboardingChecklistItem.tsx, line 377:

<comment>The evidence dropzone is not disabled while `isProcessing` is true, so users can still click/drop files that are then silently ignored by the upload guard.</comment>

<file context>
@@ -0,0 +1,412 @@
+          onDrop={onFileDrop}
+          onDragOver={(e) => e.preventDefault()}
+          onClick={() => dropzoneInputRef.current?.click()}
+          className="flex cursor-pointer flex-col items-center gap-2 rounded-md border-2 border-dashed border-muted-foreground/25 px-4 py-6 text-center transition hover:border-muted-foreground/50 hover:bg-muted/25"
+        >
+          <Upload size={20} className="text-muted-foreground" />
</file context>
Fix with Cubic

}

function escapeCsvField(value: string): string {
const escaped = value.replace(/"/g, '""');
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P2: Apply CSV formula-injection prefixing before quote-escaping in escapeCsvField to follow the repository’s required sanitization order.

(Based on your team's feedback about CSV formula-injection protection ordering.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/offboarding-checklist/offboarding-export.service.ts, line 221:

<comment>Apply CSV formula-injection prefixing before quote-escaping in `escapeCsvField` to follow the repository’s required sanitization order.

(Based on your team's feedback about CSV formula-injection protection ordering.) </comment>

<file context>
@@ -0,0 +1,226 @@
+}
+
+function escapeCsvField(value: string): string {
+  const escaped = value.replace(/"/g, '""');
+  if (/^[=+\-@\t\r]/.test(escaped)) {
+    return `'${escaped}`;
</file context>
Fix with Cubic

// Verify per-automation loading: findMany is called with individual
// checkId filters (not a single bulk load of all results).
const findManyCalls = mockDb.integrationCheckRun.findMany.mock.calls;
const perAutomationCalls = findManyCalls.filter(
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P2: Test does not verify absence of the bulk integrationCheckRun.findMany call (without checkId). The test name says "instead of all runs at once" but only checks that per-automation calls happen; a regression that adds back the bulk load while keeping per-automation calls would still pass, silently reintroducing the OOM.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/tasks/evidence-export/evidence-export.service.spec.ts, line 558:

<comment>Test does not verify absence of the bulk `integrationCheckRun.findMany` call (without `checkId`). The test name says "instead of all runs at once" but only checks that per-automation calls happen; a regression that adds back the bulk load while keeping per-automation calls would still pass, silently reintroducing the OOM.</comment>

<file context>
@@ -473,6 +473,101 @@ describe('EvidenceExportService — streaming ZIPs', () => {
+      // Verify per-automation loading: findMany is called with individual
+      // checkId filters (not a single bulk load of all results).
+      const findManyCalls = mockDb.integrationCheckRun.findMany.mock.calls;
+      const perAutomationCalls = findManyCalls.filter(
+        (call: unknown[]) =>
+          (call[0] as { where: { checkId?: string } }).where.checkId,
</file context>
Fix with Cubic

const revocation = revocationMap.get(vendor.id);
const domain = vendor.website?.replace(/^https?:\/\//, '').replace(/\/.*$/, '') ?? null;
const evidence = revocation
? await this.attachmentsService.getAttachments(
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P2: Avoid per-vendor attachment fetches in the loop; this introduces N+1 DB/signing work and slows large revocation lists.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/offboarding-checklist/access-revocation.service.ts, line 38:

<comment>Avoid per-vendor attachment fetches in the loop; this introduces N+1 DB/signing work and slows large revocation lists.</comment>

<file context>
@@ -0,0 +1,279 @@
+        const revocation = revocationMap.get(vendor.id);
+        const domain = vendor.website?.replace(/^https?:\/\//, '').replace(/\/.*$/, '') ?? null;
+        const evidence = revocation
+          ? await this.attachmentsService.getAttachments(
+              organizationId,
+              revocation.id,
</file context>
Fix with Cubic

);
}

const revocation = await db.offboardingAccessRevocation.create({
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 21, 2026

Choose a reason for hiding this comment

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

P2: Handle unique-constraint races when creating revocations so duplicate requests return a 400 instead of a server error.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/offboarding-checklist/access-revocation.service.ts, line 105:

<comment>Handle unique-constraint races when creating revocations so duplicate requests return a 400 instead of a server error.</comment>

<file context>
@@ -0,0 +1,279 @@
+      );
+    }
+
+    const revocation = await db.offboardingAccessRevocation.create({
+      data: {
+        organizationId,
</file context>
Fix with Cubic

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