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

The forge keeps a bounded history of each degraded cause: when it opened, when it closed, and its sentence #1682

Merged opened this

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

Merged

The forge now keeps a bounded history of each degraded cause, written from the 2-minute tick in `src/main.ts`. The type check is clean. `test/vitals-degraded.test.ts` and the schema and gate-list tests pass. I did not run the whole check or boot the app.

shipped production is serving it deploy b2c98db

opened this

The forge now keeps a bounded history of each degraded cause, written from the 2-minute tick in src/main.ts. The type check is clean. test/vitals-degraded.test.ts and the schema and gate-list tests pass. I did not run the whole check or boot the app.

What changed

  • Migration 79 (src/core/schema.ts) adds degraded_span: cause, sentence, href, opened_at, and closed_at (null while open). Times are epoch milliseconds. A partial unique index allows at most one open span per cause. The migration only adds, so the additive check passes.
  • src/kernel/degraded_history.ts has record(db, degraded, now, log?) and spans(db, from, to).

- record opens a span for a new cause, updates the sentence and href of one still open, closes spans whose cause is gone, and deletes spans closed more than 30 days ago. It runs as one transaction with a fixed handful of queries, and calling it twice with the same input changes nothing. - record catches its own errors and logs the first failure only. - spans returns every span overlapping the window, with its full length in milliseconds. An open span runs to to. - A cause that returns within a minute of closing reopens that span instead of adding a new one.

  • src/main.ts: the existing 120-second tick (tickAsks) now calls vitals() and passes its degraded list to record. No new timer was added. A vitals failure is logged as a warning and never reaches the tick.
  • test/vitals-degraded.test.ts joins GATE in scripts/gate.ts. It runs on in-memory SQLite with a fixed now and covers:

- a cause that appears and goes leaves one closed span; - a cause still there an hour later stays one open span, and record is idempotent; - the sentence is the latest one; - a span closed 31 days ago is gone, while one closed 29 days ago stays; - spans over a day finds a span that began the day before; - flap extension; - a broken table is logged once and not thrown.

Choices to check

  • opened_at is when the tick first saw the cause, not the since that vitals() reports. A span can therefore start up to 2 minutes after the real onset. I kept it so spans are never older than what this table observed.
  • vitals() is called here without envs, since only degraded is used.

Left out

  • Nothing reads spans() yet; there is no route or UI, as the issue asked.
  • I did not run the 10-run random-order flake hunt that admitting a file to GATE asks for in the pull description.

To try it: run bun test test/vitals-degraded.test.ts.

Closes #1296

Approved

Matches #1296 point for point: migration 79, record/spans in kernel/degraded_history.ts, called from the existing tick (no new timer), 30-day prune in the same transaction, errors caught and logged once, test file joins GATE. No route or page by design (second of three pieces), so there is no UI or JS-off surface to judge. Two things I would do next: (1) surface spans on the page and in src/tty.ts, with the README saying how, in the follow-up piece, so both doors have it; (2) the main.ts try/catch around vitals() duplicates record own catch only for vitals failure, which is fine, but consider logging that warn once too, so a persistently failing vitals does not write a line every 2 minutes.

commented

The full gate on land was red and named no test; the pull is open again.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs