Automations and webhooks for the AT Protocol airglow.run

fix(form): decide the default Start-in mode instead of #88 quietly switching new automations to dry run #94

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

Follow-up to #88.

Problem #

#88 added the "Start in" choice to the New automation form and preselects Dry run. Before #88, the primary "Create automation" button created a live automation. Now the same button creates a dry-run one, unless the user notices the radios and picks Live. #88 listed "the default mode a new automation starts in" as out of scope, so this change of default was never decided.

It matters more than a label: dry-run automations turn themselves off after 7 days (lib/runtime/dry-run-expiration.ts). A user who creates an automation the usual way, without reading the radios, gets one that never acts and then disables itself.

Evidence #

  • app/islands/AutomationForm.tsx:688: useState(restoredRef.current?.dryRun ?? true).
  • lib/runtime/dry-run-expiration.ts: the 7-day auto-disable for dry-run automations.

Expected #

The default is a decision the owner makes, and the form makes it obvious. Either:

  • the default goes back to Live, as before #88, or
  • Dry run stays the default, and the primary button and its surroundings say so (for example "Create in dry run"), including that a dry run turns off after 7 days.

Acceptance criteria #

Out of scope #

Changing the dry-run expiry itself.

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

loop:queued cid=bafyreihbhmmejjlkseeyet5nwcm6rf3as2h6zoia6hg6wyrumflp4rmwwy

Working on this, attempt 1.

loop:claim run=contribute-20261103-131716-94 branch=loop/94 attempt=1 issue=94

The session needs an answer from the owner.

fix(form): decide the default Start-in mode for new automations

I stopped before changing anything: the issue says the default Start-in mode is the owner's decision, and comments.md has no answer yet. Both options touch the same lines (app/islands/AutomationForm.tsx:688 and the create button near line 3607), so I can't do one without guessing.

Question for the owner #

