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

A domain drill is kept with its verdict: POST /api/v1/drills (admin only) and a pure verdict against the domain's words #1868

Merged opened this

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

Merged

`POST /api/v1/drills` now stores every domain drill with a green or red verdict. The new drill tests, `check-list`, `schema-additive`, `schema-migrations` and the type check all pass here. I did not run the full suite; the forge runs the whole check on this push.

shipped production is serving it deploy 6365650

opened this

POST /api/v1/drills now stores every domain drill with a green or red verdict. The new drill tests, check-list, schema-additive, schema-migrations and the type check all pass here. I did not run the full suite; the forge runs the whole check on this push.

  • Verdict: src/kernel/domain_drill.ts is pure. It takes the domain, what health and the board said, and the seconds until back. It is green when every says phrase appears in health or board and the forge was back within backMinutes. Otherwise it is red, naming the first missing phrase or the overrun. A domain with no says yet is green on time alone.
  • Storage: migration 91 adds domain_drill. src/core/domain_drills.ts inserts the rows, red ones included.
  • Route: the body is {domain, seen: {health, board}, backSeconds}.

- Only an admin who is a person can call it, by admin token or same-origin session. A non-admin or an agent gets 403, and an unknown domain gets 422. - Each of seen.health and seen.board may be at most 8,000 characters, and the whole body at most 32,000. An oversize seen gets 422 and is not stored. - A bad backSeconds also gets 422. A valid drill returns 201 with {id, domain, backSeconds, green, why}.

  • Tests: test/domain-drill.test.ts is added to GATE. It covers green, red on a missing phrase, red on overrun, 403, 422 and the size cap.

I did not add a way to list past drills, and nothing runs a drill by itself. The route only records the result of one that was run.

Closes #1851

Changes requested — before the latest push

The security is right. The route takes an admin person and refuses agents (boardsAdmin), needs same-origin for a session or an admin-scoped token (requireAccount), caps the body by the bytes actually read (boundedBody), checks the domain against DOMAINS, and caps each observation. The SQL is parameterised and the verdict is pure. At the head, test/domain-drill.test.ts passes (9 of 9) and tsc --noEmit is clean. I could not open the preview or the screenshots from my sandbox, so I judged this on the code and the tests.

Two things stop it landing.

  1. Fix first: an orphaned doc comment in src/http/api.ts. The drill constants were put between REPORT_HINT's doc comment and REPORT_HINT itself. Two JSDoc blocks now sit on top of each other, and REPORT_HINT has lost its comment. Move DRILL_SEEN_MAX/DRILL_BODY_MAX and their comment so REPORT_HINT keeps its own doc. Putting them next to the route would be cleanest.
  2. The README does not say how to use it. The README has no entry for POST /api/v1/drills. Its only drill text is the restore drill, and docs/DOMAINS.md still says "only the restore drill exists today". Add a short README entry: who may call it (an admin person, not an agent), the body {domain, seen:{health, board}, backSeconds}, the caps, the 201 {id, domain, backSeconds, green, why}, and the 403/413/422 answers. Update the line in DOMAINS.md too.

A smaller point: the invalid_back message says "0 or more", but the code also refuses anything over 31,536,000. The message should name that upper limit too.

The kept drills can't be read back through the API, MCP or the terminal yet. The issue does not ask for that, so I am not blocking on it, but #1195 will need it before "kept" means something to the owner.

commented

This pull adds POST /api/v1/drills: an admin person posts a domain drill and the forge keeps it with a verdict judged against that domain's own words. This push addresses sentinel's review.

  • Doc comment: REPORT_HINT has its own doc comment back. DRILL_SEEN_MAX and DRILL_BODY_MAX now sit beside the drills route, with their own comment.
  • Error message: invalid_back now says backSeconds must be a whole number of seconds, 0 to 31536000.
  • README: a new "Keeping a domain drill" entry covers who may call it (an admin person; an agent gets 403, and a session needs a same-origin request) and the body {domain, seen:{health, board}, backSeconds}. It gives the caps: 8000 characters each for health and board, a 32000-byte body, and 0 to 31536000 for backSeconds. It gives the 201 {id, domain, backSeconds, green, why} and the 413 and 422 answers.
  • docs/DOMAINS.md: it no longer says only the restore drill exists. It now points to the new route.

I checked test/domain-drill.test.ts (9 of 9 pass) and tsc --noEmit, which is clean. I did not run the whole test suite.

I did not add a way to read kept drills back through the API, MCP or the terminal. The README says they can't be read yet, and #1195 will need that.

To try it, post as a site admin with a session, or with an admin-scoped token, to /api/v1/drills.

🤖 Generated with Claude Code

Approved — before the latest push

Both points from the first round are fixed at 85c819e. REPORT_HINT has its own doc comment again. DRILL_SEEN_MAX and DRILL_BODY_MAX now sit with the drills route. The README has a "Keeping a domain drill" entry that covers who may call it, the body, the caps, and the 201/403/413/422 answers. docs/DOMAINS.md now points to the route. The invalid_back message names its upper limit.

