docs(6205): lesson_rules comments state the reason, not the instance that taught it
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m10s
CI & Build / Build & push image (push) Canceled after 0s
CI & Build / Python tests (push) Canceled after 1m48s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m10s
CI & Build / Build & push image (push) Canceled after 0s
CI & Build / Python tests (push) Canceled after 1m48s
Issue, milestone, snippet and rule-number citations are removed; the scope docstring states the leak it prevents without naming the milestone that closed it for the direct arms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
"""Lessons point at rules (milestone 440, #4196).
|
"""Lessons point at rules.
|
||||||
|
|
||||||
A lesson is a non-binding record of one situation; a rule is the binding
|
A lesson is a non-binding record of one situation; a rule is the binding
|
||||||
choice for a class of them. This service owns the link between the two — which
|
choice for a class of them. This service owns the link between the two — which
|
||||||
@@ -7,17 +7,18 @@ rule a lesson is an instance of — and both directions of reading it.
|
|||||||
THE STATES (models/lesson_rule_link.py says why each exists):
|
THE STATES (models/lesson_rule_link.py says why each exists):
|
||||||
`suggested` while evidence accumulates, `confirmed` or `rejected` once a
|
`suggested` while evidence accumulates, `confirmed` or `rejected` once a
|
||||||
judgment is made. Every write here is a JUDGMENT, so every write lands as
|
judgment is made. Every write here is a JUDGMENT, so every write lands as
|
||||||
confirmed or rejected; `suggested` rows come from the co-surfacing recorder
|
confirmed or rejected; `suggested` rows come from the co-surfacing recorder,
|
||||||
(#4637), never from a caller naming a rule.
|
never from a caller naming a rule.
|
||||||
|
|
||||||
THE THIRD ANSWER (#4631). "Which rule is this an instance of?" has three
|
THE THIRD ANSWER. "Which rule is this an instance of?" has three
|
||||||
answers, and only two of them are links: a rule named (confirmed), or NO RULE
|
answers, and only two of them are links: a rule named (confirmed), or NO RULE
|
||||||
FITS — a `lesson_no_rule` row carrying the reason. A lesson with neither is
|
FITS — a `lesson_no_rule` row carrying the reason. A lesson with neither is
|
||||||
UNJUDGED, and `list_unjudged` lists those. The three are kept apart because a
|
UNJUDGED, and `list_unjudged` lists those. The three are kept apart because a
|
||||||
lesson that stands alone is the raw material for a rule nobody has written yet
|
lesson that stands alone is the raw material for a rule nobody has written yet
|
||||||
(#4634), and that is unreadable if "nobody looked" means the same thing.
|
(see Convergence below), and that is unreadable if "nobody looked" means the
|
||||||
|
same thing.
|
||||||
|
|
||||||
THE SOFT LINK (#4637). When one hook request puts a lesson and a rule in
|
THE SOFT LINK. When one hook request puts a lesson and a rule in
|
||||||
front of the reader together, `co_surfaced` records the pair as evidence on a
|
front of the reader together, `co_surfaced` records the pair as evidence on a
|
||||||
SUGGESTED link, and once that evidence crosses a bar the next co-arrival asks
|
SUGGESTED link, and once that evidence crosses a bar the next co-arrival asks
|
||||||
the reader to judge it. The evidence counts distinct SITUATIONS, not
|
the reader to judge it. The evidence counts distinct SITUATIONS, not
|
||||||
@@ -25,7 +26,7 @@ arrivals: two records that resemble each other arrive together on every
|
|||||||
similar prompt, and counting each time would measure their similarity over
|
similar prompt, and counting each time would measure their similarity over
|
||||||
and over rather than any relation between them.
|
and over rather than any relation between them.
|
||||||
|
|
||||||
ACL (rule 78). Linking changes what a lesson says about itself, so it needs
|
ACL (services/access.py). Linking changes what a lesson says about itself, so it needs
|
||||||
WRITE on the lesson (`access.can_write_note`, share-aware). It names a rule, so
|
WRITE on the lesson (`access.can_write_note`, share-aware). It names a rule, so
|
||||||
it needs the rule to be one the caller may read — rules are owner-scoped, and
|
it needs the rule to be one the caller may read — rules are owner-scoped, and
|
||||||
`rulebooks._fetch_owned_rule` / `_owned_rules_clause` are that check's two
|
`rulebooks._fetch_owned_rule` / `_owned_rules_clause` are that check's two
|
||||||
@@ -67,8 +68,8 @@ _NO_RULE_NOTE = "no rule fits: "
|
|||||||
# What `rule_judgment` reads on a lesson payload — the three answers.
|
# What `rule_judgment` reads on a lesson payload — the three answers.
|
||||||
LINKED, NO_RULE, UNJUDGED = "linked", "no_rule", "unjudged"
|
LINKED, NO_RULE, UNJUDGED = "linked", "no_rule", "unjudged"
|
||||||
|
|
||||||
# THE SOFT-LINK EVIDENCE BAR (#4637) — defaults, stated as defaults (rules 32,
|
# THE SOFT-LINK EVIDENCE BAR — defaults, stated as defaults: nothing about an
|
||||||
# 115): nothing about this install chose them.
|
# install chose them.
|
||||||
#
|
#
|
||||||
# Three distinct situations before a pair is put to the reader. One is
|
# Three distinct situations before a pair is put to the reader. One is
|
||||||
# coincidence and two is a pattern only by courtesy; three different prompts
|
# coincidence and two is a pattern only by courtesy; three different prompts
|
||||||
@@ -361,7 +362,7 @@ async def attach_lesson_rules(user_id: int, rows: list[dict], *, key: str = "id"
|
|||||||
"""Add `rules` and `rule_judgment` (plus `no_rule` when that is the answer)
|
"""Add `rules` and `rule_judgment` (plus `no_rule` when that is the answer)
|
||||||
to each lesson payload row, in place — a fixed number of queries per page.
|
to each lesson payload row, in place — a fixed number of queries per page.
|
||||||
|
|
||||||
Fail-open (snippet #4286): this decorates a lesson the caller already has,
|
Fail-open: this decorates a lesson the caller already has,
|
||||||
so a failed lookup leaves the keys off and logs, rather than refusing the
|
so a failed lookup leaves the keys off and logs, rather than refusing the
|
||||||
lesson. An absent key reads as "not attached", never as "no rule".
|
lesson. An absent key reads as "not attached", never as "no rule".
|
||||||
"""
|
"""
|
||||||
@@ -470,7 +471,7 @@ async def rule_candidates(
|
|||||||
async def attach_rule_lessons(user_id: int, data: dict, rule_id: int) -> None:
|
async def attach_rule_lessons(user_id: int, data: dict, rule_id: int) -> None:
|
||||||
"""Add `lessons` to a rule payload, in place, when there are any.
|
"""Add `lessons` to a rule payload, in place, when there are any.
|
||||||
|
|
||||||
Present-only, as a rule's `systems` and `relations` are (#2483), and
|
Present-only, as a rule's `systems` and `relations` are, and
|
||||||
fail-open for the reason `attach_lesson_rules` gives.
|
fail-open for the reason `attach_lesson_rules` gives.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
@@ -487,8 +488,8 @@ def situation_key(arm: str, text: str) -> str:
|
|||||||
"""A fingerprint of one situation, so a repeat counts once.
|
"""A fingerprint of one situation, so a repeat counts once.
|
||||||
|
|
||||||
`arm` is part of the key — "p" for a prompt, "w" for a file being written,
|
`arm` is part of the key — "p" for a prompt, "w" for a file being written,
|
||||||
"s" for a session (the rule↔moment signal, milestone 458 step 7), "d"
|
"s" for a session (the rule↔moment signal), "d" for a day (the misfire
|
||||||
for a day (the misfire signal, step 7b, whose door carries no session) —
|
signal, whose door carries no session) —
|
||||||
because each names a different kind of situation and must not collide.
|
because each names a different kind of situation and must not collide.
|
||||||
On the prompt arm the text is the prompt: lowercased, split into word
|
On the prompt arm the text is the prompt: lowercased, split into word
|
||||||
tokens of _MIN_TOKEN or more, de-duplicated and sorted, so the same ask
|
tokens of _MIN_TOKEN or more, de-duplicated and sorted, so the same ask
|
||||||
@@ -657,7 +658,7 @@ async def _co_surfaced(user_id, lesson_ids, rule_ids, arm, situation, project_id
|
|||||||
return _proposal_line(lid, lesson_title, rid, rule.title, rule.kind or "rule", ev)
|
return _proposal_line(lid, lesson_title, rid, rule.title, rule.kind or "rule", ev)
|
||||||
|
|
||||||
|
|
||||||
# ── Reaching a rule through its lessons (#4633) ──────────────────────────────
|
# ── Reaching a rule through its lessons ──────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
async def confirmed_lessons(user_id: int) -> set[int]:
|
async def confirmed_lessons(user_id: int) -> set[int]:
|
||||||
@@ -666,8 +667,8 @@ async def confirmed_lessons(user_id: int) -> set[int]:
|
|||||||
|
|
||||||
CONFIRMED ONLY, and the reason is the soft link's: a suggested link that
|
CONFIRMED ONLY, and the reason is the soft link's: a suggested link that
|
||||||
carried its rule would put the two side by side on every prompt that
|
carried its rule would put the two side by side on every prompt that
|
||||||
found the lesson, which is exactly the co-arrival #4637 counts as
|
found the lesson, which is exactly the co-arrival the soft link counts
|
||||||
evidence — the suggestion would manufacture its own proof.
|
as evidence — the suggestion would manufacture its own proof.
|
||||||
|
|
||||||
Asked first and cheaply, so an arm on an install with no confirmed links
|
Asked first and cheaply, so an arm on an install with no confirmed links
|
||||||
— every install, until someone judges one — never pays for a search.
|
— every install, until someone judges one — never pays for a search.
|
||||||
@@ -695,11 +696,11 @@ async def confirmed_rules_in_scope(
|
|||||||
) -> dict[int, list]:
|
) -> dict[int, list]:
|
||||||
"""{lesson_id: [Rule]} — each lesson's CONFIRMED rules that may surface here.
|
"""{lesson_id: [Rule]} — each lesson's CONFIRMED rules that may surface here.
|
||||||
|
|
||||||
"May surface here" is the rule arms' own scope (milestone 414): a global
|
"May surface here" is the rule arms' own scope: a global rule, or a rule
|
||||||
rule, or a rule of THIS project. A lesson is project-independent and
|
of THIS project. A lesson is project-independent and surfaces everywhere,
|
||||||
surfaces everywhere, so without this a lesson linked to project A's rule
|
so without this a lesson linked to project A's rule would carry that rule
|
||||||
would carry that rule into project B's sessions — the leak milestone 414
|
into project B's sessions — a leak the direct arms' scope prevents,
|
||||||
closed for the direct arms, reopened through a side door.
|
reopened through a side door.
|
||||||
"""
|
"""
|
||||||
from scribe.services.rulebooks import _owned_rules_clause
|
from scribe.services.rulebooks import _owned_rules_clause
|
||||||
|
|
||||||
@@ -727,16 +728,16 @@ async def confirmed_rules_in_scope(
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
# ── Convergence: lessons with no rule that keep landing in one place (#4634) ─
|
# ── Convergence: lessons with no rule that keep landing in one place ────────
|
||||||
#
|
#
|
||||||
# A lesson answered "no rule fits" is a situation nothing binds. One is a
|
# A lesson answered "no rule fits" is a situation nothing binds. One is a
|
||||||
# lesson. Several that resemble each other are what a missing rule looks like
|
# lesson. Several that resemble each other are what a missing rule looks like
|
||||||
# from the outside — the same moment met again and again, each time written
|
# from the outside — the same moment met again and again, each time written
|
||||||
# down as advice. This is noticed at the WRITE, when the newest of them is
|
# down as advice. This is noticed at the WRITE, when the newest of them is
|
||||||
# answered, and never by a sweep or a timer (#4183): the reader is in the
|
# answered, and never by a sweep or a timer: the reader is in the
|
||||||
# situation then, and a nudge arriving anywhere else is one nobody acts on.
|
# situation then, and a nudge arriving anywhere else is one nobody acts on.
|
||||||
#
|
#
|
||||||
# WHAT "RESEMBLE" MEANS (#5193). Not a fixed similarity. Lessons are written
|
# WHAT "RESEMBLE" MEANS. Not a fixed similarity. Lessons are written
|
||||||
# in one register — a sentence of advice and the moment it applies — so an
|
# in one register — a sentence of advice and the moment it applies — so an
|
||||||
# embedder scores any two of them well above two unrelated texts, and a
|
# embedder scores any two of them well above two unrelated texts, and a
|
||||||
# constant bar sits inside that band: every no-rule lesson then "resembles"
|
# constant bar sits inside that band: every no-rule lesson then "resembles"
|
||||||
@@ -750,10 +751,10 @@ async def confirmed_rules_in_scope(
|
|||||||
#
|
#
|
||||||
# And resemblance must be MUTUAL, among every member, not just to the lesson
|
# And resemblance must be MUTUAL, among every member, not just to the lesson
|
||||||
# being answered. A lesson broad enough to stand near many others is a hub,
|
# being answered. A lesson broad enough to stand near many others is a hub,
|
||||||
# and hub-and-spoke is how three unrelated lessons used to make a "group"
|
# and hub-and-spoke lets three unrelated lessons make a "group" through one
|
||||||
# through one general one. A clique is the shape of one situation met again.
|
# general one. A clique is the shape of one situation met again.
|
||||||
#
|
#
|
||||||
# DEFAULTS, stated as defaults (rules 32, 115). Three lessons — the new one
|
# DEFAULTS, stated as defaults. Three lessons — the new one
|
||||||
# and two it resembles — is the smallest group that is a pattern rather than
|
# and two it resembles — is the smallest group that is a pattern rather than
|
||||||
# a pair. One spread above the background is a modest bar for one direction
|
# a pair. One spread above the background is a modest bar for one direction
|
||||||
# alone; the strictness comes from requiring it of every pair in both
|
# alone; the strictness comes from requiring it of every pair in both
|
||||||
@@ -834,7 +835,7 @@ async def convergence_for(user_id: int, lesson_id: int) -> dict | None:
|
|||||||
"""The convergence a newly answered no-rule lesson completes, or None.
|
"""The convergence a newly answered no-rule lesson completes, or None.
|
||||||
|
|
||||||
Fail-open: this decorates a write that already succeeded, so a failed
|
Fail-open: this decorates a write that already succeeded, so a failed
|
||||||
search leaves the response as it was (snippet #4286's reasoning).
|
search leaves the response as it was.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return await _convergence_for(user_id, lesson_id)
|
return await _convergence_for(user_id, lesson_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user