Skip to content

[comp] Production Deploy#2597

Merged
carhartlewis merged 22 commits into
releasefrom
main
Apr 19, 2026
Merged

[comp] Production Deploy#2597
carhartlewis merged 22 commits into
releasefrom
main

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented Apr 19, 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

Unifies findings into a single overview with create/detail sheets and a unified API, adds severity and area support (including general buckets), surfaces legacy scope in the UI, tightens permissions and notifications, and fixes enum validation, select label overflow, and role-gated status options.

  • New Features

    • New /overview/findings with search, filters, open-count tab, deep-linking (auto-opens the sheet), and create/detail sheets.
    • Unified API: one GET /v1/findings with filters (status, severity, area, targets). Admin reuses the same DTOs and service.
    • Target model: each finding ties to exactly one target (task, evidence submission, form type, policy, vendor, risk, member, device) or an area; areas include general risks/vendors/policies.
    • Create/Detail sheets: support evidence submissions and general areas; role-gated status options; add copy-link and delete confirmation; show a legacy scope hint for backfilled findings.
    • Notifications: task findings notify only stakeholders; evidence form types are normalized; ready-for-review created by platform admins fall back to owners/admins.
    • Cleanup/UX: removed findings UIs from Tasks, Documents, and People; Admin tab reuses the new create sheet with org-scoped pickers and correct evidence-form endpoint; Export All Evidence now uses a Sheet; People/Devices tabs render faster with skeletons and non-blocking Fleet load. Truncated long select labels and fixed a SelectTrigger type error. DTO hardening: accepts new FindingArea values even if the process started before a Prisma client regen. Dropped a redundant creation audit log to prevent duplicate activity entries.
  • Migration

    • Schema adds FindingSeverity, FindingArea (including other, risks, vendors, policies), and nullable FKs for policy/vendor/risk/member/device; drops FindingScope and backfills legacy rows to area='people' (kept as-is; legacy scope is shown in the UI).
    • Run the DB migration; no manual data steps. Auditor-only finding:create/delete remains; owners/admins keep read/update.

Written for commit 6f2474b. Summary will update on new commits.

github-actions Bot and others added 6 commits April 18, 2026 16:32
  Findings were scattered across ~7 entry points (Tasks, People directory,
  People Tasks, People Devices, People Chart, Documents/evidence form types,
  Questionnaire context, Admin). Consolidate everything into a dedicated
  `/overview/findings` route so auditors raise findings in one place and
  everyone else sees one list with deep-links back to the linked item.

  Highlights

  - Schema: drop `FindingScope`; add `FindingSeverity`, `FindingArea`; add
    nullable FKs for policy / vendor / risk / member / device alongside
    existing task / evidence-submission / evidence-form-type targets.
    Migration backfills legacy `scope` rows onto `area = 'people'`.
  - API: rewrite FindingsService, audit service, notifier, and controllers
    around the unified target validator (exactly one of 8 targets or an
    area). Single `GET /v1/findings` supports filtering by status, severity,
    area, and any target FK. Admin controller reuses the same DTO.
  - Permissions: `finding:create` and `finding:delete` are auditor-only;
    owners/admins keep `read` + `update` so they can transition status.
  - Frontend
    - New `/overview/findings` route with Link-based tabs so the dashboard
      no longer loads findings data. Open-count badge on the Findings tab.
    - DS-styled `FindingsTab` table with search + status / severity /
      framework filters, DS `Empty` state, 360px balanced columns, and
      truncated content with a hover tooltip.
    - `CreateFindingSheet`: target-type picker (task / policy / vendor /
      risk / person / device / document type / area) with live comboboxes
      backed by existing endpoints, capitalised labels, and
      auto-selection of the org's framework when only one is configured.
    - `FindingDetailSheet`: edit content (auditor-only), change status /
      severity, add revision note, view activity log, and delete. Content
      renders as read-only for non-auditors.
    - `use-findings-api` collapsed to `useOrganizationFindings`,
      `useFindingHistory`, `useFindingTemplates`, `useFindingActions`.
  - Deletions: `tasks/[taskId]/components/findings/` (whole folder),
    `PeopleFindingsUnifiedList`, `DocumentFindingsSection`,
    `FindingsOverview`, plus call-site cleanup in SingleTask,
    CompanyFormPageClient, people/page, and PeoplePageTabs.
  - Admin: old `FindingForm` removed; admin `FindingsTab` now reuses the
    unified `CreateFindingSheet` via a `createFn` override that targets
    `/v1/admin/organizations/:orgId/findings`.
  - People perf: wrap `TeamMembers` RSC in `<Suspense>` with a skeleton so
    the tabs paint immediately; `DevicesTabContent` renders agent devices
    right away and shows a small loading pill while the slower Fleet SWR
    resolves. Dev-only 3s delay in `useFleetHosts` for local testing.
  - Audit log: drop the "Admin"/"Auditor" prefix from finding descriptions
    in `extractFindingDescription` — the actor name already says it.
  - DX: `packages/db/scripts/seed-load-test.ts` seeds ~100 members, 25
    devices, and a 10×10 org-chart grid for local load testing.
  - Misc: auditor view "Export All Evidence" swapped from broken
    Popover-over-render-Button to a proper DS `Sheet`; local DS patch
    adding `nativeButton={false}` to pagination `ButtonPrimitive`s while
    the upstream fix lands.
  - Tests: placeholder specs replaced with real coverage for the target
    validator, notifier recipient resolution, admin findings tab, and the
    unified FindingsTab (empty state + auditor-gated create).
