MuseHub Roadmap
Program: Knowtation domain — vault-native VCS intelligence for agent knowledge systems.
Authority: This document governs work in the MUSE_HUB workspace (gabriel-muse, musehub, and related repos). It is separate from Scooling's docs/ROADMAP.md. Scooling consumes Knowtation via REST/MCP adapters in parallel; it does not block this track.
Source spec: gabriel/muse issue #2 (frozen phase order — each phase unlocks the next for new work; local code may span multiple phases).
Handover relay: docs/MUSEHUB-OVERSEER-HANDOVER.md — paste the NEXT SESSION block into a fresh chat.
Phase Model Key
| Label | Meaning |
|---|---|
| Thinking | Contract, architecture, open-question resolution, test matrix — freeze spec before build |
| Auto | Mechanical implementation against a frozen spec; run tests; Muse commit on feature branch |
| Thinking → Auto | Split into {step}a (Thinking) then {step}b (Auto) in the handover doc |
| Operator + Auto | Tier 3 human authorization for staging push / live gate; Auto for implementation |
Handover rule: Every next-step table and paste block must include Model:.
Next step at a glance (2026-07-10)
| Item | Status |
|---|---|
| Active track | Knowtation domain — staging push + attestation verification |
| Plugin code (Phases 1–4, 7, KD-5b) | DONE locally — gabriel-muse/muse/plugins/knowtation/ + muse/mcp/ (36 MCP tests, 1606 knowtation tests green) |
| Staging plugin repo | DONE — aaronrene/gabriel-muse @ sha256:660fcac1… (2026-06-21) — pre-KD-5b; re-push needed |
| Marketplace registration | DONE — @aaronrene/knowtation on staging (2026-07-05); viewer_type: generic (optional fix) |
| Vault activation smoke | DONE — knowtation-vault-smoke (5 PASS / 3 PARTIAL — see handover matrix) |
| Open questions (KD-OQ) | DONE (2026-07-10) — all 5 issue #2 questions resolved (see table below) |
| MCP surface (KD-5b) | DONE (2026-07-10) — 9 tools / 8 resources / 4 prompts at muse/mcp/; activation gate; 7-tier + Claude Desktop stdio integration green on knowtation-vault-smoke |
| Vault UI contract (KD-6a) | DONE (2026-07-10) — §KD-6a frozen: 4 page groups (landing, note detail, proposals, vault search), routes, MCP read-mapping, error enums, 7-tier matrix + Playwright E2E |
| Vault UI (KD-6b) | DONE (2026-07-10) — knowtation_view adapter, ui_vault.py, templates/SCSS, harmony diff, 7-tier + Playwright E2E green locally |
| NEXT | KD-STAGING — Tier 3 push gabriel-muse (KD-5b) + musehub (KD-6b) to staging; KD-7 attestation smoke on staging vault |
| Parked | — |
| Parallel (Scooling) | Phase 9A overseer gates — independent |
Build status table
| ID | Phase | Repo(s) | Model | Status |
|---|---|---|---|---|
| KD-0 | Recovery + rc14 re-baseline | gabriel-muse | Auto | DONE (2026-06-20) — corrupt rc3 history discarded; code intact; 1570 tests green |
| KD-SD8-PUSH | Push plugin to staging | gabriel-muse | Auto (Tier 3) | DONE (2026-06-21) — aaronrene/gabriel-muse @ 660fcac1… |
| KD-SD8-REG | Marketplace registration | musehub (API) | Auto (Tier 3) | DONE (2026-07-05) — @aaronrene/knowtation; viewer_type: generic (fix optional) |
| KD-1 | Domain foundation (detector, parser, plugin, symbols, domain-info) | gabriel-muse | Auto | DONE — proven on vault smoke (domain-info PASS; symbols CLI PARTIAL) |
| KD-2 | Semantic diff & harmony merge | gabriel-muse | Auto | DONE — differ/merger API proven; CLI merge/diff PARTIAL (see handover) |
| KD-3 | Code intelligence (deps, impact, hotspots, dead, clones, entangle, velocity) | gabriel-muse | Auto | DONE — deps/impact/hotspots/dead PASS on test vault |
| KD-4 | Memory timeline (event schema, symbol-log, consolidation hook, prime) | gabriel-muse | Auto | DONE — log event_type PASS; prime_context API PASS (no CLI hook) |
| KD-7 | Attestation & ICP push hook | gabriel-muse | Auto | DONE (local; attestation smoke post-activation) |
| KD-ACTIVATE | Vault activation + Phase 1–4 smoke | gabriel-muse + test vault | Auto | DONE (2026-07-10) — knowtation-vault-smoke; 5 PASS / 3 PARTIAL |
| KD-OQ | Resolve issue #2 open questions (5 items) | gabriel-muse + musehub | Thinking | DONE (2026-07-10) — 5/5 resolved with source-backed rationale; gate cleared for KD-5a |
| KD-5a | MCP domain surface — contract + test matrix | gabriel-muse + musehub | Thinking | DONE (2026-07-10) — contract frozen (§KD-5a); open items (a)–(e) resolved; 7-tier matrix + Claude Desktop integration test specified |
| KD-5b | MCP domain surface — implementation (9 tools, 8 resources, 4 prompts) | gabriel-muse + musehub | Auto | DONE (2026-07-10) — muse/mcp/ server + namespace; 36 MCP tests + stdio E2E green on knowtation-vault-smoke |
| KD-6a | MuseHub vault UI — contract + test matrix | musehub | Thinking | DONE (2026-07-10) — contract frozen (§KD-6a); 4 page groups, routes, MCP read-mapping, error enums, 7-tier matrix + Playwright E2E specified |
| KD-6b | MuseHub vault UI — implementation | musehub | Auto | DONE (2026-07-10) — knowtation_view adapter, ui_vault.py, vault templates/SCSS, harmony diff, 7-tier + Playwright E2E green locally |
| KD-STAGING | Staging push (KD-5b plugin + KD-6b musehub) + KD-7 attestation smoke | gabriel-muse + musehub | Operator + Auto (Tier 3) | NEXT |
Repo boundaries
| Repo | Role in this program | VCS |
|---|---|---|
| gabriel-muse | Canonical home of muse/plugins/knowtation/ — detector, parser, merger, code intel, attestation |
Muse; staging remote aaronrene/gabriel-muse |
| musehub | Marketplace API, domain registration, Phase 6 vault-centric UI, MCP server mount | Muse / git per repo convention |
| knowtation (product) | Vault store, Hub API, MCP — not the Muse domain plugin | Separate; Scooling adapter target |
| knowtation-vault-smoke | KD-ACTIVATE test vault — not product repo | Local Muse repo; domain=knowtation |
| scooling | Consumer UI — parallel track; wires domain features after KD-5/KD-6 land | See Scooling governance docs |
Important: The Knowtation product repo (.museattributes → domain = "code") is not a vault repo. Activation uses a vault repo — Markdown notes with Knowtation frontmatter (source_type, source_id, project, tags, entity).
Phase detail (frozen from issue #2)
KD-1 — Domain Foundation
Goal: Muse recognises Knowtation vault files as a distinct domain and introspects them structurally.
Deliverables (verified in tree):
detector.py,parser.py,plugin.py,symbols.py,stats.pydefaults.museattributessnippetmuse domain-info --jsonshape;muse code symbols --file note.md
Exit criteria: KD-ACTIVATE smoke PASS on a real vault repo — MET (domain-info PASS; symbols CLI routes through code AST — gap recorded).
KD-2 — Semantic Diff & Harmony Merge
Goal: Section-aware diff; policy-based merge for tags, entities, source_id conflicts.
Deliverables (verified in tree):
differ.py,merger.py,policies.py,strategies.py,rerere.py- Harmony policies for tag union, entity union, prefer-newer-date, §Summary escalation
Exit criteria: muse merge resolves tag/entity conflicts without raw-text panics — PARTIAL (merge_notes union PASS; muse merge CLI file-conflict on tag-only divergence).
KD-3 — Code Intelligence
Goal: muse code deps, impact, hotspots, gravity, dead, clones, entangle, velocity on vault repos.
Deliverables (verified in tree):
link_index.py,code_intel.py,note_metrics.py,cli_hooks.py
Exit criteria: All six commands return meaningful JSON on test vault — MET (deps, impact, hotspots, dead verified).
KD-4 — Memory Timeline
Goal: Memory event types in commit metadata; symbol-log as memory replay; prime bootstrap from commit graph.
Deliverables (verified in tree):
events.py(15 event kinds, schema locked v1.0.0)hooks.py(consolidation pre-merge)prime_context.py(knowtation://primefrom commit graph)
Exit criteria: muse log --json surfaces event_type; prime JSON smoke PASS — MET (log PASS; prime via build_prime_context API, no CLI).
KD-5 — MCP Domain Surface (KD-5a contract frozen; KD-5b implementation DONE)
Status (KD-5b): DONE (2026-07-10). Server at muse/mcp/; 9 tools / 8 resources / 4 prompts; activation gate on repo.json domain==knowtation; 36 MCP tests + stdio E2E green on knowtation-vault-smoke.
Goal: Knowtation domain ops via Muse MCP (knowtation/search, get-note, history, consolidate, impact, dead, prime, propose, attest — 9 tools + 8 resources + 4 prompts per issue #2 Phase 5).
Entry criteria: KD-ACTIVATE PASS; KD-OQ resolved. Both MET (2026-07-10).
Frozen inputs from KD-OQ: search backend = Muse structural index + Knowtation live vector index
(OQ2); knowtation/consolidate = async dispatch, non-blocking (OQ3); knowtation/attest = generic
MuseAttestationProvider reusing the ICP canister (OQ4); no agent.md merge (OQ5); detection =
frontmatter-primary with optional marker (OQ1).
Model: Thinking → Auto (KD-5a Thinking DONE — contract frozen below; KD-5b Auto is NEXT).
KD-5a Contract — Muse MCP Domain Surface (knowtation namespace) — FROZEN 2026-07-10
Status: DONE (Thinking). This section is the frozen spec KD-5b (Auto) implements against. Do not redesign during Build. Interfaces below are the contract; the seven-tier test matrix is in §KD-5a.F. Implementation-only (KD-5b) items are tagged [KD-5b new work].
Authority chain: issue #2 Phase 5 tables (9 tools / 8 resources / 4 prompts) + KD-OQ frozen
decisions (OQ1–OQ5). The prompt count is 4 (issue #2 §5.3 lists 3 and adds
vault/knowledge-gaps in its deliverables line; the corrected count is 4 — see change log
2026-07-10).
KD-5a.0 — Transport, envelope & shared conventions
- Protocol: JSON-RPC 2.0 per MCP (
tools/list,tools/call,resources/list,resources/read,prompts/list,prompts/get). - Tool names:
knowtation/<verb>(slash-namespaced). Resource URIs:knowtation://…scheme. Prompt names:vault/<name>. - Repo binding: every call operates on the server's bound repo root (the process CWD's
.muse/); no tool takes a filesystem root argument (prevents path-escape and cross-repo reads). refgrammar (shared):HEAD| branch name | full/abbrev sha256 commit id. Resolved viamuse.core.store.resolve_commit_ref; unknown ref →REF_NOT_FOUND.pathgrammar (shared): vault-relative POSIX path. Reject absolute paths,..segments, and any resolved path escaping the repo root →PATH_INVALID(reuses thelink_index._normalise_rel_pathescape check). Non-.md/.markdown/.mdx→NOT_A_NOTE.- Error envelope: JSON-RPC error
{code, message, data:{reason, detail?}}wherereasonis a stable UPPER_SNAKE token (enumerated per tool). Humandetailis control-char sanitised (sanitize_display). No stack traces, no secrets, no absolute host paths inmessage/data. - Determinism: read tools are pure/idempotent; write/dispatch tools (
consolidate,propose,attest) are the only side-effecting members and are individually gated (see below). - Bounds (DoS): every commit walk is capped (
max/max_commits, clamp1..100000, default per tool); every note body read reuses the 16 MiB cap (link_index._MAX_NOTE_BYTES); every HTTP response is byte-capped (attestation 64 KiB; hub calls 256 KiB).
KD-5a.A — Resolution of the five open contract items (a)–(e)
(a) knowtation/search fan-out — FROZEN.
Two backends, merged by the server:
- Muse lexical/structural (owned by Muse, OQ2): live-vault content grep +
build_link_indextitle/heading/frontmatter hits. [KD-5b new work] — nocontent_grephelper exists in the plugin today; KD-5b addsmuse/plugins/knowtation/search.py::lexical_search(root, query, *, top_k)returning[{path, score∈[0,1], section_address?, snippet}]. - Knowtation vector (canonical for semantic at HEAD, OQ2): HTTP
GETto the local hub (http://localhost:<KNOWTATION_HUB_PORT>/api/v1/search?q=…&k=…) — Muse never rebuilds or versionsdata/. [KD-5b new work] — thin client, SSRF-guarded port validation reused verbatim fromhooks._resolve_port.
- Merge/rank: normalise each backend's score to
[0,1]; combinedscore = w_lex·lex + w_vec·vecwith frozen weightsw_lex=0.4,w_vec=0.6(semantic favoured at HEAD, per OQ2). A result present in only one backend uses that backend's normalised score with the missing side treated as0. - Dedupe key: vault-relative
path(case-insensitive basename collision resolved the same waylink_indexresolves it — first walk-order path wins; the other is dropped and counted indeduped). When both backends return the samepath, scores are combined (not summed twice). - Tie-breakers (stable): higher combined
score→ then more recent HEAD mtime → then lexicographicpath. - Fallback / timeout: vector call timeout 2.0 s; on timeout/connection-refused/non-200 the
server returns lexical-only results with
degraded=true,backends.vector="unavailable", and a populatedindex_freshness(ornull) — never an error. If the lexical side raises, return vector-only withbackends.lexical="error". Both down →results:[],degraded=true(still a success response so agents can proceed).
(b) knowtation/consolidate return schema — FROZEN.
The tool wraps KnowtationMergeHook.pre_merge_hook (OQ3, fire-and-forget). Output is the
dispatch receipt exactly mirroring hooks.hook_result_to_json:
{hook_name:string, fired:bool, out_of_band:bool, message:string, error:string|null, dispatched_at:iso8601, hub_port:int}.
- Async completion is NOT observable through this tool (by design — merge never blocks). The
agent observes completion out-of-band via
knowtation://memory/recent(a later commit withevent_type∈{consolidation,consolidation_pass}) orknowtation/history. This is documented in the tool description so agents do not poll. - Port-validation failure and dispatch failure are returned in
errorwithfired=false— the tool never raises for hub-side problems (parity with the hook contract).
(c) knowtation/attest runtime wiring — FROZEN.
- Who registers: the MCP server bootstrap registers both providers when the bound repo's
active domain is
knowtation— it callsattestation.register_knowtation_attestation_provider(cfg)andicp_push_hook.register_knowtation_icp_push_hook()at server start. Today only the merge hook auto-registers inplugins/knowtation/__init__.py; the attest/ICP registration is [KD-5b new work] (abootstrap_knowtation_mcp(root)call — it must not be moved into__init__.py, to keep import side-effect-free per OQ4). - Required vs optional mode: driven by
.muse/air.yamlair.required(loaded viaattestation.load_air_config). The tool accepts an optionalrequired?:boolthat overrides for this call only.required=true+ endpoint unreachable →AttestationRequiredErrorsurfaced as errorreason=ATTESTATION_REQUIRED.required=false→ placeholder record returned withanchored=false(never an error). - Provider unregistered: the tool returns a structured error
reason=PROVIDER_UNREGISTERED(never a silent success, never a placeholder masquerading as real). ICP anchoring is out-of-band (out_of_band=true) and its failure is reported inanchorasnullwithanchor_error, never failing the tool.
(d) .museignore data/ — FROZEN: YES (ignore for snapshots; keep readable on disk).
KD-5b adds a [domain.knowtation] section to .museignore with patterns = ["data/", ".obsidian/"]
so Muse never snapshots/versions Knowtation's vector/sqlite index (consistent with OQ2 "not
rebuilt/versioned by Muse"). This is a snapshot-exclusion rule only: stats.collect_vault_stats
still stat()s data/ on the live filesystem for index_freshness (a filesystem read, unaffected
by .museignore), and link_index/code_intel already skip non-.md files so the vector store is
never parsed as a note. .knowtation remains an always-ignored marker via
plugin._ALWAYS_IGNORE_DIRS; data/ is domain-scoped ignore (only when active domain is knowtation).
(e) MCP-scope activation semantics — FROZEN: explicit repo.json domain, detector is advisory.
The knowtation/* namespace mounts/advertises only when .muse/repo.json domain == "knowtation"
(explicit activation via muse init --domain knowtation). detector.classify_repo is advisory
only: the server MAY surface detected_domain + confidence as a capability hint (so a
misconfigured repo can be diagnosed) but never auto-mounts vault tools on a repo whose active
domain is code/mist. This prevents a code repo containing a few notes from exposing vault
write/attest surfaces. When domain≠knowtation the server returns an empty knowtation/* tool list.
KD-5a.B — Tool contracts (9)
Legend: Backing = existing plugin/CLI API the tool wraps; [KD-5b new work] = no backing API, implement in KD-5b. All inputs validated against §KD-5a.0 grammars before dispatch.
| # | Tool | Input (JSON-RPC params) | Output (result) | Backing API | Error reasons |
|---|---|---|---|---|---|
| 1 | knowtation/search |
{query:string!, top_k?:int=10 (1..100), project?:string, mode?:"hybrid"\|"lexical"\|"semantic"="hybrid", ref?:string="HEAD"} |
{query, mode, results:[{path, score, source:"muse-lexical"\|"knowtation-vector"\|"both", snippet, section_address?, frontmatter:{project,tags,source_type}}], index_freshness:string\|null, degraded:bool, deduped:int, backends:{lexical:"ok"\|"error", vector:"ok"\|"unavailable"}} |
lexical [KD-5b new work] (search.lexical_search) + link_index + hub vector client [KD-5b new work]; stats.collect_vault_stats for freshness |
QUERY_INVALID, REF_NOT_FOUND (never errors on backend outage — see A(a)) |
| 2 | knowtation/get-note |
{path:string!, ref?:string="HEAD", format?:"json"\|"raw"="json"} |
{path, ref, exists:bool, frontmatter:object, body:string, symbols:[symbol…], content_hash:string} |
muse cat@ref (core.store manifest+object read) + symbols.extract_symbols |
PATH_INVALID, NOT_A_NOTE, NOTE_NOT_FOUND, REF_NOT_FOUND |
| 3 | knowtation/history |
{address:string!, from_ref?:string="HEAD", max?:int=500 (1..100000)} |
{address, start_ref, total_commits_scanned, truncated, events:[{event, commit_id, message, committed_at, address, detail, new_address, event_type, agent_id, model_id}]} |
cli/commands/symbol_log.py (_find_events_in_commit + walk_commits_bfs) — existing |
ADDRESS_INVALID, REF_NOT_FOUND |
| 4 | knowtation/consolidate |
{branch?:string, ours_ref?:string, theirs_ref?:string} (metadata passthrough; hook ignores today) |
dispatch receipt (see A(b)): {hook_name, fired, out_of_band, message, error, dispatched_at, hub_port} |
hooks.KnowtationMergeHook.pre_merge_hook + hook_result_to_json — existing |
(never raises; failures in error) |
| 5 | knowtation/impact |
{path:string!, direction?:"reverse"\|"forward"="reverse", depth?:int=0 (>=0), include_attachments?:bool=true} |
{note, direction, depth_limit, by_depth, total, include_attachments} |
code_intel.impact_for_note/note_impact — existing |
PATH_INVALID, NOTE_NOT_FOUND, DEPTH_NEGATIVE |
| 6 | knowtation/dead |
{use_mtime?:bool=true, mtime_days_threshold?:int=180 (>=0), project?:string} |
{total_notes_indexed, dead_count, dead_notes:[{path, mtime?}]} |
note_metrics.note_dead (+ build_link_index) — existing |
NOT_KNOWTATION_REPO |
| 7 | knowtation/prime |
{max_commits?:int=100 (1..100000), hot_note_count?:int=10 (1..100)} |
PrimeContext.to_dict: {schema_version, source, commits_scanned, last_consolidation, hot_notes} (+ active_projects, suggested_next enrichment) |
prime_context.build_prime_context — existing; active_projects/suggested_next via stats [KD-5b new work] |
(never fatal; partial on graph-read error) |
| 8 | knowtation/propose |
{path:string!, title:string!, proposed_body?:string, proposed_frontmatter?:object, base_ref?:string="HEAD", scope?:"personal"\|"project"\|"org"="personal", rationale?:string} |
{proposal_id, status:"created", hub_url, path, base_ref, scope} |
muse hub proposal create → Knowtation Hub proposal endpoint — [KD-5b new work] (no proposal API in plugin today) |
PATH_INVALID, NOTE_NOT_FOUND, HUB_UNREACHABLE, SCOPE_UNAUTHORIZED, VALIDATION_FAILED |
| 9 | knowtation/attest |
{commit_id:string!, action?:string="write", required?:bool} |
{record:{id, action, path, timestamp, content_hash, algorithm, provider_id, sig?}, anchored:bool, anchor:{tx_id, anchor_url, provider_id}\|null, anchor_error?:string, provider_registered:bool} |
attestation.KnowtationHmacAttestationProvider (compute+anchor) + icp_push_hook — existing; runtime registration [KD-5b new work] (see A(c)) |
PROVIDER_UNREGISTERED, COMMIT_NOT_FOUND, ATTESTATION_REQUIRED |
Write/side-effect gating: tools 4, 8, 9 are the only non-read members. propose is
review-before-write (scope enforced server-side, per Scooling boundary rules); consolidate/attest
are dispatch/opt-in. Read tools (1,2,3,5,6,7) have zero durable side effects.
KD-5a.C — Resource contracts (8)
| # | Resource URI | Content (result) | Backing API | Notes |
|---|---|---|---|---|
| 1 | knowtation://vault |
{notes:[{path, title, project, tags, source_type}], stats:VaultStats} |
stats.collect_vault_stats + note-level walk (parse_frontmatter/extract_symbols) — listing walk [KD-5b new work] |
Root listing + frontmatter summary |
| 2 | knowtation://vault/{path} |
note at HEAD — same body as knowtation/get-note (ref=HEAD) |
get-note core |
{path} obeys shared path grammar |
| 3 | knowtation://vault/{path}@{ref} |
note at {ref} — same body as knowtation/get-note |
get-note core |
URI-template @{ref} parsed then validated |
| 4 | knowtation://prime |
PrimeContext.to_dict |
prime_context.build_prime_context — existing |
Mirrors knowtation/prime (no args) |
| 5 | knowtation://graph |
{notes_indexed, forward:{src:[edge…]}, backward:{tgt:[edge…]}, broken_links:[edge…], entities:{label:[path…]}} |
link_index.build_link_index + JSON serializer [KD-5b new work] (LinkIndex has no to_json today) |
Edge = describe_link shape |
| 6 | knowtation://memory/recent |
{events:[{commit_id, committed_at, message, event_type, agent_id, model_id}], count} (last 20) |
walk_commits_bfs + filter metadata.event_type present — composition [KD-5b new work] |
Event kinds validated against events.MemoryEventKind |
| 7 | knowtation://projects |
{projects:[{project, note_count, last_modified}]} |
stats.collect_vault_stats.projects + _query.group_by_project + mtime — composition [KD-5b new work] |
|
| 8 | knowtation://hotspots |
note_hotspots JSON: {commits_analysed, total_notes_touched, ranking:[{path, changes}]} (top 10) |
note_metrics.note_hotspots + make_snapshot_loader — existing |
top=10 fixed for the resource |
KD-5a.D — Prompt contracts (4)
MCP prompts return messages[]; each composes the tools/resources above (no new backend).
| # | Prompt | Arguments | Composition (backing) | Output |
|---|---|---|---|---|
| 1 | vault/daily-brief |
{since_hours?:int=24 (1..168), project?:string} |
walk_commits_bfs filtered to window + knowtation://memory/recent |
Prompt messages summarising last-24h commits/projects |
| 2 | vault/search-synthesis |
{query:string!, top_k?:int=5 (1..20)} |
knowtation/search (top_k) → format top-5 + synthesis instruction |
Prompt messages: results + synthesis directive |
| 3 | vault/memory-session |
{project?:string} |
knowtation/prime + knowtation://memory/recent |
Prompt messages: prime + recent events to resume session |
| 4 | vault/knowledge-gaps |
{} |
knowtation/dead × link_index.entity_index cross-reference |
Prompt messages: dead notes joined with entity coverage gaps |
KD-5a.E — Namespace mount point (gabriel-muse) & musehub hosting
- Server home [KD-5b new work]:
gabriel-muse/muse/mcp/— no MCP server exists today (confirmed: nomuse/mcp/tree;prime_context.pyonly anticipates Phase 5). KD-5b scaffoldsmuse/mcp/server.py(aMuseMCPServerwith a namespace registry) +muse/mcp/namespaces/knowtation.py(registers the 9 tools / 8 resources / 4 prompts under theknowtation/tool prefix andknowtation://resource scheme). - Activation gate (A(e)): on start, the server reads
.muse/repo.json; it registers theknowtationnamespace iffdomain == "knowtation". It then callsbootstrap_knowtation_mcp(root)which registers the attestation provider + ICP push hook (A(c)). When domain≠knowtation,tools/listreturns noknowtation/*entries. - musehub hosting:
musehublaunches the Muse MCP server per repo — stdio transport for local MCP clients (Claude Desktop, Cursor), and a hosted HTTP+SSE transport for remote agents. The server process runs with the repo root as CWD so all binding is implicit (no path args cross the wire). The marketplace listing@aaronrene/knowtation(already registered, KD-SD8-REG) gains its MCP capability descriptor in KD-5b; the vault UI is KD-6. - Secret hygiene at the mount: the server never echoes
MUSE_AIR_HMAC_KEY,air.endpoint,MUSE_ICP_IDENTITY_PEM,KNOWTATION_HUB_PORTinternals, or absolute host paths in any tool/resource/prompt result, log line, or errordata. Attestationsig(an HMAC output, not key material) MAY be returned; the HMAC key MUST NOT.
KD-5a.F — Seven-tier test matrix (per group + Claude Desktop integration)
All tiers run in gabriel-muse (pytest tests/test_knowtation_mcp_*.py — new files in KD-5b) against
~/MUSE_HUB/knowtation-vault-smoke. Each row is the required minimum for KD-5b Definition of Done.
| Tier | Tools (1–9) | Resources (1–8) | Prompts (1–4) |
|---|---|---|---|
| 1 Unit | Each tool's input-schema validation (grammar A.0), output serialization shape, error-reason mapping; per-tool arg defaults/clamps | Each resource URI parse (incl. @{ref} template), output shape, empty-vault case |
Argument schema + message assembly for each prompt (mocked tool outputs) |
| 2 Integration | Tool → backing API on the smoke vault (search fan-out both backends; get-note@ref; history events; consolidate receipt; impact/dead/prime/hotspots real JSON; propose→hub stub; attest→provider stub) | Resource → backing on smoke vault; graph/memory/recent/projects composition correctness |
Prompt → real tool chain on smoke vault (search-synthesis, memory-session) |
| 3 E2E | tools/list+tools/call over JSON-RPC through the mounted server (domain=knowtation) |
resources/list+resources/read round-trip |
prompts/list+prompts/get round-trip |
| 4 Stress | 5k-note vault: search/impact/dead/hotspots within perf budget; max clamps honoured; vector-backend timeout path under load |
graph on 5k notes; vault listing pagination bound |
daily-brief over 500-commit window; knowledge-gaps over large entity index |
| 5 Data-integrity | history/memory/recent event_type parity vs events.EVENTS_SCHEMA_HASH; get-note content_hash matches store; attest id determinism (compute_attestation_id) |
resource payloads round-trip; graph edges match describe_link; index_freshness == data/ mtime |
prompt outputs cite only real notes (no fabricated paths) |
| 6 Performance | search < 2 s hybrid (lexical budget < 1 s + vector timeout 2 s); impact/dead/hotspots < 2 s on 5k vault (reuse §3.3 budgets); prime < 1 s / 100 commits | graph build < 10 s / 5k (reuse link_index budget) |
prompt assembly < 500 ms excluding model call |
| 7 Security | path-traversal reject (get-note/impact/propose); SSRF-guard on search-vector + consolidate port + attest endpoint + ICP canister-id (reuse existing regex guards); no secret in any result/log/error; air.required rejection; PROVIDER_UNREGISTERED never silent-success; sensitive-key scan (events._has_sensitive_keys) on surfaced note data; activation gate blocks knowtation/* on domain≠knowtation |
resource path-escape reject; memory/recent strips sensitive metadata |
prompt injection: note bodies are data-not-instructions; control-char sanitised |
Claude Desktop integration test (required, Tier 3/E2E): register the KD-5b server in Claude
Desktop's MCP config bound to knowtation-vault-smoke; verify tools/list shows the 9 tools,
resources/list shows the 8 resources, prompts/list shows the 4 prompts, and one live
knowtation/search + knowtation://prime round-trip returns valid payloads. Evidence captured in the
KD-5b handover matrix.
KD-5a.G — Security invariants (frozen)
- No secrets anywhere on the wire: HMAC key, AIR endpoint, identity PEM path, hub port internals,
absolute host paths are never in results, logs, or error
data(§E). - Reuse existing guards:
_normalise_rel_path(path escape),hooks._resolve_port+attestation._validate_endpoint_url+icp_push_hook._is_valid_canister_id(SSRF), body/response byte caps,events._has_sensitive_keys(payload scrub). - Misuse-resistant verification: attest never returns a placeholder as if real;
verifypartial-failures are hard errors (percore.attestationcontract). - Bounded everything: commit walks, note reads, HTTP responses all capped (§0).
- Activation gate is a security control, not a convenience: it prevents non-vault repos from exposing vault write/attest tools (A(e)).
KD-5a.H — KD-5b new-work summary (no backing API today)
knowtation/searchlexical helper (search.lexical_search) + hub vector client + fan-out merge.knowtation/propose→muse hub proposal create+ Knowtation Hub proposal endpoint wiring.- Attestation/ICP runtime registration (
bootstrap_knowtation_mcp) — not in__init__.py. .museignore[domain.knowtation]section (data/,.obsidian/).- Resource serializers:
knowtation://graph,://memory/recent,://projects,://vaultlisting. primeenrichment (active_projects,suggested_next).- The MCP server scaffold itself:
muse/mcp/server.py+muse/mcp/namespaces/knowtation.py.
Everything else (tools 2,3,5,6,7 and resources 2,3,4,8) maps to existing, tested plugin/CLI APIs.
KD-6 — MuseHub UI for Vault Repos (KD-6a contract frozen; KD-6b implementation TODO)
Goal: Domain-aware repo landing, note detail page, knowledge proposals UI, vault search.
Entry criteria: KD-1 symbols + KD-2 diff available on staging vault; KD-5 MCP tools landed (KD-5b DONE). All MET (2026-07-10).
Repo: musehub/ primarily.
Model: Thinking → Auto (KD-6a Thinking DONE — contract frozen in §KD-6a below; KD-6b Auto is NEXT).
KD-6a Contract — MuseHub Vault UI (musehub) — FROZEN 2026-07-10
Status: DONE (Thinking). This section is the frozen spec KD-6b (Auto) implements against. Do not redesign during Build. Interfaces below are the contract; the seven-tier test matrix is in §KD-6a.F. Implementation-only (KD-6b) items are tagged [KD-6b new work]. The KD-5b MCP surface (9 tools / 8 resources / 4 prompts) is a read dependency consumed as-is — do not redesign KD-5 MCP interfaces (hard stop).
Authority chain: issue #2 Phase 6 (vault-centric UI) + KD-5a frozen MCP contract (§KD-5a) +
existing MuseHub UI conventions verified in tree (api/routes/musehub/ui_*.py,
templates/musehub/pages/, src/scss/). Boundary rules from Scooling CROSS-REPO-COORDINATION.md
apply: MuseHub enriches (version/provenance/marketplace); it does not own note bodies;
Knowtation is the canonical vault store + permission authority; review-before-write; scope is
authorization enforced server-side.
KD-6a.0 — Surface conventions & shared contract
- URL scheme (reuse, do not fork): vault pages are domain-conditional branches of the existing
repo route tree
/{owner}/{repo_slug}/…(api/routes/musehub/ui_repo.py::repo_page+ siblings), not a new/vault/…tree. Ref selection reuses the existing?ref=<branch|sha>query param. - Domain activation gate (mirrors KD-5a.E — security control, not convenience): a page renders
its vault variant iff the repo's active marketplace domain resolves to
knowtation(_nav_ctx._resolve_repo_domain/_resolve_domain_viewer_type→ domain@aaronrene/knowtation). Acode/mistrepo that merely contains a few notes never shows vault tabs, the note-detail variant, or the knowledge-proposal write surface.detector.classify_repois advisory only (may power a "looks like a vault — activate?" hint), never auto-mounts. - Content negotiation (frozen, reuse verbatim):
?format=jsonorAccept: application/json→ structured JSON (camelCase,by_alias);HX-Request: true→ bare HTMX fragment (partial swap target); default → full SSR page. Canonical machine-readable REST lives under/api/…; UI routes are the same-URL negotiated surface (established pattern inui_repo.py,ui_domains.py). - Read surface split (frozen):
- Note bodies, tree, commits, diffs, refs are served from MuseHub's own versioned snapshot
store (
services/musehub_repository,muse_cli/snapshot.py) — exactly as code repos are today. This is versioned enrichment, not body ownership; the canonical body still lives in the Knowtation vault. - Domain intelligence (hybrid search, backlinks/impact, dead notes, link graph, prime, memory
timeline, hotspots, history) is read from the mounted Muse domain MCP
knowtation/*surface (KD-5b) — the 9 tools / 8 resources / 4 prompts, consumed as-is.
- Note bodies, tree, commits, diffs, refs are served from MuseHub's own versioned snapshot
store (
- Adapter boundary (frozen): no Jinja template or route handler calls an MCP tool / provider
SDK inline. All MCP reads go through a hub-side
services/knowtation_viewadapter [KD-6b new work] that (i) resolves the mounted per-repo Muse MCP server (stdio for local, hosted HTTP+SSE per KD-5a.E), (ii) issuestools/call+resources/read, (iii) normalises the JSON-RPC envelope into hub view-models, and (iv) maps MCP errorreasontokens → UI states (§KD-6a.E). This is the KD-6 analog of the Scooling "no component calls a provider directly" rule. - Write surface (frozen — review-before-write): knowledge-proposal creation dispatches to
knowtation/propose(→ Knowtation Hub proposal endpoint; scopepersonal|project|orgenforced server-side). MuseHub renders, lists, threads, and reviews proposals via the existing proposals surface (ui_proposals.py,MusehubProposal); it never writes note bodies directly and never enforces scope in the UI layer. - Resilience / degradation (frozen): the MuseHub-owned data path (bodies/tree/history from the
snapshot store) is independent of the MCP path. If the mounted MCP server is unavailable or a
tool returns
degraded=true, vault-intel panels render a non-fatal "domain intelligence unavailable" / "semantic search unavailable — showing lexical results" state; the page still renders bodies, tree, and history. A domain-intel outage is never a 5xx. - Error envelope: HTML → standard hub error pages (403/404/410/500) with domain-aware empty
states; JSON → existing hub error shape
{error:{code,message}}; MCP-sourced problems surface as the UI-state enum in §KD-6a.E, not as raw JSON-RPC errors. - Bounds & determinism: reuse hub cursor pagination for lists; note-body render reuses the 16 MiB cap; search/impact/history reuse the MCP tool clamps (KD-5a.0); no unbounded walks initiated from the UI.
- Secret hygiene: never render
KNOWTATION_HUB_PORTinternals,air.endpoint,MUSE_AIR_HMAC_KEY,MUSE_ICP_IDENTITY_PEM, or absolute host paths in any HTML, JSON,data-*attribute, OG tag, or JSON-LD block. Attestationsig(an HMAC output) MAY render; key material MUST NOT.
KD-6a.A — Resolution of the five open UI contract items (a)–(e)
(a) Note-body rendering & sanitisation — FROZEN: server-side render, sanitised, wikilinks resolved.
Markdown → HTML is rendered server-side reusing the hub's existing renderer path (the
README/blob pipeline behind _ui_helpers._fetch_readme + blob.html), extended [KD-6b new work]
with a Knowtation post-processor that (i) resolves [[wikilinks]] and [[wikilink#section]] to
/{owner}/{repo_slug}/note/{path} (broken links get a broken class, never a raw link), and (ii)
renders frontmatter as a metadata panel, never executed or reflected into script context. Raw
inline HTML in note bodies is sanitised (allowlist; no <script>/event handlers) — bodies are
data, not instructions (parity with KD-5a.G prompt-injection stance). Attachments/images resolve
through the existing blob route; external URLs are not fetched server-side.
(b) Vault search backend — FROZEN: knowtation/search hybrid, not hub DB global_search.
The vault search page calls the Muse domain MCP knowtation/search (hybrid: w_lex=0.4,
w_vec=0.6, KD-5a.A(a)) through the knowtation_view adapter, with project? filter and mode
toggle (hybrid|lexical|semantic). It does not reuse musehub_repository.global_search (which is
commit-message keyword search, cross-repo). On degraded=true (vector backend down) the page shows
lexical-only results with a banner and index_freshness. Cross-repo global search (/api/search,
global_search.html) is unchanged and remains hub-DB keyword search.
(c) Proposals UI ownership split — FROZEN: MuseHub renders/reviews; Knowtation owns create+scope.
MuseHub owns proposal listing, detail, review threads, comments, and section-aware diff display
(reuse ui_proposals.py + proposal_detail.html; the diff panel renders the KD-2 section-aware
harmony diff — tag/entity/§Summary semantics — rather than a raw text diff [KD-6b new work]:
a differ-output → HTML view). Proposal creation and scope authorization are Knowtation-canonical
via knowtation/propose; the "Propose edit" affordance on the note-detail page opens a form that
POSTs to a thin hub route which delegates to the adapter's propose call. Scope
(personal|project|org) is displayed as a badge but enforced server-side by Knowtation.
(d) Domain-intel freshness & failure isolation — FROZEN.
Every vault-intel panel surfaces the MCP-reported index_freshness (or null). The MCP read path is
isolated from the snapshot-store read path (§KD-6a.0 resilience): a mounted-server outage degrades
only intel panels. The repo-landing intel strip reuses _ui_helpers._load_intel_strip's slot,
populated for vault repos by dead/hotspots/prime [KD-6b new work] instead of the code-intel
loaders.
(e) Activation semantics & viewer_type — FROZEN: gate on active domain; vault viewer_type optional.
The vault UI variant mounts iff active domain == knowtation (as (§KD-6a.0) — the same gate as
KD-5a.E), independent of the marketplace viewer_type. The existing ViewerType enum
(symbol_graph|piano_roll|generic, api/routes/musehub/domains.py) currently classes
@aaronrene/knowtation as generic. Adding a first-class vault viewer_type is an optional,
deferred palette change (musehub#117 lineage) — KD-6b MUST NOT block on it and MUST NOT force an
enum migration; the domain gate is the source of truth.
KD-6a.B — Page contracts (4 groups)
Legend: Store = MuseHub snapshot/DB read; MCP = knowtation_view adapter → KD-5b surface;
[KD-6b new work] = net-new template/route/adapter code.
| # | Page group | Purpose | Primary sections | Data sources |
|---|---|---|---|---|
| 1 | Vault repo landing (variant of repo_home.html) |
Domain-aware home for a knowtation repo |
Vault header (note count, projects, index_freshness); note tree (folders); recent memory timeline; intel strip (dead / hotspots); prime "resume here" card; clone/MCP-connect hints |
Store: tree, recent commits, README. MCP: knowtation://vault (listing+stats), knowtation://projects, knowtation://memory/recent, knowtation://hotspots, knowtation/dead, knowtation://prime |
| 2 | Note detail (note_detail.html [KD-6b new work]) |
Rendered note @ref with provenance & graph | Rendered body (A(a)); frontmatter panel; symbol outline (headings/wikilinks/entities); backlinks / forward-links (impact); change history timeline; provenance/attestation strip; "Propose edit" affordance | Store: body/manifest @ref, commit list. MCP: knowtation/get-note, knowtation/impact (reverse+forward), knowtation/history, knowtation://graph (neighbourhood), knowtation/attest (read-only provenance strip — no new attest issued from UI) |
| 3 | Knowledge proposals (reuse proposal_list.html + proposal_detail.html) |
List/review vault-note proposals; section-aware diff | Proposal list (scope badges, status); detail with KD-2 harmony diff (tag/entity/§Summary); review threads/comments; scope badge; create form → propose |
Store: MusehubProposal rows, review/comment threads, base/proposed manifests. MCP: knowtation/propose (create only); KD-2 differ output rendered for the diff panel |
| 4 | Vault search (vault_search.html [KD-6b new work]) |
Hybrid vault search with project filter | Query box + mode toggle (hybrid/lexical/semantic) + project filter; result cards (path, snippet, section address, score, source badge lexical/vector/both); degraded/index_freshness banner |
MCP: knowtation/search (A(b)); optional vault/search-synthesis prompt for an agent "synthesise" action (deferred, not required for KD-6b DoD) |
KD-6a.C — Route / API contracts
All routes are content-negotiated (§KD-6a.0). New HTML routes live in a new
api/routes/musehub/ui_vault.py [KD-6b new work]; proposals reuse ui_proposals.py.
| # | Method + Route | Handler (module) | Response (HTML / JSON) | Notes |
|---|---|---|---|---|
| 1 | GET /{owner}/{repo_slug} |
ui_repo.py::repo_page (existing; branch on domain [KD-6b new work]) |
vault repo_home.html variant / RepoResponse+vault meta |
Reuses existing route; adds vault landing branch when domain==knowtation |
| 2 | GET /{owner}/{repo_slug}/note/{path:path} |
ui_vault.py::note_detail_page [KD-6b new work] |
note_detail.html / {path, ref, frontmatter, body_html, symbols, backlinks, forward_links, history, provenance, freshness} |
?ref=; {path} obeys the KD-5a.0 path grammar (reject ../absolute/non-.md) |
| 3 | GET /{owner}/{repo_slug}/vault/search |
ui_vault.py::vault_search_page [KD-6b new work] |
vault_search.html / {query, mode, results[], degraded, index_freshness, backends} |
?q=&mode=&project=&top_k=; rate-limited (reuse SEARCH_LIMIT) |
| 4 | GET /{owner}/{repo_slug}/vault/graph |
ui_vault.py::vault_graph_fragment [KD-6b new work] |
HTMX fragment / knowtation://graph JSON |
Backlink/neighbourhood panel; lazy hx-get from note detail & landing |
| 5 | GET /{owner}/{repo_slug}/vault/memory |
ui_vault.py::vault_memory_fragment [KD-6b new work] |
HTMX fragment / knowtation://memory/recent JSON |
Recent memory timeline strip |
| 6 | GET /{owner}/{repo_slug}/proposals · /proposals/{id} |
ui_proposals.py (existing; vault diff renderer [KD-6b new work]) |
proposal_list.html / proposal_detail.html |
Domain-aware diff panel (KD-2 section-aware) for vault repos |
| 7 | POST /{owner}/{repo_slug}/proposals/new (vault) |
ui_vault.py::create_note_proposal [KD-6b new work] |
redirect / {proposal_id, status, hub_url, scope} |
Thin delegate → adapter propose; CSRF-protected (reuse hub session/MSign); scope enforced server-side |
No new canonical REST namespace is introduced for domain intelligence — the machine-readable
surface for those reads is already the Muse domain MCP (knowtation/*, KD-5b). The hub UI's JSON
negotiation exists for its own SSR data; agents that want raw domain intel use MCP directly.
KD-6a.D — MCP tool/resource → UI mapping (read surfaces, KD-5b consumed as-is)
| KD-5b surface | Kind | Consumed by (page/panel) | UI use |
|---|---|---|---|
knowtation/search |
tool | Vault search (B4) | Hybrid results, mode toggle, degraded banner |
knowtation/get-note |
tool | Note detail (B2) | Body @ref cross-check + symbols/content_hash (bodies primarily from store) |
knowtation/history |
tool | Note detail history timeline (B2) | Per-address event timeline (event_type, agent/model) |
knowtation/impact |
tool | Note detail backlinks/forward (B2) | Reverse + forward link neighbourhood by depth |
knowtation/dead |
tool | Landing intel strip (B1) | Dead-note count + list panel |
knowtation/prime |
tool | Landing "resume here" card (B1) | Hot notes, active projects, suggested next |
knowtation/propose |
tool | Proposal create (B3, C7) | Review-before-write creation only |
knowtation/attest |
tool | Note/commit provenance strip (B2) | Read-only render of existing attestation record; UI issues no new attest |
knowtation/consolidate |
tool | (not surfaced in KD-6b UI) | Out-of-band; observed via memory timeline — no UI trigger in KD-6 |
knowtation://vault |
resource | Landing note tree + stats (B1) | Listing + VaultStats |
knowtation://vault/{path}[@{ref}] |
resource | Note detail (B2) | Note payload at ref (parity with get-note) |
knowtation://prime |
resource | Landing (B1) | Prime card (no-arg) |
knowtation://graph |
resource | Graph fragment (C4), note detail neighbourhood | Forward/backward edges, broken links, entities |
knowtation://memory/recent |
resource | Memory fragment (C5), landing (B1) | Last-20 memory events |
knowtation://projects |
resource | Landing projects panel (B1) | Project → note-count/last-modified |
knowtation://hotspots |
resource | Landing intel strip (B1) | Top-10 churn ranking |
vault/daily-brief, vault/search-synthesis, vault/memory-session, vault/knowledge-gaps |
prompts | Optional agent affordances (deferred) | Not required for KD-6b DoD; wired only as opt-in "ask an agent" buttons if time permits |
KD-6a.E — UI error / empty-state enum (stable tokens)
The knowtation_view adapter maps MCP reason tokens + hub HTTP outcomes into these stable UI
states (each renders a specific empty/banner/error template, never a stack trace or secret):
| UI state token | Trigger (MCP reason / hub) | Rendered as |
|---|---|---|
VAULT_DOMAIN_INACTIVE |
active domain ≠ knowtation | 404-style "no vault view for this repo" (gate) |
NOTE_NOT_FOUND |
NOTE_NOT_FOUND / store miss |
404 note page with tree fallback |
REF_NOT_FOUND |
REF_NOT_FOUND |
Inline "unknown ref" notice, default to HEAD |
PATH_INVALID / NOT_A_NOTE |
PATH_INVALID, NOT_A_NOTE |
400 "invalid note path" (path grammar reject) |
DOMAIN_INTEL_UNAVAILABLE |
mounted MCP server unreachable / tool timeout | Non-fatal panel banner; body/tree/history still render |
SEARCH_DEGRADED |
search degraded=true |
"Semantic search unavailable — lexical results" banner |
PROPOSAL_SCOPE_UNAUTHORIZED |
SCOPE_UNAUTHORIZED |
403 on create; badge + reason, no write |
PROPOSE_HUB_UNREACHABLE |
HUB_UNREACHABLE |
Retryable "proposal service unavailable" notice |
PROPOSE_VALIDATION_FAILED |
VALIDATION_FAILED |
Inline form validation errors |
PROVENANCE_UNAVAILABLE |
PROVIDER_UNREGISTERED / COMMIT_NOT_FOUND |
Provenance strip shows "not attested" (never fake) |
KD-6a.F — Seven-tier test matrix (per page group + browser E2E)
All UI tiers run in musehub (pytest tests/… + vitest/Playwright for browser E2E) against a
knowtation-domain fixture repo mirroring ~/MUSE_HUB/knowtation-vault-smoke. Each row is the
required minimum for KD-6b Definition of Done.
| Tier | Landing (B1) | Note detail (B2) | Proposals (B3) | Vault search (B4) |
|---|---|---|---|---|
| 1 Unit | Domain-gate branch selects vault variant; view-model assembly from mocked adapter; empty-vault render | Wikilink/frontmatter render + sanitisation (A(a)); symbol outline; path-grammar validation | Scope-badge render; harmony-diff view-model from mocked KD-2 differ; create-form validation | Query/mode/project param parse; result-card view-model; degraded banner logic |
| 2 Integration | Route → knowtation_view adapter → live KD-5b tools on fixture (vault/projects/memory/hotspots/dead/prime) |
note/{path} → store body + get-note/impact/history/graph compose correctly |
list/detail → MusehubProposal + KD-2 diff; create → adapter propose (stubbed hub) |
search page → knowtation/search hybrid on fixture; project filter |
| 3 E2E (browser) | Playwright: load vault landing, tree + intel strip visible | Navigate note → backlinks → linked note; history expands | Open proposal → diff renders → add review comment | Type query → results render → click result → note detail |
| 4 Stress | 5k-note vault landing within perf budget; tree pagination bound | note detail with 500-commit history + large backlink set | proposal list pagination; large section-aware diff | search over 5k vault; rapid query debounce; vector-timeout path |
| 5 Data-integrity | tree/stats match store; index_freshness == MCP report |
rendered body content_hash matches store; history event_type parity vs events.EVENTS_SCHEMA_HASH; provenance record matches attest |
diff reflects true base↔proposed; scope badge matches server decision | results cite only real note paths (no fabricated); scores in [0,1] |
| 6 Performance | landing SSR < 2 s on 5k vault (reuse repo-home budget) | note detail < 2 s incl. impact/history | proposal detail render < 2 s | search round-trip < 2 s hybrid (lexical < 1 s + 2 s vector timeout) |
| 7 Security | activation gate blocks vault tabs on domain≠knowtation; no secret in HTML/JSON/OG/JSON-LD | path-traversal reject on note/{path}; markdown/wikilink XSS-sanitised; bodies-as-data |
scope enforced server-side (UI cannot bypass); CSRF on create; review actions authz | rate-limit (SEARCH_LIMIT); query injection-safe; degraded never leaks internals |
Browser integration test (required, Tier 3/E2E): a Playwright run against the fixture vault repo that (i) loads the vault landing and asserts tree + intel strip + prime card, (ii) opens a note detail and asserts rendered body + backlinks + history, (iii) runs a vault search and clicks through a result, and (iv) opens a knowledge proposal and asserts the section-aware diff renders. Evidence captured in the KD-6b handover matrix (analog to the KD-5b Claude Desktop test).
KD-6a.G — Security invariants (frozen)
- Activation gate is a security control: vault write/proposal/attest surfaces never appear on a
non-
knowtationrepo (§KD-6a.0, .A(e)). - Bodies are data, not instructions: server-side markdown sanitisation (allowlist), wikilink resolution never emits executable content, frontmatter never reflected into script context (A(a)).
- No secrets on the wire: hub port internals, AIR endpoint, HMAC key, identity PEM path, absolute
host paths never in HTML/JSON/
data-*/OG/JSON-LD (§KD-6a.0). AttestationsigMAY render; key material MUST NOT. - Scope is server-side authorization: the UI displays scope but never enforces it;
proposescope + all durable writes are authorised by Knowtation (SCOPE_UNAUTHORIZED→ 403). - Path grammar reuse:
note/{path}reuses the KD-5a.0 path escape rules (reject.., absolute, non-note extensions) before any store/MCP read. - CSRF + rate limits: proposal-create POST is CSRF-protected via the existing hub session/MSign
path; search reuses
SEARCH_LIMIT. - Failure isolation: a mounted-MCP outage degrades intel panels only and never a 5xx (§KD-6a.0);
provenance never renders a placeholder as if real (
PROVENANCE_UNAVAILABLE).
KD-6a.H — KD-6b new-work summary (net-new vs reused)
Net-new [KD-6b new work]:
services/knowtation_viewadapter (MCP mount resolution + envelope normalisation + error mapping).api/routes/musehub/ui_vault.py(note detail, vault search, graph/memory fragments, proposal-create delegate).templates/musehub/pages/note_detail.html+vault_search.html; vault variant ofrepo_home.html; SCSS (_note-detail.scss,_vault.scss; reuse_repo-home.scss,_search.scss,_diff.scss,_proposals.scss).- Markdown+wikilink+frontmatter Knowtation post-processor with sanitisation (A(a)).
- KD-2 section-aware harmony-diff → HTML renderer for the vault proposal detail (A(c)).
- Vault intel-strip loader (dead/hotspots/prime) filling the existing
_load_intel_stripslot (A(d)). - Playwright browser E2E fixture + suite (§KD-6a.F).
Reused as-is: repo route tree + content negotiation (ui_repo.py); domain resolution
(_nav_ctx); proposals list/detail/threads (ui_proposals.py, MusehubProposal); snapshot/manifest
store (services/musehub_repository, muse_cli); blob/README render pipeline; rate limits; the entire
KD-5b MCP surface (9 tools / 8 resources / 4 prompts — not redesigned).
KD-7 — Attestation (done locally, verification pending)
Goal: HMAC attestation, ICP push hook, verify-commit --attest, provenance strip on Hub.
Deliverables (verified in tree): attestation.py, icp_push_hook.py, quorum.py
Exit criteria: Attestation smoke on staging vault with --attest flag (post KD-ACTIVATE).
Open questions (issue #2 — resolved in KD-OQ, 2026-07-10)
All five closed. Each decision is grounded in shipped plugin source (not a guess); the "Evidence" column cites the file that already implements or constrains the choice.
| # | Question | Options (summary) | Decision | Evidence |
|---|---|---|---|---|
| 1 | Vault detection boundary | Frontmatter-only vs require .knowtation / config/local.yaml marker |
DECIDED — Detector is frontmatter fingerprint primary; .knowtation marker is an optional override (not required). Note (verified): the detector classifies; a repo's active domain is still set explicitly via muse init --domain knowtation → .muse/repo.json, so detection informs classification rather than auto-activating a repo. Marker is an escape hatch for sparse-frontmatter vaults. |
detector.py classify_note rules 1–4 (frontmatter) + classify_repo rule A (marker, conf 1.0) → rule B (majority ≥0.50) → rule C (mist); activation via repo.json |
| 2 | Vector index ownership | Muse parallel index vs query Knowtation data/ index directly |
DECIDED — Split by capability. Muse owns structural/lexical intel — link graph, symbols, hotspots from live-vault parsing (build_link_index/code_intel), plus commit-graph history/hot-notes (prime_context); Knowtation's live data/ vector index stays canonical for semantic search (queried at HEAD, not rebuilt or versioned by Muse). Muse surfaces index_freshness (mtime of data/) so agents know vector recency. KD-5a must resolve: whether data/ needs .museignore (today only .knowtation is always-ignored) and the knowtation/search fan-out/ranking. |
link_index.py build_link_index(root) + code_intel.py walk live files; prime_context.py walks commit graph; stats.py reads data/ mtime only ("no vector store"); plugin.py _ALWAYS_IGNORE_DIRS excludes .knowtation (not data/) |
| 3 | Consolidation daemon coupling | muse merge --consolidate pre-hook vs async watcher |
DECIDED — Pre-merge hook that dispatches asynchronously. --consolidate is the explicit opt-in trigger (default off); execution is fire-and-forget background POST to the local hub daemon so merge never blocks on LLM/network latency. Hook errors are caught by the merge CLI and the registry, so they never fail the merge. |
hooks.py KnowtationMergeHook.pre_merge_hook (default consolidate=False) → _post_consolidate_in_background, out_of_band=True; auto-registered at import via __init__.py |
| 4 | ICP canister reuse | Knowtation Motoko canister vs generic Muse attestation interface | DECIDED — Both layers: generic MuseAttestationProvider interface in core, implemented by the knowtation plugin, which REUSES the existing Knowtation Motoko canister (dejku-syaaa-aaaaa-qgy3q-cai) + AIR HMAC daemon. No new ICP infra. Note (verified): the interface + implementation + registration helpers exist, but only the merge hook is auto-registered at import today — wiring the attestation provider / ICP push hook into the runtime commit/push path is KD-5a/5b work. |
core MuseAttestationProvider protocol + registry; attestation.py KnowtationHmacAttestationProvider, icp_push_hook.py KnowtationICPPushHook; helpers register_knowtation_attestation_provider / _icp_push_hook (not yet called in __init__.py); canister-ID SSRF guard |
| 5 | SKILL.md agent discovery | Merge into Muse agent.md vs keep separate |
DECIDED — Keep separate. muse agent-config stays a canonical (.muse/agent.md) → derived-adapter generator; it does NOT auto-discover/inline product SKILL.md files. Optional (prospective, not built): a reference pointer to the skill when a knowtation domain is detected — no content merge. |
cli/commands/agent_config.py source→adapter model (_ADAPTERS, run_sync); no SKILL.md scan exists; SKILL.md owned by product .cursor/skills/ (11 files) |
Downstream effect on KD-5a: these answers freeze the MCP contract's search ownership split (OQ2),
consolidate coupling (OQ3 async dispatch), attest architecture (OQ4 generic interface + canister reuse),
and confirm no agent.md merge work (OQ5). Contract-level detail the decisions deliberately left to the
contract — (a) knowtation/search fan-out algorithm/weights/dedupe/timeout; (b) knowtation/consolidate
return schema; (c) knowtation/attest runtime registration + required-mode; (d) whether Muse must
.museignore data/; (e) MCP-scope activation semantics — is now RESOLVED and frozen in §KD-5a Contract
(KD-5a.A), 2026-07-10.
Cross-repo coordination (read-only pointers)
| Document | Repo | Purpose |
|---|---|---|
docs/CROSS-REPO-COORDINATION.md |
scooling | SD-8 namespace decision; canonical-first ordering |
docs/KNOWTATION-MUSEHUB-WIRING.md |
scooling | How Scooling will consume domain features long-term |
docs/VERIFICATION-MATRIX.md |
scooling | Source-backed facts on plugin manifest |
musehub/docs/issues/custom-domain-registration.md |
musehub | Marketplace URL + author-identity hardening notes |
Scooling Phase 1 rule: must not require live MuseHub or production Knowtation Domain. Domain activation does not block Scooling Phase 9A.
Governance hygiene (manual until orchestrator is live)
At session end for any KD phase:
- Update this file — build status table, "Next step at a glance", phase Status lines.
- Update
docs/MUSEHUB-OVERSEER-HANDOVER.md— rotate NEXT SESSION, verified snapshot, VCS table, change log, paste-ready prompt. - Muse commit on feature branch (Tier 1); staging push / merge to
mainonly with Tier 3 authorization. - Run test suite for touched areas before commit.
A session that ends without both governance docs updated is incomplete.
Orchestrator: The cross-workspace overseer orchestrator (Scooling session) is not required for this track yet. Use these two docs manually until that tooling ships.
Verified snapshot (2026-07-10)
| Fact | Source |
|---|---|
| Plugin path | ~/MUSE_HUB/gabriel-muse/muse/plugins/knowtation/ — 26 .py files |
| Test files | ~/MUSE_HUB/gabriel-muse/tests/test_knowtation_*.py — 27 files |
| Last full-suite count | 1570 passed, 0 failed (2026-06-20 recovery) |
| Staging plugin repo | https://staging.musehub.ai/aaronrene/gabriel-muse — HTTP 200 |
| Marketplace listing | @aaronrene/knowtation — version 1.0.0, 6 dimensions, install_count: 0 |
| Test vault | ~/MUSE_HUB/knowtation-vault-smoke — 3 notes, domain=knowtation, Harmony policies |
| Issue tracker | gabriel/muse #2 — assigned aaronrene |
| OQ resolution basis | Shipped plugin source — detector.py, link_index.py, stats.py, hooks.py, attestation.py, icp_push_hook.py, cli/commands/agent_config.py |
Change log
- 2026-07-10 — KD-6a DONE (Thinking). Froze the MuseHub vault UI contract for the
musehubrepo (§KD-6a Contract): 4 page groups (vault repo landing, note detail, knowledge proposals, vault search) with page/route/MCP-mapping/error-enum tables and a seven-tier test matrix + required Playwright browser E2E. Resolved five open UI contract items: (a) server-side sanitised markdown+wikilink+frontmatter render (bodies-as-data); (b) vault search =knowtation/searchhybrid (not hub-DBglobal_search), lexical-degrade banner; (c) proposals ownership split — MuseHub renders/reviews (KD-2 section-aware harmony diff), Knowtation owns create + server-side scope viaknowtation/propose; (d) domain-intel freshness + MCP/store read-path failure isolation (intel outage never 5xx); (e) activation gate on active domain==knowtation (notviewer_type),vaultviewer_type deferred/optional. Frozen conventions: reuse/{owner}/{repo_slug}route tree + content negotiation; no template calls MCP inline — hub-sideservices/knowtation_viewadapter boundary; KD-5b MCP surface (9 tools / 8 resources / 4 prompts) consumed as-is (no redesign). Flagged KD-6b new work (adapter,ui_vault.py,note_detail.html/vault_search.html, markdown post-processor, KD-2 diff renderer, vault intel-strip loader, Playwright suite). Muse commit onfeat/kd-6a. NEXT → KD-6b (Auto). - 2026-07-10 — KD-5b DONE (Auto). Implemented the frozen §KD-5a MCP domain surface:
muse/mcp/server.py+muse/mcp/namespaces/knowtation.pywith activation gate (repo.json domain==knowtation),bootstrap_knowtation_mcp(attestation + ICP hook registration),search.lexical_search+ vector client + hybrid fan-out, hubpropose, resource serializers, prime enrichment, 4 vault prompts, stdio entry (python -m muse.mcp.stdio_server). Seven-tier test matrix: 36 tests intests/test_knowtation_mcp_*.py+ Claude Desktop stdio integration againstknowtation-vault-smoke(9 tools / 8 resources / 4 prompts list + search/prime round-trip). Full knowtation suite: 1606 passed. Muse commit onfeat/kd-5b. NEXT → KD-6a (Thinking). - 2026-07-10 — KD-5a DONE (Thinking). Froze the Muse MCP domain-surface contract for the
knowtationnamespace (§KD-5a Contract): 9 tools, 8 resources, 4 prompts — each with JSON-RPC input/output schema, backing plugin API mapping, and error-reason enum. Resolved the five open contract items: (a) search fan-outw_lex=0.4/w_vec=0.6, path dedupe, 2 s vector timeout → lexical-onlydegraded; (b)consolidatedispatch-receipt schema, async completion observed viamemory/recent; (c) attest runtime registration viabootstrap_knowtation_mcp(not__init__.py),air.requiredmode,PROVIDER_UNREGISTEREDstructured error; (d).museignore [domain.knowtation]ignoresdata/for snapshots whilestatsstill reads its mtime; (e) namespace mounts only on.muse/repo.json domain==knowtation, detector advisory. Specified themuse/mcp/mount point (server +knowtationnamespace, both KD-5b new work) and musehub stdio/SSE hosting. Defined the seven-tier test matrix + Claude Desktop integration test againstknowtation-vault-smoke. Flagged KD-5b new work (search lexical+vector, propose→hub, attest registration, resource serializers, MCP server scaffold). Muse commit onfeat/kd-5a. NEXT → KD-5b (Auto). - 2026-07-10 — KD-OQ review pass (independent model, read-only). Verified all 5 decisions against source; decisions stand. Corrected: prompt count 3→4 (issue #2 Phase 5.3) and KD-5 tool list now includes
consolidate(9 tools); tightened OQ1 (detector classifies; activation viarepo.json), OQ2 (link_index/code_intel are live-file, not commit-snapshot;data/not auto-ignored), OQ4 (registration helpers exist but only the merge hook auto-registers — runtime wiring is KD-5a/5b). Added explicit KD-5a open contract items (search fan-out, consolidate return schema, attest wiring,data/ignore, activation semantics). - 2026-07-10 — KD-OQ DONE. All 5 issue #2 open questions resolved with source-backed rationale (see table). Decisions: OQ1 frontmatter-primary + optional
.knowtationmarker; OQ2 Muse structural index + Knowtation live vector index (split); OQ3 async-dispatch pre-merge hook; OQ4 genericMuseAttestationProviderreusing the ICP canister; OQ5 keep SKILL.md separate. NEXT → KD-5a (MCP contract, Thinking). - 2026-07-10 — KD-ACTIVATE DONE. Vault
knowtation-vault-smokeactivated; smoke 5 PASS / 3 PARTIAL. NEXT → KD-OQ. CLI gaps documented (merge dispatch, symbols routing, diff granularity). - 2026-07-10 — Initial MuseHub Roadmap created. Phases 1–4 + 7 marked DONE (local); KD-ACTIVATE set as NEXT.