Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

16 KiB

Scratchpad: Stale Code Quick Wins

Key Discoveries

Media package exists for a reason that no longer applies

The @alga-psa/media package was created to break the cycle documents -> users -> media -> documents. This cycle was resolved and removed from the known-cycles baseline. The media package's dynamic import workarounds (getStorageServiceAsync(), deleteDocumentAsync(), getDocumentTypeIdAsync()) are no longer needed -- the canonical documents/lib/entityImageService.ts uses direct imports to @alga-psa/storage/StorageService and internal documentActions.

Three copies of avatarUtils exist

  1. @alga-psa/formatting/avatarUtils.ts (228 LOC) -- uses getImageUrlInternal from ./imageUrl.ts
  2. @alga-psa/user-composition/lib/avatarUtils.ts (~95 LOC) -- has its own getImageUrlInternalLite inline
  3. @alga-psa/media/lib/avatarUtils.ts (87 LOC) -- uses dynamic import from documents

This PR deletes #3. Consolidating #1 and #2 is a separate future task.

getTeamAvatarUrl only exists in media

The formatting package's EntityType already includes 'team', and getEntityImageUrl works for any entity type. But there's no getTeamAvatarUrl convenience function. Adding one is a trivial 4-line wrapper.

documents version of EntityImageService is the canonical one

The @alga-psa/documents version:

  • Uses direct StorageService import (not dynamic)
  • Uses direct deleteDocument/getDocumentTypeId calls
  • Has isActionPermissionError handling
  • Already used by 4 other packages (clients, client-portal, tenancy, documents-internal)

The media version:

  • Uses dynamic imports to break cycles (no longer needed)
  • Less robust error handling (no permission error check)
  • Only used by 3 callers (users x2, teams x1)

Cross-package violations trade-off

Adding users -> documents creates 2 new lint warnings. But:

  • 4 other packages already do this exact import
  • We delete an entire package (8 files) in exchange
  • teams -> documents creates NO warning (teams not in VERTICAL_PACKAGES)

Test file reads source files via fs.readFileSync

server/src/test/teams-v2-improvements.test.ts does string-content assertions on source files:

const mediaAvatarUtils = read('packages/media/src/lib/avatarUtils.ts');

This must be updated to point at the formatting file, and assertions T085/T089 need adjustment.

Avatar Bug 1: Client portal user avatars on MSP user list

Root cause chain:

  1. Client portal user uploads avatar via uploadContactAvatar(user.contact_id, formData) in clientUserActions.ts:289
  2. This stores entity_type='contact', entity_id=contact_id in document_associations
  3. MSP UserList fetches via getUserAvatarUrlAction(user.user_id) which queries entity_type='user', entity_id=user_id
  4. No match → null → no avatar displayed

Fix: Branch on user.user_type in UserList.tsx avatar fetch loop. Use getContactAvatarUrlAction(user.contact_id) for client users.

Avatar Bug 2: Contact avatar '?' fallback

Root cause chain:

  1. ContactAvatarUpload.tsx:38 passes entityName="" to EntityImageUpload
  2. EntityImageUpload renders UserAvatar with userName=""
  3. ContactAvatar.getContactInitials returns '?' when !name (line 12 of ContactAvatar.tsx)

Fix: Add contactName: string prop to ContactAvatarUpload, pass it as entityName. Update 2 callers (ContactDetails.tsx, ContactDetailsEdit.tsx) to pass the contact's full_name.

Build Command

Use instead of npm run build:

NODE_OPTIONS=--max-old-space-size=32768 npx nx run-many -t build --maxParallel=4

File Inventory

Files to delete (10 total)

  • server/src/lib/posthog.ts (7 LOC)
  • server/src/lib/actions/tenant-secret-actions.ts (222 LOC)
  • packages/media/src/index.ts (3 LOC)
  • packages/media/src/lib/avatarUtils.ts (87 LOC)
  • packages/media/src/lib/documentsHelpers.ts (32 LOC)
  • packages/media/src/services/EntityImageService.ts (296 LOC)
  • packages/media/package.json
  • packages/media/project.json
  • packages/media/tsconfig.json
  • packages/media/tsup.config.ts