…b functionality

- Modified the deep link format in `finding-notifier.service.ts` to direct users to the Findings page with the finding sheet pre-opened.
- Enhanced `FindingsTab.tsx` to support deep linking by auto-opening the specified finding's sheet when the page loads, and removing the query parameter to prevent reopening on refresh.
- Integrated `useRouter` and `useSearchParams` from Next.js for improved navigation and state management.
… sharing functionality

- Implemented an AlertDialog for confirming the deletion of findings, enhancing user experience by preventing accidental deletions.
- Added a "Copy link" button to share the finding's link, allowing users to easily share findings with their team.
- Updated the delete button to open the confirmation dialog instead of directly deleting the finding.
`getTaskRecipients` was using `user.role != 'admin'` (better-auth platform
admin) in the where clause, which effectively matched every active
non-platform-admin member of the org. That meant a task-scoped finding
would email every employee/contractor/auditor/custom-role member with a
200-char preview of the finding content plus a deep link — rather than
just the people who can actually act on it.

This mirrors what the policy/vendor/risk/device branches already do:
fetch the target's assignee and union with org owners/admins via
`includeAdmins`. Finding content is now only disclosed to stakeholders
of that specific task.

Pre-existing behaviour (not introduced by the unified-findings PR) but
worth fixing here since it's a concrete over-share of audit content.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
[dev] [carhartlewis] lewis/comp-people-fix
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 19, 2026

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

Project Deployment Actions Updated (UTC)
app (staging) Ready Ready Preview, Comment Apr 19, 2026 5:36pm
comp-framework-editor Ready Ready Preview, Comment Apr 19, 2026 5:36pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
portal (staging) Skipped Skipped Apr 19, 2026 5:36pm

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.

10 issues found across 59 files

Note: This PR contains a large number of files. During the trial, cubic reviews up to 50 files per PR. Paid plans get a higher limit.

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/CreateFindingSheet.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/overview/components/CreateFindingSheet.tsx:69">
P2: `evidenceSubmission` is handled in code but missing from the target dropdown, so users cannot create findings linked to an evidence submission from this form.</violation>
</file>

<file name="packages/db/prisma/schema/finding.prisma">

<violation number="1" location="packages/db/prisma/schema/finding.prisma:50">
P1: The model documents an "exactly one target or area" rule, but the schema/migration do not enforce it, allowing ambiguous or targetless findings to be persisted.</violation>
</file>

<file name="apps/api/src/findings/findings.service.ts">

<violation number="1" location="apps/api/src/findings/findings.service.ts:341">
P2: Normalize the finding before notifying on create, otherwise notification labels can expose DB enum values instead of external form-type names.</violation>

