Conversation
Adds `Organization.isInternal` so platform-operated organizations can run compliance with their platform-admin staff as real participants — assignable, counted toward compliance progress, and included in notifications and device rollups. Behavior is unchanged for every other organization (flag defaults to false; admin-only toggle on the org admin screen). Centralizes the rule behind a single `isOrgParticipant` predicate (`@trycompai/auth/participation`, mirrored for the Trigger.dev bundle), replacing the scattered inline `user.role === 'admin'` participation checks across assignment/approval, compliance counting, notifications, reminders, and device queries. Platform-admin access guards are intentionally untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UWG1YQ8KsjefFAvRV7pdtb
…idation, keep dep boundary - run-browser-automation: use the shared `orgParticipantMemberWhere` helper instead of an inline copy, so it matches the other task notifiers (uses the role constant + owner exception) and still includes everyone in internal orgs. - update-admin-organization DTO: reject an explicit `null` isInternal at validation (400) instead of letting it reach the non-null column (500). - org-participation (app): stop re-exporting `isOrgParticipant` from the module that imports `@db/server`; consumers import it from the dependency-free `org-participation-rule` so server-only code can't leak into client/Trigger bundles. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UWG1YQ8KsjefFAvRV7pdtb
…rule, guard boundaries - generate-vendor-mitigation: fail closed — only assign the author when the member actually exists in this org (add `author &&` guard) so a missed lookup can never write an unknown/cross-org member id. - SelectAssignee: use the shared `isOrgParticipant` predicate instead of an inline copy, so the picker can't drift from the backend rule. - lib/org-participation.ts: add `import 'server-only'` (matches compliance.ts / permissions.server.ts) so this DB-backed helper can't be pulled into a client bundle. - Add a drift guard test asserting the app-local participation rule stays identical to `@trycompai/auth/participation`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UWG1YQ8KsjefFAvRV7pdtb
…r, skip needless query - orgParticipantMemberWhere: make it a faithful Prisma translation of isOrgParticipant — for non-internal orgs exclude only platform admins (include null roles; drop the owner carve-out, which the isInternal flag now supersedes). Removes the split source of truth between the predicate and the query fragment. - generate-vendor-mitigation: add `deactivated: false` to the author lookup so a deactivated member can't be auto-assigned (matches the other trigger tasks). - filterComplianceMembers (app + api): skip the `getOrgIsInternal` query when the member list has no platform admins — the flag can't change the result there. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UWG1YQ8KsjefFAvRV7pdtb
…ternal toggle
- org-participation: return the participant where-fragment wrapped in `AND`
(via new `orgParticipantMemberWhereForFlag`) so spreading it no longer
overwrites a caller's existing `user` filter. The two mention notifiers
constrain `user: { id: { in } }`, which the bare `user` key clobbered, so
notifications reached every non-admin member instead of the mentioned ones.
Adds a spread-safety unit test.
- OrganizationDetail: refresh the server layout (router.refresh) after toggling
isInternal for the current org so OrgInternalProvider isn't stale, and require
a confirmation dialog before changing this org-wide setting.
- run-browser-automation: select `isInternal` in the existing org query and use
the pure where-builder, dropping the redundant getOrgIsInternal query.
- Normalize participation onto isOrgParticipant everywhere: drop the per-member
owner carve-out from weekly-task-reminder, task-schedule, policy-schedule and
the task-item assignment notifier (matches the API where-fragment and the
predicate); remove now-dead role selections and stale comments.
- tests: add a PointerEvent shim to the vitest setup (jsdom lacks it, base-ui
Switch needs it), fix admin-org test mocks missing the new isInternal field,
and cover the internal-toggle confirmation flow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XzEVwhhoFghng6SDMGecMq
…rticipation # Conflicts: # apps/app/src/app/api/people/agent-devices/route.ts
The assignee guards only threw when a member was found in the org and failed the
participant check. When the org-scoped `member.findFirst` returned null — the id
belongs to another org or doesn't exist — the guard was skipped and the id was
persisted, since assignee columns don't enforce same-org membership. This gap is
pre-existing (the prior `member?.user.role === 'admin'` check had the same null
hole); hardened here while the participation refactor already touches these
guards. Every assignee guard now rejects a non-member with a clear 400.
Covers tasks (bulk + single update), task items (create + update), policies
(create), vendors, and risks. Approver guards already rejected null members.
Also consolidate task-notifier's automation-failure paths, which fetched the org
twice (once for isInternal via orgParticipantMemberWhere, once for name), into a
single `{ name, isInternal }` lookup + orgParticipantMemberWhereForFlag.
Adds a regression test for the cross-org assignee rejection.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XzEVwhhoFghng6SDMGecMq
feat(org): treat platform admins as members in internal organizations
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
Contributor
There was a problem hiding this comment.
2 issues found across 45 files
Confidence score: 3/5
- In
apps/app/src/app/(app)/[orgId]/admin/organizations/[adminOrgId]/components/OrganizationDetail.tsx, the update path hits an API route gated by a hardcoded globaladminrole, so custom roles can be incorrectly blocked from a sensitive setting; merging as-is risks breaking delegated admin workflows for orgs using custom RBAC. Route this mutation through the existing API permission guard before merging. - In
apps/api/src/tasks/task-notifier.service.ts, the new internal-org participant filter lacks regression tests, so future changes toorgParticipantMemberWhereForFlagcould silently misroute or suppress automation-failure notifications. Add internal-organization and customer-organization coverage to de-risk this behavior.
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/tasks/task-notifier.service.ts">
<violation number="1" location="apps/api/src/tasks/task-notifier.service.ts:1093">
P3: Automation-failure notifications have no regression coverage for the new internal-org participant filter. Add cases for internal and customer organizations so a change to `orgParticipantMemberWhereForFlag` cannot silently route notifications to the wrong recipients.</violation>
</file>
<file name="apps/app/src/app/(app)/[orgId]/admin/organizations/[adminOrgId]/components/OrganizationDetail.tsx">
<violation number="1" location="apps/app/src/app/(app)/[orgId]/admin/organizations/[adminOrgId]/components/OrganizationDetail.tsx:58">
P2: Custom roles cannot be granted access to this sensitive organization setting because this mutation reaches an API route gated by the hardcoded global `admin` role. Route this update through the API permission guard with an explicit RBAC permission instead of `PlatformAdminGuard`'s role-string check.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
…8) (#3391) * feat(isms): add Roles, Responsibilities & Authorities document (CS-698) Add a new ISMS > Roles sub-page (ISO 27001 Clause 5.3): four seeded governance roles (Top Management, SPO, Deputy SPO, Internal Auditor) with pre-filled text, member assignments with per-member competence evidence (Clause 7.2), an Internal Auditor route picker with an independence soft-warning, 1-3 / 4+ team-size bands, and generate-time validation. The Clause 5.3 document renders to branded PDF + DOCX through the existing ISMS export pipeline, including the two auto-generated governance rows and an operational-responsibilities summary read from per-artifact owners. Mirrors the existing ISMS register pattern; reuses the evidence permission resource and the is-isms-enabled flag gate. Role seeding is idempotent (never destructive) so member assignments survive a regenerate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015bx4urA8n2E6zzBAjEuxwR * fix(isms): address CS-698 review — validation, concurrency, UI hardening - Enforce Clause 5.3 completeness server-side on submit-for-approval (not just the client), so an incomplete Roles doc can't be published by calling the API directly. Validation now iterates the required seeded roles, catching an entirely-missing row, not only present-but-unassigned ones (client + server). - Add a unique (documentId, roleKey) index + skipDuplicates so concurrent first-load provisioning can't double-seed governance roles. - Make the role-assignment idempotency check run inside the per-document lock, so concurrent duplicate adds return the existing row instead of hitting the unique index. - Extract a shared parseOptionalDate util (was duplicated in both role services). - Neutralise the small-team note so it no longer asserts an external audit route that can contradict the customer's chosen route. - Audit-route member pickers: drop the free-text fallback (a member id is required); disable the select when no members exist. - Swallow already-handled mutation rejections in the add-member and delete-role handlers to avoid unhandled promise rejections. - Fix stale "all six ISMS documents" JSDoc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015bx4urA8n2E6zzBAjEuxwR * fix(isms): harden roles submit gate + strict date parsing (CS-698 review 2) - Server-side clause-5.3 gate now counts only assignments that resolve to an active member, so a required role assigned solely to a deactivated/removed member no longer passes submit-for-approval. - parseOptionalDate now validates a strict YYYY-MM-DD format and round-trips the parsed value, rejecting ambiguous/rolled-over input (e.g. 2026-02-30) instead of relying on permissive Date parsing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015bx4urA8n2E6zzBAjEuxwR * fix(isms): close CS-698 review round 3 — submit race, active-member + route validation, doc-type scoping - Serialize submit-for-approval: validate completeness and flip status inside one transaction under the per-document lock, and take that same lock on role/ assignment update+remove, so an edit can't invalidate the doc between the check and the needs_review transition (TOCTOU). - Enforce active (non-deactivated) members everywhere: role audit-route member, assignment member, and the completeness gate count only active-member assignments; the client validation mirrors it via the active-member set so the Submit button and the server agree. - Route-specific Internal Auditor validation (client + server): external requires firm/person + evidence reference; in-house requires a member; training-planned requires member + course + due date. - Scope role + assignment register lookups to roles_and_responsibilities documents so role rows can't attach to unrelated ISMS documents. - Generated governance table renders only the four seeded roles + two auto rows (custom roles are managed on the page but not part of the standardized 5.3 table). - AuditRoutePicker: swallow the already-handled save rejection (no unhandled promise rejection). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015bx4urA8n2E6zzBAjEuxwR * fix(isms): audit-route validation counts active members + trims required text (CS-698 review 4) - The Internal Auditor's in-house/training-planned member must now resolve to an ACTIVE member (same active-member set used for assignments), on both the client gate and the server completeness check — a deactivated auditor no longer passes. - Required audit text is trimmed before the check: external firm/person + evidence and the training-planned course can't be satisfied by whitespace via the API. - Unify the active-member logic in roleValidationMessages (assignments + audit member) so client and server agree. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015bx4urA8n2E6zzBAjEuxwR --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ation (#3393) The API's Trigger.dev deploy bundles task notifiers with esbuild, which failed with "Could not resolve @trycompai/auth/participation". The auth package's dist is not built in the trigger deploy workflow (it builds db/email/integration- platform only), and the API's org-participation code — reachable from the task notifiers — imported that subpath, so esbuild had nothing to resolve. Mirror the app's approach for Trigger.dev-bundled code: use a dependency-free local copy of the tiny participation predicate (`org-participation-rule.ts`) instead of importing `@trycompai/auth`. This keeps the trigger bundle free of the auth package (which also pulls better-auth, unbundleable). A drift-guard spec asserts the mirror stays identical to `@trycompai/auth/participation`, and a jest moduleNameMapper resolves that subpath to source so the guard runs without needing the auth dist built. Claude-Session: https://claude.ai/code/session_01XzEVwhhoFghng6SDMGecMq Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e notifiers (#3394) Adds regression cases for `notifyAutomationFailures` and `notifyBulkAutomationFailures` asserting the recipient `member.findMany` query applies `orgParticipantMemberWhereForFlag`: platform admins are excluded for customer orgs (isInternal false) and included for internal orgs (isInternal true). Guards against a future change to the participation helper silently routing automation-failure notifications to the wrong recipients (cubic P3 on the release PR #3392). Claude-Session: https://claude.ai/code/session_01XzEVwhhoFghng6SDMGecMq Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
|
🎉 This PR is included in version 3.101.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 an internal-organization flag so platform admins are excluded in customer orgs but included as real members in internal orgs, and introduces a new ISMS “Roles, Responsibilities & Authorities” document (ISO 27001 5.3) with seeded roles, member assignments, audit-route selection, and PDF/DOCX exports. Applies the participation rule consistently across assignments, approvers, notifications, compliance, device views, and Trigger jobs, and gates Roles submission with server/client validation.
New Features
Organization.isInternal(default false) with admin toggle + confirmation; single rule viaisOrgParticipantin@trycompai/auth/participationwith dependency‑free mirrors for Trigger bundles (API + app) used across API/services, schedules/notifiers/device queries, andSelectAssignee; helpersorgParticipantMemberWhere/orgParticipantMemberWhereForFlagandgetOrgIsInternal.roles_and_responsibilitiestype with four seeded governance roles, custom roles, member assignments with clause‑7.2 competence, Internal Auditor route (in_house/external/training_planned) with validation, team‑size band (1–3 vs 4+), idempotent seeding and submit‑for‑approval gate; new services (IsmsRoleService,IsmsRoleAssignmentService), UI page/components, and export integration (PDF/DOCX). DB addsIsmsRole/IsmsRoleAssignment, enums (IsmsCompetenceBasis,IsmsAuditRoute), and a unique(documentId, roleKey)index; wizard generation order updated.Bug Fixes
@trycompai/auth/participationimports in API notifiers with a local, dependency‑free mirror; add a drift‑guard spec and Jest mapper for@trycompai/auth/participationto source to keep tests green.YYYY‑MM‑DDparsing for date inputs; serialize Roles submit to avoid races; scope Roles lookups to the new doc type; add PointerEvent test shim and new unit tests.Written for commit f37aa34. Summary will update on new commits.