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

7.6 KiB
Raw Blame History

Scratchpad — Ticket Close Rules

  • Plan slug: 2026-06-10-ticket-close-rules
  • Created: 2026-06-10
  • Design doc: docs/plans/2026-06-10-ticket-close-rules-design.md

Decisions

  • (2026-06-10) Per-board scope for all rules — matches statuses/SLA/email settings scoping.
  • (2026-06-10) Hard block + ticket:close_override permission; overrides audit-logged with failure list.
  • (2026-06-10) Client portal exempt from gates (customers can't satisfy internal-hygiene conditions) but gets the closure-recording fix; portal bypass audit-logged.
  • (2026-06-10) Auto-close engine = single 15-min recurring scan via IJobRunner (Temporal EE / pg-boss CE). Per-ticket Temporal workflows rejected: CE would still need the scan, inactivity resets need signal plumbing across every comment path (SLA Temporal workflow already grew a self-healing poll for this drift), config changes invalidate in-flight timers. ticket_auto_close_state is engine-agnostic if we ever swap.
  • (2026-06-10) Template items are copied onto tickets (provenance via template_id), never referenced — template edits must not mutate history. Idempotency key = template_id present among ticket's items.
  • (2026-06-10) closed_by = null for auto-closes; attribution via audit row (actor_type: 'system', source: 'system'). No dedicated system user UUID exists in the codebase.
  • (2026-06-10) Checklist accountability = permanent completed_by/completed_at displayed inline, no confirm dialog; uncheck clears but audit preserves prior signoff.
  • (2026-06-10) Test strategy 80/20: automated tests own logic/data correctness (units, integration, races, tenant isolation, regressions — 37 tests); screen flows, job wiring, and i18n moved to the risk-framed manual pass in SMOKE_TESTS.md. Former T034 folded into T032; T036/T038T045/T048 replaced by smoke flows.

Discoveries / Constraints

  • No pre-close validation exists anywhere today; only idempotency guards in workflow tickets.close and the "closed status can't be board default" config constraint (packages/reference-data/.../statusActions.ts:161).

  • Four open→closed flip detection sites must be wired: ticketActions.ts updateTicket (~7241064), optimizedTicketActions.ts updateTicketInTransaction (~23862442), TicketService.ts update (11881221), portal client-tickets.ts updateTicketStatus (712, currently doesn't even set closure fields — known gap, fixed by F029).

  • Resolution comment markers: comments.is_resolution boolean AND metadata->>'closes_ticket' (set by the TicketDetails close-with-comment flow at optimizedTicketActions.ts:2734). Gate accepts either. The close-flow inserts the comment before the status update, so gate ordering works naturally.

  • Time entry linkage: time_entries.work_item_id + work_item_type = 'ticket'.

  • Bundles: inline tickets.master_ticket_id, no join table. Open child = closed_at IS NULL.

  • Board settings dialog pattern to copy: inbound-reply-reopen bordered section in BoardsSettings.tsx (~11391218); status list uses up/down buttons, not DnD — reuse for template item ordering.

  • New settings tab goes in TicketingSettings.tsx TICKETING_TAB_IDS, not the top-level SettingsPage.tsx.

  • Job model: reconcile-bucket-usage registration in registerAllHandlers.ts (~266275) + per-tenant cron loop in initializeScheduledJobs.ts.

  • Notification template model migration: 20250226090000_add_credit_expiration_notification.cjs. Subtype name string must exactly match what's passed to sendNotificationIfEnabled.

  • Audit: writeTicketActivity (shared/lib/ticketActivity/), event constants in types.ts, rendering in TicketActivityTimeline.tsx. Actor types include system; sources include system and client_portal.

  • RBAC: hasPermission(user, resource, action) from packages/authorization/src/rbac.ts; permission seed model 20250619120000_add_comprehensive_permissions.cjs.

  • 422 pattern: throw ValidationError('...', details)ApiBaseController serializes with HTTP 422.

  • Workflow action registry: getActionRegistryV2().register in registerTicketActions(); next free action ID is A09.

  • (2026-06-10) RLS is retired for new tables (see 20251111120000_disable_rls_on_survey_tables.cjs); the close-rules tables follow ticket_audit_logs: composite (tenant, id) PKs + Citus distribution + guarded raw FKs, no RLS.

  • (2026-06-10) comments.author_type DB enum has no 'system' value — TicketModel.createComment maps system→'internal'; auto-close comment provenance lives in metadata.source = 'auto_close'.

  • (2026-06-10) The blocked-close dialog uses a non-throwing checkTicketClosure server action instead of catching TicketCloseValidationError client-side: custom Error fields don't survive the Next server-action boundary.

  • (2026-06-10) Auto-apply hooks live at TicketModel.createTicket (all creation paths) and the two update paths; the apply helpers moved to shared/lib/ticketChecklists to avoid a packages/tickets→shared cycle. Bypass-audit helpers live in shared/lib/ticketCloseRules for the same reason (workflow runtime can't import packages/tickets).

  • (2026-06-10) updateTicketInTransaction gained options.systemActor for the auto-close engine: closed_by stays null, events publish with a SYSTEM actor (v2 ticketClosedEventPayloadSchema allows omitted closedByUserId), live updates are skipped, and the audit row is system-sourced.

  • (2026-06-10) updateTicket's catch-all used to flatten every error into 'Failed to update ticket'; TicketCloseValidationError is now re-thrown so the fallback UI path surfaces the unmet conditions.

Commands / Runbooks

  • Migrations (CE+EE combined): cd server && DB_HOST=localhost DB_PORT=5472 DB_NAME_SERVER=server DB_USER_ADMIN=postgres DB_PASSWORD_ADMIN=$(cat ../secrets/postgres_password) node scripts/run-ee-migrations.js latest.
  • Integration tests: cd server && DB_HOST=localhost DB_PORT=5472 DB_PASSWORD_ADMIN=$(cat ../secrets/postgres_password) DB_PASSWORD_SERVER=$(cat ../secrets/db_password_server) npx vitest run src/test/integration/ticketCloseRules.integration.test.ts src/test/integration/ticketChecklists.integration.test.ts src/test/integration/autoCloseTickets.integration.test.ts --coverage.enabled=false. The explicit password env overrides are required: .env.localtest sets DB_PASSWORD_* to /run/secrets paths that don't exist on the host, and the secret provider then falls back to those literal strings.
  • Server type-check needs a big heap: NODE_OPTIONS=--max-old-space-size=16384 npx tsc --noEmit -p tsconfig.json (default heap OOMs).
  • Dev stack for this worktree: alga-dev-env-manager / alga-env-manager skills.
  • Design doc: docs/plans/2026-06-10-ticket-close-rules-design.md (commit 1a64dccb0d)
  • Prior art for plan format: docs/plans/2026-05-15-outbound-webhooks-for-projects/

Known issues (not from this feature)

  • commentActionsThreading.integration.test.ts T078 fails on this branch AND on the pre-implementation commit (cade9ab5a5): its cleanup deletes users that ticket_audit_logs rows reference (ticket_audit_logs_actor_user_fkey). Pre-existing; not introduced by close rules.

Open Questions

  • Required-fields allowed set frozen at category/subcategory/priority/assignee for v1 — revisit if customers ask for custom fields.
  • Mobile/REST checklist CRUD endpoints deferred (non-goal §3) — revisit when mobile wants checklists.
  • Email-created tickets: auto-apply hooks TicketModel.createTicket, the single chokepoint all creation paths (UI, API, CSV, inbound email) funnel through — resolved.
  • The progress chip renders in the banner row above TicketInfo rather than literally beside the status dropdown (which lives deep in TicketInfo); revisit if the placement doesn't land.