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

Settings › Features shows each scheduled tool's last run, how it went and its next run #10235

Merged opened this

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

Merged

Settings › Features now shows one line per scheduled tool, whether or not it has ever run. Before this, only tools that had already fired got a row. Tests for the touched areas, `tsc`, `design-lint` and `gate.ts` all pass here. The forge's full check runs on the push.

shipped production is serving it deploy 9e8c29c

opened this

Settings › Features now shows one line per scheduled tool, whether or not it has ever run. Before this, only tools that had already fired got a row. Tests for the touched areas, tsc, design-lint and gate.ts all pass here. The forge's full check runs on the push.

What changed

  • Line function: src/apps/feature_schedule.ts has two new plain functions, scheduleStatus and scheduleLine. They give lines like scan every 1h · last 10:02, ok · next 11:02 (UTC).
  • Failed: the error's sentence replaces ok, for example last 10:02, the app answered 500 · next 11:02.
  • Never run: the line reads scan every 1h · never run · next on the next look.
  • Reachability: src/actions/features.ts adds schedule to the feature view, built by scheduleStatus. The features MCP tool and the API return that view, so I did not change the tool and added no new one. I did not run test/mcp-size.test.ts on its own, only in a group with the other test files.
  • Page: the "Scheduled" block in src/web/pages/features.ts reads from schedule. It uses the existing row with time() and adds no new class. The "Run now" button now also shows for a tool that has never run.
  • Docs: docs/features/absorb.md no longer has "Not done yet". The Schedule section says where to see the line.
  • Tests: the new test/feature-schedule-line.test.ts asserts the line for ok, failed and never-run. It cannot pass on main because scheduleLine does not exist there. In test/features.test.ts the fixture now declares a schedule. The page test checks the new line and the never-run state; before, it expected the Scheduled block to disappear once the fire record was cleared.

What I chose not to do

  • No history beyond the last fire.
  • No local-time rendering.
  • The never-run line says "next on the next look", because the scheduler checks once a minute and fires anything that has never run.

How to try it Absorb an app that declares a schedule, then open /settings/features/<slug>.

Closes #10200

commented

Settings › Features now shows one line per scheduled tool, whether or not it has ever run. Before this, only tools that had already fired got a row. Tests for the touched areas, tsc, design-lint and gate.ts all pass here. The forge's full check runs on the push.

What changed

  • Line function: src/apps/feature_schedule.ts has two new plain functions, scheduleStatus and scheduleLine. They give lines like scan every 1h · last 10:02, ok · next 11:02 (UTC).
  • Failed: the error's sentence replaces ok, for example last 10:02, the app answered 500 · next 11:02.
  • Never run: the line reads scan every 1h · never run · next on the next look.
  • Reachability: src/actions/features.ts adds schedule to the feature view, built by scheduleStatus. The features MCP tool and the API return that view, so I did not change the tool and added no new one. I did not run test/mcp-size.test.ts on its own, only in a group with the other test files.
  • Page: the "Scheduled" block in src/web/pages/features.ts reads from schedule. It uses the existing row with time() and adds no new class. The "Run now" button now also shows for a tool that has never run.
  • Docs: docs/features/absorb.md no longer has "Not done yet". The Schedule section says where to see the line.
  • Tests: the new test/feature-schedule-line.test.ts asserts the line for ok, failed and never-run. It cannot pass on main because scheduleLine does not exist there. In test/features.test.ts the fixture now declares a schedule. The page test checks the new line and the never-run state; before, it expected the Scheduled block to disappear once the fire record was cleared.

What I chose not to do

  • No history beyond the last fire.
  • No local-time rendering.
  • The never-run line says "next on the next look", because the scheduler checks once a minute and fires anything that has never run.

How to try it Absorb an app that declares a schedule, then open /settings/features/<slug>.

commented

The check failed on the gate rule that wants a Try: line in a commit message. I added an empty commit with that line and pushed. bun scripts/gate.ts now passes here, but I haven't seen the forge's check re-run on the push.

The failing rule said no commit on the pull had a Try: line, since the pull touches src/actions/features.ts and src/web/pages/features.ts. No code or tests changed.

Try: on the preview, open /settings/features/<slug> for an absorbed app that declares a schedule. Each entry shows scan every 1h · last 10:02, ok · next 11:02, or the error's sentence when it failed, or never run before its first fire.

Changes requested — before the latest push

Two fixes, then this lands. (1) scheduleLine has no caller outside its own test: neither the page nor the features tool uses it, and the page re-implements the line by hand. Box 1 asks for one plain function both reuse. (2) The terminal still shows the old fires, so the new line is not in both doors. Also fix the absorb.md sentence that says the tool returns scheduleLine; it returns ScheduleStatus data. No security findings: output is escaped through html, and the route and its checks are unchanged.

commented

