feat(features,gateway,host,desktop): a window writes any trigger, says it in one sentence, and copies a webhook's fresh secret through the host

Merged
DA
4d ago
+441-83

Summary

Part 1, step 3a of Dean's "GitHub events and webhooks". This is the plumbing behind the editor's Trigger section; the section itself is the next PR. Stacked on #2770.

  • The window can write any trigger. automation-view.ts drops its schedule-only filter, so a window's create or change may name a GitHub event or a webhook.
  • The draft model holds the trigger (automation-draft-model.ts): its kind, plus a GitHub event (a new draft's is pull-request-updated).
    • A GitHub trigger keeps the label and branch filters it was given, which the editor doesn't show.
    • A save sends the trigger only where its kind, its schedule, or its event moved.
  • One sentence per trigger (automationTriggerText), for the list and the editor:
    • "When a pull request is opened or updated in acme/ledger";
    • with filters: "… in acme/api, acme/web, or acme/ledger labelled bug or p1 on main";
    • "When its webhook is called".
    • The repository-changed reason now reads "It no longer works in the repository the event came from."
  • The webhook secret never enters a Gateway answer (root AGENTS.md's "Never").
    • A new automations.copyWebhookSecret method has the host mint the secret (32 random bytes) and put it on the system clipboard, through the native node's new os.copyText capability. Only then does it send the service the secret's SHA-256 (PATCH { webhookSecretHash }, #2770).
    • A clipboard that refuses replaces nothing, and the old secret keeps working (Devin's review). The service never sees the secret.
    • The window hears { automation, secretCopied: true }, never the secret.
    • The secret a create mints is still dropped by the client, as today.
    • Node invocations aren't events and travel only on the node's own connection, and the dev trace taps only voice data-channel events.
  • The list's rows say each trigger as that sentence, naming the repository.

Evidence

  • Platform-independent checks: pnpm check:affected passes.
  • New tests, each failing without its change:
    • compose-automations.test.ts: the secret is on the clipboard before its hash alone is sent (the hash matches what was copied), the window hears only secretCopied, no answer carries the secret, and a refusing clipboard asks the service nothing.
    • automation-client.test.ts: the replace PATCHes { webhookSecretHash }, and no answer carries a service-minted secret.
    • automations-page.test.ts: a GitHub automation's row says the sentence, naming its repository.
    • automation-draft-model.test.ts: a GitHub-event or webhook draft is made and changed as one, and a GitHub trigger keeps its filters.
    • automation-list-model.test.ts: the GitHub and webhook sentences.
    • automation-acts.test.ts: the voice and call windows are refused the new act.
  • Changed: the client test that asserted "the window writes a schedule alone" now asserts a webhook write travels, the deliberate change of this PR.
  • Test count: +5.

Invariants

  • ./scripts/check.sh green. (CI)
  • JSON Schema goldens unchanged.
  • Every behavior change has, in this PR, the test that fails without it; no change-detector test added.
  • Gateway envelope goldens unchanged. The new method's answer carries no secret.
  • No new as assertion, no run outside the edges, no raw async primitives, and no dropped Effect.
  • Test count delta listed (+4).
  • No hand-rolled file replaced.
  • No CLAUDE.md/AGENTS.md sentence names a changed part. The "Never" rule holds by design: the secret goes to the clipboard, not a Gateway answer.
  • PRIVACY.md unchanged.
  • Package deps unchanged.
  • Barrel/door rule: the renderer imports only @luke/features/automation-* doors and @luke/host/acts.

Notes

  • A deliberate deviation from "shown once with Copy": the window never shows the secret. Copy mints a fresh one onto the clipboard, and the old one stops working once the new one's hash lands, because AGENTS.md forbids a credential in a Gateway answer and Dean approved only the body carve-out. To show the text itself, Dean would need to approve a second carve-out.
  • Mac: this PR has no visible UI change (the editor compiles against the new draft). CI could not verify the Mac UI.

šŸ¤– Generated with Claude Code