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

A maintenance duty registered in one file runs on its interval and leaves a maintenance_run row with its findings and the probe behind each #11212

Parked opened this
Closed without merging

A maintenance duty is now one file that runs on its interval and leaves a `maintenance_run` row. I did not call `keep_plan`; that tool wasn't available in this session, so the issue has no plan on it.

stopped parked by the crew deploy f7ea4d8

opened this

A maintenance duty is now one file that runs on its interval and leaves a maintenance_run row. I did not call keep_plan; that tool wasn't available in this session, so the issue has no plan on it.

I ran tsc, bun scripts/gate.ts and the tests for this change; all passed. They were test/maintain.test.ts, job-slices, timers-named and schema-migrations. The full suite is left to the forge's check on this push.

  • Duty and registry: a duty is a file in src/maintain/duties/ with a name, an interval and a handler returning findings and actions. It is registered with one line in src/maintain/registry.ts.
  • Runner: it ticks every 60 s inside during("maintain"). It runs a duty when its interval has passed since the duty last started, and it never runs the same duty twice at once. Duties take turns, with the loop yielding between them. main.ts starts it and stops it on shutdown.
  • Record: migration 20261111100000-maintenance-run (pinned) creates the table with duty, started, finished, findings, actions, probes and outcome. It also adds issue_open_age on (kind, state, created_at) so the example duty's count walks an index, which a test proves from the query plan.
  • Refusal: each finding needs a probe: query or measurement, its number, its time. If any finding lacks one, the run keeps no findings or actions and the outcome reads refused: finding "…" has no probe. A duty that throws is recorded as failed: <message> and the next duty still runs.
  • Example duty: pulls-waiting counts open, unparked pulls older than 24 h, with the SQL and its number as the probe.
  • Endpoint: GET /api/v1/maintenance/runs?limit=N needs a forge admin and ro scope. It answers 401 without a credential and 404 for a non-admin. The limit is capped at 100 and defaults to 20.
  • Tests and docs: test/maintain.test.ts covers schedule, record, one-at-a-time, refusal, failure, the example duty, the index and the endpoint. test/job-slices.test.ts now times maintain against the busy fixture; it measured about 0.1 ms. The how-to is in docs/features/maintain.md.

I left out:

  • Any terminal screen or page for the runs; they are reachable by API only.
  • Pruning old rows.
  • Per-duty slice enforcement beyond the shared job-slice test.
  • Duties that act. The actions field is recorded but the example takes none.

To try it: as a forge admin, call GET /api/v1/maintenance/runs on the preview. The pulls-waiting row appears about 75 s after boot. The commit carries the Try: line the repo requires.

Closes #11204

commented

A maintenance duty is now one file that runs on its interval and leaves a maintenance_run row. I did not call keep_plan; that tool wasn't available in this session, so the issue has no plan on it.

I ran tsc, bun scripts/gate.ts and the tests for this change; all passed. They were test/maintain.test.ts, job-slices, timers-named and schema-migrations. The full suite is left to the forge's check on this push.

  • Duty and registry: a duty is a file in src/maintain/duties/ with a name, an interval and a handler returning findings and actions. It is registered with one line in src/maintain/registry.ts.
  • Runner: it ticks every 60 s inside during("maintain"). It runs a duty when its interval has passed since the duty last started, and it never runs the same duty twice at once. Duties take turns, with the loop yielding between them. main.ts starts it and stops it on shutdown.
  • Record: migration 20261111100000-maintenance-run (pinned) creates the table with duty, started, finished, findings, actions, probes and outcome. It also adds issue_open_age on (kind, state, created_at) so the example duty's count walks an index, which a test proves from the query plan.
  • Refusal: each finding needs a probe: query or measurement, its number, its time. If any finding lacks one, the run keeps no findings or actions and the outcome reads refused: finding "…" has no probe. A duty that throws is recorded as failed: <message> and the next duty still runs.
  • Example duty: pulls-waiting counts open, unparked pulls older than 24 h, with the SQL and its number as the probe.
  • Endpoint: GET /api/v1/maintenance/runs?limit=N needs a forge admin and ro scope. It answers 401 without a credential and 404 for a non-admin. The limit is capped at 100 and defaults to 20.
  • Tests and docs: test/maintain.test.ts covers schedule, record, one-at-a-time, refusal, failure, the example duty, the index and the endpoint. test/job-slices.test.ts now times maintain against the busy fixture; it measured about 0.1 ms. The how-to is in docs/features/maintain.md.

I left out:

  • Any terminal screen or page for the runs; they are reachable by API only.
  • Pruning old rows.
  • Per-duty slice enforcement beyond the shared job-slice test.
  • Duties that act. The actions field is recorded but the example takes none.

To try it: as a forge admin, call GET /api/v1/maintenance/runs on the preview. The pulls-waiting row appears about 75 s after boot. The commit carries the Try: line the repo requires.

commented

The check failed on the gate's "pipeline first" rule: this pull adds docs/features/maintain.md and changes no pipeline file. I added a commit carrying a Direction: line instead of touching the pipeline, and pushed it. The code is unchanged.

The line says the duty runner is the base that the pipeline's own upkeep duties will register on, so it comes before them. The forge's check will say whether the gate accepts that.

bun scripts/gate.ts ran clean on this branch before the push, but it takes the base from its own checkout, so it can't stand in for the forge's check of the rule. I did not re-run tsc or the tests, since nothing in the code changed.

The pull description from my earlier message still stands as written.

commented

A maintenance duty registered in one file now runs on its interval and leaves a maintenance_run row holding its findings and their probes. This push fixes four of the five check failures; the Billing one is not mine and is still red.

What the push fixes:

  • OpenAPI row: GET /api/v1/maintenance/runs had none. I added getApiV1MaintenanceRuns.ts and regenerated the route index.
  • Session phase: the same route now records the session phase, as /api/v1/admin/maintenance does.
  • Cold-read plans: my migration created issue_open_age on issue. With that index there, the planner stopped using issue_created for the cold-pull window read, which failed "the cold pull reads walk an index" and "200 open pulls … walking an index". I removed the index. The pulls-waiting duty runs hourly and still searches an existing index.
  • Migration pin: the migration is not on main yet, so editing it was allowed. I re-pinned it, and bun scripts/gate.ts and tsc --noEmit are clean.
  • My test: my test asserted issue_open_age by name. It now asserts that the plan searches an index and does not scan the table.

The test files I ran are green: maintain, cold-home-queries, team-queue, schema-migrations, job-slices, openapi-coverage and route-timing-coverage.

Billing failure, not mine: "Settings → Billing: set three links, the home shows them" fails the same way on the base commit 4fe3b8e2b, with the home showing "Get a forge" once instead of three times. I did not change it.

To try it: register a duty in src/maintain/registry.ts, then as a forge admin read GET /api/v1/maintenance/runs?limit=20.

I chose not to add an index on issue for the duty's count.

commented

Parked by the crew: the check stayed red through 2 automatic tries

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs