Intent: the watcher keeps a 15-minute window; the same file says 24 h / 7 d history is not yet. A regression that crept in over days is invisible.
Done:
[ ] Once an hour the watcher writes one row per route pattern (route, hour, n, p50, p95) to a small table, capped at 200 routes x 168 hours, older rows deleted in the same write; state stays under FORGE_APP_DATA / the forge's own db the way read_hour does.
[ ] GET /api/v1/telemetry/routes?hours=N (admin) answers them; N is validated 1-168.
[ ] Tests: a write, the cap, the purge of an 8-day-old row, a 400 for hours=0.
[ ] The OpenAPI contract lists it (test/openapi-coverage.test.ts passes).
Files: src/perf/history.ts (new, one concept), src/perf/duty.ts (call it), the route in src/http/api.ts, src/contract/openapi.ts, test/perf-history.test.ts, docs/features/performance-watcher.md
Risks: src/http/api.ts is a busy file: keep the route to a few lines delegating to the new module; the write must not run on the request path
Brief: a performance builder (torque). A person assigns.
Every behaviour added has a test that fails without it; bun test passes; touching src/web runs bun scripts/design-lint.ts.
Intent: the watcher keeps a 15-minute window; the same file says 24 h / 7 d history is not yet. A regression that crept in over days is invisible.
Done:
read_hourdoes.GET /api/v1/telemetry/routes?hours=N(admin) answers them; N is validated 1-168.Files: src/perf/history.ts (new, one concept), src/perf/duty.ts (call it), the route in src/http/api.ts, src/contract/openapi.ts, test/perf-history.test.ts, docs/features/performance-watcher.md Risks: src/http/api.ts is a busy file: keep the route to a few lines delegating to the new module; the write must not run on the request path Brief: a performance builder (torque). A person assigns. Every behaviour added has a test that fails without it;
bun testpasses; touching src/web runsbun scripts/design-lint.ts.