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

27 KiB

Scratchpad — Ticket Watch Lists and CC-Style Notifications

  • Plan slug: ticket-watch-lists
  • Created: 2026-02-25

What This Is

Working notes for the ticket watch-list feature plan. This log captures clarified scope, discovered code touchpoints, and validation commands.

Decisions

  • (2026-02-25) Delivery mode for watcher notifications is separate individual emails (not CC header batching).
  • (2026-02-25) Watchers are notified for customer-visible ticket updates only.
  • (2026-02-25) Initial persistence strategy is tickets.attributes.watch_list (no schema migration in v1).
  • (2026-02-25) Inbound email ingestion auto-adds To/CC recipients to watch list; users can uncheck/remove afterward.
  • (2026-02-27) Scope expansion approved: Watch List add flow now includes internal users + contacts in addition to manual email entry.
  • (2026-02-27) Persistence decision: store email + optional entity metadata (entity_type, entity_id) for picker-added watchers; email remains canonical for dedupe/send.
  • (2026-02-27) UX decision: optimize for common workflow with client-first contact picker (ticket-associated client contacts by default).
  • (2026-02-27) Fallback decision: provide secondary Search all contacts path for cross-client additions.
  • (2026-02-27) Contact-scope decision: all-contacts fallback includes active contacts only by default.
  • (2026-02-27) Inactive identity behavior: do not auto-disable watcher sends when linked user/contact becomes inactive; active watcher + valid email still sends.

Discoveries / Constraints

  • (2026-02-25) Existing ticket notification fan-out is centralized in server/src/lib/eventBus/subscribers/ticketEmailSubscriber.ts across created/updated/assigned/comment/closed handlers.
  • (2026-02-25) Inbound processing path is shared/services/email/processInboundEmailInApp.ts and already includes inbound to metadata in comment payloads.
  • (2026-02-25) Shared workflow actions (shared/workflow/actions/emailWorkflowActions.ts) are the right boundary for DB-backed helper actions used by inbound processing.
  • (2026-02-25) Ticket UI/property editing surfaces already use attribute updates via updateTicketWithCache, so watch-list UI can piggyback on existing ticket update permissions.
  • (2026-02-25) Must avoid adding provider mailbox/self addresses as watchers to reduce self-notification loop risk.
  • (2026-02-27) TicketDetails already loads availableAgents (all users for tenant); internal-user watch-list picker can reuse this dataset without new backend lookups.
  • (2026-02-27) TicketDetails currently loads contacts scoped to ticket.client_id; this supports fast default contact-add path directly.
  • (2026-02-27) Global contact search requires a new all-contacts action (active-only) and should be lazily fetched to avoid heavier initial ticket payloads.
  • (2026-02-27) Existing reusable UI components (UserPicker, ContactPicker) already match needed interaction patterns and should be reused to minimize bespoke UI.

Commands / Runbooks

  • (2026-02-25) Scaffold plan:
    • python3 /Users/roberisaacs/.codex/skills/alga-plan/scripts/scaffold_plan.py "Ticket Watch Lists and CC-Style Notifications" --slug ticket-watch-lists
  • (2026-02-25) Validate plan artifacts:
    • python3 /Users/roberisaacs/.codex/skills/alga-plan/scripts/validate_plan.py ee/docs/plans/2026-02-25-ticket-watch-lists
  • (2026-02-25) Useful discovery commands:
    • rg -n "ticketEmailSubscriber|processInboundEmailInApp|updateTicketWithCache" packages server shared -S
    • sed -n '1,260p' server/src/lib/eventBus/subscribers/ticketEmailSubscriber.ts
    • sed -n '1,320p' shared/services/email/processInboundEmailInApp.ts
  • (2026-02-27) Scope-expansion discovery commands:
    • rg -n "Watch List|watch_list|onUpdateWatchList|upsertTicketWatchListRecipients|extractActiveWatcherEmails" packages/tickets shared server -S
    • sed -n '1,260p' packages/tickets/src/components/ticket/TicketWatchListCard.tsx
    • sed -n '1,260p' packages/ui/src/components/ContactPicker.tsx
    • sed -n '1,260p' packages/ui/src/components/UserPicker.tsx
    • sed -n '220,430p' packages/tickets/src/actions/optimizedTicketActions.ts
  • Customer requirement summary (captured in chat):
    • ServiceNow-style watch list, auto-add from inbound To/CC, uncheck/remove from ticket.
  • Key implementation files:
    • packages/tickets/src/components/ticket/TicketProperties.tsx
    • packages/tickets/src/components/ticket/TicketDetails.tsx
    • shared/services/email/processInboundEmailInApp.ts
    • shared/workflow/actions/emailWorkflowActions.ts
    • server/src/lib/eventBus/subscribers/ticketEmailSubscriber.ts

