docs(6205): retrieval_pipeline comments state the reason, not the instance that taught it
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m8s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 23s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m8s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 23s
Record ids, milestone and step numbers, and the stories of copies drifting apart are removed; each invariant and slot is justified in present tense. The rule-band probe table stays as calibration. Section headers drop their milestone tags. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,30 +1,26 @@
|
||||
"""One retrieval pipeline: a ranked surface is a spec, not a hand-copied arm.
|
||||
|
||||
Milestone 456. Every ranked rule surface used to write out the same sequence
|
||||
by hand — search, band, split fresh from repeats, log the call before any early
|
||||
return, reserve a slot, render, record what was surfaced — once per arm, each
|
||||
modelled on the nearest sibling. The copies drifted, and the defects that
|
||||
mattered were fixed one copy at a time (#3497, #3750, #3752): "this arm shipped
|
||||
with the same defect inherited from its sibling" was a comment in the code.
|
||||
The invariants were held together by a parametrised test over three copies
|
||||
rather than by there being one implementation.
|
||||
Every ranked rule surface runs the same sequence — search, band, split fresh
|
||||
from repeats, log the call before any early return, reserve a slot, render,
|
||||
record what was surfaced. Written out by hand once per arm, each modelled on
|
||||
the nearest sibling, the copies drift, and a defect gets fixed in one copy
|
||||
and inherited by the next.
|
||||
|
||||
Here the sequence is written ONCE (`run_rule_arm`), and an arm is a `RuleArm`:
|
||||
which source it records under, and which of the stages it takes. The
|
||||
differences between arms that used to be buried in their copies are now four
|
||||
booleans on one screen — and the ones that are accidents rather than
|
||||
decisions are milestone 456 step 7's to settle, one at a time, on evidence.
|
||||
differences between arms are four booleans on one screen, where an accident
|
||||
is visible as one.
|
||||
|
||||
THE INVARIANTS, each written once:
|
||||
|
||||
- the call row is written before any early return (#3497), because a call
|
||||
that found nothing is the only evidence a bar is too high;
|
||||
- the call row is written before any early return, because a call that
|
||||
found nothing is the only evidence a bar is too high;
|
||||
- the call row's results and the surfacing rows are both the FRESH cut, so
|
||||
the two tables describe the same delivery (#3668, #3752);
|
||||
the two tables describe the same delivery;
|
||||
- the session ledger decides how a repeat RENDERS, never whether it is
|
||||
retrieved (#3750, #4101);
|
||||
- band before dedup (#3851), so "you were shown this" never changes what
|
||||
counts as relevant;
|
||||
retrieved;
|
||||
- band before dedup, so "you were shown this" never changes what counts
|
||||
as relevant;
|
||||
- telemetry never costs the reader a line, and the arm as a whole fails
|
||||
open: a recall aid may never break the prompt, the command or the write.
|
||||
|
||||
@@ -43,14 +39,14 @@ from scribe.services.lessons import LESSON_NOTE_TYPE, claim_line
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ── Shared rendering and ranking helpers (moved from plugin_context) ──────
|
||||
# ── Shared rendering and ranking helpers ─────────────────────────────────
|
||||
|
||||
# MEASURED, NOT REASONED — and the reasoning it replaced was wrong (#3851).
|
||||
# MEASURED, NOT REASONED.
|
||||
#
|
||||
# The prediction was that rules would rank SHARPLY, because `rule_document()`
|
||||
# The natural prediction is that rules rank SHARPLY, because `rule_document()`
|
||||
# shapes them the way snippets are shaped — trigger in the embedded title and
|
||||
# again above the body — and note 2485 measured snippets separating their top
|
||||
# hit by 0.153 while every other kind managed 0.010–0.023.
|
||||
# again above the body — and snippets separate their top hit by ~0.15 while
|
||||
# every other kind manages 0.010–0.023.
|
||||
#
|
||||
# They do not. Three probes against real act queries, scored by the same
|
||||
# embedding the arms use:
|
||||
@@ -60,21 +56,20 @@ logger = logging.getLogger(__name__)
|
||||
# a bare-owner-filter query top 0.656, gap to second 0.020
|
||||
#
|
||||
# That is dev-log territory, not snippet territory: rules arrive as a
|
||||
# tightly-packed block. Shaping alone did not buy separation, which is worth
|
||||
# recording because the opposite was the natural inference from 2485.
|
||||
# tightly-packed block. Shaping alone does not buy separation.
|
||||
#
|
||||
# So the band is narrow BECAUSE the corpus is flat. At 0.10 — the notes
|
||||
# menu's value — every one of the top eight on the push probe falls inside,
|
||||
# including a CI-registry rule and another project's branch policy. At 0.05
|
||||
# it admits roughly three ranks, which is the span where the scores are still
|
||||
# saying something. 2485's Finding 3 is the standing caveat: no band value
|
||||
# fixes a tie, and if rules ever rank as flat as dev-logs did this control
|
||||
# stops working and the answer is a reranker (#1038), not a smaller number.
|
||||
# saying something. The standing caveat: no band value fixes a tie, and if
|
||||
# rules ever rank as flat as dev-logs this control stops working and the
|
||||
# answer is a reranker, not a smaller number.
|
||||
_RULEHINT_BAND = 0.05
|
||||
|
||||
|
||||
def _rule_band(hits: list) -> list:
|
||||
"""The top hit, plus every hit within `_RULEHINT_BAND` of it (#3851).
|
||||
"""The top hit, plus every hit within `_RULEHINT_BAND` of it.
|
||||
|
||||
The instrument that lets an act surface a SET without inventing one: a
|
||||
fixed k fills its slots whether or not anything deserves them, while this
|
||||
@@ -82,8 +77,8 @@ def _rule_band(hits: list) -> list:
|
||||
still shows one and four competing rules show four.
|
||||
|
||||
Sync and pure, and deliberately its own function rather than a comparison
|
||||
written twice — the two act arms are the pair #3497 records drifting apart
|
||||
by being modelled on each other instead of sharing.
|
||||
written twice: the two act arms drift apart when each is modelled on the
|
||||
other instead of sharing.
|
||||
|
||||
Takes `(score, rule)` pairs already ordered best-first, as both
|
||||
`semantic_search_*` helpers return them.
|
||||
@@ -100,9 +95,7 @@ def checkpoint_for(
|
||||
"""The one rule, if any, that should STOP this act rather than annotate it.
|
||||
|
||||
Sync and pure so it can be read against a fixed list of hits without a
|
||||
database — the two arms share it for the reason `_rule_band` is shared:
|
||||
#3497 is the record of these two drifting apart by being modelled on each
|
||||
other instead of sharing one function.
|
||||
database — the two arms share it for the reason `_rule_band` is shared.
|
||||
|
||||
FOUR CONDITIONS, AND EACH IS A DIFFERENT KIND OF WRONG IT PREVENTS.
|
||||
|
||||
@@ -119,7 +112,7 @@ def checkpoint_for(
|
||||
names it.
|
||||
|
||||
3. THE SESSION HAS NOT OPENED IT. `held` is observable — a PostToolUse
|
||||
hook watches for the `get_rule` call (#4100) — so this is a recorded
|
||||
hook watches for the `get_rule` call — so this is a recorded
|
||||
event and not a model's self-report about its own context. A session
|
||||
that read the rule has already had the thing the checkpoint exists to
|
||||
produce, and stopping it again would be punishing the behaviour being
|
||||
@@ -131,9 +124,8 @@ def checkpoint_for(
|
||||
a stop justified by a score nobody claimed.
|
||||
|
||||
Returns a dict rather than a rule or a tuple. Widening a tuple is an
|
||||
interface change to every unpack site that the compiler does not report
|
||||
(#4207, learned the expensive way in this same milestone's first week), and
|
||||
this value crosses a JSON boundary into a shell script where a missing
|
||||
interface change to every unpack site that the compiler does not report,
|
||||
and this value crosses a JSON boundary into a shell script where a missing
|
||||
field is a silently empty variable.
|
||||
"""
|
||||
if not kept or floor <= 0:
|
||||
@@ -154,7 +146,7 @@ def checkpoint_for(
|
||||
}
|
||||
# RENDERED HERE, at the one call site, rather than by each 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 exact pair. The
|
||||
# what a stop says. The
|
||||
# text stays a separate function so it can be read and tested without a
|
||||
# rule object, but nothing outside this line decides whether to call it.
|
||||
found["reason"] = checkpoint_reason(found)
|
||||
@@ -164,7 +156,7 @@ def checkpoint_for(
|
||||
def checkpoint_reason(checkpoint: dict) -> str:
|
||||
"""The text the agent reads INSTEAD of running the act.
|
||||
|
||||
Written as a practice rather than a prohibition (rule 165): it says what
|
||||
Written as a practice rather than a prohibition: it says what
|
||||
to do and why it is worth doing, not what is forbidden. The act is not
|
||||
wrong — nothing here knows whether it is — and saying so plainly is what
|
||||
keeps the stop from reading as an accusation the system is in no position
|
||||
@@ -195,10 +187,10 @@ def checkpoint_reason(checkpoint: dict) -> str:
|
||||
def _rule_hint_line(
|
||||
rule, *, where: str, seen: bool, held: bool = False, compact: bool = False,
|
||||
) -> str:
|
||||
"""One rule hint line — both arms, both tails, both kinds (#3750, #3849).
|
||||
"""One rule hint line — both arms, both tails, both kinds.
|
||||
|
||||
THREE INDEPENDENT AXES SINCE #3851. `compact` joins `kind` and `seen`, and
|
||||
like them it reads none of the others: it says how much ROOM this line
|
||||
THREE INDEPENDENT AXES. `compact` joins `kind` and `seen`, and like them
|
||||
it reads none of the others: it says how much ROOM this line
|
||||
gets, which is a fact about its rank among today's hits rather than about
|
||||
the rule. A compact line is still a full claim that the rule may apply —
|
||||
it simply cites the rule instead of quoting its trigger. Crucially it
|
||||
@@ -206,13 +198,11 @@ def _rule_hint_line(
|
||||
independent: shortening a line must not decide what it says about whether
|
||||
the session is holding the rule.
|
||||
|
||||
WHY THE LATER LINES ARE QUIETER. Measured at #3851: a full line runs ~143
|
||||
tokens once the trigger is rendered, and #3855 tripled trigger lengths
|
||||
across the corpus, so five full lines cost ~568 tokens before every Bash
|
||||
call. Top-full-plus-references costs ~299 — about 2x the old single line,
|
||||
for four more rules. The budget argument that once justified a single slot
|
||||
was real; what it actually forbids is four voices at full volume, not four
|
||||
voices.
|
||||
WHY THE LATER LINES ARE QUIETER. A full line runs ~143 tokens once the
|
||||
trigger is rendered, so five full lines cost ~568 tokens before every Bash
|
||||
call. Top-full-plus-references costs ~299 — about 2x a single full line,
|
||||
for four more rules. The token budget forbids four voices at full volume,
|
||||
not four voices.
|
||||
|
||||
The top hit keeps the full rendering because it is the one the ranker is
|
||||
most confident about, and a reader who acts on exactly one line should
|
||||
@@ -222,32 +212,31 @@ def _rule_hint_line(
|
||||
and neither reads the other. A preference and a rule differ in force; a
|
||||
repeat and a first surfacing differ in whether the session already holds
|
||||
the line. Those are unrelated facts, and keeping them unrelated in the
|
||||
code is what stopped the second kind from reopening the repeat question.
|
||||
code is what lets a new kind join without reopening the repeat question.
|
||||
|
||||
ONE FUNCTION BECAUSE THE TAILS MUST NOT DRIFT. The two arms phrase their
|
||||
heads differently ("may apply here" vs "may apply to this Bash call") and
|
||||
that difference is deliberate. Everything after it must not differ, and
|
||||
#3497's history is that the pre-tool arm inherited a defect from its
|
||||
sibling by being modelled on it rather than sharing with it. Two copies of
|
||||
a two-branch string is how one branch gets fixed and the other does not.
|
||||
that difference is deliberate. Everything after it must not differ: an
|
||||
arm modelled on its sibling rather than sharing with it inherits its
|
||||
defects, and two copies of a two-branch string is how one branch gets
|
||||
fixed and the other does not.
|
||||
|
||||
WHY A REPEAT GETS A LINE AT ALL. Both arms used to drop a hit whose id was
|
||||
already on the session's exclusion ledger and emit nothing. That is correct
|
||||
only while the session still HOLDS what it was told, and a compaction
|
||||
breaks exactly that: the earlier injection is summarized away while the id
|
||||
stays on the ledger, so the rule is absent from context AND unreachable for
|
||||
the rest of the session (#3749 closes the compaction half; this closes the
|
||||
ordinary half, where a session simply stops holding a line it read an hour
|
||||
ago).
|
||||
WHY A REPEAT GETS A LINE AT ALL. Dropping a hit whose id is already on the
|
||||
session's exclusion ledger is correct only while the session still HOLDS
|
||||
what it was told, and a compaction breaks exactly that: the earlier
|
||||
injection is summarized away while the id stays on the ledger, so the rule
|
||||
would be absent from context AND unreachable for the rest of the session.
|
||||
The same holds, more slowly, for a session that simply stops holding a
|
||||
line it read an hour ago.
|
||||
|
||||
Only ONE CLAUSE of the original line is false on a repeat — the claim that
|
||||
Only ONE CLAUSE of a first-surfacing line is false on a repeat — the claim that
|
||||
the rule is not in the session's loaded set. So only that clause changes.
|
||||
Title, trigger and pull pointer are identical either way, the statement is
|
||||
never injected either way, and a repeat therefore costs the same ~40 tokens
|
||||
as a first surfacing and no more.
|
||||
|
||||
DELIBERATELY NOT ASKING THE SESSION WHETHER IT HOLDS THE RULE. A model
|
||||
asked "do you still hold rule 156?" will say yes, and the claim is
|
||||
asked "do you still hold rule N?" will say yes, and the claim is
|
||||
unverifiable self-report about its own context. The answer is also not
|
||||
needed: the line is cheap enough to always emit and carries its own remedy
|
||||
in both branches. Removing the question removes the fragility rather than
|
||||
@@ -265,8 +254,8 @@ def _rule_hint_line(
|
||||
# A reader skimming an injected block gets one word to place the register \u2014
|
||||
# so it is the SECOND word that moves, and it is the word naming force.
|
||||
# Everything structural after it is identical, so the three kinds read as
|
||||
# one set rather than three formats (milestone 385's step 5 writes the
|
||||
# lesson voice against these two; they are one paragraph, not three).
|
||||
# one set rather than three formats (the lesson voice is written against
|
||||
# these two; they are one paragraph, not three).
|
||||
noun = "Preference" if preference else "Standing rule"
|
||||
# The only other place force is asserted. A rule's line tells the reader
|
||||
# not to dismiss it unread, because dismissing a rule unread is how the
|
||||
@@ -277,21 +266,21 @@ def _rule_hint_line(
|
||||
"for how this has been done before" if preference
|
||||
else "before deciding it does not apply"
|
||||
)
|
||||
# THREE STATES, BECAUSE TWO OF THEM WERE BEING TOLD THE SAME LIE (#4100).
|
||||
# THREE STATES, BECAUSE TWO OF THEM MUST NOT BE TOLD THE SAME THING.
|
||||
#
|
||||
# `seen` means an arm NAMED this rule earlier. It does not mean the session
|
||||
# read it — the line is a teaser, and a teaser skimmed past leaves nothing
|
||||
# behind, least of all after a compaction summarises the turn it arrived
|
||||
# in. "You saw it earlier this session" asserted something about the
|
||||
# reader's context that the server had no way to know.
|
||||
# in. "You saw it earlier this session" would assert something about the
|
||||
# reader's context that the server has no way to know.
|
||||
#
|
||||
# `held` is the observable half: a PostToolUse hook watches for the
|
||||
# `get_rule` call itself, so this is a recorded EVENT rather than a claim.
|
||||
# That distinction is what keeps the non-goal above intact — the objection
|
||||
# was to asking a model about its own context, not to noticing what it did.
|
||||
#
|
||||
# The middle state is the honest one and the one that was missing: named,
|
||||
# not opened. It gets the full invitation, because a session that skipped
|
||||
# The middle state is the honest one: named, not opened. It gets the full
|
||||
# invitation, because a session that skipped
|
||||
# the teaser is in almost the same position as one that never saw it.
|
||||
if held:
|
||||
tail = (
|
||||
@@ -310,17 +299,17 @@ def _rule_hint_line(
|
||||
)
|
||||
if compact:
|
||||
# THE TRIGGER GOES; THE TAIL STAYS. Only one of the two is expensive \u2014
|
||||
# a trigger runs 300-400 characters after #3855, the tail about 100 \u2014
|
||||
# a trigger runs 300-400 characters, the tail about 100 \u2014
|
||||
# so dropping the trigger is nearly the whole saving and dropping the
|
||||
# tail would be mostly sacrifice.
|
||||
#
|
||||
# It would also destroy the one thing #3750 exists to say. The tail is
|
||||
# what tells a reader whether they were already told this and may no
|
||||
# longer be holding it, and that is the entire difference they can act
|
||||
# on; a reference with no tail reads as a first surfacing whether it is
|
||||
# one or not. This branch shipped without it for one commit and
|
||||
# It would also destroy the one thing a repeat line exists to say. The
|
||||
# tail is what tells a reader whether they were already told this and
|
||||
# may no longer be holding it, and that is the entire difference they
|
||||
# can act on; a reference with no tail reads as a first surfacing
|
||||
# whether it is one or not.
|
||||
# test_a_rule_the_session_already_holds_is_referenced_not_re_offered
|
||||
# caught it, which is the guard working exactly as #3750 intended.
|
||||
# pins this.
|
||||
return (
|
||||
f"Also \u2014 {noun.lower()} \u201c{rule.title}\u201d. {tail}"
|
||||
)
|
||||
@@ -331,14 +320,14 @@ def _rule_hint_line(
|
||||
)
|
||||
|
||||
|
||||
# ── What a spec declares about itself (milestone 456 step 5) ─────────────
|
||||
# ── What a spec declares about itself ────────────────────────────────────
|
||||
#
|
||||
# A ranked surface used to be described in three hand-kept lists besides its
|
||||
# code: its tuning pair in `retrieval_surfaces.SURFACES`, its row in
|
||||
# A ranked surface is described in three places besides its code: its tuning
|
||||
# pair in `retrieval_surfaces.SURFACES`, its row in
|
||||
# `retrieval_registry.POINTS`, and — for a rule surface — its membership in
|
||||
# `rule_usage.RANKED_SOURCES`. Tests existed to make the four agree. Now the
|
||||
# spec carries all of it, and the three lists are read off the specs, so an
|
||||
# arm added here is tunable, measured and counted by construction.
|
||||
# `rule_usage.RANKED_SOURCES`. The spec carries all of it, and the three lists
|
||||
# are read off the specs, so an arm added here is tunable, measured and
|
||||
# counted by construction rather than by four hand-kept lists agreeing.
|
||||
#
|
||||
# THE CLASSES LIVE HERE, NOT BESIDE THE LISTS, for the import graph: the
|
||||
# registry, the tuning table and the usage counter all read the specs, so the
|
||||
@@ -353,8 +342,8 @@ class Surface:
|
||||
rendered by the tuning tool and the Settings UI. A floor cannot be moved
|
||||
sensibly by anyone, model or human, who does not know what the query is, what
|
||||
corpus it runs against, or how often it costs something. Those three facts
|
||||
are exactly what separates these arms from each other, and they were
|
||||
previously recoverable only by reading `plugin_context.py`.
|
||||
are exactly what separates these arms from each other, and without them
|
||||
here they are recoverable only by reading `plugin_context.py`.
|
||||
"""
|
||||
|
||||
name: str
|
||||
@@ -375,13 +364,13 @@ class Surface:
|
||||
fires: str
|
||||
measured_model: str = "BAAI/bge-small-en-v1.5"
|
||||
measured_shape: int = 1
|
||||
"""What the SHIPPED defaults above were measured against (#4104).
|
||||
"""What the SHIPPED defaults above were measured against.
|
||||
|
||||
A floor is a distance in one embedding model's geometry, over documents cut
|
||||
one particular way. Either can change, and when one does every number in
|
||||
this table describes something that no longer exists.
|
||||
|
||||
TWO FIELDS, NEVER ONE FUSED STRING (rule 149). A mismatch has to be able to
|
||||
TWO FIELDS, NEVER ONE FUSED STRING. A mismatch has to be able to
|
||||
say WHICH half moved: a new embedding model and a re-cut document shape
|
||||
invalidate the same numbers for different reasons and call for different
|
||||
responses. `"<model>@<n>"` could only report that something changed, which
|
||||
@@ -401,11 +390,11 @@ class Surface:
|
||||
budget_falls_back_to: str = ""
|
||||
"""A budget key to inherit when this surface has none of its own set.
|
||||
|
||||
Only `write_path` uses it, and only because it USED to share auto-inject's
|
||||
`top_k` outright. Giving it a key without this would silently reset the
|
||||
budget of every install that had tuned the shared one — a behaviour change
|
||||
delivered as a default, which is the shape of regression nobody reports
|
||||
because nothing looks broken.
|
||||
Only `write_path` uses it: it inherits auto-inject's `top_k` until its own
|
||||
key is set. Without the fallback, an install that tuned the shared budget
|
||||
would have it silently reset — a behaviour change delivered as a default,
|
||||
which is the shape of regression nobody reports because nothing looks
|
||||
broken.
|
||||
"""
|
||||
|
||||
|
||||
@@ -419,7 +408,7 @@ class Declared:
|
||||
"""One line, for an agent reading a warning that names this source."""
|
||||
|
||||
quiet_because: str = ""
|
||||
"""Set when silence over an active window is correct, saying why (#2475)."""
|
||||
"""Set when silence over an active window is correct, saying why."""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -442,7 +431,7 @@ class RuleArm:
|
||||
"""The telemetry `source`. MUST equal the registry and SURFACES key."""
|
||||
|
||||
band: bool
|
||||
"""Keep only the hits within `_RULEHINT_BAND` of the top one (#3851)."""
|
||||
"""Keep only the hits within `_RULEHINT_BAND` of the top one."""
|
||||
|
||||
compact_tail: bool
|
||||
"""Lines after the first cite the rule instead of quoting its trigger."""
|
||||
@@ -451,13 +440,13 @@ class RuleArm:
|
||||
"""The top hit may hold the act for one read (`checkpoint_for`)."""
|
||||
|
||||
preference_slot: bool
|
||||
"""Reserve one line for a preference that lost the ranking (#3894)."""
|
||||
"""Reserve one line for a preference that lost the ranking."""
|
||||
|
||||
kind: str | None = None
|
||||
"""Ask the ranker for one record kind only ("preference"), or every kind."""
|
||||
|
||||
stop_only: bool = False
|
||||
"""The door shows nothing but the stop (milestone 458's reply moment).
|
||||
"""The door shows nothing but the stop (the reply moment).
|
||||
|
||||
At the end of a turn there is no context to annotate: a hit either holds
|
||||
the reply for one read or reaches nobody. So only the checkpoint's rule is
|
||||
@@ -471,9 +460,9 @@ class RuleArm:
|
||||
declared: Declared | None = None
|
||||
|
||||
|
||||
# Today's differences, reproduced exactly (milestone 456 step 2). Whether the
|
||||
# prompt arm should band, and whether the act arms should reserve a
|
||||
# preference, are step 7's questions — not settled by this refactor.
|
||||
# The arms differ on purpose where these flags differ. Whether the prompt arm
|
||||
# should band, and whether the act arms should reserve a preference, are open
|
||||
# questions to settle on telemetry, not by symmetry.
|
||||
PROMPT_RULE = RuleArm(
|
||||
"prompt_rule", band=False, compact_tail=False, checkpoint=False,
|
||||
preference_slot=True,
|
||||
@@ -525,7 +514,7 @@ WRITE_PATH_RULE = RuleArm(
|
||||
REPLY_RULE = RuleArm(
|
||||
"reply_rule", band=False, compact_tail=False, checkpoint=True,
|
||||
preference_slot=False, stop_only=True,
|
||||
# THE REPLY BACKSTOP (milestone 458, folded in from 456 step 8). Its floor
|
||||
# THE REPLY BACKSTOP. Its floor
|
||||
# is a STOP bar, not a hint bar: at the end of a turn nothing can be shown
|
||||
# beside the reply, so a hit either holds the reply for one read or says
|
||||
# nothing. Hence a default at the checkpoint's level and a budget of one —
|
||||
@@ -582,7 +571,7 @@ class RuleMoment:
|
||||
query: str
|
||||
project_id: int | None
|
||||
"""The bound project, 0 or None when unbound. Logged as given, searched as
|
||||
`project_id or None` — global rules plus that project's own (milestone 414)."""
|
||||
`project_id or None` — global rules plus that project's own."""
|
||||
|
||||
where: str = ""
|
||||
"""How a line names the moment: "here", "to this Bash call", …"""
|
||||
@@ -592,7 +581,7 @@ class RuleMoment:
|
||||
"""Rules the session has been shown — rendered as repeats, not hidden."""
|
||||
|
||||
held: frozenset[int] = frozenset()
|
||||
"""Rules the session has OPENED (#4100)."""
|
||||
"""Rules the session has OPENED."""
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -624,7 +613,7 @@ def _telemetry_failed(source: str) -> None:
|
||||
The recorders are called DIRECTLY at each site, inside their own try,
|
||||
rather than through a wrapper taking the recorder as an argument: the
|
||||
registry test finds recorder call sites by their name, and a wrapper would
|
||||
hide every site in this module from it (#3191's narrowing).
|
||||
hide every site in this module from it.
|
||||
"""
|
||||
logger.debug("retrieval telemetry failed for %s", source, exc_info=True)
|
||||
|
||||
@@ -637,7 +626,7 @@ async def _ranked(
|
||||
|
||||
Returns (hits, kept, fresh): what the ranker returned, what the band kept,
|
||||
and the kept hits this session has not been shown. The row is written
|
||||
HERE, before any caller can return early (#3497), with `results` the fresh
|
||||
HERE, before any caller can return early, with `results` the fresh
|
||||
cut and `suppressed` counting both the band and the ledger, so a long
|
||||
session's all-repeat zero reads apart from a bar that turned everything
|
||||
away.
|
||||
@@ -681,7 +670,7 @@ async def _ranked(
|
||||
async def _reserve_slot_for_preference(
|
||||
io: RuleIO, moment: RuleMoment, hits: list, *, floor: float,
|
||||
) -> tuple[list, int | None]:
|
||||
"""Guarantee a preference one slot, if one clears the bar (#3894).
|
||||
"""Guarantee a preference one slot, if one clears the bar.
|
||||
|
||||
THE ASYMMETRY THIS EXISTS FOR. A rule and a preference are not equally
|
||||
served by a shared score contest, because their losses are not equal:
|
||||
@@ -697,33 +686,32 @@ async def _reserve_slot_for_preference(
|
||||
over the one whose loss is total, and it does so INVISIBLY: the rule that
|
||||
won is a legitimate hit, the telemetry looks healthy, and the only symptom
|
||||
is a preference that quietly never arrives. `reuse_slot` exists for the
|
||||
same shape one corpus over (#2463), where snippets kept losing to project
|
||||
records that merely resembled the query.
|
||||
same shape one corpus over, where snippets lose to project records that
|
||||
merely resemble the query.
|
||||
|
||||
THE SLOT BUYS POSITION, NOT A LOWER BAR — same as `reuse_slot`, which also
|
||||
reserves at `cfg["threshold"]`. A weak preference cannot buy the slot, so
|
||||
silence stays the default and the reserved line is never worse than the
|
||||
ones it sits beside. If `preference_slot` later shows a stream of
|
||||
near-misses, `best_available_id` (#3807) names which preference was
|
||||
near-misses, `best_available_id` names which preference was
|
||||
refused and a separate bar becomes an argument with evidence behind it
|
||||
rather than a knob added on a guess.
|
||||
|
||||
LEDGER REPEATS STILL COUNT AS REPRESENTED. A preference already on the
|
||||
session's ledger occupies the slot rather than being skipped for a fresh
|
||||
one: it is still rendered (#3750), just with the tail that says so, and a
|
||||
one: it is still rendered, just with the tail that says so, and a
|
||||
preference is the kind of record where being reminded is the point.
|
||||
|
||||
Returns the possibly-extended hit list, and the id the slot spent — the
|
||||
caller needs that to keep each source's surfaced set matching its own log
|
||||
row (#3668), since the slot logs under its own name.
|
||||
row, since the slot logs under its own name.
|
||||
"""
|
||||
if any(rule.kind == "preference" for _s, rule in hits):
|
||||
return hits, None
|
||||
|
||||
# ITS OWN SOURCE, and both sides of the trade logged. #2463's own finding
|
||||
# is the warning rather than the precedent here: the hit that slot pushed
|
||||
# OUT was in retrieval_logs while the query that pushed it out was not, so
|
||||
# the slot could never be judged against what it displaced.
|
||||
# ITS OWN SOURCE, and both sides of the trade logged. A slot whose query
|
||||
# is not logged beside the hit it pushed out can never be judged against
|
||||
# what it displaced.
|
||||
found, _kept, _fresh = await _ranked(
|
||||
io, moment, source=PREFERENCE_SLOT_SOURCE, floor=floor, limit=1,
|
||||
kind="preference",
|
||||
@@ -748,11 +736,10 @@ async def _reserve_slot_for_preference(
|
||||
# ledger rather than taste. A displaced hit was RETURNED by the general
|
||||
# search and is sitting in that call's `retrieval_logs` row, but would not
|
||||
# have been shown — so `prompt_rule`'s surfaced set would stop matching
|
||||
# its own log row, and #3668's identity would break for a reason nothing
|
||||
# in the data explains. That identity is the cheapest true statement
|
||||
# available about this pair of tables, and milestone #379 is what it costs
|
||||
# to lose it: five steps planned against a gap that was two counters
|
||||
# disagreeing, not a write path dropping rows.
|
||||
# its own log row, and that identity would break for a reason nothing in
|
||||
# the data explains. It is the cheapest true statement available about
|
||||
# this pair of tables; without it, two counters disagreeing reads as a
|
||||
# write path dropping rows.
|
||||
#
|
||||
# The price is one extra line, only when the general search already filled
|
||||
# the limit AND a preference cleared the bar without placing. Cheap, and
|
||||
@@ -788,7 +775,7 @@ async def run_rule_arm(
|
||||
shown, _slot_id = await _reserve_slot_for_preference(
|
||||
io, moment, shown, floor=floor,
|
||||
)
|
||||
# `shown`, not `fresh` (#3750): a call whose only hit is a repeat
|
||||
# `shown`, not `fresh`: a call whose only hit is a repeat
|
||||
# still has something to say, it just says it differently.
|
||||
if not shown:
|
||||
return RuleResult()
|
||||
@@ -798,16 +785,16 @@ async def run_rule_arm(
|
||||
rule, where=moment.where,
|
||||
seen=rule.id in moment.exclude,
|
||||
held=rule.id in moment.held,
|
||||
# Rank decides volume (#3851): the ranker's best guess gets
|
||||
# Rank decides volume: the ranker's best guess gets
|
||||
# the trigger, the rest get cited.
|
||||
compact=arm.compact_tail and idx > 0,
|
||||
)
|
||||
for idx, (_score, rule) in enumerate(shown)
|
||||
]
|
||||
# FRESH-ONLY (#3752): a repeat is a rendering decision, not a
|
||||
# retrieval outcome, and counting it would inflate the denominator
|
||||
# FRESH-ONLY: a repeat is a rendering decision, not a retrieval
|
||||
# outcome, and counting it would inflate the denominator
|
||||
# pull_through is read from. It is also exactly what this call's own
|
||||
# row recorded, so the two tables stay equal (#3668). RANKED, not
|
||||
# row recorded, so the two tables stay equal. RANKED, not
|
||||
# ambient: the source is in `rule_usage.RANKED_SOURCES`.
|
||||
rule_ids = [rule.id for _score, rule in fresh]
|
||||
source = arm.source
|
||||
@@ -838,20 +825,19 @@ async def run_rule_arm(
|
||||
return RuleResult()
|
||||
|
||||
|
||||
# ── The notes corpus (milestone 456 step 4) ──────────────────────────────
|
||||
# ── The notes corpus ─────────────────────────────────────────────────────
|
||||
#
|
||||
# The same stages over the other corpus: search, withhold what this response
|
||||
# already lists, split fresh from repeats, log the call before any early
|
||||
# return, band, reserved slots, surfacing rows. Two arms take them — the
|
||||
# prompt's menu (`auto_inject`) and the write path's match by meaning
|
||||
# (`write_path`) — and they differed only in which kinds they ask for, whether
|
||||
# (`write_path`) — and they differ only in which kinds they ask for, whether
|
||||
# a menu sits above them in the same response, and which slots they reserve.
|
||||
#
|
||||
# ONE DIFFERENCE FROM THE RULE ARMS IS KEPT, ON PURPOSE, FOR STEP 7. A notes
|
||||
# arm logs the cut BEFORE its band; a rule arm logs after it. #2085 chose the
|
||||
# first for notes (the log row is the candidate set a floor is tuned against,
|
||||
# the surfacing rows are what the reader saw) and #3851 the second for rules.
|
||||
# Both are recorded decisions, so this refactor reproduces both.
|
||||
# ONE DIFFERENCE FROM THE RULE ARMS IS KEPT, ON PURPOSE. A notes arm logs the
|
||||
# cut BEFORE its band; a rule arm logs after it. For notes the log row is the
|
||||
# candidate set a floor is tuned against and the surfacing rows are what the
|
||||
# reader saw; for rules the band is part of the cut being judged.
|
||||
#
|
||||
# What stays OUTSIDE: every lookup. The records named by number, the snippets
|
||||
# recorded at a path, the rulings, the design system and the shape ledger
|
||||
@@ -860,7 +846,7 @@ async def run_rule_arm(
|
||||
|
||||
# Margin gate: drop any hit more than this far below the top hit's score, so a
|
||||
# single strong match doesn't drag in a wall of barely-passing neighbours.
|
||||
# Twice the rule band, which is narrow because the rule corpus is flat (#3851).
|
||||
# Twice the rule band, which is narrow because the rule corpus is flat.
|
||||
_NOTE_BAND = 0.10
|
||||
|
||||
|
||||
@@ -877,9 +863,8 @@ def _record_kind(note) -> str:
|
||||
|
||||
A TASK ALSO CARRIES ITS STATUS, because for that kind alone the line is
|
||||
read as a claim about live work. A finished step and an open one rendered
|
||||
identically is not a cosmetic gap: a done step was cited as a milestone's
|
||||
open one on the strength of a line exactly like this, which carries an id,
|
||||
a kind and a title and said nothing about where the work stood (#4154).
|
||||
identically is not a cosmetic gap: a line carrying only an id, a kind and
|
||||
a title invites a reader to cite a done step as open work.
|
||||
Only for tasks — a note or a snippet has no status to be wrong about.
|
||||
"""
|
||||
if note.is_task:
|
||||
@@ -895,22 +880,22 @@ def _record_kind(note) -> str:
|
||||
return note.note_type or "note"
|
||||
|
||||
|
||||
# WHAT A MENU LINE CARRIES (#4364): the record's NAME, its kind and System,
|
||||
# WHAT A MENU LINE CARRIES: the record's NAME, its kind and System,
|
||||
# and the WHOLE passage that matched. Metadata plus the evidence, rather than a
|
||||
# title asked to be both.
|
||||
#
|
||||
# The name, not the title. A snippet's or lesson's title is `name — when it
|
||||
# applies` by construction (`embeddings.trigger_title`), because that join is
|
||||
# what makes it rank on its situation. That is an EMBEDDING shape, and rendered
|
||||
# as a menu line it ran to 1,500+ characters — the trigger paragraph spent
|
||||
# as a menu line it runs to 1,500+ characters — the trigger paragraph spent
|
||||
# again on every line, and again on every repeat. The trigger still arrives
|
||||
# when it is what matched: it is in the chunk, and `_menu_passage` hands it
|
||||
# over when the title was the whole match.
|
||||
#
|
||||
# The whole passage, not 200 characters of it. The search already chose the
|
||||
# chunk that matched; the old cut kept its head and tail, and the head is the
|
||||
# title every chunk is prefixed with — so the reader got the title twice and
|
||||
# lost the middle, which is where the match was (lesson #4248). A chunk is at
|
||||
# The whole passage, not a head-and-tail cut of it. The search already chose
|
||||
# the chunk that matched, and its head is the title every chunk is prefixed
|
||||
# with — so a cut gives the reader the title twice and loses the middle,
|
||||
# which is where the match was. A chunk is at
|
||||
# most ~1.4 KB (`embeddings._CHUNK_CHAR_BUDGET`), and it is shown once: a
|
||||
# repeat is a one-line pointer (`_menu_seen_line`), not a second copy.
|
||||
|
||||
@@ -928,7 +913,7 @@ def _menu_name(title: str | None, note_type: str | None, data=None, body: str |
|
||||
from scribe.services.embeddings import untrigger_title
|
||||
from scribe.services.lessons import lesson_trigger
|
||||
trigger = lesson_trigger(SimpleNamespace(data=data, body=body or ""))
|
||||
# One line even when the stored name is a story (#4797).
|
||||
# One line even when the stored name is a story.
|
||||
return claim_line(untrigger_title(title, trigger).strip() or title)
|
||||
return title
|
||||
|
||||
@@ -972,16 +957,16 @@ def menu_entry(
|
||||
) -> list[str]:
|
||||
"""One record on a notes menu: its line, and what sits under it.
|
||||
|
||||
The prompt menu's two blocks — records named by number and records ranked
|
||||
by meaning — used to write this out twice, differing only in what they
|
||||
pass: a ranked line carries its score, a named one does not, and each puts
|
||||
its own text under the line (the matched passage, or the record's opening).
|
||||
Shared by the prompt menu's two blocks — records named by number and
|
||||
records ranked by meaning — which differ only in what they pass: a ranked
|
||||
line carries its score, a named one does not, and each puts its own text
|
||||
under the line (the matched passage, or the record's opening).
|
||||
|
||||
A repeat is a POINTER, not a copy (#4364). The record is in this session's
|
||||
A repeat is a POINTER, not a copy. The record is in this session's
|
||||
context already — the ledger is cleared at compaction, so "seen" stays true
|
||||
— and re-rendering it spent its whole line again for nothing.
|
||||
— and re-rendering it would spend its whole line again for nothing.
|
||||
|
||||
A superseded record is DEMOTED, not removed (#278), so one can still reach
|
||||
A superseded record is DEMOTED, not removed, so one can still reach
|
||||
a menu, and when it does the reader has to be told: an agent handed stale
|
||||
material with nothing marking it acts on it with full confidence.
|
||||
"""
|
||||
@@ -1047,7 +1032,7 @@ class NoteArm:
|
||||
note_type: tuple[str, ...] | None = None
|
||||
task_kind: str | None = None
|
||||
include_global_kinds: bool = True
|
||||
"""Lessons are project-independent (#3730), so they join the candidate set
|
||||
"""Lessons are project-independent, so they join the candidate set
|
||||
from wherever they were learned."""
|
||||
|
||||
scope: str = "browse"
|
||||
@@ -1058,7 +1043,7 @@ class NoteArm:
|
||||
withholds: bool = False
|
||||
"""A menu sits above this arm in the same response, and what it lists is
|
||||
kept out of the search (`exclude_ids`) — same-call duplication, which is a
|
||||
different claim from the session ledger (#4101)."""
|
||||
different claim from the session ledger."""
|
||||
|
||||
tuning: Surface | None = None
|
||||
declared: Declared | None = None
|
||||
@@ -1078,11 +1063,11 @@ AUTO_INJECT = NoteArm(
|
||||
),
|
||||
declared=Declared("the notes menu offered at the prompt boundary"),
|
||||
)
|
||||
# Snippets AND recorded experience (#2246): an issue saying "we tried this and
|
||||
# it deadlocked" is prior art for the code about to be written. `task_kind`
|
||||
# Snippets AND recorded experience: an issue saying "we tried this and it
|
||||
# deadlocked" is prior art for the code about to be written. `task_kind`
|
||||
# keeps the open to-do list out — a task resembles the code and answers
|
||||
# nothing. Lessons too (milestone 385 step 5): the arm is kind-FILTERED, so a
|
||||
# kind absent here is unreachable, not merely outranked (#3702). No reserved
|
||||
# nothing. Lessons too: the arm is kind-FILTERED, so a kind absent here is
|
||||
# unreachable, not merely outranked. No reserved
|
||||
# slot: this arm fires before every Write and Edit, and the field is already
|
||||
# narrow enough that the 200:1 dilution a slot answers does not happen.
|
||||
WRITE_PATH = NoteArm(
|
||||
@@ -1153,7 +1138,7 @@ class NoteMoment:
|
||||
"""Searched and logged as `project_id or None`."""
|
||||
|
||||
seen: frozenset[int] = frozenset()
|
||||
"""The session ledger: rendered as a pointer, never withheld (#4101)."""
|
||||
"""The session ledger: rendered as a pointer, never withheld."""
|
||||
|
||||
named: frozenset[int] = frozenset()
|
||||
"""Records this response shows by LOOKUP (named by number). Shown in their
|
||||
@@ -1192,8 +1177,7 @@ def _note_band(hits: list) -> list:
|
||||
|
||||
Computed over ALL hits, repeats included: the band measures distance from
|
||||
the top SCORE, and letting the ledger move that cutoff would make "you
|
||||
were shown this" change what counts as relevant (#3851's axis
|
||||
independence).
|
||||
were shown this" change what counts as relevant.
|
||||
"""
|
||||
if not hits:
|
||||
return []
|
||||
@@ -1207,11 +1191,11 @@ async def _reserve_note_slot(
|
||||
) -> tuple[list, int | None]:
|
||||
"""Guarantee `slot.kinds` one line, if one clears the arm's own bar.
|
||||
|
||||
WHY A SLOT AT ALL (#2246, milestone 385 step 5). Ranking by raw cosine is
|
||||
blind to what KIND of record answers what kind of ask, and the corpus
|
||||
makes that fatal: Scribe's project records are about software work, so a
|
||||
task about building a helper outranks the snippet that IS one. Snippets
|
||||
were ~0.5% of the corpus when this was measured; no floor fixes 200:1. A
|
||||
WHY A SLOT AT ALL. Ranking by raw cosine is blind to what KIND of record
|
||||
answers what kind of ask, and the corpus makes that fatal: Scribe's
|
||||
project records are about software work, so a task about building a
|
||||
helper outranks the snippet that IS one. Snippets can be ~0.5% of a
|
||||
corpus, and no floor fixes 200:1. A
|
||||
lesson crowded out is worse still — it exists only to be met at the moment
|
||||
it applies, so the arm that surfaces it IS its delivery.
|
||||
|
||||
@@ -1219,11 +1203,11 @@ async def _reserve_note_slot(
|
||||
floor and is NOT held to the band (the top score is the very thing these
|
||||
kinds lose to), so a weak record cannot buy the line.
|
||||
|
||||
ITS OWN SOURCE, from the first deploy. A guarantee has to be falsifiable,
|
||||
and the hit a slot pushed out sits in the arm's row while the query that
|
||||
pushed it out would otherwise be nowhere (#2463).
|
||||
ITS OWN SOURCE. A guarantee has to be falsifiable, and the hit a slot
|
||||
pushed out sits in the arm's row while the query that pushed it out would
|
||||
otherwise be nowhere.
|
||||
|
||||
THE LEDGER IS NOT AN EXCLUSION HERE EITHER (#4101), but this call's own
|
||||
THE LEDGER IS NOT AN EXCLUSION HERE EITHER, but this call's own
|
||||
menu is: a record already on it must not be shown twice, while one shown
|
||||
in an EARLIER call is exactly what a slot may spend itself on — a snippet
|
||||
relevant then and now is the reuse case, not a duplicate of it.
|
||||
@@ -1270,7 +1254,7 @@ async def _reserve_note_slot(
|
||||
return menu, None
|
||||
|
||||
slot_id = int(placed[0][1].id)
|
||||
# FRESH ONLY, matching the row above: a ledger repeat is rendered (#4101)
|
||||
# FRESH ONLY, matching the row above: a ledger repeat is rendered
|
||||
# but is not a new surfacing.
|
||||
if slot.books_own and slot_id not in shown:
|
||||
try:
|
||||
@@ -1283,7 +1267,7 @@ async def _reserve_note_slot(
|
||||
if not slot.evicts:
|
||||
# IT EXTENDS, IT NEVER DISPLACES, siding with `preference_slot`: a
|
||||
# displaced hit sits in the arm's row, and evicting it would make the
|
||||
# two tables disagree about the same call (#3668). And a record that
|
||||
# two tables disagree about the same call. And a record that
|
||||
# does not bind should not throw a better-scoring one off the menu.
|
||||
return menu + placed, slot_id
|
||||
# Take the LAST line, never the first: the strongest overall hit is still
|
||||
@@ -1316,15 +1300,15 @@ async def run_note_arm(
|
||||
kwargs["exclude_ids"] = set(moment.in_menu - scored)
|
||||
answered = await io.search(moment.user_id, moment.query, **kwargs)
|
||||
hits = [(s, n) for s, n in answered if int(n.id) not in moment.in_menu]
|
||||
# WHAT THIS ARM WITHHELD AFTER THE SEARCH ANSWERED (#3739). The score
|
||||
# the search reports is measured BEFORE that drop, so a record already
|
||||
# WHAT THIS ARM WITHHELD AFTER THE SEARCH ANSWERED. The score the
|
||||
# search reports is measured BEFORE that drop, so a record already
|
||||
# listed in this response could be logged as one the BAR turned away
|
||||
# — live proof once read a "rejection" at 0.822 against a lowest
|
||||
# acceptance of 0.6857. Whenever this removed anything, the honest
|
||||
# best-available is null: "not measured on this call".
|
||||
# — a "rejection" scoring well above the lowest acceptance. Whenever
|
||||
# this removed anything, the honest best-available is null: "not
|
||||
# measured on this call".
|
||||
withheld = len(answered) - len(hits)
|
||||
hits = hits[:budget]
|
||||
# `results=fresh` and `suppressed` together (#3752): a rendered repeat
|
||||
# `results=fresh` and `suppressed` together: a rendered repeat
|
||||
# is not a new surfacing, so it is COUNTED rather than reported, and an
|
||||
# all-repeat zero reads apart from a bar nothing cleared. A record
|
||||
# NAMED in this response is counted the same way — shown, but by the
|
||||
@@ -1374,8 +1358,8 @@ async def run_note_arm(
|
||||
result.menu = menu
|
||||
|
||||
# What SURVIVED the band — the menu the reader saw — where the call
|
||||
# row holds the candidate set a floor is tuned against (#2085). FRESH
|
||||
# ONLY, the row's own cut (#4101, #3668). A slot that books its own
|
||||
# row holds the candidate set a floor is tuned against. FRESH ONLY,
|
||||
# the row's own cut. A slot that books its own
|
||||
# line is not this arm's surfacing: counting it twice would leave the
|
||||
# slot's two tables describing different numbers of one event.
|
||||
own = {
|
||||
@@ -1401,7 +1385,7 @@ async def run_note_arm(
|
||||
return NoteResult()
|
||||
|
||||
|
||||
# ── A rule reached through its lessons (milestone 440, #4633) ────────────
|
||||
# ── A rule reached through its lessons ───────────────────────────────────
|
||||
#
|
||||
# A rule's own document is written in the rule's words, which are general by
|
||||
# design; the situations that keep proving it are often closer to what a
|
||||
@@ -1411,11 +1395,10 @@ async def run_note_arm(
|
||||
# It searches the NOTES corpus and answers with rules, which is why it sits
|
||||
# between the two halves of this module.
|
||||
#
|
||||
# ITS OWN SLOT, NOT A RULE SLOT, as a stated default. There was nothing to
|
||||
# measure until links are confirmed (#4632), so the asymmetry decides it: a
|
||||
# ITS OWN SLOT, NOT A RULE SLOT, as a stated default decided by asymmetry: a
|
||||
# via-lesson line that took a rule slot could push out a rule the ranker
|
||||
# matched DIRECTLY, a stronger claim displaced by a weaker one, while an extra
|
||||
# line costs one line. Logged as its own source so it can be judged (#4636).
|
||||
# line costs one line. Logged as its own source so it can be judged.
|
||||
|
||||
VIA_LESSON_SOURCE = "rule_via_lesson"
|
||||
VIA_LESSON = RankedSource(
|
||||
@@ -1493,7 +1476,8 @@ async def run_via_lesson_arm(
|
||||
chosen.append((s, rule, n))
|
||||
if len(chosen) >= VIA_LESSON_LIMIT:
|
||||
break
|
||||
# Logged whenever a search ran, results or not — the #3497 guard. The
|
||||
# Logged whenever a search ran, results or not: a call that found
|
||||
# nothing is the only evidence a bar is too high. The
|
||||
# score is the LESSON's, because the lesson is what was matched; the
|
||||
# best-available id is left off for the same reason, since the row's
|
||||
# results are rules and an id beside them would read as a rule id.
|
||||
@@ -1536,7 +1520,7 @@ async def run_via_lesson_arm(
|
||||
return RuleResult()
|
||||
|
||||
|
||||
# ── The specs, read as lists (milestone 456 step 5) ──────────────────────
|
||||
# ── The specs, read as lists ─────────────────────────────────────────────
|
||||
#
|
||||
# What `retrieval_surfaces.SURFACES`, the ranked rows of
|
||||
# `retrieval_registry.POINTS` and `rule_usage.RANKED_SOURCES` are read from.
|
||||
@@ -1558,20 +1542,20 @@ RULE_RANKED_SOURCES: tuple[str, ...] = (
|
||||
pull can confirm or refute it (`rule_usage.RANKED_SOURCES`)."""
|
||||
|
||||
|
||||
# ── The moment arm (milestone 458) ───────────────────────────────────────
|
||||
# ── The moment arm ───────────────────────────────────────────────────────
|
||||
#
|
||||
# A LOOKUP beside the ranked arms, not one of them. A rule mounted on a moment
|
||||
# arrives because somebody said it belongs there, not because the work's words
|
||||
# resemble it — which is the whole point: a rule about WHEN work is finished
|
||||
# never scores against what is said while finishing it. So there is no score,
|
||||
# no floor and no band, and it writes no retrieval_logs row (the rulings arm's
|
||||
# precedent, #4769): every score-shaped warning would misread a lookup. What it
|
||||
# no floor and no band, and it writes no retrieval_logs row (as the rulings
|
||||
# arm writes none): every score-shaped warning would misread a lookup. What it
|
||||
# records is the surfacing, with the moment beside each row, so a mount's
|
||||
# pull-through can be read per moment.
|
||||
#
|
||||
# It shares everything else with the ranked arms: the line renderer, the
|
||||
# session ledger (a repeat renders as a citation, never hidden — #3750) and
|
||||
# the fresh-only surfacing rows (#3752).
|
||||
# session ledger (a repeat renders as a citation, never hidden) and the
|
||||
# fresh-only surfacing rows.
|
||||
|
||||
MOMENT_RULE_SOURCE = "moment_rule"
|
||||
|
||||
@@ -1608,7 +1592,7 @@ async def run_moment_arm(
|
||||
action that reached it — and every line says both ("at work.deliver,
|
||||
reached by `git push`"), so a moment that fired on the wrong act is
|
||||
visible in the line itself and can be corrected in the session with
|
||||
`unmap_action` (step 2's ruling). Fresh rules come first and carry their
|
||||
`unmap_action`. Fresh rules come first and carry their
|
||||
trigger; a rule the session was already shown is cited, not repeated in
|
||||
full. Fails open to an empty result.
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user