[dev] [Marfuen] mariano/cs-312-feature-add-personnel-employment-events-date-tracking#2880
Conversation
…ent types Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tering Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a new "Employment Evidence" tab to the employee detail page that allows uploading/downloading/deleting onboarding and offboarding evidence documents. The tab appears between Device and Background Check, and evidence sections are conditionally shown based on whether onboardDate or offboardDate is set on the member. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds EmploymentEvidence and EmploymentEvidenceSection components and wires a new "Employment Evidence" tab into Employee.tsx between Device and Background Check. Evidence sections appear based on onboardDate and offboardDate values. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…auto-set offboardDate on deactivation Onboarded column falls back to member.createdAt when no explicit date is set. Offboarded column is blank for active members. Deactivating a member now auto-sets offboardDate if not already set. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes calendar squished layout, missing hover/selection states, and Button className override bug. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
….17) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ompletion tracking Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ints Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Evidence-required items expand to show a drag-and-drop zone instead of silently opening a file picker on checkbox click. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… inside card The wrapper div between Collapsible and CollapsibleTrigger caused base-ui to render the panel as a sibling outside the border. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shows when evidence files exist. Opens all evidence download URLs for auditors to collect offboarding proof. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ders and CSVs Adds a new GET endpoint that generates a zip file containing all offboarding evidence for a member, organized for auditor review. The zip includes a summary CSV, vendor access revocations CSV with evidence files, and numbered checklist item folders with their evidence. The frontend Export button now downloads this zip via a Next.js proxy route instead of opening individual files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…iant when evidence exists Shows as primary/filled when evidence exists, disabled outline when no evidence yet. Always visible so auditors can find it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New API endpoint GET /v1/offboarding-checklist/export-all returns a zip with a subfolder per offboarded employee containing their checklist summary CSV, vendor revocations CSV, and all evidence files - Menu button next to Add User with "Export all offboarding data" option - Next.js proxy route supports ?all=true for the bulk export Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Single pending member links directly to their offboarding tab. Multiple links to the people page where Quick Actions shows the list. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
onboardDate should only show a value when explicitly set — createdAt reflects when they joined Comp AI, not when they joined the company. Table shows "—" for members without an onboard date. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Added startDate field to SyncEmployeeSchema - Google Workspace: maps creationTime → onboardDate - Rippling: maps start_date → onboardDate - JumpCloud: maps created → onboardDate - GenericEmployeeSyncService: passes startDate → onboardDate on member creation - Rebuilt integration-platform package Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a sync encounters an existing member with no onboardDate and the provider has a date, it now updates the member. This means the next sync after deployment will populate onboardDate for all existing members across Google Workspace, Rippling, JumpCloud, and dynamic integrations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ent-events-date-tracking
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rt route 1. handleFileUpload now checks isProcessing before starting, preventing duplicate uploads from rapid interactions 2. Export proxy route wraps fetch in try/catch, returns 502 on network failure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ctly Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lassName Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ent-events-date-tracking
…downMenuTrigger DS components don't accept these props. Move styling to inner wrapper divs instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…igger Base-ui's Menu.Trigger renders a <button>, so wrapping a <Button> inside it creates invalid nested buttons. Use the render prop to replace the trigger element directly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DS PopoverContent already renders bg, shadow, ring, and rounded-lg. Extra wrapper divs with duplicate styling created a visible double-card effect. Removed wrappers and let the DS popup handle the chrome. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
cubic analysis
23 issues found across 55 files
Confidence score: 2/5
- There is a high-risk authorization gap in
apps/api/src/offboarding-checklist/offboarding-checklist.controller.ts: revoke/undo flows can operate onmemberIdvalues outside the currentorganizationId, which creates cross-tenant write risk. apps/api/src/people/people.controller.tshas concrete data-integrity risks (unscoped deletion ownership checks and unvalidated date query params) that can lead to unintended deletions or runtime errors.- Auditor-facing evidence retention appears fragile in
packages/db/prisma/schema/offboarding-checklist.prisma(revokedBy/completedBydelete behavior), andapps/api/src/offboarding-checklist/offboarding-export.service.tsstill needs CSV formula-injection hardening; together these are meaningful compliance and security concerns. - Pay close attention to
apps/api/src/offboarding-checklist/offboarding-checklist.controller.ts,apps/api/src/people/people.controller.ts,packages/db/prisma/schema/offboarding-checklist.prisma, andapps/api/src/offboarding-checklist/offboarding-export.service.ts- tenant scoping, deletion safety, evidence retention, and export sanitization are the key merge risks.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/Employee.tsx">
<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/Employee.tsx:77">
P2: Tab query validation does not respect feature/data-gated tabs, so some `?tab=` values can select a tab that is not rendered and leave the page in a blank tab state.</violation>
</file>
<file name="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/OffboardingChecklist.tsx">
<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/OffboardingChecklist.tsx:73">
P2: Use `noopener`/`noreferrer` when opening download URLs in a new tab to prevent reverse-tabnabbing via `window.opener`.</violation>
</file>
<file name="apps/app/src/hooks/use-offboarding-checklist.ts">
<violation number="1" location="apps/app/src/hooks/use-offboarding-checklist.ts:133">
P3: This adds duplicate file-to-base64 conversion logic that already exists in other hooks; extracting a shared helper would prevent drift and keep file-upload behavior consistent.</violation>
</file>
<file name="apps/app/src/app/(app)/[orgId]/overview/components/TodosOverview.tsx">
<violation number="1" location="apps/app/src/app/(app)/[orgId]/overview/components/TodosOverview.tsx:47">
P2: Failed API requests are rendered as a success empty state (`All clear — no pending items`), which can hide real pending offboarding work.</violation>
</file>
<file name="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/EmployeeDetails.tsx">
<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/EmployeeDetails.tsx:73">
P2: Validate date order before saving so offboard dates cannot be earlier than onboard dates; otherwise invalid employment-event timelines can be persisted and break date-based review filtering.</violation>
</file>
<file name="apps/api/src/people/people.controller.ts">
<violation number="1" location="apps/api/src/people/people.controller.ts:121">
P2: Validate date query params before building filters; invalid date strings currently propagate to Prisma and can cause server errors.</violation>
<violation number="2" location="apps/api/src/people/people.controller.ts:613">
P1: Deletion is not scoped to the requested member/event type; verify attachment ownership before deleting.</violation>
</file>
<file name="apps/app/src/app/(app)/[orgId]/people/all/components/TeamMembersClient.tsx">
<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/all/components/TeamMembersClient.tsx:151">
P2: Pending invitation rows are incorrectly kept when date filters are active, so onboarding/offboarding filters still show irrelevant non-member records.</violation>
<violation number="2" location="apps/app/src/app/(app)/[orgId]/people/all/components/TeamMembersClient.tsx:579">
P2: Preset date ranges use a time-of-day `from` boundary, which can incorrectly exclude records on the starting day.</violation>
</file>
<file name="apps/api/src/offboarding-checklist/offboarding-checklist.service.ts">
<violation number="1" location="apps/api/src/offboarding-checklist/offboarding-checklist.service.ts:337">
P2: Pending-offboarding detection can be incorrect because it counts completions for disabled checklist items when comparing against enabled template count.</violation>
</file>
<file name="packages/db/prisma/schema/offboarding-checklist.prisma">
<violation number="1" location="packages/db/prisma/schema/offboarding-checklist.prisma:33">
P1: Use `SetNull` (with an optional FK) for `completedBy` so deleting a user does not delete offboarding completion evidence.
According to linked Linear issue CS-312, these records are part of auditor-facing employment-event evidence and should be retained.</violation>
<violation number="2" location="packages/db/prisma/schema/offboarding-checklist.prisma:52">
P1: Do not cascade-delete access revocation evidence when a user is removed; make `revokedBy` nullable and use `onDelete: SetNull`.
According to linked Linear issue CS-312, termination evidence must remain available for auditor review.</violation>
</file>
<file name="apps/api/src/offboarding-checklist/offboarding-checklist.controller.ts">
<violation number="1" location="apps/api/src/offboarding-checklist/offboarding-checklist.controller.ts:253">
P2: Reject partial evidence payloads instead of silently discarding them; otherwise revocation can succeed while intended supporting evidence is lost.
According to linked Linear issue CS-312, supporting evidence capture is a core requirement for these employment events.</violation>
<violation number="2" location="apps/api/src/offboarding-checklist/offboarding-checklist.controller.ts:256">
P1: Validate that `memberId` belongs to `organizationId` before revocation writes; current flow allows cross-tenant member IDs to be used in revoke/undo operations.</violation>
</file>
<file name="apps/api/src/integration-platform/services/generic-employee-sync.service.ts">
<violation number="1" location="apps/api/src/integration-platform/services/generic-employee-sync.service.ts:198">
P2: Validate `employee.startDate` before converting it to `Date`; malformed provider values can make sync writes fail.</violation>
</file>
<file name="apps/api/src/people/utils/member-queries.ts">
<violation number="1" location="apps/api/src/people/utils/member-queries.ts:26">
P2: The response DTO is not updated for the newly selected `onboardDate`/`offboardDate` fields, so API typing and generated schema remain out of sync with actual returned data.</violation>
<violation number="2" location="apps/api/src/people/utils/member-queries.ts:73">
P2: The new date-range filters accept unvalidated `Date` objects; invalid query dates can flow into Prisma and fail at runtime instead of being rejected as bad request input.</violation>
</file>
<file name="apps/api/src/offboarding-checklist/offboarding-export.service.ts">
<violation number="1" location="apps/api/src/offboarding-checklist/offboarding-export.service.ts:115">
P2: Attachment filenames are inserted into ZIP paths without sanitization, which can create unsafe/nested archive entries and filename collisions.</violation>
<violation number="2" location="apps/api/src/offboarding-checklist/offboarding-export.service.ts:166">
P2: `exportAllOffboardings` uses only `safeName` for folder paths, so members with identical names can overwrite each other’s exported evidence.</violation>
<violation number="3" location="apps/api/src/offboarding-checklist/offboarding-export.service.ts:196">
P2: Attachment download errors are swallowed, causing silent data loss in exported evidence bundles.</violation>
<violation number="4" location="apps/api/src/offboarding-checklist/offboarding-export.service.ts:203">
P1: CSV export is vulnerable to formula injection because fields are quote-escaped but not formula-sanitized.
(Based on your team's feedback about CSV formula injection hardening.) [FEEDBACK_USED]</violation>
</file>
<file name="apps/api/src/offboarding-checklist/access-revocation.service.ts">
<violation number="1" location="apps/api/src/offboarding-checklist/access-revocation.service.ts:143">
P2: Undoing a revocation should delete its uploaded evidence to avoid orphaned attachment records/files.</violation>
<violation number="2" location="apps/api/src/offboarding-checklist/access-revocation.service.ts:204">
P2: Use direct count queries in completion sync instead of calling `getAccessRevocations`, which does expensive attachment loading.</violation>
</file>
Linked issue analysis
Linked issue: CS-312: [Feature] Add personnel Employment events date tracking + supporting evidence upload section
| Status | Acceptance criteria | Notes |
|---|---|---|
| ✅ | Add optional onboardDate and offboardDate fields to Member (DB + API) | Database schema and migration add the columns and attachment enum values; API DTOs and prisma schema updated to expose the fields. |
| ✅ | Treat blank values for imported employees as pre-existing (avoid mandatory backfill) | Sync/import logic only writes onboardDate when not already set and when a source date is available, leaving blanks for pre-existing/imported users. |
| ✅ | Allow filtering/sorting of employees by onboard/offboard date (backend + API) | Member queries and PeopleService accept onboard/offboard filter parameters and openapi includes the query params, enabling backend filtering support. |
| ✅ | Set offboardDate when a member is deactivated | PeopleService updates offboardDate when marking a member deactivated, so offboard events are recorded automatically on deactivation. |
| ✅ | Add evidence upload endpoints and attachment types for employment events | New attachment enum values and client/server hooks and DTOs support uploading employment evidence for onboard/offboard events. |
| ✅ | Add an offboarding checklist / evidence UI tied to employment events and access revocation tracking | A full offboarding-checklist module was added (controller/service/module), many frontend components/hooks for checklist items, access revocation lists, summary card, settings, and export are present. |
| ✅ | Provide export capability for auditor review of member evidence | Services and an API route exist to assemble and stream an archive of offboarding evidence for a member or all members. |
| ✅ | Surface pending offboarding items / prompt admins to complete evidence (auditor-friendly UI) | UI changes include an OffboardingBanner, ToDo/Todos overview additions, and ToDo tabs that surface pending offboarding items to admins. |
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
| this.resolveEventType(eventType); | ||
| await this.peopleService.findById(memberId, organizationId); | ||
| await this.attachmentsService.deleteAttachment(organizationId, attachmentId); |
There was a problem hiding this comment.
P1: Deletion is not scoped to the requested member/event type; verify attachment ownership before deleting.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/people/people.controller.ts, line 613:
<comment>Deletion is not scoped to the requested member/event type; verify attachment ownership before deleting.</comment>
<file context>
@@ -529,6 +556,66 @@ export class PeopleController {
+ @Param('attachmentId') attachmentId: string,
+ @OrganizationId() organizationId: string,
+ ) {
+ this.resolveEventType(eventType);
+ await this.peopleService.findById(memberId, organizationId);
+ await this.attachmentsService.deleteAttachment(organizationId, attachmentId);
</file context>
| this.resolveEventType(eventType); | |
| await this.peopleService.findById(memberId, organizationId); | |
| await this.attachmentsService.deleteAttachment(organizationId, attachmentId); | |
| const entityType = this.resolveEventType(eventType); | |
| await this.peopleService.findById(memberId, organizationId); | |
| const attachments = await this.attachmentsService.getAttachments( | |
| organizationId, | |
| memberId, | |
| entityType, | |
| ); | |
| if (!attachments.some((attachment) => attachment.id === attachmentId)) { | |
| throw new BadRequestException('Attachment not found for this employment event'); | |
| } | |
| await this.attachmentsService.deleteAttachment(organizationId, attachmentId); |
| organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) | ||
| member Member @relation(fields: [memberId], references: [id], onDelete: Cascade) | ||
| vendor Vendor @relation(fields: [vendorId], references: [id], onDelete: Cascade) | ||
| revokedBy User @relation("AccessRevocationRevokedBy", fields: [revokedById], references: [id], onDelete: Cascade) |
There was a problem hiding this comment.
P1: Do not cascade-delete access revocation evidence when a user is removed; make revokedBy nullable and use onDelete: SetNull.
According to linked Linear issue CS-312, termination evidence must remain available for auditor review.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/db/prisma/schema/offboarding-checklist.prisma, line 52:
<comment>Do not cascade-delete access revocation evidence when a user is removed; make `revokedBy` nullable and use `onDelete: SetNull`.
According to linked Linear issue CS-312, termination evidence must remain available for auditor review.</comment>
<file context>
@@ -0,0 +1,57 @@
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
+ member Member @relation(fields: [memberId], references: [id], onDelete: Cascade)
+ vendor Vendor @relation(fields: [vendorId], references: [id], onDelete: Cascade)
+ revokedBy User @relation("AccessRevocationRevokedBy", fields: [revokedById], references: [id], onDelete: Cascade)
+
+ @@unique([memberId, vendorId])
</file context>
| organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) | ||
| member Member @relation(fields: [memberId], references: [id], onDelete: Cascade) | ||
| templateItem OffboardingChecklistTemplate @relation(fields: [templateItemId], references: [id], onDelete: Cascade) | ||
| completedBy User @relation("OffboardingChecklistCompletedBy", fields: [completedById], references: [id], onDelete: Cascade) |
There was a problem hiding this comment.
P1: Use SetNull (with an optional FK) for completedBy so deleting a user does not delete offboarding completion evidence.
According to linked Linear issue CS-312, these records are part of auditor-facing employment-event evidence and should be retained.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/db/prisma/schema/offboarding-checklist.prisma, line 33:
<comment>Use `SetNull` (with an optional FK) for `completedBy` so deleting a user does not delete offboarding completion evidence.
According to linked Linear issue CS-312, these records are part of auditor-facing employment-event evidence and should be retained.</comment>
<file context>
@@ -0,0 +1,57 @@
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
+ member Member @relation(fields: [memberId], references: [id], onDelete: Cascade)
+ templateItem OffboardingChecklistTemplate @relation(fields: [templateItemId], references: [id], onDelete: Cascade)
+ completedBy User @relation("OffboardingChecklistCompletedBy", fields: [completedById], references: [id], onDelete: Cascade)
+
+ @@unique([memberId, templateItemId])
</file context>
| const evidence = body?.fileName && body?.fileType && body?.fileData | ||
| ? { fileName: body.fileName, fileType: body.fileType, fileData: body.fileData } | ||
| : undefined; | ||
| return this.offboardingChecklistService.revokeVendorAccess({ |
There was a problem hiding this comment.
P1: Validate that memberId belongs to organizationId before revocation writes; current flow allows cross-tenant member IDs to be used in revoke/undo operations.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/offboarding-checklist/offboarding-checklist.controller.ts, line 256:
<comment>Validate that `memberId` belongs to `organizationId` before revocation writes; current flow allows cross-tenant member IDs to be used in revoke/undo operations.</comment>
<file context>
@@ -0,0 +1,283 @@
+ const evidence = body?.fileName && body?.fileType && body?.fileData
+ ? { fileName: body.fileName, fileType: body.fileType, fileData: body.fileData }
+ : undefined;
+ return this.offboardingChecklistService.revokeVendorAccess({
+ organizationId,
+ memberId,
</file context>
| } | ||
|
|
||
| function escapeCsvField(value: string): string { | ||
| return value.replace(/"/g, '""'); |
There was a problem hiding this comment.
P1: CSV export is vulnerable to formula injection because fields are quote-escaped but not formula-sanitized.
(Based on your team's feedback about CSV formula injection hardening.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/offboarding-checklist/offboarding-export.service.ts, line 203:
<comment>CSV export is vulnerable to formula injection because fields are quote-escaped but not formula-sanitized.
(Based on your team's feedback about CSV formula injection hardening.) </comment>
<file context>
@@ -0,0 +1,204 @@
+}
+
+function escapeCsvField(value: string): string {
+ return value.replace(/"/g, '""');
+}
</file context>
| const buffer = await this.getAttachmentBuffer(organizationId, file.id); | ||
| if (!buffer) continue; | ||
| archive.append(buffer, { | ||
| name: `${prefix}vendor-access-revocations/evidence/${file.name}`, |
There was a problem hiding this comment.
P2: Attachment filenames are inserted into ZIP paths without sanitization, which can create unsafe/nested archive entries and filename collisions.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/offboarding-checklist/offboarding-export.service.ts, line 115:
<comment>Attachment filenames are inserted into ZIP paths without sanitization, which can create unsafe/nested archive entries and filename collisions.</comment>
<file context>
@@ -0,0 +1,204 @@
+ const buffer = await this.getAttachmentBuffer(organizationId, file.id);
+ if (!buffer) continue;
+ archive.append(buffer, {
+ name: `${prefix}vendor-access-revocations/evidence/${file.name}`,
+ });
+ }
</file context>
| }); | ||
| if (!attachment) return null; | ||
| return await this.attachmentsService.getObjectBuffer(attachment.url); | ||
| } catch { |
There was a problem hiding this comment.
P2: Attachment download errors are swallowed, causing silent data loss in exported evidence bundles.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/offboarding-checklist/offboarding-export.service.ts, line 196:
<comment>Attachment download errors are swallowed, causing silent data loss in exported evidence bundles.</comment>
<file context>
@@ -0,0 +1,204 @@
+ });
+ if (!attachment) return null;
+ return await this.attachmentsService.getObjectBuffer(attachment.url);
+ } catch {
+ return null;
+ }
</file context>
| throw new NotFoundException('Revocation record not found'); | ||
| } | ||
|
|
||
| await db.offboardingAccessRevocation.delete({ |
There was a problem hiding this comment.
P2: Undoing a revocation should delete its uploaded evidence to avoid orphaned attachment records/files.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/offboarding-checklist/access-revocation.service.ts, line 143:
<comment>Undoing a revocation should delete its uploaded evidence to avoid orphaned attachment records/files.</comment>
<file context>
@@ -0,0 +1,233 @@
+ throw new NotFoundException('Revocation record not found');
+ }
+
+ await db.offboardingAccessRevocation.delete({
+ where: { id: revocation.id },
+ });
</file context>
| return; | ||
| } | ||
|
|
||
| const { totalVendors, revokedCount } = await this.getAccessRevocations( |
There was a problem hiding this comment.
P2: Use direct count queries in completion sync instead of calling getAccessRevocations, which does expensive attachment loading.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/offboarding-checklist/access-revocation.service.ts, line 204:
<comment>Use direct count queries in completion sync instead of calling `getAccessRevocations`, which does expensive attachment loading.</comment>
<file context>
@@ -0,0 +1,233 @@
+ return;
+ }
+
+ const { totalVendors, revokedCount } = await this.getAccessRevocations(
+ organizationId,
+ memberId,
</file context>
| }; | ||
| } | ||
|
|
||
| function fileToBase64(file: File): Promise<string> { |
There was a problem hiding this comment.
P3: This adds duplicate file-to-base64 conversion logic that already exists in other hooks; extracting a shared helper would prevent drift and keep file-upload behavior consistent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/app/src/hooks/use-offboarding-checklist.ts, line 133:
<comment>This adds duplicate file-to-base64 conversion logic that already exists in other hooks; extracting a shared helper would prevent drift and keep file-upload behavior consistent.</comment>
<file context>
@@ -0,0 +1,143 @@
+ };
+}
+
+function fileToBase64(file: File): Promise<string> {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
</file context>
This is an automated pull request to merge mariano/cs-312-feature-add-personnel-employment-events-date-tracking into dev.
It was created by the [Auto Pull Request] action.
Summary by cubic
Add employment event tracking and a guided offboarding workflow. Teams can record onboard/offboard dates, complete a checklist with evidence, confirm per‑vendor access revocations, and export audit-ready bundles.
New Features
onboardDateandoffboardDateon people; offboard date auto-sets on deactivation.startDatein the generic employee schema.@trycompai/design-systemto1.1.19.Migration
Linked Linear issue CS-312: Implements employment event date tracking and supporting evidence capture to streamline auditor review.
Written for commit a1d26ff. Summary will update on new commits. Review in cubic