docs(6209): forty test files — design cascade and stylesheet, embeddings, family canon, forge, backup round trips, inception, placement, platforms, moments, rule move and verification, search scope, facets

Module and test docstrings state what is pinned without the record number
or step that introduced it. Incident accounts become the failure shape in
the present tense: the pipefail head, the code_shapes import, the export
order, the facet dialects, the cross-kind repeat, the write-once kind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 21:40:25 -04:00
co-authored by Claude Opus 5.5
parent 63e97a6f0f
commit 79de8938b9
40 changed files with 105 additions and 110 deletions
+6 -6
View File
@@ -1,8 +1,8 @@
"""The auto-inject query carries the conversation beside a thin prompt (#4364). """The auto-inject query carries the conversation beside a thin prompt.
The notes arm used to retrieve against the operator's typed words alone, and a The operator's typed words alone are a poor query: a mid-session follow-up —
mid-session follow-up — "yes do that", "now fix the filter" — names nothing a "yes do that", "now fix the filter" — names nothing a record can match. The
record can match. The hook now reads the tail of the last assistant reply out hook reads the tail of the last assistant reply out
of the transcript and sends it as `ctx`; the server appends it to a SHORT of the transcript and sends it as `ctx`; the server appends it to a SHORT
prompt only. Two halves, pinned separately: what the server builds, and what prompt only. Two halves, pinned separately: what the server builds, and what
the hook extracts. the hook extracts.
@@ -103,10 +103,10 @@ def test_no_transcript_is_silence_not_failure(tmp_path):
assert recent(tmp_path, []) == "" assert recent(tmp_path, []) == ""
# --- what a menu line carries (#4364) ---------------------------------------- # --- what a menu line carries -----------------------------------------------
# #
# The name, the kind and System, and the WHOLE matched passage — once. A repeat # The name, the kind and System, and the WHOLE matched passage — once. A repeat
# is a pointer. These pin the shape against the three ways it had gone wrong: # is a pointer. These pin the shape against the three ways it goes wrong:
# a trigger-composed title rendered as the line (1,500+ chars), the passage cut # a trigger-composed title rendered as the line (1,500+ chars), the passage cut
# to its head (which was the title again), and a `seen` repeat re-rendered whole. # to its head (which was the title again), and a `seen` repeat re-rendered whole.
+2 -2
View File
@@ -1,4 +1,4 @@
"""The CI-side token check (#2277). """The CI-side token check.
The checker is stdlib-only and lives in scripts/ so CI can run it without an The checker is stdlib-only and lives in scripts/ so CI can run it without an
install, so these tests import it by path rather than as a package. install, so these tests import it by path rather than as a package.
@@ -45,7 +45,7 @@ def test_declarations_after_a_comment_are_not_lost():
def test_the_supersedes_block_is_read_from_the_sheet_not_hardcoded(): def test_the_supersedes_block_is_read_from_the_sheet_not_hardcoded():
"""Rule #115: the checker must know nothing about any install's palette. """The checker knows nothing about any install's palette.
Everything it enforces comes out of the stylesheet it is pointed at.""" Everything it enforces comes out of the stylesheet it is pointed at."""
assert check.superseded_literals(SHEET) == { assert check.superseded_literals(SHEET) == {
"#fff": "--fs-text-primary", "#fff": "--fs-text-primary",
+2 -3
View File
@@ -1,12 +1,11 @@
"""Every create_* tool says what it is NOT for (#3123). """Every create_* tool says what it is NOT for.
WHY THIS EXISTS WHY THIS EXISTS
Scribe's record kinds are reached for interchangeably — a note written where Scribe's record kinds are reached for interchangeably — a note written where
a task was owed, a rule written where a snippet belonged — and the moment of a task was owed, a rule written where a snippet belonged — and the moment of
choice is the only moment a correction is cheap. The tool docstring IS the choice is the only moment a correction is cheap. The tool docstring IS the
agent-facing contract (rule 119 puts product guidance there and nowhere agent-facing contract (product guidance lives there and nowhere else), so a docstring that only documents parameters answers "how do I call
else), so a docstring that only documents parameters answers "how do I call
this" while leaving "should I be calling this at all" unasked. this" while leaving "should I be calling this at all" unasked.
The gap was lopsided before this: `create_rule` and `start_planning` both The gap was lopsided before this: `create_rule` and `start_planning` both
+3 -3
View File
@@ -1,4 +1,4 @@
"""The parent chain: walking it, and refusing to close it (milestone #254 step 1). """The parent chain: walking it, and refusing to close it.
Pure functions over a `{id: parent_id}` literal, so a whole hierarchy is one line Pure functions over a `{id: parent_id}` literal, so a whole hierarchy is one line
of setup and no database is involved. That is the reason the cascade lives in its of setup and no database is involved. That is the reason the cascade lives in its
@@ -99,7 +99,7 @@ def test_the_guard_survives_a_hierarchy_that_is_already_corrupt():
# --- resolution ------------------------------------------------------------- # --- resolution -------------------------------------------------------------
# #
# The cascade proper (milestone #254 step 2). Hierarchies are stated as literals # The cascade proper. Hierarchies are stated as literals
# because resolve_tokens is pure and duck-typed — which is the whole reason it # because resolve_tokens is pure and duck-typed — which is the whole reason it
# lives here rather than inside the service. # lives here rather than inside the service.
@@ -384,7 +384,7 @@ def test_rows_without_a_supersedes_attribute_at_all_still_resolve():
def test_rationale_cascades_like_purpose_and_is_a_different_question(): def test_rationale_cascades_like_purpose_and_is_a_different_question():
"""`purpose` is what the token is FOR; `rationale` is why it is this value. """`purpose` is what the token is FOR; `rationale` is why it is this value.
Rules carry the second routinely ("Success = Moss, aligned by design") and a Rules carry the second routinely ("Success = Moss, aligned by design") and a
token row had nowhere to put it until now.""" token row needs a place to put it."""
resolved = _by_name(resolve_tokens( resolved = _by_name(resolve_tokens(
APP, PARENTS, APP, PARENTS,
{ {
+3 -3
View File
@@ -120,8 +120,8 @@ def test_the_header_says_what_the_sheet_is_for():
# --- modes ------------------------------------------------------------------ # --- modes ------------------------------------------------------------------
def test_base_goes_on_the_root_selector_and_other_modes_layer_over_it(): def test_base_goes_on_the_root_selector_and_other_modes_layer_over_it():
"""Matches the convention already in the codebase, and the one-way scoping """Matches the convention already in the codebase, and its one-way
#251 recorded: light on `:root`, dark layered on an attribute selector.""" scoping: light on `:root`, dark layered on an attribute selector."""
css = render_stylesheet([ css = render_stylesheet([
design_token_stub(name="--fs-bg", value_by_mode={"base": "#f5f1e8", "dark": "#14171a"}), design_token_stub(name="--fs-bg", value_by_mode={"base": "#f5f1e8", "dark": "#14171a"}),
]) ])
@@ -188,7 +188,7 @@ def test_a_declared_token_with_no_value_appears_as_a_comment_not_a_silence():
def test_an_empty_system_renders_a_sheet_with_no_blocks_rather_than_failing(): def test_an_empty_system_renders_a_sheet_with_no_blocks_rather_than_failing():
"""A system with no tokens is an ordinary state (rule #115), including one """A system with no tokens is an ordinary state, including one
that was just created.""" that was just created."""
css = render_stylesheet([]) css = render_stylesheet([])
assert "{" not in css assert "{" not in css
+2 -2
View File
@@ -9,7 +9,7 @@ the dangerous one: a recurring task embedded to a different shape than the rest
of the corpus is ranked against documents it doesn't match, and nothing reports of the corpus is ranked against documents it doesn't match, and nothing reports
it. A wrong vector returns results; it just returns the wrong ones. it. A wrong vector returns results; it just returns the wrong ones.
It is also the precondition for #2486. A dev-log's vector separates from five It is also the precondition for comparing document shapes. A dev-log's vector separates from five
unrelated dev-logs by 0.023 where a snippet separates by 0.153, and the leading unrelated dev-logs by 0.023 where a snippet separates by 0.153, and the leading
explanation is shape — a snippet states its purpose twice in a short document. explanation is shape — a snippet states its purpose twice in a short document.
Testing an alternative against three copies would mean testing a shape that Testing an alternative against three copies would mean testing a shape that
@@ -77,5 +77,5 @@ def test_nothing_else_builds_the_embedding_document_itself():
f"these build the embedding document inline instead of calling " f"these build the embedding document inline instead of calling "
f"embedding_text(): {offenders}. One definition — an inlined copy is " f"embedding_text(): {offenders}. One definition — an inlined copy is "
f"ranked against a corpus it no longer matches the moment the shape " f"ranked against a corpus it no longer matches the moment the shape "
f"changes, and nothing reports it (#2486)." f"changes, and nothing reports it."
) )
+3 -3
View File
@@ -1,4 +1,4 @@
"""The embedding refresh must LOSE to a delete, not race it (#3262). """The embedding refresh must LOSE to a delete, not race it.
Both upserts replace a record's vectors as delete-then-insert. That takes two Both upserts replace a record's vectors as delete-then-insert. That takes two
row locks — the chunk rows, then the parent row via the insert's foreign key — row locks — the chunk rows, then the parent row via the insert's foreign key —
@@ -32,7 +32,7 @@ def _mock_session(lock_result: object = 7, execute_side_effect=None):
session = MagicMock() session = MagicMock()
claimed = MagicMock() claimed = MagicMock()
claimed.scalar_one_or_none.return_value = lock_result claimed.scalar_one_or_none.return_value = lock_result
# A task's document carries its work logs (#4251), so the refresh reads # A task's document carries its work logs, so the refresh reads
# task_logs before it builds chunks. Answered explicitly — left to # task_logs before it builds chunks. Answered explicitly — left to
# autovivify, `list(result.all())` would raise and be swallowed, and these # autovivify, `list(result.all())` would raise and be swallowed, and these
# tests would be exercising the failure path without saying so. # tests would be exercising the failure path without saying so.
@@ -72,7 +72,7 @@ async def test_a_note_refresh_claims_the_row_before_rewriting_its_vectors():
sqls = [compiled_sql(c.args[0], dialect=PG) for c in session.execute.call_args_list] sqls = [compiled_sql(c.args[0], dialect=PG) for c in session.execute.call_args_list]
# Located by what they ARE rather than by position: the work-log read a # Located by what they ARE rather than by position: the work-log read a
# task's document needs (#4251) runs before any of this, and an index is # task's document needs runs before any of this, and an index is
# not what the claim is about. # not what the claim is about.
claim = next(i for i, sql in enumerate(sqls) if "FOR KEY SHARE NOWAIT" in sql) claim = next(i for i, sql in enumerate(sqls) if "FOR KEY SHARE NOWAIT" in sql)
replace = next( replace = next(
+1 -1
View File
@@ -74,7 +74,7 @@ async def test_get_embedding_propagates_model_load_failures():
await get_embedding("x") await get_embedding("x")
# ─── query_embedding_memo (milestone 456 step 1) ───────────────────────────── # ─── query_embedding_memo ────────────────────────────────────────────────────
def _counting_embeddings(): def _counting_embeddings():
+2 -2
View File
@@ -1,4 +1,4 @@
"""The promotion engine without a database (milestone 463 step 3). """The promotion engine without a database.
The parts that decide — which citations claim lineage, which criteria veto — The parts that decide — which citations claim lineage, which criteria veto —
are pure and pinned here, beside the doors' wiring: the triggers ride the are pure and pinned here, beside the doors' wiring: the triggers ride the
@@ -89,7 +89,7 @@ def test_proven_needs_named_evidence():
def test_the_criteria_the_agent_is_told_are_the_ones_enforced(): def test_the_criteria_the_agent_is_told_are_the_ones_enforced():
"""Rule 119: the criteria are product text. The promote tool's docstring """The criteria are product text. The promote tool's docstring
must name every criterion the service checks, by its parameter name.""" must name every criterion the service checks, by its parameter name."""
doc = family_tools.promote_family_idea.__doc__ doc = family_tools.promote_family_idea.__doc__
for key in CRITERIA_KEYS: for key in CRITERIA_KEYS:
+2 -2
View File
@@ -1,4 +1,4 @@
"""The adoption ledger without a database (milestone 463 step 4). """The adoption ledger without a database.
The parts that decide are pure and pinned here: what an assessment needs The parts that decide are pure and pinned here: what an assessment needs
before it can be recorded, what each ground of the conflict order needs — before it can be recorded, what each ground of the conflict order needs —
@@ -166,7 +166,7 @@ def test_no_reference_at_all_points_at_the_idea():
# --- the product text and the doors --------------------------------------------------- # --- the product text and the doors ---------------------------------------------------
def test_the_outcomes_the_agent_is_told_are_the_ones_enforced(): def test_the_outcomes_the_agent_is_told_are_the_ones_enforced():
"""Rule 119: the outcomes and the order are product text. The tool """The outcomes and the order are product text. The tool
docstrings must name every outcome and every ground the service checks, docstrings must name every outcome and every ground the service checks,
in the service's order.""" in the service's order."""
assess_doc = tool_doc("scribe.mcp.tools.family", "assess_family_adoption") assess_doc = tool_doc("scribe.mcp.tools.family", "assess_family_adoption")
+1 -1
View File
@@ -1,4 +1,4 @@
"""How family canon reaches a session (milestone 463 step 6), without a """How family canon reaches a session, without a
database: the line enter_project carries, the skill and its triggers, and the database: the line enter_project carries, the skill and its triggers, and the
pointers every other surface gives to it. Retrieval reach, the readout's pointers every other surface gives to it. Retrieval reach, the readout's
counts and the owed answers against Postgres are in counts and the owed answers against Postgres are in
+1 -1
View File
@@ -1,4 +1,4 @@
"""Forge-connection tokens are stored sealed (milestone 523, #5877). """Forge-connection tokens are stored sealed.
Migration 0122 seals the rows that existed before, with its OWN frozen copy of Migration 0122 seals the rows that existed before, with its OWN frozen copy of
the v1 key derivation (so a later edit to services/secrets.py can never change the v1 key derivation (so a later edit to services/secrets.py can never change
+2 -2
View File
@@ -1,4 +1,4 @@
"""Forge push webhook (#2691) — signature gate, payload parsing, and the """Forge push webhook — signature gate, payload parsing, and the
end-to-end drift flag on real Postgres. end-to-end drift flag on real Postgres.
The webhook is the seam that makes verification scale: a push names exactly The webhook is the seam that makes verification scale: a push names exactly
@@ -43,7 +43,7 @@ def test_signature_gate():
def test_delivered_signature_reads_both_forges_headers(): def test_delivered_signature_reads_both_forges_headers():
"""#2693: GitHub signs the same HMAC but ships it as """GitHub signs the same HMAC but ships it as
X-Hub-Signature-256: sha256=<hex> — the whole webhook payload mapping is X-Hub-Signature-256: sha256=<hex> — the whole webhook payload mapping is
this header, so pin it.""" this header, so pin it."""
hexsig = _sign(b"{}") hexsig = _sign(b"{}")
+1 -1
View File
@@ -1,4 +1,4 @@
"""The create gate's similarity bars are settings, clamped and fail-open (#4385).""" """The create gate's similarity bars are settings, clamped and fail-open."""
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
import pytest import pytest
+5 -5
View File
@@ -1,14 +1,14 @@
"""Under `pipefail`, an early-exiting `head` must not void the output it kept (#4042). """Under `pipefail`, an early-exiting `head` must not void the output it kept.
The hooks run with `set -uo pipefail`. `hits=$(git grep -l … | head -4) || The hooks run with `set -uo pipefail`. `hits=$(git grep -l … | head -4) ||
hits=""` lost its four hits whenever git grep was still writing when head hits=""` loses its four hits whenever git grep is still writing when head
exited: SIGPIPE, a failed substitution, and the fallback wiped the result. It exits: SIGPIPE, a failed substitution, and the fallback wipes the result. It
only showed on big outputs, so the by-name duplicate arm went silent for only shows on big outputs, so the by-name duplicate arm goes silent for
exactly the most-duplicated names. exactly the most-duplicated names.
Two halves: the real function on a repo big enough to overflow the pipe, and Two halves: the real function on a repo big enough to overflow the pipe, and
the check_plugin pattern that keeps the shape out of every hook (shown able to the check_plugin pattern that keeps the shape out of every hook (shown able to
fail, rule #167). fail).
""" """
from __future__ import annotations from __future__ import annotations
@@ -1,11 +1,11 @@
"""Real-Postgres round trip for code_shapes (#4197). """Real-Postgres round trip for code_shapes.
**What this guards is a column that was simply not there.** `code_shapes` **What this guards is a column the import leaves out.** If `code_shapes`
exported `reason_code`, `recheck_at` and `diverges_from` and imported none of exports `reason_code`, `recheck_at` and `diverges_from` and imports none of
them — a restore reported success and came back with every judgment's verdict them, a restore reports success and comes back with every judgment's verdict
and no code for why, every recheck flag cleared, and every divergence pointer and no code for why, every recheck flag cleared, and every divergence pointer
gone. The unit-level column guard in `test_services_backup.py` is what found gone. The unit-level column guard in `test_services_backup.py` catches a
it and is what stops it recurring; this file is the behavioural half, because column missing from either side; this file is the behavioural half, because
"the builder passes the kwarg" and "the value survives a real restore" are "the builder passes the kwarg" and "the value survives a real restore" are
different claims. different claims.
@@ -1,11 +1,11 @@
"""Real-Postgres check that the full export is ORDERED, so the backup file """Real-Postgres check that the full export is ORDERED, so the backup file
deduplicates between runs (milestone 523). deduplicates between runs.
Only Postgres shows this. An unordered select returns rows in heap order, and Only Postgres shows this. An unordered select returns rows in heap order, and
an UPDATE writes the row's new version at the end of the heap — so before an UPDATE writes the row's new version at the end of the heap — so without an
export_full_backup ordered its queries, editing one old note moved it to the ORDER BY in export_full_backup, editing one old note moves it to the bottom of
bottom of `notes`. Every byte after its old place shifted, and a restic run `notes`. Every byte after its old place shifts, and a restic run over the next
over the next backup stored most of the table again. A mocked session returns backup stores most of the table again. A mocked session returns
whatever order it was handed and could never catch it. whatever order it was handed and could never catch it.
""" """
import io import io
@@ -101,7 +101,7 @@ async def test_the_tar_restores_with_every_id_remapped(written):
assert (note.project_id, note.milestone_id) == (project.id, milestone.id) assert (note.project_id, note.milestone_id) == (project.id, milestone.id)
assert note.body == "From the tar, not the JSON." assert note.body == "From the tar, not the JSON."
assert note.status == "todo" assert note.status == "todo"
# The Profile page travels with its user (#5879); a field left blank stays # The Profile page travels with its user; a field left blank stays
# blank rather than restoring as the page's default. # blank rather than restoring as the page's default.
assert (profile.display_name, profile.tone) == ("Drill Owner", "technical") assert (profile.display_name, profile.tone) == ("Drill Owner", "technical")
assert profile.interests == ["backups"] assert profile.interests == ["backups"]
@@ -1,4 +1,4 @@
"""Real-Postgres round trip for rule_versions (milestone 323 step 1). """Real-Postgres round trip for rule_versions.
Two things here cannot be shown with mocks, and both are the kind that fail Two things here cannot be shown with mocks, and both are the kind that fail
QUIETLY — a restore reports success and hands back history that is wrong. QUIETLY — a restore reports success and hands back history that is wrong.
@@ -7,7 +7,7 @@ QUIETLY — a restore reports success and hands back history that is wrong.
A restore that writes it straight through succeeds and attaches every A restore that writes it straight through succeeds and attaches every
snapshot to whatever rule happens to hold that number here — an edit snapshot to whatever rule happens to hold that number here — an edit
history filed against the wrong binding instruction, which is worse than history filed against the wrong binding instruction, which is worse than
no history at all. This is #3182's `arose_from_id` trap on a new table. no history at all. It is the same trap as a note's `arose_from_id`.
2. **A null actor does not drop the row.** `user_id` is SET NULL precisely so 2. **A null actor does not drop the row.** `user_id` is SET NULL precisely so
history outlives the account that wrote it, so the restore deliberately history outlives the account that wrote it, so the restore deliberately
@@ -217,7 +217,7 @@ async def test_both_snapshots_come_back(restored):
async def test_the_history_attaches_to_the_RESTORED_rule(restored): async def test_the_history_attaches_to_the_RESTORED_rule(restored):
"""#3182's trap. The source rule still exists and holds a different id, so """The remap trap. The source rule still exists and holds a different id, so
a straight-through copy would file this history against it — or against a straight-through copy would file this history against it — or against
whatever unrelated rule owns that number.""" whatever unrelated rule owns that number."""
new_rule_id = restored["rule"].id new_rule_id = restored["rule"].id
+3 -3
View File
@@ -1,4 +1,4 @@
"""The adoption ledger against real Postgres (milestone 463 step 4). """The adoption ledger against real Postgres.
What the unit lane can't show: an answer moves the row and logs exactly one What the unit lane can't show: an answer moves the row and logs exactly one
decision; an owed answer files its task in the OWING project, tagged to the decision; an owed answer files its task in the OWING project, tagged to the
@@ -236,7 +236,7 @@ async def test_another_projects_answer_is_recorded_as_precedent(family):
assert first["decision"]["id"] in second["decision"]["precedent_ids"] assert first["decision"]["id"] in second["decision"]["precedent_ids"]
assert second["precedents"][0]["relation"] == "same idea, another project" assert second["precedents"][0]["relation"] == "same idea, another project"
assert second["precedents"][0]["project_title"] == "android one" assert second["precedents"][0]["project_title"] == "android one"
# A precedent says its reason once: before/after are state only (#5131). # A precedent says its reason once: before/after are state only.
assert second["precedents"][0]["reason"] == "the source app" assert second["precedents"][0]["reason"] == "the source app"
assert "reason" not in second["precedents"][0]["after"] assert "reason" not in second["precedents"][0]["after"]
@@ -392,7 +392,7 @@ async def test_the_matrix_hides_what_the_caller_cannot_read(family):
assert all(i["note_id"] != family["note"] for i in matrix["ideas"]) assert all(i["note_id"] != family["note"] for i in matrix["ideas"])
# --- the shape ledger as evidence (step 5) ------------------------------------------- # --- the shape ledger as evidence ----------------------------------------------------
REPO = "git.example/android-two" REPO = "git.example/android-two"
KOTLIN_BODY = ( KOTLIN_BODY = (
+1 -1
View File
@@ -1,4 +1,4 @@
"""Real-Postgres checks for family canon's schema (milestone 463 step 1). """Real-Postgres checks for family canon's schema.
Two kinds of claim live here, and both need a database: Two kinds of claim live here, and both need a database:
+4 -4
View File
@@ -1,4 +1,4 @@
"""The promotion engine against real Postgres (milestone 463 step 3). """The promotion engine against real Postgres.
What the unit lane can't show: a promotion opens the right ledger rows and What the unit lane can't show: a promotion opens the right ledger rows and
no others, a veto changes nothing but the log, an undo puts back exactly the no others, a veto changes nothing but the log, an undo puts back exactly the
@@ -279,9 +279,9 @@ async def test_a_repeat_with_no_shared_platform_is_silent(family):
async def test_a_repeat_across_kinds_is_silent(family): async def test_a_repeat_across_kinds_is_silent(family):
"""A snippet whose only match is a NOTE is not a repeat (#5130): a dated """A snippet whose only match is a NOTE is not a repeat: a dated
release log scored 0.83 against a signed-APK CI snippet on shared release log can score above the bar against a signed-APK CI snippet on
vocabulary and was opened as a candidate. The stub hands back the shared vocabulary alone, and would open as a candidate. The stub hands back the
cross-kind hit directly, so this pins the trigger's own check, not the cross-kind hit directly, so this pins the trigger's own check, not the
search filter in front of it.""" search filter in front of it."""
async with async_session() as s: async with async_session() as s:
+2 -3
View File
@@ -1,7 +1,6 @@
"""A family idea reaches every project on its platform, and no other """A family idea reaches every project on its platform, and no other.
(milestone 463 step 6).
WHY THIS IS AN INTEGRATION TEST — the lesson-reach reasoning (#3730) holds WHY THIS IS AN INTEGRATION TEST — the lesson-reach reasoning holds
here too: the reach is one `OR` inside the search's project filter, and only here too: the reach is one `OR` inside the search's project filter, and only
the ROWS that come back prove it. Every note embeds identically to the query, the ROWS that come back prove it. Every note embeds identically to the query,
so scoping is the only thing that can separate them. The embedder is stubbed; so scoping is the only thing that can separate them. The embedder is stubbed;
+1 -1
View File
@@ -1,4 +1,4 @@
"""Real-Postgres integration tests for the forge keyring (#2778). """Real-Postgres integration tests for the forge keyring.
Runs only in the CI integration lane (real Postgres, schema built by Runs only in the CI integration lane (real Postgres, schema built by
`alembic upgrade head`, which includes migration 0078's forge_connections `alembic upgrade head`, which includes migration 0078's forge_connections
+1 -1
View File
@@ -1,4 +1,4 @@
"""Real-Postgres tests for "recently touched" in the enter_project handshake (#4045). """Real-Postgres tests for "recently touched" in the enter_project handshake.
What a mock cannot show: that a work-log moves its task up the list even What a mock cannot show: that a work-log moves its task up the list even
though logging never bumps the task's updated_at, and that a milestone whose though logging never bumps the task's updated_at, and that a milestone whose
+2 -2
View File
@@ -1,10 +1,10 @@
"""Real-Postgres integration tests for project inception (milestone 297). """Real-Postgres integration tests for project inception.
What mocks can't prove: a decision's effects land through the real services What mocks can't prove: a decision's effects land through the real services
(the design system points, the standard Systems seed once), the record is (the design system points, the standard Systems seed once), the record is
written last, a bad target applies nothing. written last, a bad target applies nothing.
Rules are not part of inception since milestone 414: a rule in a rulebook is Rules are not part of inception: a rule in a rulebook is
global and applies to every project, so there is nothing to subscribe to. global and applies to every project, so there is nothing to subscribe to.
""" """
import pytest import pytest
+1 -1
View File
@@ -1,4 +1,4 @@
"""A lesson is reachable from a project it was not written on (step 3). """A lesson is reachable from a project it was not written on.
WHY THIS IS AN INTEGRATION TEST WHY THIS IS AN INTEGRATION TEST
+4 -5
View File
@@ -1,8 +1,7 @@
"""Real-Postgres tests for finding a plan by meaning (milestone 415, steps 3 and 4). """Real-Postgres tests for finding a plan by meaning.
A project's roadmap written as milestones was invisible to recall: `search` A project's roadmap is written as milestones, and `search` reaches them, so
covered notes, tasks and rules, so "is there already a plan for this?" had no "is there already a plan for this?" has a tool. What a mock cannot show is the join scoping the vectors to a project and
tool. What a mock cannot show is the join scoping the vectors to a project and
to what the caller may read, so these seed real milestones with hand-made to what the caller may read, so these seed real milestones with hand-made
vectors and stub only the embedder. vectors and stub only the embedder.
""" """
@@ -84,7 +83,7 @@ async def test_a_project_the_caller_cannot_read_returns_nothing(roadmap):
assert await _found(roadmap["stranger"]) == [] assert await _found(roadmap["stranger"]) == []
# ── the plan gate (step 4): start_planning finds the plan before making another ── # ── the plan gate: start_planning finds the plan before making another ──
LONG = "Resolve works and editions against the metadata providers. " * 5 LONG = "Resolve works and editions against the metadata providers. " * 5
+1 -1
View File
@@ -1,4 +1,4 @@
"""Real-Postgres tests for an install's moment mappings (milestone 458 step 2). """Real-Postgres tests for an install's moment mappings.
What these pin is what the in-session correction promises the operator: a What these pin is what the in-session correction promises the operator: a
mapping made is in force on the next call, a default switched off stays off, mapping made is in force on the next call, a default switched off stays off,
+3 -3
View File
@@ -32,7 +32,7 @@ def _vec(*nonzero_first):
def _emb(note_id, user_id, chunk_index, vec): def _emb(note_id, user_id, chunk_index, vec):
"""A chunk row at the current chunker version (#280, migration 0077).""" """A chunk row at the current chunker version."""
from scribe.services.embeddings import CHUNKER_VERSION, EMBEDDING_MODEL from scribe.services.embeddings import CHUNKER_VERSION, EMBEDDING_MODEL
return NoteEmbedding( return NoteEmbedding(
@@ -63,7 +63,7 @@ async def seeded():
# query vector will be [1,0,0,...]; near ~ identical (sim≈1.0), # query vector will be [1,0,0,...]; near ~ identical (sim≈1.0),
# far is orthogonal (sim≈0.0 -> filtered by the default threshold). # far is orthogonal (sim≈0.0 -> filtered by the default threshold).
# near gets a SECOND, weaker chunk (sim≈0.6) — the collapse to # near gets a SECOND, weaker chunk (sim≈0.6) — the collapse to
# best-chunk-per-note (#280) is under test: near must come back once, # best-chunk-per-note is under test: near must come back once,
# at its best chunk's score, not twice. # at its best chunk's score, not twice.
s.add(_emb(near.id, user.id, 0, _vec(1.0))) s.add(_emb(near.id, user.id, 0, _vec(1.0)))
s.add(_emb(near.id, user.id, 1, _vec(0.6, 0.8))) s.add(_emb(near.id, user.id, 1, _vec(0.6, 0.8)))
@@ -94,7 +94,7 @@ async def test_semantic_search_ranks_and_thresholds_via_pgvector(seeded):
assert near_id in ids assert near_id in ids
assert far_id not in ids assert far_id not in ids
assert ids[0] == near_id assert ids[0] == near_id
# Chunk collapse (#280): near has TWO chunk rows above the floor (sim≈1.0 # Chunk collapse: near has TWO chunk rows above the floor (sim≈1.0
# and ≈0.6) and must appear exactly once, at its best chunk's score. # and ≈0.6) and must appear exactly once, at its best chunk's score.
assert ids.count(near_id) == 1 assert ids.count(near_id) == 1
top_score = results[0][0] top_score = results[0][0]
+1 -1
View File
@@ -1,4 +1,4 @@
"""Real-Postgres tests for task placement (milestone 409 step 1). """Real-Postgres tests for task placement.
What a mock cannot show: that step order is creation order rather than update What a mock cannot show: that step order is creation order rather than update
order, that position and progress are computed over the steps the CALLER may order, that position and progress are computed over the steps the CALLER may
+1 -1
View File
@@ -1,4 +1,4 @@
"""Real-Postgres checks for platform membership (milestone 463 step 2). """Real-Postgres checks for platform membership.
The one invariant: detection only ever ADDS, and only where nobody has The one invariant: detection only ever ADDS, and only where nobody has
answered. A refresh that overwrote a "no" would put a project back into a answered. A refresh that overwrote a "no" would put a project back into a
+1 -1
View File
@@ -1,4 +1,4 @@
"""Real-Postgres proof that a batch create cannot be interfered with (#4016). """Real-Postgres proof that a batch create cannot be interfered with.
What mocks cannot show: that the ids a placeholder is rewritten to are the ids What mocks cannot show: that the ids a placeholder is rewritten to are the ids
the rows actually got, that two batches running at the same time each cite the rows actually got, that two batches running at the same time each cite
@@ -1,6 +1,6 @@
"""Real-Postgres tests for moment proposals and judgments (milestone 458 step 7). """Real-Postgres tests for moment proposals and judgments.
What the step promises, against the real tables: What the feature promises, against the real tables:
- the pass reads only rules nobody has answered for, and any answer — a - the pass reads only rules nobody has answered for, and any answer — a
proposal, a mount, "no moment fits" — takes a rule off that list; proposal, a mount, "no moment fits" — takes a rule off that list;
- a proposal mounts nothing; a confirm mounts, a reject is kept, and a judged - a proposal mounts nothing; a confirm mounts, a reject is kept, and a judged
@@ -8,7 +8,7 @@ What the step promises, against the real tables:
- an edit that takes a moment off a rule is a rejection the signal respects; - an edit that takes a moment off a rule is a rejection the signal respects;
- the signal counts SESSIONS, not opens, and asks once the bar is crossed; - the signal counts SESSIONS, not opens, and asks once the bar is crossed;
- only the owner can propose, judge, or feed the signal; - only the owner can propose, judge, or feed the signal;
- the misfire (step 7b): only a mount can misfire, reports count DAYS, the - the misfire: only a mount can misfire, reports count DAYS, the
bar makes an unmount proposal, reject takes it off, confirm keeps it and bar makes an unmount proposal, reject takes it off, confirm keeps it and
stops the asking, and a re-mount starts the count again. stops the asking, and a re-mount starts the count again.
""" """
@@ -196,7 +196,7 @@ async def test_the_open_resolves_acts_through_the_installs_mappings(world):
assert (await _row(done.id, "work.deliver")).source == "signal" assert (await _row(done.id, "work.deliver")).source == "signal"
# ── The misfire (step 7b) ──────────────────────────────────────────────────── # ── The misfire ──────────────────────────────────────────────────────────────
@pytest.fixture @pytest.fixture
+2 -2
View File
@@ -1,4 +1,4 @@
"""Real-Postgres tests for moving a rule between homes (milestone 414, step 3). """Real-Postgres tests for moving a rule between homes.
A rule's home is its reach: a rulebook topic makes it global, a project makes A rule's home is its reach: a rulebook topic makes it global, a project makes
it that project's. The alternative to a move — recreate the rule in the other it that project's. The alternative to a move — recreate the rule in the other
@@ -84,7 +84,7 @@ async def test_a_project_rule_becomes_global_and_keeps_everything(homes):
async def test_a_move_writes_no_version(homes): async def test_a_move_writes_no_version(homes):
"""A version records what a rule SAID (milestone 323, decision 4). A move """A version records what a rule SAID. A move
changes where it binds, not a word of it.""" changes where it binds, not a word of it."""
before = len(await rv_svc.list_versions(homes["rule"])) before = len(await rv_svc.list_versions(homes["rule"]))
await rulebooks_svc.move_rule(homes["rule"], homes["owner"], topic_id=homes["topic"]) await rulebooks_svc.move_rule(homes["rule"], homes["owner"], topic_id=homes["topic"])
+2 -2
View File
@@ -1,4 +1,4 @@
"""Real-Postgres tests for a rule's CHECK — the write half (milestone 312). """Real-Postgres tests for a rule's CHECK — the write half.
What mocks cannot prove, and what the staleness sweep depends on: What mocks cannot prove, and what the staleness sweep depends on:
@@ -132,7 +132,7 @@ async def test_editing_anything_else_leaves_the_stamp_alone(constraint):
assert row.why.startswith("act_runner picks the shell") assert row.why.startswith("act_runner picks the shell")
# ── the sweep itself (step 3) ────────────────────────────────────────── # ── the sweep itself ───────────────────────────────────────────────────
@pytest_asyncio.fixture @pytest_asyncio.fixture
async def rulebook_of_three(): async def rulebook_of_three():
+6 -6
View File
@@ -1,14 +1,14 @@
"""Real-Postgres crowd tests: an in-scope record is found however many nearer """Real-Postgres crowd tests: an in-scope record is found however many nearer
records sit outside the scope (#4961; the rule search's is #4958's, in records sit outside the scope (the rule search's crowd test is in
test_integration_rule_scope). test_integration_rule_scope).
Ordered straight off a `*_embeddings` table, the planner can walk the HNSW Ordered straight off a `*_embeddings` table, the planner can walk the HNSW
index, which takes ~`hnsw.ef_search` (40) nearest chunks from every owner and index, which takes ~`hnsw.ef_search` (40) nearest chunks from every owner and
project and only then applies the scope. Each test here puts 80 out-of-scope project and only then applies the scope. Each test here puts 80 out-of-scope
records nearer the query than the reader's one in-scope record, so the old records nearer the query than the reader's one in-scope record, so a query
shape could not reach it when the index served the order. What a test cannot that scopes after the index walk cannot reach it when the index serves the
force is the plan — the shape guard in test_search_scope_shape is what fails order. What a test cannot force is the plan — the shape guard in
on the old query whatever Postgres picks. test_search_scope_shape is what fails on that query whatever Postgres picks.
Rows are inserted directly with hand-made vectors and the embedder is stubbed, Rows are inserted directly with hand-made vectors and the embedder is stubbed,
so the scope alone decides what comes back. so the scope alone decides what comes back.
@@ -57,7 +57,7 @@ def _chunk(model, key: str, record_id: int, vec: list[float], **extra):
async def _found(search, user_id: int, **scope) -> set[int]: async def _found(search, user_id: int, **scope) -> set[int]:
"""The ids a search finds. Every search fails open, so a query that raised """The ids a search finds. Every search fails open, so a query that raised
reads as an empty result — the swallowed traceback is captured and named reads as an empty result — the swallowed traceback is captured and named
in the failure instead (#4958).""" in the failure instead."""
raised: list[str] = [] raised: list[str] = []
with patch("scribe.services.embeddings.get_embedding", with patch("scribe.services.embeddings.get_embedding",
AsyncMock(return_value=QUERY_VEC)), \ AsyncMock(return_value=QUERY_VEC)), \
+1 -1
View File
@@ -1,4 +1,4 @@
"""Real-Postgres integration test for the snippet location reverse lookup (#2083). """Real-Postgres integration test for the snippet location reverse lookup.
Runs only in the CI integration lane (real Postgres, schema built by Runs only in the CI integration lane (real Postgres, schema built by
`alembic upgrade head`, which includes migration 0070's `notes.data` + GIN index). `alembic upgrade head`, which includes migration 0070's `notes.data` + GIN index).
+5 -6
View File
@@ -1,6 +1,6 @@
"""Real-Postgres test that the CHECK actually accepts 'spike' (0091). """Real-Postgres test that the CHECK actually accepts 'spike' (0091).
Rule 36 exists because the value and the constraint can drift apart: the The value and the constraint can drift apart: the
code starts writing a new kind while the database still refuses it, and code starts writing a new kind while the database still refuses it, and
nothing catches it until a write fails in front of someone. A mock cannot nothing catches it until a write fails in front of someone. A mock cannot
show that — it has no CHECK — so the constraint gets its own real-DB test, show that — it has no CHECK — so the constraint gets its own real-DB test,
@@ -68,19 +68,18 @@ async def test_an_unknown_kind_is_still_refused(owner_id):
Without this, every assertion above would pass just as happily against a Without this, every assertion above would pass just as happily against a
table whose CHECK had been dropped and never re-added — which is exactly table whose CHECK had been dropped and never re-added — which is exactly
the failure rule 36 is written against. the drift this file is written against.
""" """
with pytest.raises(IntegrityError): with pytest.raises(IntegrityError):
await _write(owner_id, "investigation") await _write(owner_id, "investigation")
async def test_a_kind_can_be_corrected_after_the_fact(owner_id): async def test_a_kind_can_be_corrected_after_the_fact(owner_id):
"""The write-once bug, asserted against a real column (#3129). """A kind is not write-once, asserted against a real column.
The read-back is the whole test. A version that only asserted the update The read-back is the whole test. A version that only asserted the update
call succeeded would have passed against the broken code — the route call succeeded would pass against a route that returns 200 while dropping
returned 200 while dropping the field, which is exactly how this survived the field, which is a failure nothing else reports.
long enough to be found by hand.
""" """
from scribe.services import notes as notes_svc from scribe.services import notes as notes_svc
+8 -9
View File
@@ -3,14 +3,14 @@
The facet predicate is written twice by necessity — as SQL for rows the The facet predicate is written twice by necessity — as SQL for rows the
database hands back, and as Python for candidates the vector search has database hands back, and as Python for candidates the vector search has
already fetched — plus a third time as the `is_task` pre-filter that narrows already fetched — plus a third time as the `is_task` pre-filter that narrows
the candidate set before it exists. Those three used to be hand-written and the candidate set before it exists. Hand-written, those three
agreed only by luck: adding `issue` to the SQL arm alone would have set the agree only by luck: adding `issue` to the SQL arm alone would set the
pre-filter to is_task=False, handed the Python arm a candidate set with no pre-filter to is_task=False, hand the Python arm a candidate set with no
tasks in it, and returned an empty semantic half for the Issues facet forever tasks in it, and return an empty semantic half for the Issues facet forever
with nothing red anywhere (#3128). with nothing red anywhere.
They are now generated from one table. These tests pin the property that made So they are generated from one table, and these tests pin the property that
the trap possible, so it cannot come back by a different route. makes the trap possible, so it cannot come back by a different route.
""" """
import pytest import pytest
@@ -104,8 +104,7 @@ def test_the_live_task_kinds_are_all_facets():
def test_non_task_facets_are_the_note_types_and_only_those(): def test_non_task_facets_are_the_note_types_and_only_those():
"""Spelled out rather than derived, so adding a kind to `_FACETS` has to """Spelled out rather than derived, so adding a kind to `_FACETS` has to
be a deliberate edit in two places. `lesson` joined in milestone 385 step be a deliberate edit in two places. `lesson` is non-task on purpose: `is_task` IS `status is not None`,
2 (#3729) and is non-task on purpose: `is_task` IS `status is not None`,
so a lesson that acquired a status would stop being a lesson.""" so a lesson that acquired a status would stop being a lesson."""
assert set(NON_TASK_FACETS) == {"note", "process", "snippet", "lesson"} assert set(NON_TASK_FACETS) == {"note", "process", "snippet", "lesson"}