Settings › Features, the features tool and the terminal now all get the scheduled-tool wording from one function, scheduleMeta in src/apps/feature_schedule.ts. The type check, the gate, design-lint and the three test files that cover this change pass. I did not run the full suite; the forge's check does that on the push.

  • One wording. scheduleMeta(s) returns the pieces after scan every 1h: never run · next on the next look, or last X, ok|<error> · next Y. scheduleLine joins those pieces with UTC HH:MM clocks. The page maps the same pieces to markup: a <time> for the last run, the error in <span class="danger"> (so a failed run keeps its danger styling), and a <time> for the next run.
  • Tool payload. Each ScheduleStatus from scheduleStatus now carries line, so features returns the sentence along with the status data.
  • Terminal. The feed screen (src/tui/screens/featurefeed.ts) now prints those lines after Scheduled:, instead of the old x.ok/x.error fires. The r key still runs from fires.
  • Next-run bug fixed. My earlier commit passed { date: false } to time(), which does nothing, so a future next run showed as "just now". It now shows HH:MM UTC.
  • absorb.md. The sentence now says the tool returns schedule as status data plus line, and that the terminal's feed screen prints the lines under Scheduled:.

Tests

  • Page: it now asserts the full row for a failed run, with the escaped error and the next <time>, and the exact never-run sentence.
  • Tool payload: it asserts schedule[0].line for both the failed and never-run cases.
  • Terminal: it asserts the full Scheduled: scan every 1h · last … · next … line. Its fixture gained a scan tool and an hourly schedule, because it declared none before.

Not done: the time() helper still ignores its date option for how the relative text is worded; I left it alone.

To try it, open /settings/features/radar on the preview, https://lab--10235--forge-trevato-apps.open-platform.sh, for an absorbed app with a schedule.

🤖 Generated with Claude Code

Changes requested — before the latest push

The idea is right and the tests for the line are good, but the page departs from the design system and loses information it showed before. Fix in one round: (1) The page hand-rolls a <time> for the next run and uses time() for the last run, so one row reads last 2m ago, ok · next 11:02 UTC. Use one clock for both (the existing time()), no hand-rolled markup. (2) The old row showed durationMs; the new one drops it. Keep it. (3) 'next on the next look' is the scheduler's jargon (principle 8); say 'next soon' or 'due now'. (4) Drop the p.at === x.lastAt identity test that picks the format; have scheduleMeta tag the pieces (last / next). (5) absorb.md: the Schedule paragraph is one dense sentence; split it, and put the status/line detail in feature-schedule.md. (6) The page test depends on the text '2m ago'; make it independent of the clock. (7) The failed-fire ?? "failed" fallback is written twice; keep it once. Boxes: 1 is mostly met but the page and terminal word the line differently; 2 and 3 are met.

commented

Each scheduled tool now gets one line on Settings › Features › the app, in the features tool and in the terminal's feed screen. It reads scan every 1h · last 10:02, ok · 42 ms · next 11:02; a failure's error sentence replaces ok, and before the first run it reads scan every 1h · never run · next soon. The type check, scripts/gate.ts, design-lint and the three test files this change reaches (test/features.test.ts, test/features-tui.test.ts, test/feature-schedule-line.test.ts) pass locally; the full suite runs on the forge.

Where it lives

  • scheduleStatus in src/apps/feature_schedule.ts builds one status per entry from the app's schedule and the last fire of each tool. It decides the failure sentence (?? "failed") once, carries durationMs, and adds the plain-text line.
  • scheduleMeta words the line once and tags its pieces as last, next or error. scheduleLine prints those pieces as plain text with UTC clocks.

Each door

  • Page: it renders the same pieces with the existing time(), with no hand-built <time> and no comparing instants to pick a format. The last run shows as relative time (2m ago) and the next run as its day, since the design system says that is how time() words an instant still ahead. Both carry the exact instant in their tooltip. A failure keeps the danger styling, and the duration (· 42 ms) is back as before.
  • features tool and API: GET /api/v1/features/:slug returns schedule, one { tool, every, lastAt, ok, error, durationMs, nextAt, line } per entry, alongside the existing fires.
  • Terminal: Feature feeds › the app prints those lines after Scheduled:. Pressing r still runs the scheduled tools.

Docs: the Schedule paragraph in docs/features/absorb.md is now short sentences that point to docs/features/feature-schedule.md. That file, under "Read it", gives the full line, what each door shows, and the fields of schedule.

Tests: the page assertion checks the whole row, including <time> elements, escaped danger text, the duration and the next day, without depending on the current time. The tool payload and the terminal line are asserted in full. The terminal test's fixture gained a scan tool and a schedule, because it declared neither before.

Choices made

  • An hourly tool's next run reads as a date on the page, which is coarse. Teaching time() to word future instants (in 58m) would change a shared helper used by every page, so I left it for its own issue.
  • The text line keeps UTC HH:MM clocks, because a reader of the tool or the terminal has no tooltip to fall back on.

To try it, open /settings/features/radar on https://lab--10235--forge-trevato-apps.open-platform.sh for an absorbed app with a schedule, then press Run now.

🤖 Generated with Claude Code

Approved

Earlier asks: (1) one plain function reused by the page and the tool: met. scheduleMeta words the line once, scheduleStatus carries line into the features view and API, and the page maps the same pieces to markup. (2) The terminal now prints the schedule lines, not the old fires: met. (3) absorb.md no longer says the tool returns scheduleLine: met, and Not done yet is gone. No security findings; output stays escaped through html and the new test checks the escaped error. Two things I would do next: (a) teach time() to word future instants (in 58m) so an hourly next run is not shown as a bare date on the page; (b) run test/mcp-size.test.ts on its own, since the features view grew by a schedule array.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs