Skip to content

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.

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_pairs answers, source_documents via their passages, and reference_items), built on the {131.20} §9 ratified search/sourcing contract (search-ALL-grains default + bounded answer-first boost).
  • Surface B — a source_document detail/provenance viewer at /documents/[id]: the provenance field set, the version chain, a citations panel, and the list of q_a_pairs derived from the document.

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_items is eliminated by id-131 BI-9 — this read breaks and must re-point to q_a_pairs (see BI-29 / BND-1).
    • app/api/source-documents/[id]/versions/route.ts exists and calls the get_document_version_chain RPC (id-117) — version-chain read is ready.
  • ccc search fallback disclosure: the cocoindex-code daemon errors in this environment (DaemonStartError — same failure {135.1} recorded), so the ccc orientation 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. 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.

TagOQTypeDecision baked into this specAlternative / dependency
RD-1OQ-1RATIFICATION DECISIONSurface 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-2OQ-4RATIFICATION DECISIONSurface 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-3OQ-8RATIFICATION 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-4OQ-9RATIFICATION 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-1OQ-2ASSUMPTION-AGAINST-TARGETThe 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-2OQ-3ASSUMPTION-AGAINST-TARGETSurface 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-3OQ-4ASSUMPTION-AGAINST-TARGETRanking = 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_idapplication_type threading into /api/search.
AAT-4OQ-6ASSUMPTION-AGAINST-TARGETSurface 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-1OQ-5BOUNDARY → DR-012 decidedSurface 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-2OQ-10BOUNDARY (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-1OQ-7OUT 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 answer results into /library on the premise that id-131 BI-33 repoints /library to a q_a_pair viewer. That repoint was deferred out of id-131 to the id-71 family ({71.16}/ID-59/ID-69); /library still reads content_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.

Invariants below are this spec’s own (BI-1 … BI-32). References to other Tasks’ invariants are Task-qualified per the convention note above.

  1. BI-1 (authenticated, read-only). Both surfaces require authentication; neither is added to proxy.ts publicRoutes, so an unauthenticated visitor is redirected to /login. Server reads go through the authenticated-client pattern (getAuthorisedClient() / getAuthenticatedClient() → check auth.success, route API failures via authFailureResponse(auth)). Neither surface exposes any create/update/delete affordance for corpus records.

  2. 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.

  3. 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.)

  4. 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.

  5. BI-5 (UK English, DD/MM/YYYY). All copy is UK English (“colour”, “organisation”); all dates render DD/MM/YYYY.

  6. 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_search scoping (application_type derived from workspace_id, §9) for Surface A and the source_documents row-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”
  1. 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.

  2. 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).

  3. 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.

  4. BI-10 (default scope = ALL grains). The default search scope is ALL owner_kinds — q_a_pair + content_chunk (collapsed up to its source_document) + reference_item — retrieved per-grain, normalised, then merged. The default is NOT scope-narrowed to q_a_pairs. (§9; AAT-1.)

  5. BI-11 (answer-first is an implicit bounded boost). Ordering applies the one shipped application_type ranking 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.)

  6. 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_chunk hits never appear as their own kind: each collapses up to its source_document so one underlying fact is one hit, de-duplicated by provenance (§9).

  7. 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 /library for single-pair read/edit.
    • document (source_document) → classified title or filename, summary, and primary_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/library link target assumes the BI-33 q_a_pair viewer that was deferred out of id-131 (to the id-71 family); /library still reads content_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.

  8. 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_type field carries owner_kind). The card never mis-routes a kind to the wrong destination. (AAT-2 — the typed polymorphic /api/search shape supplies the kind + id.)

  9. 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/scope param 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.

  10. BI-16 (metadata filters). Surface A offers a reduced filter set (domain / subtopic / date), reusing the id-111 ReferenceFilterControls UX, applied on top of a query to narrow the merged results. q_a_pair results are restricted to publication_status = published (id-131 BI-20, shipped) — unpublished/superseded answers never surface here.

  11. BI-17 (loading state). While a search is in flight, Surface A shows a skeleton placeholder (reusing the id-111 ReferenceLoadingSkeleton pattern), 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).

  12. 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.

  13. 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.

  14. 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.

  15. 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”
  1. BI-22 (net-new detail page, not in nav). Surface B is the net-new source_document detail 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 a source_document; it has no top-level navigation slot of its own (§4).

  2. BI-23 (not-found / invalid-id handling). An invalid document-id format (non-UUID) and a well-formed id that resolves to no source_document both render a not-found state (reusing the id-111 detail notFound() pattern) — never a 500, a blank page, or a partially-rendered shell.

  3. 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.

  4. 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_chain RPC (via the existing /api/source-documents/[id]/versions route), ordered along the version/parent_id chain 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.)

  5. BI-26 (diff link — reuse, never rebuild). Each non-current version offers a link to /documents/[id]/diff for pairwise comparison, reusing the shipped id-117 UnifiedDiffContainer and source-document-revision adapter. 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 composes UnifiedDiffContainer inline (RD-4).

  6. 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 — citations is 0 rows today), the panel shows a clear empty state, not an error. (AAT-4.)

  7. BI-28 (derived q_a_pairs list). Surface B lists the q_a_pairs derived from this document — those whose source_document_id equals the document id, restricted to publication_status = published. Each entry links into /library for single-pair read. When a document has produced no published answers, the list shows a clear empty state, not an error.

  8. 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 eliminated content_items (it currently .from('content_items')…eq('source_document_id', id)… and returns content_items: items) and instead read q_a_pairs via source_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 the derived_pairs field only (Task-level dep, {135.11} dropped).

  9. 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_document loaded 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.

  10. 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.

  1. 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.