Skip to content

[comp] Production Deploy#2878

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

[comp] Production Deploy#2878
github-actions[bot] wants to merge 19 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 access revocation tracking, evidence uploads/export, and onboard/offboard date tracking across People and integrations. Also adds universal step‑repair for Cloud Security auto‑remediation, enables API key/service token mutations with clear audit attribution, sets Frameworks to default to a Controls view, fixes OOMs in task evidence export, hardens offboarding exports/validations, and fixes a multi‑select dropdown focus trap.

  • 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 and filters; auto‑sets offboardDate on deactivate.
    • Cloud Security: API key and service token callers can mark exceptions/revoke/update AWS scan mode; mutations attributed via an ActingUser resolver with audit labels like [via API key "…"]. Auto‑remediation adds universal step‑repair on AWS validation errors, validates required params before execution, normalizes plans to backfill service‑linked role AWSServiceName (plus prompt mapping), and improves configure‑only diffs.
    • 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 updates; 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. Dependency bump: @trycompai/design-system1.1.19.
  • Bug Fixes

    • Evidence export: Prevent OOM by loading automation headers first and streaming each automation with cursor‑based batching; add deterministic ordering; extract mappers and JSON builder.
    • Offboarding hardening: Sanitize ZIP paths and prefix dangerous CSV chars; include member ID in export folder names; scope revocation lookups by org; validate date query params; roll back completion if evidence upload fails; count completions only for enabled items; scope attachment deletion to member/event type; clear file inputs after uploads; validate tab param; show error state in TodosOverview; display “Until {date}” when only an end date is set.
    • UI: Close MultipleSelector dropdown on blur to prevent it blocking adjacent form controls.

Written for commit b837dfb. 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)
comp-framework-editor (staging) Ready Ready Preview, Comment May 21, 2026 5:06am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
app (staging) Skipped Skipped May 21, 2026 5:06am
portal (staging) Skipped Skipped May 21, 2026 5:06am

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
return;
}

const { totalVendors, revokedCount } = await this.getAccessRevocations(
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: Avoid calling the full getAccessRevocations path in completion sync; it does expensive attachment fetches that are unnecessary for count checks.

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 204:

<comment>Avoid calling the full `getAccessRevocations` path in completion sync; it does expensive attachment fetches that are unnecessary for count checks.</comment>

<file context>
@@ -0,0 +1,233 @@
+      return;
+    }
+
+    const { totalVendors, revokedCount } = await this.getAccessRevocations(
+      organizationId,
+      memberId,
</file context>
Fix with Cubic

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

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
@tofikwest
Copy link
Copy Markdown
Contributor

@cubic-dev-ai review it

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented May 21, 2026

@cubic-dev-ai review it

@tofikwest 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 74 files

Confidence score: 2/5

  • I’m rating this as high merge risk because several high-confidence issues are user-impacting: unhandled archive errors in apps/api/src/offboarding-checklist/offboarding-export.service.ts can crash exports, and unbounded findMany in the same flow can cause memory spikes on large organizations.
  • There are concrete data-integrity concerns: onDelete: Cascade in packages/db/prisma/schema/offboarding-checklist.prisma can remove historical completion records, and failed evidence upload in apps/api/src/offboarding-checklist/access-revocation.service.ts can leave partial revocation data behind.
  • Auth and state-consistency gaps could cause incorrect behavior after merge, including missing Authorization forwarding in apps/app/src/app/api/offboarding-export/route.ts and reactivation not clearing offboardDate in apps/api/src/integration-platform/services/generic-employee-sync.service.ts.
  • Pay close attention to apps/api/src/offboarding-checklist/offboarding-export.service.ts, packages/db/prisma/schema/offboarding-checklist.prisma, apps/api/src/offboarding-checklist/access-revocation.service.ts, and apps/app/src/app/api/offboarding-export/route.ts - they contain crash, data-loss, transactional consistency, and auth-forwarding 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]/overview/components/ToDoOverview.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/overview/components/ToDoOverview.tsx:69">
P2: The offboarding tab collapses loading/error into an empty state, so failed fetches appear as “No pending offboardings.” Handle SWR loading/error states before showing the empty-success message.</violation>
</file>

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

