Automations and webhooks for the AT Protocol airglow.run

Copied placeholders include braces, but condition fields reject braces #17

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

Problem #

Click-to-copy puts {{event.did}} on the clipboard, but condition fields expect a bare path. The condition panel doesn't use the bare variant (AutomationForm.tsx:296-306; hint at AutomationForm.tsx:1504/1604-1608).

Pasting the braced form gives cryptic errors: "Schema unavailable: all operators shown..." or references "{{event", but no step defines it (step-drafts.ts:767).

Related: pasting {{...}} as a variable name shows no visible error (AutomationForm.tsx:2065-2090, seen at 0:47:20).

Seen in the session #

In https://www.youtube.com/watch?v=5MnT-ZgbBIc:

  • 1:05: "you can copy it and it copies it with the brackets. But then if I had the brackets in there, it angry about it."

Fix direction #

Copy bare paths in condition contexts, or strip the braces on paste and accept both forms.

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

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

loop:queued cid=bafyreig44v5iirpt4jjxmuzw7eumr2vmqckauqlelrd5ioduilk6c3lzwe

Working on this, attempt 1.

loop:claim run=contribute-20261024-151315-3my32g5t66f22 branch=loop/3my32g5t66f22 attempt=1 issue=17

fix(form): accept a copied {{path}} placeholder in condition fields and variable names

Placeholder chips copy {{event.did}}, but a condition's check field and a variable's name take a bare path or name, so pasting a chip produced cryptic errors ("Schema unavailable...", references "{{event", but no step defines it). Both inputs now accept either form: a value that is exactly one {{path}} placeholder is unwrapped to path as it is typed or pasted. A variable name the pattern rejects now also shows an inline error under the input instead of failing silently.

What changed #

  • app/islands/step-drafts/references.ts: new unwrapPlaceholder(text). It returns the inner path when the whole text is one {{...}} placeholder (surrounding whitespace allowed), and returns anything else unchanged (partial input, mixed text, two placeholders, {{}}).
  • app/islands/AutomationForm.tsx: the condition field and variable name inputs read through readUnwrapped. It also writes the bare value back into the DOM input, because when the state already holds that value no re-render would fix what the input shows. The variable name gets an inline errorText message, with aria-invalid/aria-describedby, when the trimmed name does not match the step-name pattern.
  • Condition values are templates, so they keep their braces. That is why the fix unwraps on paste and the chips still copy the braced form.

Verified #

  • New tests in app/islands/AutomationForm.test.tsx: pasting {{event.did}} into a condition field leaves event.did in the input and in the payload, with no "Schema unavailable" or reference error. Pasting a placeholder that equals the current field fixes the DOM. A condition value keeps {{self}}. A pasted {{listUri}} variable name becomes listUri. list-uri shows the inline error and list_uri clears it.
  • New unit tests for unwrapPlaceholder in app/islands/step-drafts/references.test.ts.
  • pnpm run check: passes. pnpm run test: 161 files, 2545 tests pass.

