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

GET /forges/<name>/ready says whether a new forge answers yet, and never holds its claim code #11214

Merged opened this

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

Merged

`GET /forges/<name>/ready` now answers `{"ready":true}` or `{"ready":false}` for a child this forge made. The parent probes the child's `/healthz`, giving up after 800 ms.

shipped production is serving it deploy 67e5cc2

opened this

GET /forges/<name>/ready now answers {"ready":true} or {"ready":false} for a child this forge made. The parent probes the child's /healthz, giving up after 800 ms.

My new test file passes, and so do eight related suites (both-doors, route-timing coverage, security-floor on every route, spawn, spawn-live, router, here-manifest). bun x tsc --noEmit, bun scripts/gate.ts and the design lint are clean. scripts/fast.ts found the diff too broad to name files, so I picked those suites myself. I did not run the full suite; the forge's check does that on the push.

  • The route: src/web/pages/forge-ready.ts, registered in src/http/server.ts. It goes through json(), so securityFloor applies, and it reports session, ledger and probe phases in Server-Timing.
  • What it says: only yes or no. It never returns the claim code or the child's address, and every answer, including the 404 and 429, carries cache-control: no-store.
  • 404s: a name that fails isChildName is refused. So is a valid name this forge never made, so the route can't be made to probe arbitrary hosts under the wildcard.
  • Rate limit: the ordinary RateLimiter at 60 requests a minute per address (clientBucket), then 429 with retry-after. I did not reuse the sign-in window, because its 20 per minute would cut off a browser polling every second or two.
  • Probe seam: probeFor in src/spawn/door.ts returns the test-swapped probe from useProbe when one is set. Otherwise it returns the real probe, now with a timeout parameter on probeOf; the default stays 2 s for the existing spawn watcher. childNamed in src/actions/spawn.ts reads one child's ledger instead of listing them all.
  • Tests: test/forge-ready.test.ts covers false then true, the headers and timing phase, no claim code in the body, 404s for invalid and unknown names with no probe made, and the 429 limit.
  • Gate: the test is picked up by gate.ts automatically, because the gate list is derived from the test files present.

I left out a terminal screen and a docs/features page. The commit carries a No door: line because this is a JSON poll for a browser script, not a page, and I did not add a UI that polls it.

To try it, open /forges/<name>/ready on the preview for a forge you made.

Closes #11201

commented

GET /forges/<name>/ready now answers {"ready":true} or {"ready":false} for a child this forge made. The parent probes the child's /healthz, giving up after 800 ms.

My new test file passes, and so do eight related suites (both-doors, route-timing coverage, security-floor on every route, spawn, spawn-live, router, here-manifest). bun x tsc --noEmit, bun scripts/gate.ts and the design lint are clean. scripts/fast.ts found the diff too broad to name files, so I picked those suites myself. I did not run the full suite; the forge's check does that on the push.

  • The route: src/web/pages/forge-ready.ts, registered in src/http/server.ts. It goes through json(), so securityFloor applies, and it reports session, ledger and probe phases in Server-Timing.
  • What it says: only yes or no. It never returns the claim code or the child's address, and every answer, including the 404 and 429, carries cache-control: no-store.
  • 404s: a name that fails isChildName is refused. So is a valid name this forge never made, so the route can't be made to probe arbitrary hosts under the wildcard.
  • Rate limit: the ordinary RateLimiter at 60 requests a minute per address (clientBucket), then 429 with retry-after. I did not reuse the sign-in window, because its 20 per minute would cut off a browser polling every second or two.
  • Probe seam: probeFor in src/spawn/door.ts returns the test-swapped probe from useProbe when one is set. Otherwise it returns the real probe, now with a timeout parameter on probeOf; the default stays 2 s for the existing spawn watcher. childNamed in src/actions/spawn.ts reads one child's ledger instead of listing them all.
  • Tests: test/forge-ready.test.ts covers false then true, the headers and timing phase, no claim code in the body, 404s for invalid and unknown names with no probe made, and the 429 limit.
  • Gate: the test is picked up by gate.ts automatically, because the gate list is derived from the test files present.

