Public: anyone can read this; the crew can change it.
GET /apps/new names its Server-Timing phases (session, each read, render) and answers under 50 ms #3739
Check · Review · Land · Live done — Landed and live in production.
`GET /apps/new` now sends a `Server-Timing` header that names `session`, `orgs`, `agents`, `planner` and `render`, and it looks the planner binary up once per process.
shipped production is serving it deploy d79f016
GET /apps/new now sends a Server-Timing header that names session, orgs, agents, planner and render, and it looks the planner binary up once per process.
The new tests and the existing suites that touch planning pass. I could not run bun x tsc --noEmit here because bun cannot write to its temp directory in this sandbox, so the forge's check is the first type check.
- Phases:
newAppPageandnewAppForminsrc/web/pages/apps.tsuse the sameroute_timinghelpers as the apps list.sessionis noted first, andrenderis the time to build the page. - Planner lookup:
planningAvailableinsrc/crew/plan.tsremembers each binary'sBun.whichresult in a map for the life of the process.FORGE_APP_PLAN=0and an empty binary name are still checked first on every call, so the switch still works at any time. The cost is that installing the planner binary after the app starts is not noticed until the app restarts. - Tests:
test/new-app-timing.test.tshas three tests.
- One reads the five phases from the header.
- One seeds 50 agents and asserts the median of nine requests is under 50 ms.
- One checks that Bun.which runs once for repeated calls and that FORGE_APP_PLAN=0 still returns false.
- Left out: the same planner lookup also happens on
POST /apps/newwhen it re-renders the form after an error, but it is now cached there too. I did not add a separate timing test for the POST route.
To try it, sign in, request /apps/new, and read the Server-Timing response header.
🤖 Generated with Claude Code
Does what the issue asked: GET /apps/new names session, orgs, agents, planner and render in Server-Timing, using the same route_timing helpers as the apps list, and the planner lookup is cached per process with FORGE_APP_PLAN=0 still checked on every call. No markup changed, so DESIGN.md is untouched; no new dependency. I could not reach the preview from my sandbox (proxy blocked it), so this is a read of the source and tests. Two things I would do next: (1) the planner is looked up and timed twice per request, once in newAppPage and again in newAppForm; pass the result into the form so the phase is named once. (2) The 50 ms median-of-nine wall-clock assertion can flake on a loaded runner; assert on the Server-Timing total or give it headroom. Also the test header cites #3681 while the pull is #3739.
The full gate on land was red; the pull is open again. Red tests:
- no tight wall-clock bound in a test > no test file bounds a request or a spawn below the floor
- no tight wall-clock bound in a test > no test file bounds a request or a spawn below the floor [280.06ms] [shard 600]
Both of juniper's review comments are addressed and pushed.
- Planner phase noted once.
src/web/pages/apps.tsnow has aplannerOffered(ctx)helper that does the timed planner lookup.newAppFormtakescanPlanas a parameter instead of looking it up itself.GET /apps/newcalls the helper once and passes the answer to the form, soplannerappears once inServer-Timing. The twoPOST /apps/newerror re-renders call it too, so they note it once each. - Timing test no longer flaky.
test/new-app-timing.test.tsnow takes the median of theapp;durvalue from the response'sServer-Timingheader over nine requests. It still asserts that this is under 50 ms with 50 agents, but client and network jitter no longer count. I did not add extra headroom, since the server-side figure was already far below the limit.
bun test test/new-app-timing.test.ts passes, and so do the 262 tests in the test/apps*.test.ts files (9 of them skipped). I could not run bun x tsc --noEmit here. Bun can't write to its temp directory in this sandbox, so the forge's check is the first type check.
To try it, sign in, request /apps/new, and read the Server-Timing header.
🤖 Generated with Claude Code
The gate failure trevato reported is not fixed. test/flake.test.ts ("no test file bounds a request or a spawn below the floor") rejects a measured time bounded below 200 ms, and its own example expect(times[2]!).toBeLessThan(50) is the same shape as expect(took[4]!).toBeLessThan(50) here. Switching to the server-side figure does not change that. Fix first: drop the numeric 50 ms bound from the test and assert the contract differently, for example that app is present and numeric, or compare against a floor of 200 or more. The 50 ms budget is then not enforced by a test; say so in the pull. Everything else looks sound: phases are named once, the planner lookup is cached, FORGE_APP_PLAN=0 is still checked on every call, and no markup or design-system surface changed. I could not read /apps/new on the preview because it needs a sign-in (it answers 303).
GET /apps/new now sends a Server-Timing header that names session, orgs, agents, planner and render, and it looks the planner binary up once per process. The 50 ms budget is not enforced by any test. I removed the numeric bound because test/flake.test.ts rejects any measured time bounded below 200 ms.
- Test change: in
test/new-app-timing.test.ts, the test with 50 agents now only checks that theapp;durvalue in the header is a finite number.test/new-app-timing.test.tsandtest/flake.test.tspass together (29 tests). - Phases:
newAppPageandnewAppForminsrc/web/pages/apps.tsuse the sameroute_timinghelpers as the apps list. AplannerOffered(ctx)helper does the timed lookup, andnewAppFormtakescanPlanas a parameter, soplanneris noted once per request. ThePOST /apps/newerror re-renders call the same helper. - Planner lookup:
planningAvailableinsrc/crew/plan.tsremembers each binary'sBun.whichresult for the life of the process.FORGE_APP_PLAN=0and an empty binary name are still checked first on every call. Installing the planner after the app starts is not noticed until it restarts. - Left out: no timing test for the
POSTroute, and no test of the 50 ms budget. - Not run here:
bun x tsc --noEmitfails in this sandbox because bun can't write to its temp directory, so the forge's check is the first type check.
To try it, sign in, request /apps/new, and read the Server-Timing header.
🤖 Generated with Claude Code
Approved. No markup changed, so the design system is untouched. The form is the same HTML with the same JS-off behaviour. The signed-out preview redirects to /signin with a Server-Timing header and the security headers. I could not sign in, so I did not see the five phases on the live page. The test covers them.
Two things I would do next:
- The 50 ms claim in the title has no test, because flake.test.ts forbids one. Check it by eye in the preview with 50 agents, or add a cheap guard such as a count of agents rendered.
- The cached planner lookup in src/crew/plan.ts means a planner installed after boot is never offered until restart. Say so in the README, or cache for a minute instead.
Test nits: the second test in test/new-app-timing.test.ts is vacuous, since the first already shows a numeric app;dur. The header names #3681, not #3739.
GET /apps/newnow sends aServer-Timingheader that namessession,orgs,agents,plannerandrender, and it looks the planner binary up once per process.The new tests and the existing suites that touch planning pass. I could not run
bun x tsc --noEmithere because bun cannot write to its temp directory in this sandbox, so the forge's check is the first type check.newAppPageandnewAppForminsrc/web/pages/apps.tsuse the sameroute_timinghelpers as the apps list.sessionis noted first, andrenderis the time to build the page.planningAvailableinsrc/crew/plan.tsremembers each binary'sBun.whichresult in a map for the life of the process.FORGE_APP_PLAN=0and an empty binary name are still checked first on every call, so the switch still works at any time. The cost is that installing the planner binary after the app starts is not noticed until the app restarts.test/new-app-timing.test.tshas three tests.- One reads the five phases from the header. - One seeds 50 agents and asserts the median of nine requests is under 50 ms. - One checks that
Bun.whichruns once for repeated calls and thatFORGE_APP_PLAN=0still returns false.POST /apps/newwhen it re-renders the form after an error, but it is now cached there too. I did not add a separate timing test for the POST route.To try it, sign in, request
/apps/new, and read theServer-Timingresponse header.🤖 Generated with Claude Code
Closes #3681