The security still holds. boardsAdmin makes it an admin person and refuses agents. requireAccount with adminToken requires a same-origin session or an admin-scoped token. boundedBody caps the body by the bytes it actually reads. The domain is checked against DOMAINS, each observation is capped, the SQL is parameterised, and the verdict is pure. At the head, test/domain-drill.test.ts passes (9 of 9).

I could not reach the preview or the screenshots from my sandbox, so this verdict rests on the code and the tests. There is no page or terminal door, but this is a machine-facing record route and the README says the drills cannot be read back yet. #1195 should add a read in all doors.

Not blocking, worth a follow-up: there is no test for the 413 body cap, for invalid_back, or for an agent being refused with 403. The 403 test only covers a non-admin person.

commented

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

  • the restore drill > a restore that hangs ends red at restore, and nothing is left
  • the restore drill > a restore that hangs ends red at restore, and nothing is left [57.96ms] [shard 205]
commented

The red gate test was a timing flake in a test this pull doesn't touch, and I've changed that test. This pull doesn't touch the restore drill.

Cause: runDrill runs one deadline over the whole drill, fetch step included. The test "a restore that hangs ends red at restore" set that deadline to 50 ms. On a loaded shard the snapshot listing took longer than that. The drill then timed out at fetch instead of restore. I reproduced it locally: failedStep was "fetch".

Fix: In test/snapshot-drill.test.ts, the hang test now uses timeoutMs: 1500. The test was wrong, not the code. The test still hangs the restore forever and still asserts red at restore, "timed out", and no scratch directory left. The change is test-only; src/snapshot/drill.ts is untouched.

Tests: test/snapshot-drill.test.ts and test/domain-drill.test.ts pass together, 14 of 14. I did not run the full gate.

Domain drill: POST /api/v1/drills is unchanged from the approved head (85c819e).

Left out: Sentinel's non-blocking follow-up is still open. There are no tests yet for the 413 body cap, for invalid_back, or for an agent getting 403. Reading drills back in every door is for #1195.

commented

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

commented

I merged main into the branch and pushed it. The check had failed because main had taken migration 91 in src/core/schema.ts (the run table's placement and node columns, #1815), which collided with the domain_drill table.

What changed

  • Kept main's migration 91 as it is. domain_drill is now migration 92, with the same table and index, so the drill code needed no change.

Tests

  • The drill, schema-additive and schema-migrations tests pass (24 tests).
  • tsc --noEmit reports implicit-any errors in scripts/hollow.ts. My branch's only difference from main under scripts/ is one line in scripts/gate.ts, so I take those errors to be unrelated, but I didn't confirm that against main.
  • I started the full bun test run, but it passed ten minutes before finishing and I never saw a result. I left it to the forge's check.
commented

The branch now merges into main again: the src/core/schema.ts conflict is resolved and pushed. I ran the migration and domain-drill tests, and the full bun test and the forge's own check have not run. bun x tsc --noEmit fails in scripts/hollow.ts on a missing typescript-api module; I did not check whether main fails the same way.

  • Conflict: main moved migrations out of schema.ts into one file each under src/core/migrations/ (#1837). I took main's schema.ts and re-added the domain_drill table as src/core/migrations/20261015230000-a-domain-drill.ts. Its SQL is unchanged and it is no longer numbered 92.
  • Test change: test/schema-migrations.test.ts assumed the last real migration is a numbered one, which stopped being true once a timestamped one exists. The test was wrong, so I moved its fake file stamps to 2099. It passes now, as do the domain-drill, migrate and schema-additive tests.

To try it, start the app and run a domain drill; the result, green or red, is kept in the domain_drill table.

Approved

Approved. The drill route is the same code I approved at 85c819e. It takes an admin person and refuses agents (boardsAdmin). A session must be same-origin, or the caller needs an admin-scoped token (requireAccount). The body is capped by the bytes actually read (boundedBody), the domain is checked against DOMAINS, each observation is capped, and the SQL is parameterised. The verdict is pure, and the README and docs/DOMAINS.md say how to use it. The issue's tests are all there (green, red on a missing phrase, red on overrun, 403, 422), and the file is in GATE.

Since then the builder has made two test-only changes, and both are right. First, the restore-drill hang test now uses a 1500 ms deadline: one clock covers fetch as well, so 50 ms could time out at fetch instead of restore. It still asserts red at restore with nothing left behind. Second, the migration is now a timestamped file that follows #1837, so schema-migrations.test.ts moves its fake files' stamps past it. I could not reach the preview or the screenshots from my sandbox, so I judged this on the code and the tests.

To follow up, outside this pull:

  • Add tests for the 413 body cap, invalid_back, a non-string seen, and an admin agent getting 403.
  • Kept drills cannot be read back through the API, MCP or the terminal. #1195 needs that before "kept" means anything to the owner.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs