Scribe is renamed Fabled Grimoire #212
@@ -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
|
||||
mid-session follow-up — "yes do that", "now fix the filter" — names nothing a
|
||||
record can match. The hook now reads the tail of the last assistant reply out
|
||||
The operator's typed words alone are a poor query: a mid-session follow-up —
|
||||
"yes do that", "now fix the filter" — names nothing a record can match. The
|
||||
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
|
||||
prompt only. Two halves, pinned separately: what the server builds, and what
|
||||
the hook extracts.
|
||||
@@ -103,10 +103,10 @@ def test_no_transcript_is_silence_not_failure(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
|
||||
# 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
|
||||
# to its head (which was the title again), and a `seen` repeat re-rendered whole.
|
||||
|
||||
|
||||
@@ -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
|
||||
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():
|
||||
"""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."""
|
||||
assert check.superseded_literals(SHEET) == {
|
||||
"#fff": "--fs-text-primary",
|
||||
|
||||
@@ -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
|
||||
|
||||
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
|
||||
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
|
||||
else), so a docstring that only documents parameters answers "how do I call
|
||||
agent-facing contract (product guidance lives there and nowhere else), so a docstring that only documents parameters answers "how do I call
|
||||
this" while leaving "should I be calling this at all" unasked.
|
||||
|
||||
The gap was lopsided before this: `create_rule` and `start_planning` both
|
||||
|
||||
@@ -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
|
||||
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 -------------------------------------------------------------
|
||||
#
|
||||
# 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
|
||||
# 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():
|
||||
"""`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
|
||||
token row had nowhere to put it until now."""
|
||||
token row needs a place to put it."""
|
||||
resolved = _by_name(resolve_tokens(
|
||||
APP, PARENTS,
|
||||
{
|
||||
|
||||
@@ -120,8 +120,8 @@ def test_the_header_says_what_the_sheet_is_for():
|
||||
# --- modes ------------------------------------------------------------------
|
||||
|
||||
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
|
||||
#251 recorded: light on `:root`, dark layered on an attribute selector."""
|
||||
"""Matches the convention already in the codebase, and its one-way
|
||||
scoping: light on `:root`, dark layered on an attribute selector."""
|
||||
css = render_stylesheet([
|
||||
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():
|
||||
"""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."""
|
||||
css = render_stylesheet([])
|
||||
assert "{" not in css
|
||||
|
||||
@@ -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
|
||||
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
|
||||
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
|
||||
@@ -77,5 +77,5 @@ def test_nothing_else_builds_the_embedding_document_itself():
|
||||
f"these build the embedding document inline instead of calling "
|
||||
f"embedding_text(): {offenders}. One definition — an inlined copy is "
|
||||
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."
|
||||
)
|
||||
|
||||
@@ -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
|
||||
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()
|
||||
claimed = MagicMock()
|
||||
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
|
||||
# autovivify, `list(result.all())` would raise and be swallowed, and these
|
||||
# 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]
|
||||
# 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.
|
||||
claim = next(i for i, sql in enumerate(sqls) if "FOR KEY SHARE NOWAIT" in sql)
|
||||
replace = next(
|
||||
|
||||
@@ -74,7 +74,7 @@ async def test_get_embedding_propagates_model_load_failures():
|
||||
await get_embedding("x")
|
||||
|
||||
|
||||
# ─── query_embedding_memo (milestone 456 step 1) ─────────────────────────────
|
||||
# ─── query_embedding_memo ────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _counting_embeddings():
|
||||
|
||||
@@ -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 —
|
||||
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():
|
||||
"""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."""
|
||||
doc = family_tools.promote_family_idea.__doc__
|
||||
for key in CRITERIA_KEYS:
|
||||
|
||||
@@ -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
|
||||
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 ---------------------------------------------------
|
||||
|
||||
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,
|
||||
in the service's order."""
|
||||
assess_doc = tool_doc("scribe.mcp.tools.family", "assess_family_adoption")
|
||||
|
||||
@@ -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
|
||||
pointers every other surface gives to it. Retrieval reach, the readout's
|
||||
counts and the owed answers against Postgres are in
|
||||
|
||||
@@ -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
|
||||
the v1 key derivation (so a later edit to services/secrets.py can never change
|
||||
|
||||
@@ -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.
|
||||
|
||||
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():
|
||||
"""#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
|
||||
this header, so pin it."""
|
||||
hexsig = _sign(b"{}")
|
||||
|
||||
@@ -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
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -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) ||
|
||||
hits=""` lost its four hits whenever git grep was still writing when head
|
||||
exited: SIGPIPE, a failed substitution, and the fallback wiped the result. It
|
||||
only showed on big outputs, so the by-name duplicate arm went silent for
|
||||
hits=""` loses its four hits whenever git grep is still writing when head
|
||||
exits: SIGPIPE, a failed substitution, and the fallback wipes the result. It
|
||||
only shows on big outputs, so the by-name duplicate arm goes silent for
|
||||
exactly the most-duplicated names.
|
||||
|
||||
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
|
||||
fail, rule #167).
|
||||
fail).
|
||||
"""
|
||||
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`
|
||||
exported `reason_code`, `recheck_at` and `diverges_from` and imported none of
|
||||
them — a restore reported success and came back with every judgment's verdict
|
||||
**What this guards is a column the import leaves out.** If `code_shapes`
|
||||
exports `reason_code`, `recheck_at` and `diverges_from` and imports none of
|
||||
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
|
||||
gone. The unit-level column guard in `test_services_backup.py` is what found
|
||||
it and is what stops it recurring; this file is the behavioural half, because
|
||||
gone. The unit-level column guard in `test_services_backup.py` catches a
|
||||
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
|
||||
different claims.
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"""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
|
||||
an UPDATE writes the row's new version at the end of the heap — so before
|
||||
export_full_backup ordered its queries, editing one old note moved it to the
|
||||
bottom of `notes`. Every byte after its old place shifted, and a restic run
|
||||
over the next backup stored most of the table again. A mocked session returns
|
||||
an UPDATE writes the row's new version at the end of the heap — so without an
|
||||
ORDER BY in export_full_backup, editing one old note moves it to the bottom of
|
||||
`notes`. Every byte after its old place shifts, and a restic run over the next
|
||||
backup stores most of the table again. A mocked session returns
|
||||
whatever order it was handed and could never catch it.
|
||||
"""
|
||||
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.body == "From the tar, not the JSON."
|
||||
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.
|
||||
assert (profile.display_name, profile.tone) == ("Drill Owner", "technical")
|
||||
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
|
||||
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
|
||||
snapshot to whatever rule happens to hold that number here — an edit
|
||||
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
|
||||
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):
|
||||
"""#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
|
||||
whatever unrelated rule owns that number."""
|
||||
new_rule_id = restored["rule"].id
|
||||
|
||||
@@ -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
|
||||
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 second["precedents"][0]["relation"] == "same idea, another project"
|
||||
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 "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"])
|
||||
|
||||
|
||||
# --- the shape ledger as evidence (step 5) -------------------------------------------
|
||||
# --- the shape ledger as evidence ----------------------------------------------------
|
||||
|
||||
REPO = "git.example/android-two"
|
||||
KOTLIN_BODY = (
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
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):
|
||||
"""A snippet whose only match is a NOTE is not a repeat (#5130): a dated
|
||||
release log scored 0.83 against a signed-APK CI snippet on shared
|
||||
vocabulary and was opened as a candidate. The stub hands back the
|
||||
"""A snippet whose only match is a NOTE is not a repeat: a dated
|
||||
release log can score above the bar against a signed-APK CI snippet on
|
||||
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
|
||||
search filter in front of it."""
|
||||
async with async_session() as s:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""A family idea reaches every project on its platform, and no other
|
||||
(milestone 463 step 6).
|
||||
"""A family idea reaches every project on its platform, and no other.
|
||||
|
||||
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
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
`alembic upgrade head`, which includes migration 0078's forge_connections
|
||||
|
||||
@@ -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
|
||||
though logging never bumps the task's updated_at, and that a milestone whose
|
||||
|
||||
@@ -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
|
||||
(the design system points, the standard Systems seed once), the record is
|
||||
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.
|
||||
"""
|
||||
import pytest
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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`
|
||||
covered notes, tasks and rules, so "is there already a plan for this?" had no
|
||||
tool. What a mock cannot show is the join scoping the vectors to a project and
|
||||
A project's roadmap is written as milestones, and `search` reaches them, so
|
||||
"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
|
||||
to what the caller may read, so these seed real milestones with hand-made
|
||||
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"]) == []
|
||||
|
||||
|
||||
# ── 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
|
||||
|
||||
|
||||
@@ -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
|
||||
mapping made is in force on the next call, a default switched off stays off,
|
||||
|
||||
@@ -32,7 +32,7 @@ def _vec(*nonzero_first):
|
||||
|
||||
|
||||
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
|
||||
|
||||
return NoteEmbedding(
|
||||
@@ -63,7 +63,7 @@ async def seeded():
|
||||
# query vector will be [1,0,0,...]; near ~ identical (sim≈1.0),
|
||||
# far is orthogonal (sim≈0.0 -> filtered by the default threshold).
|
||||
# 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.
|
||||
s.add(_emb(near.id, user.id, 0, _vec(1.0)))
|
||||
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 far_id not in ids
|
||||
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.
|
||||
assert ids.count(near_id) == 1
|
||||
top_score = results[0][0]
|
||||
|
||||
@@ -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
|
||||
order, that position and progress are computed over the steps the CALLER may
|
||||
|
||||
@@ -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
|
||||
answered. A refresh that overwrote a "no" would put a project back into a
|
||||
|
||||
@@ -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
|
||||
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
|
||||
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
|
||||
@@ -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;
|
||||
- the signal counts SESSIONS, not opens, and asks once the bar is crossed;
|
||||
- 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
|
||||
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"
|
||||
|
||||
|
||||
# ── The misfire (step 7b) ────────────────────────────────────────────────────
|
||||
# ── The misfire ──────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@@ -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
|
||||
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):
|
||||
"""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."""
|
||||
before = len(await rv_svc.list_versions(homes["rule"]))
|
||||
await rulebooks_svc.move_rule(homes["rule"], homes["owner"], topic_id=homes["topic"])
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -132,7 +132,7 @@ async def test_editing_anything_else_leaves_the_stamp_alone(constraint):
|
||||
assert row.why.startswith("act_runner picks the shell")
|
||||
|
||||
|
||||
# ── the sweep itself (step 3) ──────────────────────────────────────────
|
||||
# ── the sweep itself ───────────────────────────────────────────────────
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def rulebook_of_three():
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
"""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).
|
||||
|
||||
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
|
||||
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
|
||||
shape could not reach it when the index served the order. What a test cannot
|
||||
force is the plan — the shape guard in test_search_scope_shape is what fails
|
||||
on the old query whatever Postgres picks.
|
||||
records nearer the query than the reader's one in-scope record, so a query
|
||||
that scopes after the index walk cannot reach it when the index serves the
|
||||
order. What a test cannot force is the plan — the shape guard in
|
||||
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,
|
||||
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]:
|
||||
"""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
|
||||
in the failure instead (#4958)."""
|
||||
in the failure instead."""
|
||||
raised: list[str] = []
|
||||
with patch("scribe.services.embeddings.get_embedding",
|
||||
AsyncMock(return_value=QUERY_VEC)), \
|
||||
|
||||
@@ -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
|
||||
`alembic upgrade head`, which includes migration 0070's `notes.data` + GIN index).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""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
|
||||
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,
|
||||
@@ -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
|
||||
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):
|
||||
await _write(owner_id, "investigation")
|
||||
|
||||
|
||||
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
|
||||
call succeeded would have passed against the broken code — the route
|
||||
returned 200 while dropping the field, which is exactly how this survived
|
||||
long enough to be found by hand.
|
||||
call succeeded would pass against a route that returns 200 while dropping
|
||||
the field, which is a failure nothing else reports.
|
||||
"""
|
||||
from scribe.services import notes as notes_svc
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
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
|
||||
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
|
||||
agreed only by luck: adding `issue` to the SQL arm alone would have set the
|
||||
pre-filter to is_task=False, handed the Python arm a candidate set with no
|
||||
tasks in it, and returned an empty semantic half for the Issues facet forever
|
||||
with nothing red anywhere (#3128).
|
||||
the candidate set before it exists. Hand-written, those three
|
||||
agree only by luck: adding `issue` to the SQL arm alone would set the
|
||||
pre-filter to is_task=False, hand the Python arm a candidate set with no
|
||||
tasks in it, and return an empty semantic half for the Issues facet forever
|
||||
with nothing red anywhere.
|
||||
|
||||
They are now generated from one table. These tests pin the property that made
|
||||
the trap possible, so it cannot come back by a different route.
|
||||
So they are generated from one table, and these tests pin the property that
|
||||
makes the trap possible, so it cannot come back by a different route.
|
||||
"""
|
||||
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():
|
||||
"""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
|
||||
2 (#3729) and is non-task on purpose: `is_task` IS `status is not None`,
|
||||
be a deliberate edit in two places. `lesson` is non-task on purpose: `is_task` IS `status is not None`,
|
||||
so a lesson that acquired a status would stop being a lesson."""
|
||||
assert set(NON_TASK_FACETS) == {"note", "process", "snippet", "lesson"}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user