docs(6209): forge, dedup, tuning, registry, reply shapes and discipline, checkpoint, lesson kind tests
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 25s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / integration (push) Successful in 1m24s
CI & Build / Python tests (push) Successful in 2m13s
CI & Build / Build & push image (push) Successful in 13s
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 25s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / integration (push) Successful in 1m24s
CI & Build / Python tests (push) Successful in 2m13s
CI & Build / Build & push image (push) Successful in 13s
Eight more test files state their properties without issue, milestone, rule and decision numbers. The tuning and suspension notes describe the symptom a shipped default or a late ledger produces instead of one install's dated readings; the reply-discipline and checkpoint docstrings state the failure they prevent instead of the session that measured it. test_empty_selector_is_the_rule_115_baseline is renamed for what it checks: the no-forge baseline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,18 +1,17 @@
|
|||||||
"""The `lesson` kind — its trigger, its title, and its place in the vocabulary.
|
"""The `lesson` kind — its trigger, its title, and its place in the vocabulary.
|
||||||
|
|
||||||
WHY THIS EXISTS (milestone 385 step 2, #3729)
|
WHY THIS EXISTS
|
||||||
|
|
||||||
A lesson is a note that must be findable by WHEN IT APPLIES rather than by what
|
A lesson is a note that must be findable by WHEN IT APPLIES rather than by what
|
||||||
it is about. That is a document-shape fact: the trigger has to reach the
|
it is about. That is a document-shape fact: the trigger has to reach the
|
||||||
embedded text, and decision #4157 put it in `notes.data` with a mirror in the
|
embedded text, so it lives in `notes.data` with a mirror in the title and the
|
||||||
title and the head of the body — the shape snippets already use.
|
head of the body — the shape snippets use.
|
||||||
|
|
||||||
THE ONE THAT MATTERS MOST
|
THE ONE THAT MATTERS MOST
|
||||||
|
|
||||||
`test_one_join_builds_every_trigger_title`. Three kinds now rank on a
|
`test_one_join_builds_every_trigger_title`. Three kinds now rank on a
|
||||||
`{subject} — {trigger}` title: rules, snippets and lessons. That join had three
|
`{subject} — {trigger}` title: rules, snippets and lessons, through ONE join —
|
||||||
implementations before this step and would have had four; #3207 records what
|
a copy per kind is a fix that has to be found in three places. The guard is behavioural rather than `assert a is b`, because the
|
||||||
that costs. The guard is behavioural rather than `assert a is b`, because the
|
|
||||||
three are reached through different public names and a test that compared
|
three are reached through different public names and a test that compared
|
||||||
identities would pass on a re-implementation that merely re-exported.
|
identities would pass on a re-implementation that merely re-exported.
|
||||||
|
|
||||||
@@ -75,7 +74,7 @@ def test_one_join_builds_every_trigger_title():
|
|||||||
expected = f"{subject} — {trigger}"
|
expected = f"{subject} — {trigger}"
|
||||||
|
|
||||||
assert trigger_title(subject, trigger) == expected
|
assert trigger_title(subject, trigger) == expected
|
||||||
# The two note kinds reach it through the EMBEDDED title (milestone 427),
|
# The two note kinds reach it through the EMBEDDED title,
|
||||||
# from their own mirror key — never a hand-rolled join.
|
# from their own mirror key — never a hand-rolled join.
|
||||||
from scribe.services.embeddings import document_title
|
from scribe.services.embeddings import document_title
|
||||||
assert document_title(subject, "lesson", {"when_to_apply": trigger}) == expected
|
assert document_title(subject, "lesson", {"when_to_apply": trigger}) == expected
|
||||||
@@ -95,7 +94,7 @@ def test_the_kind_is_in_the_browse_vocabulary():
|
|||||||
|
|
||||||
Asserted through the public table rather than a literal, because the door's
|
Asserted through the public table rather than a literal, because the door's
|
||||||
validation, the counts and both dialects of the type filter are all
|
validation, the counts and both dialects of the type filter are all
|
||||||
generated from it (#3161) — so this is the one place that decides.
|
generated from it — so this is the one place that decides.
|
||||||
"""
|
"""
|
||||||
assert lessons_svc.LESSON_NOTE_TYPE in knowledge_svc.FACET_TYPES
|
assert lessons_svc.LESSON_NOTE_TYPE in knowledge_svc.FACET_TYPES
|
||||||
assert lessons_svc.LESSON_NOTE_TYPE in knowledge_svc.NON_TASK_FACETS
|
assert lessons_svc.LESSON_NOTE_TYPE in knowledge_svc.NON_TASK_FACETS
|
||||||
@@ -104,7 +103,7 @@ def test_the_kind_is_in_the_browse_vocabulary():
|
|||||||
def test_the_global_kinds_list_names_the_lesson_and_nothing_else():
|
def test_the_global_kinds_list_names_the_lesson_and_nothing_else():
|
||||||
"""`embeddings.GLOBAL_NOTE_TYPES` spells "lesson" as a literal because
|
"""`embeddings.GLOBAL_NOTE_TYPES` spells "lesson" as a literal because
|
||||||
`services.lessons` imports `trigger_title` from that module, so importing
|
`services.lessons` imports `trigger_title` from that module, so importing
|
||||||
it back would close a cycle (milestone 385 step 3). The copy is pinned
|
it back would close a cycle. The copy is pinned
|
||||||
here instead — the one thing a literal costs is that it can drift, and
|
here instead — the one thing a literal costs is that it can drift, and
|
||||||
this is what stops it.
|
this is what stops it.
|
||||||
"""
|
"""
|
||||||
@@ -114,7 +113,7 @@ def test_the_global_kinds_list_names_the_lesson_and_nothing_else():
|
|||||||
|
|
||||||
|
|
||||||
def test_a_lesson_is_not_a_task_on_either_arm_of_the_filter():
|
def test_a_lesson_is_not_a_task_on_either_arm_of_the_filter():
|
||||||
"""The cell left empty on purpose (#3163).
|
"""The cell left empty on purpose.
|
||||||
|
|
||||||
`is_task` IS `status is not None`, so a lesson that acquired a status would
|
`is_task` IS `status is not None`, so a lesson that acquired a status would
|
||||||
become a task and start appearing in open-work listings — the one way
|
become a task and start appearing in open-work listings — the one way
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
"""A rule put IN FRONT of an act, not beside its result (#4214, milestone 419).
|
"""A rule put IN FRONT of an act, not beside its result.
|
||||||
|
|
||||||
WHY THIS EXISTS
|
WHY THIS EXISTS
|
||||||
|
|
||||||
Every other rule surface in this plugin returns `additionalContext`, which
|
Every other rule surface in this plugin returns `additionalContext`, which
|
||||||
Claude Code delivers alongside the tool RESULT. By the time the line is read
|
Claude Code delivers alongside the tool RESULT. By the time the line is read
|
||||||
the call is written, so the rule reads as commentary on a decision already
|
the call is written, so the rule reads as commentary on a decision already
|
||||||
made. Milestone 419 measured the cost over one session: seven misses, three
|
made — and the common miss is acting on the thing in hand without reading
|
||||||
caught by the operator and none by this system, five of them the same move —
|
the contract around it.
|
||||||
acting on the thing in hand without reading the contract around it.
|
|
||||||
|
|
||||||
A checkpoint spends the same retrieval differently. The hook returns a `deny`,
|
A checkpoint spends the same retrieval differently. The hook returns a `deny`,
|
||||||
the act does not run, and the rule's own text can be read before the call
|
the act does not run, and the rule's own text can be read before the call
|
||||||
@@ -22,11 +21,10 @@ itself (a tuning value, and a test asserting 0.80 would fail on every retune
|
|||||||
while proving nothing) or the wording of the reason (prose, and it will be
|
while proving nothing) or the wording of the reason (prose, and it will be
|
||||||
rewritten). The cases below express scores relative to the constant.
|
rewritten). The cases below express scores relative to the constant.
|
||||||
|
|
||||||
THE ONE BOUNDARY THAT IS A RECORDED DECISION, NOT A DESIGN CHOICE. Only the
|
THE ONE BOUNDARY THAT IS A STANDING DECISION, NOT A DESIGN CHOICE. Only the
|
||||||
ACTION arm can hold. `scribe_prior_art.sh` carries a tested property that it
|
ACTION arm can hold. `scribe_prior_art.sh` carries a tested property that it
|
||||||
never returns a permissionDecision — the operator's decision that a recall aid
|
never returns a permissionDecision — a recall aid may not stand in the way of
|
||||||
may not stand in the way of a write — and this milestone does not get to
|
a write — and a checkpoint does not get to quietly overturn that. The write-path arm computes the same block and returns
|
||||||
quietly overturn it. The write-path arm computes the same block and returns
|
|
||||||
it, so the decision can be revisited with evidence; the hook ignores it. The
|
it, so the decision can be revisited with evidence; the hook ignores it. The
|
||||||
last test here asserts that boundary holds, because it is exactly the kind of
|
last test here asserts that boundary holds, because it is exactly the kind of
|
||||||
property that erodes when someone extends the feature later.
|
property that erodes when someone extends the feature later.
|
||||||
@@ -101,7 +99,7 @@ def test_a_preference_never_holds_an_act():
|
|||||||
|
|
||||||
def test_a_rule_the_session_already_opened_never_holds_an_act():
|
def test_a_rule_the_session_already_opened_never_holds_an_act():
|
||||||
"""`held` is observable — a PostToolUse hook watches for the `get_rule`
|
"""`held` is observable — a PostToolUse hook watches for the `get_rule`
|
||||||
call (#4100) — so this is a recorded event, not a model's self-report
|
call — so this is a recorded event, not a model's self-report
|
||||||
about its own context. A session that read the rule has already had the
|
about its own context. A session that read the rule has already had the
|
||||||
thing the checkpoint exists to produce, and holding it again would punish
|
thing the checkpoint exists to produce, and holding it again would punish
|
||||||
the behaviour being asked for."""
|
the behaviour being asked for."""
|
||||||
@@ -167,7 +165,7 @@ def test_a_rule_with_no_trigger_renders_without_an_empty_bracket():
|
|||||||
def test_every_held_act_carries_a_rendered_reason():
|
def test_every_held_act_carries_a_rendered_reason():
|
||||||
"""Rendered at the one call site inside `checkpoint_for`, never by each
|
"""Rendered at the one call site inside `checkpoint_for`, never by each
|
||||||
arm: two arms that each remember to render it are two arms that can stop
|
arm: two arms that each remember to render it are two arms that can stop
|
||||||
agreeing on what a stop says, which is #3497's history for this pair."""
|
agreeing on what a stop says."""
|
||||||
assert hold([hit(0.99, 1)])["reason"].strip()
|
assert hold([hit(0.99, 1)])["reason"].strip()
|
||||||
|
|
||||||
|
|
||||||
@@ -253,8 +251,8 @@ def test_only_the_action_hook_can_hold_an_act():
|
|||||||
than left to memory.
|
than left to memory.
|
||||||
|
|
||||||
`scribe_prior_art.sh` runs before every Write and Edit and has never been
|
`scribe_prior_art.sh` runs before every Write and Edit and has never been
|
||||||
able to stop one. That is the operator's decision — a recall aid may not
|
able to stop one. That is a standing decision — a recall aid may not stand
|
||||||
stand in the way of the work — and `test_hook_never_returns_a_permission_
|
in the way of the work — and `test_hook_never_returns_a_permission_
|
||||||
decision` in test_write_path_trigger.py holds the other half of it.
|
decision` in test_write_path_trigger.py holds the other half of it.
|
||||||
|
|
||||||
This milestone has a live argument for extending the checkpoint to writes:
|
This milestone has a live argument for extending the checkpoint to writes:
|
||||||
@@ -273,7 +271,7 @@ def test_only_the_action_hook_can_hold_an_act():
|
|||||||
|
|
||||||
|
|
||||||
def test_the_action_hook_caps_every_hold_it_emits():
|
def test_the_action_hook_caps_every_hold_it_emits():
|
||||||
"""Structural, and able to fail (rule 167): the deny and the ledger call
|
"""Structural, and able to fail: the deny and the ledger call
|
||||||
must appear together. A deny emitted outside the guard is a session that
|
must appear together. A deny emitted outside the guard is a session that
|
||||||
can be held by the same rule on every command, which is the one outcome
|
can be held by the same rule on every command, which is the one outcome
|
||||||
both guards exist to prevent."""
|
both guards exist to prevent."""
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
"""A reply's sections are chosen, not filled (#4153, milestone 409 step 7).
|
"""A reply's sections are chosen, not filled.
|
||||||
|
|
||||||
WHY THIS EXISTS
|
WHY THIS EXISTS
|
||||||
|
|
||||||
Step 6 measured the scaffold on live sessions and found the two halves
|
A reply can carry every section the table asks for — placement, what changed,
|
||||||
disagreeing: **adherence passed and the read test failed.** Completion replies
|
what needs the operator, what comes next — and still be unreadable: adherence
|
||||||
carried every section the table asks for — placement, what changed, what needs
|
passes and the read test fails. An instruction to pick a kind of reply "then
|
||||||
the operator, what comes next — and the operator still could not read them.
|
fill its sections" is an instruction to complete a form, and with no ceiling a
|
||||||
|
section with a heading and nothing to say gets filled rather than dropped. A
|
||||||
The cause was in the skill rather than in compliance with it. It said to pick a
|
faithful reply and an unreadable one become the same reply.
|
||||||
kind of reply "then fill its sections… keep them even when one is short", which
|
|
||||||
is an instruction to complete a form. Nothing anywhere set a ceiling, and a
|
|
||||||
section with a heading and nothing to say gets filled rather than dropped. So a
|
|
||||||
faithful reply and an unreadable one were the same reply.
|
|
||||||
|
|
||||||
WHAT IS PINNED
|
WHAT IS PINNED
|
||||||
|
|
||||||
@@ -25,18 +21,18 @@ unchanged and are not this file's subject:
|
|||||||
3. "Needs you" takes BOTH tests: theirs to decide, and blocking.
|
3. "Needs you" takes BOTH tests: theirs to decide, and blocking.
|
||||||
4. A decision already made is acted on rather than re-argued.
|
4. A decision already made is acted on rather than re-argued.
|
||||||
|
|
||||||
RULE 167, AND WHY THERE IS NO ABSENCE CHECK HERE
|
WHY THERE IS NO ABSENCE CHECK HERE
|
||||||
|
|
||||||
The obvious guard — assert the skill no longer tells anyone to "fill" a
|
The obvious guard — assert the skill never tells anyone to "fill" a section —
|
||||||
section — is the trap snippet #3352 names. This skill legitimately discusses
|
is a trap. This skill legitimately discusses
|
||||||
filling in order to warn against it ("not a form to complete", "a section
|
filling in order to warn against it ("not a form to complete", "a section
|
||||||
filled because it was in the table"), so an absence check would fail on the
|
filled because it was in the table"), so an absence check would fail on the
|
||||||
corrected text: a false alarm about the very thing it protects.
|
corrected text: a false alarm about the very thing it protects.
|
||||||
|
|
||||||
So every assertion here is a PRESENCE check, and one is POSITIONAL —
|
So every assertion here is a PRESENCE check, and one is POSITIONAL —
|
||||||
`test_the_needs_you_test_sits_with_the_needs_you_section` pins where the test
|
`test_the_needs_you_test_sits_with_the_needs_you_section` pins where the test
|
||||||
lives, not merely that the words occur somewhere in the file. All four were
|
lives, not merely that the words occur somewhere in the file. Each fails
|
||||||
falsified against the pre-#4153 text before being committed.
|
against a skill that says to fill its sections.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -111,7 +107,7 @@ def test_the_needs_you_test_sits_with_the_needs_you_section():
|
|||||||
def test_a_settled_decision_is_acted_on_rather_than_re_argued():
|
def test_a_settled_decision_is_acted_on_rather_than_re_argued():
|
||||||
"""Re-opening a decision reads as self-contradiction, not as diligence.
|
"""Re-opening a decision reads as self-contradiction, not as diligence.
|
||||||
|
|
||||||
Phrased as a practice rather than a prohibition (rule 165): the instruction
|
Phrased as a practice rather than a prohibition: the instruction
|
||||||
is what to DO with a decision, with the failure named after it.
|
is what to DO with a decision, with the failure named after it.
|
||||||
"""
|
"""
|
||||||
text = _flat()
|
text = _flat()
|
||||||
@@ -126,14 +122,13 @@ def test_the_scaffold_itself_is_untouched():
|
|||||||
"""This step changed the discipline AROUND the sections, not the sections.
|
"""This step changed the discipline AROUND the sections, not the sections.
|
||||||
|
|
||||||
If a future edit deletes a category while tightening the prose, that is a
|
If a future edit deletes a category while tightening the prose, that is a
|
||||||
change to #4009's subject and should not ride along silently here.
|
change to the scaffold itself and should not ride along silently here.
|
||||||
"""
|
"""
|
||||||
text = _flat()
|
text = _flat()
|
||||||
for section in ("where this sits", "what now works", "how / why",
|
for section in ("where this sits", "what now works", "how / why",
|
||||||
"needs you", "next"):
|
"needs you", "next"):
|
||||||
assert section in text, f"completion-report section {section!r} is gone"
|
assert section in text, f"completion-report section {section!r} is gone"
|
||||||
# The kinds live in the delivered shapes since milestone 500 step 4; the
|
# The kinds live in the delivered shapes; the skill keeps the reasoning and the worked completion report.
|
||||||
# skill keeps the reasoning and the worked completion report.
|
|
||||||
from scribe.services import reply_shapes
|
from scribe.services import reply_shapes
|
||||||
|
|
||||||
shapes = " ".join(s.title + " " + s.text for s in reply_shapes.SHAPES.values()).lower()
|
shapes = " ".join(s.title + " " + s.text for s in reply_shapes.SHAPES.values()).lower()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""The default reply shapes and the moments they ride (milestone 500 step 1).
|
"""The default reply shapes and the moments they ride.
|
||||||
|
|
||||||
What this pins is what delivery will depend on: every shape rides a moment that
|
What this pins is what delivery will depend on: every shape rides a moment that
|
||||||
exists, the core is small enough to pay for on every turn, no slice grows back
|
exists, the core is small enough to pay for on every turn, no slice grows back
|
||||||
@@ -15,7 +15,7 @@ _REAL_RECORD = reply_shapes.record_delivery
|
|||||||
|
|
||||||
|
|
||||||
def test_there_is_a_core_and_at_least_one_slice():
|
def test_there_is_a_core_and_at_least_one_slice():
|
||||||
"""The sweeps below are vacuous over an empty catalog (rule 167)."""
|
"""The sweeps below are vacuous over an empty catalog."""
|
||||||
assert reply_shapes.CORE_KEY in reply_shapes.SHAPES
|
assert reply_shapes.CORE_KEY in reply_shapes.SHAPES
|
||||||
assert len(reply_shapes.SHAPES) >= 2
|
assert len(reply_shapes.SHAPES) >= 2
|
||||||
|
|
||||||
@@ -74,16 +74,16 @@ def test_every_shape_says_what_it_is_and_when_it_arrives(key):
|
|||||||
|
|
||||||
|
|
||||||
def test_the_core_carries_the_length_discipline():
|
def test_the_core_carries_the_length_discipline():
|
||||||
"""Milestone 409's live read: replies that had every section were still
|
"""A reply that has every section can still be too long. Delivery cannot
|
||||||
too long. Delivery cannot fix that; the core has to say it."""
|
fix that; the core has to say it."""
|
||||||
text = reply_shapes.core().text.lower()
|
text = reply_shapes.core().text.lower()
|
||||||
assert "shortest reply that carries the answer" in text
|
assert "shortest reply that carries the answer" in text
|
||||||
assert "what can go" in text
|
assert "what can go" in text
|
||||||
|
|
||||||
|
|
||||||
def test_who_decides_what_rides_every_reply_and_every_ask():
|
def test_who_decides_what_rides_every_reply_and_every_ask():
|
||||||
"""The operator's line (milestone 500 step 2): the agent settles what
|
"""Who decides: the agent settles what only it can see, the operator
|
||||||
only it can see, the operator decides direction. A one-liner every turn,
|
decides direction. A one-liner every turn,
|
||||||
in full where a question is about to be handed back."""
|
in full where a question is about to be handed back."""
|
||||||
core = reply_shapes.core().text.lower()
|
core = reply_shapes.core().text.lower()
|
||||||
asks = reply_shapes.SHAPES["asks"].text.lower()
|
asks = reply_shapes.SHAPES["asks"].text.lower()
|
||||||
@@ -130,7 +130,7 @@ def test_the_tool_is_registered_as_a_read():
|
|||||||
|
|
||||||
|
|
||||||
def test_both_doors_read_one_catalog():
|
def test_both_doors_read_one_catalog():
|
||||||
"""Rule 33 parity: the session and the Settings view cannot show
|
"""Parity: the session and the Settings view cannot show
|
||||||
different defaults."""
|
different defaults."""
|
||||||
from scribe.mcp.tools import moments as tool
|
from scribe.mcp.tools import moments as tool
|
||||||
from scribe.routes import retrieval as routes
|
from scribe.routes import retrieval as routes
|
||||||
@@ -158,7 +158,7 @@ async def test_the_route_returns_the_operators_overview_with_a_bounded_window():
|
|||||||
assert view.await_args.kwargs == {"days": days}
|
assert view.await_args.kwargs == {"days": days}
|
||||||
|
|
||||||
|
|
||||||
# ── the operator's view (milestone 500 step 5) ──────────────────────────
|
# ── the Settings view ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
async def test_the_overview_lists_the_preferences_mounted_on_each_shapes_moment():
|
async def test_the_overview_lists_the_preferences_mounted_on_each_shapes_moment():
|
||||||
@@ -226,7 +226,7 @@ async def test_delivery_counts_tally_each_shape_by_form_and_skip_what_does_not_p
|
|||||||
assert "bogus" not in counts
|
assert "bogus" not in counts
|
||||||
|
|
||||||
|
|
||||||
# ── delivery (milestone 500 step 3) ─────────────────────────────────────
|
# ── delivery ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("key", list(reply_shapes.SHAPES))
|
@pytest.mark.parametrize("key", list(reply_shapes.SHAPES))
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"""Every retrieval point is declared, derived from the CALL SITES (#3431).
|
"""Every retrieval point is declared, derived from the CALL SITES.
|
||||||
|
|
||||||
WHY AST AND NOT GREP, demonstrated rather than asserted. Two of the sources in
|
WHY AST AND NOT GREP, demonstrated rather than asserted. Two of the sources in
|
||||||
this system reach their recorder as `source=SOURCE` through a module-level
|
this system reach their recorder as `source=SOURCE` through a module-level
|
||||||
constant — `wide_net` and `preference_slot` — so a grep for `source="` finds
|
constant — `wide_net` and `preference_slot` — so a grep for `source="` finds
|
||||||
neither. A registry test built on that grep would pass while being blind to
|
neither. A registry test built on that grep would pass while being blind to
|
||||||
two arms, which is the narrowing #3191 warns about: a check that looks
|
two arms — a check that looks
|
||||||
thorough and quietly covers less than it claims.
|
thorough and quietly covers less than it claims.
|
||||||
|
|
||||||
So the extractor below parses each module, resolves module-level string
|
So the extractor below parses each module, resolves module-level string
|
||||||
@@ -35,13 +35,13 @@ from scribe.services.retrieval_surfaces import SURFACES
|
|||||||
SRC = Path(__file__).resolve().parents[1] / "src"
|
SRC = Path(__file__).resolve().parents[1] / "src"
|
||||||
|
|
||||||
# The recorders, plus the one FORWARDER. `rules_payload` takes a `source` and
|
# The recorders, plus the one FORWARDER. `rules_payload` takes a `source` and
|
||||||
# passes it to `record_rule_surfaced` on its caller's behalf (snippet #2858),
|
# passes it to `record_rule_surfaced` on its caller's behalf,
|
||||||
# so its callers are the real declaration site and a scan of recorders alone
|
# so its callers are the real declaration site and a scan of recorders alone
|
||||||
# would miss every ambient rule surfacing.
|
# would miss every ambient rule surfacing.
|
||||||
RECORDERS = {
|
RECORDERS = {
|
||||||
"record_retrieval", "record_surfaced", "record_pulled",
|
"record_retrieval", "record_surfaced", "record_pulled",
|
||||||
"record_rule_surfaced", "record_rule_pulled", "rules_payload",
|
"record_rule_surfaced", "record_rule_pulled", "rules_payload",
|
||||||
# The rulings arm (#4769): its two recorders, and `rulings_for_paths`,
|
# The rulings arm: its two recorders, and `rulings_for_paths`,
|
||||||
# which forwards its caller's `source` the way `rules_payload` does.
|
# which forwards its caller's `source` the way `rules_payload` does.
|
||||||
"record_system_surfaced", "record_system_pulled", "rulings_for_paths",
|
"record_system_surfaced", "record_system_pulled", "rulings_for_paths",
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ def call_sites() -> tuple[dict[str, set[str]], list[str]]:
|
|||||||
|
|
||||||
|
|
||||||
def test_the_extractor_finds_something() -> None:
|
def test_the_extractor_finds_something() -> None:
|
||||||
"""The guard has to be able to fail (rule 167).
|
"""The guard has to be able to fail.
|
||||||
|
|
||||||
An extractor that silently matched nothing would make every assertion
|
An extractor that silently matched nothing would make every assertion
|
||||||
below vacuously true — a green suite proving the opposite of what it
|
below vacuously true — a green suite proving the opposite of what it
|
||||||
@@ -130,7 +130,7 @@ def test_every_unresolved_call_site_is_declared() -> None:
|
|||||||
# EQUALITY, not prefix. The first spelling of this compared against a
|
# EQUALITY, not prefix. The first spelling of this compared against a
|
||||||
# prefix that could never match — the paths begin `scribe/` — so the check
|
# prefix that could never match — the paths begin `scribe/` — so the check
|
||||||
# passed by matching nothing, which is the failure mode a guard is most
|
# passed by matching nothing, which is the failure mode a guard is most
|
||||||
# likely to have and least likely to show (rule 167).
|
# likely to have and least likely to show.
|
||||||
undeclared = sorted({u for u in unresolved if u not in FAN_OUT_SITES})
|
undeclared = sorted({u for u in unresolved if u not in FAN_OUT_SITES})
|
||||||
assert not undeclared, (
|
assert not undeclared, (
|
||||||
"these call sites pass `source` as a value this test cannot resolve, "
|
"these call sites pass `source` as a value this test cannot resolve, "
|
||||||
@@ -157,7 +157,7 @@ def test_every_tunable_surface_is_also_a_registered_point() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_a_quiet_point_says_why() -> None:
|
def test_a_quiet_point_says_why() -> None:
|
||||||
"""A justified silence must carry its justification (#2475).
|
"""A justified silence must carry its justification.
|
||||||
|
|
||||||
Without the reason the reader cannot tell a decision from an oversight,
|
Without the reason the reader cannot tell a decision from an oversight,
|
||||||
which is the whole difference this field exists to record.
|
which is the whole difference this field exists to record.
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
"""Moving a floor, and the reason that has to come with it (#4102).
|
"""Moving a floor, and the reason that has to come with it.
|
||||||
|
|
||||||
WHY THIS EXISTS
|
WHY THIS EXISTS
|
||||||
|
|
||||||
The operator handed the dial to the model: *"the user should be able to touch it
|
The model moves these dials most of the time, and a person can too. Everything
|
||||||
but the model should be the thing handling it 9 times out of 10."* Everything
|
here guards the half of that arrangement people skip — the person still has to
|
||||||
here guards the half of that sentence people skip — the operator still has to be
|
be able to see what was done on their behalf and disagree with it.
|
||||||
able to see what was done on their behalf and disagree with it.
|
|
||||||
|
|
||||||
WHAT THIS PINS
|
WHAT THIS PINS
|
||||||
|
|
||||||
@@ -20,7 +19,7 @@ WHAT THIS PINS
|
|||||||
question anyone asks of a surface behaving oddly.
|
question anyone asks of a surface behaving oddly.
|
||||||
3. **A clamp is reported, never swallowed.** A caller that believes it set 1.4
|
3. **A clamp is reported, never swallowed.** A caller that believes it set 1.4
|
||||||
will read the next telemetry as evidence about a bar that was never in
|
will read the next telemetry as evidence about a bar that was never in
|
||||||
force — the same class of error as #3739, one layer up.
|
force.
|
||||||
4. **An unknown surface is refused before anything is written.** Settings keys
|
4. **An unknown surface is refused before anything is written.** Settings keys
|
||||||
are free-form strings, so a typo'd surface would write a key nothing reads:
|
are free-form strings, so a typo'd surface would write a key nothing reads:
|
||||||
a change that reports success and alters nothing.
|
a change that reports success and alters nothing.
|
||||||
@@ -167,7 +166,7 @@ async def test_an_unknown_actor_is_refused():
|
|||||||
|
|
||||||
|
|
||||||
def test_the_tool_teaches_reading_the_records_not_the_percentile():
|
def test_the_tool_teaches_reading_the_records_not_the_percentile():
|
||||||
"""The contract an agent actually reads (rule 167).
|
"""The contract an agent actually reads.
|
||||||
|
|
||||||
The failure this prevents is a model moving a bar because `near_misses.p90`
|
The failure this prevents is a model moving a bar because `near_misses.p90`
|
||||||
sat close to it. That has been measured pointing the wrong way — 69 declines
|
sat close to it. That has been measured pointing the wrong way — 69 declines
|
||||||
@@ -198,20 +197,19 @@ def test_every_tool_in_the_module_is_registered():
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# ── record_release_defaults / floor_moves_since (#4225) ───────────────────
|
# ── record_release_defaults / floor_moves_since ───────────────────────────
|
||||||
#
|
#
|
||||||
# THE HOLE THESE FILL. This table records dial turns — a person or a model
|
# THE HOLE THESE FILL. This table records dial turns — a person or a model
|
||||||
# choosing a number. It was silent about the other way a floor moves: somebody
|
# choosing a number. The other way a floor moves is somebody editing
|
||||||
# edits `floor_default` in the registry and ships it. That change is invisible
|
# `floor_default` in the registry and shipping it, and without a release row
|
||||||
# to every consumer of this table, and one consumer is `band_hugs_floor`, which
|
# that change is invisible to every consumer of this table — including
|
||||||
# compares a band against a floor and could not notice they came from different
|
# `band_hugs_floor`, which compares a band against a floor and cannot notice
|
||||||
# regimes.
|
# they came from different regimes.
|
||||||
#
|
#
|
||||||
# Measured on the instance that found it: `write_path_rule` went 0.68 -> 0.72
|
# The symptom: a window that spans a shipped default change holds calls made
|
||||||
# on 2026-09-02 as a shipped default, so a 30-day window opening 2026-08-22
|
# under the old bar, and the readout reports the band a negative distance
|
||||||
# held six days of calls made under the old bar. The readout reported the band
|
# "above" its floor — what a two-population comparison looks like when it
|
||||||
# sitting "-0.0216 above" its floor — which is what a two-population comparison
|
# finally says so out loud.
|
||||||
# looks like when it finally says so out loud.
|
|
||||||
|
|
||||||
|
|
||||||
def _release_rows(rows):
|
def _release_rows(rows):
|
||||||
@@ -350,12 +348,10 @@ async def test_a_baseline_is_not_reported_as_a_floor_move():
|
|||||||
# WATCHING. Before an arm's first row there is nothing to move, nothing to
|
# WATCHING. Before an arm's first row there is nothing to move, nothing to
|
||||||
# report, and no way to tell a steady floor from an unrecorded change.
|
# report, and no way to tell a steady floor from an unrecorded change.
|
||||||
#
|
#
|
||||||
# Measured the day the ledger was seeded on this instance: its earliest event
|
# The symptom: a window that opens before an arm's first floor row reports the
|
||||||
# of any kind is 2026-09-17, its `write_path_rule` floor rows begin at
|
# band a negative distance "above" its floor. That is the documented tell of a
|
||||||
# 2026-09-21, and a 30-day window opening 2026-08-22 reported that arm's band
|
# raise inside the window — announced by the check the suspension exists to
|
||||||
# sitting "-0.0208 above" its floor. That negative distance is the documented
|
# stop, because the suspension has nothing to read.
|
||||||
# tell of a raise inside the window — announced by the check the suspension
|
|
||||||
# was built to stop, because the suspension had nothing to read.
|
|
||||||
|
|
||||||
|
|
||||||
def _gap_rows(rows):
|
def _gap_rows(rows):
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ async def test_short_body_skips_semantic_check():
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_semantic_match_when_body_substantial():
|
async def test_semantic_match_when_body_substantial():
|
||||||
# A note blocks only in the copy band (#4306).
|
# A note blocks only in the copy band.
|
||||||
hit = fake_note(id=20, title="Existing", note_type="note")
|
hit = fake_note(id=20, title="Existing", note_type="note")
|
||||||
sem = AsyncMock(return_value=[(0.99, hit)])
|
sem = AsyncMock(return_value=[(0.99, hit)])
|
||||||
with patch("scribe.services.dedup.async_session",
|
with patch("scribe.services.dedup.async_session",
|
||||||
@@ -60,8 +60,8 @@ async def test_semantic_match_when_body_substantial():
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_a_near_note_is_surfaced_not_blocked():
|
async def test_a_near_note_is_surfaced_not_blocked():
|
||||||
"""#4306: sibling notes — the next dev-log, another part of one design —
|
"""Sibling notes — the next dev-log, another part of one design — measure
|
||||||
measured 0.90–0.98, so a match there is shown for the session to judge
|
0.90–0.98, so a match there is shown for the session to judge
|
||||||
instead of refusing the write."""
|
instead of refusing the write."""
|
||||||
from scribe.services.dedup import _NOTE_OVERLAP_FLOOR
|
from scribe.services.dedup import _NOTE_OVERLAP_FLOOR
|
||||||
|
|
||||||
@@ -133,9 +133,9 @@ def test_a_batch_overlap_names_its_record():
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_gate_catches_a_duplicate_hiding_in_a_later_chunk():
|
async def test_gate_catches_a_duplicate_hiding_in_a_later_chunk():
|
||||||
"""The capability #280 adds to the gate: a long candidate that duplicates
|
"""A long candidate that duplicates an existing record in ONE SECTION is
|
||||||
an existing record in ONE SECTION is caught, where the whole-document
|
caught; a whole-document query would dilute exactly the section that
|
||||||
query this replaces diluted exactly the section that mattered. The gate
|
matters. The gate
|
||||||
queries once per chunk and any chunk's hit blocks."""
|
queries once per chunk and any chunk's hit blocks."""
|
||||||
para = ("This section restates an existing decision in enough words to be "
|
para = ("This section restates an existing decision in enough words to be "
|
||||||
"a real paragraph of content for the chunker to keep. ") * 4
|
"a real paragraph of content for the chunker to keep. ") * 4
|
||||||
@@ -202,13 +202,13 @@ def test_duplicate_response_shape():
|
|||||||
assert "update_task" in r["message"]
|
assert "update_task" in r["message"]
|
||||||
|
|
||||||
|
|
||||||
# --- snippet structural identity (#2518) -------------------------------------
|
# --- snippet structural identity ---------------------------------------------
|
||||||
#
|
#
|
||||||
# The gate used to compare a snippet's rendered DOCUMENT, which is mostly prose
|
# A snippet's rendered DOCUMENT is mostly prose about the code, and comparing
|
||||||
# about the code. Measured on the button corpus, that failed in both directions
|
# it fails in both directions at once: two deliberately-parallel variants
|
||||||
# at once: two deliberately-parallel variants were refused at 0.92, while a
|
# refused at 0.92, while a verbatim re-record under a different name scores
|
||||||
# verbatim re-record of one snippet under a different name scored below 0.90 and
|
# below 0.90 and is created. These tests pin the structural signals used
|
||||||
# was created. These tests pin the structural signals that replaced it.
|
# instead.
|
||||||
|
|
||||||
|
|
||||||
def _session_sequence(results):
|
def _session_sequence(results):
|
||||||
@@ -371,7 +371,7 @@ def test_duplicate_response_names_what_matched_for_structural_hits():
|
|||||||
assert "identical code" in r["message"]
|
assert "identical code" in r["message"]
|
||||||
|
|
||||||
|
|
||||||
# --- the generalised report (#2547) ------------------------------------------
|
# --- the generalised report -------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -404,7 +404,7 @@ def test_every_kind_has_a_suggestion_and_none_proposes_merging_notes():
|
|||||||
"""The suggestion is the report's point: what to DO differs by what the
|
"""The suggestion is the report's point: what to DO differs by what the
|
||||||
records are, and 'merge' is only ever the answer for snippets — folding two
|
records are, and 'merge' is only ever the answer for snippets — folding two
|
||||||
notes destroys what each said, which is why consolidated_at was dropped
|
notes destroys what each said, which is why consolidated_at was dropped
|
||||||
rather than built (#2483)."""
|
rather than built."""
|
||||||
from scribe.services.dedup import _KIND_SUGGESTION, _REPORT_KINDS
|
from scribe.services.dedup import _KIND_SUGGESTION, _REPORT_KINDS
|
||||||
|
|
||||||
for kind in _REPORT_KINDS:
|
for kind in _REPORT_KINDS:
|
||||||
@@ -418,7 +418,7 @@ def test_every_kind_has_a_suggestion_and_none_proposes_merging_notes():
|
|||||||
assert "supersedes" in _KIND_SUGGESTION["note"]
|
assert "supersedes" in _KIND_SUGGESTION["note"]
|
||||||
|
|
||||||
|
|
||||||
# ── the plan gate (milestone 415, step 4) ─────────────────────────────────────
|
# ── the plan gate ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Forge adapter contract tests (#2689) — the Gitea implementation against a
|
"""Forge adapter contract tests — the Gitea implementation against a
|
||||||
mocked transport, plus the configuration gate.
|
mocked transport, plus the configuration gate.
|
||||||
|
|
||||||
httpx.MockTransport is the fake forge: the adapter takes an injectable
|
httpx.MockTransport is the fake forge: the adapter takes an injectable
|
||||||
@@ -9,7 +9,7 @@ reproduce.
|
|||||||
|
|
||||||
The most load-bearing tests are the OFF ones: a user with no usable connection
|
The most load-bearing tests are the OFF ones: a user with no usable connection
|
||||||
must get an empty selector, because every consumer treats that as "behave as
|
must get an empty selector, because every consumer treats that as "behave as
|
||||||
if the module didn't exist" (rule #115 — the baseline user has no forge).
|
if the module didn't exist" (the baseline user has no forge).
|
||||||
DB-backed keyring behavior (get_forges: rows, the project pin, the admin-only
|
DB-backed keyring behavior (get_forges: rows, the project pin, the admin-only
|
||||||
env entry) lives in tests/test_integration_forge_keyring.py.
|
env entry) lives in tests/test_integration_forge_keyring.py.
|
||||||
"""
|
"""
|
||||||
@@ -175,9 +175,9 @@ def test_full_values_build_a_github_adapter():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# --- the selector (#2778): host-keyed resolution over a keyring ---------------
|
# --- the selector: host-keyed resolution over a keyring ----------------------
|
||||||
|
|
||||||
def test_empty_selector_is_the_rule_115_baseline():
|
def test_empty_selector_is_the_no_forge_baseline():
|
||||||
selector = ForgeSelector()
|
selector = ForgeSelector()
|
||||||
assert not selector.configured
|
assert not selector.configured
|
||||||
assert selector.resolve(f"{BASE}/alice/widget") is None
|
assert selector.resolve(f"{BASE}/alice/widget") is None
|
||||||
@@ -204,7 +204,7 @@ def test_forge_error_taxonomy_is_catchable_as_one_family():
|
|||||||
|
|
||||||
def test_adapter_contract_surface():
|
def test_adapter_contract_surface():
|
||||||
"""Both adapters implement exactly this surface — the second
|
"""Both adapters implement exactly this surface — the second
|
||||||
implementation is what proves it's a contract (#2693)."""
|
implementation is what proves it's a contract."""
|
||||||
from scribe.services.forge import FORGE_KINDS, GitHubForge
|
from scribe.services.forge import FORGE_KINDS, GitHubForge
|
||||||
|
|
||||||
for cls in (GiteaForge, GitHubForge):
|
for cls in (GiteaForge, GitHubForge):
|
||||||
@@ -222,7 +222,7 @@ def test_forge_routes_registered_at_their_post_2778_homes():
|
|||||||
"""Connections are USER settings; only the webhook secret stays admin.
|
"""Connections are USER settings; only the webhook secret stays admin.
|
||||||
The old instance-wide /api/admin/forge endpoints must be GONE, not
|
The old instance-wide /api/admin/forge endpoints must be GONE, not
|
||||||
coexisting — a legacy config surface that still wrote admin settings
|
coexisting — a legacy config surface that still wrote admin settings
|
||||||
would silently configure nothing (rule #22: no dual path)."""
|
would silently configure nothing (one path, not two)."""
|
||||||
from scribe.app import create_app
|
from scribe.app import create_app
|
||||||
from scribe.routes import admin as admin_routes
|
from scribe.routes import admin as admin_routes
|
||||||
|
|
||||||
@@ -239,11 +239,10 @@ def test_forge_routes_registered_at_their_post_2778_homes():
|
|||||||
|
|
||||||
|
|
||||||
def test_settings_kv_surface_masks_the_webhook_secret():
|
def test_settings_kv_surface_masks_the_webhook_secret():
|
||||||
"""The generic /api/settings dump masked nothing — the admin endpoints'
|
"""The generic /api/settings dump masks secrets too — otherwise the admin
|
||||||
masking was bypassable by reading the raw KV rows (found while wiring the
|
endpoints' masking is bypassable by reading the raw KV rows. The forge
|
||||||
forge token; smtp_password had the same exposure). forge_token left the
|
token lives on keyring rows, not in the KV, so it is deliberately not a
|
||||||
KV with #2778 (keyring rows carry it now), so it is deliberately no
|
secret KEY here."""
|
||||||
longer a secret KEY here."""
|
|
||||||
from scribe.routes.settings import _SECRET_KEYS, _masked
|
from scribe.routes.settings import _SECRET_KEYS, _masked
|
||||||
|
|
||||||
out = _masked({"forge_webhook_secret": "sec", "smtp_password": "pw", "theme": "dark"})
|
out = _masked({"forge_webhook_secret": "sec", "smtp_password": "pw", "theme": "dark"})
|
||||||
@@ -270,7 +269,7 @@ def test_config_has_the_docker_secret_channel():
|
|||||||
assert hasattr(Config, attr)
|
assert hasattr(Config, attr)
|
||||||
|
|
||||||
|
|
||||||
# --- the GitHub adapter (#2693) ----------------------------------------------
|
# --- the GitHub adapter -----------------------------------------------------
|
||||||
# Same contract, second implementation. Where behavior below differs from the
|
# Same contract, second implementation. Where behavior below differs from the
|
||||||
# Gitea tests above, that difference IS the adapter's job: API host mapping,
|
# Gitea tests above, that difference IS the adapter's job: API host mapping,
|
||||||
# Bearer auth, the missing last_commit_sha, the codeload redirect.
|
# Bearer auth, the missing last_commit_sha, the codeload redirect.
|
||||||
|
|||||||
Reference in New Issue
Block a user