From 6ec78b530ea23fc6d049464fff2d40b4e7e0104a Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 21 Oct 2026 20:43:32 -0400 Subject: [PATCH] docs(6206): webhooks, search, rulebooks routes and retrieval_tuning, note, models/__init__ drop citations routes/search states the derived-facets property rather than the silent whole-corpus bug that motivated it; routes/rulebooks gives the preference restore exception its reasoning without the milestone numbers it cited. Co-Authored-By: Claude Opus 5.5 --- src/scribe/models/__init__.py | 8 ++++---- src/scribe/models/note.py | 10 +++++----- src/scribe/models/retrieval_tuning.py | 10 +++++----- src/scribe/routes/rulebooks.py | 13 ++++++------- src/scribe/routes/search.py | 17 ++++++++--------- src/scribe/routes/webhooks.py | 8 ++++---- 6 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/scribe/models/__init__.py b/src/scribe/models/__init__.py index 01f7ef99..1d287050 100644 --- a/src/scribe/models/__init__.py +++ b/src/scribe/models/__init__.py @@ -8,7 +8,7 @@ from scribe.config import Config # inline dict cannot be recovered from the engine — and a guard that cannot # read the value it guards is a guard that passes forever. _CONNECT_ARGS: dict = { - # A DEADLINE ON ESTABLISHING A CONNECTION (#4181, rule 156). + # A DEADLINE ON ESTABLISHING A CONNECTION. # # asyncpg's `timeout` bounds the CONNECT — the TCP handshake plus session # setup — and nothing else. Against a host whose storage has wedged, that @@ -80,9 +80,9 @@ from scribe.models.canonical_system import CanonicalSystem # noqa: E402, F401 from scribe.models.rulebook import ( # noqa: E402, F401 Rulebook, RulebookTopic, Rule, RuleRelation, rule_moments, rule_systems, ) -# After notes and rules: it foreign-keys both (milestone 440). +# After notes and rules: it foreign-keys both. from scribe.models.lesson_rule_link import LessonNoRule, LessonRuleLink # noqa: E402, F401 -# After rules: it foreign-keys them (milestone 458 step 7). +# After rules: it foreign-keys them. from scribe.models.rule_moment_judgment import RuleMomentJudgment # noqa: E402, F401 from scribe.models.repo_binding import RepoBinding # noqa: E402, F401 from scribe.models.forge_connection import ForgeConnection # noqa: E402, F401 @@ -90,7 +90,7 @@ from scribe.models.code_shape import CodeShape, CodeShapeConsumer, CodeShapeEven from scribe.models.system import System, RecordSystem # noqa: E402, F401 from scribe.models.design_system import DesignSystem, DesignToken # noqa: E402, F401 # After notes, projects and rulebook topics: family canon foreign-keys all -# three (milestone 463). +# three. from scribe.models.family import ( # noqa: E402, F401 FamilyAdoption, FamilyDecision, FamilyIdea, FamilyIdeaPlatform, FamilyIdeaReference, Platform, ProjectPlatform, diff --git a/src/scribe/models/note.py b/src/scribe/models/note.py index 58cda560..613d77e2 100644 --- a/src/scribe/models/note.py +++ b/src/scribe/models/note.py @@ -76,7 +76,7 @@ class Note(Base, TimestampMixin, SoftDeleteMixin): recurrence_next_spawn_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True ) - # The claim — which session is working this task NOW (milestone 381). + # The claim — which session is working this task NOW. # Orthogonal to `status`: status is the work's state and durable, the claim # is a session's attention and dies on read once `claim_touched_at` is past # the lease. Semantics in services/task_claims.py. @@ -93,7 +93,7 @@ class Note(Base, TimestampMixin, SoftDeleteMixin): # snippet — a reusable shape, with its structured fields mirrored in # `data` and its trigger composed into the title (0070) # lesson — a transferable insight, findable by WHEN IT APPLIES rather - # than by topic (milestone 385). Same mirror discipline as a + # than by topic. Same mirror discipline as a # snippet: the trigger lives in `data.when_to_apply` and is # composed into the title and the head of the body, which is # what puts it in the embedded document. A lesson is never a @@ -102,8 +102,8 @@ class Note(Base, TimestampMixin, SoftDeleteMixin): # # DELIBERATELY UNGATED. Unlike `task_kind` there is no CHECK on this column: # migration 0036 added it as plain Text with a server default and nothing - # has constrained it since, so rule 36 has no whitelist to expand when a - # kind is added. The vocabulary that actually decides what a reader can + # has constrained it since, so there is no whitelist to expand when a kind + # is added. The vocabulary that actually decides what a reader can # reach is `services.knowledge._FACETS`; an unrecognised value there # resolves to a filter matching nothing, which is the intended answer to a # typo. @@ -117,7 +117,7 @@ class Note(Base, TimestampMixin, SoftDeleteMixin): # so historical plan-tasks stay writable # Only meaningful when the note is a task (status is not None); ordinary # notes keep the 'work' default and ignore it. Orthogonal to note_type - # (which is the note/entity axis). CHECK notes_task_kind_check (rule 36). + # (which is the note/entity axis). CHECK notes_task_kind_check. task_kind: Mapped[str] = mapped_column(Text, default="work", server_default="work") # Queryable structured fields for typed records — currently snippets, whose # name/language/signature/locations live here so they can be INDEXED. The diff --git a/src/scribe/models/retrieval_tuning.py b/src/scribe/models/retrieval_tuning.py index dff3fe82..2111616a 100644 --- a/src/scribe/models/retrieval_tuning.py +++ b/src/scribe/models/retrieval_tuning.py @@ -7,7 +7,7 @@ from scribe.models import Base class RetrievalTuningEvent(Base): - """One row per change to a retrieval surface's floor or budget (#4102). + """One row per change to a retrieval surface's floor or budget. WHY A TABLE AND NOT JUST THE SETTING @@ -53,8 +53,8 @@ class RetrievalTuningEvent(Base): # did; `tests/test_retrieval_surfaces.py` asserts it rather than trusting it. surface: Mapped[str] = mapped_column(Text, nullable=False) - # "floor" or "budget". Text rather than an enum because rule 36 makes a new - # CHECK value a migration, and this is a young surface that may well grow a + # "floor" or "budget". Text rather than an enum because a new CHECK value is + # a migration, and this is a young surface that may well grow a # third tunable — a constraint bought nothing here except a future migration. dial: Mapped[str] = mapped_column(Text, nullable=False) @@ -75,7 +75,7 @@ class RetrievalTuningEvent(Base): # becomes a formality; the service refuses a blank one. reason: Mapped[str] = mapped_column(Text, nullable=False) - # WHAT SPACE THIS NUMBER WAS MEASURED IN (#4104). A floor is a cosine + # WHAT SPACE THIS NUMBER WAS MEASURED IN. A floor is a cosine # distance in one embedding model's geometry, over documents cut one # particular way — change either and the number describes something that # no longer exists. @@ -111,7 +111,7 @@ class RetrievalTuningEvent(Base): "new_value": self.new_value, "actor": self.actor, "reason": self.reason, - # Null for every row written before #4104, and rendered as + # Null on rows written before the stamp existed, and rendered as # "unstamped" rather than guessed at — see the column comments. "embedding_model": self.embedding_model, "shape_version": self.shape_version, diff --git a/src/scribe/routes/rulebooks.py b/src/scribe/routes/rulebooks.py index a4e754e2..c0db7109 100644 --- a/src/scribe/routes/rulebooks.py +++ b/src/scribe/routes/rulebooks.py @@ -280,19 +280,18 @@ async def get_rule_version(rule_id: int, version_id: int): return jsonify(version.to_dict(include_text=True)) -# NO restore route FOR A RULE, deliberately (milestone 323). A note version +# NO restore route FOR A RULE, deliberately. A note version # can be restored; a binding instruction should not be revertible in one # click. Putting a rewrite back goes through update_rule, which takes its own # snapshot and leaves the undo in the history like any other edit — a silent # revert would erase the only record of why the rewrite happened. # # A PREFERENCE IS THE EXCEPTION, and the route below refuses anything else. -# 323's reasoning turns on the rewrite being the operator's own decision. +# The reasoning above turns on the rewrite being the operator's own decision. # A preference's rewrite is not: the agent makes it mid-work without asking, # which is what the kind is for. Reverting one is a veto over someone else's -# edit rather than an undo of your own, and milestone 399 named the cost of -# making that veto expensive — drift supervised in name only. The safeguard -# 323 actually wanted survives intact, because the restore goes through +# edit rather than an undo of your own, and making that veto expensive leaves +# drift supervised in name only. The safeguard survives intact, because the restore goes through # update_rule too: the rewrite stays in the history with the revert recorded # after it, so the history gains an entry rather than losing one. @@ -369,7 +368,7 @@ async def unrelate_rules(relation_id: int): @login_required async def move_rule(rule_id: int): """Give a rule a new home: {topic_id} makes it global, {project_id} makes - it that project's. The MCP twin is move_rule (rule 33: same names).""" + it that project's. The MCP twin is move_rule, under the same names.""" data = await request.get_json() or {} uid = get_current_user_id() try: @@ -452,7 +451,7 @@ async def create_project_rule(project_id: int): )), 201 -# ── The staleness sweep (milestone 312) ──────────────────────────────── +# ── The staleness sweep ──────────────────────────────────────────────── @rulebooks_bp.get("/rules-due-for-verification") @login_required diff --git a/src/scribe/routes/search.py b/src/scribe/routes/search.py index 4bc8f9f2..d13a2a99 100644 --- a/src/scribe/routes/search.py +++ b/src/scribe/routes/search.py @@ -11,8 +11,8 @@ from scribe.services.embeddings import semantic_search_notes from scribe.services.knowledge import content_type_filters from scribe.services.retrieval_telemetry import record_retrieval -# The interactive floor lives in embeddings.py now, shared with Browse search -# and the list views' semantic `q` — one number, one rationale (#2463). +# The interactive floor lives in embeddings.py, shared with Browse search and +# the list views' semantic `q` — one number, one rationale. search_bp = Blueprint("search", __name__, url_prefix="/api/search") @@ -26,10 +26,9 @@ async def search_route(): return jsonify({"error": "q is required"}), 400 limit = min(request.args.get("limit", 10, type=int), 50) - # Every kind the facet table declares, derived rather than mapped here — - # this route used to know exactly two and read anything else as "no - # filter", so `?content_type=snippets` silently returned the whole corpus - # (#4250). An unknown kind is now a 400 naming the ones that exist: a + # Every kind the facet table declares, derived rather than mapped here, so + # no kind can fall through to "no filter" and silently return the whole + # corpus. An unknown kind is a 400 naming the ones that exist: a # result set is an answer, and it should not be able to answer a question # nobody asked. try: @@ -37,9 +36,9 @@ async def search_route(): except ValueError as exc: return jsonify({"error": str(exc)}), 400 is_task = filters.get("is_task") - # Same association filters the MCP tool takes (#33). Optional, default - # global: this route has NO frontend consumer today (measured 2026-08-08 — - # the web UI searches through /api/knowledge), so it serves API callers, + # Same association filters the MCP tool takes. Optional, default global: + # the web UI searches through /api/knowledge, so this route serves API + # callers, # and an API caller states its scope explicitly. system_id = request.args.get("system_id", type=int) project_id = request.args.get("project_id", type=int) diff --git a/src/scribe/routes/webhooks.py b/src/scribe/routes/webhooks.py index 12ca7dea..b50019db 100644 --- a/src/scribe/routes/webhooks.py +++ b/src/scribe/routes/webhooks.py @@ -1,4 +1,4 @@ -"""Forge push webhook — drift flagging at the moment the repo moves (#2691). +"""Forge push webhook — drift flagging at the moment the repo moves. The forge POSTs here on every push; changed paths are matched against recorded snippet locations (through repo bindings) and matched verdicts get an @@ -14,7 +14,7 @@ Contract with the forge's delivery loop: - No secret configured → 404: the endpoint doesn't exist until an operator creates it. Bad signature → 401: that's a caller problem worth signaling. - A PROCESSING failure returns 200 with ``{"ok": false}`` and drops a - WARNING + AppLog row (the #2663 canary pattern): repeated 5xx responses + WARNING + AppLog row (the telemetry canary pattern): repeated 5xx responses make forges mark deliveries failed and operators disable the hook, which would silently turn the feature off — the exact failure mode this milestone exists to prevent. @@ -55,7 +55,7 @@ def signature_ok(secret: str, body: bytes, signature: str) -> bool: def delivered_signature(headers) -> str: """The HMAC hex a delivery carries, whichever forge sent it: Gitea's X-Gitea-Signature verbatim, or GitHub's X-Hub-Signature-256 minus its - "sha256=" scheme prefix (#2693). Empty when neither header is present.""" + "sha256=" scheme prefix. Empty when neither header is present.""" return headers.get("X-Gitea-Signature", "") or headers.get( "X-Hub-Signature-256", "" ).removeprefix("sha256=") @@ -104,7 +104,7 @@ async def forge_push(): "forge push %s flagged %d snippet(s) for recheck", head[:12], flagged ) # A push is exactly when the coverage number goes stale — recompute it - # off the delivery path (#2692). Fire-and-forget: the forge's delivery + # off the delivery path. Fire-and-forget: the forge's delivery # loop must not wait on an archive download, and a failure is a # WARNING from spawn(), never a failed delivery. This also SEEDS the # cache on a webhook-configured instance — no manual first refresh.