Skip to content

[comp] Production Deploy#2616

Merged
Marfuen merged 14 commits intoreleasefrom
main
Apr 20, 2026
Merged

[comp] Production Deploy#2616
Marfuen merged 14 commits intoreleasefrom
main

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

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

Adds a feature‑flagged compliance timeline across API and app with templates, phases, and auto‑completion driven by org metrics. Also adds per‑org PostHog flag admin, improves device compliance with a “stale” state and CSV export, fixes Prisma client generation for Docker builds, and publishes a public integrations catalog with safe, sanitized metadata.

  • New Features

    • Compliance timelines (gated by is-timeline-enabled): new TimelinesModule with customer endpoints (GET /v1/timelines, mark phase ready) and admin endpoints (activate/pause/resume, edit phases, CRUD templates, start next cycle). Default templates for SOC 2 Type 1/2 and ISO 27001 with grouped phases, date recalculation, Slack notifications, and live AUTO_* phase completion (tasks, policies, people, findings) from org metrics. UI: Overview Timeline tab, framework timeline, admin org Timeline tab with editor, and a full timeline template editor.
    • Per‑org feature flags: admin APIs to list/toggle PostHog flags per organization (audit‑logged) and a new “Feature Flags” admin tab. Frontend uses OrganizationIdentifier and useFeatureFlag for org group targeting.
    • Devices: three‑state compliance with “stale”; API returns complianceStatus and daysSinceLastCheckIn; UI shows “Stale (Nd)” and adds CSV export. Daily job flags stale devices non‑compliant. Staleness helpers in @trycompai/utils.
    • Public integrations catalog: adds a sanitized catalog of ~540 integrations under integrations-catalog/ with vendor info, auth type, setup steps, and check summaries. Includes sync tooling in tools/integrations-catalog-sync/ with secret scanning, manual refresh, and request pacing to avoid 429s.
  • Bug Fixes

    • CSV export hardening (UTF‑8 BOM, CRLF, quoting, formula‑injection guard) and safe filename; guard invalid timestamps in daysSinceCheckIn; charts count stale as non‑compliant; fix cron maxDuration units.
    • Timelines: pure‑read GET /timelines with reconciliation moved to mutation hooks; handles advancement and regression safely, pins dates on early completion, idempotent ready‑for‑review with single Slack ping, and consistent status transitions; Slack helper checks response, escapes text, and uses safe URL fallback.
    • PostHog/infra: paginate and origin‑validate PostHog REST flag listing; prefer backend POSTHOG_API_KEY; cache “not configured” warning; treat non‑empty variants as enabled; handle Prisma P1001 in dynamic manifest loader.
    • Build: Dockerfile explicitly generates the Prisma client so @prisma/client is available at build time; extended .gitignore for generated clients and worktrees.

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

tofikwest and others added 3 commits April 17, 2026 16:16
…anitized inputs check

Expand the GitHub Sanitized Inputs check to cover more ecosystems and libraries, and tighten the Python matcher so false positives on substrings (e.g. "schema" inside "jsonschema") and comments no longer pass.

- JS/TS: zod (existing) + yup, joi, @effect/schema, effect, valibot, ajv, class-validator, io-ts, superstruct, runtypes
- Python: pydantic (existing) + marshmallow, cerberus, voluptuous, jsonschema, schematics, typeguard
- PHP: new — laravel/framework, respect/validation, symfony/validator, vlucas/valitron (detected via composer.json)
- Python matcher rewritten to parse lines, strip comments, and match package names as standalone tokens
- Failure message updated to list all supported libraries so customers know what we check for

Closes customer complaints surfaced in Slack (Yup, Joi, Marshmallow, Effect Schema, Laravel).

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

* feat(utils): add device staleness helpers

* feat(api): derive device complianceStatus on read

* feat(api): surface stale status in /v1/devices

* feat(trigger): daily cron to flag stale devices non-compliant

* fix(trigger): surface error detail on flag-stale-devices failure

* feat(devices): add client-side CSV export helper

* fix(devices): harden CSV export (CRLF, BOM, filename sanitization)

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

* feat(devices): render stale state and add CSV export button

* refactor(devices): hoist orgId to parent, avoid per-row useParams

* chore(utils): apply prettier formatting to devices.test.ts

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

* fix(devices): chart treats stale devices as non-compliant

* chore(devices): use DS Button iconLeft prop for CSV export

* fix(devices): address cubic P1/P2 review feedback

- packages/utils: guard daysSinceCheckIn against NaN so corrupt
  lastCheckIn strings are treated as stale (P1).