Files to edit (15 total)

  • server/src/components/settings/secrets/SecretsManagement.tsx (import path)
  • server/src/components/settings/secrets/SecretDialog.tsx (import path)
  • packages/formatting/src/avatarUtils.ts (add getTeamAvatarUrl)
  • packages/users/src/services/UserService.ts (import paths)
  • packages/users/src/actions/user-actions/userActions.ts (import path)
  • packages/users/package.json (dependencies)
  • packages/teams/src/actions/team-actions/avatarActions.ts (import paths)
  • packages/teams/package.json (dependencies)
  • server/next.config.mjs (remove media alias)
  • server/tsconfig.json (remove media paths)
  • ee/server/tsconfig.json (remove media paths)
  • services/workflow-worker/Dockerfile (remove media workspace)
  • server/src/test/teams-v2-improvements.test.ts (update file paths + assertions)
  • server/src/components/settings/general/UserList.tsx (avatar fetch branch)
  • packages/clients/src/components/contacts/ContactAvatarUpload.tsx (add contactName prop)
  • packages/clients/src/components/contacts/ContactDetails.tsx (pass contactName)
  • packages/clients/src/components/contacts/ContactDetailsEdit.tsx (pass contactName)

Execution Order

  1. Task 1: Delete posthog.ts (independent, no deps)
  2. Task 2: Redirect tenant-secret-actions callers, delete server copy (independent)
  3. Task 3: Media deletion (depends on nothing, but do in sub-steps): a. Add getTeamAvatarUrl to formatting b. Update 3 caller files (import paths) c. Update 2 package.json files (dependencies) d. Update test file e. Update config files (next.config, tsconfig x2, Dockerfile) f. Delete packages/media/ directory g. npm install to update lock file
  4. Task 4: Fix UserList.tsx avatar fetch for client portal users
  5. Task 5: Fix ContactAvatarUpload.tsx and its 2 callers
  6. Build verification

Gotchas

  • The teams-v2-improvements.test.ts test does string matching on file content -- it will ENOENT if media files are deleted before the test path is updated.
  • server/next.config.mjs has media referenced in 3 separate places (aliases object, transpilePackages array, resolve.alias). All 3 must be removed.
  • package-lock.json will have stale entries for @alga-psa/media until npm install is run.
  • The Dockerfile --workspace=@alga-psa/media line is in a COPY instruction context -- verify the line can be cleanly removed without breaking the multi-line command.
  • T102 in teams-v2-improvements.test.ts checks that @alga-psa/media is in teams package.json deps -- must update to check for @alga-psa/documents and @alga-psa/formatting instead.