<violation number="2" location="apps/api/src/findings/findings.service.ts:448">
P2: Pass normalized form-type values to status-change notifications to avoid user-facing DB enum strings.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/overview/components/FindingDetailSheet.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/overview/components/FindingDetailSheet.tsx:74">
P2: Risk finding links point to `/risks/...` but the actual route is `/risk/...`, so the linked item navigation breaks for risk targets.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/admin/organizations/[adminOrgId]/components/FindingsTab.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/admin/organizations/[adminOrgId]/components/FindingsTab.tsx:219">
P1: `CreateFindingSheet` is wired into the admin org page, but it fetches task/policy/vendor/etc. options from current-org endpoints instead of the selected admin organization, causing incorrect target selection context.</violation>
</file>

<file name="apps/api/src/findings/dto/create-finding.dto.ts">

<violation number="1" location="apps/api/src/findings/dto/create-finding.dto.ts:39">
P1: The new target ID fields allow empty strings, which can bypass target/entity validation and then fail at DB write time.</violation>
</file>

<file name="apps/api/src/findings/findings.service.spec.ts">

<violation number="1" location="apps/api/src/findings/findings.service.spec.ts:35">
P2: Add `FindingSeverity` to the `@db` mock; `CreateFindingDto` uses it at import time and the current mock can break module initialization.</violation>
</file>

<file name="apps/api/src/findings/finding-notifier.service.ts">

<violation number="1" location="apps/api/src/findings/finding-notifier.service.ts:158">
P2: `ready_for_review` notifications are dropped for findings created by platform admins because the new logic returns early when `createdById` is null.</violation>

<violation number="2" location="apps/api/src/findings/finding-notifier.service.ts:433">
P2: `includeAdmins` bypasses org active-member checks for the primary recipient, which can send notifications to deactivated users.</violation>
</file>

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

Comment thread packages/db/prisma/schema/finding.prisma
Comment thread apps/api/src/findings/dto/create-finding.dto.ts
Comment thread apps/api/src/findings/findings.service.ts
Comment thread apps/api/src/findings/findings.service.ts
Comment thread apps/app/src/app/(app)/[orgId]/overview/components/FindingDetailSheet.tsx Outdated
Comment thread apps/api/src/findings/findings.service.spec.ts
Comment thread apps/api/src/findings/finding-notifier.service.ts Outdated
Comment thread apps/api/src/findings/finding-notifier.service.ts Outdated
carhartlewis and others added 8 commits April 19, 2026 13:57
The unified_findings migration (20260419120000) mapped every legacy
`scope IN (people, people_tasks, people_devices, people_chart)` row
onto `area = 'people'` so they stayed queryable. That's misleading in
the new model — a finding that used to have scope `people_chart` was
about the org chart, not the people directory. Reading the table now
makes those rows look like first-class people-area findings.

Fix:
- Add `'other'` to the `FindingArea` enum (20260419140000) — separate
  migration because Postgres 12+ disallows using a newly-added enum
  value in the same transaction it was added.
- Reclassify `area='people' AND createdAt < 2026-04-19 12:00:00 UTC`
  rows to `area='other'` (20260419140001). The cutoff matches the
  unified_findings migration timestamp: before that point the `area`
  column didn't exist, so any `area='people'` row pre-dating it can
  only be a backfill artefact. Genuine `area='people'` findings
  created after the cutoff are preserved.

Prod safety: migrations run back-to-back during a single deploy with
no app activity in between, so there is no window for a real
`area='people'` finding to be created before the reclass runs. For
staging, which has already applied the unified migration, any finding
created between then and the reclass deploy will have `createdAt`
after the cutoff and will not be touched.

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

Previous version used a hard-coded `createdAt < 2026-04-19 12:00:00`
cutoff to decide which `area='people'` rows were backfilled legacy
rows vs. genuine post-migration people-area findings. That's broken
for prod:

- Prod will keep using the old `FindingScope` code path until the
  deploy that runs 20260419120000.
- Findings created on prod between now and that deploy have
  `createdAt > cutoff` but are still legacy scope rows.
- Migration 1 backfills them to `area='people'`. Migration 3 would
  leave them alone because their `createdAt` is past the cutoff —
  wrong.