- devices-csv: neutralize CSV formula injection (leading =, +, -, @,
  tab, CR) by prefixing with apostrophe per OWASP (P2).
- flag-stale-devices: maxDuration is in seconds, not ms (P2).

---------

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

vercel Bot commented Apr 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 Apr 20, 2026 10:05pm
comp-framework-editor Ready Ready Preview, Comment Apr 20, 2026 10:05pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
portal (staging) Skipped Skipped Apr 20, 2026 10:05pm

Request Review

github-actions Bot and others added 2 commits April 20, 2026 16:18
* feat(db): add timeline models and enums for compliance timeline feature

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

* feat(api): add default timeline template definitions as code constants

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

* feat(api): add timeline date recalculation helper with tests

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

* feat(api): add timelines service with CRUD, activation, pause/resume, phase completion

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

* refactor(api): split timelines service into focused modules under 300 lines each

Extract lifecycle, phase editing, template management, and template resolution
into separate files to comply with the max 300 lines per file rule.

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

* feat(api): add timeline DTOs, controllers, module registration, and Swagger cleanup

- DTOs: activate-timeline, update-phase, create-template, create-phase-template,
  update-template, update-phase-template with class-validator decorators
- Customer controller: GET /timelines, GET /timelines/:id,
  POST /timelines/:id/phases/:phaseId/ready with Slack webhook notification
- Admin template controller: full CRUD for timeline templates and their phases
- Admin org timelines controller: activate, pause, resume, phase CRUD, complete
- TimelinesModule registered in AppModule with all services and controllers
- Added @ApiExcludeController() to all 8 existing admin controllers

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

* feat(api): auto-create timeline on framework add and auto-complete phases on 100% tasks

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

* feat(app): add SWR hooks for timelines and admin timeline management

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

* feat(app): add reusable TimelinePhaseBar component

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

* fix: add generated prisma to gitignore and add completedBy include to findOne

* feat(app): add TimelineOverview component to Overview page

Add a compliance timeline section above the existing dashboard grid,
showing stacked timeline cards with phase bars, status badges, and
date summaries. Also updates the Timeline hook types to match the
actual API response (DRAFT/ACTIVE/PAUSED/COMPLETED statuses).

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

* feat(app): add expanded timeline view to framework detail page

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

* feat(app): add admin timeline template management page

Add a new admin page for managing timeline templates with CRUD operations.
Includes a template list with phase bar previews and a sheet editor for
creating/editing templates and their phases. Added sidebar navigation link.

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

* feat(app): add Timeline tab to admin org detail page

Add a Timeline tab to the admin organization detail view showing all
timelines for an org. Includes status badges, phase tables, and action
buttons for activating (with date picker), pausing, resuming, and
editing individual phases via a sheet editor.

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

* fix(api): auto-create timelines for existing framework instances on first load

* fix(api): fix type error in date helper tests

* feat(api): smart timeline backfill using trust status, scores, and task completion data

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

* fix(app): use design system tokens, remove outer card, add next cycle date

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

* fix(app): improve phase bar with progress indicator and add timeline summary

* fix(api): use 'Attestation' instead of 'Certification' for HIPAA and GDPR

* feat: separate SOC 2 Type 1 and Type 2 timelines, add date markers to phase bar

* fix: replace em dashes with hyphens in template names

* fix(api): wrap admin timeline endpoints in { data, count } response format

* fix(app): use correct timeline status values (DRAFT/ACTIVE/PAUSED/COMPLETED) in admin components

* fix(app): fix missing closing brace in TimelineCard

* fix(app): replace table layout with stacked phase cards in admin timeline view

* feat(app): add tabs to Overview page with Timeline tab and compact teaser

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

* fix(app): simplify timeline teaser to clean single-line banner without phase bar

* fix(app): remove timeline teaser from Overview tab

* fix(app): remove redundant header and summary from Timeline tab

* fix(app): use defaultDurationWeeks for template phase duration (fixes NaN)

* fix: recalculate end date and downstream phases when duration or start date changes

* feat(api): auto-complete timeline phases immediately when tasks are marked done

* feat: add phase grouping with groupLabel for SOC 2 Type 2 sub-phases

* feat(admin): add delete, reset, and recreate timeline actions with confirmation dialogs

* fix(app): close confirmation dialogs immediately on confirm

* fix(api): recreate also deletes DB templates so code defaults are re-seeded

* fix(api): force-refresh templates from code defaults during recreate