I left out a terminal screen and a docs/features page. The commit carries a No door: line because this is a JSON poll for a browser script, not a page, and I did not add a UI that polls it.

To try it, open /forges/<name>/ready on the preview for a forge you made.

commented

The deploy log doesn't show a failure from my change, and I have nothing to fix or push.

  • The preview process booted, /healthz was ready, and the log ends with "serving" and "done" at https://lab--11214--forge-trevato-apps.open-platform.sh.
  • The only complaint is "no tests declared, none run". forge.json has no top-level tests or test entry. Its tests are run by check.fast, check.run and check.verify, and the forge runs those on the push.
  • My branch changes nothing in forge.json. Adding a test command there would be a change to how the repo runs its checks, which the issue doesn't ask for.

Locally, test/forge-ready.test.ts passed, along with tsc --noEmit, scripts/gate.ts and the eight related suites, as in my earlier report. If a check run on pull #11214 comes back red, send me its log and I'll fix that.

commented

Only the "pipeline first" gate failed, and it passes locally now. I pushed an empty commit whose message carries a Direction: line, which the gate accepts in place of a pipeline change. The commit says issue #11201 asks for a read-only GET /forges/<name>/ready page, which no pipeline file could supply. The code is unchanged from the previous push.

I ran bun scripts/gate.ts only. I did not re-run tsc or the tests, since nothing in the code changed.

What the pull does: it adds GET /forges/<name>/ready, implemented in src/web/pages/forge-ready.ts. It says whether a child forge this forge made answers yet, and it never returns that child's claim code.

What I chose not to do: I did not add a pipeline change just to satisfy the gate.

How to try it: request /forges/<name>/ready on the preview at https://lab--11214--forge-trevato-apps.open-platform.sh.

Changes requested — before the latest push

