feat(instructions): a rule proposal has five answers, and three of them route (#3733, #3896)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Failing after 10s
CI & Build / integration (push) Successful in 51s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / Python tests (push) Successful in 1m31s
CI & Build / Build & push image (push) Successful in 24s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Failing after 10s
CI & Build / integration (push) Successful in 51s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / Python tests (push) Successful in 1m31s
CI & Build / Build & push image (push) Successful in 24s
Step 6 of both milestone 385 (lessons) and 399 (preferences). #3896 asked for the fourth and fifth answers in one pass, because two people each adding one branch to a three-way distinction produce a list that does not read as a set. create_rule now opens by asking what kind of thing is being held, with one question that sorts it — what happens if someone doesn't do this? Something breaks, a boundary is crossed: a rule. It gets done a way the operator didn't want: a preference. They lose time rediscovering it: a lesson. The closing question grew the two matching answers, and they are named as first-class outcomes rather than places a proposal lands when it fails. An observation that turns out to be a lesson has been routed, not dropped. Stated as a practice, not a prohibition (rule 165). #3557's first cut opened "NOT YOURS TO CALL UNPROMPTED" and cost the noticing; the wanted behaviour here is still more proposals, and what changes is only which door they go through. create_note says the same from its side, so routing does not depend on having opened create_rule first — and its existing rule test ("a mistake, not merely uninformed") turned out to name the lesson exactly. create_lesson names the fifth kind so the set is complete from every door. create_project_rule's citation of the loop names five answers, since it cites rather than repeats. The force axis has one owner (decision #4027): using-scribe states all three strengths, the sorting question, that updating a preference mid-work is the normal case, and that preferences shape how work is done and never what gets recorded. _INSTRUCTIONS carries the pointer — "Rules bind; preferences guide and you keep them current; lessons inform." It had 14 characters of headroom, so the clause is paid for by trimming atmosphere from three other lines; 1998 of 2000 now. Guards: the proposal-loop test learns the preference branch, the lesson branch and the force question, on both rule surfaces; guidance-ownership gains the force-axis topic (shared with the docstrings, for the moment a proposal is actually written) and the preference-scope topic; a new guard pins that the index names all three strengths and who keeps the middle one current, with its can-fail case being the omission that actually happens — a kind added to the product while the index still describes the corpus that came before it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
This commit is contained in:
@@ -119,6 +119,19 @@ TOPICS: tuple[Topic, ...] = (
|
||||
index=('content_type="rule"', "nothing matched")),
|
||||
Topic("rules bind, preferences guide and are kept current", U, ("preference", "update_preference"),
|
||||
"a preference is the one record you keep current yourself", index=("preferences guide",)),
|
||||
Topic("kind is force: a rule binds, a preference guides, a lesson informs", U,
|
||||
("lesson", "costs time", "costs consistency"),
|
||||
"what happens if someone doesn't do this",
|
||||
index=("lessons inform",),
|
||||
# Stated twice on purpose, at the two moments it is needed: the
|
||||
# skill while deciding what a record IS, and create_rule while a
|
||||
# proposal is actually being written — which is where the
|
||||
# mis-routing #3733 recorded happens, and the caller there has not
|
||||
# necessarily read the skill.
|
||||
shared_with=("docstrings",)),
|
||||
Topic("preferences shape how work is done, never what is recorded", U,
|
||||
("never what gets recorded",),
|
||||
"a preference never makes a task into a note"),
|
||||
Topic("recall before acting", U, ("recall before acting",), "for related prior work", index=("recall",)),
|
||||
Topic("stay inside the active project's scope", U, ("stay inside the active project", "cross-project"),
|
||||
"stay inside the active project's scope", index=("project_id",)),
|
||||
|
||||
@@ -131,6 +131,53 @@ def test_a_surface_claiming_rules_bind_also_names_what_does_not():
|
||||
)
|
||||
|
||||
|
||||
# ── the index names every strength, and who keeps the middle one current ──
|
||||
#
|
||||
# Milestones 385 and 399 both end here, and for the same reason. The index is
|
||||
# the one guidance surface EVERY MCP client receives — a client that reads no
|
||||
# Agent Skills still gets it — so a strength the index omits does not exist for
|
||||
# that session. Before the lesson kind, "rules bind; preferences guide" was the
|
||||
# whole corpus; a session that meets a third line shape with no name for it
|
||||
# falls back on the nearest one it has, which is the binding one.
|
||||
#
|
||||
# The currency half is the claim #3896 was written around: a preference is the
|
||||
# one record a session updates ITSELF, without asking. Stated only in the
|
||||
# skill, it reaches a session that loaded the skill — which is exactly the
|
||||
# session least likely to need telling. In the index it reaches everyone.
|
||||
#
|
||||
# Words, not wording: each entry is a family, and a rewrite satisfies it by
|
||||
# saying the thing in any of these ways.
|
||||
INDEX_STRENGTHS = {
|
||||
"that rules bind": ("rules bind",),
|
||||
"that preferences guide": ("preferences guide",),
|
||||
"that lessons inform": ("lessons inform", "lessons are", "a lesson informs"),
|
||||
"that a preference is kept current": ("current",),
|
||||
}
|
||||
|
||||
|
||||
def test_the_index_names_all_three_strengths():
|
||||
text = " ".join(_instructions_text().split()).lower()
|
||||
missing = [claim for claim, family in INDEX_STRENGTHS.items()
|
||||
if not any(t in text for t in family)]
|
||||
assert not missing, (
|
||||
f"_INSTRUCTIONS no longer says {missing}. It is the only guidance every "
|
||||
f"client receives, and a record kind it does not name is one the session "
|
||||
f"has to guess the force of — which it does by reaching for the kind it "
|
||||
f"does know. Keep it to a clause; the full statement lives on "
|
||||
f"using-scribe (decision #4027)."
|
||||
)
|
||||
|
||||
|
||||
def test_the_strength_guard_can_fail():
|
||||
"""Rule 167. The guard has to be able to bite, and it bites on omission —
|
||||
the failure that actually happens, when a kind is added to the product and
|
||||
the index is left describing the corpus that came before it."""
|
||||
text = "rules bind; preferences guide and you keep them current."
|
||||
missing = [claim for claim, family in INDEX_STRENGTHS.items()
|
||||
if not any(t in text for t in family)]
|
||||
assert missing == ["that lessons inform"]
|
||||
|
||||
|
||||
# ── Reporting back (milestone 409 step 3) ──────────────────────────────
|
||||
#
|
||||
# The reporting-back skill carries the shapes, but a skill only helps if it
|
||||
|
||||
@@ -76,6 +76,20 @@ _ELEMENTS = {
|
||||
),
|
||||
"how it would be enforced": ("enforc",),
|
||||
"the answers offered back": ("as written", "talk about it", "discuss"),
|
||||
# The fourth and fifth answers (#3733, #3896). They are what makes the
|
||||
# question a ROUTING question rather than a yes/no one: an observation
|
||||
# that turns out to be a preference or a lesson has found its home, not
|
||||
# failed to become a rule. Written in one pass, because two people each
|
||||
# adding one branch to a three-way distinction produce a list that does
|
||||
# not read as a set.
|
||||
"the preference branch": ("create_preference", "a preference"),
|
||||
"the lesson branch": ("create_lesson", "a lesson"),
|
||||
# And the test that routes BEFORE the proposal is drafted, so the kind is
|
||||
# a question asked out loud rather than one settled silently.
|
||||
"the force question": (
|
||||
"what happens if someone doesn't", "force is the axis",
|
||||
"a rule binds, a preference guides",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user