Domain migration. Depends on D2 and PAR-412. Backend for PAR-403 (triage queue) and PAR-404 (Discord source card).
triage domain:
ListTriageQueue (source message, bot summary, suggested labels, priority and project, possible duplicates from PAR-228's semantic search)AcceptTriage, MergeIntoIssue (sets the duplicate relation), DeclineTriage(reason) and SnoozeTriage(until)triage/ai, through a platform OpenAI gateway (model from config, timeout, cost metric). That's rule 23.TaskInstructionListener, GuildJoinListener and the rest become thin. They build the RequestContext from the guild and user, and call facades.
DiscordDeliveryQueue.discord/ or workspaces settings.Plan (branch par-420-discord-triage from rebuild). Scope is large, so it ships as two PRs, the second rebased on the first.
PR 1: triage domain + API + web
triage/TriageFacade is the only entry. It holds two things: (a) the assistant, i.e. the existing @mention//task decide + apply flow, now ai/DecideInstruction (LLM through a new platform/ai/AiGateway port, model from config, rule 23) and usecase/ApplyDecision, with no TaskSystem; (b) the new queue: ListTriageQueue, AcceptTriage, MergeIntoIssue, DeclineTriage(reason), SnoozeTriage(until).triage, enriched by a new tenant-scoped triage_item table (V43: Discord source message, bot summary, suggested labels/priority/project, snooze, decision). Possible duplicates come from the existing SemanticIssueSearch / IssuesFacade.rankedSearch. Issue changes go through IssuesFacade, comments through ActivityFacade.addComment (Discord provenance). "Merge" = move to Canceled, comment on both issues, record merged_into; no relation table exists yet./api/public/w/{slug}/triage (GET @MembersOnly; writes need issue.update; added to GuestReadPathTest). No MCP triage tools exist today, so none are added.discord.thread_reply (JSON contract; the handler is PR 2).domains/triage (data + the PAR-403 two-pane page), sidebar triage count from the API.TriageService entries (applyCreate, applyUpdate, search, task, constructor).PR 2: Discord adapters + outbox
discord/DiscordFacade: guild to workspace resolution, channel routes and role allow-list (replaces GuildConfigService + the Discord half of IntegrationApiController). It lives in discord, not workspaces: the data is Discord-only and the delivery code needs it.RequestContext.discord(...) from the guild and user (workspace by guild, never TaskSystem.defaultWorkspaceId), validate, call one facade method, reply. No ThreadLocals.discord.thread_reply, discord.activity_flush). DiscordDeliveryQueue is replaced by a digest buffer in discord/persistence plus the flush handler./api/public/w/{slug}/discord… (settings.manage); web integrations page reads and writes through the API, and the Discord half of lib/sql/integrations.ts is removed.PR 1 of 2 (triage domain, queue API, triage screen): https://github.com/ParadauxIO/taskl.ie/pull/62 (against rebuild). Part 2 (Discord domain, outbox, integrations settings) is stacked on it and follows.
PR 2 of 2 (Discord domain, thin listeners, outbox delivery, integrations settings): https://github.com/ParadauxIO/taskl.ie/pull/65, stacked on #62. Includes the bridge so the deployment's configured server keeps filing into its workspace.
Both parts are merged into rebuild: #62 (triage) and #65 (Discord).
What changed
tasklie.discord.guild-id → tasklie.tasks.tenant-slug), which keeps filing as before. A test covers this.Ratchet
Follow-ups
Notes from the PAR-415 merge (#54):
TriageService(applyCreate,applyUpdate,search,task→IssueError,TaskModels.Task) andTaskInstructionListener(theTaskSystemfield and constructor,contextFor→TaskSystem.defaultWorkspaceId).IssuesFacade, andissues/event/IssueEventsrecords an event together with its outbox message.