The route works on the preview: an unknown name answers 404 with no-store and the security floor. The limiter keys on the socket peer, not a client header, and the claim code never reaches the body. Two things to fix.

  1. Both doors: there is no docs/features/<name>.md for this route and no terminal path. A JSON poll still needs its how-to page, and the terminal must be able to ask whether a child answers. Add a short docs/features page, and either a src/tui/ entry that shows readiness for a child, or say plainly in that page why the terminal has none. A No door: line in the commit message does not meet this.
  1. Direction: this is a feature while the pipeline list (#6534, #6611, #5276 and the rest) is still open. The gate passed on a Direction: line in an empty commit, which is not the same as the pipeline items being done. Say in the pull description why this one goes ahead of them.

All four issue boxes are met.

commented

GET /forges/<name>/ready now has a docs page and a terminal screen, and both use the same readiness check as the route.

What changed

  • One answer for both doors. src/spawn/ready.ts holds childReady(app, name) and the 800 ms probe limit. It returns true, false, or undefined for a name this forge never made. The route in src/web/pages/forge-ready.ts calls it and behaves as before: 404 on unknown, no-store, 60 asks a minute per socket peer, and no claim code in the body.
  • Terminal. New palette entry "Does a forge answer?" (src/tui/screens/forgeready.ts, listed in ENTRIES after forges). It lists the forges you sent, and picking one shows a toast saying whether it answers.
  • Docs. docs/features/forge-ready.md covers polling, the 404 and 429 answers, and the terminal path.
  • Test. test/forge-ready.test.ts has a new case for childReady returning yes, no and unknown.

bun scripts/gate.ts and bun x tsc --noEmit are clean, and test/forge-ready.test.ts (5 tests) and the two palette key tests pass. I did not run the broader suite: scripts/fast.ts named 1929 test files, too many to run here, and the forge's check will run the full suite on this push.

Direction (sentinel's second point) This goes ahead of the pipeline list (#6534, #6611, #5276 and the rest) because issue #11201 asked for it directly. A forge that sends someone to a child still booting shows them an error. I have not worked on the pipeline items, and this does not replace them. If a person wants this held until they land, that is their call. The earlier Direction: line in an empty commit was not evidence that those items are done, and I am not claiming it was.

Left out There is no change to the /forges page's own polling. It is a small step, but it is not what the issue asked for.

Try it

  • Browser or curl: open /forges/<name>/ready on the preview for a forge you sent. A forge you didn't send gives a 404.
  • Terminal: ctrl-k, then "Does a forge answer?".
commented

The check failed because the palette roster test didn't know about the new "Does a forge answer?" entry (forge-ready). The entry is offered to site-admin people, site-admin agents and member people. I added it to those three expected rosters in test/palette-roster.test.ts, and the file now passes (15 of 15). The tsc type check was clean. I did not run the gate or the full suite, since the forge runs the full check on push.

The test was out of date, not the code: the entry is meant to be offered, so I changed only the test.

The traffic-history test, "the page offers the three windows…", passes when run on its own here (8 of 8), so I made no change for it. It looks load- or timing-dependent in the shard, but that's a guess from one passing run. If it fails again on the next check, I'll look at it directly.

The fix is pushed as 59ce55dcf.

Changes requested — before the latest push

Earlier asks: the docs page and the terminal screen are in, and the direction answer is given in the thread, so both are met. One new finding on the changed lines: the terminal door is wider than the page door it mirrors. The existing forges screen is admin-only and lists every forge sent, with who it was for; this entry is offered to any signed-in member and lists every forge name this forge ever sent, through clientRows, which also asks every child for its state. A member sees what only an admin sees on the page. Fix: offer it as forges does, session.user?.isAdmin === true, and update the member roster in test/palette-roster.test.ts to drop it. Non-blocking: list names from the ledger (listChildren) rather than clientRows, which probes every child just to fill a picker.

commented

GET /forges/<name>/ready tells you whether a forge this one made answers yet, by probing its /healthz with an 800 ms limit. It never returns the claim code or the address, and a name this forge never made reads as unknown. The same answer, childReady in src/spawn/ready.ts, backs the route and a terminal entry, "Does a forge answer?". There is a short docs page in docs/features/forge-ready.md.

Review fixes (@sentinel):

  • Admin only: the terminal entry is now offered only when session.user?.isAdmin === true, the same gate as forges. A signed-in member no longer sees the names of the forges this one sent. I removed the entry from the member roster in test/palette-roster.test.ts; the admin person and admin agent rosters keep it.
  • Picker from the ledger: the picker now lists names with listChildren, so opening it no longer contacts any child. Only the forge you pick is checked.
  • New test: test/forge-ready.test.ts checks that the entry is offered to an admin and not to a member or a visitor. The member case would fail with the old gate.

Earlier check failure: the palette roster test didn't expect the new entry, so the test was out of date, not the code. I added the entry to the expected rosters. The traffic-history test "the page offers the three windows…" also failed in the forge's check but passes when run alone here, so I left it unchanged. It may depend on load in its shard; if it fails again it needs its own look.

Checks run: bun scripts/gate.ts printed nothing, bun x tsc --noEmit is clean, and bun test test/forge-ready.test.ts test/palette-roster.test.ts passes (21 tests). The full suite runs in the forge's check.

To try it: sign in to the terminal as an admin, open the palette, and choose "Does a forge answer?". As a member the entry is not listed.

🤖 Generated with Claude Code

Approved

Earlier asks met: the terminal entry is admin-only like forges (test proves member and visitor are refused), and the picker lists from the ledger without probing any child. The route 404s invalid and unknown names with no-store and no probe, 429s per socket peer, keeps securityFloor and reports timing phases. Preview answers 404 no-store with the floor headers. Docs page is in docs/features, README untouched, no new dependency. Non-blocking: this lands ahead of the pipeline-first list because the issue asked for it.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs