Slice 30 - Viewer File Route
Scope#
Add the read-only viewer route that answers: which wiki pages mention this file or folder reference?
This restores the old local viewer's /file?path=... contract without adding a
source-code browser.
Out Of Scope#
- No file-content preview.
- No source runtime reuse.
- No AI calls.
- No wiki writes.
- No hosted or remote file access.
Design#
ViewerService owns the browser payload:
result = app.viewer.file(
ViewerFileRequest(cwd=repo, path="src/auth/session.py")
)The service delegates matching to the existing SQLite read model:
pages = index.search(
workspace.workspace_id,
SearchIndexRequest(mentions=request.path, limit=request.limit),
)The HTTP route stays query-shaped:
GET /api/file?path=src/auth/session.pyFile and folder paths contain /, so using a query parameter avoids route-path
ambiguity and matches the old viewer contract.
Boundary Notes#
ViewerFileRequestvalidates the path as a wiki reference path, not as an actual filesystem path.- Parent traversal such as
../secret.txtis rejected before the index query. - Folder refs keep the trailing slash.
- The index keeps owning GLOB-safe file/folder mention semantics.
- The frontend links page rail file references to
#/file/<path>.
Verification#
- Focused viewer service and server tests cover file refs, folder refs, and invalid parent traversal.
- Full verification should include full pytest, ruff, diff check, live serve API dogfood, and package asset inspection.