diff --git a/STRATEGY.md b/STRATEGY.md new file mode 100644 index 0000000..20b64de --- /dev/null +++ b/STRATEGY.md @@ -0,0 +1,53 @@ +--- +name: librarian-mcp +last_updated: 2026-05-30 +--- + +# librarian-mcp Strategy + +## Target problem + +Every time Neal (or an agent acting for him) starts work, he re-establishes what was already decided, where each project stands, and what's true for him personally — because that judgment lives scattered across repo docs, email, texts, and ephemeral Claude memory, never retained in one place. Decisions get re-derived, sometimes silently re-litigated, and the vault that should hold them is instead a pile of scraped research. + +## Our approach + +Maintain a judgment layer in the brain (decisions, rationale, entity pages, project state) as synthesis-as-docs that act as canonical commitments; recompute the awareness layer (news, what-changed, daily flux) on demand. Maintain what reduces decisions; recompute what's just news. + +## Who it's for + +**Primary:** Neal — single operator running a portfolio of projects (trading/Voltron/QF2, signupspark, ourgospelstudy, localcred, librarian-mcp, the Threshold book, …). He's hiring the brain to retain his accumulated judgment so he stops re-deriving past decisions and project state at the start of every session. + +**Secondary:** Agents acting on Neal's behalf — Claude sessions across the portfolio. They're hiring the brain as authoritative context so they don't silently re-decide things or drift off Neal's voice. + +## Key metrics + +- **Decision/state freshness coverage** — fraction of active projects with a current (<14-day) decision & state page in the vault. Measurable via the vault + `library_changes`; can regress (staleness, gaps). +- **Re-derivation rate** — fraction of session-start "what did we decide / where are we / what's true for Neal" questions answered from the brain vs re-derived. Qualitative/sampled; the most direct read on decision-reduction. +- **Retrieval quality** — `library_eval`'s recall@2hops and ranked precision@10. Already measured deterministically (v0.1.2); regresses if structural rot returns. +- **Personal-corpus presence** — share of "true to Neal" entity pages (relationships, preferences, commitments) with non-empty, current content. Captures the personal-corpus gap. + +## Tracks + +### Judgment layer (maintained synthesis-as-docs + agent-proposed steering) + +The output side: per-project decision & state pages, per-entity pages (people, projects, recurring concepts), kept current as canonical commitments. Two halves: **retention** (decisions, rationale, ratified outcomes — never re-derived) and **steering** (an on-demand `/librarian propose` reads the page's accumulated outcome history plus its declared research scope and drafts ranked, cited candidate next moves into a fenced managed block). Replaces "ephemeral synthesis" with retained judgment that also compensates for thin domain experience when the operator next picks up the project. + +_Why it serves the approach:_ this IS the maintained judgment layer — without it the brain has nothing to retain (decisions stay re-derivable) and nothing to steer with (the operator works from instinct rather than evidence). + +### Corpus acquisition (get the right inputs in) + +The input side: pipe decision-bearing content from project repos (plans, ADRs, brainstorm outcomes, `PROJECT_STATUS`) and the personal corpus (email, texts, commitments) into the vault. Privacy-local for personal sources; never committed/published. + +_Why it serves the approach:_ the biggest current gap. The brain cannot reduce decisions it doesn't contain — most of them live outside the vault today. + +### Awareness layer (recompute on demand) + +The ephemeral side: on-demand briefings, "what changed" recency queries (`library_changes`), the future spatial dashboard. Cheap to regenerate, no commitment value, additive output only. + +_Why it serves the approach:_ keeps the maintained layer focused on decisions; everything that's just current-awareness goes here so it never accretes maintenance debt. + +## Not working on + +- A local mirror of the internet / a RAG search index over arbitrary scraped content — the "bad collection" failure mode. +- Maintained synthesis of ephemeral awareness (daily news pages that need reconciliation) — rejected on durability grounds; the awareness layer is recompute-only. +- librarian-mcp generating synthesis itself — the Rust server holds no LLM; synthesis is the agent's job. The server provides structure + primitives only. diff --git a/docs/brainstorms/2026-05-27-obsidian-project-command-center-requirements.md b/docs/brainstorms/2026-05-27-obsidian-project-command-center-requirements.md new file mode 100644 index 0000000..251fbee --- /dev/null +++ b/docs/brainstorms/2026-05-27-obsidian-project-command-center-requirements.md @@ -0,0 +1,168 @@ +--- +date: 2026-05-27 +topic: obsidian-project-command-center +--- + +# Obsidian Project Command Center + +## Summary + +A native-Obsidian project command center, powered by librarian-mcp as a data layer. librarian maintains structured per-project **status notes** (written at session-close) and per-project **news notes** (fetched from RSS feeds on a schedule); a **Dataview dashboard note** renders both into status tiles and per-project news inside Obsidian — so opening one note replaces asking "what's next." + +--- + +## Problem Frame + +Neal runs a portfolio of active projects (trading/Voltron/QF2, signupspark, ourgospelstudy, localcred, librarian-mcp, the Threshold book, etc.). Today, re-establishing "where is everything and what should I do next" is an **active pull**: he runs `/portfolio-resume` or `/project-status`, or simply asks the agent "what's next." Each answer is generated on demand and evaporates — there's no persistent surface that's already current when he opens his vault in the morning. External signal (releases, news, competitor moves) for each project is even more scattered: it lives in feeds and sites he'd have to check project by project. + +The cost is recurring friction at the start of every work session and staleness between sessions. The vault is already the daily home (Obsidian, the Karpathy "LLM wiki" pattern librarian leans into), but it has no living front page. The pain is sharpest first thing in the day and whenever Neal context-switches between projects. + +--- + +## Actors + +- A1. Neal (operator): opens the dashboard daily, consumes status + news, configures which RSS feeds each project watches. +- A2. session-close agent: at the end of a project work session, writes/updates that project's status note in the vault. +- A3. scheduled news agent: on a cadence, fetches each project's RSS feeds and writes news notes into the vault. +- A4. librarian-mcp (data layer): provides tools to write status/news notes and to scaffold/refresh the Dataview dashboard note. Does **not** render the dashboard. +- A5. Obsidian + Dataview (render layer): renders the dashboard note from the structured status/news notes. + +--- + +## Key Flows + +- F1. Daily glance + - **Trigger:** Neal opens the dashboard note in Obsidian. + - **Actors:** A1, A5 + - **Steps:** Dataview queries the status notes → renders per-project tiles (health, next action, last-updated age) → renders recent news per project below/beside each. + - **Outcome:** Neal sees the whole portfolio's state and fresh signal at a glance, without running a command or prompting. + - **Covered by:** R7, R8, R9 + +- F2. Status update at session-close + - **Trigger:** A project work session ends and `session-close` runs. + - **Actors:** A2, A4 + - **Steps:** session-close synthesizes the project's current status (health, next action, blockers) → writes/updates that project's status note via librarian. + - **Outcome:** That project's tile reflects reality as of the just-finished session. + - **Covered by:** R3, R4 + +- F3. Scheduled news refresh + - **Trigger:** Scheduled run (e.g., daily). + - **Actors:** A3, A4 + - **Steps:** For each project with configured feeds, fetch RSS/Atom entries → dedupe against existing news notes → write new entries as news notes tagged to the project. + - **Outcome:** Each project's news is current; no duplicates accumulate. + - **Covered by:** R5, R6, R11 + +- F4. Configure a project's feeds + - **Trigger:** Neal adds a project or wants to watch new sources. + - **Actors:** A1, A4 + - **Steps:** Neal records the project's RSS feed URLs in the project's status note (or registry) → next scheduled run picks them up. + - **Outcome:** The project participates in the news pipeline. + - **Covered by:** R2, R5 + +--- + +## Requirements + +**Data model** +- R1. Each project has a single status note carrying at least: project name, health/status, next action, blockers, last-updated timestamp, link to the repo, and the project's RSS feed list. +- R2. The set of projects the dashboard covers is exactly the set of status notes present in the vault — no separate registry note. An `active` frontmatter flag hides a project without deleting its note; `group` and `order` fields control sectioning and sort. +- R11. Each news item is a note carrying at least: owning project, title, source, publish date, and URL — enough for Dataview to group, sort, and link it. + +**Status pipeline (session-driven)** +- R3. `session-close` writes/updates the active project's status note as part of its existing reconcile step. +- R4. A status update overwrites the prior status for that project (latest-wins) and refreshes the last-updated timestamp. + +**News pipeline (scheduled RSS)** +- R5. A scheduled agent fetches each project's configured RSS/Atom feeds and writes new entries as news notes. +- R6. News ingestion is idempotent: an entry already present (by URL) is not duplicated on re-runs. +- R12. A project with no configured feeds is handled gracefully — it simply shows no news, never an error. + +**Dashboard rendering (Dataview, in Obsidian)** +- R7. A dashboard note renders one tile/row per project showing health/status, next action, and last-updated age, using Dataview/dataviewjs + CSS. +- R8. Each project surfaces its most recent N news items inline, linked to source. +- R9. The dashboard visibly flags stale status — when a project's last-updated age exceeds the staleness threshold (default 30 days), the tile signals it (e.g., a "stale" marker), so old status is never read as current. The threshold is one vault-wide setting, not per-project. +- R10. The dashboard requires no manual rebuild to reflect new status/news notes — opening/refreshing the note re-runs the queries. + +**librarian-mcp tooling** +- R13. librarian provides a tool to scaffold the dashboard note and the status/news note structure for a vault (so the feature can be stood up and re-applied generically on any vault). +- R14. librarian's status/news note writes go through its normal write path (auto-link, cache update) and respect `.librarianisolate` / `.librarianignore`. +- R15. Tunable settings (staleness threshold default 30 days, recent-news count N, news cadence) live in one wide vault-level librarian config, consistent with the existing `.librarian*` config pattern — not scattered per-project. + +--- + +## Acceptance Examples + +- AE1. **Covers R9.** Given a project whose status note was last updated 40 days ago and the default 30-day staleness threshold, when Neal opens the dashboard, that project's tile shows a stale marker alongside its (old) status. +- AE2. **Covers R6.** Given a news note already exists for a feed entry URL, when the scheduled agent re-runs, no second note is created for that URL. +- AE3. **Covers R12.** Given a project with zero configured feeds, when the dashboard renders, that project shows its status tile with an empty (not errored) news section. +- AE4. **Covers R3, R4.** Given Neal finishes a session on `librarian-mcp`, when `session-close` runs, the librarian-mcp status note reflects the new next-action and a fresh timestamp, replacing the prior one. + +--- + +## Success Criteria + +- Neal stops asking "what's next" / running `/portfolio-resume` to start the day — he opens the dashboard instead. +- On open, the dashboard reflects reality: recently-worked projects show current status; untouched projects show clearly-flagged stale status; news is as fresh as the last scheduled run. +- A downstream planner (`ce-plan`) can implement without inventing the data shapes, the pipeline split, or the staleness behavior — they're specified here. +- The scaffold tool stands the dashboard up on a fresh vault without manual note authoring. + +--- + +## Scope Boundaries + +- **Browser / iframe HTML dashboard** — the rich standalone `.html` (à la `GRAPH_VIZ.html`) and embedding it in Obsidian were considered and rejected in favor of native Dataview. Kept as a documented escape hatch (see Key Decisions), not built. +- **Non-RSS news sources** — agent-reach social/web queries and reusing `/research-intake` output were considered and set aside; news is RSS/Atom only for v1. +- **Scheduled status scraping** — status is session-driven only. No agent sweeps repos on a schedule to refresh status; untouched projects rely on the staleness flag instead. +- **Multi-user / sharing** — single operator (Neal), single vault. No collaboration, permissions, or hosting. +- **librarian rendering the dashboard** — librarian maintains data; Dataview renders. librarian does not generate dashboard HTML for this feature. + +--- + +## Key Decisions + +- Native Obsidian (Dataview) over browser HTML: keeps the dashboard inside the daily tool with zero context switch; accepts a visual ceiling (badges/tables/callout-cards, not arbitrary tiles) as the cost. The HTML path remains the escape hatch if that ceiling later disappoints. +- librarian as data layer, Dataview as render layer: librarian writes structured notes; Obsidian draws them. Plays to librarian's strengths and sidesteps Obsidian's HTML/JS sanitization. +- Hybrid freshness: status from `session-close` (accurate — the agent was just in the repo), news from a scheduled job. Splits the freshness problem by data type. +- RSS/Atom as the news source: stable, low-noise, matches the "feed" mental model; per-project feed URLs are operator-configured. +- Staleness is surfaced, not hidden: because status is session-driven, the dashboard must make age visible so stale state is never mistaken for current. +- Staleness threshold = 30 days, global, set in one wide vault-level config (not per-project): a single knob to tune, consistent with the `.librarian*` config pattern, avoiding per-project config sprawl. +- Project registry = presence of a status note (no separate roster): self-maintaining; `group`/`order`/`active` frontmatter provides grouping, sort, and hide — ~95% of a dedicated roster's control with none of the two-place sync cost. + +--- + +## Dependencies / Assumptions + +- Obsidian **Dataview** plugin is installed and enabled in the vault (hard dependency for rendering). +- An RSS fetch capability is available to the scheduled agent (e.g., the agent-reach RSS channel) — *assumed available, verify during planning.* +- A scheduling mechanism exists to run the news agent on a cadence (e.g., the `/schedule` skill). +- Single vault, single operator (Neal); project status is meaningful to maintain in the vault. +- The richness the operator wants is achievable within Dataview/dataviewjs + CSS — *assumption flagged in Call outs; if false, revisit the HTML escape hatch.* + +--- + +## Outstanding Questions + +### Resolve Before Planning + +- None — the two open product decisions (staleness threshold; registry mechanism) were resolved during the brainstorm and recorded in Key Decisions (staleness = 30 days global via wide config; registry = status-note presence). + +### Deferred to Planning + +- [Affects R15][Technical] Exact wide-config format and whether it consolidates the existing `.librarian*` dotfiles or adds a new one. +- [Affects R1, R11][Technical] Exact frontmatter schema and vault folder layout for status and news notes. +- [Affects R7, R8][Technical] Dataview vs dataviewjs for the tiles, and the CSS-snippet approach for status badges/cards. +- [Affects R6][Technical] Dedup mechanism and where the "seen URLs" state lives. +- [Affects R5][Needs research] Whether the agent-reach RSS channel covers the needed feed formats, or a dedicated fetch step is required. + +--- + +## Adjacent feature candidates (broader "what else?" ask — not specced here) + +A menu surfaced by the original prompt, for separate brainstorms/prioritization. Each leans into the Karpathy LLM-wiki positioning: + +- **Vault-as-RAG context injection** — on `library_read`, auto-append the most relevant related notes (using the now-strong search + traversal) so any note read pulls its neighborhood. Extends the retrieval work just shipped. +- **Scheduled vault maintenance** — run `library_optimize` (dry-run) / `library_connect` / `library_eval` on a cadence and write a health note; keeps the graph optimized without manual runs. +- **"Ask the vault" Q&A tool** — retrieve + synthesize an answer with note citations (the read side of brain-RAG), surfacing the vault's knowledge conversationally. +- **Tag taxonomy enforcement** — the self-learn gap report flagged ~0% tag usage; a tool to propose/apply a small controlled tag vocabulary would unlock cross-folder slicing (and better Dataview queries for the dashboard). +- **Daily/weekly digest note** — auto-generate a rollup of what changed across the vault (new notes, status changes, news) — a temporal companion to the spatial dashboard. +- **Proactive link suggestions** — surface high-confidence unlinked-mention candidates as a review queue, instead of only on demand via `connect`. diff --git a/docs/brainstorms/2026-05-28-on-demand-synthesis-pipeline-requirements.md b/docs/brainstorms/2026-05-28-on-demand-synthesis-pipeline-requirements.md new file mode 100644 index 0000000..c9e5047 --- /dev/null +++ b/docs/brainstorms/2026-05-28-on-demand-synthesis-pipeline-requirements.md @@ -0,0 +1,156 @@ +--- +date: 2026-05-28 +topic: on-demand-synthesis-pipeline +--- + +# On-Demand Synthesis Pipeline + +## Summary + +An on-demand synthesis pipeline for the vault. librarian-mcp keeps the *structure* excellent (the 0.1.2 graph/search/maintenance work) and adds cheap re-synthesis **primitives**; an agent-orchestrated skill re-synthesizes on a schedule (and on demand), writing **daily and weekly briefings** as additive notes — surfacing patterns, contradictions, open questions, and the single most-surprising connection. The latest synthesis is cached into a fenced block/note purely so visual surfaces render without re-running an LLM. The MVP is the briefing loop; the spatial dashboard and an "ask the vault" Q&A surface are later surfaces of the same pipeline. + +--- + +## Problem Frame + +The vault is the daily home (the Karpathy "LLM Wiki" / "Claude + Obsidian" pattern), but it behaves like a filing cabinet, not a thinking system: you can retrieve what you saved, but opening it doesn't hand you back a synthesis, a contradiction, or a connection you wouldn't have found yourself. Re-establishing "what's going on across everything" is an active pull (`/portfolio-resume`, "what's next") and external/internal signal stays scattered. + +The tempting fix — *maintain* an evolving synthesis layer that integrates every new note — was considered and rejected: it accrues staleness and maintenance debt, mutates content, and fights the direction models are moving (longer context, native memory, cheaper inference). The durable shape is the opposite: keep the **memory** (structure) excellent and cheap to maintain, and **re-synthesize on demand** — letting synthesis quality ride model improvements instead of decaying. The pain is sharpest first thing each day and whenever signal needs connecting across projects. + +--- + +## Actors + +- A1. Neal (operator): reads the daily/weekly briefings; occasionally triggers an on-demand synthesis; keeps capturing notes (upstream). +- A2. Synthesis skill (agent, scheduled + on-demand): reads a recent window of the vault via librarian primitives, synthesizes, writes the briefing note, refreshes the cache. +- A3. librarian-mcp (structure + primitives): maintains the graph/search/links/MOCs and exposes cheap re-synthesis primitives (recent-change, cluster export, contradiction candidates). Holds no LLM. +- A4. Obsidian + Dataview (render layer): renders cached synthesis / briefing notes cheaply, no LLM call on open. + +--- + +## Key Flows + +- F1. Daily briefing + - **Trigger:** Scheduled run (e.g., morning). + - **Actors:** A2, A3 + - **Steps:** Skill asks librarian "what changed in the last ~7 days" + pulls relevant clusters → re-synthesizes → writes an additive daily briefing note (patterns, contradictions, open questions, the one most-surprising connection) → refreshes the render cache. + - **Outcome:** A current briefing exists without Neal asking; the cache is fresh for any surface. + - **Covered by:** R3, R4, R5, R7 + +- F2. Weekly briefing + - **Trigger:** Scheduled weekly run. + - **Actors:** A2, A3 + - **Steps:** Deeper sweep over ~30 days; surfaces only connections Neal would *not* have found by deliberate search; writes an additive weekly briefing note. + - **Outcome:** A periodic deeper synthesis that prioritizes non-obvious cross-topic links. + - **Covered by:** R3, R6, R7 + +- F3. On-demand re-synthesis + - **Trigger:** Neal asks for a synthesis now (a topic, a question, "what connects X and Y"). + - **Actors:** A1, A2, A3 + - **Steps:** Skill re-synthesizes live from the current structural vault → returns/writes the result. + - **Outcome:** Fresh synthesis on request, no reliance on a maintained layer. + - **Covered by:** R3, R8 + +- F4. Surface render from cache + - **Trigger:** Neal opens a visual surface (briefing note, later the dashboard). + - **Actors:** A1, A4 + - **Steps:** The surface renders the latest cached synthesis / briefing notes via Dataview — no LLM call. + - **Outcome:** Instant render; the cache reflects the last scheduled/on-demand run. + - **Covered by:** R9, R10 + +--- + +## Requirements + +**Re-synthesis primitives (librarian-mcp)** +- R1. librarian exposes a "what changed since " primitive (new/modified notes in a window) so the skill can scope a briefing without re-reading the whole vault. +- R2. librarian exposes a cluster/neighbourhood export primitive (a community's or a topic's notes) so the skill can synthesize over a coherent slice cheaply. (Extends existing graph/cluster capability.) +- R3. Re-synthesis primitives respect `.librarianisolate` / `.librarianignore` (isolated folders never bleed into a cross-topic synthesis). +- R11. librarian exposes a "candidate contradictions" primitive — pairs/sets of notes that plausibly conflict — as input the skill can vet (it surfaces candidates; the agent judges). + +**Briefing loop (agent skill)** +- R4. A scheduled skill produces a **daily** briefing over a recent window (default ~7 days): patterns, contradictions, open questions, and the single most-surprising connection. +- R6. A scheduled skill produces a **weekly** briefing over a deeper window (default ~30 days) that prioritizes connections not findable by deliberate search. +- R8. The same synthesis is invokable **on demand** for an ad-hoc topic/question, not only on schedule. +- R5. Synthesis is always **re-computed** from the current structural vault on each run — there is no persisted synthesis layer that is incrementally maintained. + +**Output contract** +- R7. Briefings are written as **additive** notes; the pipeline never edits the prose of existing notes. +- R12. Briefing notes are written through librarian's normal path (auto-link, cache update) so they join the graph and become corpus for future synthesis — this is what "updates the knowledge system" means here. +- R9. The latest synthesis is written into a **fenced cache** (a managed block / cache note) solely so visual surfaces can render without an LLM call; the cache is overwritten each run and is never treated as authoritative truth. + +**Surfaces & config** +- R10. Visual surfaces (the briefing note today; the dashboard later) render from cached synthesis + briefing notes via Dataview, with no LLM call on open. +- R13. Cadence and window sizes (daily window, weekly window, schedule times) are set in the wide vault-level librarian config (consistent with the `.librarian*` pattern), not hardcoded. +- R14. The spatial dashboard (see `docs/brainstorms/2026-05-27-obsidian-project-command-center-requirements.md`) is a later *surface* of this pipeline, reading the same structural vault + cache — not a separate system. + +--- + +## Acceptance Examples + +- AE1. **Covers R4, R7.** Given three new notes captured in the last 7 days, when the daily run fires, a new dated briefing note is created (existing notes untouched) summarizing patterns and naming one surprising connection among them. +- AE2. **Covers R5, R9.** Given the daily run ran this morning, when it runs again tomorrow, the briefing is re-synthesized from scratch over the new window and the cache is overwritten — no merge with yesterday's synthesis. +- AE3. **Covers R10.** Given Neal opens the briefing surface between scheduled runs, it renders the last cached synthesis instantly with no LLM call (and is therefore as fresh as the last run, not live). +- AE4. **Covers R3, R6.** Given the vault has an isolated folder (e.g. the Threshold book), when the weekly briefing runs, no connection crosses that folder's boundary. +- AE5. **Covers R11.** Given two notes that assert opposing claims on the same topic, when synthesis runs, the contradiction is surfaced in the briefing's contradictions section (the primitive flags the candidate; the agent confirms it). + +--- + +## Success Criteria + +- Neal reads a briefing instead of running `/portfolio-resume` or asking "what's next"; the daily/weekly briefings tell him something he didn't already know often enough to be worth opening. +- The "filing cabinet vs thinking system" test passes: asking the vault (via the briefing or on-demand) returns a synthesis/connection, not a folder of notes. +- No maintenance debt accrues: there is no synthesis layer to go stale, and a missed run costs nothing but a stale cache until the next run. +- A downstream planner can build without inventing the pipeline shape, the on-demand-vs-maintained stance, the output contract, or where synthesis vs structure lives. + +--- + +## Scope Boundaries + +### Deferred for later + +- The spatial **project dashboard** — becomes a later surface of this pipeline (its requirements are captured separately in the 2026-05-27 command-center doc). +- An **on-demand "ask the vault" Q&A** surface (cited answers to arbitrary questions) — the read side beyond scheduled briefings. +- News/RSS ingestion as a synthesis input — folded in once the briefing loop exists (it lives in the dashboard doc today). + +### Outside this product's identity + +- **Maintained / in-place evolving synthesis** (the LLM-Wiki "integrate every note into persistent summary pages" model) — explicitly rejected on durability grounds; the pipeline re-synthesizes instead. +- The **capture layer** (Telegram bot, ingestion, zero-friction inflow) — upstream of librarian; handled by research-intake / agent-reach, not this pipeline. +- librarian-mcp **performing synthesis itself** — the Rust server holds no LLM; synthesis is the agent skill's job. librarian provides structure + primitives only. + +--- + +## Key Decisions + +- Re-synthesis on demand over a maintained synthesis layer: avoids staleness and maintenance debt, mutates nothing, and rides model improvements (longer context, native memory, cheaper inference) instead of fighting them. This is the durability stance — settled after explicitly weighing the maintained-synthesis alternative. +- Memory vs intelligence kept separate: librarian = structural memory + cheap re-synthesis primitives; the agent skill = intelligence. The MCP server cannot and should not call an LLM. +- Additive output + render cache: briefings are new notes (never edit prose); the only persisted synthesis is a disposable cache so Dataview surfaces render without an LLM. "Updates the knowledge system" = briefings join the corpus and graph, not a maintained layer. +- Lean on the graph, not folder reorg: cross-topic connection quality comes from librarian's graph (which already bridges topic-based folders), so there is no need to re-file the vault into ZEUS-style type-based folders. The 0.1.2 graph-quality work is the enabler. +- MVP = the daily/weekly briefing loop; dashboard and ask-the-vault are later surfaces of the same engine. + +--- + +## Dependencies / Assumptions + +- A scheduling mechanism runs the skill daily/weekly (e.g. the `/schedule` skill) — *assumed available.* +- Obsidian **Dataview** renders the cache/briefings cheaply (shared with the dashboard track). +- The 0.1.2 structural graph is good enough that on-demand synthesis surfaces genuinely useful connections — *the eval metrics (recall, ranked precision) are the proxy; revisit if briefings feel generic.* +- Per-run LLM cost (daily + weekly re-synthesis) is acceptable at single-operator scale — *a recurring compute cost, not free, but small.* +- Single vault, single operator (Neal). + +--- + +## Outstanding Questions + +### Resolve Before Planning + +- None — the load-bearing product decisions (on-demand vs maintained, output contract, MVP slice, structure-vs-synthesis split) were resolved during the brainstorm and recorded in Key Decisions. + +### Deferred to Planning + +- [Affects R1, R2, R11][Technical] Which re-synthesis primitives are worth building as Rust tools vs. composed by the skill from existing tools (search/traverse/cluster/stats). +- [Affects R11][Needs research] How to generate good *candidate* contradictions cheaply (e.g., opposing-claim heuristics over same-cluster notes) without an LLM in the server. +- [Affects R9][Technical] Cache representation — a fenced block in a dashboard note vs a dedicated cache note — and how Dataview reads it. +- [Affects R4, R6][Technical] Briefing note layout/location and how the "most-surprising connection" is selected and de-duplicated across runs. +- [Affects R13][Technical] Exact wide-config keys for cadence/windows (shares the config mechanism deferred in the dashboard doc). diff --git a/docs/brainstorms/2026-05-30-judgment-layer-project-decision-state-pages-requirements.md b/docs/brainstorms/2026-05-30-judgment-layer-project-decision-state-pages-requirements.md new file mode 100644 index 0000000..30d1631 --- /dev/null +++ b/docs/brainstorms/2026-05-30-judgment-layer-project-decision-state-pages-requirements.md @@ -0,0 +1,171 @@ +--- +date: 2026-05-30 +topic: judgment-layer-project-decision-state-pages +--- + +# Judgment Layer — Project Decision & State Pages + +## Summary + +A generic, open-source feature: one **decision & state page per project** in the vault, structured as an **outcome-anchored experiment log**. Each project declares one or two anchor metrics (Sharpe, CTR, conversion, retention, etc.); the page records what changed, the observed metric delta, ratified decisions, and a ranked list of **agent-proposed candidate next moves** drawn from the vault's accumulated research. Runs on-demand via a `/librarian` skill subcommand; additive-only; never mutates the operator's prose. Voltron is the pilot validation; every aspect of the design is general-purpose so the feature works for any operator on any vault. + +--- + +## Problem Frame + +The vault holds the research, but the steering decisions for each project live elsewhere — in plans, ADRs, session memory, and the operator's head. So each new session re-derives "what did we decide, what's working, what should we try next" instead of compounding judgment from the accumulated record. The pain is sharpest in projects where the operator has limited domain experience: Voltron has 30+ trading research notes (Markov, RBI, Kelly sizing, gap-and-go, multi-timeframe, etc.), but with no falsifiable record of *what was tried, what moved Sharpe, and what's most likely to move it next*, the operator is steering by guess. + +The strategy reset (`STRATEGY.md`) names this as the brain's reason to exist — decision reduction. But "reduction" alone (retention) doesn't address operators with thin domain experience. **An outcome-anchored experiment log + agent-proposed next moves does both**: it stops re-derivation *and* compensates for missing instinct by grounding proposals in the accumulated research and the project's own outcome history. + +--- + +## Actors + +- A1. **Operator** (generic; Neal is the pilot) — declares each project's anchor outcome(s); logs experiment outcomes (the metric delta); ratifies decisions; accepts, rejects, or edits agent-proposed next moves. +- A2. **Agent skill** (`/librarian` subcommand) — reads the project's decision & state page plus the scoped research corpus; proposes ranked next moves with cited evidence; writes proposals as fenced managed blocks; never mutates operator prose. +- A3. **librarian-mcp** — provides structure and primitives (read/write notes with auto-link + isolation, scope a project's research via folder/community/tag, list recent changes). No LLM; no synthesis itself. +- A4. **External source-of-truth for outcomes** — wherever the metric actually lives (backtest, A/B platform, analytics, spreadsheet). **Not integrated in this MVP** — the operator brings the delta to the page. + +--- + +## Key Flows + +- F1. Initialize a project's decision & state page + - **Trigger:** Operator starts treating a project as governed by this layer. + - **Actors:** A1, A3 + - **Steps:** Operator creates the page (or runs a `/librarian` init command), declares anchor outcome(s) in frontmatter, declares the research scope (folder / community / tag), and optionally seeds initial experiment-log entries from existing plans/PRs. + - **Outcome:** The project has a canonical page librarian and the agent can read and write to. + - **Covered by:** R1, R2, R3 + +- F2. Log an experiment outcome + - **Trigger:** Operator finishes (or revisits) an experiment / shipped change. + - **Actors:** A1 + - **Steps:** Operator appends a log entry — what changed, expected effect, observed delta on the anchor metric, link to the plan/PR/research that motivated it. Optionally marks an existing decision as ratified/killed by this outcome. + - **Outcome:** The falsifiable record grows; the page reflects what actually moved the metric. + - **Covered by:** R4, R5, R12 + +- F3. Get agent-proposed candidate next moves + - **Trigger:** Operator at a steering moment — "what should I try next?" + - **Actors:** A1, A2, A3 + - **Steps:** Operator runs the `/librarian` propose subcommand on the project. Agent reads the page (anchor, log, ratified decisions, prior proposals), pulls scoped research via librarian primitives, and writes a ranked list of candidate next moves into a fenced managed block on the page — each candidate carrying the move, expected impact on the anchor metric, and citations to research notes that motivated it. + - **Outcome:** A current, evidence-cited slate of next moves sits on the page for the operator to weigh. + - **Covered by:** R6, R7, R8, R10, R13 + +- F4. Accept / reject / edit an agent proposal + - **Trigger:** Operator reviews the proposed-next-moves block. + - **Actors:** A1 + - **Steps:** Operator picks one (or none); marks the choice on the page; when the resulting experiment runs and an outcome is logged (F2), the loop closes. + - **Outcome:** A proposal moves from candidate → ratified decision → experiment outcome → record. + - **Covered by:** R5, R9, R12 + +--- + +## Requirements + +**Page shape (generic, open-source)** +- R1. Each project gets one decision & state page identified by a `type: decision-state` frontmatter marker and a project name; the file lives in a per-vault convention (e.g., a `Projects/` folder) that the feature documents but does not enforce beyond presence of the marker. +- R2. The page's frontmatter declares one or more **anchor outcome metrics** (operator-chosen string + unit, e.g., `anchor_outcome: Sharpe` or `anchor_outcome: ["CTR", "conversion"]`), and a **research scope** (folder paths, communities, tags, or wikilinks — the operator declares what counts as the project's corpus). +- R3. The page has named, fenced sections for: **anchor outcomes** (current value, target), **experiment & outcome log** (append-only entries), **ratified decisions** (may fold into log entries — see Outstanding Questions), and **agent-proposed candidate next moves** (managed by the agent in a fenced block). + +**Operator authoring** +- R4. The operator can append a new experiment-log entry (what changed, expected effect, observed delta on the anchor metric, link to source plan/PR/research). Entries are append-only; existing entries are not silently rewritten. +- R5. The operator can mark an experiment outcome as **ratifying** or **killing** a decision; ratified decisions are surfaced as a derived view (or, if `Ratified decisions` is kept as a standalone section, written there additively). +- R12. The operator brings the metric delta from the external source-of-truth (backtest, A/B platform, etc.); the feature never assumes it can read the metric directly. + +**Agent steering (`/librarian` subcommand)** +- R6. A `/librarian` skill subcommand (working name: `propose`) reads the project's decision & state page plus the declared research scope and writes a ranked list of candidate next moves into a fenced managed block on the page. +- R7. Each candidate carries: the proposed move (action), expected effect on the anchor metric (qualitative or quantitative), and **citations** to specific research notes that motivated it. +- R8. Candidates are ranked by expected anchor-metric impact, drawing on prior experiment outcomes (what's worked) and accumulated research (what's been argued). +- R10. The agent re-running `propose` overwrites only the agent-managed block on the page; the experiment log, ratified decisions, anchor outcomes, and any operator-authored prose are untouched. +- R13. The agent never cites research notes from a folder listed in `.librarianisolate` — isolation is honored end-to-end. + +**Project research scoping (librarian primitives)** +- R9. The agent uses librarian primitives to gather the project's research corpus from the scope declared in frontmatter (folder, community, tag, or explicit wikilinks). The exact primitive composition is a planning concern; the brainstorm decision is that scope is **operator-declared** in the page, not auto-inferred. + +**Generic / open-source contract** +- R11. The feature works on any vault: no Neal-specific paths, project names, metric names, or folder conventions are baked in. All vault-specific values come from the page's frontmatter or `.librarian*` config. +- R14. The feature degrades gracefully: a project page with no research scope still produces a (degraded) experiment log and accepts manual decisions; a project page with no logged outcomes still produces (lower-confidence) proposals from research alone. + +--- + +## Acceptance Examples + +- AE1. **Covers R6, R7, R8.** Given the Voltron page declares `anchor_outcome: Sharpe` and research scope `community: QuantFlow`, when the operator runs the propose subcommand, the page receives a fenced block listing 3–5 ranked candidate moves, each citing 1+ research notes from the QuantFlow community and naming an expected effect on Sharpe. +- AE2. **Covers R4.** Given the operator appends a new experiment-log entry recording "Sharpe 1.24 → 1.41 after adding regime-slope gate, link to plan", when the page is reopened, the prior entries are unchanged and the new entry is the most recent. +- AE3. **Covers R10.** Given the propose block exists from a prior run, when propose runs again, only the propose block is overwritten — the experiment log, anchor outcomes, ratified decisions, and any operator prose remain byte-identical. +- AE4. **Covers R13.** Given `.librarianisolate` lists `Threshold`, when propose runs on a project whose research scope includes the wider vault, no candidate cites any note inside `Threshold/`. +- AE5. **Covers R12, R14.** Given a project page declares an anchor outcome but has no logged experiment outcomes yet, when propose runs, the agent produces candidates citing research only (no outcome history), and the page notes the lower confidence. +- AE6. **Covers R5.** Given an experiment outcome that confirms a hypothesis, when the operator marks the corresponding decision as ratified, that decision is visible as ratified on the next page read without rewriting prior log entries. + +--- + +## Success Criteria + +- The **Voltron pilot** is meaningful: opening Voltron's decision & state page before a steering session shows the project's anchor (Sharpe), the experiments tried with their deltas, the ratified decisions to date, and a ranked, cited candidate-next-moves block — and the operator can act from the page without re-reading the broader trading research corpus. +- **Decision-reduction** is observable: in a follow-up session, the operator does not re-derive past steering decisions for Voltron — they read them off the page. +- **Decision-quality** is observable: the operator can articulate *why* a candidate was chosen by pointing to research citations, not domain instinct they don't have. +- The feature is **generic**: a second pilot on a different project type (SignUpSpark CTR, or another) works with no code change — only different frontmatter (anchor + scope). +- A downstream planner can implement without inventing the page shape, the propose contract, the additive/managed-block boundary, or the operator-declared-research-scope mechanism. + +--- + +## Scope Boundaries + +- **Entity pages** (people, recurring concepts) — same Track 1 family but not in this MVP; a future widening once project pages are working. +- **Automated ingestion from repos** (plans, ADRs, brainstorm outcomes, `PROJECT_STATUS.md`) — Track 2 (Corpus acquisition). MVP is operator-seeded: the operator pastes/links existing plan content if useful, but no automated repo→vault sync. +- **Awareness surfaces** — daily/weekly briefings, the spatial dashboard, on-demand "ask the vault" — different track (Awareness); not built here. +- **Reading external metric sources directly** — backtests, A/B platforms, analytics. The operator brings the delta; metric integrations are out of scope for v1. +- **Auto-scheduled propose runs** — the propose subcommand is on-demand only. No daemon, no cron. Avoids the maintenance-debt failure mode. +- **Cross-project synthesis** — the agent considers one project's scope at a time; portfolio-level synthesis is a future feature. +- **Mutation of operator prose anywhere on the page** — the agent only writes inside its fenced managed block. + +--- + +## Key Decisions + +- **Outcome-anchored, not decision-anchored:** every log entry is evaluated against a declared anchor metric (Sharpe, CTR, conversion, retention…). Makes the record falsifiable and lets accumulated outcomes compound judgment — directly addresses the "thin domain experience" pain that retention alone doesn't. +- **Retention + agent-proposed steering** is the MVP scope, not retention-only. This widens Track 1 beyond `STRATEGY.md`'s current wording (which still says retention) — strategy needs a small update on its next pass. +- **On-demand `/librarian` subcommand, operator-driven** — agent does not auto-maintain the page; the operator runs propose at a steering moment. Avoids maintenance debt; respects the additive contract. +- **Additive + fenced managed blocks** (the `## Related (auto)` pattern, generalized) — agent only writes within its block; operator prose is never touched. This is the durability story: stale agent content is always re-derivable, never blocking. +- **Operator-declared research scope** in frontmatter (folder/community/tag/wikilinks), not auto-inferred — keeps the feature generic and predictable; the agent never has to guess what "Voltron research" means. +- **Operator brings the metric delta** — the feature is generic precisely because it doesn't integrate with any specific metric source. The trade-off is operator discipline: if deltas aren't logged, the log decays back into narrative. +- **Voltron is the pilot, not the design target** — every requirement is generic; Voltron just stress-tests them. + +--- + +## Dependencies / Assumptions + +- The 0.1.2 librarian-mcp substrate (graph, search, write with auto-link + isolation) is the foundation; assumed live. +- The agent (Claude in a `/librarian` skill subcommand) handles the LLM work — proposing, citing, ranking. The Rust MCP server holds no LLM and does not perform synthesis itself. +- A scoping primitive exists or can be cheaply composed to gather a project's research corpus from frontmatter declaration (folder path, community, tag, or explicit wikilinks) — verify against `library_cluster` / `library_search` / `library_traverse` in planning. +- Anchor metric source-of-truth (backtest, analytics, etc.) is operator-managed; no external integrations in this MVP. +- Single operator per vault (matches `STRATEGY.md`'s Who section); multi-operator concurrency on the same page is not a current concern. + +--- + +## Outstanding Questions + +### Resolve Before Planning + +- None — the load-bearing product decisions (scope = retention + steering, outcome-anchored shape, on-demand subcommand, additive + managed blocks, operator-declared research scope, operator-brings-deltas, generic across vaults) were resolved during the brainstorm and recorded in Key Decisions. + +### Deferred to Planning + +- [Affects R3, R5][Soft scope] Whether **Ratified decisions** is a standalone section on the page or a derived view of the experiment log (each log entry that ratifies/kills a decision). Brainstorm preference: include as standalone in MVP, demote to derived after the pilot if it proves redundant. +- [Affects R9][Soft scope] Whether **Research index** is a hand-curated section on the page or auto-derived from the declared research scope. Brainstorm preference: derive from the scope (no manual section) unless the pilot shows the operator needs to override. +- [Affects R1, R3][Technical] Exact vault-folder convention for project pages (e.g., `Projects/.md`) and the frontmatter schema (field names, types). +- [Affects R9][Technical] How the agent composes "the project's research corpus" from the declared scope using existing librarian primitives (`library_cluster`, `library_search`, `library_traverse`, the pending `library_changes`). +- [Affects R6, R10][Technical] Fenced-block protocol for the agent-proposed section: marker syntax, idempotency on re-runs, preserving prior proposals as history vs overwriting. +- [Affects R8][Needs research] How the agent ranks candidates by expected anchor-metric impact when prior outcome history is sparse — agent-only judgment with cited research vs explicit confidence scoring. +- [Affects all] `STRATEGY.md` update on next pass: Track 1 widens from "retention" to "retention + agent-proposed steering" to honor the MVP scope chosen here. + +--- + +## The operator contract (worth naming explicitly) + +The feature's whole loop depends on one operator habit: **logging the outcome delta when an experiment completes**. If deltas aren't logged, the page decays into a narrative log and the falsifiability advantage evaporates. The MVP spec should: +- Surface this in the propose subcommand's output ("last delta logged: 14 days ago" as a freshness flag), +- Optionally remind on session-close when an experiment shipped without an outcome entry, +- Treat "delta-logging hygiene" as the load-bearing metric the pilot watches. + +This is the equivalent of ZEUS's "capture is the single most important piece of infrastructure" rule, applied to this layer. diff --git a/docs/plans/2026-05-28-001-feat-library-changes-primitive-plan.md b/docs/plans/2026-05-28-001-feat-library-changes-primitive-plan.md new file mode 100644 index 0000000..f9ca387 --- /dev/null +++ b/docs/plans/2026-05-28-001-feat-library-changes-primitive-plan.md @@ -0,0 +1,177 @@ +--- +title: "feat: library_changes recency primitive" +type: feat +status: active +date: 2026-05-28 +origin: docs/brainstorms/2026-05-28-on-demand-synthesis-pipeline-requirements.md +--- + +# feat: library_changes recency primitive + +## Summary + +Add one new MCP tool, `library_changes`, that returns the vault notes modified within the last N days, most-recent first. It reads the modification times librarian already tracks (`VaultCache.file_mtimes`) — no new indexing. This is the first structural primitive of the on-demand synthesis pipeline (origin: `docs/brainstorms/2026-05-28-on-demand-synthesis-pipeline-requirements.md`, requirement R1), built now because it is independently useful (recency view, "what's new since I was last here," session-resume) ahead of the deferred briefing loop that will also consume it. + +--- + +## Problem Frame + +The vault has no way to answer "what changed recently?" — every tool is point-in-time (search, read, graph) or whole-vault (stats, report). The future briefing loop needs a cheap "changed in the last N days" query to scope a synthesis without re-reading the whole vault, and the operator wants the same answer directly today. The data already exists in the cache (per-file mtime); nothing surfaces it. + +--- + +## Requirements + +- R1. Expose a primitive that lists notes changed within a recent window, scoped by N days, suitable for scoping a later synthesis pass and for direct recency queries. (origin R1) + +**Origin actors:** A2 (synthesis skill — future consumer), A3 (librarian-mcp — provider of this primitive) +**Origin flows:** F1 (daily briefing) and F2 (weekly briefing) will consume this primitive when built; not implemented here. +**Origin acceptance examples:** none of the origin AEs target this primitive directly (they target the briefing loop); this plan adds its own scenarios. + +--- + +## Scope Boundaries + +- The briefing loop / `/librarian briefing` subcommand — deferred (origin "Deferred for later"; the user chose to build the primitive ahead of its consumer). +- The spatial dashboard surface and on-demand "ask the vault" — deferred (origin). +- Cluster-content export (origin R2) — not built; `library_cluster` + `library_read` already compose it and there is no consumer yet. +- Contradiction-candidate primitive (origin R11) — not built; genuinely novel/hard, deferred until the briefing needs it. +- No "created vs modified" distinction — the cache tracks filesystem mtime only; this primitive reports *modified* within the window (see Key Technical Decisions). + +### Deferred to Follow-Up Work + +- Version bump + release (e.g., 0.1.3) and Homebrew tap update once this lands: separate release step, following the established cargo-dist + tap flow. + +--- + +## Context & Research + +### Relevant Code and Patterns + +- `src/cache.rs` — `VaultCache.file_mtimes: HashMap` already holds per-file modification times, populated in `build_full` and kept current by `check_and_refresh`. The field is currently private to the cache; this plan adds a query method rather than exposing the field. +- `src/tools.rs` — tool registration pattern: a param struct deriving `Serialize/Deserialize/JsonSchema`, plus an `async fn` decorated with `#[tool(description = …)]` inside the `#[tool_router] impl LibraryServer` block. `library_daily` (writes `Journal/YYYY/…`) and `library_stats` are the closest existing handlers to mirror for "read cache → format JSON result." +- `src/tools.rs` — every tool handler calls `cache.check_and_refresh(self)` first so the result reflects current disk state; `self.relative_path(&abs)` converts absolute paths to vault-relative for output. +- `all_md_files()` / the cache already exclude `.librarianignore`d files, so a changes list inherits that exclusion for free. +- `src/tools.rs` tests module — existing unit tests (`advertises_tools_capability`, `auto_link_respects_stoplist`) construct a `LibraryServer` with empty vaults + `VaultCache::default()`; mirror this for the new test. + +### Institutional Learnings + +- This session's 0.1.x work established that new tools must be advertised (the `get_info` capability fix) — adding a tool needs no capability change (the router advertises it), but the regression test `advertises_tools_capability` should still pass. +- `.librarianisolate` governs *link-crossing*, not *visibility* — a recency listing intentionally does not apply isolation (it lists changed files regardless of folder; it creates no links). + +### External References + +- None — fully covered by local patterns. + +--- + +## Key Technical Decisions + +- Add a query method on `VaultCache` (e.g., `changed_within(cutoff: SystemTime) -> Vec<(PathBuf, SystemTime)>`) rather than making `file_mtimes` public: keeps the cache's internal representation encapsulated and gives the tool a testable, pure surface. +- Window expressed in **days** (`days`, default 7): matches the brainstorm's daily(~7)/weekly(~30) windows and is the simplest operator-facing knob. An absolute-date variant is unnecessary for the current consumers. +- Report **modified** within the window, not "created": the cache only has filesystem mtime. Document this in the tool description so callers don't assume creation semantics. +- Result shape: vault-relative `path` + a recency indicator (age in days or ISO mtime), sorted most-recent-first, optionally capped by `limit`. Exact field names are an implementation detail (see Deferred to Implementation). +- Listing ignores `.librarianisolate` (visibility ≠ link-crossing) but honors `.librarianignore` (inherited from the cache/file walk). + +--- + +## Open Questions + +### Resolved During Planning + +- Subcommand vs tool: this is a librarian-mcp **tool** (Rust), not a skill verb — it's a structural primitive with no LLM involvement. +- Build ahead of consumer? Yes — `library_changes` is independently useful, so it is not speculative despite the briefing being deferred. + +### Deferred to Implementation + +- Exact output field names and recency representation (age-in-days vs ISO timestamp vs both). +- Whether to add an optional `directory` filter and/or `limit` param now or leave the result uncapped — decide when wiring the handler; default to a `limit` with a sensible cap if result sizes prove large. + +--- + +## Implementation Units + +### U1. `library_changes` tool + cache query method + +**Goal:** Add a `changed_within` query method to `VaultCache` and a `library_changes` MCP tool that returns notes modified within the last N days, most-recent-first. + +**Requirements:** R1 + +**Dependencies:** None + +**Files:** +- Modify: `src/cache.rs` (add `changed_within` method over `file_mtimes`) +- Modify: `src/tools.rs` (add `ChangesParams` struct + `library_changes` handler in the `#[tool_router]` impl) +- Test: `src/cache.rs` (unit test for `changed_within`) and/or `src/tools.rs` tests module + +**Approach:** +- `changed_within(cutoff)` filters `file_mtimes` to entries with `mtime >= cutoff`, returns them sorted by mtime descending. +- The tool: `check_and_refresh` → compute `cutoff = now - days` → call `changed_within` → map each absolute path to `self.relative_path(...)` → serialize JSON list. Default `days = 7` when omitted. +- Keep the tool description explicit that it reports *modified* (mtime) within the window and is bounded by `.librarianignore`. + +**Patterns to follow:** +- Param struct + `#[tool(description=…)]` handler shape from existing tools in `src/tools.rs` (`library_stats`, `library_daily`). +- `check_and_refresh` + `relative_path` usage from any existing read tool. + +**Test scenarios:** +- Happy path: given files with synthetic mtimes 2 and 10 days old and `days=7`, `changed_within` returns only the 2-day file. Covers R1. +- Happy path: multiple in-window files are returned sorted most-recent-first. +- Edge case: `days` omitted defaults to 7. +- Edge case: nothing modified within the window → empty list, not an error. +- Edge case: an `.librarianignore`d file is absent from results (inherited from the cache/file set). +- Regression: `advertises_tools_capability` still passes (the new tool is advertised by the router). + +**Verification:** +- `cargo test` passes including the new `changed_within` test and the existing capability/stoplist/isolation tests. +- A manual `tools/list` handshake shows `library_changes` present; a `tools/call` returns recently-modified notes for the live vault, newest first. + +--- + +### U2. Documentation: tool reference + counts + +**Goal:** Reflect the new tool in user-facing docs so counts and references stay accurate. + +**Requirements:** R1 (supporting) + +**Dependencies:** U1 + +**Files:** +- Modify: `README.md` (tool list / count) +- Modify: `markdown/CLAUDE.md` workspace doc tool list if it enumerates tools (currently says "17 tools" — already stale; update to current count) +- Modify: the `/librarian` skill `SKILL.md` Tool Reference table (add `library_changes`) — note this lives in the user's skills dir, not this repo; flag for the operator rather than editing repo-external files silently. + +**Approach:** +- Update enumerated tool counts/tables to include `library_changes` with a one-line description. + +**Test scenarios:** +- Test expectation: none — documentation only, no behavioral change. + +**Verification:** +- Tool counts in README/workspace doc match the actual advertised tool count. + +--- + +## System-Wide Impact + +- **API surface parity:** Adds one tool to the advertised MCP surface; no change to existing tools. Distributed via the Homebrew tap, so it reaches users only after a release (deferred follow-up). +- **Error propagation:** Read-only tool; on a missing/unreadable mtime, skip that file rather than failing the call. +- **State lifecycle risks:** None — read-only over existing cache state; `check_and_refresh` already handles staleness. +- **Unchanged invariants:** No change to auto-linking, isolation, write paths, or any existing tool's output. `.librarianignore` exclusion preserved; `.librarianisolate` intentionally not applied to a visibility listing. + +--- + +## Risks & Dependencies + +| Risk | Mitigation | +|------|------------| +| Building ahead of the (deferred) briefing consumer | Tool is independently useful (recency/session-resume); scope is one small read-only tool, low carrying cost | +| `file_mtimes` reflects librarian's own writes (auto-link rewrites bump mtime) | Acceptable — "changed" includes librarian-touched notes; document the mtime semantics | +| Large result sets on busy vaults | Optional `limit` param (deferred-to-implementation decision) | + +--- + +## Sources & References + +- **Origin document:** docs/brainstorms/2026-05-28-on-demand-synthesis-pipeline-requirements.md (R1; primitives section) +- Related code: `src/cache.rs` (`file_mtimes`, `check_and_refresh`), `src/tools.rs` (`library_stats`, `library_daily`, tests module) +- Companion brainstorm: docs/brainstorms/2026-05-27-obsidian-project-command-center-requirements.md (deferred dashboard surface) diff --git a/docs/plans/2026-05-30-001-feat-judgment-layer-mvp-plan.md b/docs/plans/2026-05-30-001-feat-judgment-layer-mvp-plan.md new file mode 100644 index 0000000..a971329 --- /dev/null +++ b/docs/plans/2026-05-30-001-feat-judgment-layer-mvp-plan.md @@ -0,0 +1,346 @@ +--- +title: "feat: Judgment layer MVP — project decision & state pages + propose subcommand" +type: feat +status: completed +date: 2026-05-30 +origin: docs/brainstorms/2026-05-30-judgment-layer-project-decision-state-pages-requirements.md +--- + +# feat: Judgment layer MVP — project decision & state pages + propose subcommand + +## Summary + +Ship the Judgment-layer MVP from the origin brainstorm as **skill-content + page conventions in the librarian-mcp repo**, deployed via the existing `--setup` flow. No new Rust primitives. Three new `/librarian` subcommands — `project init`, `project log`, and `propose` — let any operator declare a per-project decision & state page (with an anchor outcome and a research scope), log experiment outcomes, and request ranked, cited candidate next moves drafted into a fenced managed block on the page. Voltron is the pilot. + +--- + +## Problem Frame + +Per origin: the operator (and the agents acting for them) re-derives decisions and project state at the start of every session, and lacks decision-quality help where domain experience is thin. The Judgment layer turns the vault into a falsifiable, outcome-anchored substrate. This plan executes the *how*: a small skill surface over existing librarian primitives, with a deployable artifact in the open-source repo. + +--- + +## Requirements + +(Origin R-IDs in `docs/brainstorms/2026-05-30-judgment-layer-project-decision-state-pages-requirements.md`. The plan covers R1–R14 except where the brainstorm itself deferred work to Track 2 / later surfaces.) + +- R1. Per-project decision & state page identified by `type: decision-state` frontmatter; lives at a default repo convention (`Projects/.md`) but works wherever the marker is present. +- R2. Frontmatter declares one or more anchor outcomes and an explicit research scope (folders / communities / tags / wikilinks). No inference. +- R3. Named sections: anchor outcomes, experiment & outcome log, ratified decisions, agent-managed candidate-next-moves block. +- R4. Operator can append experiment-log entries; existing entries are append-only and never silently rewritten. +- R5. Operator can mark an outcome as ratifying / killing a decision. +- R6. A `/librarian` skill subcommand reads the page + scoped research and writes ranked candidate next moves into a fenced managed block on the page. +- R7. Each candidate carries: proposed move, expected effect on anchor, and citations to specific research notes. +- R8. Candidates ranked by expected anchor-metric impact, drawing on prior outcome history + accumulated research. +- R9. Research-corpus composition uses **existing** librarian primitives (`library_search`, `library_traverse`, `library_cluster`, `library_tags`, `library_list`, `library_read`, `library_metadata`) — no new Rust primitive in this plan (see Key Technical Decisions). +- R10. Re-running `propose` overwrites only the managed block; everything else byte-stable. +- R11. Feature is generic: no repo-, project-, metric-, or folder-specific values are baked in. All vault-specific values come from page frontmatter or `.librarian*` config. +- R12. Operator brings the metric delta — no metric-source integrations. +- R13. `.librarianisolate` honored end-to-end: agent never cites notes from isolated folders. +- R14. Degrades gracefully: page with no scope still accepts manual logs; page with no outcome history still produces (lower-confidence) proposals from research alone. + +**Origin actors:** A1 (Operator), A2 (Agent skill — `/librarian` subcommand), A3 (librarian-mcp — structure + primitives), A4 (External metric source-of-truth — not integrated). + +**Origin flows:** F1 (Initialize page), F2 (Log experiment outcome), F3 (Get proposed candidate next moves), F4 (Accept/reject/edit a proposal). + +**Origin acceptance examples:** AE1–AE6 (covered in unit test scenarios below). + +--- + +## Scope Boundaries + +- **Entity pages** (people, recurring concepts) — Track 1 extension, not in MVP. (Origin: Deferred for later.) +- **Automated repo→vault ingestion** (plans, ADRs, `PROJECT_STATUS.md`, brainstorm outcomes) — Track 2 (Corpus acquisition); MVP is operator-seeded. (Origin: Deferred for later.) +- **Awareness surfaces** — briefings, dashboard, ask-the-vault — different track. (Origin: Deferred for later.) +- **Direct metric-source integrations** (backtests, A/B platforms, analytics) — operator brings the delta. (Origin: Outside this product's identity for v1.) +- **Cross-project synthesis** — agent considers one project's scope at a time. +- **Mutation of operator prose** anywhere on the page — agent only writes inside the fenced managed block. + +### Deferred to Follow-Up Work + +- New Rust primitive for "scoped research corpus" — composes existing primitives in v1; promoted to Rust if pilot reveals performance/UX issues. +- `STRATEGY.md` update widening Track 1 wording from "retention" to "retention + steering" — documentation-only follow-up, can land alongside or after this plan. +- The `docs/brainstorms/2026-05-28-on-demand-synthesis-pipeline-requirements.md` doc still carries its old on-demand-only stance; either annotate as superseded by STRATEGY or update it. Not blocking this plan. + +--- + +## Context & Research + +### Relevant Code and Patterns + +- **Fenced managed-block upsert pattern.** `src/tools.rs::upsert_related_block` (added in v0.1.2 for `library_optimize`) is the canonical pattern: find an H2 marker (`## Related (auto)`), replace the section until the next `\n## `, idempotent on re-run. The propose subcommand mirrors this pattern but with a different section name. The skill performs the upsert from the agent side (read → string-edit → write) — no new Rust helper required. +- **Frontmatter parsing.** `src/server.rs::extract_frontmatter` returns the YAML block between the leading `---` markers. The skill reads page frontmatter via `library_read` then parses; alternatively `library_metadata` returns parsed frontmatter directly. +- **Isolation enforcement.** `src/server.rs::crosses_isolation` and `top_folder` are honored automatically when the skill uses `library_write` (isolated-folder candidates are filtered from auto-link). For the `propose` *citations*, the skill explicitly filters out any candidate research note whose path starts with an isolated folder by checking `.librarianisolate` (loaded into `LibraryServer::isolated_folders` at start; alternatively the skill reads `.librarianisolate` directly). +- **Research-scope primitives** (all shipped in v0.1.2): + - `library_search` (term-based BM25) — for tag-like or keyword scoping. + - `library_cluster` — returns community-of-stem; for `community: