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:
@@ -45,18 +45,18 @@ client reads Agent Skills) and in each tool's description. The index:
|
||||
decide_project_inception.
|
||||
- RULES: nothing preloads; a rule arrives when your work matches it. Before a
|
||||
consequential act — or before handing work back unsure you may finish it —
|
||||
what_might_apply("what you are about to do"): the wide net, fifty ranked
|
||||
what_might_apply("what you are about to do"): fifty ranked
|
||||
candidates, no bar. search(content_type="rule") reads one you already
|
||||
suspect. Silence means nothing matched, not none. Rules bind; preferences
|
||||
guide.
|
||||
- MISSED: a rule that should have reached you is a trigger to fix, not a
|
||||
guide and you keep them current; lessons inform.
|
||||
- MISSED: a rule that missed you is a trigger to fix, not a
|
||||
floor to move (retrieval_telemetry).
|
||||
- RECALL: search before acting, scoped with the active project_id.
|
||||
- RECORD: create_task; a fix is kind="issue". add_task_log as you go; status
|
||||
in_progress on start, done on finish. Tag system_ids as you write.
|
||||
- PLAN work with an arc: find the existing plan first
|
||||
(search(content_type="milestone")) and add steps to it; else
|
||||
start_planning(steps=[...]). The plan is a milestone and each step a task.
|
||||
start_planning(steps=[...]). The plan is a milestone, each step a task.
|
||||
- IDS exist only once a create returns them. Records that cite each other go
|
||||
through create_records, writing {{ref:N}} for the Nth record.
|
||||
- REUSE: search snippets before building; create_snippet what you build.
|
||||
|
||||
@@ -111,10 +111,13 @@ async def create_lesson(
|
||||
surface that is both global and situation-keyed, and proposing rules for
|
||||
things that should never have bound anyone.
|
||||
|
||||
WHAT A LESSON IS NOT: a shape to copy is a SNIPPET (create_snippet); a
|
||||
procedure followed start to finish is a PROCESS (create_process); a record
|
||||
of what happened, findable by topic, is a NOTE (create_note). A lesson is
|
||||
the claim you would want handed to you in the same situation next time.
|
||||
WHAT A LESSON IS NOT: how the operator wants work DONE is a PREFERENCE
|
||||
(create_preference) — it guides every session rather than informing one,
|
||||
and it is kept current as they correct you. A shape to copy is a SNIPPET
|
||||
(create_snippet); a procedure followed start to finish is a PROCESS
|
||||
(create_process); a record of what happened, findable by topic, is a NOTE
|
||||
(create_note). A lesson is the claim you would want handed to you in the
|
||||
same situation next time.
|
||||
|
||||
`when_to_apply` IS THE RECORD. Everything else is the payload.
|
||||
|
||||
|
||||
@@ -120,6 +120,15 @@ async def create_note(
|
||||
is a task nobody will ever see again.
|
||||
- future sessions must OBEY it -> create_rule. The test is whether
|
||||
ignoring it would be a mistake, not merely uninformed.
|
||||
- it is how the operator wants work DONE, and ignoring it costs
|
||||
consistency rather than correctness -> create_preference. No approval
|
||||
loop, and it stays yours to keep current as they correct you.
|
||||
- "merely uninformed" is the other half of the rule test, and it has its
|
||||
own home: something you learned that a later session should meet at the
|
||||
moment it applies -> create_lesson. A note is found by topic when
|
||||
someone goes looking; a lesson arrives on its own, keyed to the
|
||||
SITUATION. If you would want this handed to you next time you were in
|
||||
that spot, it is a lesson.
|
||||
- reusable code with a place in a repo -> create_snippet. The location is
|
||||
what lets it be found from the file someone is about to edit.
|
||||
- a procedure followed start to finish -> create_process.
|
||||
|
||||
@@ -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