* fix(app): add bracket lines connecting group label to phase edges

* fix(app): render grouped phases as cohesive blocks with no internal gaps

* fix(app): show one start/end date per group instead of per sub-phase

* feat: add AUTO_POLICIES and AUTO_PEOPLE completion types for independent sub-phase tracking

* fix(api): use getOverviewScores for AUTO_POLICIES and AUTO_PEOPLE checks

* fix(api): use correct property names from scores (total/published)

* fix(app): wrap phase groups to new rows on small screens

* fix(app): vertical phase stack on mobile, horizontal bar on lg+ screens

* fix(app): keep grouped phases in one row on mobile, only ungrouped phases stack

* fix(app): treat ungrouped phases as individual groups so each gets its own row on mobile

* fix(app): show group labels and dates per row on mobile phase bar

* fix(app): replace mobile phase bars with clean vertical step list

* fix(app): compact horizontal bars on mobile without per-phase dates

* fix(app): remove checkmarks from completed phase bars

* fix(app): add diagonal stripe pattern to unfilled portion of active phase

* fix(app): use primary color for stripe pattern on active phase

* fix(app): use oklch color-mix for stripe pattern instead of hsl wrapper

* fix(app): reduce stripe opacity to 10% for subtler effect

* fix(app): reduce gap between phases from 3px to 1px

* fix(api): rename SOC 2 Type 2 - Year 1 to SOC 2 Type 2

* fix(app): remove redundant Started/Est. completion text from timeline cards

* feat: live completion percentages for AUTO_* phases, revert if metric drops below 100%

* fix(api): rename SOC 2 sub-phases to Policies, Evidence, People

* feat(app): show live completion % and fill for all AUTO_* sub-phases independently

* feat(app): show avg completion % on group label, single cohesive fill for grouped phases

* fix(app): show individual sub-phase percentages inside grouped bar

* fix(app): add pulsing progress marker to grouped phase bar

* feat(app): add dedicated full-page timeline template editor

Replace the drawer-based template editor with a dedicated page at
[orgId]/admin/timeline-templates/[templateId]. The new editor includes
metadata form, live phase bar preview, individual phase cards with
save/delete/reorder, and group label support with color indicators.

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

* feat(app): framework dropdown in template creation and editing

* feat(api): centralized Slack notifications for phase completion, timeline completion, and ready-for-review

* fix(api): assign transaction result to txResult variable

* fix(api): use template name in Slack notifications to distinguish SOC 2 Type 1 vs Type 2

* fix(api): use Slack Block Kit for cleaner notification formatting

* feat(api): add clickable org link to Slack notifications with admin timeline URL

* fix(api): show org ID in Slack notification messages

* fix(app): only show stripes on IN_PROGRESS phases, plain muted for PENDING

* fix(api): only advance next phase to IN_PROGRESS when all prior phases are completed

* fix(app): show framework name instead of ID in template editor dropdown

* feat(app): group sub-phases under parent card in template editor

Phases that share a groupLabel are now nested inside a parent group
card with an editable label, total duration display, and "Add Sub-phase"
button. Sub-phases render as compact inline rows instead of full cards.
Ungrouped phases retain the existing full PhaseCard rendering.

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

* feat(api): add Auditor Review + Draft Report phases to SOC 2 templates

* fix(app): phase numbering counts groups as 1, label below Phase N, add wk suffix to duration

* fix(app): use DS Text component for weeks label instead of custom span

* fix(app): add column labels (Name, Duration, Completion) above sub-phase rows

* feat(app): add confirmation dialogs to all delete actions in template editor

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

* fix(api): add groupLabel to phase template DTOs and service

* fix(api): remove AdminAuditLogInterceptor from template controller (no org context)

* fix(api): pass groupLabel from DTO to service in addPhase and updatePhase

* feat(api): add timeline auto-completion hooks to policies, members, tasks, and evidence services

Wire up checkAutoCompletePhases fire-and-forget calls in all services
that can affect compliance progress metrics, enabling automatic phase
completion/reversion when tasks, policies, people, or evidence change.

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

* feat(api): add Preparing for Audit group (Policies, Evidence, People) to SOC 2 Type 1

* fix(api): auto-complete phases at 100% on page load, not just on events

* fix(app): group phase cards visually in admin timeline, show template name as title

* fix(api): use actual completion date as anchor for downstream phase recalculation

* chore(db): add migration for AUTO_POLICIES and AUTO_PEOPLE completion types

* feat: add Start Next Cycle action for completed timelines (admin only)