Switch the signal to the creation AuditLog. The old
finding-audit.service always wrote the source scope into
`data.findingScope` on the creation entry. Presence of that field is
a precise, environment-agnostic marker of "created via the old
scope-based flow". The new service writes `targetKind`/`area` instead,
so genuine post-migration findings won't match and will be left alone.

Also appends a human-readable footer to `content` so the detail sheet
still shows the original scope ("Originally logged against People ›
Devices"). The content field is preserved verbatim and renders in the
existing read-only paragraph for non-auditors.

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

The previous reclass migration (20260419140001) would have moved legacy
people-scope findings to area='other' and appended a content footer.
That reclass breaks the /people deep-link that owners/admins currently
rely on (targetHref returns null for area='other'), so it's worse for
operability than leaving rows at area='people'.

Do it in the UI instead:
- `FindingHistoryEntry.data` typed with optional `findingScope`.
- `legacyScopeLabelFromHistory` reads the creation AuditLog entry and
  maps `findingScope` → "People › Devices" / "Directory" / "Tasks" /
  "Org chart".
- `FindingDetailSheet` renders a muted "Originally logged against …"
  line below the target label when a legacy scope is present.

No DB migration required. The `FindingArea` enum retains `other` for
future use / manual categorisation, but no rows are force-reclassified.
Legacy rows keep area='people' and their /people link, so owners/admins
can still drill in to fix what the auditor found.

Drops migration 20260419140001 (never applied). Keeps 20260419140000
(just adds the `other` enum value — safe no-op for existing rows).

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

Two issues Paul flagged:

1. "Create Finding" sheet required picking a specific Risk / Vendor /
   Policy — no way to log "no risks tracked" or "missing policy" when
   the row doesn't exist yet. Auditors were forced to pick a random
   target just to get past the "Please select a target" validation.
2. Every finding's activity log showed two "created" entries at the
   same timestamp — "created a finding" (from the global
   AuditLogInterceptor) and "created this finding" (from
   FindingsService.create calling findingAuditService.logFindingCreated).
   Same for deletions.

Fixes:

- Extend `FindingArea` enum with `risks` / `vendors` / `policies`
  (migration 20260419150000). Each new value is its own ALTER TYPE
  ADD VALUE statement because Postgres 12+ disallows using a newly-
  added enum value in the same transaction.
- Surface the new values in `CreateFindingSheet` as "Risks (general)",
  "Vendors (general)", "Policies (general)" under the existing "Area"
  target type. Detail sheet + list row render the label verbatim and
  link general findings to `/risks`, `/vendors`, `/policies` so the
  owner/admin can drill in.
- Remove the explicit `findingAuditService.logFindingCreated` and
  `logFindingDeleted` calls from `FindingsService`. The global
  `AuditLogInterceptor` already writes those via
  `extractFindingDescription`. Status / type / content-change audits
  are kept — the interceptor can't derive those deltas.

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

- create-finding.dto.ts: add @isnotempty() to every optional target ID
  (taskId, evidenceSubmissionId, policyId, vendorId, riskId, memberId,
  deviceId, templateId). Empty strings now get rejected with 400 before
  reaching resolveTarget.
- finding-notifier.service.ts:
  - normalize evidenceFormType via toExternalEvidenceFormType in
    findingLabel so email/Novu labels show "board-meeting" instead of
    the DB enum "board_meeting".
  - When a finding was created by a platform admin (createdById null,
    createdByAdminId set), fall back to notifying org owners/admins on
    ready_for_review transitions so the review isn't silently dropped.
  - includeAdmins now resolves the primary recipient through
    db.member.findFirst with deactivated=false + isActive=true so
    deactivated/ex-members are excluded, matching getOwnersAndAdmins.
- FindingDetailSheet: correct risk deep link — route is /risk/:id
  (singular), not /risks. Covers both specific risk targets and the
  general "Risks" area.
- CreateFindingSheet: new `endpointOverrides` and `disabledTargetKinds`
  props so the admin surface can point pickers at admin-scoped
  endpoints and hide target types with no admin equivalent.
- admin FindingsTab: supplies admin endpoints for task/policy/vendor/
  evidenceFormType and hides risk/member/device until admin equivalents
  exist. Pickers now show the correct org's data.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tab switches on /people were 700ms-2.2s because the RSC for page.tsx
blocked on four sequential awaits (session, currentUserMember,
complianceMembers findMany, filterComplianceMembers) before returning
the shell. Every query-param change — including switching to Chart or
Devices, which don't need compliance data — ran the whole chain.

Move the two heavy queries into <TeamMembers>, which is already
Suspense-wrapped. The page shell now only waits on session +
currentUserMember (needed for tab/permission gating), so tab
navigation paints immediately and compliance bookkeeping streams in
underneath.

Drops the `complianceMemberIds` prop on <TeamMembers> — it already
computes the ID list from its own members fetch via
filterComplianceMembers, so the page-level query was duplicated work.
Also removes the `showEmployeeTasks` gate; the Tasks tab now always
renders (empty state handles zero-employee orgs), which avoided the
prior need to count employees server-side on every navigation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add `evidenceSubmission` to CreateFindingSheet target dropdown so
  users can actually reach the free-text submission-ID picker the form
  already supports.
- Add `FindingSeverity` to the `@db` jest mock; CreateFindingDto
  imports it at module load time, so its absence risked breaking
  module initialization in tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
[dev] [carhartlewis] lewis/comp-findings-fixes
@carhartlewis
Copy link
Copy Markdown
Contributor

  • Issue 1 (P2) — CONFIRMED, fixed: Added evidenceSubmission entry to TARGET_OPTIONS in CreateFindingSheet.tsx.
  • Issue 2 (P1) — Valid observation but fix skipped: Adding a DB CHECK constraint requires a migration and risks rejecting any legacy rows; deferring to a planned cleanup.
  • Issue 3 (P2) — FALSE: finding-notifier.service.ts already runs every form-type value through normalizeFormType() before rendering labels. Notifier receives raw enums by design.
  • Issue 4 (P2) — FALSE: Line 74 already points to /risk/${id} (singular), matching the actual route directory.
  • Issue 5 (P1) — FALSE: FindingsTab.tsx:219 passes organizationId={orgId}, adminCreateFn (posting to /v1/admin/organizations/${orgId}/findings), and endpointOverrides that route every picker to admin endpoints.
  • Issue 6 (P1) — FALSE: @IsOptional() only skips validators when value is null/undefined; @isnotempty() fires on "" and returns 400. Empty strings can't reach the DB.
  • Issue 7 (P2) — CONFIRMED, fixed: Added FindingSeverity to the @db mock in findings.service.spec.ts.
  • Issue 8A (P2) — FALSE: notifyStatusChanged explicitly falls back to getOwnersAndAdmins when createdById is null (lines 173-187, with comment).
  • Issue 8B (P2) — Violation as written is FALSE (primary recipient does check both deactivated: false and isActive: true). A minor asymmetry exists on admins but doesn't match the claim; skipping.

carhartlewis and others added 2 commits April 19, 2026 16:55
`@IsEnum(FindingArea)` captures the Prisma-generated enum object at
decorator-eval time. If the API dev server was started before the last
`prisma generate` picked up the new `risks` / `vendors` / `policies` /
`other` values, class-validator keeps rejecting them with "area must
be one of the following values: people, documents, compliance" even
though the schema and generated client are up to date.

Swap to an explicit @isin([...]) list so the DTO's accepted values
don't depend on the exact prisma client the process booted with.

Note: the DB enum still needs migrations 20260419140000 +
20260419150000 applied for the insert to succeed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Long risk/policy/template titles overflowed the SelectTrigger and
spilled outside the sheet. Wrap the selected-option label in a
`block truncate` span so overly long titles get ellipsis'd inside the
trigger.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
carhartlewis and others added 2 commits April 19, 2026 17:01
SelectTrigger doesn't accept className. The previous commit added it
for truncation and broke the app build. Move the truncation onto the
inner span via `block max-w-full truncate` instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
[dev] [carhartlewis] lewis/comp-findings-fixes-v2
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

5 issues found across 62 files

Note: This PR contains a large number of files. During the trial, cubic reviews up to 50 files per PR. Paid plans get a higher limit.

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/findings/finding-audit.service.ts">

<violation number="1" location="apps/api/src/findings/finding-audit.service.ts:11">
P2: `logFindingCreated` remains an unused code path, so the newly added target mapping is dead code and can diverge from real audit behavior.</violation>
</file>

<file name="apps/api/src/findings/findings.service.ts">

<violation number="1" location="apps/api/src/findings/findings.service.ts:131">
P2: `listForOrganization` does not support filtering by severity, so callers cannot retrieve findings by severity even though severity is now persisted.</violation>
</file>

<file name="packages/db/prisma/schema/finding.prisma">

<violation number="1" location="packages/db/prisma/schema/finding.prisma:55">
P1: The schema documents an "exactly one target or area" invariant, but it is not enforced at the database level, so invalid findings (zero or multiple targets) can be persisted.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/overview/components/FindingDetailSheet.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/overview/components/FindingDetailSheet.tsx:329">
P2: Filter status options by role before rendering; the current dropdown exposes backend-forbidden statuses and leads to predictable 403 save failures.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/overview/components/CreateFindingSheet.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/overview/components/CreateFindingSheet.tsx:147">
P2: `extractOptions` does not handle object-map responses, so the document-type target picker can render empty for evidence-form endpoints that return keyed objects.</violation>
</file>

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

Comment thread packages/db/prisma/schema/finding.prisma
Comment thread apps/api/src/findings/finding-audit.service.ts Outdated
Comment thread apps/api/src/findings/findings.service.ts
Comment thread apps/app/src/app/(app)/[orgId]/overview/components/FindingDetailSheet.tsx Outdated
carhartlewis and others added 4 commits April 19, 2026 17:58
- Filter status dropdown by auditor/platform-admin so users can't pick
  backend-forbidden statuses and hit predictable 403s on save
- Drop dead `logFindingCreated` audit method (creation is logged via the
  global AuditLogInterceptor; the helper had drifted from real behavior)
- Add `severity` query param to GET /v1/findings list endpoint
- Stop pointing the admin findings picker at `/v1/admin/.../evidence-forms`
  (returns a status map, not form definitions); fall back to the static
  `/v1/evidence-forms` so the document-type picker actually populates

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The status dropdown was using `permissions.finding.includes('create')` as
a proxy for auditor, but the API gates transitions on the literal role
string. A custom role granting `finding:create` would have been
mis-classified as auditor and seen the wrong status options. Expose
`roles` from `usePermissions` and check `roles.includes('auditor')` to
mirror the server.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
[dev] [carhartlewis] lewis/comp-findings-fixes-v3
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.

4 issues found across 63 files

Note: This PR contains a large number of files. During the trial, cubic reviews up to 50 files per PR. Paid plans get a higher limit.

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/findings/findings.controller.spec.ts">

<violation number="1" location="apps/api/src/findings/findings.controller.spec.ts:4">
P2: Replace this placeholder test with a real controller assertion. As written, the spec always passes and provides no coverage for `FindingsController` regressions.</violation>
</file>

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

<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/devices/components/DevicesTabContent.tsx:43">
P2: This agent-only loading gate can show a false empty-state message while Fleet is still loading. Keep the skeleton until Fleet resolves when there is no agent data yet.</violation>
</file>

<file name="apps/api/src/findings/finding-notifier.service.ts">

<violation number="1" location="apps/api/src/findings/finding-notifier.service.ts:383">
P1: Scope recipient lookup queries by `organizationId`; current ID-only lookups can resolve cross-tenant recipients and leak finding notifications.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/overview/components/FindingsTab.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/overview/components/FindingsTab.tsx:145">
P2: Deep-link handling does not switch to the requested finding when a different finding is already selected.</violation>
</file>

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

Comment thread apps/api/src/findings/finding-notifier.service.ts
Comment thread apps/api/src/findings/findings.controller.spec.ts
Comment thread apps/app/src/app/(app)/[orgId]/overview/components/FindingsTab.tsx
@carhartlewis
Copy link
Copy Markdown
Contributor

Approved by @Marfuen

@carhartlewis carhartlewis merged commit 09689ee into release Apr 19, 2026
15 checks passed
@claudfuen
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.24.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants