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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
65 lines
15 KiB
JSON
65 lines
15 KiB
JSON
[
|
|
{ "id": "F001", "description": "Migration: create comment_threads table with tenant/thread_id PK, polymorphic ticket_id/project_task_id (CHECK exactly one), root_comment_id, is_internal, reply_count, last_activity_at, email_message_id, email_references[], email_provider_thread_id, FKs to tickets and project_tasks", "implemented": true, "prdRefs": ["Data / API / Integrations"] },
|
|
{ "id": "F002", "description": "Migration: indexes on comment_threads — (tenant, ticket_id, last_activity_at DESC), (tenant, project_task_id, last_activity_at DESC), partial (tenant, email_message_id) WHERE email_message_id IS NOT NULL", "implemented": true, "prdRefs": ["Non-functional Requirements"] },
|
|
{ "id": "F003", "description": "Migration: add thread_id (nullable), parent_comment_id (nullable), deleted_at (nullable) columns to comments; FKs to comment_threads and self-FK to comments", "implemented": true, "prdRefs": ["Data / API / Integrations"] },
|
|
{ "id": "F004", "description": "Migration: add thread_id (nullable), parent_comment_id (nullable), deleted_at (nullable) columns to project_task_comments; analogous FKs", "implemented": true, "prdRefs": ["Data / API / Integrations"] },
|
|
{ "id": "F005", "description": "Migration: backfill — for each existing comments row, insert a comment_threads row (root_comment_id=comment_id, is_internal=root.is_internal, reply_count=0, last_activity_at=created_at, email_message_id=metadata->'email'->>'messageId') and set comment.thread_id; chunked + idempotent", "implemented": true, "prdRefs": ["Functional Requirements §3", "Rollout / Migration"] },
|
|
{ "id": "F006", "description": "Migration: backfill the same way for project_task_comments → comment_threads with project_task_id set; chunked + idempotent", "implemented": true, "prdRefs": ["Functional Requirements §3", "Rollout / Migration"] },
|
|
{ "id": "F007", "description": "Migration: enforce NOT NULL on comments.thread_id and project_task_comments.thread_id (separate migration after backfill)", "implemented": true, "prdRefs": ["Rollout / Migration"] },
|
|
{ "id": "F008", "description": "Migration: add comment_thread_id column on email_sending_logs (FK to comment_threads.thread_id); leave existing thread_id column as email_provider_thread_id (or rename in same migration if safe)", "implemented": true, "prdRefs": ["Functional Requirements §11"] },
|
|
{ "id": "F009", "description": "Types: create ICommentThread interface in packages/types/src/interfaces/commentThread.interface.ts covering all comment_threads columns", "implemented": true, "prdRefs": ["TypeScript Interfaces"] },
|
|
{ "id": "F010", "description": "Types: extend IComment with thread_id (string), parent_comment_id (string|null), deleted_at (string|null)", "implemented": true, "prdRefs": ["TypeScript Interfaces"] },
|
|
{ "id": "F011", "description": "Types: extend IProjectTaskComment with thread_id, parent_comment_id, deleted_at", "implemented": true, "prdRefs": ["TypeScript Interfaces"] },
|
|
{ "id": "F012", "description": "Model: comments.createComment — when parent_comment_id NULL, create a new comment_threads row in same transaction, set comment.thread_id to new id, root_comment_id to comment_id", "implemented": true, "prdRefs": ["Functional Requirements §4"] },
|
|
{ "id": "F013", "description": "Model: comments.createComment — when parent_comment_id set, look up parent (same tenant + ticket), inherit thread_id, validate parent not deleted, validate is_internal compatibility with thread root", "implemented": true, "prdRefs": ["Functional Requirements §5", "Security / Permissions"] },
|
|
{ "id": "F014", "description": "Model: comments — on insert of a reply, increment comment_threads.reply_count and bump last_activity_at in the same transaction", "implemented": true, "prdRefs": ["Functional Requirements §5", "Non-functional Requirements"] },
|
|
{ "id": "F015", "description": "Model: comments.deleteComment — if leaf (no children), hard-delete and decrement reply_count; if root-with-children or interior, soft-delete (set deleted_at, replace note with '[deleted]')", "implemented": true, "prdRefs": ["Functional Requirements §6"] },
|
|
{ "id": "F016", "description": "Model: comments.findCommentsByTicketId returns thread_id + parent_comment_id + deleted_at; ordered by created_at asc; includes soft-deleted comments so tree stays well-formed", "implemented": true, "prdRefs": ["Functional Requirements §6"] },
|
|
{ "id": "F017", "description": "Model: projectTaskComment.createTaskComment — same NULL-parent thread creation flow", "implemented": true, "prdRefs": ["Functional Requirements §4", "Functional Requirements §12"] },
|
|
{ "id": "F018", "description": "Model: projectTaskComment.createTaskComment — same parent-inheritance flow with validation", "implemented": true, "prdRefs": ["Functional Requirements §5"] },
|
|
{ "id": "F019", "description": "Model: projectTaskComment — reply_count + last_activity_at maintenance on insert", "implemented": true, "prdRefs": ["Functional Requirements §5"] },
|
|
{ "id": "F020", "description": "Model: projectTaskComment.deleteTaskComment — leaf vs root-with-children soft-delete behavior mirroring tickets", "implemented": true, "prdRefs": ["Functional Requirements §6"] },
|
|
{ "id": "F021", "description": "Model: projectTaskComment.getTaskComments returns thread_id + parent_comment_id + deleted_at", "implemented": true, "prdRefs": ["Functional Requirements §6"] },
|
|
{ "id": "F022", "description": "Action: commentActions.createComment accepts optional parent_comment_id in payload; passes through to model", "implemented": true, "prdRefs": ["Server Actions"] },
|
|
{ "id": "F023", "description": "Action: commentActions — TICKET_COMMENT_ADDED event payload includes thread_id, parent_comment_id, is_reply", "implemented": true, "prdRefs": ["Server Actions"] },
|
|
{ "id": "F024", "description": "Action: commentActions — RBAC reaffirms is_internal compatibility with thread root and rejects internal reply on client thread (or vice versa)", "implemented": true, "prdRefs": ["Security / Permissions"] },
|
|
{ "id": "F025", "description": "Action: projectTaskCommentActions.createTaskComment accepts optional parent_comment_id", "implemented": true, "prdRefs": ["Server Actions"] },
|
|
{ "id": "F026", "description": "Action: projectTaskCommentActions — TASK_COMMENT_ADDED event payload includes thread_id, parent_comment_id, is_reply", "implemented": true, "prdRefs": ["Server Actions"] },
|
|
{ "id": "F027", "description": "Action: projectTaskCommentActions.assertOwnCommentOrInternalUser handles replies (own reply or internal user)", "implemented": true, "prdRefs": ["Security / Permissions"] },
|
|
{ "id": "F028", "description": "Email inbound: reply-token resolution branch — when token resolves to a comment, derive thread_id from that comment and create reply as child of latest comment in thread", "implemented": true, "prdRefs": ["Functional Requirements §10.1"] },
|
|
{ "id": "F029", "description": "Email inbound: In-Reply-To resolution — look up rfc_message_id in email_sending_logs, get comment_thread_id, create reply as child of latest comment in thread", "implemented": true, "prdRefs": ["Functional Requirements §10.2"] },
|
|
{ "id": "F030", "description": "Email inbound: References[] chain walk — iterate from last to first; first match in email_sending_logs.rfc_message_id wins", "implemented": true, "prdRefs": ["Functional Requirements §10.3"] },
|
|
{ "id": "F031", "description": "Email inbound: provider thread id match — direct equality against comment_threads.email_provider_thread_id", "implemented": true, "prdRefs": ["Functional Requirements §10.4"] },
|
|
{ "id": "F032", "description": "Email inbound: fallback — existing ticket-level match creates a NEW top-level thread on the matched ticket (preserves today's UX)", "implemented": true, "prdRefs": ["Functional Requirements §10.5"] },
|
|
{ "id": "F033", "description": "Email inbound: comment creation populates thread_id + parent_comment_id from resolved thread (parent = latest comment in thread)", "implemented": true, "prdRefs": ["Functional Requirements §10"] },
|
|
{ "id": "F034", "description": "Email outbound: when sending a top-level comment-as-email, generate a fresh RFC Message-ID and persist into comment_threads.email_message_id; no In-Reply-To header", "implemented": true, "prdRefs": ["Functional Requirements §11"] },
|
|
{ "id": "F035", "description": "Email outbound: when sending a reply in an existing thread, set In-Reply-To to the latest outbound rfc_message_id for that thread (lookup email_sending_logs by comment_thread_id, ORDER BY created_at DESC LIMIT 1)", "implemented": true, "prdRefs": ["Functional Requirements §11"] },
|
|
{ "id": "F036", "description": "Email outbound: accumulate References[] header — append to comment_threads.email_references and emit the array as RFC References", "implemented": true, "prdRefs": ["Functional Requirements §11"] },
|
|
{ "id": "F037", "description": "Email outbound: issue a reply_token in email_reply_tokens scoped to the new outbound comment_id; embed [ALGA-REPLY-TOKEN:...] marker as today", "implemented": true, "prdRefs": ["Functional Requirements §11"] },
|
|
{ "id": "F038", "description": "Email outbound: persist email_sending_logs row with comment_thread_id, rfc_message_id, reply_token_hash, provider_message_id", "implemented": true, "prdRefs": ["Functional Requirements §11"] },
|
|
{ "id": "F039", "description": "Shared UI: new CommentThreadList component that takes flat comments[], groups by thread_id, renders one HybridThreadGroup per thread sorted by thread.last_activity_at", "implemented": true, "prdRefs": ["UX / UI Notes"] },
|
|
{ "id": "F040", "description": "Shared UI: new HybridThreadNode (recursive) — renders a comment, then thread bar (if has children), then indented children. Caps margin-left at depth 4", "implemented": true, "prdRefs": ["UX / UI Notes", "Functional Requirements §13"] },
|
|
{ "id": "F041", "description": "Shared UI: HybridThreadNode collapse state — Collapse button when expanded; Expand + Open in drawer when collapsed", "implemented": true, "prdRefs": ["Users and Primary Flows §2"] },
|
|
{ "id": "F042", "description": "Shared UI: HybridThreadNode 'Open in drawer' action — calls onOpenPanel(commentId) prop, lifted to parent container", "implemented": true, "prdRefs": ["Users and Primary Flows §3"] },
|
|
{ "id": "F043", "description": "Shared UI: InlineReplyComposer — small composer rendered under a comment when Reply is clicked; uses TextEditor (BlockNote); shows Mark-as-Internal switch only (no Mark-as-Resolution); inherits parent's is_internal as default", "implemented": true, "prdRefs": ["Functional Requirements §8", "Functional Requirements §9"] },
|
|
{ "id": "F044", "description": "Shared UI: CommentThreadDrawer — opens via existing packages/ui Drawer; shows thread head + all replies + bottom composer; close returns to inline view", "implemented": true, "prdRefs": ["Users and Primary Flows §3"] },
|
|
{ "id": "F045", "description": "Shared UI: CommentThread.module.css — indent left rail (2px #E5E7EB), thread-bar pill, depth-{1..4} indent classes (cap), sub-thread dashed-border style, hover Reply button styles", "implemented": true, "prdRefs": ["UX / UI Notes"] },
|
|
{ "id": "F046", "description": "Tickets UI: CommentItem — add Reply button (CornerUpLeft icon) to the hover action row, between React and Pencil; calls onReply(comment) prop", "implemented": true, "prdRefs": ["UX / UI Notes", "Users and Primary Flows §1"] },
|
|
{ "id": "F047", "description": "Tickets UI: TicketConversation — replace flat .map render with <CommentThreadList comments={...} />; preserve existing tabs, composer, sort toggle", "implemented": true, "prdRefs": ["Users and Primary Flows §1"] },
|
|
{ "id": "F048", "description": "Tickets UI: TicketConversation — tab filtering operates on threads (All / Client / Internal / Resolution per PRD rules)", "implemented": true, "prdRefs": ["Functional Requirements §7"] },
|
|
{ "id": "F049", "description": "Tickets UI: TicketConversation — sort order toggle reorders threads by last_activity_at; within-thread replies stay chronological", "implemented": true, "prdRefs": ["UX / UI Notes"] },
|
|
{ "id": "F050", "description": "Tickets UI: TicketConversation — wire drawer state (openPanelRoot) and pass onOpenPanel down through CommentThreadList", "implemented": true, "prdRefs": ["Users and Primary Flows §3"] },
|
|
{ "id": "F051", "description": "Tickets UI: hovering a comment shows Reply; clicking opens InlineReplyComposer beneath the comment", "implemented": true, "prdRefs": ["Users and Primary Flows §1"] },
|
|
{ "id": "F052", "description": "Tickets UI: submitting an inline reply calls commentActions.createComment with parent_comment_id, refreshes the local conversation list, closes the composer", "implemented": true, "prdRefs": ["Users and Primary Flows §1"] },
|
|
{ "id": "F053", "description": "Tickets UI: when drawer is open, replying inside drawer also calls createComment with parent_comment_id; on close, inline list reflects the new reply", "implemented": true, "prdRefs": ["Users and Primary Flows §3"] },
|
|
{ "id": "F054", "description": "Project Tasks UI: TaskComment — add Reply button on hover", "implemented": true, "prdRefs": ["Functional Requirements §12"] },
|
|
{ "id": "F055", "description": "Project Tasks UI: TaskCommentThread — replace flat list render with <CommentThreadList>", "implemented": true, "prdRefs": ["Functional Requirements §12"] },
|
|
{ "id": "F056", "description": "Project Tasks UI: TaskCommentForm — pass parent_comment_id when invoked as reply; default inherits is_internal=false (tasks have no internal/client split)", "implemented": true, "prdRefs": ["Functional Requirements §12"] },
|
|
{ "id": "F057", "description": "Project Tasks UI: drawer wiring identical to ticket flow", "implemented": true, "prdRefs": ["Users and Primary Flows §3"] },
|
|
{ "id": "F058", "description": "UI: soft-deleted comments render as '[deleted]' placeholder with reduced opacity; Reply button suppressed; preserves tree structure", "implemented": true, "prdRefs": ["Functional Requirements §6"] },
|
|
{ "id": "F059", "description": "UI: depth-4 cap in CSS — .depth-4 .thread-children { margin-left: 16px; } and no further indent past it; data still nests", "implemented": true, "prdRefs": ["Functional Requirements §13"] },
|
|
{ "id": "F060", "description": "UI: subthread bars (depth >= 1) use dashed border + white background per design; indigo-violet count color", "implemented": true, "prdRefs": ["UX / UI Notes"] },
|
|
{ "id": "F061", "description": "Persistence: ticket conversation 'oldest/newest first' preference applies at thread granularity (existing ticketConversationOrderPreference helper continues to work; just consumed at thread sort time)", "implemented": true, "prdRefs": ["UX / UI Notes"] },
|
|
{ "id": "F062", "description": "Seed compatibility: update dev comment seed to create comment_threads rows and set comments.thread_id after NOT NULL enforcement", "implemented": true, "prdRefs": ["Rollout / Migration"] }
|
|
]
|