* fix(app): use render prop on AlertDialogTrigger to avoid nested buttons

* fix(app): destructure onStartNextCycle from TimelineActions props

* feat(app): group timeline cycles by framework with stacked card effect for past cycles

* fix(app): fix stacked card effect to show behind the main card

* fix(app): stacked card effect as thin strips peeking below main card

* fix(app): simple lip button below card for showing previous cycles

* fix(app): remove bottom border radius from card when previous cycles lip is attached

* fix(app): subtle muted background on previous cycles lip

* fix(app): reduce lip padding and font size for sleeker look

* refactor(app): migrate TimelineOverview to DS components (Card, Badge, Text, Stack)

* fix(app): improve timeline card padding, title size, and spacing

* fix(app): remove bg from previous cycles lip, only show on hover

* fix(app): remove lip, show cycle badge (Year 2) when multiple cycles exist

* fix(app): show Year badge on all timelines, computed from cycle count not internal ID

* fix(api): always use now() for completedAt and endDate when auto-completing phases

* fix(api): pin phase dates on early completion so recalculation doesn't overwrite them

* fix(api): use min(endDate, now) for completedAt in backfill to avoid future dates

* chore: update OpenAPI spec with timeline endpoints

* feat(timelines): add track-based SOC2 timeline model

* feat(findings): integrate timelines module and add AUTO_FINDINGS phase completion logic

* chore: ignore .worktrees directory

* feat(flags): add per-org PostHog feature flag admin toggle

Gates features behind PostHog flags evaluated per organization via group
targeting. Platform admins can list/toggle flags for a specific org from
the admin panel; end users pick up changes on route navigation.

- register org as PostHog group via OrganizationIdentifier in org layout
- useFeatureFlag hook (posthog-js/react based, auto-reactive)
- reload flags on route change so toggles propagate without full refresh
- admin endpoints list + patch flags for a given org (groupIdentify)
- Feature Flags tab in admin org panel with search + toggle
- gate Timeline tab in Overview behind is-timeline-enabled

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

* chore: timeline cleanup — Card DS fix, dedup import, timeline migration

- TimelineOverview uses DS Card title/headerAction props (no className)
- remove duplicate ApiExcludeController import
- add timeline prisma migration
- regenerate openapi.json

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

* fix(timeline,flags): address AI review feedback

API:
- reset timeline status to ACTIVE when reopening regressed phases so a
  COMPLETED timeline never holds an IN_PROGRESS phase (fix data corruption)
- fire-and-forget createTimelinesForFrameworks so timeline creation errors
  don't mask successful framework creation
- log errors in checkAutoCompletePhases .catch() in tasks/people/policies
  services instead of swallowing silently
- apply AdminAuditLogInterceptor to feature flag admin controller so
  PATCH toggles get audit-logged
- drop unused AdminAuditLogInterceptor import from admin-timeline-templates
  controller (no orgId in path, interceptor would no-op)
- cache PostHog "not configured" state so warning logs once, not per request
- read Slack webhook URL + APP_URL at call time instead of module load time
  so env var loading order doesn't silently disable notifications

App:
- surface SWR error state in FeatureFlagsTab so failed fetches don't render
  as an empty "no flags found" panel
- check .error on api.delete / api.patch / api.post in template-actions so
  partial saves throw instead of silently succeeding
- non-mutating sort of template.phases in TemplateEditorPage to avoid
  corrupting SWR cache

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

* fix(timeline): address remaining AI review P2 feedback

API:
- extract COMPLETION_TYPES to shared timeline-constants, narrow DTO
  completionType from string to CompletionType union
- trigger checkAutoCompletePhases on both done and not_relevant task
  statuses (AUTO_TASKS counts both as finished)
- drop unnecessary checkAutoCompletePhases after task creation — a todo
  task can't advance any AUTO phase

App:
- extract COMPLETION_OPTIONS constant to timeline-templates/constants
  and share across PhaseRow, TemplateEditor, template-actions
- remove orderIndex from form state (controlled/uncontrolled hybrid);
  compute from array position at submit time in template-actions
- convert TemplateMetadataForm's framework fetch from raw useEffect to
  SWR with proper error handling
- delete duplicate admin layout under timeline-templates (parent admin
  layout already guards the route)

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

* refactor(timelines): make findAllForOrganization pure-read

Previously GET /timelines mutated state (completed phases, reverted
regressed phases, updated timeline status). That violates HTTP semantics
and races under concurrent reads.

