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:
@@ -268,6 +268,23 @@ async def create_rule(
|
||||
When the operator asks for a rule in so many words, that IS the yes —
|
||||
write it and move on. The loop below is for the rule you thought of.
|
||||
|
||||
FIRST, ASK WHAT KIND OF THING YOU ARE HOLDING. Force is the axis, and one
|
||||
question sorts it: *what happens if someone doesn't do this?*
|
||||
|
||||
* "something breaks, or a boundary is crossed" → a RULE. It must be
|
||||
followed, so it is the operator's to agree to. Propose it here.
|
||||
* "it gets done a way the operator didn't want" → a PREFERENCE
|
||||
(create_preference). How they want work done; ignoring it costs
|
||||
consistency rather than correctness. No approval loop — record it.
|
||||
* "they lose time rediscovering it" → a LESSON (create_lesson). A better
|
||||
way to think about a problem, or a solution that transfers, met again
|
||||
at the moment it applies. It binds nobody, so it needs no yes.
|
||||
|
||||
Asking this raises the value of noticing rather than lowering it: the
|
||||
observation is worth keeping in all three cases, and what changes is
|
||||
only which door it goes through. A proposal that turns out to be a
|
||||
lesson has not failed — it has been routed.
|
||||
|
||||
A PROPOSAL CARRIES FOUR THINGS, and the fourth is the one that decides it:
|
||||
|
||||
1. WHAT it would require — the statement, in the words it would carry,
|
||||
@@ -289,8 +306,8 @@ async def create_rule(
|
||||
shrinks only on purpose, so a question that prevents a rule is worth
|
||||
more than any question that improves one's wording.
|
||||
|
||||
THEN CLOSE WITH A QUESTION THEY CAN ANSWER IN ONE WORD. Offer three
|
||||
answers, and make the middle one the easy one:
|
||||
THEN CLOSE WITH A QUESTION THEY CAN ANSWER IN ONE WORD. Offer these
|
||||
answers, and make "LET'S TALK ABOUT IT" the easy one:
|
||||
|
||||
* "Approve it AS WRITTEN" — you create it with the statement exactly as
|
||||
shown. This is what makes element 1 load-bearing: they approved TEXT,
|
||||
@@ -298,14 +315,27 @@ async def create_rule(
|
||||
* "LET'S TALK ABOUT IT" — the wording, the scope, whether it
|
||||
wants to be a rule at all. Most good rules arrive this way, so treat
|
||||
this answer as the expected one rather than a setback.
|
||||
* "NO" — let it go. If the observation is still worth keeping, it is a
|
||||
note (create_note): recorded, findable, and binding on nobody.
|
||||
* "MAKE IT A PREFERENCE" — they want it done this way, but nothing
|
||||
breaks if it isn't. create_preference records it without a loop, and
|
||||
it stays yours to keep current as they correct you.
|
||||
* "MAKE IT A LESSON" — worth knowing, binding nobody. create_lesson
|
||||
stores it against the SITUATION it applies to, so a later session
|
||||
meets it at that moment rather than having to go looking. Reach for
|
||||
this whenever the answer to "what happens if someone doesn't do
|
||||
this" was "they lose time".
|
||||
* "NO" — let it go. If the observation is still worth keeping and none
|
||||
of the above fits, it is a note (create_note): recorded, findable,
|
||||
and binding on nobody.
|
||||
|
||||
The middle three are not consolation prizes. They are where most good
|
||||
observations belong, and the reason the kind question is worth asking
|
||||
out loud rather than settled silently before the proposal.
|
||||
|
||||
Where the interface offers structured choices, ask it that way — a
|
||||
question with named options is answered in a click, while the same
|
||||
question inside a paragraph is answered by scrolling past. Where it does
|
||||
not, write the three options out as three options. Either way ask once
|
||||
and let the answer stand; re-raising a declined proposal argues a rule
|
||||
not, write the answers out as a list. Either way ask once and let the
|
||||
answer stand; re-raising a declined proposal argues a rule
|
||||
into existence, which is the thing this whole loop exists to prevent.
|
||||
|
||||
A rulebook rule is GLOBAL — it applies in every project — so it must read as
|
||||
@@ -431,14 +461,16 @@ async def create_project_rule(
|
||||
in get_project's project_rules and in list_rules(project_id=...).
|
||||
|
||||
PROPOSE, THEN WRITE ON A YES — create_rule's opening carries the whole
|
||||
loop: the four things a proposal states (what it would require, its
|
||||
intent, why now, and how it would be enforced) and the one-word question
|
||||
that closes it (approve as written / talk about it / no). All of it
|
||||
applies here unchanged. Reach for that loop MORE readily on this surface,
|
||||
not less: a project rule stays out of an unfiltered list_rules(), and a
|
||||
conditional one stays out of session start too, so the operator's yes is
|
||||
the one moment this rule is certain to have been seen by the person it
|
||||
binds.
|
||||
loop: the kind question that comes first (what happens if someone doesn't
|
||||
do this — a rule binds, a preference guides, a lesson informs), the four
|
||||
things a proposal states (what it would require, its intent, why now, and
|
||||
how it would be enforced) and the one-word question that closes it
|
||||
(approve as written / talk about it / make it a preference / make it a
|
||||
lesson / no). All of it applies here unchanged. Reach for that loop MORE
|
||||
readily on this surface, not less: a project rule stays out of an
|
||||
unfiltered list_rules(), and a conditional one stays out of session start
|
||||
too, so the operator's yes is the one moment this rule is certain to have
|
||||
been seen by the person it binds.
|
||||
|
||||
Check first whether a rule is the right shape at all — create_rule's
|
||||
opening asks that question and it applies identically here. A visual
|
||||
|
||||
Reference in New Issue
Block a user