docs(6205): snippets 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 15s
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 22s

Issue, decision, milestone and rule citations are removed; the snippet-mirror
docstring drops the paragraph about its own earlier wording, and the
pointer-model and freshness comments state the design rather than the plan
step that introduced it. Access notes point at services/access.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 20:14:09 -04:00
co-authored by Claude Opus 5.5
parent 5598b307fa
commit 57773c8b55
+66 -74
View File
@@ -11,7 +11,7 @@ has (embeddings, ACL, project/System association, dedup) and, crucially, becomes
eligible for semantic recall the moment it's embedded:
- ``title`` = ``name``. The trigger joins it only in the embedded document
(``embeddings.document_title``, milestone 427).
(``embeddings.document_title``).
- ``tags`` = ``[language, "snippet", *caller_tags]``.
- ``body`` = templated markdown (When to use / Signature / Location, then a
fenced code block, then an optional ``## Notes`` section).
@@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
SNIPPET_NOTE_TYPE = "snippet"
SNIPPET_TAG = "snippet"
# The body section free-text notes live under (#4378). A heading rather than a
# The body section free-text notes live under. A heading rather than a
# `**Notes:**` line because notes are paragraphs, and a heading is the boundary
# `embeddings.chunk_document` splits at.
NOTES_HEADING = "## Notes"
@@ -103,11 +103,11 @@ def resolve_locations(
are the single-location shorthand and apply only when `locations` was not
given — passing both is not a merge, it is the caller having decided.
Extracted because compose_body, create_snippet and the dedup gate must all
read the shorthand the SAME way. They each had their own copy of the
`if locations is None` fallback, which is fine until one of them gains a
rule the others don't — and the gate (#2518) is the one where a disagreement
would mean comparing a location the record won't actually be stored with.
One function because compose_body, create_snippet and the dedup gate must
all read the shorthand the SAME way. Three copies of the
`if locations is None` fallback are fine until one of them gains a rule
the others don't — and the gate is the one where a disagreement would mean
comparing a location the record won't actually be stored with.
"""
if locations is None:
locations = [{"repo": repo, "path": path, "symbol": symbol}]
@@ -138,8 +138,8 @@ def _normalize_merged_from(entries: list | None) -> list[dict]:
Order is history, not sorting — earlier merges stay first, so the list reads
as the sequence of things folded in.
Each entry records WHAT THAT SOURCE CONTRIBUTED, which is what makes un-merge
(#2165) exact. Two problems it solves at once:
Each entry records WHAT THAT SOURCE CONTRIBUTED, which is what makes
un-merge exact. Two problems it solves at once:
- A location can arrive from a source AND genuinely be the survivor's own.
Recording only what the source ADDED means reversing it can never strip a
@@ -200,7 +200,7 @@ def compose_body(
"""Render structured fields into the snippet body markdown. Empty fields are
omitted so the body stays clean.
``notes`` is the free text a snippet had no home for (#4378): why it is
``notes`` is the free text a snippet has no other home for: why it is
shaped this way, what superseded what, the caveats. Without it that prose
went into ``when_to_use`` — the trigger, which is joined onto the title of
EVERY chunk the snippet is embedded as, so a 3 KB write-up there blurred
@@ -371,7 +371,7 @@ _DATA_FIELDS = (
"verification", "provenance",
)
# --- drift check (#2086) -----------------------------------------------------
# --- drift check -------------------------------------------------------------
# A recorded snippet points at a repo · path · symbol that WILL rot: files move,
# symbols get renamed, implementations diverge from the copy stored here. Left
# undetected, the record degrades from "canonical reference" to "confidently
@@ -379,9 +379,9 @@ _DATA_FIELDS = (
# same authority either way.
#
# WHERE THE CHECK RUNS. Not here. Scribe has no checkout of the operator's repos
# and must not acquire one (rule #115 — the instance stays agnostic about where
# code lives; giving the server repo access would make every install a
# credential problem). The agent already has the working tree, so IT does the
# and must not acquire one (the instance stays agnostic about where code
# lives; giving the server repo access would make every install a credential
# problem). The agent already has the working tree, so IT does the
# comparing and reports a verdict; the server's job is to remember the verdict,
# make it queryable, and know when it has expired.
#
@@ -445,10 +445,10 @@ def compose_verification(
out["detail"] = detail.strip()
if (path or "").strip():
out["path"] = path.strip()
# The repo commit the working tree was at when the check ran (#2688). The
# The repo commit the working tree was at when the check ran. The
# code_sha above expires a verdict when the RECORD is edited; this makes
# "the REPO moved on since the check" computable too, once the forge
# integration can compare it against the current head.
# "the REPO moved on since the check" computable too, against the forge's
# current head (`_verdict_still_vouches`).
if (commit_sha or "").strip():
out["commit_sha"] = commit_sha.strip()
return out
@@ -458,12 +458,11 @@ def compose_provenance(*, commit_sha: str, fetched_at: str = "") -> dict | None:
"""Build the `data.provenance` record: which commit the cached body was
read at, and when.
This is the pointer-model half of decision #2686 — the recorded location
is the source of truth for the code and the stored body is a CACHE of it.
Provenance says what that cache is a cache OF, so a reader (and later the
forge fetch, step 5 of milestone 288) can judge staleness instead of
guessing. Absent provenance is valid and means exactly what every snippet
meant before this existed: a body captured by hand at an unknown point.
The pointer model: the recorded location is the source of truth for the
code and the stored body is a CACHE of it. Provenance says what that cache
is a cache OF, so a reader (and the forge fetch at pull time) can judge
staleness instead of guessing. Absent provenance is valid and means a body
captured by hand at an unknown point.
"""
sha = (commit_sha or "").strip()
if not sha:
@@ -493,7 +492,7 @@ def verification_view(note, fields: dict) -> dict:
"detail": stored.get("detail"),
"path": stored.get("path"),
"commit_sha": stored.get("commit_sha"),
# A push touched the recorded location since this verdict (#2691) —
# A push touched the recorded location since this verdict —
# the repo moved under it. Cleared by the next verdict, which builds
# a fresh dict.
"invalidated_by": stored.get("invalidated_by"),
@@ -548,7 +547,7 @@ def compose_data(
# either: the verdict's code_sha expires it on read if the code moved on.
if verification:
out["verification"] = verification
# Also carried: what commit the cached body was read at (#2688). The caller
# Also carried: what commit the cached body was read at. The caller
# owns the live-or-die rule — update_snippet drops it when the code changes
# without a fresh SHA, because keeping it would claim the new body came
# from the old commit.
@@ -565,7 +564,7 @@ def compose_data(
return out
# Past this, a trigger is advised to move its explanation into `notes` (#4378).
# Past this, a trigger is advised to move its explanation into `notes`.
# The trigger is joined onto the title of EVERY chunk (`chunk_document`), so at
# 600 characters it is already over 40% of a 1,400-character chunk — each
# vector is then more about the trigger's prose than about the code or the
@@ -605,14 +604,13 @@ def recompose_data(note) -> dict:
For the GENERIC note door. `update_snippet` composes the mirror itself from
the field set it just merged and never needs this; a plain
`update_note(body=...)` — which the Knowledge feed's editor issues, because
a snippet card there routes to /notes/:id — has no idea the mirror exists,
and left it stale. `snippet_fields` then PREFERS the stale mirror, so the
row reported its old repo/path/symbol to prior-art recall while displaying
its new body: confidently wrong, which is worse than no record (#3128).
a snippet card there routes to /notes/:id — has no idea the mirror exists.
Left stale, the mirror wins: `snippet_fields` PREFERS it, so the row would
report its old repo/path/symbol to prior-art recall while displaying its
new body — confidently wrong, which is worse than no record.
The body is the authority; the mirror is derived. That is already the rule
this file states — it just had no enforcement on the path that bypasses
`update_snippet`.
The body is the authority; the mirror is derived. This is that rule's
enforcement on the path that bypasses `update_snippet`.
`verification` and `provenance` are CARRIED, not recomposed, because
neither is in the body to parse — the same carry `compose_data` does for
@@ -642,15 +640,11 @@ def snippet_fields(note) -> dict:
THE BODY IS THE AUTHORITY; `data` is a mirror derived from it. Every writer
keeps them in step — the snippet service composes the mirror from the field
set it just merged, `update_note` recomposes it when a body reaches the
generic door (#3128), and `backfill_snippet_data` filled the pre-0070 rows
generic door, and `backfill_snippet_data` fills any row without a mirror
at startup. The fallback below is therefore a belt to that braces, not a
second source of truth: it is what a row looks like before the backfill has
run, and it must keep agreeing with the mirror.
(This docstring used to say those rows were "never backfilled". That was
true when 0070 landed and stopped being true when the backfill shipped; it
is corrected here because the sentence read as licence for a stale mirror,
which is exactly the bug #3128 found.)
run, and it must keep agreeing with the mirror. A stale mirror is a bug,
never a state to tolerate.
`code` only ever comes from the body, since `data` doesn't carry it.
"""
@@ -678,7 +672,7 @@ async def backfill_snippet_data(*, batch: int = 500) -> int:
Migration 0070 deliberately left `data` NULL on existing rows, because
*reading* a snippet never needed it — `snippet_fields` falls back to parsing
the body. Querying does: the location reverse lookup (#2083) is a jsonpath
the body. Querying does: the location reverse lookup is a jsonpath
over this column, so a NULL-`data` snippet would be reported as "nothing
recorded here" and the caller would write the helper again — silently worse
than having no reverse lookup at all. Every consumer of the column
@@ -779,8 +773,8 @@ async def create_snippet(
``repo``/``path``/``symbol`` are the one-location shorthand.
``commit_sha`` stamps the body's provenance — the commit the recording
session read the code at (#2688). Optional: absent means what it always
meant, a body captured at an unknown point."""
session read the code at. Optional: absent means a body captured at an
unknown point."""
locations = resolve_locations(repo, path, symbol, locations)
note = await notes_svc.create_note(
user_id,
@@ -807,9 +801,10 @@ async def get_snippet(user_id: int, snippet_id: int):
"""Fetch a snippet by id, or None if it doesn't exist / isn't a snippet /
isn't readable by this user.
Share-aware (rule #78): a fetch by id is an explicit act, so it resolves the
caller's full read scope rather than ownership alone. Without this, a snippet
that a search legitimately surfaced could not then be opened — see #2093."""
Share-aware (services/access.py): a fetch by id is an explicit act, so it
resolves the caller's full read scope rather than ownership alone. Without
this, a snippet that a search legitimately surfaced could not then be
opened."""
result = await notes_svc.get_note_for_user(user_id, snippet_id)
if result is None:
return None
@@ -883,12 +878,12 @@ async def update_snippet(
merged field set back into title/body/tags. Returns the Note, or None if the
id isn't a snippet the caller can see.
``commit_sha`` restamps the body's provenance (#2688). It lives or dies
``commit_sha`` restamps the body's provenance. It lives or dies
with the code: passed → restamped at that commit; code changed without it →
dropped, because keeping it would claim the new body came from the old
commit; code untouched → carried.
Share-aware (rule #47/#78): resolves the read scope, then requires WRITE —
Share-aware (services/access.py): resolves the read scope, then requires WRITE —
so an editor/admin grant lets the holder edit, and a viewer grant does not.
Raises PermissionError when the caller can read but not write, because "not
found" would be a lie about a record they can plainly open. The write itself
@@ -930,7 +925,7 @@ async def update_snippet(
else:
merged_locations = cur["locations"]
# Provenance follows the code (#2688): a fresh SHA restamps it; a code
# Provenance follows the code: a fresh SHA restamps it; a code
# change without one drops it; an edit that leaves the code alone carries
# it. Order matters — the explicit SHA wins even when the code changed,
# because that is precisely the caller saying where the new body came from.
@@ -1037,7 +1032,7 @@ async def record_verification(
# An "ok" verdict at a known commit IS a provenance claim — the checker
# just established that the cached body matches the source there — so
# it restamps. Any other verdict carries what was known: a verdict is
# about the code, not a change to it, and must not erase it (#2688).
# about the code, not a change to it, and must not erase it.
provenance=(
compose_provenance(commit_sha=commit_sha)
if status == VERIFY_OK and (commit_sha or "").strip()
@@ -1047,7 +1042,7 @@ async def record_verification(
return await notes_svc.update_note(note.user_id, snippet_id, data=data)
# --- pull-time freshness (#2690) ---------------------------------------------
# --- pull-time freshness -----------------------------------------------------
# A pull is the moment freshness matters: the reader is about to trust the
# cached body. When the record owner's keyring serves a forge, the pull
# fetches the recorded file and answers the one mechanically-answerable
@@ -1057,12 +1052,11 @@ async def record_verification(
# fetching can honestly deliver, and divergence is reported, not overwritten.
#
# With no forge configured this function attaches NOTHING — the response is
# byte-identical to pre-forge behavior (rule #115's baseline).
# exactly what an install without a forge gets everywhere else.
# Total budget for the in-pull fetch. Tighter than the adapter's own timeout:
# the pull is the moment a session decides whether pulling is worth it
# (#2663's pull-through finding), so a slow forge must cost bounded time and
# then the cache serves.
# the pull is the moment a session decides whether pulling is worth it, so a
# slow forge must cost bounded time and then the cache serves.
PULL_FETCH_BUDGET_S = 2.5
@@ -1099,8 +1093,8 @@ def _verdict_still_vouches(note, fields: dict, fetched_commit_sha: str) -> bool:
"""Does a standing `ok` verdict still speak for this body, at this commit?
Containment (cached code ∈ fetched file) is the fast path, and it is right
for a record kept verbatim. It is WRONG for a deliberately annotated one
(#2782): a record whose job is to say why the shape is what it is carries
for a record kept verbatim. It is WRONG for a deliberately annotated one:
a record whose job is to say why the shape is what it is carries
commentary the source does not, so containment fails forever and the record
reads `diverged` on every pull. That turns the one honest drift signal into
a permanent false positive — and annotation is a sanctioned record style,
@@ -1109,15 +1103,14 @@ def _verdict_still_vouches(note, fields: dict, fetched_commit_sha: str) -> bool:
The escape hatch is the verdict itself. `verify_snippet` is precisely where
a human or agent already judged this body a faithful rendering of that
source, and `verification.commit_sha` records the repo commit they judged
it at — a field whose own docstring (#2688) anticipated this use: "makes
'the REPO moved on since the check' computable, once the forge integration
can compare it against the current head." This is that comparison.
it at, which makes "the REPO moved on since the check" computable. This
is that comparison.
All four conditions, and none is optional:
- the verdict says `ok`;
- it has not EXPIRED — `verification_view` recomputes `code_sha` against
the record's current body, so editing the record retires the verdict;
- it was not INVALIDATED by a push touching the location (#2691);
- it was not INVALIDATED by a push touching the location;
- the file we just fetched is at the very commit the verdict was stamped
at. Any later commit means nobody has judged what is there now.
@@ -1136,10 +1129,9 @@ def _verdict_still_vouches(note, fields: dict, fetched_commit_sha: str) -> bool:
async def attach_live_body(note, data: dict) -> None:
"""Decorate a PULL response with forge-checked freshness (#2690).
"""Decorate a PULL response with forge-checked freshness.
Adds, when (and only when) the record owner's keyring serves a forge
(#2778):
Adds, when (and only when) the record owner's keyring serves a forge:
- ``body_source``: "forge" (confirmed against the source just now) or
"cache" (the stored body, for whatever reason follows)
- ``body_freshness``: "current" | "diverged" | "missing" |
@@ -1156,7 +1148,7 @@ async def attach_live_body(note, data: dict) -> None:
from scribe.services.forge import ForgeError, ForgeNotFound, get_forges
try:
# The OWNER's keyring, honoring the project pin (#2778) — freshness
# The OWNER's keyring, honoring the project pin — freshness
# for a record is checked with its owner's credential, never the
# reader's.
selector = await get_forges(note.user_id, getattr(note, "project_id", None))
@@ -1183,7 +1175,7 @@ async def attach_live_body(note, data: dict) -> None:
return
# Recorded location repos are free-form names ("Scribe"), which can't
# address a forge API — the project's repo BINDING is the identity that
# can (#2691). Try the location string first (it may be a real remote),
# can. Try the location string first (it may be a real remote),
# then fall back to the bindings of the snippet's project.
resolved = selector.resolve(loc["repo"])
if resolved is None and getattr(note, "project_id", None):
@@ -1202,7 +1194,7 @@ async def attach_live_body(note, data: dict) -> None:
stored_prov_sha = (fields.get("provenance") or {}).get("commit_sha") or ""
async def _probe():
# Cached-SHA short-circuit (#2693): provenance names the commit the
# Cached-SHA short-circuit: provenance names the commit the
# cached code was last confirmed at, so one cheap "newest commit
# touching this path" call can prove the file hasn't moved since —
# no content transfer. That economy is what fits pull-time freshness
@@ -1266,7 +1258,7 @@ async def attach_live_body(note, data: dict) -> None:
elif _verdict_still_vouches(note, fields, fetched.commit_sha or ""):
# Containment failed, but an unexpired `ok` verdict stamped at exactly
# this commit already judged this body a faithful rendering of it —
# the annotated-record case (#2782). Trust the judgment over the
# the annotated-record case. Trust the judgment over the
# substring test; `data["verification"]` travels in the same payload,
# so a reader can see the basis rather than take "current" on faith.
data["body_source"] = "forge"
@@ -1276,7 +1268,7 @@ async def attach_live_body(note, data: dict) -> None:
data["body_freshness"] = "diverged"
# --- push-time drift flagging (#2691) ----------------------------------------
# --- push-time drift flagging ------------------------------------------------
def _path_touches(recorded: str, changed: str) -> bool:
"""The location-path semantics, applied to a pushed file: the recorded path
@@ -1294,7 +1286,7 @@ async def invalidate_for_push(
removed: list[str],
commit_sha: str,
) -> int:
"""Flag snippets whose recorded location a push just touched (#2691).
"""Flag snippets whose recorded location a push just touched.
Writes ``verification.invalidated_by = {commit_sha, at, path, removed}``
onto matched snippets that CARRY a verdict — the flag means "the repo
@@ -1449,7 +1441,7 @@ async def merge_snippets(user_id: int, target_id: int, source_ids: list[int]):
Share-aware and write-gated, like update: an editor/admin grant is enough, a
viewer grant is not. Raises PermissionError when the caller can read the
target but not write it. Every source must share the TARGET'S OWNER —
cross-owner merge stays out of scope (#231) — and must itself be writable;
cross-owner merge is out of scope — and must itself be writable;
sources failing either test are skipped rather than silently half-merged.
The survivor records what it absorbed as `merged_from` — in the `data` mirror
@@ -1499,7 +1491,7 @@ async def merge_snippets(user_id: int, target_id: int, source_ids: list[int]):
# a target merged twice keeps both histories.
#
# Each entry also carries what THAT source contributed, which is what makes
# un-merge exact (#2165) rather than a blind subtraction that would strip
# un-merge exact rather than a blind subtraction that would strip
# call sites the survivor legitimately owns.
merged_from = _normalize_merged_from(
list(tgt_fields.get("merged_from") or [])
@@ -1561,8 +1553,8 @@ async def unmerge_snippet(user_id: int, survivor_id: int, source_id: int):
one record type. Instead un-merge performs the restore itself, so the inverse
is one operation with one authorization check and the trash path stays
ignorant. Restoring from the trash directly is still allowed and still leaves
both records claiming the same call sites — which is why this exists — but it
is no longer the only way back.
both records claiming the same call sites — which is why this exists — so it
is not the only way back.
EXACTNESS. Subtraction uses the contribution recorded at merge time, not the
source's current locations. A source that was itself edited after being