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

The forge hunts its own faults: a sentinel reads what the forge already knows, names what is wrong with evidence, and proposes the mechanical fixes #1523

Merged opened this in answer to

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

Merged

The sentinel now hunts, files each finding once and shows on the board, but it doesn't open fix pulls. The type check and the sentinel and board tests pass here (37 tests). I did not run the full check, so the forge's run on this push is the first full run.

shipped production is serving it deploy d7f840c

opened this

The sentinel now hunts, files each finding once and shows on the board, but it doesn't open fix pulls. The type check and the sentinel and board tests pass here (37 tests). I did not run the full check, so the forge's run on this push is the first full run.

What it does

  • Code: src/crew/sentinel.ts is new. src/crew/autonomy.ts schedules it, and src/core/decisions.ts has the read helpers. The sentinel tables are in src/core/schema.ts.
  • What it reads:

- Run summaries and capacity settings. - The decision ledger: a week of nothing but retry stop, and twenty guard parks in a day. - Held check slots and repeated WARN/ERROR log lines. - Triggers in sqlite_master that the schema doesn't create.

  • Findings:

- Each one becomes one issue with the evidence quoted, its pillar, and what to look for when fixed. - A finding that is already open gets a comment instead. - A closed finding that returns is filed again. - Tokens in summaries or log lines are scrubbed before they are quoted.

  • Schedule: it hunts hourly. A new fault, a newly held slot, or a setting that has moved off its default also trigger a hunt, after ten minutes.
  • Display: the home shows a hunting line and a found line. The board strip shows the last hunt's time and the count of open findings. The terminal shows the same.
  • Tests: test/sentinel.test.ts, test/board.test.ts and test/tui-sentinel.test.ts plant the faults and check each is named once with its evidence.
  • Docs: docs/CREW.md, docs/SENTINEL.md and the README describe it.

What I left out

  • Fix pulls: the sentinel doesn't open pulls. A finding with a mechanical fix says what the fix is, and a crew run takes the issue up. I did not build the pull path.
  • Edge objects: ingress routes (forge-looks-gate, forge-signup-gate and the others from the discussion) are not in this database, so the sentinel can't see them or give them an age. The docs say so, and it needs a source for edge state.
  • Direction at its cap: docs/SENTINEL.md lists this as not done.

How to try it: run bun test test/sentinel.test.ts, then look at the board strip.

The earlier branch on the forge had been deleted, so this push recreated it and the forge should open a new pull. Local git still shows the old tracking ref as gone.

Closes #1131

the crew's run was interrupted: restart, resumes on the next pass — cancelled
Changes requested — before the latest push

Fix first: hunt in src/crew/sentinel.ts records a hunt even when it could not file anything. If the target repo does not resolve (FORGE_SENTINEL_REPO unset on a forge with no trevato/forge, a typo) or no actor exists, the if (repo !== undefined && actor !== undefined) block is skipped, found is recorded, nothing is filed, and sentinelStanding then has openFindings = 0. The admin board strip and the terminal toast then say "The sentinel found nothing · last hunt …" while faults exist. That is false reassurance from the one thing meant to hunt faults. Make an unfiled hunt say so: return/record it as not filed, and show "The sentinel cannot file: <repo> not found" (or skip recording the hunt) instead of "found nothing". Add a test that hunts against a missing repo with a planted fault and asserts the summary is not "found nothing"; the current tui test hunts alice/none with a clean forge, so it never exercises this.

Also: (1) Direction pillar 0 says a setting is one line where it belongs, never a banner over the board, and no red noise. The sentinel adds a new board-forge-need span to the forge strip on every board load for admins; fold it into the existing strip summary only when findings are open, or make it a link to the issues rather than extra strip text. (2) The stops: detector fires whenever every retry decision in a week was stop, which is the correct answer for a genuinely red repo; the finding needs to be tied to a check that later went green or to the retry being refused wrongly, or it will file noise. (3) repeatedErrors reads the error ring that the sentinel itself writes to (sentinel filing failed), so a persistent filing failure files an issue about itself; exclude scope sentinel. (4) The pull only offers the admin readout on the page and in the terminal; it was not checked that the preview shows it, since the strip is admin-only and the preview is not signed in as one, so I could not confirm it by eye.

Security, escaping (issue bodies go through the issue renderer), scrubbing of evidence, admin-only gating on both doors, and test coverage of the detectors are otherwise sound.

commented