- extract the sync loop into TimelinesService.reconcileAutoPhasesForOrganization
  (handles both advancement and regression, replaces the inline logic)
- findAllForOrganization is now a pure read: ensureTimelinesExist (idempotent
  backfill) + fetch + in-memory enrichment with live completion percentages
- call reconcileAutoPhasesForOrganization from checkAutoCompletePhases so
  every existing event hook (task/policy/people/findings mutations) drives
  both advancement and regression — no stale state left for reads to repair
- recreateAllForOrganization explicitly reconciles before returning so
  freshly-recreated timelines are synced against current metrics

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

* fix(timelines): always reconcile after checkAutoCompletePhases

The reconcile call sat after an early return for phases.length === 0,
which skipped it in the exact case it mattered: when all AUTO phases are
already COMPLETED and a metric drops (regression). Wrap the advancement
in a try/finally so reconciliation fires unconditionally on every event
hook — extract advancement into runPhaseAdvancement for readability.

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

* chore(db): reorder timeline migrations to apply after main's latest

Three of the branch's timeline migrations were dated 2026-04-07/08, which
is before main's 20260410120000_add_finding_scope. On merge, Prisma would
apply them out-of-order relative to what staging/prod already have.

Rename them to 20260410130000-20260410130002 so they sit right after main's
latest migration while preserving internal dependency order (timeline
models → phase group label → auto policies/people completion types →
rest of timeline migrations).

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

* fix: address cubic P1/P2 review feedback batch

API:
- remove unused UseInterceptors import from admin-timeline-templates
- add @Transform trim to UnlockTimelineDto so whitespace-only reason fails
  @isnotempty validation
- log errors (this.logger.warn) on checkAutoCompletePhases .catch() in
  people-invite and evidence-forms services (matches pattern elsewhere)
- replace unreachable ECONNREFUSED check in dynamic-manifest-loader with
  the correct Prisma error code P1001
- forbidNonWhitelisted on FF admin controller's ValidationPipe so bogus
  body keys return 400 instead of silently stripping
- @ISINT (not @IsNumber) on cycleNumber so 1.5 etc. is rejected
- wrap posthog groupIdentify + flush in try/catch in FF service — network
  failures now surface as meaningful BadRequestException + log instead of
  raw 500
- check response.ok in Slack helper so non-200s are logged
- return sendSlack promise from notify* helpers instead of floating it
- drop getScores' checkAutoCompletePhases call — mutation event hooks
  already drive both advancement and regression; the dashboard read
  shouldn't trigger heavy writes on every load

App:
- TemplateMetadataForm: reset(values) after save so isDirty flips back,
  wrap setSaving in try/finally
- TemplateEditorPage: render distinct error state when the SWR fetch fails
  instead of falling through to "Template not found"
- TimelineActivateForm: validate date before ISO conversion and wrap body
  in try/finally so a bad date can't leave the button stuck in loading

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

* fix: address new cubic P1/P2 review batch

P1:
- template-actions: upsert phases first, then delete removed ones. Reverse
  order meant a mid-save failure could drop phases before their replacements
  were successfully written.

P2:
- tasks.service: trigger checkAutoCompletePhases on ANY status change
  (bulk and single update). Limiting it to done/not_relevant missed
  regressions when a finished task is reopened.
- findings.service: same — run reconciliation on every finding status
  transition, not only when moving to closed.
- admin-feature-flags.service: paginate the PostHog feature_flags REST
  response (follow `next` cursor) so orgs with >200 flags don't silently
  drop the tail.
- admin-feature-flags.service: treat any non-empty string variant returned
  by getAllFlags as enabled (multivariate flags report variant name, not
  boolean true), not just strict `=== true`.
- DTOs: use @ISINT instead of @IsNumber for cycleNumber, orderIndex,
  durationWeeks, defaultDurationWeeks — rejects fractional values at the
  boundary instead of failing later at the DB Int column.
- timelines-phases.service: wire data.endDate and data.datesPinned
  through the update. Previously the DTO advertised them but the service
  silently ignored both.
- admin-org-timelines.controller: pass datesPinned from DTO to service.
- TimelineActivateForm: parse date input as local midnight (match
  YYYY-MM-DD, construct Date with local components) so a negative-UTC-offset
  user's chosen day isn't shifted by ISO serialization.
- FeatureFlagsTab: disable all flag switches while any PATCH is in flight
  so a late rollback from a failed request can't overwrite a newer
  successful toggle.

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

* fix: address new cubic P1/P2 batch after main merge