Open Questions

  • Should watcher sends respect internal user-level notification preferences if watcher email maps to an internal user?

  • Should watch-list add/remove/toggle actions be surfaced in ticket comment/activity history?

  • Should bundle-child fan-out explicitly include watcher behavior or rely strictly on per-ticket event scope?

  • (2026-02-25) Implemented F001 by introducing shared watch-list utilities and contract (shared/lib/tickets/watchList.ts) with normalized lowercase email + active booleans persisted in tickets.attributes.watch_list.

  • (2026-02-25) Implemented F002: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F003: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F004: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F005: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F006: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F007: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F008: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F009: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F010: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F011: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F012: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F013: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F014: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F015: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F016: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F017: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F018: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F019: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F020: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F021: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F022: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F023: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented F024: completed planned scope for this feature item in the ticket watch-list delivery.

  • (2026-02-25) Implemented T01: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T02: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T03: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T04: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T05: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T06: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T07: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T08: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T09: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T10: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T11: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T12: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T13: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T14: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T15: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T16: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T17: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T18: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T19: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T20: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T21: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T22: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T23: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T24: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T25: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T26: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T27: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T28: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T29: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T30: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T31: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T32: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T33: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T34: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T35: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T36: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T37: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T38: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T39: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T40: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T41: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T42: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T43: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Implemented T44: added or updated automated coverage for this planned test scenario during watch-list delivery.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T001 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T002 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T003 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T004 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T005 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T006 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T007 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T008 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T009 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T010 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T011 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T012 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T013 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T014 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T015 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T016 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T017 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T018 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T019 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T020 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T021 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T022 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T023 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T024 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T025 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T026 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T027 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T028 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T029 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T030 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T031 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T032 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T033 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T034 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T035 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T036 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T037 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T038 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T039 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T040 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T041 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T042 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T043 after validating watch-list test coverage.

  • (2026-02-25) Bookkeeping: set tests.json implemented=true for T044 after validating watch-list test coverage.

  • (2026-02-27) Implemented F025 by extending shared/lib/tickets/watchList.ts contract types and normalization to accept/persist optional entity_type (user|contact) and entity_id metadata while keeping normalized email as canonical.

  • (2026-02-27) Validation run for F025: cd shared && npx vitest run lib/tickets/__tests__/watchList.test.ts --config vitest.config.ts.

  • (2026-02-27) Implemented F026 by updating duplicate-email merge behavior in shared/lib/tickets/watchList.ts to preserve existing entity metadata and backfill missing metadata from incoming entries without changing legacy watcher validity.

  • (2026-02-27) Added unit coverage in shared/lib/tickets/__tests__/watchList.test.ts for metadata parse/merge scenarios (T045/T046/T047), used now as feature-validation scaffolding ahead of test-checklist bookkeeping.

  • (2026-02-27) Validation run for F026: cd shared && npx vitest run lib/tickets/__tests__/watchList.test.ts --config vitest.config.ts.

  • (2026-02-27) Implemented F027 by adding an internal UserPicker + Add User flow in TicketWatchListCard that writes watcher entries from selected internal users using normalized email and user metadata.

  • (2026-02-27) Wired internalUsers into TicketWatchListCard from TicketProperties.availableAgents to make the picker functional in the ticket properties panel.

  • (2026-02-27) Validation run for F027: cd packages/tickets && npx vitest run src/components/ticket/__tests__/TicketWatchListCard.test.tsx --config vitest.config.ts.

  • (2026-02-27) Implemented F028 by adding a client-contact quick-add path (ContactPicker + Add Contact) in TicketWatchListCard, defaulting to the ticket-scoped contacts list already loaded in TicketDetails.

  • (2026-02-27) Wired clientContacts through TicketProperties into TicketWatchListCard so the quick-add source is ticket-client contacts by default.

  • (2026-02-27) Validation run for F028: cd packages/tickets && npx vitest run src/components/ticket/__tests__/TicketWatchListCard.test.tsx --config vitest.config.ts.

  • (2026-02-27) Implemented F029 by adding a secondary, initially hidden Search all contacts UI path in TicketWatchListCard, with separate selection/add controls intended for cross-client contact adds.

  • (2026-02-27) Added lazy-load hook points in TicketWatchListCard (onLoadAllContacts, allContactsLoading) so the all-contacts list can be fetched on demand instead of eagerly.

  • (2026-02-27) Validation run for F029: cd packages/tickets && npx vitest run src/components/ticket/__tests__/TicketWatchListCard.test.tsx --config vitest.config.ts.

  • (2026-02-27) Implemented F030 by enforcing picker email guards in TicketWatchListCard: selected users/contacts without a valid normalized email now surface explicit errors and abort onUpdateWatchList persistence.

  • (2026-02-27) Implemented F031 by wiring all-contact fallback props/handler through TicketDetails -> TicketProperties -> TicketWatchListCard (allContactsForWatchList, loading state, and lazy-load callback) while preserving existing manual/user/client-contact watch-list behavior.

  • (2026-02-27) Validation for F031: TicketWatchListCard suite still passes. Expanded check TicketDetails.originBadge.contract.test.ts currently fails on existing translation-key string expectations (tickets.origin.*) that predate this change set and are unrelated to watch-list wiring.

  • (2026-02-27) Implemented F032 by adding getAllActiveContacts in shared ticket-client query helpers and exposing it via packages/tickets/src/actions/clientLookupActions.ts; TicketDetails now lazily calls this action only when all-contacts search is requested.

  • (2026-02-27) Added packages/tickets/src/actions/clientLookupActions.watchList.test.ts to validate action wiring/default sort behavior and updated packages/tickets/vitest.config.ts aliases needed for isolated action tests.

  • (2026-02-27) Validation run for F032: cd packages/tickets && npx vitest run src/actions/clientLookupActions.watchList.test.ts src/components/ticket/__tests__/TicketWatchListCard.test.tsx --config vitest.config.ts.

  • (2026-02-27) Implemented F033 by keeping all add paths (manual/user/contact/all-contacts) on the shared mergeTicketWatchListRecipients utility so duplicate-email dedupe/reactivation behavior remains unified and manual entry stays backward-compatible.

  • (2026-02-27) Implemented F034 by persisting picker additions with source: manual plus identity metadata (name, entity_type, entity_id) from selected users/contacts before saving through setTicketWatchListOnAttributes.

  • (2026-02-27) Implemented F035 by enhancing watch-list rows in TicketWatchListCard to show name and an identity-type hint badge (user/contact) while always rendering canonical email.

  • (2026-02-27) Validation run for F035: cd packages/tickets && npx vitest run src/components/ticket/__tests__/TicketWatchListCard.test.tsx --config vitest.config.ts.

  • (2026-02-27) Implemented F036 by adding subscriber regression coverage that watcher extraction/sending remains email-driven even when watcher entries include entity_type/entity_id metadata.

  • (2026-02-27) Validation run for F036: cd server && npx vitest run src/test/unit/notifications/ticketEmailSubscriber.watchers.test.ts --config vitest.config.ts.

  • (2026-02-27) Implemented F037 by asserting inactive/deleted linked identity metadata does not suppress active watcher sends; send eligibility remains active + valid email.

  • (2026-02-27) Implemented T045 in shared/lib/tickets/__tests__/watchList.test.ts validating parser acceptance of entity_type/entity_id metadata on watch-list entries.

  • (2026-02-27) Implemented T046 in shared/lib/tickets/__tests__/watchList.test.ts ensuring duplicate-email merge preserves existing entity metadata when re-added through a different path.

  • (2026-02-27) Implemented T047 in shared/lib/tickets/__tests__/watchList.test.ts verifying missing entity metadata is backfilled when duplicate emails are re-added via picker metadata.

  • (2026-02-27) Implemented T048 by expanding packages/tickets/src/components/ticket/__tests__/TicketWatchListCard.test.tsx with picker-control UI assertions; test harness now mocks pickers for deterministic watch-list add-flow coverage.

  • (2026-02-27) Implemented T049 in TicketWatchListCard.test.tsx covering internal-user picker add persistence with normalized email plus entity_type=user and entity_id.

  • (2026-02-27) Implemented T050 in TicketWatchListCard.test.tsx asserting client-scoped contact quick-add controls render as the default contact-add path.

  • (2026-02-27) Implemented T051 in TicketWatchListCard.test.tsx validating client-contact quick-add persistence of metadata (name, entity_type=contact, entity_id).

  • (2026-02-27) Implemented T052 in TicketWatchListCard.test.tsx verifying Search all contacts remains secondary/hidden until explicitly requested and quick client-contact add works without it.

  • (2026-02-27) Implemented T053 in TicketWatchListCard.test.tsx covering lazy invocation of the all-contacts loader and successful cross-client contact add via the secondary search path.

  • (2026-02-27) Implemented T054 in TicketWatchListCard.test.tsx validating clear error + no persist when internal-user picker selection lacks a valid email.

  • (2026-02-27) Implemented T055 in TicketWatchListCard.test.tsx validating clear error + no persist when contact picker selection lacks a valid email.

  • (2026-02-27) Implemented T056 in TicketWatchListCard.test.tsx covering manual-vs-picker dedupe behavior so identical emails never create duplicate watcher rows.

  • (2026-02-27) Implemented T057 with packages/tickets/src/components/ticket/__tests__/TicketWatchListWiring.contract.test.ts, asserting watcher-picker props are wired through TicketDetails and TicketProperties into TicketWatchListCard.

  • (2026-02-27) Implemented T058 in packages/tickets/src/actions/clientLookupActions.watchList.test.ts validating all-contact lookup action wiring and default ascending full-name sort path.

  • (2026-02-27) Implemented T059 in packages/tickets/src/actions/clientLookupActions.watchList.test.ts validating guard-path behavior for active-only lookup and explicit sort-direction override.

  • (2026-02-27) Implemented T060 by extending shared/workflow/actions/__tests__/emailWorkflowActions.watchList.test.ts with a happy-path assertion that picker metadata persists to tickets.attributes.watch_list.

  • (2026-02-27) Implemented T061 by extending emailWorkflowActions.watchList.test.ts with guard assertions that metadata-only recipients lacking valid emails are ignored without ticket mutation.

  • (2026-02-27) Implemented T062 in server/src/test/unit/notifications/ticketEmailSubscriber.watchers.test.ts ensuring watcher extraction/sending stays email-driven despite entity metadata fields.

  • (2026-02-27) Implemented T063 in ticketEmailSubscriber.watchers.test.ts ensuring linked inactive/deleted identity metadata does not block sends for active watcher entries with valid emails.

  • (2026-02-27) Implemented T064 in TicketWatchListCard.test.tsx validating watcher rows render identity hints (name/type) while continuing to show canonical email.