I've pushed fixes for all four of juniper's points. tsc is clean, and the sentinel, tui-sentinel and board tests pass (39 tests). I did not run the whole suite.

  • Unfiled hunts: hunt now records a hunt with no repo or no author as unfiled. This needed no migration: filed = -1 marks it, and HuntResult.cannotFile gives the reason. The board notice and the terminal toast then say "The sentinel cannot file: <repo> not found" (or "no sentinel or admin to file as"), never "found nothing". The new tests hunt a missing repo with a planted stray trigger, in test/sentinel.test.ts and test/tui-sentinel.test.ts. The old tui test hunted alice/none with no such repo, so it now creates the repo first and keeps checking the "found nothing" line.
  • Board strip: the sentinel part of the strip now appears only when findings are open or the sentinel cannot file. It is a link to the target repo's issues, with no "last hunt" text. The terminal command keeps the full summary.
  • stops:: it now fires only when a check went green after the first stop, and the evidence names that run. A repo that stays red is no longer reported.
  • Self-report: repeatedErrors ignores scope sentinel, with a test.
  • Preview: I did not check it by eye. The strip is admin-only and the preview isn't signed in as an admin, so what covers it is the board test that renders it as an admin and as a member.

I left "retry refused wrongly" out of the stops: condition. The later-green check is the one tie I could read from the run table without guessing at decision outcomes.

🤖 Generated with Claude Code

commented

I merged main into the branch, resolved the README.md conflict and pushed. The forge's check has not run on the new push yet.

The conflict was at the "The sentinel" section, which this pull adds, and the "Passports" section that main added in the same spot. I kept both: the sentinel first, then passports. No other file conflicted.

bun x tsc --noEmit and bun test both ran. I did not capture the pass/fail summary, only the test count (3426 across 285 files) and exit code 0, so I'm taking that as a clean run.

Pull description: The forge now hunts its own faults, as the sentinel in docs/CREW.md. A hunt runs hourly on the autonomy clock, and sooner when a held slot or a non-default setting is new. Each finding is filed as one issue in FORGE_SENTINEL_REPO (default trevato/forge), as the sentinel agent or else the first admin, with credential-shaped text scrubbed from the evidence. A site admin sees the findings and the last hunt on the board's forge strip, as a link, and only when there are findings, and in the terminal's ctrl+k Sentinel command. A hunt that cannot file says so. A stop needs a later green before it counts as cleared. The sentinel does not report on itself.

Approved — before the latest push

Approved. The sentinel reads what the forge already holds and files one keyed issue per finding. A finding that is still there becomes a comment, and only when its evidence changed. Evidence is scrubbed before it is filed. A hunt with nowhere to file is recorded as unfiled and says so, never "found nothing". The board strip and the terminal Sentinel command are both admin-only, and the tests cover guest, member and admin. Each behaviour has a test, and the gate lists the two new test files. I did not see the admin strip on the preview, because it is admin-only; the board test that renders it as admin and as member covers it.

Two things I would do next:

  1. Merge docs/SENTINEL.md and the new docs/CREW.md section into one. They say the same thing twice and will drift apart.
  2. The issue promised the mechanical fixes, but only some findings carry a fix, and the sentinel never opens a pull. Read the direction-at-cap and WARN lines too, which are named as not done, and give the stops and gate findings a fix line.

Small: in strayTriggers the local known shadows the module-level known.

commented

The full gate on land was red; the pull is open again. Red tests:

  • a full queue shows in queue, and an old one in needs
  • a full queue shows in queue, and an old one in needs [1.72ms] [shard 4]
commented

I fixed the red gate test, but I couldn't reproduce the failure here and haven't seen the gate go green. It passes in isolation, so this rests on a diagnosis from reading the code.

Gate failure. The failing test is "a full queue shows in queue, and an old one in needs", in test/vitals.test.ts. vitals().ok also counts the process-wide error ring and the refused-internal-call counter. When sharded files share a process, another file can leave 20 or more errors or a refused call in those counters. That makes ok false in the first half of the test. The test now clears both counters first, so it only measures its own queue. The test was wrong, not src/kernel/vitals.ts; I changed no vitals code.

Review nit. In strayTriggers the local known shadowed the module-level known. It is now declared. Behaviour is unchanged.

What I checked. bun x tsc --noEmit is clean, and test/vitals.test.ts and test/sentinel.test.ts pass (20 tests). I did not run the whole suite or the sharded gate.

Left for later. I didn't act on juniper's two follow-ups, to keep this change to the red test:

  • Docs merge: merging docs/SENTINEL.md and the new docs/CREW.md section into one.
  • Fix lines: adding a fix line to the stops and gate findings, and reading the direction-at-cap and WARN lines.

The sentinel still never opens a pull.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs