Automations and webhooks for the AT Protocol airglow.run

fix(activity): stop counting the "Re-enabled after re-authorization" system row as a successful delivery #91

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

reactivateSessionLostAutomations (lib/jetstream/session-loss.ts) writes a delivery_logs row with statusCode: null, error: null and the message "Re-enabled after re-authorization." getActivitySummary (lib/actions/activity-summary.ts) takes the newest resolved row as "Last delivery" and counts every row in "Last 7 days". activityBadge in app/routes/u/[handle]/[rkey]/index.tsx falls through to the "Success" badge for a row with no error and no status code. So after a re-auth, the automation page reports a successful delivery that never happened. That is the false "it works" signal #38 is about. I found this by reading those three functions while making the activity summary skip the new update markers (kind IS NULL).

Where to start: give these system rows (and the session-loss and rate-limit disable rows) a delivery_logs.kind value, as the update markers now have, so the summary and the log views can treat them as markers. Or exclude statusCode IS NULL AND error IS NULL AND run_id IS NULL from the summary.

Found by the loop while working on #38, rkey 38, branch loop/38, merged onto main as 398d2142bd7f.

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

loop:queued cid=bafyreidh5zhvnnoiwu4uu77zivhqd3pfxwpz6fzqjar4y5bncujud6ejq4

Working on this, attempt 1.

loop:claim run=contribute-20261102-214609-91 branch=loop/91 attempt=1 issue=91

fix(activity): stop counting the "Re-enabled after re-authorization" row as a successful delivery

The row that reactivateSessionLostAutomations writes after a re-auth now has kind: "automation-reactivated", a new value in DELIVERY_LOG_KINDS, next to the existing automation-updated markers. Every place that already leaves out rows with a kind now leaves this row out too. The activity summary no longer shows it as the "Last delivery" with a "Success" badge, and it is not counted in "Last 7 days". Stats no longer count it as a successful action. The row also gets countsTowardRateLimit: false, because it is not an execution and was counting toward the hourly rate-limit window.

Verified #

  • New test in lib/jetstream/session-loss.test.ts: five 401 deliveries, then a re-auth. getActivitySummary still reports the last 401 as the last delivery and counts 5 deliveries, not 6. The existing row-shape test now also asserts kind and countsTowardRateLimit. With the writer change reverted, both tests fail.
  • pnpm db:generate reports "No schema changes, nothing to migrate": kind is a plain text column whose enum exists only in TypeScript, so there is no migration.
  • pnpm run check and pnpm run test pass (185 files, 2853 tests).

Acceptance #

What each view shows #

  • Automation page, Recent activity: "Last delivery" is the newest real delivery. If the automation has no other rows, it shows "No deliveries yet."
  • Delivery log (DeliveryLog) and admin logs (LogsBrowser): the row shows as a full-width marker line, "<date> · Re-enabled after re-authorization.", the same way update markers show. It no longer shows as a delivery row with a Success status. A log that has only marker rows shows "No deliveries yet."
  • No controls changed.

Decisions the owner can reverse #

  • The session-loss and rate-limit disable rows are left alone. They carry an error, so they already read as failures, not as false successes. Making them markers would drop their error styling in the log. That is a separate UX change.
  • Existing rows are not backfilled. Re-enable rows written before this change still have kind NULL, and the rules forbid hand-written migrations. To convert them, run this once: UPDATE delivery_logs SET kind = 'automation-reactivated', counts_toward_rate_limit = false WHERE kind IS NULL AND message = 'Re-enabled after re-authorization.' AND status_code IS NULL AND error IS NULL AND run_id IS NULL;
  • The issue's other option was not used. Excluding every row with status_code IS NULL AND error IS NULL AND run_id IS NULL would also hide real deliveries: legacy successful PDS actions have exactly that shape, as lib/stats/compute.ts notes.

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

loop:pushed run=contribute-20261102-214609-91 sha=ca14746ac89520c56ea2af0dbbf59014103cec8c

Changes requested. The code change is right: the re-enable row now has kind: "automation-reactivated", so it drops out of "Last delivery", "Last 7 days", the stats and the rate-limit window, and the log views show it as a marker. Two things are left. The shared vocabulary still says a marker row is only written when the owner saves. And re-enable rows already in the database still read as successful deliveries. The fix for those exists only as SQL in the PR description, which nobody may run.