P1:
- timelines-phases.service: if caller sets an explicit start/end date,
  force datesPinned=true regardless of what the DTO says — storing a
  specific date with datesPinned=false would let the downstream date
  recalculator silently overwrite it
- template-actions.createNewTemplate: wrap the phase-creation loop in
  try/catch and delete the orphan template on failure, so a failed phase
  POST can't leave a template with zero phases in the framework's list
- timelines.service.ensureTimelinesExist: always call backfillTimeline
  (which is idempotent per-track) instead of short-circuiting when any
  timeline already exists. Repairs partial state from a half-succeeded
  createTimelinesForFrameworks call (e.g. SOC 2 with only Type 1 created)

P2:
- markReadyForReview: return idempotently (alreadyReady=true) when the
  phase is already marked, and only fire the Slack ping on the first
  transition. Double-clicks / retries no longer spam the CX channel.
- timelines-slack.helper: use the same base-URL fallback chain the other
  notifiers use (NEXT_PUBLIC_APP_URL → BETTER_AUTH_URL → APP_URL), and
  escape user-supplied text before interpolating into Slack mrkdwn so org
  names with `<`, `>`, `&`, `|` can't break link syntax.
- posthog.service: prefer POSTHOG_API_KEY over NEXT_PUBLIC_POSTHOG_KEY so
  backend config can't be shadowed by frontend env values.
- tasks.service.createTask: re-run checkAutoCompletePhases after create —
  a new todo task reduces AUTO_TASKS completion % and can regress a
  previously COMPLETED phase.
- policies.service.spec: register a TimelinesService stub so the test
  module compiles after the service gained that dependency.

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

* fix: address new cubic P1/P2 batch

