align(4909b): every write with a path gets its rule check, whether or not prior art matched
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m4s
CI & Build / Python tests (push) Successful in 1m56s
CI & Build / Build & push image (push) Successful in 22s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m4s
CI & Build / Python tests (push) Successful in 1m56s
CI & Build / Build & push image (push) Successful in 22s
The write-path rule arm ran only past the notes guard: a write whose code matched no snippet, issue or lesson returned before any rule was searched. It now runs before that guard, on `code or path`, and a write that matched nothing but a rule still returns the rule lines. Operator ruling (2026-10-05): yes. A rule about the file being written does not depend on a snippet resembling it. Cost: one more rule search per write that matched no prior art, with its own embedding (the query is `code or path`, not the notes arm's concept query, so the per-request memo does not cover it). Plus the preference slot from (c) on those writes. Expected telemetry shift, to be read on its own once this reaches main: write_path_rule calls rise toward write_path's count (14 days to 2026-10-22: write_path_rule 4,634 against write_path 3,786 — the notes arm skips trivial payloads, the rule arm does not, so the rule arm will now exceed it). Its zero-result share rises too, since the newly covered writes are the ones that matched nothing in the notes corpus. Tests: the gate test is inverted (a write with no prior art still gets its rule check); the design-only write now runs the rule arm on its path; two notes-arm tests read the write_path row instead of any row, since the rule arm writes its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1458,14 +1458,10 @@ async def build_write_path_hint(
|
||||
logger.warning("write-time derive check failed", exc_info=True)
|
||||
staleness: list[str] = []
|
||||
|
||||
# The standing-rule arm further down is deliberately left on the far side
|
||||
# of the guard below: it runs a SEMANTIC search, and moving it above would
|
||||
# run an embedding query on every write in the session.
|
||||
# The design arm is decided HERE, above the guard, so a UI write
|
||||
# that matched no prior art still carries it — and it returns on its own
|
||||
# rather than joining the guard's condition, because joining it would let
|
||||
# a design line switch the standing-rule arm below on for writes where it
|
||||
# has never run, moving that arm's call distribution under its floor.
|
||||
# rather than joining the guard's condition: a design line alone is not a
|
||||
# reason to render the prior-art menu's frame.
|
||||
design_text, design_dedup = await _design_arm(
|
||||
user_id, project_id, path, set(exclude_derive or []),
|
||||
)
|
||||
@@ -1476,7 +1472,62 @@ async def build_write_path_hint(
|
||||
user_id, project_id, [path], seen=seen_ruling_systems,
|
||||
source="rulings_write_path",
|
||||
)
|
||||
if not staleness and not synced and not menu and not suggested and not divergence and not derive:
|
||||
# ── Standing rules that may apply here ───────────────────────────────
|
||||
#
|
||||
# A SUGGESTION, not a binding surface, and the distinction is the design:
|
||||
# a rule BINDS by being tagged to an area the project works in,
|
||||
# resolved deterministically at enter_project. This arm reaches for
|
||||
# something weaker and still useful — a conditional rule whose trigger
|
||||
# resembles what is being written, noticed at the moment it is relevant
|
||||
# rather than by being resident in every session.
|
||||
#
|
||||
# EVERY RULE IS ELIGIBLE, including one already named this session: a
|
||||
# rule the session was told about an hour ago is not a rule in front of
|
||||
# the reader now.
|
||||
#
|
||||
# The arm itself is `retrieval_pipeline.run_rule_arm`: the band, the
|
||||
# fresh/repeat split, the unconditional call row and the fresh-only
|
||||
# surfacing rows are written there once for all three
|
||||
# rule arms. What is decided HERE is only what makes this moment this
|
||||
# moment — the query is the code being written (or the path, for an edit
|
||||
# with no body), and a line says the rule may apply "here".
|
||||
#
|
||||
# EVERY WRITE WITH A PATH GETS THE CHECK, whether or not prior art matched:
|
||||
# a rule about the file being written does not depend on a snippet
|
||||
# resembling it. It runs before the notes guard below for that reason,
|
||||
# at the price of one more search per write; its query, `code or path`,
|
||||
# differs from the notes arm's concept query, so it is its own embedding.
|
||||
#
|
||||
# Fails open like every other arm: a rule hint must never break a write.
|
||||
# The direct band, then a rule reached through a linked lesson,
|
||||
# composed by `_rule_moment` exactly as the other two rule builders are.
|
||||
rule_lines: list[str] = []
|
||||
rule_ids: list[int] = []
|
||||
shown_rule_ids: list[int] = []
|
||||
checkpoint: dict = {}
|
||||
try:
|
||||
result = await _rule_moment(
|
||||
rp.WRITE_PATH_RULE,
|
||||
rp.RuleMoment(
|
||||
user_id=user_id, query=code or path, project_id=project_id,
|
||||
where="here", checkpoint_where="here",
|
||||
exclude=frozenset(exclude_rule_ids or []),
|
||||
held=frozenset(held_rule_ids or []),
|
||||
),
|
||||
floor=cfg["rule_threshold"], budget=cfg["rule_top_k"],
|
||||
checkpoint_floor=cfg["checkpoint_threshold"],
|
||||
)
|
||||
rule_lines = result.lines
|
||||
rule_ids.extend(result.rule_ids)
|
||||
# Every rule line the band showed, repeats included — what the
|
||||
# soft-link recorder pairs with this response's lessons.
|
||||
shown_rule_ids = result.shown_rule_ids
|
||||
checkpoint = result.checkpoint
|
||||
except Exception:
|
||||
logger.debug("write-path rule arm failed", exc_info=True)
|
||||
|
||||
if (not staleness and not synced and not menu and not suggested
|
||||
and not divergence and not derive and not rule_lines):
|
||||
if design_text or rulings["lines"]:
|
||||
return {
|
||||
**empty,
|
||||
@@ -1612,52 +1663,7 @@ async def build_write_path_hint(
|
||||
user_id=user_id, note_ids=ids, source=arm, project_id=project_id,
|
||||
)
|
||||
|
||||
# ── Standing rules that may apply here ───────────────────────────────
|
||||
#
|
||||
# A SUGGESTION, not a binding surface, and the distinction is the design:
|
||||
# a rule BINDS by being tagged to an area the project works in,
|
||||
# resolved deterministically at enter_project. This arm reaches for
|
||||
# something weaker and still useful — a conditional rule whose trigger
|
||||
# resembles what is being written, noticed at the moment it is relevant
|
||||
# rather than by being resident in every session.
|
||||
#
|
||||
# EVERY RULE IS ELIGIBLE, including one already named this session: a
|
||||
# rule the session was told about an hour ago is not a rule in front of
|
||||
# the reader now.
|
||||
#
|
||||
# The arm itself is `retrieval_pipeline.run_rule_arm`: the band, the
|
||||
# fresh/repeat split, the unconditional call row and the fresh-only
|
||||
# surfacing rows are written there once for all three
|
||||
# rule arms. What is decided HERE is only what makes this moment this
|
||||
# moment — the query is the code being written (or the path, for an edit
|
||||
# with no body), and a line says the rule may apply "here".
|
||||
#
|
||||
# Fails open like every other arm: a rule hint must never break a write.
|
||||
# The direct band, then a rule reached through a linked lesson,
|
||||
# composed by `_rule_moment` exactly as the other two rule builders are.
|
||||
rule_ids: list[int] = []
|
||||
shown_rule_ids: list[int] = []
|
||||
checkpoint: dict = {}
|
||||
try:
|
||||
result = await _rule_moment(
|
||||
rp.WRITE_PATH_RULE,
|
||||
rp.RuleMoment(
|
||||
user_id=user_id, query=code or path, project_id=project_id,
|
||||
where="here", checkpoint_where="here",
|
||||
exclude=frozenset(exclude_rule_ids or []),
|
||||
held=frozenset(held_rule_ids or []),
|
||||
),
|
||||
floor=cfg["rule_threshold"], budget=cfg["rule_top_k"],
|
||||
checkpoint_floor=cfg["checkpoint_threshold"],
|
||||
)
|
||||
lines.extend(result.lines)
|
||||
rule_ids.extend(result.rule_ids)
|
||||
# Every rule line the band showed, repeats included — what the
|
||||
# soft-link recorder pairs with this response's lessons.
|
||||
shown_rule_ids = result.shown_rule_ids
|
||||
checkpoint = result.checkpoint
|
||||
except Exception:
|
||||
logger.debug("write-path rule arm failed", exc_info=True)
|
||||
lines.extend(rule_lines)
|
||||
|
||||
# A lesson and a rule on this one response: the soft-link
|
||||
# recorder counts the pair, keyed on the FILE — every edit to one file is
|
||||
|
||||
Reference in New Issue
Block a user