Build Triggers console and source display

Task 5366 · Done · Coder · created · updated

Epic 5336 — Event-driven Task triggers. Upstream: PRD = Task 5337 `prd.md`; Architecture = Task 5338 `architecture.md` (artifact 3566). Read §0 (row on `/triggers`), §3.5 (the API you consume, already merged) and §7 IN FULL. ## Original requirement (epic body, verbatim) > A `task_triggers` row beside the cron row. … health: Active / Degraded / Failed / Paused with reason … a deliveries log (dispatched, filtered, deduped, rate-capped, skipped) … a `source` column on the filed Task so the board filters by origin. ## Requirements (PRD, verbatim) **R1** A `task_triggers` row sits beside `crons`. … It carries a source kind, a source config, a target Task shape, preflight, limits, health and state. **R4** Every source renders a template from its event. Gmail exposes `{{email.from}}`, `{{email.subject}}` and `{{email.body}}`. Slack exposes `{{slack.text}}`, `{{slack.user}}`, `{{slack.channel}}` and `{{slack.permalink}}`. GitHub exposes `{{body.…}}` and `{{headers.…}}` as a webhook does. Preflight items expose `{{item.id}}` and `{{item.…}}`. **R14** One row per inbound delivery, or per preflight item. It records time, outcome, reason, item id and the filed Task link. … The owner can filter the log by outcome. Bodies are stored redacted of auth headers, and truncated to a readable preview. **R17** … A trigger-filed Task also links to its trigger. ## Approach (architecture §7, settled) - Nav: `AUTOMATION_TABS` gains `{id:'triggers', label:'Triggers', path:'/triggers', orgAdminOnly:true}`; `routeGroups` gains `AUTOMATION_TAB_PATHS.triggers`. Routes `/triggers`, `/triggers/new`, `/triggers/:id` guarded by `RequireSuperadmin orgAdmin` (twin of Schedule). `WorkflowsPage` adds `triggers` to its legacy-tab redirects, so `/workflows?tab=triggers` lands on `/triggers` (`/workflows/:name` is taken). - List: name **wraps** to two lines and is never ellipsized below 24ch; project; source as icon + label; health badge **with the reason as visible text**; last delivery relative, absolute on hover. Under 640 px → cards; name + health always show; no horizontal scroll. - Detail page = sections, not hidden tabs: Setup (URL, secret reveal/rotate), Configuration editor for ALL FOUR source kinds' `source_config` (webhook auth; GitHub events/actions/repository; Gmail to/from/subject; Slack channel ids), persona/model/Full Auto (with the prompt-injection help text from §11 risk 2)/hidden, preflight fields, limits, auto-pause settings; template field + **Test** panel (sample → rendered title, body, missing paths); Health with Pause/Resume; Deliveries log with outcome filter chips, readable reason, `#<id> <task title>` link, expand to preview; State document JSON editor with validation + 64 KiB counter; Dedup entries with Clear. Every empty state is a sentence. - Secret: shown once in a dialog after create/rotate with Copy + "I've stored it"; afterwards "Secret hidden — rotate to get a new one"; a trigger whose secret is still unrevealed (`secret_revealed_at` null, e.g. advisor-created) shows "Reveal secret (one time)". - No internal codes shown: `rate_capped` displays as "Rate capped: 10/hour reached"; no truncated ids in place of names. - Task detail: "Source: Webhook · Trigger <name>" linking to the trigger (the budget flag is the health child's). - R16 is dropped: NO memory or last-report section, even though AC-1.2's text lists one. ## Acceptance (PRD, verbatim) - **AC-1.1** On dev, `/workflows` has a **Triggers** tab beside the Cron tab, and it is ON by default with no flag. An org admin can create, edit, pause, resume and delete a trigger of each in-scope source kind (webhook, GitHub, Gmail, Slack). The list shows each trigger's name (readable, never truncated to an identifier), project, source, health badge with its reason, and last-delivery time. → verify: open dev `/workflows?tab=triggers` as an org admin. Create one trigger per source kind. Observe all four in the list with readable names and an Active badge. - **AC-1.2** A trigger's detail page shows its config, its health and reason, its deliveries log (R14), its state document (R10), ~~its memory and last report (R16)~~, and its dedup entries (R9). → verify: open each trigger created in AC-1.1 and observe every one of those sections, each with an empty state that says so in words. - **AC-1.3** A user who is not an org admin cannot reach the create/edit controls or the API. → verify: as a non-admin member, open the tab and observe no create control. - **AC-1.4** The page is legible at the supported viewport widths (`docs/standards/product-testing-requirements.md`), with no horizontal page scroll. → verify: a Playwright run at each supported width; observe no overflow and readable names. - **AC-4.1** The editor offers a **Test** action. It renders the template against a pasted sample payload and shows the result without filing anything. → verify: on dev, paste a sample and observe the rendered body. No Task is created. - **AC-17.1 (end-to-end re-verify)** `/tasks` on dev has a **Source** filter … → verify: filter to "Webhook" and observe only trigger-filed Tasks. Reload the URL and observe the filter kept. - **AC-17.2** A trigger-filed Task's detail shows its source and a link to the trigger. → verify: on dev. Out of scope: Do NOT change the §3.5 API contract (consume it; a genuine gap is a named follow-up, or a minimal additive field). Do NOT build GitHub setup instructions panel content (GitHub child), the Gmail unmatched panel (Gmail child), the Task-detail budget flag (health child), notices/bell kinds, or advisor verbs. No memory/last-report UI (R16 dropped). PRD §5 (verbatim, binds): No deferred source in this epic. No skill attachment. No budget enforcement. No GitHub reads that decide filing. No public exposure beyond the two fenced paths. Cron behaviour is unchanged (do not alter the Cron tab). No new licence for any persona other than advisor. Slack: no Events API. Secrets: the secret appears only in the one-time dialog — never in a list/detail read, URL, localStorage or log. Done means: - AC-1.1 — on dev in a real browser as an org admin: `/workflows?tab=triggers` redirects to `/triggers`; create one trigger of each of the four kinds (Gmail in the operator org); observe all four listed with full readable names, project, source label, "Active" badge, last delivery; edit, pause (badge shows "Paused by <name> …" as text), resume and delete one. Screenshot evidence. - AC-1.2 — open each; observe config, health+reason, deliveries, state document, dedup sections, each empty state a sentence; no memory section. - AC-1.3 — as a non-admin member: no Triggers tab / no create control. - AC-1.4 — Playwright at 448 and 1024 (the supported widths): no horizontal overflow; a 60-character trigger name wraps and stays fully readable (not ellipsized to a fragment); health reason visible as text. - AC-4.1 — paste `{"issue":{"title":"Login broken"}}` against `Fix: {{body.issue.title}}`: observe "Fix: Login broken", missing paths listed for a bad path, and no Task created. - Secret dialog: create → shown once with Copy; reload → "Secret hidden — rotate to get a new one"; rotate → new secret once. - Deliveries log on a dev webhook trigger with real deliveries (post a few signed requests, incl. a bad signature and a dedup): each outcome shows a readable reason (no raw codes) and a working `#<id> <title>` link; outcome chips filter. - AC-17.1 end to end: filter `/tasks` to "Webhook", observe only trigger-filed Tasks; reload, filter kept. AC-17.2: a trigger-filed Task's detail shows "Source: Webhook · Trigger <name>" and the link opens the trigger. - `./dc check full` green.

Outcome

Built the Triggers console (list, sectioned detail page, Test panel, deliveries/state/dedup) and the task Source field. Full gate and CI green; PR #4691 merged with human security approval. Checked live on a local stack; dev unreachable, re-check there after deploy.

Pull request

Artifacts

Activity

  • Draft
  • Ready
  • Runner started
  • Awaiting response
  • Awaiting response
  • Runner resumed
  • Runner ended (done)