ADR-007: Information Architecture — Everything Is a Resource Kind
Status: Amended (2026-05-30, 2026-06-11) — see Amendment and Amendment: frontend kind UIDate: 2026-05-28 Deciders: Yuxing Wu Related: ADR-001, spec 002-ui-shell
Context
Spec 001-mcp-gateway introduced the mcp_server resource kind on top of a kind-agnostic Resource framework (see ADR-001). mcp_server is the kind that ships today.
Spec 002-ui-shell has to decide how resource kinds — and cross-cutting user-facing concepts that are not resources in the domain sense (the audit log, settings) — surface in the navigation. The naïve approach is to introduce a second top-level concept in the IA: a "surface" axis sitting next to the "resource kind" axis, so the sidebar reads as two unrelated groups (Resources vs Features). The redesign goal was the opposite: one consistent navigation model that does not have to be re-litigated as the product grows.
A related question is what to do in the sidebar with a kind that is not built yet. The temptation is to list it as a "coming soon" placeholder. In practice this fills the sidebar with dead entries and makes the product look like an unfinished scaffold.
Decision
Adopt a single-axis information architecture: every user-facing managed entity is a resource kind, surfaced through the same sidebar group.
Concrete consequences:
- The sidebar has exactly two groups today — Resources (resource kinds) and System (cross-cutting tooling: Observability, Settings). When a new kind ships, it appears in the Resources group; no new group is introduced.
- The kind-agnostic Resource framework is the only abstraction the UI models; there is no separate "surface" registry sitting beside it.
- Sidebar policy: no "soon" placeholders. A kind is not shown in the sidebar until it works. The rendered UI shows only what works today.
Consequences
Positive
- A new kind plugs into the same Resources group with a single nav entry — no IA renegotiation per kind.
- The sidebar always reads as "here is what Coffer does", not "here is what Coffer plans to do." A first-time visitor sees a product, not a backlog.
- The Resource framework from ADR-001 is the only abstraction the UI needs to understand; the UI does not need a separate "surface" registry.
Negative
- "Resource" is forced to carry concepts that are not always called "resources" in user conversation. Mitigated by the user-facing label being the kind's own noun (e.g. "MCP server"), not the word "resource".
- The rule "don't show unbuilt kinds in the sidebar" trades roadmap discoverability for a less noisy day-one UI. Users who want the roadmap read
.specify/memory/roadmap.md, not the sidebar.
Alternatives Considered
Add a separate "surface" concept alongside resource kinds. Rejected.
- Doubles the IA complexity without buying anything: the cross-cutting surfaces we have (Observability, Settings) are single fixed entries handled by the System group.
- Forces every future spec to first decide "is this a kind or a surface?" — a decision that adds no user-visible value.
Show unbuilt kinds in the sidebar as disabled / "soon" placeholders. Rejected.
- Reads as an unfinished scaffold (the explicit anti-goal in spec 002's motivation).
- The forward-visibility benefit is real but small, and already provided by
.specify/memory/roadmap.md.
Per-kind separate top-level navigation (no Resources group). Rejected.
- Works for one kind, but degrades into a flat, ungrouped list as kinds are added. The Resources / System split keeps the sidebar readable.
Amendment (2026-05-30)
The original decision modelled every user-facing managed entity as a resource kind on a single axis. Implementation surfaced a concept that does not fit that mould: agents (the Claude Code / Codex / built-in agents you use). An agent is a consumer of vault assets, not an asset the vault manages — so collapsing it onto the resource-kind axis mislabels it. The shipped IA therefore keeps the kind-agnostic Resource framework but adds a second axis for consumers, and groups the sidebar by role rather than by "is this a kind?".
What changed:
- Agents are a separate axis, not a resource kind. They are surfaced at
/agents(list) and/agents/:name(detail) under their own Agents sidebar group. They are NOT shown in the/resourceskind browser, because they are not vault assets. - The sidebar is grouped by role, not on a single resource-kind axis:
- Agents — the consumers (
/agents). - Resources — the assets agents draw on, modelled as kind-agnostic resource kinds and surfaced through the kind registry. The nav entry is labelled "MCP servers" and lists only the kinds that register a list/card UI (today just
mcp_server); the route is/mcp-servers(/resourcesis kept as a legacy redirect for old bookmarks). - System — cross-cutting tooling: the Audit log (
/audit) and Settings (/settings).
- Agents — the consumers (
- The audit log lives at
/audit, not/observability. The shipped router maps/audit→ the audit-log page and keeps/observabilityas a legacy redirect to/audit. Observability (system health / metrics) is a DISTINCT, reserved future surface — it is not the audit log and is not in the sidebar yet. - List surfaces converge on one shared, searchable, paginated table. All list views use a single
DataTablecomponent (built-in search / filter / pagination) where a row click opens that item's detail page and row actions are compact icons. Cards are reserved for welcome / empty states only. This supersedes the earlier card-grid description of resource lists. - Future groups/items (Chat, Channels, Skills, Knowledge, Memory, Observability) are planned but not shown today. The "no soon placeholders" policy from the original decision still holds: the sidebar documents the role-based structure but adds no dead nav entries.
What survives unchanged from the original decision: the kind-agnostic Resource framework is still the only abstraction for assets; new resource kinds still plug into the Resources group with a single nav entry; and the sidebar still shows only what ships today. The amendment narrows "everything is a resource kind" to "every asset is a resource kind, surfaced under Resources; consumers (agents) and cross-cutting tooling (System) are their own role-based groups."
Amendment (2026-06-11) — frontend kind UI
Spec 005 shipped skills as a backend resource kind (identity, lifecycle, audit, on_delete cascade all ride the kind-agnostic framework) but as bespoke frontend pages (/skills, /skills/:name) rather than through the frontend kind registry. That left the UI in an undeclared half-state: the registry promised "one generic browse surface per kind" while skills quietly bypassed it.
This amendment settles the frontend pattern:
- The backend axis is unchanged. Every asset is a resource kind; the unify-identity/lifecycle/audit rule and the never-unify-invocation rule (ADR-001) still hold for all kinds.
- The frontend kind registry is scoped, not universal. It serves kinds whose UI fits the generic browse pattern (card/table + config-centric detail) — today
mcp_serverunder/mcp-servers. It is NOT a promise that every kind renders through it. - Asset kinds with richer interaction models ship bespoke page-sets. Skills (per-agent bindings, file viewer, drift verify) live at
/skillswith their own pages. This is the sanctioned pattern, not an exception. - New kinds choose explicitly at design time: register a kind UI for generic browsing, or ship a bespoke page-set under their own route. The spec for the kind must state which.
Consequence: "skills appear in the sidebar's Resources group" (the 2026-05-30 amendment's future-groups list) is now shipped — the Resources group contains MCP servers (/mcp-servers, registry-driven) and Skills (/skills, bespoke).