What should a blank New automation form start in?

  1. Live (the default before #88). Line 688 changes to restoredRef.current?.dryRun ?? false and the button keeps "Create automation". Tests in AutomationForm.test.tsx check that a blank form has Live checked and posts dryRun: false, and that a restored stash with dryRun: true still comes back as Dry run.
  2. Dry run (the default now). The default stays. The create button's label follows the choice: "Create in dry run" when Dry run is checked, "Create automation" when Live is checked. The Dry run radio's hint names the limit, using DRY_RUN_MAX_DAYS from lib/runtime/dry-run-expiration.ts: "Logs what each action would do. Nothing is written, posted or sent. Turns off after 7 days." Tests check the default, the label in both modes and the hint text.

Reply with 1 or 2, plus any change to the wording in option 2. The next run picks up from your answer.

Acceptance criteria #

loop:awaiting-owner run=contribute-20261103-131716-94 why=needs-info

Default to live

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

loop:queued cid=bafyreihbhmmejjlkseeyet5nwcm6rf3as2h6zoia6hg6wyrumflp4rmwwy

Working on this, attempt 1.

loop:claim run=contribute-20261108-203958-94 branch=loop/94 attempt=1 issue=94

fix(form): preselect Live in the New automation form's Start-in choice

#88 added the "Start in" choice to the New automation form and preselected Dry run, so the one "Create automation" button quietly started creating dry-run automations, which turn themselves off after 7 days. The owner decided the default goes back to Live, as before #88: the form now preselects Live, and Dry run is something the user picks.

The change is one initial value in app/islands/AutomationForm.tsx (restoredRef.current?.dryRun ?? false) plus its comment; the tests in app/islands/AutomationForm.test.tsx that pinned the old default now pin the new one.

Verified #

  • pnpm run check: passes.
  • pnpm run test: 186 files, 2784 tests pass.
  • AutomationForm.test.tsx covers the default: a blank form (no name, lexicon, triggers or steps) and a seeded form both start with Live checked and Dry run unchecked; the create button is described by start-live-hint by default and by start-dry-run-hint once Dry run is picked; a default submit POSTs dryRun: false; picking Dry run POSTs dryRun: true; going back to Live POSTs dryRun: false; the scope-upgrade round trip restores a Dry run choice (the non-default one, so the test proves restoration and not the default).

Acceptance criteria #

What the form shows #

  • New, blank or filled: "Start in" with Live checked ("Runs the actions for real as soon as it's created."), and Dry run unchecked under it. The create button reads out the Live hint, then the list of what's left to fix when the form is invalid.
  • New, Dry run picked: the Dry run radio is checked and the button reads out the Dry run hint. That choice survives the scope-upgrade redirect, as before.
  • Edit: no change. There's no Start-in choice, and the PATCH still leaves out dryRun.

No markup, styles or controls changed, so keyboard use and focus work as before.

Decisions the owner can reverse #

  • The radios keep their #88 order, Dry run first and then Live. Only which one is checked changed.
  • The API's own default when a POST leaves out dryRun is still dry run (app/routes/api/automations/index.ts). The form always sends the field, so this only affects direct API callers, and the issue covers the form. The form's comment now says this.
  • A scope-upgrade stash saved before this change with no dryRun field now comes back as Live. #88 always wrote the field, so this is unlikely in practice.
  • The dry-run expiry is unchanged (out of scope).

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

loop:pushed run=contribute-20261108-203958-94 sha=b7e4856b5679801cef823753c848bf3420768dac

Verdict: changes-requested. The branch does what the owner asked ("Default to live"): a blank New automation form now starts with Live checked, and AutomationForm.test.tsx covers that. Check and tests pass. But flipping the default changes what a restored autosaved draft falls back to. The autosave never kept the Start-in choice, so a user who picks Dry run and then reloads, crashes or leaves and comes back gets the draft back with Live checked. If they then click Create, the automation starts live, with real writes they had opted out of. On main the same gap only lost a Live choice, and the draft fell back to the harmless Dry run.

What I checked:

  • Ran pnpm run check: passes. Ran pnpm run test: 186 files, 2784 tests pass.
  • Read the diff. It changes one initial value (restoredRef.current?.dryRun ?? false) plus its comment, and turns the tests around. Each acceptance criterion holds: the default is Live, the dry-run hint criterion does not apply, and tests cover the blank and seeded default. Nothing touches the dry-run expiry, which is out of scope. The form is the only client that POSTs to /api/automations. The API's own dry-run default when dryRun is missing is untouched, and the form always sends the field.
  • No markup or style changed, and no doc names the old default. Before #88 the primary "Create automation" button was live (git show 51a528a), so this restores that.
  • Listed every place the form's draft state is saved and restored: the scope-upgrade stash, the autosave (currentDraft / writeAutosave), pristineRef, and applyDraft (used by discard, held-draft and stale-draft restores). dryRun reaches only the scope-upgrade stash.
  • Reproduced the gap with a throwaway test in a copy of AutomationForm.test.tsx. On BRANCH_SEED I chose Dry run, typed a name and let autosave run, then remounted. The restored form had Live checked, and a submit POSTed dryRun: false.

Changes:

  1. Make the autosaved draft keep the create form's Start-in choice, so a restored draft creates in the mode the user picked. In app/islands/AutomationForm.tsx:

    • add ...(isEdit ? {} : { dryRun: startDryRun }) to currentDraft, with isEdit and startDryRun in its deps;
    • add ...(isEdit ? {} : { dryRun: false }) to pristineRef.current, so an untouched form stays pristine and is not autosaved;
    • initialise with useState<boolean>(restored?.dryRun ?? false), not restoredRef.current?.dryRun, so both the stash and the autosave restore it;
    • call setStartDryRun(draft.dryRun ?? false) in applyDraft, so discarding a restored draft goes back to Live and loading a held or stale draft brings its choice back;
    • update the FormDraft.dryRun doc comment, which says only the scope-upgrade stash keeps it.

    I tried exactly this in the clone, in a throwaway test file:

    • The Dry-run, reload and restore case above now comes back with Dry run checked.
    • Choosing only Dry run autosaves, and a remount restores it.
    • "Discard" on the restored draft returns to Live and clears storage, and a later visit starts on Live.
    • All 506 tests in app/islands still pass. vp check only wants a reformat of the longer deps array (vp check --fix).
  2. Add tests for this to the AutomationForm autosave block in app/islands/AutomationForm.test.tsx:

    • a Dry run choice survives an autosave restore, and the create POSTs dryRun: true;
    • discarding the restored draft returns the form to Live.

loop:verdict run=review-20261108-204514-94 sha=b7e4856b5679801cef823753c848bf3420768dac verdict=changes-requested

Fix round 1: the review asked for changes.

loop:fix run=contribute-20261108-204909-94 round=1 issue=94

fix(form): preselect Live in the New automation form's Start-in choice

#88 added the "Start in" choice to the New automation form and preselected Dry run, so the one "Create automation" button quietly started creating dry-run automations, which turn themselves off after 7 days. The owner decided the default goes back to Live, as before #88: the form now preselects Live, and Dry run is something the user picks. The autosaved draft now keeps that choice too, so a restored draft creates in the mode the user picked and does not fall back to Live.

This round (review): the autosave did not keep the Start-in choice, so with Live as the default, a user who picked Dry run and then reloaded got the draft back with Live checked. currentDraft and pristineRef in app/islands/AutomationForm.tsx now hold dryRun on create (edit leaves it out). The initial state reads it from the scope-upgrade stash or the autosave, and applyDraft sets it, so discard goes back to Live and a held or stale draft brings its choice back. The FormDraft.dryRun comment says so, and three autosave tests cover it.

Verified #

  • pnpm run check: passes.
  • pnpm run test: 186 files, 2787 tests pass.
  • AutomationForm.test.tsx covers the default: a blank form and a seeded form both start with Live checked and Dry run unchecked. The create button is described by start-live-hint by default and by start-dry-run-hint once Dry run is picked. A default submit POSTs dryRun: false, picking Dry run POSTs dryRun: true, and going back to Live POSTs dryRun: false. The scope-upgrade round trip restores a Dry run choice.
  • New autosave tests: picking Dry run, typing a name and remounting restores Dry run, and Create POSTs dryRun: true. Picking only Dry run autosaves, and a remount restores it. Discarding a restored Dry run draft returns the form to Live, clears storage, and a later visit starts on Live. I ran them without the AutomationForm.tsx change, and all three fail.
  • An untouched create or edit form is still not autosaved, because the existing "does not autosave an untouched form" tests pass with dryRun: false in the pristine state.

Acceptance criteria #

What the form shows #

  • New, blank or filled: "Start in" with Live checked ("Runs the actions for real as soon as it's created."), and Dry run unchecked under it. The create button reads out the Live hint, then the list of what's left to fix when the form is invalid.
  • New, Dry run picked: the Dry run radio is checked and the button reads out the Dry run hint. That choice survives the scope-upgrade redirect and an autosave restore (reload, crash, leaving and coming back).
  • New, restored draft discarded: back to Live.
  • Edit: no change. There's no Start-in choice, the edit draft leaves out dryRun, and the PATCH still leaves out dryRun.

No markup, styles or controls changed, so keyboard use and focus work as before.

Decisions the owner can reverse #

  • The radios keep their #88 order, Dry run first and then Live. Only which one is checked changed.
  • The API's own default when a POST leaves out dryRun is still dry run (app/routes/api/automations/index.ts). The form always sends the field, so this only affects direct API callers, and the issue covers the form.
  • An autosaved or stashed draft saved before this change has no dryRun field, so it comes back as Live.
  • Picking only Dry run on a blank form gets autosaved, but it does not set off the leave-page warning, because the create form's dirty check looks at the typed fields only. I didn't change that.
  • The dry-run expiry is unchanged (out of scope).

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

loop:pushed run=contribute-20261108-204909-94 sha=42d7d17d86b335e0efc10e87f825baea305335bd

Verdict: merge. The branch does what the owner decided in comments.md ("Default to live"). A blank New automation form now preselects Live, and AutomationForm.test.tsx covers that default. This round also fixes the gap the first review raised: the autosave keeps the create form's Start-in choice, so a restored draft no longer turns an opted-in Dry run back into Live. pnpm run check and pnpm run test pass.

What I checked and how:

  • Ran pnpm run check: passes. Ran pnpm run test: 186 files and 2787 tests pass.
  • Acceptance criteria, checked against the code:
    • The default is now useState(restored?.dryRun ?? false), so a blank form starts on Live.
    • The dry-run hint criterion does not apply, because the default is Live.
    • Tests cover the blank and seeded defaults, the aria-describedby hint, and the POSTed dryRun both by default and after switching.
  • Out of scope: lib/runtime/dry-run-expiration.ts and the API's own default for a missing dryRun are unchanged.
  • The previous round asked for five changes, and each one is in app/islands/AutomationForm.tsx:
    • currentDraft carries dryRun on create, with isEdit and startDryRun in its deps.
    • pristineRef holds dryRun: false on create, so an untouched form is still not autosaved (the existing "untouched form" tests pass).
    • The initial state reads from restored, which covers both the scope-upgrade stash and the autosave.
    • applyDraft sets startDryRun, which covers discard and restoring a held or stale draft.
    • The FormDraft.dryRun comment is updated.
    • The three new autosave tests are there too. I restored AutomationForm.tsx to the previous head, b7e4856, and all three fail, so they test the fix.
  • Listed every place the draft is saved or restored: the scope-upgrade stash (already had dryRun), the autosave write, pristineRef, applyDraft (used by discard, held-draft restore and stale-draft restore) and the initial state. Each one now handles dryRun the same way, and edit drafts and the PATCH leave it out.
  • The scope-upgrade round-trip fix from the first round still works: its test, now built on a Dry run choice, passes.
  • Other callers: the duplicate page also uses create mode, so it now starts on Live too. That matches how the single create button behaved before #88. No doc or README names the old default.
  • No markup, style or control changed, so there is no new keyboard, focus or design-rule surface to check.

loop:verdict run=review-20261108-205451-94 sha=42d7d17d86b335e0efc10e87f825baea305335bd verdict=merge

Merged onto main as 3dfd2a23a74f964a63f1cef478b081012c62417f.

loop:merged sha=3dfd2a23a74f964a63f1cef478b081012c62417f head=42d7d17d86b335e0efc10e87f825baea305335bd

Sign up or Sign in to add to the discussion.