<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/OffboardingChecklist.tsx:73">
P2: Opening download URLs with `window.open(..., '_blank')` without `noopener,noreferrer` leaves `window.opener` exposed. Add `noopener,noreferrer` to the window features.</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/integration-platform/services/generic-employee-sync.service.ts">

<violation number="1" location="apps/api/src/integration-platform/services/generic-employee-sync.service.ts:198">
P2: Reactivating a member does not clear `offboardDate`, so active members can remain marked as offboarded.</violation>

<violation number="2" location="apps/api/src/integration-platform/services/generic-employee-sync.service.ts:241">
P2: `startDate` is written without validating date parseability, so malformed provider values can fail sync writes.</violation>
</file>

<file name="apps/app/src/app/api/offboarding-export/route.ts">

<violation number="1" location="apps/app/src/app/api/offboarding-export/route.ts:27">
P2: Forward the incoming Authorization header to the backend export API; otherwise token-authenticated callers will be rejected.</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:33">
P1: Add explicit `archive` error handling; unhandled archiver errors can crash the export path.</violation>

<violation number="2" location="apps/api/src/offboarding-checklist/offboarding-export.service.ts:116">
P2: Vendor evidence paths are collision-prone; include a unique prefix (e.g., attachment id) in the archive filename.</violation>

<violation number="3" location="apps/api/src/offboarding-checklist/offboarding-export.service.ts:157">
P1: `exportAllOffboardings` uses unbounded `findMany`; paginate with cursor batching to avoid memory spikes on large org exports.</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:187">
P2: `evidenceRequired` is not enforced when completing an item, so required-evidence tasks can be completed without any evidence.</violation>

<violation number="2" location="apps/api/src/offboarding-checklist/offboarding-checklist.service.ts:363">
P2: Default template seeding is race-prone and can insert duplicate default items for a new organization under concurrent requests.</violation>
</file>

<file name="packages/db/prisma/schema/offboarding-checklist.prisma">

<violation number="1" location="packages/db/prisma/schema/offboarding-checklist.prisma:32">
P1: Using `onDelete: Cascade` on `templateItem` can erase historical offboarding completion records when a template item is deleted.</violation>
</file>

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

<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/all/components/TeamMembersClient.tsx:300">
P2: Status filter label can display "Active" while data is actually filtered as "All People" when offboard date filtering is applied.</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:108">
P1: If evidence upload fails, the revocation record is left behind because this flow does not roll back the earlier create.</violation>

<violation number="2" location="apps/api/src/offboarding-checklist/access-revocation.service.ts:143">
P2: Undoing a revocation does not delete its evidence attachments, leaving orphaned attachment data.</violation>

<violation number="3" location="apps/api/src/offboarding-checklist/access-revocation.service.ts:204">
P2: Completion sync is doing expensive full access-revocation hydration just to calculate counts, which adds avoidable overhead to each mutation.</violation>
</file>

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

Re-trigger cubic

const archive = archiver('zip', { zlib: { level: 9 } });
archive.pipe(output);

const members = await db.member.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: exportAllOffboardings uses unbounded findMany; paginate with cursor batching to avoid memory spikes on large org exports.

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 157:

<comment>`exportAllOffboardings` uses unbounded `findMany`; paginate with cursor batching to avoid memory spikes on large org exports.</comment>