What I checked:

  • pnpm run check and pnpm run test both pass (185 files, 2853 tests).
  • Every reader of delivery_logs.kind and of the row:
    • getActivitySummary (both queries) and computeStats (both halves) filter on kind IS NULL.
    • DeliveryLog, LogsBrowser and AdminLogsBrowser use the generic isMarkerRow.
    • The rate limiter skips the row because of countsTowardRateLimit: false.
    • The "success" log filter already left out rows with a NULL status code.
    • The session-loss streak check still treats the row as a break in the streak, as it did before.
  • I ran the real flow in a throwaway PGlite test: five 401s, then maybeDisableForSessionLoss, then reactivateSessionLostAutomations.
    • "Last delivery" is now the disable row, with a Failure badge, not a Success.
    • The success filter, the stats and the rate limiter all leave the re-enable row out.
    • An automation whose only row is the re-enable row shows "No deliveries yet."
  • Leaving the session-loss and rate-limit disable rows unmarked is within the issue. It offered marking them only as a starting point, and they never read as a success. No controls or styles changed.

Changes:

  1. Update CONCEPTS.md. The "Update marker" entry, added with the markers in #38, defines a non-Delivery log row as one "written when the Owner saves a change to an Automation (or deactivates it as redundant with a sync)". Add the new kind there: a re-authorization that turns a session-loss disabled automation back on also writes a marker row, "Re-enabled after re-authorization.". Like the update marker, it never counts toward the send budget, the activity summary or the stats. Either widen the entry, or add a sibling entry beside it. While you are there, the markerText doc comment in app/islands/log-run-grouping.ts says "for an update row"; make it say any marker row.
  2. Ship the backfill as a script, as lib/scripts/backfill-behavior-hash.ts does. Right now the existing rows (up to 30 days, the log retention) still show as Success rows in the log. On an automation with no delivery since its re-auth, they also show as "Last delivery: Success", which is the bug this issue is about. Add lib/scripts/backfill-reactivated-markers.ts, run with pnpm tsx lib/scripts/backfill-reactivated-markers.ts. It should:
    • set kind = 'automation-reactivated' and counts_toward_rate_limit = false where kind IS NULL AND message = 'Re-enabled after re-authorization.' AND status_code IS NULL AND error IS NULL AND run_id IS NULL;
    • log how many rows it changed. Give it a test, or factor the update into a function in lib/ with a test, showing that getActivitySummary goes back to the earlier 401 and that a second run changes 0 rows. Then say in the PR description that the owner runs it once after deploy. I tried this update in a throwaway PGlite test. Before it, the summary reported the legacy row (status null, no error, which reads as Success) and counted 2 deliveries. After it, the summary reported the 401 and counted 1. A second run updated 0 rows.

loop:verdict run=review-20261103-124017-91 sha=ca14746ac89520c56ea2af0dbbf59014103cec8c verdict=changes-requested

Fix round 1: the review asked for changes.

loop:fix run=contribute-20261103-124500-91 round=1 issue=91

fix(activity): stop counting the "Re-enabled after re-authorization" row as a successful delivery

The row that reactivateSessionLostAutomations writes after a re-auth now has kind: "automation-reactivated", a new value in DELIVERY_LOG_KINDS, next to the existing automation-updated markers. Every place that already leaves out rows with a kind now leaves this row out too: the activity summary no longer shows it as the "Last delivery" with a "Success" badge or counts it in "Last 7 days", the stats no longer count it as a successful action, and it gets countsTowardRateLimit: false. A one-off script, lib/scripts/backfill-reactivated-markers.ts, converts the re-enable rows already in the database.

This round (fix round 1) added the "Re-enable marker" entry to CONCEPTS.md, fixed the markerText doc comment, and shipped the backfill as a script with a tested function instead of SQL in this description.

Owner step after deploy #

Run once: pnpm tsx lib/scripts/backfill-reactivated-markers.ts. It prints how many rows it changed and is safe to re-run (a second run changes 0). Until it runs, re-enable rows from the last 30 days (the log retention) still read as successes.

Verified #

  • lib/jetstream/session-loss.test.ts:
    • Five 401 deliveries, then a re-auth: getActivitySummary still reports the last 401 and counts 5 deliveries, not 6. The row-shape test asserts kind and countsTowardRateLimit.
    • backfillReactivatedMarkers: with a legacy re-enable row (kind NULL), the summary first reports it (status null, no error, which reads as Success) and counts 6. After the backfill (1 row changed), the row has the marker kind and countsTowardRateLimit: false, the summary reports the 401 and counts 5, and a second run changes 0 rows.
    • A legacy success with no status code, and a row with the same message but a run_id, are left alone.
  • pnpm tsx lib/scripts/backfill-reactivated-markers.ts loads its whole import chain and gets as far as connecting to the database (no database here, so it stopped at ECONNREFUSED).
  • pnpm db:generate reports no schema changes: kind is a plain text column whose enum exists only in TypeScript.
  • pnpm run check and pnpm run test pass (185 files, 2855 tests).

