/info renders JSON on hard load: move service-discovery JSON under /api, free /info for the SPA #96
Labels
No labels
burndown-2026-06
burndown-2026-08
headless
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
c#
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
role/advocate
role/director
role/exec
role/frontend
role/gamedev
role/human
role/platform
role/qa
role/science
role/sysadmin
state
ambient
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-gaming/eco-app#96
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
https://eco-app.coilysiren.me/infosometimes renders JSON instead of the React page, and API endpoints should live under/api. Filed from a batch of eco-app feedback Kai handed the director surface. Touchessrc/eco_mcp_app/http_app.py.Root cause
http_app.pyregistersRoute("/info", service_info, methods=["GET"])(the route list around line 732).service_inforeturns a JSONResponse service-discovery blob. The SPA also owns a client-side/inforoute (App.tsx). A soft in-app navigation renders the React page, but a hard load or refresh of/infohits the Starlette route first and returns JSON. That is the "sometimes json somehow" Kai saw.Fix
/info. It is a debug/discovery endpoint, not a user surface, so relocate it under the API convention, e.g./api/service(or/service.json). Kai: "I would have assumed all the api endpoints are underneath an api/."/infoso it falls through to the SPA fallback and renders the React Info page on hard load./previewJSON routes for the same convention gap (/healthz,/page-auth) and note in the PR which stay put (health/auth probes have external contracts) versus which move. Do not break the/preview*.jsondata plane the SPA fetches, and do not break/jobs/apior/replay/apimounts.Done when
A hard load of
/inforenders the React page, the service-discovery JSON lives under an/api-style path, and the SPA data plane + health/auth probes still resolve. Add or update anhttp_approute test that hard-loads/infoand asserts HTML, not JSON.ward exectest + lint gates green.🔒 Reserved by
ward agent --driver claude— containerengineer-claude-eco-app-96on hostKAI-DESKTOP-TOWERis carrying this issue (reserved 2026-07-07T06:14:43Z). Concurrentward agentruns are blocked until it finishes or the reservation goes stale (2h0m0s TTL);--forceoverrides.Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).
run seed context — what this run is carrying (ward#609)
coilyco-gaming/eco-app#96· branchissue-96· driverclaude· workflowdirect-mainengineer-claude-eco-app-96· wardv0.419.0· dispatched2026-07-07T06:14:43ZIssue body as seeded:
Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.419.0).
— Claude (she/her), via
ward agentWARD-OUTCOME: done - service-discovery JSON now lives at /api/service; /info falls through to the SPA and renders the React Info page on hard load.
Cleaner than I expected. The root cause was exactly as written: a single Starlette
Route("/info", ...)shadowing the SPA catch-all, so the fix was a one-line route move plus freeing the path. The only thing that fought back was a second, non-obvious assertion —test_smoke.py::test_infoalso hard-coded/inforeturning the JSON blob, so the suite caught the move immediately and I repointed it at/api/service(and renamed ittest_service_discovery).On the audit:
/healthzand/page-authstay on their bare paths — both are probes with external contracts (k8s liveness hits/healthz, the SPA hardcodes/page-auth), not user surfaces that collide with SPA routes, so moving them would only break consumers. The/preview*.jsondata plane and the/jobs/api+/replay/apimounts are untouched.Confident in the result: 491 tests green, lint + precommit clean, and there's now a test that hard-loads
/infoand asserts HTML-not-JSON so this can't silently regress. No follow-ups worth filing — the/preview*family arguably wants the same/api-prefix treatment for consistency someday, but that's a larger data-plane rename the issue explicitly scoped out.