<file context>
@@ -0,0 +1,214 @@
+    const archive = archiver('zip', { zlib: { level: 9 } });
+    archive.pipe(output);
+
+    const members = await db.member.findMany({
+      where: { organizationId, offboardDate: { not: null }, deactivated: true },
+      include: { user: { select: { name: true, email: true } } },
</file context>
Fix with Cubic

memberId: string;
output: NodeJS.WritableStream;
}) {
const archive = archiver('zip', { zlib: { level: 9 } });
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: Add explicit archive error handling; unhandled archiver errors can crash the export path.

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 33:

<comment>Add explicit `archive` error handling; unhandled archiver errors can crash the export path.</comment>

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


organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
member Member @relation(fields: [memberId], references: [id], onDelete: Cascade)
templateItem OffboardingChecklistTemplate @relation(fields: [templateItemId], references: [id], onDelete: Cascade)
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: Using onDelete: Cascade on templateItem can erase historical offboarding completion records when a template item is deleted.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/db/prisma/schema/offboarding-checklist.prisma, line 32:

<comment>Using `onDelete: Cascade` on `templateItem` can erase historical offboarding completion records when a template item is deleted.</comment>

<file context>
@@ -0,0 +1,57 @@
+
+  organization Organization                 @relation(fields: [organizationId], references: [id], onDelete: Cascade)
+  member       Member                       @relation(fields: [memberId], references: [id], onDelete: Cascade)
+  templateItem OffboardingChecklistTemplate @relation(fields: [templateItemId], references: [id], onDelete: Cascade)
+  completedBy  User                         @relation("OffboardingChecklistCompletedBy", fields: [completedById], references: [id], onDelete: Cascade)
+
</file context>
Fix with Cubic

});

