feat(moments): actions map onto moments, with in-session corrections (milestone 458 step 2, #4920)
CI & Build / Plugin hooks (push) Successful in 18s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m23s
CI & Build / Python tests (push) Successful in 2m0s
CI & Build / Build & push image (push) Successful in 36s
CI & Build / Plugin hooks (push) Successful in 18s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m23s
CI & Build / Python tests (push) Successful in 2m0s
CI & Build / Build & push image (push) Successful in 36s
moment_actions.resolve(tool, input) names every moment a call reaches and the action that reached it. One call can reach several: kubectl apply is a run, a deliver and a reach outside the workspace. Command tools match by how each segment of the line starts, with a word boundary; other tools by field=value arguments. The MCP server prefix and case are ignored. 56 shipped defaults cover the harness tools, Scribe tools and common command shapes. moment_mappings (migration 0116) holds what an install adds and the defaults it switches off. A removal is a stored row, so an upgrade does not switch the default back on. Per the operator ruling, corrections happen in the session: map_action and unmap_action (write tools) return now_reaches so the fix can be confirmed in the same reply. list_moments now shows each moment's actions on this install. REST mirrors both doors, recorded as human. Backup v21 carries the mappings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,17 +1,24 @@
|
||||
"""The moment catalog as an MCP tool (milestone 458 step 1).
|
||||
"""Moments as MCP tools: the catalog, and the in-session corrections to it (milestone 458).
|
||||
|
||||
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.
|
||||
A session needs the vocabulary in hand to mount a rule, to read a line that
|
||||
names a moment, and to fix a misfire — and the operator's ruling is that the
|
||||
fix happens in the session, not on a settings page:
|
||||
|
||||
"making the user leave the session to fix a misfire is not desirable and
|
||||
the llm session should be able to offer corrections"
|
||||
|
||||
So the mapping writes are tools, built to be offered mid-work and made on the
|
||||
operator's yes.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import moment_actions as actions_svc
|
||||
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.
|
||||
"""The moments of work that rules mount on, and which actions reach each one here.
|
||||
|
||||
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
|
||||
@@ -20,17 +27,85 @@ async def list_moments() -> dict:
|
||||
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`.
|
||||
names a moment and you want its meaning, or when an action reached the
|
||||
wrong moment — or none — and you are about to correct it with
|
||||
`map_action` / `unmap_action`.
|
||||
|
||||
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.
|
||||
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`).
|
||||
|
||||
`actions` maps each moment to the actions that reach it on this install:
|
||||
`via: "default"` ships with the product, `via: "install"` is this
|
||||
install's own mapping. `removed_defaults` are shipped defaults this install
|
||||
switched off.
|
||||
"""
|
||||
return moments_svc.catalog()
|
||||
out = moments_svc.catalog()
|
||||
out.update(await actions_svc.actions_by_moment(current_user_id()))
|
||||
return out
|
||||
|
||||
|
||||
async def map_action(tool: str, moment: str, match: str = "", reason: str = "") -> dict:
|
||||
"""Make an action reach a moment on this install — the in-session fix for a missed moment.
|
||||
|
||||
Use it when an action plainly happened at a moment and the moment did not
|
||||
fire: the operator ships with `make ship`, and nothing mounted on
|
||||
`work.deliver` arrived. Offer the mapping when you notice, in one line
|
||||
("that `make ship` was a deliver and nothing fired — map it?"), and make it
|
||||
on their yes. The correction lasts: every later session on this install
|
||||
gets it.
|
||||
|
||||
Args:
|
||||
tool: the tool as the harness names it — `Bash`, `Edit`,
|
||||
`update_task`. An MCP server prefix is ignored.
|
||||
moment: a moment from `list_moments`, or `skill.<name>`.
|
||||
match: which calls of the tool. Empty = every call. For a tool that
|
||||
runs a command, how the command starts (`make ship`, `./deploy.sh`)
|
||||
— it is checked against each part of a compound command line. For
|
||||
any other tool, its arguments as `field=value` pairs, comma-separated
|
||||
(`status=done`).
|
||||
reason: what misfired, or what this action is for here. Optional; it
|
||||
is shown beside the mapping when someone reviews it later.
|
||||
|
||||
Returns the change made and `now_reaches`: every moment that action
|
||||
reaches after the change, so you can confirm it in the same reply.
|
||||
Mapping a shipped default this install had switched off switches it back
|
||||
on.
|
||||
"""
|
||||
return await actions_svc.map_action(
|
||||
current_user_id(), tool, match, moment, reason=reason, actor="model",
|
||||
)
|
||||
|
||||
|
||||
async def unmap_action(tool: str, moment: str, match: str = "", reason: str = "") -> dict:
|
||||
"""Stop an action reaching a moment on this install — the fix for a moment that fires wrongly.
|
||||
|
||||
Use it when a moment fires on an action that is not that moment here — a
|
||||
`curl` to a local test server reaching `env.reach`, say — and the rules it
|
||||
brings are noise every time. Offer it when you notice, and make it on the
|
||||
operator's yes.
|
||||
|
||||
The arguments name the mapping exactly as `list_moments` shows it. This
|
||||
install's own mapping is removed; a shipped default is switched off for
|
||||
this install only, and stays off across upgrades. `map_action` with the
|
||||
same arguments switches a default back on.
|
||||
|
||||
Args:
|
||||
tool: the tool as `list_moments` names it.
|
||||
moment: the moment it should stop reaching.
|
||||
match: the mapping's match, as listed (empty for a whole-tool mapping).
|
||||
reason: why it misfires here — worth giving for a default, since it is
|
||||
the record of why this install differs from the product.
|
||||
|
||||
Returns the change made and `now_reaches` for that action.
|
||||
"""
|
||||
return await actions_svc.unmap_action(
|
||||
current_user_id(), tool, match, moment, reason=reason, actor="model",
|
||||
)
|
||||
|
||||
|
||||
def register(mcp) -> None:
|
||||
mcp.tool(name="list_moments")(list_moments)
|
||||
mcp.tool(name="map_action")(map_action)
|
||||
mcp.tool(name="unmap_action")(unmap_action)
|
||||
|
||||
Reference in New Issue
Block a user