docs(6206): routes/plugin comments drop record and milestone citations

The pre-tool rules docstring says why action rules need their own surface
instead of narrating that they once lived in the preload; the write-path
checkpoint note states the reason the hook never blocks rather than whose
decision it was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 20:36:52 -04:00
co-authored by Claude Opus 5.5
parent 87e8f8f777
commit edf725687a
+33 -35
View File
@@ -72,14 +72,14 @@ async def session_context():
unbound repo yields a "bind this repo" hint instead. This is how the
active project is determined — the plugin never pins a project id.
project_id (optional int) — the project named directly, and the only
key a caller outside a git repo has (#4085): the plugin's hooks
key a caller outside a git repo has: the plugin's hooks
send it when a `.scribe` marker file names a project. Takes
precedence over `repo`. Access-checked like any other read — an id
this account cannot read loads no project rather than failing.
source (optional str) — the host's SessionStart source (startup,
resume, compact, clear, fork); decides what the claim section says.
session_id (optional str) — the session's id, so a claim bound to it
reads as this session's own (milestone 381).
reads as this session's own.
"""
project_id, _repo, unbound_repo = await _project_scope()
result = await plugin_ctx_svc.build_session_context(
@@ -109,7 +109,7 @@ async def autoinject_retrieve():
project_id (opt) — explicit project scope override (ad-hoc/testing).
exclude_ids (opt) — comma-separated note ids already injected this
session; skipped so each note injects at most once.
ctx (opt) — the tail of the last assistant reply (#4364). The
ctx (opt) — the tail of the last assistant reply. The
notes AND rule arms append it to a short prompt,
so a follow-up like "yes do that" still names
what it is about — and a rule or lesson arrives
@@ -119,22 +119,22 @@ async def autoinject_retrieve():
(opt) session. SHARED with /prior-art and /tool-rules on
purpose: one session keeps ONE rule ledger, so a
rule named by any arm is not re-announced by
another. Ages out (#3751), so salience decays.
another. Ages out, so salience decays.
held_rule_ids — comma-separated rule ids the session actually
(opt) OPENED, observed from the `get_rule` call itself
rather than claimed. A rule on exclude_rule_ids
was NAMED; a rule here was READ, and the two say
different things about what the reader holds —
so they get different lines (#4100). Shares the
so they get different lines. Shares the
ledger directory and the same clear-on-compact.
shapes_seen (opt) — comma-separated reply-shape keys this session was
already shown in full (milestone 500). The core
already shown in full. The core
goes out in full when `core` is absent, as its
one-line reminder when present; the hook's
ledger is swept on compaction, which is how the
full core comes back after one.
THE TURN'S REPLY SHAPE COMES FIRST (milestone 500 step 3). Every turn ends
THE TURN'S REPLY SHAPE COMES FIRST. Every turn ends
in a reply and this is the last point before it is written, so the core
shape and whatever is mounted on `reply.report` lead the payload. Fresh
shape keys come back as `shape_keys` for the ledger.
@@ -165,7 +165,7 @@ async def autoinject_retrieve():
g.user.id, q, project_id=project_id, exclude_ids=exclude_ids, context=ctx,
)
# The one place both arms' lines meet, so the one place a lesson and a
# rule arriving TOGETHER can be counted (#4637). Keyed on the operator's
# rule arriving TOGETHER can be counted. Keyed on the operator's
# prompt — the situation — and fails open to "".
proposal = await lesson_rules_svc.co_surfaced(
g.user.id, result.get("lesson_ids") or [], rules.get("shown_rule_ids") or [],
@@ -191,13 +191,13 @@ async def autoinject_retrieve():
@login_required
@memoized_query_embeddings
async def pre_tool_rules():
"""Standing rules for the plugin's PreToolUse hook on ACTIONS (#3476).
"""Standing rules for the plugin's PreToolUse hook on ACTIONS.
Answers "does a recorded rule speak to the command about to be run?" — the
sibling of /prior-art, which can only answer that question about a code
write. Rules about which tool to reach for (don't curl the forge, don't
stand up a stack, don't run the suite locally) had no retrieval surface at
all before this, which is why they all had to live in the resident preload.
stand up a stack, don't run the suite locally) have no other retrieval
surface: no code write ever happens for them to ride on.
Titles + trigger only, never the statement: the hint says a rule may apply
and hands over `get_rule(id)`. One rule at most (RULEHINT_LIMIT), and empty
@@ -219,18 +219,17 @@ async def pre_tool_rules():
held_rule_ids (opt) — rule ids the session actually OPENED, as
against merely named. Named and read are
different claims about the reader's
context, so they get different lines
(#4100).
context, so they get different lines.
root, cwd (opt) — the repo's absolute root and the command's
working directory. They turn the paths the
command names into repo-relative ones, for
the rulings arm (milestone 444).
the rulings arm.
seen_ruling_systems (opt) — comma-separated System ids whose rulings
were already shown this session, by either
arm; those get a one-line reference. SHARED
with /prior-art, like exclude_rule_ids.
Returns `context`, `rule_ids`, and `checkpoint` (#4214, milestone 419),
Returns `context`, `rule_ids`, and `checkpoint`,
plus `ruling_system_ids` — the Systems whose rulings were shown in full on
this call — when there were any.
@@ -268,7 +267,7 @@ async def pre_tool_rules():
@plugin_bp.get("/moment-tools")
@login_required
async def moment_tools():
"""The tools whose calls can deliver a mounted rule (milestone 458 step 4).
"""The tools whose calls can deliver a mounted rule.
Read by the plugin's catch-all PreToolUse hook once per session window and
kept on disk, so a call that cannot reach a mounted rule costs no request.
@@ -282,7 +281,7 @@ async def moment_tools():
@plugin_bp.post("/moment")
@login_required
async def moment():
"""The rules mounted on the moments this tool call reaches (milestone 458).
"""The rules mounted on the moments this tool call reaches.
Body: the PreToolUse event as Claude Code delivers it — `tool_name` and
`tool_input` are read, nothing else. Sent whole because which input field
@@ -298,7 +297,7 @@ async def moment():
`moments` (the names reached). A lookup, not a ranked search: no
retrieval_logs row; each fresh rule is recorded surfaced with its moment.
A moment that carries a default reply shape (milestone 500) brings it too,
A moment that carries a default reply shape brings it too,
ahead of the rules: in full unless `shapes_seen` names it, then as its
reminder. `shape_keys` are the ones sent in full, for the hook's ledger.
"""
@@ -330,7 +329,7 @@ async def moment():
@plugin_bp.post("/rule-opened")
@login_required
async def rule_opened():
"""A session opened a rule shortly after these acts (milestone 458 step 7).
"""A session opened a rule shortly after these acts.
Body: `{"rule_id", "session_id", "acts": [<PreToolUse event>, ...]}` —
the calls the plugin saw in the window before the `get_rule`. Each is
@@ -360,16 +359,15 @@ async def rule_opened():
@login_required
@memoized_query_embeddings
async def reply_rules():
"""Whether the reply that ends a turn is held for one read (milestone 458).
"""Whether the reply that ends a turn is held for one read.
Called by the plugin's Stop hook with the finished reply. Two halves: the
rules MOUNTED on the reply moments (`reply.report`, and `reply.ask` when
the reply asks something), and the reply text against every rule's
trigger — the backstop for whatever the earlier arms missed.
ONE END-OF-TURN REQUEST (milestone 500 step 4): a reply that closed tasks
is also checked here for the completion sections (services/report_check),
which used to be a Stop hook and an endpoint of its own. Both holds fold
ONE END-OF-TURN REQUEST: a reply that closed tasks is also checked here
for the completion sections (services/report_check). Both holds fold
into one `reason`; `report_check` says what the section check recorded, so
the hook knows the rewrite is one to report.
@@ -447,7 +445,7 @@ async def write_path_prior_art():
exclude_ids (opt) — comma-separated REUSE-class ids already surfaced
this session.
exclude_sync_ids (opt) — comma-separated SYNC-class ids (snippets
recorded AT the edited file, #2708) already
recorded AT the edited file) already
surfaced. A separate channel on purpose: a reuse
hint shown early must not suppress the record-sync
nudge when the recorded file is edited later.
@@ -458,31 +456,31 @@ async def write_path_prior_art():
every subsequent write.
held_rule_ids (opt) — RULE ids the session actually OPENED, as
against merely named; drives the third
reference wording (#4100).
reference wording.
exclude_derive (opt) — comma-separated derive keys (a derive group id
or `canon:<snippet_id>`) already named this
session by the ledger arm (#2900); its own
session by the ledger arm; its own
channel, like the two above.
seen_ruling_systems (opt) — System ids whose rulings were already shown
this session (milestone 444); shared with
this session; shared with
/tool-rules. The response's `ruling_system_ids`
are the ones shown in full on this call.
(Returns a `checkpoint` block on the same contract as /tool-rules —
see that endpoint. The write-path HOOK deliberately does not act on
it: scribe_prior_art.sh carries a tested property that it never
returns a permissionDecision, on the operator's decision that a recall
aid may not stand in the way of a write. The block is computed and
returns a permissionDecision, because a recall aid must not stand in
the way of a write. The block is computed and
returned so that decision can be revisited with evidence rather than
re-argued, and so a change of mind is a hook edit and not a feature.)
shapes (opt) — comma-separated `kind:name` definitions the hook
found in (or enclosing) the payload, kind being
css|sym. The shape ledger's write-path feed
(#2791): when the session recently PULLED a
css|sym. The shape ledger's write-path feed:
when the session recently PULLED a
snippet this payload references or resembles,
these carry it as a proposal — evidence for the
agent's own end-of-turn judgment, never a
verdict (milestone 439). Honoured only for a
verdict. Honoured only for a
caller allowed to write — a read-scoped key still
gets the hint, and never changes the ledger on a
GET.
@@ -537,7 +535,7 @@ def _parse_shapes(raw: str) -> list[tuple[str, str]]:
@plugin_bp.get("/shape-check")
@login_required
async def shape_check():
"""The end-of-turn question (milestone 439): what did this turn write that
"""The end-of-turn question: what did this turn write that
nobody has judged?
The client's write hooks keep a ledger of the definitions each turn wrote;
@@ -602,7 +600,7 @@ async def shape_check():
@plugin_bp.get("/claim-session")
@login_required
async def claim_session():
"""Bind the harness's session id to the caller's live claim on a task (milestone 381).
"""Bind the harness's session id to the caller's live claim on a task.
Called by `scribe_claim_session.sh` after `update_task` / `add_task_log`.
The server has already stamped the claim on that write; this only says
@@ -631,7 +629,7 @@ async def claim_session():
@plugin_bp.get("/release-session")
@login_required
async def release_session():
"""Release the claims a session held, as it ends (milestone 381 step 4).
"""Release the claims a session held, as it ends.
Called by `scribe_session_end.sh`. Best-effort by design: SessionEnd does
not fire on a crash, so the claim's lease — not this call — is what makes a