Automations and webhooks for the AT Protocol airglow.run

Render handles for DIDs in post templates #36

Closed tyb.there-hugo.dev
Closed opened by tyb.there-hugo.dev

Request #

Offer a placeholder or filter that turns a DID into its handle (or a mention) in post text.

Context #

From https://www.youtube.com/watch?v=5MnT-ZgbBIc, where the user built an automation that adds conference attendees to a Bluesky list:

  • The test post showed a bare DID, which is unreadable.

Details: docs/brainstorms/2026-10-17-graze-conference-automation-feedback.md (F8).

Queued: the loop will work on this version of the issue.

loop:queued cid=bafyreialb5noqemoabs2kdxi7m6c2jqdm4rxbk5j47hdpxbgpjifx5zuce

Working on this, attempt 1.

loop:claim run=contribute-20261023-214940-3my32gp7omk22 branch=loop/3my32gp7omk22 attempt=1 issue=36

feat(automations): surface didToHandle so post templates can mention DIDs by handle

The engine could already turn a DID into a handle: {{didToHandle(event.did)}} renders @handle, and the Bluesky post action then turns that @handle into a mention. But the form never showed it, so the user in the feedback session posted a bare DID. This branch shows the function in the form: there is a new "Handles" group in each action step's "Available placeholders" panel, and the Bluesky post text field now has a hint about it. A new test runs a post through the engine to prove the DID comes out as a handle.

What changed #

  • app/islands/AutomationForm.tsx: the per-step placeholders panel gets a "Handles" group with click-to-copy chips for {{didToHandle(event.did)}} and {{didToHandle(self)}}. The group appears only on non-processor action steps. Conditions, fetches, variables, loops and the processor steps (AI, collect links) refuse function calls when you save, so they don't show it.
  • app/islands/action-editors/bsky-post.tsx: the post-text hint now says how to mention someone by DID.
  • Tests:
    • AutomationForm.test.tsx checks that the chips appear on a Bluesky post step but not on a condition or AI step, and that the post editor shows the hint.
    • bsky-post.test.ts checks that Welcome {{didToHandle(event.did)}}! publishes Welcome @alice.test! and that this exact text goes to facet detection.

Verification #

  • pnpm run check: passes (format, lint, types).
  • pnpm run test: 161 files, 2520 tests pass.
  • I did not test this by hand in a browser. This sandbox has no dev server or local PDS.

Acceptance criteria #

Decisions the owner can reverse #

  • I made the existing didToHandle(...) function easier to find instead of adding a new placeholder or filter syntax (such as {{event.did | handle}}). One way to do this keeps the template language small.
  • The chips appear on every non-processor action step, not only Bluesky posts. Notification, record and webhook-body templates accept the function too. A few single fields inside those actions still refuse it, such as a webhook callback URL or a patch-record base URI. In those fields the user gets the save-time error.
  • If a handle can't be resolved, the text falls back to @did:.... That fallback is existing behaviour and isn't linked as a mention.

Review: https://tangled.org/exosphere.site/airglow/compare/main...loop/3my32gp7omk22

loop:pushed run=contribute-20261023-214940-3my32gp7omk22 sha=098f8db4b282f4900b326a075dd895425b664059

Verdict: merge. The engine could already render a DID as a handle with {{didToHandle(...)}}, which gives @handle, and the Bluesky post action turns that into a mention. The issue was that the form never showed the function. This branch adds it to the per-step placeholders panel and to the post-text hint, and adds an end-to-end test from template to published post. That covers the issue's request without growing the template language. pnpm run check and pnpm run test (161 files, 2520 tests) both pass on 098f8db.

What I checked:

  • lib/actions/template.ts: didToHandle is a known function. validateTextTemplate and validateTemplate allow it in action templates. self resolves to the automation DID, so the {{didToHandle(self)}} chip works.
  • Conditions, fetches, variables and the AI and collect-links processors pass allowFunctions: false. The panel gating d.kind === "action" && !isProcessorAction(...) matches this. A Handles group with no chips is hidden by StepPlaceholdersPanel, which filters out empty groups.
  • New tests: the form test checks that the chips appear on the post step but not on the condition or AI step, and that the hint text is shown. The bsky-post test mocks resolveDidToHandle through the @/ alias, which resolves to the same module template.ts imports. It checks that Welcome @alice.test! reaches both facet detection and createRecord.
  • Nit, not blocking: the chips also appear on steps like follow and clear-scheduled, where an @handle output means little. The PR lists this as a decision the owner can reverse.

loop:verdict run=review-20261023-215642-3my32gp7omk22 sha=098f8db4b282f4900b326a075dd895425b664059 verdict=merge

Merged onto main as 423db2c787c6f7ecfa5d3521a039404060555b23.

loop:merged sha=423db2c787c6f7ecfa5d3521a039404060555b23 head=098f8db4b282f4900b326a075dd895425b664059

Sign up or Sign in to add to the discussion.