if (evidence) {
await this.attachmentsService.uploadAttachment(
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: If evidence upload fails, the revocation record is left behind because this flow does not roll back the earlier create.

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 108:

<comment>If evidence upload fails, the revocation record is left behind because this flow does not roll back the earlier create.</comment>

<file context>
@@ -0,0 +1,233 @@
+    });
+
+    if (evidence) {
+      await this.attachmentsService.uploadAttachment(
+        organizationId,
+        revocation.id,
</file context>
Fix with Cubic

const { data: pendingData } = useApiSWR<PendingOffboardingResponse>(
'/v1/offboarding-checklist/pending',
);
const pendingOffboardings = pendingData?.data?.members ?? [];
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 offboarding tab collapses loading/error into an empty state, so failed fetches appear as “No pending offboardings.” Handle SWR loading/error states before showing the empty-success message.

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]/overview/components/ToDoOverview.tsx, line 69:

<comment>The offboarding tab collapses loading/error into an empty state, so failed fetches appear as “No pending offboardings.” Handle SWR loading/error states before showing the empty-success message.</comment>

<file context>
@@ -48,6 +63,11 @@ export function ToDoOverview({
+  const { data: pendingData } = useApiSWR<PendingOffboardingResponse>(
+    '/v1/offboarding-checklist/pending',
+  );
+  const pendingOffboardings = pendingData?.data?.members ?? [];
+
   const isOnboardingInProgress = !!onboardingTriggerJobId;
</file context>
Fix with Cubic

}

private async seedDefaultsIfNeeded(organizationId: string) {
const count = await db.offboardingChecklistTemplate.count({
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: Default template seeding is race-prone and can insert duplicate default items for a new organization under concurrent requests.

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.ts, line 363:

<comment>Default template seeding is race-prone and can insert duplicate default items for a new organization under concurrent requests.</comment>

<file context>
@@ -0,0 +1,384 @@
+  }
+
+  private async seedDefaultsIfNeeded(organizationId: string) {
+    const count = await db.offboardingChecklistTemplate.count({
+      where: { organizationId },
+    });
</file context>
Fix with Cubic

throw new NotFoundException('Template item not found');
}

const completion = await db.offboardingChecklistCompletion.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: evidenceRequired is not enforced when completing an item, so required-evidence tasks can be completed without any evidence.

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.ts, line 187:

<comment>`evidenceRequired` is not enforced when completing an item, so required-evidence tasks can be completed without any evidence.</comment>

<file context>
@@ -0,0 +1,384 @@
+      throw new NotFoundException('Template item not found');
+    }
+
+    const completion = await db.offboardingChecklistCompletion.create({
+      data: {
+        organizationId,
</file context>
Fix with Cubic

<SelectTrigger>
<SelectValue placeholder="Active" />
<SelectValue placeholder="Active">
{{ all: 'All People', active: 'Active', pending: 'Pending', deactivated: 'Deactivated' }[statusFilter] ?? 'Active'}
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: Status filter label can display "Active" while data is actually filtered as "All People" when offboard date filtering is applied.

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/all/components/TeamMembersClient.tsx, line 300:

<comment>Status filter label can display "Active" while data is actually filtered as "All People" when offboard date filtering is applied.</comment>

<file context>
@@ -254,7 +296,9 @@ export function TeamMembersClient({
             <SelectTrigger>
-              <SelectValue placeholder="Active" />
+              <SelectValue placeholder="Active">
+                {{ all: 'All People', active: 'Active', pending: 'Pending', deactivated: 'Deactivated' }[statusFilter] ?? 'Active'}
+              </SelectValue>
             </SelectTrigger>
</file context>
Suggested change
{{ all: 'All People', active: 'Active', pending: 'Pending', deactivated: 'Deactivated' }[statusFilter] ?? 'Active'}
{{ all: 'All People', active: 'Active', pending: 'Pending', deactivated: 'Deactivated' }[effectiveStatusFilter] ?? 'Active'}
Fix with Cubic

return;
}

const { totalVendors, revokedCount } = await this.getAccessRevocations(
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: Completion sync is doing expensive full access-revocation hydration just to calculate counts, which adds avoidable overhead to each mutation.

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 204:

<comment>Completion sync is doing expensive full access-revocation hydration just to calculate counts, which adds avoidable overhead to each mutation.</comment>

<file context>
@@ -0,0 +1,233 @@
+      return;
+    }
+
+    const { totalVendors, revokedCount } = await this.getAccessRevocations(
+      organizationId,
+      memberId,
</file context>
Fix with Cubic

throw new NotFoundException('Revocation record not found');
}

await db.offboardingAccessRevocation.delete({
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: Undoing a revocation does not delete its evidence attachments, leaving orphaned attachment data.

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 143:

<comment>Undoing a revocation does not delete its evidence attachments, leaving orphaned attachment data.</comment>

<file context>
@@ -0,0 +1,233 @@
+      throw new NotFoundException('Revocation record not found');
+    }
+
+    await db.offboardingAccessRevocation.delete({
+      where: { id: revocation.id },
+    });
</file context>
Fix with Cubic

@tofikwest
Copy link
Copy Markdown
Contributor

@Marfuen take a look to bug report please, a lot of finding

tofikwest and others added 7 commits May 21, 2026 00:22
Adds a pure post-processing step that runs after the AI generates a fix
plan to backfill cross-step values the model does not reliably emit.

Today the normalizer handles a single concrete bug: when a plan needs a
service-linked role (Config, GuardDuty, Inspector, Macie, Access
Analyzer, Security Hub, Detective, Backup), the AI sometimes generates
CreateServiceLinkedRoleCommand without populating AWSServiceName. AWS
rejects the call with a cryptic "Member must not be null" error, leaving
the customer stuck on the Auto-Remediate dialog with no actionable
diagnosis.

The normalizer infers the right principal from a nearest-neighbor scan
across the plan's other steps (deterministic, idempotent, no mutation of
input). Wiring it into the AI remediation service happens in a separate
commit so each phase is reviewable in isolation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends validatePlanSteps with a REQUIRED_PARAMS check that runs before
the executor makes the AWS SDK call. Today AWS surfaces a cryptic
"Member must not be null" when a required top-level param is missing
(e.g., AWSServiceName on CreateServiceLinkedRoleCommand,
ConfigurationRecorder on PutConfigurationRecorderCommand), which leaves
the Auto-Remediate dialog with an unhelpful error and the operation
already billed.

The check is intentionally narrow — only commands where we've seen the
AI omit a required param in practice and where the SDK's error is
unhelpful. The error message includes the step index and command name so
customers know exactly which step is broken.

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

Two coordinated fixes to the AI remediation pipeline:

1. Broadens enrichEmptyState to handle plans whose actionable steps are
   not Create*. Previously a plan composed of Put*/Start*/Update*/
   Enable*/Attach*/Set*/Modify* steps with both currentState and
   proposedState empty rendered as "{} → {}" in the Auto-Remediate
   dialog. The backstop now emits configured:false → configured:true
   with a willChange list for these plans. Pure Create* plans keep the
   existing exists:false → exists:true / willCreate language.

2. Wires the plan normalizer into all three sites that produce a
   FixPlan: generateFixPlan (happy path), refineFixPlan (happy path),
   and refineFixPlan (catch fallback). The normalizer's
   CreateServiceLinkedRoleCommand AWSServiceName backfill now runs on
   every plan that reaches the UI or the executor, so the bug-reported
   "Member must not be null" failure on the AWS Config recorder finding
   is resolved.

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

Adds a mandatory mapping table after the existing service-linked role
guidance so the AI emits AWSServiceName values in the first pass for
the eight AWS services we currently auto-fix. This is a belt-and-
suspenders fix on top of the deterministic plan normalizer — the
normalizer guarantees correctness; this reduces the rate at which the
bug occurs in the first place.

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

The MultipleSelector's `CommandList` is rendered with `absolute top-1 z-10` and
floats below the input. With several selected items and a tall option list it
visually overlaps form fields below — including the `@base-ui/react` Select
used elsewhere in the same form (e.g. the "Fail on open alerts at severity"
dropdown in the GitHub integration setup).

Two interacting behaviors made that overlap turn into "click does nothing":

1. The input's `onBlur` was gated on `onScrollbar` — when the mouse was over the
   open list (which it is, by definition, when the list overlaps the field
   you're trying to click), `setOpen(false)` was skipped and the list stayed
   mounted.
2. The `CommandList` re-focused the input on every `mouseup`, immediately
   reopening the dropdown after any click that happened over it.

Together this trapped `mousedown`/`mouseup` on the floating popup. base-ui's
`Select.Trigger` (the field below) registers a one-shot document `mouseup`
listener when it receives `mousedown` and cancels the open if the `mouseup`
lands outside the trigger's bounds — which is exactly what happened: the
captured `mouseup` on `CommandList` told base-ui to cancel its open, so the
severity dropdown never appeared.

This change:

- Removes the `if (!onScrollbar)` guard so the input's `onBlur` always closes
  the popup. Clicking anywhere outside the input now dismisses the dropdown
  immediately, restoring the natural focus contract.
- Drops the `CommandList` `onMouseUp` re-focus that re-trapped the input.
  `CommandItem.onMouseDown` already calls `e.preventDefault()`, so item
  selection still keeps the input focused. The previous handler only mattered
  for stray clicks/scrollbar drags on the popup itself — and was the exact
  mechanism that made clicks on overlapped sibling controls (like the
  base-ui Select) unresponsive.

Reproducible by:
1. Setting up the GitHub integration.
2. Picking one or more repositories so `CommandList` opens with options.
3. Clicking "Fail on open alerts at severity" — before this change the dropdown
   does not open; after this change it opens on the first click.

Verified: `packages/ui` typecheck + build pass. No `MultipleSelector` consumer
relies on `onMouseUp` re-focus (it was an internal CommandList prop, not part
of the public API).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The per-command REQUIRED_PARAMS map and the SLR plan normalizer cover
the bugs we know about — but the AI can omit a required param on ANY
AWS command in ANY future plan, and we shouldn't ship a fix that needs
a code change every time a new finding hits this class of bug.

This adds a universal escape hatch:

1. New `looksLikeValidationError(message)` detector that pattern-matches
   AWS's standard validation wording ("Member must not be null",
   "failed to satisfy constraint", "ValidationException", etc.) — no
   per-command enumeration.

2. New `StepRepairFn` callback contract on `executePlanSteps`. When a
   step fails AND the rules-based fixer in `tryAutoFixValidationError`
   can't recover AND the error looks like a validation error, the
   executor calls the repair callback exactly once with the failing
   step, the AWS error, and the step index. If the callback returns a
   refined step with different params (same service + command), the
   executor retries with it.

3. New `AiRemediationService.refineStepFromError` that fulfills the
   contract — sends the failing step, AWS error verbatim, neighbor
   steps, and finding context to the model and asks for a corrected
   single step. Defensive guard: discards the refinement if the model
   swaps to a different service or command.

4. `RemediationService.executeRemediation` wires the repair callback
   at the fix-step execution site only (not reads or rollback) so the
   AI cost is paid only on user-initiated fix attempts that actually
   need it.

This is the universal fix the customer asked for: it uses AWS's own
validation as ground truth, scales to any future AWS command without
code changes, and pays the Opus cost only on the failure path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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