P1:
- admin-feature-flags.service: validate the PostHog pagination \`next\` URL
  against the configured host before following it. A compromised or
  malicious PostHog response could otherwise redirect pagination to an
  attacker-controlled host and leak the Authorization header (personal
  API key) via SSRF.
- findings.service: wire checkAutoCompletePhases into create() and
  delete() too. Previously only update() triggered reconciliation, so
  creating a new open finding or deleting the last open one could leave
  AUTO_FINDINGS phases out of sync with the actual ratio.

P2:
- frameworks-timeline.helper.createTimelinesForFrameworks: try/catch per
  track instead of wrapping the whole loop. A soc2_type1 failure no
  longer silently skips soc2_type2 — each track is attempted
  independently and logged on its own.
- timelines-date.helper: defensively copy Date objects on assignment so
  the caller's timelineStartDate (and the pinned start/end dates of
  input phases) can't be mutated by a caller writing back into the
  returned rows.
- admin-org-timelines.controller.addPhase: cast to PhaseCompletionType
  (already imported) instead of a hardcoded string union — stays in sync
  with the Prisma enum automatically when a new type is added.
- timeline.prisma: document the NULL-distinct behavior of the
  (frameworkId, templateKey) unique constraint so it's not misread as a
  bug. Rows without explicit keys are already covered by the
  (frameworkId, trackKey, cycleNumber) constraint above.

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

* test(flags): add FF tests + guard invalid POSTHOG_HOST

After auditing all cubic review threads, only two were actually still
open against the current code:

- admin-feature-flags.service: new URL(apiHost) at line 55 could throw
  if the configured host lacks a protocol (e.g. 'posthog.internal').
  Wrap it in try/catch and return [] with a logged error instead of
  crashing the whole list endpoint.
- admin-feature-flags.controller/service: no tests existed. Add a
  controller spec covering the NotFoundException path and happy-path
  delegation, plus a service spec covering: missing REST config, the
  SSRF guard refusing foreign-origin pagination, multivariate variant
  strings recognized as enabled, and setFlagForOrganization calling
  groupIdentify + flush.

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

* test(flags): use strict host match in fetch mock to silence CodeQL

The URL filter `u.includes('us.posthog.com')` could match `evil.com/us.posthog.com/`.
Harmless in a unit test but CodeQL flags the pattern. Switch to
`new URL(u).host === 'us.posthog.com'` so it's an exact-host check.

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

* chore(app): stop tracking generated Prisma client

apps/app/.gitignore had src/generated/ but not prisma/src/generated/,
which is where the schema's output setting actually writes. A previous
merge-conflict sweep committed ~96 generated files. Extend the gitignore
and untrack them — postinstall runs 'prisma generate' so the client is
recreated on install / build.

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

* chore(db): squash 9 timeline migrations into one via prisma migrate diff

Generated a single migration using 'prisma migrate diff' with
--from-migrations pointing at a copy of packages/db/prisma/migrations
that excludes the 9 timeline ones, and --to-schema pointing at the
current schema. The resulting SQL is byte-for-byte what main + the
squashed migration would produce (verified: migrate diff from the
final migrations dir against the schema reports 'No difference
detected').

Dropped:
- 20260410130000_add_timeline_models
- 20260410130001_add_phase_group_label
- 20260410130002_add_auto_policies_people_completion_types
- 20260413182638_add_timeline_lock_flags
- 20260413200000_add_timeline_lock_state_and_regression
- 20260413212000_add_timeline_template_progression_keys
- 20260413231500_add_timeline_track_keys
- 20260414113000_add_auto_findings_completion_type
- 20260417155556_timeline

Replaced with:
- 20260420000000_timeline_feature

Local devs who already applied any of the dropped migrations will need
to 'prisma migrate reset' — the old names still sit in their
_prisma_migrations table. Staging/prod haven't applied yet (branch
isn't merged) so they're unaffected.

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

* chore(app): remove the duplicated prisma/ setup

apps/app/prisma/ was a full duplicate of packages/db:
- client.ts was byte-identical to packages/db/src/client.ts
- prisma/schema/ duplicated packages/db/prisma/schema/ and was
  already drifting (a recent timeline.prisma comment existed in
  packages/db but not here)
- prisma.config.ts + postinstall + db:generate + db:getschema all
  existed to sync and generate from the local duplicated schema

The app never imports @trycompai/db directly — it uses the @db
alias (351 call sites) which maps to ./prisma/{index,server}.ts.
Those files now just re-export from @prisma/client (populated by
packages/db's build) so the local schema and generation are dead code.

Dropped:
- apps/app/prisma.config.ts
- apps/app/prisma/schema/ (45 files)
- apps/app/prisma/src/ (previously-generated tree)
- package.json: build:docker prisma-generate prefix, db:generate,
  db:getschema, prebuild, postinstall
- .gitignore: stale prisma/generated + prisma/schema/*.prisma rules

Kept: apps/app/prisma/{client,index,server}.ts — these wire up the
app's PrismaClient instance (with the PG adapter) and expose it via
the @db alias. packages/db doesn't export a ready-made db instance,
so these three thin files stay for now.

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

* fix(db,docker): restore Prisma client generation after app prisma cleanup

When apps/app lost its own postinstall + build:docker prisma-generate
step, nothing was left to populate @prisma/client. Cubic was right that
'next build' would fail — the deps stage already uses --ignore-scripts
and PRISMA_SKIP_POSTINSTALL_GENERATE=true, so neither the app nor
packages/db generated anything.

Fix in two places:

- packages/db/package.json: add a postinstall that runs
  generate-prisma-client-js. Covers local dev — 'bun install' now
  populates node_modules/@prisma/client. '|| true' mirrors the app's
  old pattern so installs in envs without DATABASE_URL don't fail.
- Dockerfile app-builder stage: after combine-schemas, explicitly run
  generate-prisma-client-js so the Docker build gets the client too
  (the deps stage skipped it intentionally).

Co-Authored-By: Claude Opus 4.7 (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>
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

2 issues found across 17 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/app/src/app/(app)/[orgId]/people/devices/lib/devices-csv.test.ts">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/devices/lib/devices-csv.test.ts:137">
P2: The formula-injection test suite is incomplete: it does not cover leading tab (`\t`) and carriage return (`\r`) cells, so regressions for those spreadsheet-formula triggers can slip through.

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

<file name="packages/utils/src/devices.ts">

<violation number="1" location="packages/utils/src/devices.ts:56">
P2: Clamp computed days to 0 so future timestamps don't return negative "days since" values.</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.

expect(lines[2].startsWith('Beta,')).toBe(true);
});

describe('formula injection neutralization', () => {
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P2: The formula-injection test suite is incomplete: it does not cover leading tab (\t) and carriage return (\r) cells, so regressions for those spreadsheet-formula triggers can slip through.

(Based on your team's feedback about CSV formula injection triggers.)

View Feedback

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/devices/lib/devices-csv.test.ts, line 137:

<comment>The formula-injection test suite is incomplete: it does not cover leading tab (`\t`) and carriage return (`\r`) cells, so regressions for those spreadsheet-formula triggers can slip through.

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

<file context>
@@ -0,0 +1,284 @@
+    expect(lines[2].startsWith('Beta,')).toBe(true);
+  });
+
+  describe('formula injection neutralization', () => {
+    function firstCell(csv: string): string {
+      const body = stripBom(csv);
</file context>
Fix with Cubic

if (lastCheckIn === null) return null;
const then = toDate(lastCheckIn).getTime();
if (Number.isNaN(then)) return null;
return Math.floor((Date.now() - then) / MS_PER_DAY);
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P2: Clamp computed days to 0 so future timestamps don't return negative "days since" values.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/utils/src/devices.ts, line 56:

<comment>Clamp computed days to 0 so future timestamps don't return negative "days since" values.</comment>

<file context>
@@ -33,3 +33,50 @@ export function mergeDeviceLists<T>(
+  if (lastCheckIn === null) return null;
+  const then = toDate(lastCheckIn).getTime();
+  if (Number.isNaN(then)) return null;
+  return Math.floor((Date.now() - then) / MS_PER_DAY);
+}
+
</file context>
Suggested change
return Math.floor((Date.now() - then) / MS_PER_DAY);
return Math.max(0, Math.floor((Date.now() - then) / MS_PER_DAY));
Fix with Cubic

tofikwest and others added 3 commits April 20, 2026 16:36
Only strip actual comments (# preceded by whitespace or at line start)
so VCS requirements like `git+https://...#egg=pydantic` remain matchable.
Also add `=` to the leading separator set so the package name inside
`#egg=<name>` is recognized as a standalone token.

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