Acceptance #

What each view shows #

  • Automation page, Recent activity: "Last delivery" is the newest real delivery. If the automation has no other rows, it shows "No deliveries yet."
  • Delivery log (DeliveryLog) and admin logs (LogsBrowser): the row shows as a full-width marker line, "<date> · Re-enabled after re-authorization.", like update markers do. It no longer shows as a delivery row with a Success status. A log that has only marker rows shows "No deliveries yet."
  • No controls or styles changed.

Decisions the owner can reverse #

  • The session-loss and rate-limit disable rows are left alone. They carry an error, so they already read as failures, not as false successes. Making them markers would drop their error styling in the log, which is a separate UX change.
  • The backfill matches only the exact old shape: kind IS NULL, that message, and no status code, error or run_id. It does not use the issue's broader option (every row with no status code, no error and no run_id) because legacy successful PDS actions have that shape too, as lib/stats/compute.ts notes.
  • The backfill function sits in lib/jetstream/session-loss.ts, next to the writer, and both use the shared REACTIVATED_MESSAGE constant.

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

loop:pushed run=contribute-20261103-124500-91 sha=a258b96f1d29ca9c9a05e48417383f714d184af8

Merge. Both changes from round 1 are fixed, and the fixes did not break the code change round 1 approved. The re-enable row is now an automation-reactivated marker, so it no longer shows as "Last delivery: Success" and is no longer counted in "Last 7 days", the stats or the rate-limit window. Rows already in the database are handled by a tested, re-runnable backfill script. The owner runs it once after deploy, as the PR description says: pnpm tsx lib/scripts/backfill-reactivated-markers.ts. Rows it misses age out in 30 days anyway (the lib/db/cleanup.ts retention).

What I checked:

  • Round 1, point 1: CONCEPTS.md now has a "Re-enable marker" entry beside "Update marker". The markerText doc comment now says "any marker row".
  • Round 1, point 2: lib/scripts/backfill-reactivated-markers.ts calls backfillReactivatedMarkers() in lib/jetstream/session-loss.ts, which matches only the exact legacy shape (kind IS NULL, the message, no status, no error, no run_id). Its tests show the summary going back to the 401, a second run changing 0 rows, and that it leaves alone both a legacy no-status success and a same-message row that has a run_id.
  • Readers of delivery_logs, my own search:
    • getActivitySummary and computeStats filter on kind IS NULL.
    • DeliveryLog, LogsBrowser and AdminLogsBrowser use the generic isMarkerRow.
    • The logs query serializes kind, and its success filter already left out null status codes.
    • The rate limiter honours countsTowardRateLimit: false.
    • The session-loss streak check behaves as before.
    • migrate-automations-to-steps.ts still matches the unchanged message.
    • No doc names the old behaviour, and no migration is needed (kind's enum exists only in TypeScript).
  • Out of scope: leaving the disable rows unmarked is within the issue. They carry an error and never read as success.
  • Check and tests: pnpm run check and pnpm run test pass (185 files, 2855 tests).
  • Throwaway PGlite test of the real flow:
    • Steps: an update marker, five 401s, maybeDisableForSessionLoss, reactivateSessionLostAutomations.
    • "Last delivery" is the disable row (Failure), and the success filter returns nothing.
    • The backfill leaves the new row and the update marker alone (0 changed).
    • A later 200 delivery becomes "Last delivery", and the count is 7 (five 401s, the disable row and the 200).

loop:verdict run=review-20261103-125216-91 sha=a258b96f1d29ca9c9a05e48417383f714d184af8 verdict=merge

Merged main into the branch: 988ab1c47c583e9bfd60c437800c3d0e9a130bf1.

loop:carry from=a258b96f1d29ca9c9a05e48417383f714d184af8 sha=988ab1c47c583e9bfd60c437800c3d0e9a130bf1

Merged main into the branch: d9d69b90afd877ab8a54a2aa538a015b6a6076da.

loop:carry from=988ab1c47c583e9bfd60c437800c3d0e9a130bf1 sha=d9d69b90afd877ab8a54a2aa538a015b6a6076da

Merged onto main as 5bf8f723a15dd29c6fffb79ba2e9331c74119c20.

loop:merged sha=5bf8f723a15dd29c6fffb79ba2e9331c74119c20 head=d9d69b90afd877ab8a54a2aa538a015b6a6076da

Sign up or Sign in to add to the discussion.