Merge pull request 'Plan gate default 0.80, and an "Approval requested" reply section' (#160) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Failing after 15s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m34s
CI & Build / Build & push image (push) Successful in 14s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Failing after 15s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m34s
CI & Build / Build & push image (push) Successful in 14s
This commit was merged in pull request #160.
This commit is contained in:
@@ -105,7 +105,7 @@ const kbDupThresholdSnippet = ref("0.82");
|
||||
const kbDupThresholdNote = ref("0.93");
|
||||
const kbDupThresholdTask = ref("0.93");
|
||||
// PLAN_MATCH_DEFAULT_THRESHOLD in services/dedup.py.
|
||||
const kbPlanMatchThreshold = ref("0.90");
|
||||
const kbPlanMatchThreshold = ref("0.80");
|
||||
const savingKbInject = ref(false);
|
||||
const kbInjectSaved = ref(false);
|
||||
|
||||
@@ -159,7 +159,7 @@ async function saveKbInject() {
|
||||
const dupTask = Math.min(1, Math.max(0, Number(kbDupThresholdTask.value) || 0.93));
|
||||
// Same `|| default` guard: a floor of 0 would hand back an existing plan
|
||||
// for every new one, and no plan could be started without force.
|
||||
const planT = Math.min(1, Math.max(0, Number(kbPlanMatchThreshold.value) || 0.9));
|
||||
const planT = Math.min(1, Math.max(0, Number(kbPlanMatchThreshold.value) || 0.8));
|
||||
// Same `|| default` reasoning: falling back to 0 would surface every
|
||||
// snippet in the corpus on every edit, which is the failure this knob fixes.
|
||||
const wpT = Math.min(1, Math.max(0, Number(kbWritePathThreshold.value) || 0.68));
|
||||
|
||||
@@ -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.09.15.1744",
|
||||
"version": "2026.09.15.1921",
|
||||
"author": {
|
||||
"name": "Bryan Van Deusen"
|
||||
},
|
||||
|
||||
@@ -23,6 +23,12 @@ when one is short — "**Needs you:** nothing" is an answer they were looking fo
|
||||
plain.
|
||||
- **End with the ask, in bold** — the one thing they need to decide or do. If
|
||||
there is nothing, say so.
|
||||
- **A request for approval gets its own section, headed "Approval requested".**
|
||||
Whenever you are holding an action until the operator says yes — a permission
|
||||
prompt their client raised, something hard to undo, a change you have
|
||||
prepared and not made — put it there, near the top, whatever kind of reply
|
||||
this is. Inside a progress or completion list it reads as a status line, and
|
||||
the operator does not see that you are waiting on them.
|
||||
- **Plain words.** Use the operator's vocabulary, not the names you coined while
|
||||
working. If a term has to appear, explain it once.
|
||||
- **Place the work in Scribe.** Name the task, issue or milestone it belongs to,
|
||||
@@ -76,6 +82,7 @@ default differ, the preference is what they asked for.
|
||||
| **Decision** | The question first · 2–4 options, each with what it changes · recommendation first |
|
||||
| **Clarification** | "My reading is X · the gap is Y · unless you say otherwise I'll do Z" |
|
||||
| **Handoff** (only they can do it) | The action · why it needs them · what it unblocks · what you'll do after · any way to skip it |
|
||||
| **Approval** (you are ready to act and holding for a yes) | **Approval requested:** exactly what happens once they approve, one numbered item per change so they can approve part · why it needs their yes · how it can be undone · what you'll do after |
|
||||
| **Conflict** (what you're about to do clashes with a rule, a plan or an earlier decision) | What it says · what you were about to do · where they clash · A or B? |
|
||||
|
||||
Before asking, check whether you can find the answer yourself — something that
|
||||
@@ -128,7 +135,8 @@ Notes on each section:
|
||||
verified**. If something could not be verified, say what and why here rather
|
||||
than letting it read as passed.
|
||||
- **Needs you** — an action, an approval, a decision, or "nothing". If it's an
|
||||
action, give the reason with it.
|
||||
action, give the reason with it. An approval you are holding for also gets
|
||||
its own **Approval requested** section, and this line points at it.
|
||||
- **Next** — from `placement.next`, or say the milestone is finished. If you
|
||||
found something you didn't fix, the offer to fix it goes here.
|
||||
|
||||
|
||||
@@ -739,16 +739,22 @@ async def find_duplicate_rule(
|
||||
# project is exactly what adding steps to its existing plan needs, so a caller
|
||||
# who reaches this gate can act on whatever it returns.
|
||||
#
|
||||
# Its own threshold, as a setting (rule 25). The note gate's 0.90 is where it
|
||||
# starts, because it asks the same question ("the same thing, reworded") with
|
||||
# the same embedder. Plan documents are shaped differently, though: a milestone
|
||||
# is embedded as title, description and plan (embeddings.milestone_document),
|
||||
# while the candidate usually has no description and carries its steps
|
||||
# instead. That difference has not been measured, and a gate that blocks on
|
||||
# noise teaches sessions to pass force=true every time, which is worse than no
|
||||
# gate. So the default is conservative and the operator can lower it.
|
||||
# Its own threshold, as a setting (rule 25), and lower than the note gate's
|
||||
# 0.90 because plan documents are shaped differently: a milestone is embedded
|
||||
# as title, description and plan (embeddings.milestone_document), while the
|
||||
# candidate usually has no description and carries its steps instead, so even
|
||||
# a faithful rewording never scores like a copy.
|
||||
#
|
||||
# Measured after the first deploy (2026-09-15, bge-small-en-v1.5, #4079): three
|
||||
# plans reworded from existing active milestones scored 0.83, 0.85 and 0.87
|
||||
# against the milestone they restated; the nearest DIFFERENT plan for each, and
|
||||
# a distinct plan on a neighbouring topic, scored 0.72-0.77. At 0.90 the
|
||||
# semantic arm matched nothing, leaving only the title arm. 0.80 sits in the
|
||||
# gap. A gate that blocks on noise teaches sessions to pass force=true every
|
||||
# time, which is why it is not lower; four samples is thin, which is why the
|
||||
# operator can move it. Retune alongside the embedder, not the corpus.
|
||||
PLAN_MATCH_THRESHOLD_KEY = "kb_plan_match_threshold"
|
||||
PLAN_MATCH_DEFAULT_THRESHOLD = 0.90
|
||||
PLAN_MATCH_DEFAULT_THRESHOLD = 0.80
|
||||
|
||||
|
||||
async def get_plan_match_threshold(user_id: int) -> float:
|
||||
|
||||
@@ -38,6 +38,16 @@ def test_the_completion_report_keeps_its_sections():
|
||||
assert section in text, f"the completion report lost its {section!r} section"
|
||||
|
||||
|
||||
def test_a_request_for_approval_has_its_own_named_section():
|
||||
"""The operator, 2026-09-15 (#4084): a session's go-ahead request sat inside
|
||||
a completion list as "Blocked by the permission check", and read as a
|
||||
fault rather than a question waiting on them. The heading is what they
|
||||
scan for, so it is pinned by name."""
|
||||
text = _text()
|
||||
assert "Approval requested" in text
|
||||
assert "| **Approval**" in text, "the Asks table lost its Approval row"
|
||||
|
||||
|
||||
def test_placement_comes_from_the_record():
|
||||
"""The failure this milestone started from: a placement written from memory
|
||||
reads exactly like a real one when it is wrong."""
|
||||
|
||||
Reference in New Issue
Block a user