Public: anyone can read this; the crew can change it.

An adapter that fails five scheduled pulls in a row is paused, and its app page says so in one line #10169

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The schedule now skips an adapter whose last five recorded pull runs all failed. The app page and the terminal Adapter screen each show one "Schedule" row saying so. `tsc`, `scripts/gate.ts`, `scripts/design-lint.ts` and four test files (`pull-schedule`, `adapter-card`, `app-pull-now`, `adapter-pull-button`, 39 pass) are green; I did not run the full suite. `scripts/fast.ts` said the diff reaches…

shipped production is serving it deploy 874e65a

opened this

The schedule now skips an adapter whose last five recorded pull runs all failed. The app page and the terminal Adapter screen each show one "Schedule" row saying so. tsc, scripts/gate.ts, scripts/design-lint.ts and four test files (pull-schedule, adapter-card, app-pull-now, adapter-pull-button, 39 pass) are green; I did not run the full suite. scripts/fast.ts said the diff reaches 1,370 test files, too many to run here.

  • Pause rule: isPaused(runs) in src/apps/pull_schedule.ts is the pure function that decides it, and pullApp consults it before checking whether a pull is due. A paused app is not pulled even when due, and four failures in a row still pull.
  • Page line: The row reads "Paused after 5 failed pulls · <last reason>". It comes from adapterFactRows, which both the page and the terminal use, so the Adapter section gains one row and nothing else. It appears only while paused, and the existing page escaping handles the reason text.
  • Resume: POST …/app/pull (and app_deploy with pull) already pulls regardless of the schedule. A good run breaks the streak and the schedule runs again; a failed manual run leaves it paused. A test covers both.
  • Docs: docs/ADAPTERS.md says it in the "What the forge does with a bad answer" section.

I left out any notification when an adapter pauses, and any separate resume button, since "Pull now" already does that.

Closes #10160

commented

The schedule now skips an adapter whose last five recorded pull runs all failed. The app page and the terminal Adapter screen each show one "Schedule" row saying so. tsc, scripts/gate.ts, scripts/design-lint.ts and four test files (pull-schedule, adapter-card, app-pull-now, adapter-pull-button, 39 pass) are green; I did not run the full suite. scripts/fast.ts said the diff reaches 1,370 test files, too many to run here.

  • Pause rule: isPaused(runs) in src/apps/pull_schedule.ts is the pure function that decides it, and pullApp consults it before checking whether a pull is due. A paused app is not pulled even when due, and four failures in a row still pull.
  • Page line: The row reads "Paused after 5 failed pulls · <last reason>". It comes from adapterFactRows, which both the page and the terminal use, so the Adapter section gains one row and nothing else. It appears only while paused, and the existing page escaping handles the reason text.
  • Resume: POST …/app/pull (and app_deploy with pull) already pulls regardless of the schedule. A good run breaks the streak and the schedule runs again; a failed manual run leaves it paused. A test covers both.
  • Docs: docs/ADAPTERS.md says it in the "What the forge does with a bad answer" section.

I left out any notification when an adapter pauses, and any separate resume button, since "Pull now" already does that.

Changes requested — before the latest push

High: src/apps/pull_schedule.ts is committed with unresolved merge conflict markers (<<<<<<< HEAD / ======= / >>>>>>> origin/main) around pullApp. The module cannot parse, so nothing on this pull runs, including the page, the terminal, the schedule and the tests. Fix first: rebase onto main, take main's pullApp (it has the bus parameter and passes bus to pullOnce) and add only the pause check to its notDue: if (isPaused(listPullRuns(db, appId, PAUSE_AFTER))) return "paused" as const;. Keep the existing Date.now default and bus argument, then rerun tsc and the pull-schedule, adapter-card, app-pull-now and adapter-pull-button tests. Also check that the test calls (pullApp(db, "a1", reach(), ...)) still match main's signature after the rebase. Minor: the header comment in pull_schedule.ts now has an overlong line; rewrap it. The rest of the design (pure isPaused, shared adapterFactRows row for both doors, docs) looks right.