Acceptance (from the issue's fix direction) #

Decisions the owner can reverse #

  • I chose "strip on paste" over "copy bare in condition contexts", because the condition panel's chips are also meant for the value field, which is a template and needs the braces.
  • The braces are stripped as the input changes, so the author sees the bare form right away. Nothing is normalized at save time, and the server validator is unchanged.
  • The inline name error is only on the variable step, which is the field the issue names. The other reference-name inputs keep only the native pattern check.

Follow-up, filed as a new issue on merge if the review agrees:

fix(form): unwrap a pasted {{path}} in the loop path and the other step-name inputs

Only the condition field and the variable name now unwrap a copied {{path}} placeholder (see readUnwrapped in app/islands/AutomationForm.tsx). The loop step's "Array path" input and the reference-name inputs of fetch, search, loop and action steps still store the pasted text verbatim. I read their onInput handlers, which save e.target.value as is. The save-time validators then reject the braces: FOR_EACH_PATH_RE and FETCH_SEARCH_NAME_RE in lib/actions/validation.ts allow no {. The name inputs give no inline error, only the native pattern attribute.

Where to start: route those onInput handlers through readUnwrapped, and reuse the variable step's inline name error (STEP_NAME_RE) for the other name inputs.

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

loop:pushed run=contribute-20261024-151315-3my32g5t66f22 sha=a5e3bf29b607c0e5d67cd387c2d9bfef62a2ed82

Verdict: merge. The branch does what issue #17 asks. A copied {{path}} pasted into a condition's check field is unwrapped to the bare path, so the "Schema unavailable" and references "{{event" errors no longer appear. A pasted {{name}} variable name is unwrapped the same way, and a name that is still invalid now shows an inline error instead of failing silently. pnpm run check and pnpm run test (161 files, 2545 tests) both pass on a5e3bf2.

What I checked:

  • Read every changed line in references.ts (unwrapPlaceholder and its regex), AutomationForm.tsx (readUnwrapped, the condition field onInput, the variable name onInput plus the aria-invalid/aria-describedby inline error) and the new tests.
  • Edge cases: partial input while typing ({{event.di) stays as typed. Mixed text, two placeholders, {{}} and {{ }} are left unchanged. Writing the bare value back to the DOM covers the case where the state already holds that value. Condition values are templates, so they keep their braces, and a test covers that.
  • The "Check field" input is the only assertion field input in the app, so the fix covers every condition context. Choosing strip-on-paste over bare-copy chips is reasonable, because the same panel's chips also feed the value field, which is a template.

Follow-up: file. I checked the claim against the code. The loop "Array path" input (AutomationForm.tsx ~2218) and the name inputs of the fetch, search, loop and action steps (the other pattern={STEP_NAME_RE_SOURCE} inputs) still save e.target.value verbatim. The server validators reject braces (FETCH_SEARCH_NAME_RE at lib/actions/validation.ts:447, FOR_EACH_PATH_RE at :567), and those inputs have only the native pattern check. This is the same user-facing problem in fields the issue does not name, and the branch does not fix it, so it is worth its own issue.

loop:verdict run=review-20261024-152434-3my32g5t66f22 sha=a5e3bf29b607c0e5d67cd387c2d9bfef62a2ed82 verdict=merge followup=1

main moved and no longer merges cleanly into the branch, so a fix round will merge it.

loop:carry from=a5e3bf29b607c0e5d67cd387c2d9bfef62a2ed82 conflict=1

Fix round 1: main no longer merges cleanly into the branch.

loop:fix run=contribute-20261024-152656-3my32g5t66f22 round=1

fix(form): accept a copied {{path}} placeholder in condition fields and variable names

Placeholder chips copy {{event.did}}, but a condition's check field and a variable's name take a bare path or name, so pasting a chip produced cryptic errors ("Schema unavailable...", references "{{event", but no step defines it). Both inputs now accept either form: a value that is exactly one {{path}} placeholder is unwrapped to path as it is typed or pasted. A variable name the pattern rejects now also shows an inline error under the input instead of failing silently.

What changed #

  • app/islands/step-drafts/references.ts: new unwrapPlaceholder(text). It returns the inner path when the whole text is one {{...}} placeholder (surrounding whitespace allowed), and returns anything else unchanged (partial input, mixed text, two placeholders, {{}}).
  • app/islands/AutomationForm.tsx: the condition field and variable name inputs read through readUnwrapped. It also writes the bare value back into the DOM input, because when the state already holds that value no re-render would fix what the input shows. The variable name gets an inline errorText message, with aria-invalid/aria-describedby, when the trimmed name does not match the step-name pattern.
  • Condition values are templates, so they keep their braces. That is why the fix unwraps on paste and the chips still copy the braced form.

Verified #

  • New tests in app/islands/AutomationForm.test.tsx: pasting {{event.did}} into a condition field leaves event.did in the input and in the payload, with no "Schema unavailable" or reference error. Pasting a placeholder that equals the current field fixes the DOM. A condition value keeps {{self}}. A pasted {{listUri}} variable name becomes listUri. list-uri shows the inline error and list_uri clears it.
  • New unit tests for unwrapPlaceholder in app/islands/step-drafts/references.test.ts.
  • pnpm run check: passes. pnpm run test: 162 files, 2556 tests pass (after merging origin/main).

This round #

  • Fix round 1 merged origin/main into the branch. The only conflict was in app/islands/AutomationForm.test.tsx, where both sides had added a new describe block at the end of the file. I kept both blocks unchanged. No other code changed in this round.

Acceptance (from the issue's fix direction) #

Decisions the owner can reverse #

  • I chose "strip on paste" over "copy bare in condition contexts", because the condition panel's chips are also meant for the value field, which is a template and needs the braces.
  • The braces are stripped as the input changes, so the author sees the bare form right away. Nothing is normalized at save time, and the server validator is unchanged.
  • The inline name error is only on the variable step, which is the field the issue names. The other reference-name inputs keep only the native pattern check.

Follow-up, filed as a new issue on merge if the review agrees:

fix(form): unwrap a pasted {{path}} in the loop path and the other step-name inputs

Only the condition field and the variable name now unwrap a copied {{path}} placeholder (see readUnwrapped in app/islands/AutomationForm.tsx). The loop step's "Array path" input and the reference-name inputs of fetch, search, loop and action steps still store the pasted text verbatim. I read their onInput handlers, which save e.target.value as is. The save-time validators then reject the braces: FOR_EACH_PATH_RE and FETCH_SEARCH_NAME_RE in lib/actions/validation.ts allow no {. The name inputs give no inline error, only the native pattern attribute.

Where to start: route those onInput handlers through readUnwrapped, and reuse the variable step's inline name error (STEP_NAME_RE) for the other name inputs.

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

loop:pushed run=contribute-20261024-152656-3my32g5t66f22 sha=854a6356285b62db684c5414a771cd1cc9ad49ea

Merge. The branch does what issue #17 asks, using the issue's second fix direction ("strip the braces on paste and accept both forms"). A condition's check field and a variable's name now unwrap a value that is exactly one {{path}} placeholder. A variable name the pattern rejects now shows an inline error instead of failing silently. pnpm run check and pnpm run test both pass.

What I checked and how:

  • I ran the commands from run.json at head 854a635. pnpm run check was clean. pnpm run test passed 162 files and 2556 tests.
  • I read every changed line. unwrapPlaceholder (references.ts) only unwraps when the whole text is one placeholder. Partial input, mixed text, two placeholders and {{}} all stay unchanged, and unit tests cover each case. readUnwrapped also writes the bare value back to the DOM input, which handles the case where state already holds that value. A form test covers this.
  • Condition values are templates, and they keep their braces. A test pins this down, and it is why stripping on paste is a better choice than making the chips copy a bare path.
  • The variable-name check uses STEP_NAME_RE on the trimmed name. That matches serialization, since convert.ts trims names, and the server's ^[a-zA-Z_][a-zA-Z0-9_]*$. The error uses aria-invalid/aria-describedby, and the tests cover both showing it and clearing it.
  • The pasted braced field no longer reaches the field-type resolver or the reference check. The test asserts that neither "Schema unavailable" nor "but no step defines it" appears, and that the payload carries event.did.
  • Nothing outside the issue's scope changed.

Follow-up: file it. I checked the claim against the code. The loop "Array path" input (AutomationForm.tsx around line 2270) still saves e.target.value verbatim. So do the fetch, search, loop and action name inputs (the other pattern={STEP_NAME_RE_SOURCE} inputs). FOR_EACH_PATH_RE and FETCH_SEARCH_NAME_RE in lib/actions/validation.ts reject {, so a pasted chip fails there the same way. Those are different fields from the two this issue names, the branch leaves them alone, and the fix in the follow-up is cheap and concrete.

loop:verdict run=review-20261024-153121-3my32g5t66f22 sha=854a6356285b62db684c5414a771cd1cc9ad49ea verdict=merge followup=1

Merged main into the branch: 4ada24a629895c5b182400c1a30e4311304f4782.

loop:carry from=854a6356285b62db684c5414a771cd1cc9ad49ea sha=4ada24a629895c5b182400c1a30e4311304f4782

Merged onto main as 8413f040a5d7566f502c3229d3da9dbb93938962.

loop:merged sha=8413f040a5d7566f502c3229d3da9dbb93938962 head=4ada24a629895c5b182400c1a30e4311304f4782

Filed the follow-up as a new issue, not marked ready: fix(form): unwrap a pasted {{path}} in the loop path and the other step-name inputs

loop:filed issue=3myn3xzv67g2e

Sign up or Sign in to add to the discussion.