The previous strip-on-whitespace-# regex was requirements.txt-specific and
left inline TOML comments intact when `#` had no preceding whitespace (e.g.
`["requests"]# pydantic`), causing false-positive library matches.

Dispatch on file name:
  - pyproject.toml (TOML): strip `#.*$` anywhere — `#` is a comment outside
    strings, and dep values are always quoted so VCS names still match via
    the preceding `"name @ ...` prefix.
  - requirements.txt (pip): keep `(^|\s)#.*$` — `#` only starts a comment
    when preceded by whitespace, so VCS `#egg=name` fragments survive.

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

feat(integration-platform): expand validation library detection for sanitized inputs
tofikwest and others added 4 commits April 20, 2026 17:14
Adds a public, manually-synced catalog of all 540 CompAI integrations at
integrations-catalog/. Each vendor gets integrations-catalog/integrations/<slug>.json
with sanitized public metadata: vendor info, auth type, customer-facing setup
instructions, credential field labels, check names + descriptions + severity,
sync support flag.

Implementation details intentionally excluded: check DSL (endpoint paths,
request bodies, response parsing, aggregation), syncDefinition, internal DB
IDs, credential field placeholders, logoUrl (to avoid exposing logo.dev
publishable tokens).

- scripts/sync.mjs: fetches /v1/internal/dynamic-integrations, dedups by slug,
  runs a secret-pattern scanner over every sanitized output and BLOCKS any
  file containing Stripe pk/sk, GitHub tokens, AWS AKIA, Anthropic keys, Jina
  keys, Slack tokens, JWTs, or Bearer tokens. Atomic-writes only when content
  hash differs. Stale cleanup only removes files whose slug is not in the
  authoritative list; preserves files whose fetch failed.
- scripts/generate-readme.mjs: regenerates README.md with full alphabetical
  vendor table per category (SEO-friendly).

Catalog is refreshed manually on demand rather than on a schedule — no cron,
no GitHub Action. To refresh, run both scripts locally and push a PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Relocates catalog regeneration tooling from integrations-catalog/scripts
to tools/integrations-catalog-sync/ for cleaner separation between
consumer-facing catalog data and internal dev tooling.

Also hardens the sync script:
- Removes hardcoded fallback for COMPAI_INTERNAL_API_BASE — the env var
  is now strictly required. No internal endpoint leaks in source.
- Adds tools/integrations-catalog-sync/README.md documenting env var
  contract, usage, safety guardrails, and the full list of stripped fields.

No change to published catalog data — same 540 files, same sanitize rules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds SYNC_MIN_INTERVAL_MS (default 100ms) to enforce a minimum gap
between HTTP requests across all workers. Paces the sync to stay under
the CompAI internal API throttle, which was triggering 429s at higher
concurrency.

Also logs backoff delays when retries fire for visibility.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat(integrations-catalog): add public catalog (540 integrations)
@vercel vercel Bot temporarily deployed to staging – portal April 20, 2026 22:01 Inactive
@Marfuen Marfuen merged commit 4c47990 into release Apr 20, 2026
13 of 14 checks passed
@claudfuen
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.27.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.

3 participants