ID-135 {135.2} PRODUCT — OKF human search/browse + source_document provenance UI
ID-135 {135.2} PRODUCT — OKF human search/browse + source_document provenance UI
Section titled “ID-135 {135.2} PRODUCT — OKF human search/browse + source_document provenance UI”Status: PRODUCT artefact — not self-ratified. Authored 28/06/2026 by a fresh Planner instance (Q-PLANNER-2 — NOT the {135.1} RESEARCH author). British English throughout; dates DD/MM/YYYY.
Predecessor:
{135.1}RESEARCH.md (gated PASS) — its per-surface reuse-or-rebuild verdict matrix (§2), read-contract dependency split (§3), IA findings (§4) and 10 OQs (§5) are treated as settled and built upon, not re-litigated.Invariant-tag convention: this spec’s own invariants are tagged BI-1 … BI-32 (the bold, list-anchored entries under Behavior). Wherever this spec references an invariant owned by another Task it is always written with the owning Task id — e.g. id-131 BI-20, id-111 B-28 — so a bare BI-N always denotes THIS spec’s own invariant.
Summary
Section titled “Summary”Two net-new, read-only, authenticated human-facing UI surfaces over the OKF L-records model, filling the gap left by the eliminated IMS browse/item UI (id-131 BI-12):
- Surface A — a corpus search/browse/filter surface over the whole knowledge base
(
q_a_pairsanswers,source_documentsvia their passages, andreference_items), built on the {131.20} §9 ratified search/sourcing contract (search-ALL-grains default + bounded answer-first boost). - Surface B — a
source_documentdetail/provenance viewer at/documents/[id]: the provenance field set, the version chain, a citations panel, and the list ofq_a_pairsderived from the document.
Problem
Section titled “Problem”The IMS browse + item-detail UI that previously let a human search and verify corpus records is
deleted under id-131 BI-12; /browse (content_items) goes away and /library is re-pointed
to q_a_pairs (id-131 BI-33). That leaves two concrete gaps verified absent on disk: there is
no reader-facing q_a_pair search/browse page (find app -path '*q-a-pairs*' -name page.tsx
returns only admin surfaces) and no source_document detail page (app/documents/[id]/
contains only diff/ — ls app/documents/[id]/page.tsx → No such file). Both surfaces are
therefore net-new as pages, but the HARD RULE from {135.1} holds: no net-new component is
proposed without first reusing/extending the shipped id-111 reference stack and id-117 diff
stack.
Code-intelligence orientation (cited verbatim, not paraphrased)
Section titled “Code-intelligence orientation (cited verbatim, not paraphrased)”GitNexus tools run against the indexed repo at absolute path
/Users/liamj/Documents/development/canonical (the bare name canonical is ambiguous — two
registered repos resolve to it; the path disambiguates). On-disk existence re-verified in the
id-135 worktree at HEAD 3a222eac.
gitnexus_context({name: 'ReferenceContent'})→Function:app/reference/reference-content.tsx:ReferenceContent(lines 44–165). incoming.calls: exactly ONE —ReferencePage(app/reference/page.tsx). outgoing.calls:ReferenceSearchBox,ReferenceFilterControls(app/reference/reference-content.tsx),ReferenceCard(components/reference/reference-card.tsx),ReferenceLoadingSkeleton/ReferenceEmptyState/ReferenceErrorState(components/reference/reference-states.tsx),Button(components/ui/button.tsx),useReferenceData(hooks/reference/use-reference-data.ts). processes: []. This is the complete Surface-A reuse base — page shell, search box, filter controls, result card, and the loading/empty/error state trio are all already built and composed here.gitnexus_context({name: 'useReferenceData'})→Function:hooks/reference/use-reference-data.ts:useReferenceData(lines 146–369). incoming.calls: exactly ONE —ReferenceContent. outgoing.accesses:isLoading,error. processes: []. The list+search data hook is single-caller — safe to copy-and-generalise to a polymorphic multi-grain result type without disturbing the reference surface.gitnexus_context({name: 'UnifiedDiffContainer'})→Function:components/diff/unified-diff-container.tsx:UnifiedDiffContainer(lines 74–105). incoming.calls: exactly ONE —DocumentDiffPage(app/documents/[id]/diff/page.tsx). outgoing.calls:BinaryDiffPane(components/diff/binary-diff-pane.tsx),toRevisionBlob,RevisionDiffView(components/item-detail/revision-diff-view.tsx),deriveRenderMode(lib/diff/unified-revision.ts). processes: []. The version-compare leg Surface B needs is already shipped (id-117) — Surface B links to / embeds it, never rebuilds it.- On-disk verification (load-bearing reads):
app/api/source-documents/[id]/route.ts(the GET behind Surface B’s “derived records”) today.from('content_items').select('id, title, content_type, primary_domain, primary_subtopic, freshness, created_at').eq('source_document_id', id).is('archived_at', null)and returns{ ...doc, content_items: items ?? [] }.content_itemsis eliminated by id-131 BI-9 — this read breaks and must re-point toq_a_pairs(see BI-29 / BND-1).app/api/source-documents/[id]/versions/route.tsexists and calls theget_document_version_chainRPC (id-117) — version-chain read is ready.
ccc searchfallback disclosure: the cocoindex-code daemon errors in this environment (DaemonStartError— same failure {135.1} recorded), so thecccorientation lane is unavailable. The findings above rest on the definitive GitNexus + on-disk evidence, which is conclusive for the two specific surfaces. This is not a greenfield surface — the reuse base is fully identified.
Goals / Non-goals
Section titled “Goals / Non-goals”Goals. Ship two read-only surfaces that (a) let any authenticated user find any corpus record by natural language with no taxonomy knowledge (the id-71 O7 “find and verify in under 2 minutes” outcome), and (b) let them open a source document and trust its provenance, version history, citations, and derived answers. Maximise reuse of the id-111 reference stack and id-117 diff stack per the {135.1} verdict matrix.
Non-goals (v1). No write/edit/delete affordances on either surface (single-pair edit lives in
the re-pointed /library; document edit/re-ingest lives in the id-117 review flow). No
related-records affordance and no keyword pre-filter facet — both are the §9-dropped co-design
backlog seam (OQ-7 / OOS-1). No new top-level nav for Surface B. No exposure of similarity
scores, model names, or “AI search” chrome on either surface (AI-invisible-infrastructure).
Figma: none provided. Both surfaces extend shipped id-111 / id-117 visual patterns (reference list/card/detail, unified-diff container) under the Warm Meridian design system; new visual states (per-kind result card, provenance panel, version list, citations panel) inherit those patterns rather than introducing a new visual language.
Ratification decisions & assumptions-against-target
Section titled “Ratification decisions & assumptions-against-target”Authored under {135.1}‘s recommended defaults. Each item below is flagged for the human ratification gate at the parent before implementation. Behaviour invariants reference these by tag.
| Tag | OQ | Type | Decision baked into this spec | Alternative / dependency |
|---|---|---|---|---|
| RD-1 | OQ-1 | RATIFICATION DECISION | Surface A is a distinct corpus-search surface that EXTENDs the id-111 reference stack and LINKS INTO /library (the id-131 BI-33 q_a_pair viewer) for single-pair read/edit and into /documents/[id] for SD detail — NOT a mode of /library. Aligns with the parent’s “net-new surfaces” framing. | Alternative: fold Surface A into the re-pointed /library as a cross-grain mode. Rejected as default — overloads a single-grain editor surface and straddles two Tasks. May need an Orchestrator Task-boundary call. |
| RD-2 | OQ-4 | RATIFICATION DECISION | Surface A exposes a user-facing kind-narrow control (answers / documents / references), since search-ALL is the default and a narrow is a natural non-AI affordance (BI-15). | Alternative: the one shipped profile is entirely implicit with no control. The type/scope param NARROWS only either way (§9). |
| RD-3 | OQ-8 | RATIFICATION DECISION (recommended) | Surface A renders reference_item hits inline as one of three result kinds, linking out to /reference/[id] and reusing the id-111 reference card (BI-12/BI-13). | Alternative: exclude reference hits from Surface A. Rejected — contradicts the §9 search-ALL default. |
| RD-4 | OQ-9 | RATIFICATION DECISION (recommended) | Surface B renders an inline version list and links out to /documents/[id]/diff for pairwise compare (BI-25/BI-26); it does not embed a full diff inline by default. | Alternative: embed UnifiedDiffContainer inline. Either way the diff component is REUSED, never rebuilt. |
| AAT-1 | OQ-2 | ASSUMPTION-AGAINST-TARGET | The whole Surface-A result model assumes the §9 polymorphic hybrid_search over EMB-STORE (per-grain normalise + merge, content_chunk→SD collapse, owner_kind in result) — BI-10/BI-12. | Implemented-to-commit on cmux-worker-subo-search-13111 (pre-merge; {131.11} G-SEARCH). Reconcile against the shipped PolymorphicSearchResult + record_embeddings (4-arm UNION); Task-level dep = {131.11} post-GO apply+merge. |
| AAT-2 | OQ-3 | ASSUMPTION-AGAINST-TARGET | Surface A consumes a typed polymorphic /api/search web result shape (BI-14), not today’s SearchResult extends ContentListItem (types/content.ts:135). | Implemented-to-commit on cmux-worker-subo-search-13111 (pre-merge; {131.11} AC4). Reconcile the card/hook field set against PolymorphicSearchResult (8-field value-path) + record_embeddings before impl. |
| AAT-3 | OQ-4 | ASSUMPTION-AGAINST-TARGET | Ranking = a named PROFILE selected by application_type (one shipped: answer-first, procurement = q_a_pair-primary), answer-first as a bounded ~×1.1 boost AFTER per-grain normalisation (BI-11). | Implemented-to-commit on cmux-worker-subo-search-13111 (pre-merge; §9 / {131.11}). q_a_pair ×1.1 application_type profile shipped over record_embeddings; workspace_id→application_type threading into /api/search. |
| AAT-4 | OQ-6 | ASSUMPTION-AGAINST-TARGET | Surface B’s citations panel (BI-27) reads the id-131 BI-23 CITE-EXT extended cited_target_kind ({q_a_pair, reference_item, source_document, concept}). | Enum SHIPPED ({131.10} G-CITE-WINRATE); read consumers land with {131.11}. citations is 0 rows today; design against the extended contract. |
| BND-1 | OQ-5 | BOUNDARY → DR-012 decided | Surface B’s “derived q_a_pairs” requires app/api/source-documents/[id]/route.ts to STOP joining eliminated content_items and read q_a_pairs via source_document_id (BI-29). | DR-012: decided — Path β. id-131 owns the route re-point (its content_items-elimination sweep); id-135 consumes the derived_pairs field only (Task-level dep). {135.11} dropped. |
| BND-2 | OQ-10 | BOUNDARY (recommended) | Surface B ADDs the sibling app/documents/[id]/page.tsx detail landing into id-117’s existing /documents/[id] route tree (BI-22). | Recommend id-135 owns the addition (same surface family); confirm no shared-seam coordination needed with id-117. |
| OOS-1 | OQ-7 | OUT OF SCOPE (decided out) | No related-records affordance and no keyword pre-filter facet in either surface v1 (BI-32). | §9 §7.4/§7.5-dropped; recorded as the named co-design backlog seam, not a v1 feature. |
S438 {135.19} reconcile (D7 → NO-1): RD-1 routes Surface A
answerresults into/libraryon the premise that id-131 BI-33 repoints/libraryto a q_a_pair viewer. That repoint was deferred out of id-131 to the id-71 family ({71.16}/ID-59/ID-69);/librarystill readscontent_items. Surface A can still be built (the card links to a route), but the answer-result deep-link destination is an open owner decision (NO-1) — not an id-131 guarantee.
Behavior
Section titled “Behavior”Invariants below are this spec’s own (BI-1 … BI-32). References to other Tasks’ invariants are Task-qualified per the convention note above.
Cross-cutting invariants (both surfaces)
Section titled “Cross-cutting invariants (both surfaces)”-
BI-1 (authenticated, read-only). Both surfaces require authentication; neither is added to
proxy.tspublicRoutes, so an unauthenticated visitor is redirected to/login. Server reads go through the authenticated-client pattern (getAuthorisedClient()/getAuthenticatedClient()→ checkauth.success, route API failures viaauthFailureResponse(auth)). Neither surface exposes any create/update/delete affordance for corpus records. -
BI-2 (role-uniform). All authenticated roles (admin, editor, reviewer, viewer) see the identical surface and the identical set of records — there is no role-based gating of which records appear or which sections render (inherit id-111 B-21). Roles differ only in what they can do on the destination surfaces they link to (e.g. editing in
/library), never here. -
BI-3 (AI-invisible infrastructure). Neither surface displays similarity/relevance scores, embedding/model names, ranking-profile internals, or any “AI search”/“semantic”/“vector” chrome. Results appear as if the data was always there and naturally findable. The answer-first boost (BI-11) and per-grain ranking are entirely implicit. (Per
ai-visibility-policy.md.) -
BI-4 (Warm Meridian semantic tokens, AA). All colour/spacing/typography uses Warm Meridian semantic tokens only — never raw Tailwind colours. Per WCAG 2.1 AA, no state, kind, or status is signalled by colour alone: every record-kind label, status badge, and error/empty state carries text and/or an icon in addition to any colour.
-
BI-5 (UK English, DD/MM/YYYY). All copy is UK English (“colour”, “organisation”); all dates render DD/MM/YYYY.
-
BI-6 (corpus visibility scope). Both surfaces show the corpus visible to the authenticated user under the shipped read/search contract. Cross-workspace visibility follows the
hybrid_searchscoping (application_typederived fromworkspace_id, §9) for Surface A and thesource_documentsrow-level read for Surface B; this spec does not widen or narrow that scoping. AAT-1 dependency: the exact Surface-A scope is whatever the shipped {131.11} contract resolves.
Surface A — corpus search / browse / filter
Section titled “Surface A — corpus search / browse / filter”-
BI-7 (distinct corpus-search entry). Surface A is a dedicated, net-new corpus search/browse page (exact route path is a TECH decision), reached from the primary navigation. It is not a mode of
/library(RD-1). It is the replacement entry point for the eliminated IMS/browse. -
BI-8 (no-query default state). With no search query entered and no filters applied, Surface A shows a clear, non-error guidance/empty state inviting the user to search (not a blank page, not a spinner, not an error). This state is visually distinct from the no-results state (BI-18) and the error state (BI-19).
-
BI-9 (query submission + URL-driven state). The user enters a free-text natural-language query (no taxonomy knowledge required). The active query and all active filters are reflected in the URL (shareable, restorable on reload, and navigable with the browser back/forward buttons), reusing the id-111 URL-driven search pattern. Re-submitting an unchanged query+filter set does not duplicate or reset results unexpectedly.
-
BI-10 (default scope = ALL grains). The default search scope is ALL owner_kinds —
q_a_pair+content_chunk(collapsed up to itssource_document) +reference_item— retrieved per-grain, normalised, then merged. The default is NOT scope-narrowed toq_a_pairs. (§9; AAT-1.) -
BI-11 (answer-first is an implicit bounded boost). Ordering applies the one shipped
application_typeranking profile (procurement = q_a_pair-primary): a bounded answer-first boost (~×1.1) applied after per-grain normalisation — never categorical priority that suppresses other kinds, and never surfaced to the user as a control or score. (§9; AAT-3.) -
BI-12 (three result kinds, labelled). Results render as one merged, ranked list containing up to three visibly-labelled record kinds — answer (
q_a_pair), document (source_document), and reference (reference_item). The kind label is text (and/or icon), never colour-only (BI-4).content_chunkhits never appear as their own kind: each collapses up to itssource_documentso one underlying fact is one hit, de-duplicated by provenance (§9). -
BI-13 (per-kind card content + link target). Each result card shows kind-appropriate content and links to the correct destination:
- answer (
q_a_pair) → question as the title, an answer snippet as the summary, with scope/domain badges; links into/libraryfor single-pair read/edit. - document (
source_document) → classified title or filename,summary, andprimary_domain/primary_subtopic({131.9} classification cols, shipped); links into/documents/[id](Surface B). - reference (
reference_item) → reference title and source; links out to/reference/[id](RD-3), reusing the id-111 reference card.
S438 {135.19} reconcile (D7): the
answer→/librarylink target assumes the BI-33 q_a_pair viewer that was deferred out of id-131 (to the id-71 family);/librarystill readscontent_items. The link destination is an open owner decision (NO-1) — the card can render the link, but single-pair read/edit will not exist there until the id-71 rebind lands. - answer (
-
BI-14 (polymorphic id resolution). A result’s link target is derived from its record kind, carried on the result’s kind field (the id-131 BI-27
content_typefield carries owner_kind). The card never mis-routes a kind to the wrong destination. (AAT-2 — the typed polymorphic/api/searchshape supplies the kind + id.) -
BI-15 (kind-narrow control narrows only). Surface A exposes a kind-narrow control (e.g. answers / documents / references) that narrows the default ALL-grain scope to a single kind (
type/scopeparam narrows only, §9) (RD-2). Selecting a narrow updates the URL state (BI-9) and the merged list re-renders to that kind. Clearing it returns to ALL grains. -
BI-16 (metadata filters). Surface A offers a reduced filter set (domain / subtopic / date), reusing the id-111
ReferenceFilterControlsUX, applied on top of a query to narrow the merged results.q_a_pairresults are restricted topublication_status = published(id-131 BI-20, shipped) — unpublished/superseded answers never surface here. -
BI-17 (loading state). While a search is in flight, Surface A shows a skeleton placeholder (reusing the id-111
ReferenceLoadingSkeletonpattern), not a blank page or layout-shifting flash. A new query submitted while one is loading supersedes the in-flight request (no stale results render over newer ones). -
BI-18 (empty/no-results state). When a query (optionally filtered) returns zero results, Surface A shows a clear “no results” state with guidance (e.g. broaden the query, clear filters), visually distinct from the no-query state (BI-8) and the error state (BI-19). No raw counts of zero or technical wording.
-
BI-19 (error state). When the search request fails (RPC error, timeout, network), Surface A shows a non-technical error message with a retry affordance. No raw error text, stack trace, model name, or score is shown. Retrying re-issues the same query+filter set.
-
BI-20 (pagination / incremental load). Results load incrementally (reusing the id-111 infinite-list pattern). Ordering is stable across page loads, and a record never appears twice across pages within one query+filter set. Reaching the end shows a clear end-of-results indication rather than an indefinite spinner.
-
BI-21 (navigation placement, co-decided with id-118). Surface A is the corpus search/browse navigation entry, placed and styled consistently with id-118’s corpus IA (in the “Knowledge” zone — DR-041; pre-ratification wording “Knowledge Base grouping” amended S456), role-uniform with active-state machinery reused from
site-header. Surface A is not shipped with a divergent nav slot. (§4; co-decision with id-118.)
Surface B — source_document detail / provenance viewer
Section titled “Surface B — source_document detail / provenance viewer”-
BI-22 (net-new detail page, not in nav). Surface B is the net-new
source_documentdetail page at/documents/[id]— a sibling of the shipped/documents/[id]/diff(BND-2). It is a detail destination reached from Surface A’s document results and from any link to asource_document; it has no top-level navigation slot of its own (§4). -
BI-23 (not-found / invalid-id handling). An invalid document-id format (non-UUID) and a well-formed id that resolves to no
source_documentboth render a not-found state (reusing the id-111 detailnotFound()pattern) — never a 500, a blank page, or a partially-rendered shell. -
BI-24 (provenance panel). Surface B renders the provenance field set reused verbatim from id-111 B-28:
original_filename/filename,mime_type,extraction_method(rendered in plain language, not the raw enum),source_url, and the landed/created date (DD/MM/YYYY). It additionally renders the {131.9} classification family —primary_domain,primary_subtopic,secondary_domain,secondary_subtopic,summary,ai_keywords— as plain metadata, with no “AI classified”/confidence-score chrome (BI-3). Any field that is null/absent is omitted or shown as a neutral “not recorded”, never as an error. -
BI-25 (version chain, inline list). Surface B renders an inline list of the document’s version chain from the shipped id-117
get_document_version_chainRPC (via the existing/api/source-documents/[id]/versionsroute), ordered along theversion/parent_idchain with each version’s landed date; the current version is clearly marked. A single-version document shows a single-entry list, not an empty/error state. (RD-4.) -
BI-26 (diff link — reuse, never rebuild). Each non-current version offers a link to
/documents/[id]/difffor pairwise comparison, reusing the shipped id-117UnifiedDiffContainerandsource-document-revisionadapter. Surface B does not introduce any net-new version/diff component (HARD RULE). Per DR-013, link-out is the only form — Surface B never composesUnifiedDiffContainerinline (RD-4). -
BI-27 (citations panel). Surface B renders a citations panel reading the id-131 BI-23 CITE-EXT extended
cited_target_kind({q_a_pair, reference_item, source_document, concept}) — what cites this document and/or what this document cites. When there are no citations (the present state —citationsis 0 rows today), the panel shows a clear empty state, not an error. (AAT-4.) -
BI-28 (derived q_a_pairs list). Surface B lists the
q_a_pairsderived from this document — those whosesource_document_idequals the document id, restricted topublication_status = published. Each entry links into/libraryfor single-pair read. When a document has produced no published answers, the list shows a clear empty state, not an error. -
BI-29 (derived-records data requirement — BND-1). The data feeding BI-28 requires the
app/api/source-documents/[id]GET to STOP joining the eliminatedcontent_items(it currently.from('content_items')…eq('source_document_id', id)…and returnscontent_items: items) and instead readq_a_pairsviasource_document_id(published only). This is a behaviour requirement for Surface B to function correctly. Per DR-012, ownership is decided — Path β: id-131 lands the route re-point (its content_items-elimination sweep); id-135 consumes thederived_pairsfield only (Task-level dep,{135.11}dropped). -
BI-30 (loading + partial-failure states). Surface B shows a skeleton while the document and its sections load. Section reads are independent: if the version-chain RPC, citations read, or derived-pairs read fails while the core
source_documentloaded successfully, the affected section shows a localised non-technical error with retry — the whole page does not fail. If the core document read itself fails (not a not-found), the page shows a single non-technical error with retry. -
BI-31 (read-only, no destructive affordances). Surface B exposes no edit, delete, or version-mutation affordance for the document or its versions. Re-ingest / send-to-review (id-117) actions are out of v1 scope and are not surfaced here unless a future ratified decision reuses an existing id-117 affordance.
Out of scope (decided)
Section titled “Out of scope (decided)”- BI-32 (no related-records, no keyword facet — OOS-1). Neither surface ships a
related-records affordance nor a keyword pre-filter facet in v1. Both are the §9-dropped
(
find_related_items/filter_by_keywords) co-design backlog seam, to be designed with these surfaces later — explicitly not v1 features.
End of {135.2} PRODUCT. 32 numbered behaviour invariants (BI-1 … BI-32) across cross-cutting / Surface A / Surface B; every ratification decision and in-flight assumption-against-target tabled and cross-referenced to its invariant(s); quality bars (read-only, authenticated, role-uniform, AI-invisible, Warm Meridian tokens, UK English, DD/MM/YYYY) encoded as cross-cutting invariants. No implementation detail, no DDL, no ledger writes, no commits. TECH {135.3} maps a one-to-one proposed change per invariant.