Progress Log

  • F001: Deleted server/src/lib/posthog.ts after validating the deprecated wrapper path had no remaining imports.
  • F002: Pointed SecretsManagement.tsx at @alga-psa/tenancy/actions so it uses the canonical tenant secret actions barrel.
  • F003: Updated SecretDialog.tsx to import secret CRUD helpers from the canonical @alga-psa/tenancy/actions barrel.
  • F004: Deleted server/src/lib/actions/tenant-secret-actions.ts once both settings callers had been redirected to @alga-psa/tenancy/actions.
  • F005: Added getTeamAvatarUrl to packages/formatting/src/avatarUtils.ts as the canonical wrapper over getEntityImageUrl('team', ...).
  • F006: Repointed packages/users/src/services/UserService.ts to import avatar URL lookup from formatting and image mutations from documents.
  • F007: Updated packages/users/src/actions/user-actions/userActions.ts to consume entity image mutations from @alga-psa/documents.
  • F008: Split packages/teams/src/actions/team-actions/avatarActions.ts imports across canonical documents and formatting packages.
  • F009: Replaced the stale @alga-psa/media dependency in packages/users/package.json with canonical documents and formatting package dependencies.
  • F010: Replaced the teams package dependency on @alga-psa/media with the canonical documents and formatting packages.
  • F011: Removed all @alga-psa/media alias and transpilation wiring from server/next.config.mjs.
  • F012: Removed the @alga-psa/media path mappings from server/tsconfig.json.
  • F013: Removed the stale @alga-psa/media path mappings from ee/server/tsconfig.json.
  • F014: Dropped the deleted @alga-psa/media workspace from services/workflow-worker/Dockerfile.
  • F015: Updated server/src/test/teams-v2-improvements.test.ts to read formatting avatar utilities and assert the new canonical package dependencies.
  • F016: Deleted packages/media/ entirely after migrating its three callers, config references, and the source-reading regression test.
  • F017: Ran npm install to refresh package-lock.json after removing @alga-psa/media and adding canonical package dependencies.
  • F018: Imported getContactAvatarUrlAction into UserList.tsx to support client-user avatar lookup via contact records.
  • F019: Updated the UserList.tsx avatar lookup loop to fetch contact avatars for client users, preserve user avatars for internal users, and fall back to null when a client user lacks contact_id.
  • F020: Added a required contactName prop to ContactAvatarUploadProps and plumbed it into the component signature.
  • F021: Replaced the hardcoded empty entityName in ContactAvatarUpload with the new contactName prop so initials render correctly.
  • F022: Updated ContactDetails.tsx to pass editedContact.full_name into ContactAvatarUpload.
  • F023: Updated ContactDetailsEdit.tsx to pass contact.full_name into ContactAvatarUpload.
  • Discovery: the first full build after F023 failed because server/src/app/api/secrets/route.ts and server/src/app/api/secrets/[name]/route.ts still imported the deleted server duplicate. Added follow-up items F023A/F023B and T041A/T041B so the plan matches the real remaining work.
  • F023A: Repointed server/src/app/api/secrets/route.ts to @alga-psa/tenancy/actions to remove the hidden stale import found by the build.
  • F023B: Repointed server/src/app/api/secrets/[name]/route.ts to @alga-psa/tenancy/actions so the build no longer looks for the deleted server duplicate.
  • F024: Re-ran NODE_OPTIONS=--max-old-space-size=32768 npx nx run-many -t build --maxParallel=4 after fixing the hidden secrets API imports; the full build completed successfully.
  • Discovery: added T042A because the PRD explicitly requires server/src/test/teams-v2-improvements.test.ts to pass after the media cleanup.
  • T001: Confirmed the deprecated @/lib/posthog wrapper path has zero remaining imports anywhere outside docs and lockfiles.
  • T002: Verified server/src/lib/posthog.ts is absent on disk after F001.
  • T003: Verified SecretsManagement.tsx imports tenant secret helpers from @alga-psa/tenancy/actions.
  • T004: Verified SecretDialog.tsx imports its secret helpers from @alga-psa/tenancy/actions.
  • T005: Verified the deleted server-side tenant-secret-actions.ts duplicate is no longer present.
  • T006: Confirmed server/src/ no longer references tenant-secret-actions outside excluded test/docs paths.
  • T007: Corrected the outdated checklist wording and verified the tenancy barrel re-exports the exact secret helpers now used by the settings UI and API routes.
  • Discovery: added T042A because the PRD explicitly requires server/src/test/teams-v2-improvements.test.ts to pass after the media cleanup.
  • T001: Confirmed the deprecated @/lib/posthog wrapper path has zero remaining imports anywhere outside docs and lockfiles.
  • T002: Verified server/src/lib/posthog.ts is absent on disk after F001.
  • T003: Verified SecretsManagement.tsx imports tenant secret helpers from @alga-psa/tenancy/actions.
  • T004: Verified SecretDialog.tsx imports its secret helpers from @alga-psa/tenancy/actions.
  • T005: Verified the deleted server-side tenant-secret-actions.ts duplicate is no longer present.
  • T006: Confirmed server/src no longer references tenant-secret-actions outside excluded test/docs paths.
  • T007: Corrected the outdated checklist wording and verified the tenancy barrel re-exports the exact secret helpers now used by the settings UI and API routes.
  • T008: Verified getTeamAvatarUrl exists in formatting and delegates to getEntityImageUrl('team', teamId, tenant).
  • T009: Confirmed getTeamAvatarUrl is exported directly from packages/formatting/src/avatarUtils.ts.
  • T010: Verified UserService.ts imports getUserAvatarUrl from formatting instead of the deleted media package.
  • T011: Verified UserService.ts imports uploadEntityImage and deleteEntityImage from @alga-psa/documents.
  • T012: Verified userActions.ts imports uploadEntityImage and deleteEntityImage from @alga-psa/documents.
  • T013: Verified teams avatarActions.ts imports uploadEntityImage and deleteEntityImage from @alga-psa/documents.
  • T014: Verified teams avatarActions.ts imports getTeamAvatarUrl from formatting instead of media.
  • T015: Verified packages/users/package.json no longer lists @alga-psa/media in dependencies.
  • T016: Verified packages/users/package.json includes canonical documents and formatting dependencies.
  • T017: Verified packages/teams/package.json no longer lists @alga-psa/media in dependencies.
  • T018: Verified packages/teams/package.json includes canonical documents and formatting dependencies.
  • T019: Confirmed server/next.config.mjs no longer contains any @alga-psa/media alias or transpile references.
  • T020: Confirmed server/tsconfig.json no longer contains @alga-psa/media path mappings.
  • T021: Confirmed ee/server/tsconfig.json no longer contains @alga-psa/media path mappings.
  • T022: Confirmed the workflow-worker Dockerfile no longer references the deleted @alga-psa/media workspace.
  • T023: Verified teams-v2-improvements.test.ts now reads packages/formatting/src/avatarUtils.ts instead of the deleted media file.
  • T024: Verified the T085 assertion now checks formatting avatar utils for the team EntityType entry.
  • T025: Verified the T089 assertion now checks formatting avatar utils for getTeamAvatarUrl.
  • T026: Verified the entire packages/media directory has been removed from disk.
  • T027: Confirmed there are no remaining @alga-psa/media references outside docs, plan artifacts, lockfiles, and AI metadata.
  • T028: Re-ran npm install successfully after the dependency cleanup; see /tmp/t028-npm-install.log for the command output from this validation run.
  • T029: Re-ran the full Nx build successfully; output captured in /tmp/t029-build.log.
  • T030: Generated a fresh Nx graph and confirmed scripts/check-circular-deps.mjs reports no new cycles against .github/known-cycles.json.
  • T031: Checked all modified source/config files and confirmed none are shim-only re-export files.
  • T032: Verified packages/documents/src/lib/entityImageService.ts exports EntityType, uploadEntityImage, and deleteEntityImage for migrated callers.
  • T033: Verified packages/formatting/src/avatarUtils.ts exports both getUserAvatarUrl and getTeamAvatarUrl.
  • T034: Verified UserList.tsx imports getContactAvatarUrlAction alongside getUserAvatarUrlAction from @alga-psa/user-composition/actions.
  • T035: Verified the avatar fetch loop branches on user.user_type === 'client' and uses getContactAvatarUrlAction(user.contact_id, user.tenant).
  • T036: Verified the avatar fetch loop still uses getUserAvatarUrlAction(user.user_id, user.tenant) for non-client users.
  • T037: Verified client users without contact_id fall back to a null avatar instead of querying the wrong entity type.
  • T038: Verified ContactAvatarUploadProps includes a required contactName: string prop.
  • T039: Verified ContactAvatarUpload passes contactName through as EntityImageUpload.entityName.
  • T040: Verified ContactDetails.tsx passes editedContact.full_name to ContactAvatarUpload.
  • T041: Verified ContactDetailsEdit.tsx passes contact.full_name to ContactAvatarUpload.
  • T041A: Verified server/src/app/api/secrets/route.ts imports tenant secret actions from @alga-psa/tenancy/actions.
  • T041B: Verified server/src/app/api/secrets/[name]/route.ts imports tenant secret actions from @alga-psa/tenancy/actions.
  • T042A: Updated server/src/test/teams-v2-improvements.test.ts to read the existing packages/user-composition/src/lib/avatarUtils.ts source and to assert the current TeamDetails/UserManagement structure before rerunning the test successfully.
  • T042: Reused the successful full-build validation from T029, which ran after all planned feature work was complete.