A4: board MCP facet (opt-in) — board_put/query/move/close#45
Merged
Conversation
WHAT: Add board_md (card_to_markdown / card_from_markdown) and the
Store::import_dir / export_dir directory sync, so the operational SQLite
cards round-trip to/from the portable markdown+frontmatter form.
- board_md.rs: emits the canonical frontmatter field order (id, project,
created, updated, summary, size, status, recurs, expires, blocked_on,
refs, author/source) + body; parses tolerantly (free-text status,
trailing `# comments` on list items, a `blocked_by` alias for
blocked_on, refs label:url map). lane/context are directory facts, left
empty by the parser and filled by the walker.
- Store::import_dir walks <root>/[<context>/]<lane>/*.md (lanes
p0|p1|p2|done|dropped), follows symlinks (the lane-view convention) but
dedups by card_id so a source file and its lane symlink import once;
returns {added, updated, skipped}.
- Store::export_dir writes flat <context>/<lane>/<card_id>.md real files
(non-destructive; never deletes files it didn't write).
- BoardConfig gains sync_dir / default_lane / default_context (all
#[serde(default)], existing configs parse unchanged).
- New dependency: serde_yaml_ng (maintained drop-in for the deprecated
serde_yaml) — parses the frontmatter map + list cleanly.
WHY: Markdown stays the portable, sovereign artifact (no lock-in); SQLite
is the fast operational store. v1 round-trip is at the card-content level
(frontmatter + body survive SQLite -> md -> SQLite) — it does NOT
reconstruct the numbered relative-symlink lane views; that's a follow-up.
Disclosure tier: internal capability + one new dependency. No MCP surface
change, no listed step yet.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
WHAT: Add a `board` step type that reads open knowledge-board cards from
the agent state store and renders them grouped by lane — the operational
counterpart of the filesystem `board-scan` step (which stays as-is).
- steps/board::Board: pure step (no subprocess), soft-skips without a
store. Params (optional): `lane` (a single lane), `project` (filter).
With no `lane`, shows the open work lanes p0/p1/p2 (closed lanes only on
request). Flags cards carrying blocked_on refs as `[blocked]`.
- data_schema(): {lanes:[{lane, cards:[{card_id, project, summary, status,
size, blocked}]}], open} — a new versioned contract (golden pinned).
- Registered in builtin_registry; documented-types test + module table
updated. board-scan vs board distinction documented (filesystem vs store).
- data-contract harness: the `board` step joins CONTRACT_CONFIG and the
golden set; a seeded card proves executed output validates against the
schema.
WHY: The store-backed board is what makes "good morning" surface the local
knowledge board with no git or filesystem layout required — cards live in
SQLite (synced from/to markdown via A2). Preferred disclosure path per
FOUNDATION: a read-only step, zero MCP tools/list cost.
Disclosure tier: step.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
WHAT: Expose the knowledge-board cards over MCP via four tools under a new
`board` facet, mirroring the store dispatch trio:
- board_put (mutates) — create/update a card (upsert by card_id);
accepts refs {label:url} + blocked_on [url]
- board_query (read) — by lane / project / status (omit all = every card)
- board_move (mutates) — change lane (and optionally context)
- board_close (mutates) — move to done (default) / dropped, stamp closed_gen
Generation stamps use current_generation() (a counter, never a clock).
WHY / budget: the `board` facet is OPT-IN — it is NOT in DEFAULT_FACETS, so
the default tools/list stays at exactly 12 (the budget pin is unchanged).
The four tools remain reachable by default via tool_search/tool_invoke,
the `board` step, and routine_eval — at zero tools/list cost. Opting in
(`[mcp] expose=[...,"board"]` or MODULEX_TOOLS=core,store,board) lists them.
Disclosure tier: facet tool, opt-in. facets.rs needs no change (expose/deny
already accept arbitrary facet strings).
Tests: surface_policy_is_pinned extended with the four rows;
default_surface_fits_the_budget unchanged (proves board absent by default);
board_facet_is_opt_in_but_discoverable; and a server-level
board_tools_round_trip_via_invoke (put -> query -> move -> close).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #44. Completes the knowledge-board capability by exposing cards over MCP, mirroring the store dispatch trio — under a new opt-in
boardfacet.board_putcard_id); acceptsrefs {label:url}+blocked_on [url]board_querylane/project/status(omit all = every card)board_moveboard_closedone(default) /dropped, stampclosed_genGeneration stamps use
current_generation()(a counter, never a clock).Tool budget (the key constraint):
boardis opt-in — NOT inDEFAULT_FACETS, so the defaulttools/liststays at exactly 12 (budget pin unchanged). The four tools remain reachable by default viatool_search/tool_invoke, theboardstep, androutine_eval— at zerotools/listcost. Opting in ([mcp] expose=[...,"board"]orMODULEX_TOOLS=core,store,board) lists them.facets.rsneeds no change.Disclosure tier: facet tool, opt-in.
Test plan
just checkgreen (fmt + clippy -D warnings + all tests; EXIT=0).surface_policy_is_pinnedextended with the four board rows.default_surface_fits_the_budgetunchanged — proves board absent by default.board_facet_is_opt_in_but_discoverable— not listed by default, lists 4 when exposed, discoverable always.board_tools_round_trip_via_invoke— server-level put → query → move → close throughtool_invoke.🤖 Generated with Claude Code