feat(moments): the moment catalog rules will mount on, readable in-session (milestone 458 step 1, #4919)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 58s
CI & Build / Python tests (push) Successful in 1m53s
CI & Build / Build & push image (push) Successful in 32s

Fourteen generic moments of work (session.start, work.start … reply.ask)
plus the skill.<name> family, each with what it means and the kinds of
action that reach it, written for any kind of work rather than software
alone. The catalog is code because every install needs the same mount
points; which actions reach a moment is per-install data (step 2).

require_moment refuses an unknown name with the catalog listed, so a
typo cannot become a mount that never fires. list_moments (read-only)
and GET /api/retrieval/moments hand out the same catalog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-05 10:50:45 -04:00
co-authored by Claude Opus 5.5
parent 0720ab6dcf
commit 2ff7f2f34f
7 changed files with 333 additions and 1 deletions
+5
View File
@@ -154,6 +154,11 @@ _READ_ONLY_TOOLS = frozenset({
# most: it is the surface the "ask before acting" reflex calls, and a key
# that could not reach it would be denied exactly the check it should run.
"what_might_apply",
# The moment catalog (milestone 458). A pure read of a constant; the
# mount and mapping writes that use it are separate tools. Spelled out for
# retrieval_telemetry's reason, and needed by a read key so that a line
# naming a moment can be understood by whoever was shown it.
"list_moments",
})
# Every tool that WRITES, by name. Nothing reads this set at runtime — a tool
+2 -1
View File
@@ -6,7 +6,7 @@ from `mcp.server.build_mcp_server`.
"""
from scribe.mcp.tools import (
design_systems, lessons, milestones, notes, processes, projects, recent, repos,
retrieval_review, retrieval_tuning,
moments, retrieval_review, retrieval_tuning,
wide_net,
rulebooks, search, shapes, snippets, systems, tags, tasks, trash,
)
@@ -18,6 +18,7 @@ def register_all(mcp) -> None:
retrieval_tuning.register(mcp)
retrieval_review.register(mcp)
wide_net.register(mcp)
moments.register(mcp)
notes.register(mcp)
tasks.register(mcp)
projects.register(mcp)
+36
View File
@@ -0,0 +1,36 @@
"""The moment catalog as an MCP tool (milestone 458 step 1).
A session needs the vocabulary in hand to mount a rule, to correct which
action reaches which moment, and to read a moment line it was shown — and it
needs it without leaving the session for a settings page. So the catalog is a
tool from the first step, ahead of the writes that will use it.
"""
from __future__ import annotations
from scribe.services import moments as moments_svc
async def list_moments() -> dict:
"""The moments of work that rules mount on — their names and what each means.
A rule mounted on a moment arrives whenever that moment happens, whatever
the words of the work look like. That is how a rule reaches you when it is
about WHEN something is done rather than WHAT it is about: a rule on
finishing work belongs at `work.finish` and `reply.report`, where nothing
said needs to resemble it.
Read this when you are about to mount a rule, when a line you were shown
names a moment and you want its meaning, or when you are correcting which
of your actions reaches which moment. Names are `<area>.<verb>`; a named
procedure (a skill or a stored process) is its own moment,
`skill.<name>`, listed under `families`.
Each moment says what is happening at it (`means`) and the kinds of action
that typically reach it (`reached_by`). The actions are examples: which of
YOUR actions reach a moment varies by install and is corrected in-session.
"""
return moments_svc.catalog()
def register(mcp) -> None:
mcp.tool(name="list_moments")(list_moments)