Milestone 444 steps 1-2: an operator's ruling lives on the System it governs, and code is read as behaviour, not intent #193

Merged
bvandeusen merged 1 commits from dev into main 2026-10-02 19:46:50 -04:00
6 changed files with 106 additions and 3 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "scribe",
"description": "Scribe for Claude Code: connects the scribe MCP server, adds the hooks that deliver live project state and relevant records at the right moment, ships the shared client-neutral Scribe skills (using-scribe, writing-plans, reporting-back, systematic-debugging, verification, brainstorming, reusing-code, shape-accounting), and syncs your saved Scribe Processes as skills (/scribe:sync).",
"version": "2026.10.01.1724",
"version": "2026.10.02.2259",
"author": {
"name": "Bryan Van Deusen"
},
+9
View File
@@ -153,6 +153,15 @@ name.
Before asking, check whether you can find the answer yourself — something that
can be read or looked up is a fact to check, not a question to send.
**An option carries assumptions, and the operator approves only what they can
see.** When an option keeps or depends on existing behaviour — a limit, a
default, a fallback already in place — say whose call that behaviour was: the
operator's, citing the ruling ("you ruled this, #N"), or a past session's that
nobody confirmed ("a past session chose this; you were never asked"). Choosing
between options is not a decision about the assumptions they share, and an
unlabelled one gets approved as if it had been asked. An assumption that
contradicts a recorded ruling is a **Conflict**, not an option's fine print.
## Answers — the operator asked something
| Kind | Sections |
+15 -1
View File
@@ -52,6 +52,12 @@ Two constraints on *how* that's achieved:
re-deriving it or opening a duplicate. When a project is in scope, pass its
`project_id` so results stay scoped.
**Code tells you what a thing does, not what was wanted.** A limit, a
default or a fallback found by reading the work is a past session's choice
until a record says the operator made it. Before presenting one as a
safeguard or a known cost, read the `Rulings` on the System it belongs to;
when none speaks to it, call it unconfirmed rather than settled.
2. **Rules are binding, and they reach you by retrieval.** No call loads the
operator's rules and no standing set is handed to a session. A rule arrives
when what you are about to do resembles what it is about — a command, the
@@ -227,6 +233,13 @@ Two constraints on *how* that's achieved:
re-measurement, a reversed decision), pass the old id in `supersedes` so the
stale record is demoted and labelled rather than left competing.
**What the operator decided an area must do is a ruling, and it lives on
that System** — a `Rulings` section in its description, written the turn
they decide. A quote in a work log reaches the next session only if a
search happens to match it; the description arrives with every record
filed there. [writing-records.md](writing-records.md) has the two tests and
the form.
10. **A note that asserts a fact about someone else's software can carry its
own check** (`verify_with`, `expires_when`), swept by
`notes_due_for_verification`. Most notes should not: read
@@ -264,7 +277,8 @@ moment rather than on every turn:
(`decide_project_inception` when `enter_project` returns `inception`).
- [writing-records.md](writing-records.md) — before writing a rule, preference
or lesson (where it goes, its `when_to_apply`, what already covers the
moment), and before giving a note a check.
moment), when the operator decides how an area must behave (a ruling), and
before giving a note a check.
- [missed-retrieval.md](missed-retrieval.md) — a rule that missed the moment it
governed, or keeps arriving where it doesn't apply.
+49 -1
View File
@@ -2,11 +2,13 @@
Part of the using-scribe skill. Read it before `create_rule`,
`create_project_rule`, `create_preference` or `create_lesson`; when a lesson
arrives that names the situation you are actually in; and before filling
arrives that names the situation you are actually in; when the operator
decides how some area of the work must behave; and before filling
`verify_with` or `expires_when` on a note.
## Contents
- Where a new rule goes — its home, its trigger, what already covers the moment
- A ruling goes on the System it governs
- A lesson grows each time it proves itself
- A lesson names the rule it is an instance of
- A note that asserts a fact can carry its own check
@@ -65,6 +67,52 @@ insight that costs time is a **lesson** (`create_lesson`), keyed to the
situation it applies to so a later session meets it there. Both are first-class
outcomes of noticing something, not what's left when a rule proposal fails.
## A ruling goes on the System it governs
A **ruling** is the operator's decision about what the work itself does in one
area: how it behaves, what it must never do, which way a trade-off goes. It is
not a rule. A rule governs how *you* work and reaches you when your work
resembles it; a ruling governs what the *thing being built* does, and it has to
reach every session working in that area, whatever words that session is using.
Two tests, and it needs both:
- **The operator decided it.** They said it, or approved it when it was put to
them as its own question. Approving an option is not approving every
assumption the option carried — only what it named.
- **A later change could plausibly undo it.** "Failed work is retried until it
succeeds" qualifies. The name of the table that tracks the retries does not;
that is implementation, and it lives in the code and the logs.
A choice a session made on its own is not a ruling, however sound. Keeping the
two apart is the point: once code embodies a session's choice, it reads exactly
like the operator's intent, and the next session builds on it as if it were.
**Where it goes: a `Rulings` section at the end of the System's description**,
one line each — the statement, who decided, when, and the record it came from:
Rulings
- Failed work is retried until it succeeds; no attempt limit. (Operator, 2026-09-19, #1234; restated 2026-10-02, #1290)
The description arrives with every record filed under that System, so a ruling
there reaches each session working in the area without having to win a search.
A quote inside a work log does not: it surfaces only when a query happens to
match it, and the passage that matches is usually the prose around it — often a
session's *reading* of the ruling rather than the ruling.
**When: in the turn the operator decides, before building on it.**
`get_system(id)`, then `update_system(id, description=...)` with the whole
description — the field is replaced, not appended to. Also when the operator
corrects work that departed from something they had already said: that ruling
existed and did not reach the work, and writing it where it will is half the
fix. No System fits? The area is probably unnamed — create it.
**It holds what is in force, not its history.** When a ruling is overturned,
change or remove its line and cite the record that changed it; the work log
keeps the history. If the charter above the section contradicts a ruling —
usually a charter written before the decision — fix that sentence in the same
edit. A stale charter line is a ruling nobody made, and it is read as one.
## A lesson grows each time it proves itself
When one arrives and the situation it names is the one you are actually in, you
+16
View File
@@ -188,6 +188,13 @@ async def create_system(
charter, not just a label: the description is what tells a later session
whether a record belongs here.
The description is also where the operator's RULINGS for the area live:
a `Rulings` section at its end, one line per decision the operator made
about how this area must behave — the statement, who decided, when, and
the record it came from. The description arrives with every record filed
under the System, so a ruling there reaches each session working in the
area; a quote in a work log reaches one only if a search matches it.
Args:
project_id: The project this system belongs to (required).
name: Short label (required).
@@ -303,6 +310,15 @@ async def update_system(
) -> dict:
"""Update a System. Only explicitly provided fields change.
`description` REPLACES the whole text, so read it with get_system first
and send it back entire. Recording a RULING — the operator's decision
about how this area must behave — is an edit here: add or change its line
in the `Rulings` section at the end, in the turn they decide, citing the
record it came from. Overturned, the line changes rather than gaining a
successor: the section holds what is in force, and the work log keeps the
history. If the charter above it contradicts a ruling, fix that sentence
in the same edit.
Args:
status: 'active' or 'archived'. Archive a system to retire it without
losing history; archived systems hide from default lists.
+16
View File
@@ -195,6 +195,16 @@ TOPICS: tuple[Topic, ...] = (
("what_might_apply",), "ask what already covers that moment"),
Topic("reference notes update in place; dev-logs don't", U, ("reference note",),
"state updates in place; chronicles don't"),
# Milestone 444: an operator's ruling was kept only as a quote in a work
# log, beside a session's reading of it that contradicted it, and three
# later sessions built on the reading. No index marker: both fire
# mid-work — when the operator decides, and when code is read as intent.
Topic("an operator's ruling lives on the System it governs", U,
("rulings", "update_system", "the operator decided it"),
"a stale charter line is a ruling nobody made"),
Topic("code says what a thing does, not what was wanted", U,
("rulings", "unconfirmed"),
"code tells you what a thing does, not what was wanted"),
# ── process arcs — owned by their skills ──
Topic("plan in a milestone, steps created together", "skill:writing-plans", ("start_planning", "{{ref:"),
"a milestone earns its place when the work has an arc", index=("start_planning",)),
@@ -229,6 +239,12 @@ TOPICS: tuple[Topic, ...] = (
Topic("a settled decision is acted on, not re-opened",
"skill:reporting-back", ("already made",),
"reads as contradicting yourself rather than as being careful"),
# Milestone 444: a cap a past session chose rode inside an option as a
# "known cost", the operator approved the option, and the cap read as
# theirs from then on.
Topic("an option says whose call each carried assumption was",
"skill:reporting-back", ("past session chose this", "conflict"),
"an unlabelled one gets approved as if it had been asked"),
# Milestone 409 step 8: `placement` rides a WRITE, so a task the reply only
# cites arrived with nothing vouching for it — which is how a step finished
# four days earlier was reported as the open one (#4154).