Sign in to edit tickets from this page.

← all tickets · home

Remove _links from the render pipeline

resolved 6dafb799-8912-4c20-bc7d-67b2abf4ddac

created_at
2026-04-24
updated_at
2026-04-24
code_context
src/render.rs, src/views.rs
priority
P2
ticket_type
chore
resolved_at
2026-04-24
resolution
accepted

Body

MOTIVATION

_links is a reserved top-level key in render payloads. The three view builders populate it with a map of { key: { href, label } }, and render::render_page emits a breadcrumb nav strip above the <h1> from that map, then filters it out of the body render so it doesn't appear twice.

Every entry across all three pages is either a site constant, a slug-derived URL, or a trivially-computable adjacent URL. None of it is data about the world, turn, or entity the page represents. It's navigation chrome — UI-only metadata — being carried through the JSON payload.

The operating principle going forward: the data model describes what exists; the UI layer decides how to present what exists. Nothing that only makes sense in the context of a rendered page belongs in the payload. _links fails that test and is removed.

This follows the same pattern and same spec shape as ticket 8d949ef3-6d47-47e8-bc8e-1356913b4639 (remove _embed). That ticket is prior art for this one; there is no parent edge.

A later ticket will reintroduce navigation properly: the renderer takes a NavContext carrying {page_type, world_slug, turn, entity_id, chain_range} and produces the breadcrumb strip from that context, not from the payload. That ticket is not filed yet and is not blocked by this one — rendering pages without a breadcrumb strip for the brief window between this ticket and the follow-up is the explicit temporary regression.

================================================================ WHAT _links DOES TODAY

Scope:

Per page, the contents are:

All derivable from the route, the slug, and the chain range. None are properties of the world, turn, or entity.

Who produces it today:

Who consumes it downstream:

================================================================ THE RULE

After this ticket: _links does not exist as a concept in the render pipeline. No payload-level navigation map. The renderer has no reserved top-level keys; every top-level key in the payload renders as body data per the universal rules.

Temporary regression: pages render without a breadcrumb strip. Navigation between pages is possible by URL or via the /dashboard listing (which was already a working discovery surface and still is).

A later ticket will add NavContext-driven breadcrumb generation in the renderer — out of scope here.

================================================================ FILE-BY-FILE CHANGES

src/render.rs

Remove:

Update doc comments:

Remove test:

Keep:

src/views.rs

In each of build_session_payload, build_turn_payload, and build_entity_payload:

Update module-level //! comment: remove the mention of _links being woven in. The builders now produce pure payloads — data about the world/turn/entity, nothing else.

Remove the prev_turn/next_turn chain-range query in build_turn_payload: the let chain = call_tool(env, "list_turns"...).ok(); block and the chain_range computation it feeds. That call only existed to gate prev_turn / next_turn link emission. With no _links to produce, the second MCP call is dead weight. Deleting it also removes one unnecessary round-trip per turn-page request.

Update the affected tests — three builders, several assertions:

src/server.rs

No source changes required in this ticket. The handlers already don't produce _links; they only pass the payload through. Once the follow-up NavContext ticket lands, server.rs will construct and pass the context. That is not this ticket.

src/lib.rs

No changes.

Other files

None.

================================================================ ACCEPTANCE

================================================================ EXPLICITLY OUT OF SCOPE

================================================================ NO OPEN KNOBS

Same narrow discipline as the _embed removal. No judgment calls required from the handler. Any question that arises mid-implementation gets a comment on the ticket, not a guess.

Proposed resolution

Done per spec. Pure removal. Bundled with ticket 6438ac5b into a single deploy at the caller's explicit request.

Source changes

src/render.rs (−79 / +5):

src/views.rs (−131 / +5):

src/server.rs / src/lib.rs / Cargo.toml / MCP handlers — unchanged.

Receipts

Commits on main, pushed to gitlab:

Tests (merged main):

Deploy: bash k8s/deploy.sh clean; pod chukwa-59bdbdf6d7-6z6fp Running 1/1; watcher reconnected emitting orphans_killed 0 (new pod, no pre-existing shells) and backlog for all three live tickets.

Production smoke on live ant-verify world:

Expected and called-out temporary regression

HTML pages at /w/:slug/... now render with NO breadcrumb strip at the top. Navigation between pages happens via URL or via the /dashboard listing (which remains a working discovery surface). The follow-up NavContext ticket (not yet filed) will restore the breadcrumb strip constructed from {page_type, world_slug, turn, entity_id, chain_range} rather than from payload directives.

replace_exact_skipping_tags continues to carry the one expected dead_code warning — still reserved for the pattern-rules follow-up (also not yet filed).

Per standing guidance I am not confirming this ticket — only proposing. Over to you.

History (4 events)

Sign in as a human to drive this ticket from the page, or use the MCP tools.