121 lines
16 KiB
Markdown
121 lines
16 KiB
Markdown
# Knowledge browser implementation plan
|
||||
|
|
|
|||
|
|
Status: implementation delivered, local verification complete, product acceptance partial, 16 October 2026. Browse/count/facet APIs, project Knowledge/Sources/Graph navigation, URL-restorable knowledge paging, source counts and paging, connected reader, and bounded project/one-hop graph views are implemented. Remaining acceptance criteria are tracked in the [acceptance note](acceptance/2026-10-16-knowledge-browser.md).
|
|||
|
|
|
|||
|
|
## Outcome and scope
|
|||
|
|
|
|||
|
|
Opening a project immediately shows its knowledge collection and accurate counts. A user can narrow and sort the list, move between pages, open a readable node, and follow its recorded connections without losing their place. Sources have their own tab. A later Graph tab offers an alternative way to explore the same authorized knowledge.
|
|||
|
|
|
|||
|
|
Deliver in three sequential milestones: browsing, connected reader, graph. Complete and verify each before starting the next. Preserve the existing read-only knowledge workflow, authentication, evidence integrity, and project isolation. Keep administration and agent credentials in secondary navigation. Do not add editing, ingestion controls, a new UI framework, or AI-generated relationships to this work.
|
|||
|
|
|
|||
|
|
The root SPEC.md currently describes ingestion relevance and review work. Do not replace it with this plan. Follow applicable project instructions and the development, visual design, and modularity protocols under docs/development/ when implementing.
|
|||
|
|
|
|||
|
|
## Findings to preserve as regression cases
|
|||
|
|
|
|||
|
|
- In web/src/shell/App.tsx, Stale knowledge, Problems, and Topics update search draft state without submitting a new search. Search remains visually active regardless of those selections.
|
|||
|
|
- runSearch and the HTTP search request validator require text or a node type. Empty-query, all-type browsing is unavailable.
|
|||
|
|
- Facet suggestions are native datalists built from the current results array. They cannot discover values elsewhere in the project.
|
|||
|
|
- Search uses 20-result cursor pages, appends them with Load more, and reports the returned page length. This is not a project or filtered total.
|
|||
|
|
- The reader already fetches node details, evidence, revisions, and neighbors. Relationship targets render as plain text. Reuse those capabilities rather than rebuilding evidence retrieval.
|
|||
|
|
- Local index inspection during review found 20 active interactive-dome nodes: 6 techniques, 5 cases, 4 concepts, 2 problems, 2 findings, and 1 event. Treat these as a dated observation, never hardcoded UI or test expectations.
|
|||
|
|
- Collaborative preview navigation succeeded, but snapshot inspection failed. Rendered behavior and accessibility still need browser verification.
|
|||
|
|
|
|||
|
|
## Product decisions
|
|||
|
|
|
|||
|
|
Use project-level navigation: **Knowledge | Sources | Graph**. Knowledge is the default. Keep the project selector and compact counts above the tabs. Graph is exposed now that a bounded graph read and accessible node list exist; selected-node neighborhood and fit-to-selection remain follow-up work.
|
|||
|
|
|
|||
|
|
Within Knowledge, Problems and Concepts are type choices, and Needs review is a freshness filter. Replace the old misleading side links. Label the stored concept type “Concepts”; do not imply that tags or domains are separate topic records. Explain Needs review as a passed freshness date, distinct from ingestion or owner approval queues.
|
|||
|
|
|
|||
|
|
Default to a compact list with title, type, short summary, and an overdue freshness marker when applicable. Use the existing editorial design tokens. Keep one primary content area, short labels, restrained metadata, and useful empty states.
|
|||
|
|
|
|||
|
|
Show search and type filtering directly. Put Tags, Audience, Use, and Domain in a Filters disclosure with searchable multi-select controls, selected chips, and Clear all. Put confidence and date controls under More filters. Selecting a filter applies it immediately; text search uses a short debounce. Show loading state without flashing an empty library.
|
|||
|
|
|
|||
|
|
Default ordering is title ascending. Offer title descending and knowledge date newest/oldest; label the date “Knowledge date” because as_of is not necessarily an edit timestamp. When searching text, offer and default to Relevance. Do not advertise Updated sorting unless a reliable update timestamp exists in the public contract.
|
|||
|
|
|
|||
|
|
Project counts mean accepted knowledge visible to the current user. Show the selected project's node and source totals independently of filters. Shared knowledge is off by default for browsing; provide an explicit Include shared toggle when authorized. Display shared counts separately when enabled. This is an intentional change from the current implicit shared search scope.
|
|||
|
|
|
|||
|
|
## Milestone 1: dependable browsing
|
|||
|
|
|
|||
|
|
### 1. Define and implement the read boundary
|
|||
|
|
|
|||
|
|
Owners: domain/application read contracts, search adapter, HTTP transport/composition root. Consumers: knowledge list, filters, counts, later graph.
|
|||
|
|
|
|||
|
|
Inspect src/gewu/application/search.py, src/gewu/adapters/sqlite_search.py, src/gewu/transports/http_models.py, src/gewu/bootstrap/app.py, and the operation catalog before choosing exact names. Add a dedicated authenticated browse operation rather than changing the retrieval semantics of /v1/search. Proposed HTTP name: POST /v1/knowledge/browse; align with repository route and envelope conventions before finalizing.
|
|||
|
|
|
|||
|
|
The operation must support an empty query, project/shared scope, node types, tags, audience, use_for, domain, freshness, confidence, date bounds, stable ordering, page size, and opaque cursor. Use OR within each multi-select facet and AND across different facets; document and test this explicitly. Browsing is deterministic filtering, without an embedding/provider dependency. Keep the existing relevance search path available for text relevance ordering and preserve its evidence behavior.
|
|||
|
|
|
|||
|
|
Return bounded rows and next_cursor plus metadata with these explicit meanings:
|
|||
|
|
|
|||
|
|
- Project totals: authorized accepted node and source counts, independent of current filters, with shared totals separate.
|
|||
|
|
- Filtered total: all matching nodes in the requested scope, independent of page size.
|
|||
|
|
- Facets: project-wide authorized values and counts for each supported label field; independent of the loaded page and selected filters. Allow bounded prefix lookup if vocabulary size exceeds the response cap; never silently truncate suggestions.
|
|||
|
|
- Index revision: bind totals, rows, and cursor to a coherent read generation. Source and node counts may require different indexes; expose freshness/revision limitations honestly if atomic alignment is unavailable.
|
|||
|
|
|
|||
|
|
Reuse existing envelope/metadata patterns where appropriate; introduce a typed browse response if the current envelope cannot express the contract clearly. Do not fetch every page in the browser to calculate counts, facets, or sorting. Count distinct node identities across project/shared scope. Metadata must use the same authorization boundary as rows: no hidden project names, facet values, graph endpoints, or counts.
|
|||
|
|
|
|||
|
|
Stable sorting needs a node identity tie-breaker. Bind cursors to scope, query, filters, order, page size, and index revision. Reuse established invalid/expired cursor errors. On expiration, offer a restart of the list and refresh its metadata; never append a new generation to old results. Keep new read exposure within the operation catalog and capability rules; no accidental MCP exposure.
|
|||
|
|
|
|||
|
|
Regenerate schema/v1/openapi.json and web/src/generated/api.ts through existing repository tooling. Do not hand-edit generated types. No canonical storage migration is expected; any derived-index change must have a rebuild and rollback path.
|
|||
|
|
|
|||
|
|
### 2. Implement the project list
|
|||
|
|
|
|||
|
|
Owners: frontend knowledge feature and route/data state. Extract bounded feature components from App.tsx as needed; leave unrelated account/admin flows alone. Suggested responsibilities: ProjectKnowledgePage, KnowledgeList, KnowledgeFilters, facet combobox, and a typed read client. Match existing folder conventions rather than creating a new architecture for names alone.
|
|||
|
|
|
|||
|
|
- Select an authorized project and automatically load its first page, counts, and facet vocabulary. A project with no nodes is a valid empty state.
|
|||
|
|
- Use real navigable project tabs with an unambiguous active state. Sources becomes a dedicated view using the existing source read operation, with pagination and accurate count metadata.
|
|||
|
|
- Render 20 rows per page, Previous/Next controls, and an accurate range such as “21–40 of 86.” Keep a cursor stack for backward navigation. Do not show arbitrary page numbers without random-access support.
|
|||
|
|
- Store project, tab, query, repeated facet values, order, shared toggle, pagination state, and selected node in shareable route state. Back/forward and reload restore the view. Preserve existing node/revision deep links and supported search URLs, translating them if routes change.
|
|||
|
|
- On project change, clear the selected node, page cursors, and project-specific filters. On a filter/order/query change, reset pagination. Cancel or ignore stale responses so the previous project cannot overwrite the current view.
|
|||
|
|
- Give zero matches a Clear filters action. Distinguish no matches, an empty project, loading, expired pagination, unauthorized data, and network failure with retry. Never show a failed count request as zero.
|
|||
|
|
- Use accessible combobox/listbox semantics with keyboard selection, dismissal, visible focus, removable chips, and a visible empty-suggestion state. Arbitrary typed text must not silently become an exact filter with no known matching label.
|
|||
|
|
|
|||
|
|
### Milestone 1 acceptance
|
|||
|
|
|
|||
|
|
An authorized user opens interactive-dome and sees all currently indexed node types without entering a query. Project totals agree with independently scoped fixture data. A label absent from the first page can still be suggested and selected. Type/freshness changes immediately alter the list. Multi-select semantics, stable sorting, page boundaries, clear filters, shared scope, back/forward, and reload work. A denied project contributes no rows or metadata. Repeat these checks with more than 40 nodes and duplicate sort titles; the current 20-node corpus alone cannot prove pagination.
|
|||
|
|
|
|||
|
|
## Milestone 2: readable, connected node pages
|
|||
|
|
|
|||
|
|
Owners: frontend node reader, existing node/history/neighbor APIs; extend the neighbor read contract only where required.
|
|||
|
|
|
|||
|
|
Open a node in a dedicated readable route within the project shell. On wide screens a compact optional list pane is acceptable, but the article must have enough width and remain usable on a phone. Use a real link for list titles and related targets so open-in-new-tab works. Back to knowledge restores filters, page, and scroll position.
|
|||
|
|
|
|||
|
|
Content order: title and summary; recorded claims/knowledge; assumptions and limitations; related knowledge; sources/evidence. Present Tags, Audience, Use, and Domain as compact labeled metadata. Keep freshness visible when relevant. Put detailed assessment rationale, extraction internals, and revision history behind disclosures. Preserve all currently available evidence and exact source-location access; simplify the presentation without losing information.
|
|||
|
|
|
|||
|
|
Render authorized incoming and outgoing relationships as clickable targets with direction and a human-readable relationship label. Distinguish accepted relationships from suggested similarity. Provide a clear empty state and paginate neighbors if needed; do not label only the first neighbor page as the complete graph or relationship count. Source targets open the source view; node targets open the reader. Unknown/unavailable targets have explicit non-clickable states without revealing excluded records. Inspect neighbor direction/target metadata and add compatible fields if the existing response is insufficient.
|
|||
|
|
|
|||
|
|
Preserve revision-pinned node links. The current reader intentionally omits relationships for historical revisions because the index reflects current state; keep that behavior and explanation unless historical relationship support is explicitly implemented. Do not attach current edges to a historical node silently.
|
|||
|
|
|
|||
|
|
### Milestone 2 acceptance
|
|||
|
|
|
|||
|
|
Read a node, follow an outgoing link, follow a backlink, open its cited source and exact location, then return to the original list position. Verify direct URLs, browser back/forward, a missing node, denied target, more than one page of relationships, and revision-pinned links. Distinguish an accepted link from a similarity suggestion. Verify keyboard focus on route changes and restoration after closing any disclosure/dialog.
|
|||
|
|
|
|||
|
|
## Milestone 3: optional graph exploration
|
|||
|
|
|
|||
|
|
Owners: bounded graph read use case/adapter and frontend graph feature. Reuse browse filter/scope semantics and the same node reader.
|
|||
|
|
|
|||
|
|
Start with a selected node's one-hop neighborhood and offer a project graph with an explicit configurable cap, initially 200 nodes and 500 edges. An additive graph read operation should return authorized node/edge summaries, edge direction/type, index revision, and truncation/continuation metadata. Avoid fetching the entire corpus or issuing one neighbor request per visible node. Render only edges whose endpoints are visible and authorized. Never imply a truncated graph is complete.
|
|||
|
|
|
|||
|
|
Provide search/select, type filters, zoom/reset, fit to selection, and grouping by type. Node selection shows a compact preview with links to the readable node route. Draw accepted typed edges prominently and shared-source links faintly, with a legend that names each relationship basis. A shared-source link is derived only when both nodes cite the same source revision; do not infer links from visual proximity or semantic similarity. Keep graph viewport state when returning from the reader. List sorting remains in Knowledge; graph controls describe grouping/layout, not alphabetical positions.
|
|||
|
|
|
|||
|
|
Choose a graph library only after checking existing dependencies, bundle cost, license, and keyboard support. Keep layout/rendering behind a replaceable frontend component. Respect reduced motion; do not require dragging or canvas interaction to discover/open nodes. Supply a synchronized accessible list of the visible graph nodes.
|
|||
|
|
|
|||
|
|
### Milestone 3 acceptance
|
|||
|
|
|
|||
|
|
Selection opens the same canonical reader as the list. Scope and filters agree between graph and list. Empty, disconnected, isolated-node, denied-edge, truncated, and failed-load states are clear. Controls work by keyboard and at narrow widths. Check interaction on a capped fixture and report measured load/render behavior; do not claim large-corpus performance from the dome dataset.
|
|||
|
|
|
|||
|
|
## Verification and delivery
|
|||
|
|
|
|||
|
|
Use focused backend contract tests for empty browsing, facet semantics/counts, authorization, ordering, cursor generation/expiry, and bounded graph reads. Add frontend behavior tests for route restoration, filter selection, stale-response protection, pagination, and connected navigation. Extend web/e2e/reader.spec.ts or add a focused browser flow with a synthetic multipage corpus; do not mutate the user's real knowledge to create fixtures.
|
|||
|
|
|
|||
|
|
Run relevant Python checks using the repository's documented commands and npm --prefix web run check plus npm --prefix web run build. Run affected browser tests through the repository test setup. Inspect the running UI through T3 preview tools at desktop and narrow widths; verify keyboard use, visible focus, readable density, and basic contrast. If browser inspection is unavailable, record that limit rather than claiming visual verification.
|
|||
|
|
|
|||
|
|
For each milestone, record changed public contracts, checks run/results, remaining limitations, and screenshots where available in an acceptance note linked from docs/acceptance/ledger.md. Leave a concise handoff if work stops between milestones. Do not mark the graph complete when only its tab or placeholders exist.
|
|||
|
|
|
|||
|
|
Suggested delivery slices:
|
|||
|
|
|
|||
|
|
1. Browse/count/facet backend contract and scoped fixture tests.
|
|||
|
|
2. Knowledge/Sources navigation, filter controls, URL state, and pagination.
|
|||
|
|
3. Readable node route, navigable relationships/backlinks, and evidence disclosures.
|
|||
|
|
4. Bounded graph API and accessible graph view.
|
|||
|
|
|
|||
|
|
Luna can begin with slice 1 without another product-design round. Resolve routine component and endpoint naming against existing conventions. Escalate only a material requirement conflict or an authorization boundary that cannot satisfy this plan.
|