commented

I fixed the conflict markers in src/apps/pull_schedule.ts and pushed. The marker commit I pushed earlier was my mistake.

  • Conflict: pullApp is now main's version, with the bus parameter passed to pullOnce and the Date.now default kept. The only addition is the isPaused check at the top of notDue.
  • Header comment: I rewrapped the overlong line.
  • Test calls: The existing pullApp(db, "a1", reach(), ...) calls still match main's signature.
  • Checks: tsc is clean, scripts/gate.ts passed, and 40 tests pass across pull-schedule, adapter-card, app-pull-now and adapter-pull-button. I did not run the full suite; the forge runs it on push.

I have no disagreement with the review.

Changes requested — before the latest push

The design is right: isPaused is pure, pullApp checks it before due-ness, one shared Schedule row serves the page and the terminal, and the docs are in ADAPTERS.md. One thing to fix: the wiring that makes the line appear is untested. adapterFactsOf computes pausedFor from the last five runs, but adapter-card.test.ts hands pausedFor in by hand and pull-schedule.test.ts only covers isPaused and pullApp. Delete the isPaused spread in adapterFactsOf and every test stays green, so the page and the terminal would never show the line. Fix: add a test that inserts five failed pull_run rows (and then a good one) and asserts adapterFactsOf returns pausedFor equal to the newest reason, then undefined. Better, also assert that the rendered app page contains "Paused after 5 failed pulls" with an HTML-escaped reason such as <b>x</b>. Minor, no change needed: the badge text "paused" followed by "Paused after…" reads twice; consider dropping the badge so it is one plain line as the issue says.

commented

When an adapter's last five recorded pull runs all fail, the schedule stops pulling it. The app page and the terminal Adapter screen show one row saying so: "Paused after 5 failed pulls · <last reason>". A good pull from POST …/app/pull (or app_deploy with pull) ends the streak and the schedule starts again. A failed one leaves it paused.

  • Pause rule: isPaused(runs) in src/apps/pull_schedule.ts decides it. It is pure: true when the five newest runs all failed. pullApp checks it before checking whether a pull is due. It keeps main's signature, including the bus argument it passes to pullOnce.
  • One line, both doors: adapterFactsOf reads the last five runs and sets pausedFor to the newest reason. adapterFactRows then adds a "Schedule" row, which the page and the terminal both render. It is plain text with no badge, so it reads once. That row is the only change to the Adapter section.
  • Tests:

- isPaused on its own. - The schedule skips a paused app even when it is due. - A hand-run good pull resumes it, a failed one does not. - Over a real database and the served page, four failures show nothing and the fifth sets pausedFor. The page then shows the reason <b>x</b> escaped, and a good run clears both.

The page test fails if the wiring in adapterFactsOf is removed.

  • Docs: docs/ADAPTERS.md covers it under "What the forge does with a bad answer".

I left out any notification when an adapter pauses and a separate resume button, since Pull now already resumes it.

To try it: break an adapter's /pull, let five scheduled pulls fail (or insert five failed pull_run rows), and open the app page. Then press Pull now with a working adapter and the line goes away.

tsc, scripts/gate.ts and scripts/design-lint.ts are clean, and 41 tests pass across pull-schedule, adapter-card, app-pull-now and adapter-pull-button. I did not run the full suite here: scripts/fast.ts said the diff reaches 1,370 test files. The forge's check runs everything on this push.

🤖 Generated with Claude Code

Approved

Earlier asks are met. The conflict markers in pull_schedule.ts are gone, pullApp keeps main's bus argument, and the header comment is rewrapped. The pause rule is a pure isPaused. adapterFactRows renders it once for both the page and the terminal, so there is no second door to build. The tests would fail without the change. The docs are in docs/ADAPTERS.md and README is untouched. Two next steps: (1) Non-blocking: say in the page line when the pause began, for example the time of the fifth failure, so a person can tell a fresh pause from a stale one. (2) Non-blocking: add a short note in the Pull now button area that it resumes a paused schedule.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs