docs(6209): test prose in the present tense — "used to", "previously", "before this" restated as the failure shape
Twenty-eight test files described the code they guard by what it did before a fix. Each is now the property and the failure it prevents, in the present tense: the backfill snapshot, the per-dial calibration query, the rule scope, the lesson trigger filter, the subshell global, the trash owner scope, the version readout fallback, and others. A docstring line that narrated the backup suite's own history is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
"""A backfill repairs to current truth; it does not replay a snapshot.
|
"""A backfill repairs to current truth; it does not replay a snapshot.
|
||||||
|
|
||||||
The bug these pin: every backfill used to SELECT the text alongside the id and
|
The failure these pin: a backfill that SELECTs the text alongside the id and
|
||||||
then iterate that snapshot, sleeping between records. A run over a real corpus
|
then iterates that snapshot, sleeping between records. A run over a real corpus
|
||||||
takes minutes, so anything edited inside that window was re-embedded by its own
|
takes minutes, so anything edited inside that window is re-embedded by its own
|
||||||
write path and then OVERWRITTEN with the pre-edit text the scan had captured —
|
write path and then OVERWRITTEN with the pre-edit text the scan captured — and
|
||||||
and stamped at the current chunker version, so the next boot considered it
|
stamped at the current chunker version, so the next boot considers it current
|
||||||
current and never repaired it. The record stayed findable by what it used to
|
and never repairs it. The record stays findable by what it used to say,
|
||||||
say, silently, until someone happened to edit it again.
|
silently, until someone happens to edit it again.
|
||||||
|
|
||||||
That window is exactly "forward work continues while a long backfill runs",
|
That window is exactly "forward work continues while a long backfill runs",
|
||||||
which is the normal condition, not an unlucky one.
|
which is the normal condition, not an unlucky one.
|
||||||
|
|||||||
@@ -195,8 +195,8 @@ async def test_a_dial_is_read_per_dial_not_from_the_newest_two_rows():
|
|||||||
|
|
||||||
"The newest two rows" and "the newest row of each dial" differ the moment
|
"The newest two rows" and "the newest row of each dial" differ the moment
|
||||||
one dial moves more often than the other — which is the normal case, since
|
one dial moves more often than the other — which is the normal case, since
|
||||||
floors get walked and budgets rarely do. Before this was one query per dial,
|
floors get walked and budgets rarely do. Without a query per dial, a
|
||||||
a surface with three floor changes and one budget change reported the budget
|
surface with three floor changes and one budget change reports the budget
|
||||||
as untouched: a WRONG calibration answer rather than a missing one, and
|
as untouched: a WRONG calibration answer rather than a missing one, and
|
||||||
wrong in the direction a reader would not think to check.
|
wrong in the direction a reader would not think to check.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ def _assert_nothing_thin_and_nothing_lost(title: str, body: str) -> list[str]:
|
|||||||
|
|
||||||
|
|
||||||
def test_a_heading_over_one_long_paragraph_is_not_a_chunk_of_its_own():
|
def test_a_heading_over_one_long_paragraph_is_not_a_chunk_of_its_own():
|
||||||
"""A work log whose entry is one long paragraph: the split used to flush
|
"""A work log whose entry is one long paragraph: the split must not flush
|
||||||
the heading as its own piece before cutting the paragraph."""
|
the heading as its own piece before cutting the paragraph."""
|
||||||
entry = " ".join(["The deploy was verified against the live instance."] * 40)
|
entry = " ".join(["The deploy was verified against the live instance."] * 40)
|
||||||
_assert_nothing_thin_and_nothing_lost(
|
_assert_nothing_thin_and_nothing_lost(
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ choice is the only moment a correction is cheap. The tool docstring IS the
|
|||||||
agent-facing contract (product guidance lives 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.
|
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 tends to be lopsided: `create_rule` and `start_planning` are where a
|
||||||
carried a real disambiguator, and `create_note` — far and away the
|
disambiguator gets written first, while `create_note` — far and away the
|
||||||
highest-volume surface — carried none. Guidance sat in the rarest tool and
|
highest-volume surface — is where it matters most. Guidance that sits only in
|
||||||
was missing from the most common one.
|
the rarest tools misses the most common one.
|
||||||
|
|
||||||
WHAT THIS PINS, AND WHAT IT DOES NOT
|
WHAT THIS PINS, AND WHAT IT DOES NOT
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ resolves is indistinguishable from work still in progress. The practice names
|
|||||||
clients, a bare `await` on a stream — supplying the deadline is part of
|
clients, a bare `await` on a stream — supplying the deadline is part of
|
||||||
using it, not a hardening pass for later.
|
using it, not a hardening pass for later.
|
||||||
|
|
||||||
Before this guard, no request in the app carried one.
|
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ RESTORED_USERNAME = "backup_roundtrip_restored"
|
|||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
async def source():
|
async def source():
|
||||||
"""A snippet, a process, and an issue that arose from a work task — the
|
"""A snippet, a process, and an issue that arose from a work task — the
|
||||||
shapes whose identity the backup used to drop — exported as backup rows.
|
shapes whose identity a backup can drop — exported as backup rows.
|
||||||
|
|
||||||
Deleted children-first: `arose_from_id` is a real FK, so removing the
|
Deleted children-first: `arose_from_id` is a real FK, so removing the
|
||||||
origin while the issue still points at it is asking the database a
|
origin while the issue still points at it is asking the database a
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"""Real-Postgres tests for WHERE a rule reaches.
|
"""Real-Postgres tests for WHERE a rule reaches.
|
||||||
|
|
||||||
A rule lives in a rulebook topic (global) or on one project. Retrieval used to
|
A rule lives in a rulebook topic (global) or on one project. Retrieval that
|
||||||
ignore that and search every rule the user owned, so each project's rules were
|
ignores the home and searches every rule the user owns injects each project's
|
||||||
injected into every other project's sessions. What a mock cannot show is the
|
rules into every other project's sessions. What a mock cannot show is the
|
||||||
join doing the scoping: these seed real rules with hand-made vectors and stub
|
join doing the scoping: these seed real rules with hand-made vectors and stub
|
||||||
only the embedder, so every rule is an equally good match and the home alone
|
only the embedder, so every rule is an equally good match and the home alone
|
||||||
decides what comes back.
|
decides what comes back.
|
||||||
|
|||||||
@@ -121,11 +121,11 @@ async def test_the_repeat_is_counted_not_reported_as_a_result():
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_a_call_where_everything_was_already_seen_is_readable():
|
async def test_a_call_where_everything_was_already_seen_is_readable():
|
||||||
"""The fact that could not be expressed before.
|
"""A call where every hit was already seen is not a call nothing cleared.
|
||||||
|
|
||||||
Previously this call logged zero results with no suppression count, so it
|
Logged as zero results with no suppression count, it would be
|
||||||
was indistinguishable from a bar nothing cleared — and the operator tuning
|
indistinguishable from a bar nothing cleared — and the operator tuning
|
||||||
that bar would have been reading the wrong number.
|
that bar would be reading the wrong number.
|
||||||
"""
|
"""
|
||||||
rec = MagicMock()
|
rec = MagicMock()
|
||||||
hits = [(0.80, fake_note(id=11, title="Pool sizing decision", user_id=1))]
|
hits = [(0.80, fake_note(id=11, title="Pool sizing decision", user_id=1))]
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ worth having wherever a record is stored composed and edited in parts: not
|
|||||||
"does the strip remove the marker" — it did — but "does an edit that changes
|
"does the strip remove the marker" — it did — but "does an edit that changes
|
||||||
nothing change nothing".
|
nothing change nothing".
|
||||||
|
|
||||||
WHAT WENT WRONG. The derivation used to filter out body LINES matching the
|
WHAT GOES WRONG. A derivation that filters out body LINES matching the
|
||||||
trigger marker's pattern, which is `re.M` and therefore matches one line.
|
trigger marker's pattern, which is `re.M` and therefore matches one line,
|
||||||
`when_to_apply` is free text and is routinely several paragraphs; the
|
leaves the rest of the trigger behind. `when_to_apply` is free text and is
|
||||||
composer writes all of it after the marker, so paragraphs 2..n were ordinary
|
routinely several paragraphs; the composer writes all of it after the marker,
|
||||||
lines that the filter had no reason to touch. They came back as "the
|
so paragraphs 2..n are ordinary lines that the filter has no reason to touch.
|
||||||
insight", the full trigger was prepended again, and the stored lesson gained
|
They come back as "the insight", the full trigger is prepended again, and the
|
||||||
(n-1) paragraphs per update — silently, compounding, and in the half of the
|
stored lesson gains (n-1) paragraphs per update — silently, compounding, and in the half of the
|
||||||
document that does the retrieving.
|
document that does the retrieving.
|
||||||
|
|
||||||
On a three-paragraph trigger the body grows by the length of paragraphs 2 and
|
On a three-paragraph trigger the body grows by the length of paragraphs 2 and
|
||||||
@@ -180,7 +180,7 @@ def test_the_insight_is_derived_from_the_row_not_from_the_body_alone():
|
|||||||
"""Why the function takes a note.
|
"""Why the function takes a note.
|
||||||
|
|
||||||
The body on its own does not say where the trigger ends — that is the
|
The body on its own does not say where the trigger ends — that is the
|
||||||
information the old shape did not have and guessed at. The mirror in
|
information a line-wise filter does not have and guesses at. The mirror in
|
||||||
`data` is where it comes from, so a caller holding only the body string
|
`data` is where it comes from, so a caller holding only the body string
|
||||||
cannot be served, and this pins that the argument is the record."""
|
cannot be served, and this pins that the argument is the record."""
|
||||||
note = _stored(MANY_PARAGRAPHS, INSIGHT, [])
|
note = _stored(MANY_PARAGRAPHS, INSIGHT, [])
|
||||||
|
|||||||
@@ -16,11 +16,10 @@ a real promise rather than a shape coincidence.
|
|||||||
|
|
||||||
THE CONTRACT: a `limit` without an `offset` is a truncation with no
|
THE CONTRACT: a `limit` without an `offset` is a truncation with no
|
||||||
continuation. The caller is told there are 250 results and handed 50, with no
|
continuation. The caller is told there are 250 results and handed 50, with no
|
||||||
way to ask for the rest. Both tools that had this were capped over a service
|
way to ask for the rest. The usual cause is a tool capped over a service that
|
||||||
that already accepted an offset — `snippets_svc.list_snippets(offset=0)` was
|
already accepts an offset — the service's `offset` simply not exposed, or a
|
||||||
simply not exposed, and `list_processes` passed a hardcoded `offset=0` into
|
hardcoded `offset=0` passed into `query_knowledge`. The capability exists one
|
||||||
`query_knowledge`. The capability existed one layer down in both cases; only
|
layer down; only the door is missing.
|
||||||
the door was missing.
|
|
||||||
|
|
||||||
As in `test_mcp_auth`, the CANDIDATES are derived and the DECISION is explicit.
|
As in `test_mcp_auth`, the CANDIDATES are derived and the DECISION is explicit.
|
||||||
Deriving the exemption too would make the contract follow a naming convention,
|
Deriving the exemption too would make the contract follow a naming convention,
|
||||||
|
|||||||
@@ -363,9 +363,9 @@ async def test_enter_project_fires_the_coverage_seed_on_the_owner(
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_enter_project_hands_back_the_design_system_when_the_project_has_one():
|
async def test_enter_project_hands_back_the_design_system_when_the_project_has_one():
|
||||||
"""The handshake is where an agent learns what binds it, and a design
|
"""The handshake is where an agent learns what binds it, and a design
|
||||||
system binds the same way a rule does. Before this it was reachable only by
|
system binds the same way a rule does. Reachable only by an agent that
|
||||||
an agent that already knew to call resolve_design_system — so the standards
|
already knows to call resolve_design_system, the standards would be present
|
||||||
were present in the store and absent from the work."""
|
in the store and absent from the work."""
|
||||||
p = fake_project(id=5, design_system_id=9)
|
p = fake_project(id=5, design_system_id=9)
|
||||||
design = {"id": 9, "title": "App kit", "guidance": [{"title": "House"}],
|
design = {"id": 9, "title": "App kit", "guidance": [{"title": "House"}],
|
||||||
"token_count": 95, "token_groups": ["surface"],
|
"token_count": 95, "token_groups": ["surface"],
|
||||||
|
|||||||
@@ -168,8 +168,8 @@ async def test_fable_search_content_type_filters_at_service_layer():
|
|||||||
async def test_each_specific_kind_reaches_the_engine_filter_it_names(
|
async def test_each_specific_kind_reaches_the_engine_filter_it_names(
|
||||||
content_type, expected
|
content_type, expected
|
||||||
):
|
):
|
||||||
"""`note_type` and `task_kind` were parameters of the search all along —
|
"""`note_type` and `task_kind` are parameters of the search, and the
|
||||||
what was missing was a way to ask for them from the agent's door. Asking
|
agent's door passes them through. Asking
|
||||||
for a snippet must narrow to snippets, not merely to non-tasks."""
|
for a snippet must narrow to snippets, not merely to non-tasks."""
|
||||||
_user_id_ctx.set(7)
|
_user_id_ctx.set(7)
|
||||||
mock_search = AsyncMock(return_value=[])
|
mock_search = AsyncMock(return_value=[])
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ async def _enter(*stubs):
|
|||||||
async def test_enter_project_stays_small_however_large_the_project():
|
async def test_enter_project_stays_small_however_large_the_project():
|
||||||
"""The ceiling is the point: a long-lived project's handshake must not
|
"""The ceiling is the point: a long-lived project's handshake must not
|
||||||
grow with its history. 200 milestones with 5k-character plans, 60 project
|
grow with its history. 200 milestones with 5k-character plans, 60 project
|
||||||
rules and 40 Systems would be well over 1M characters in the old shape."""
|
rules and 40 Systems would be well over 1M characters carried whole."""
|
||||||
project = fake_project(id=5, design_system_id=9, goal=GOAL,
|
project = fake_project(id=5, design_system_id=9, goal=GOAL,
|
||||||
description="Background. " * 300)
|
description="Background. " * 300)
|
||||||
rules = {
|
rules = {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
Covers the payload shaping and the two contracts that make this safe to leave in
|
Covers the payload shaping and the two contracts that make this safe to leave in
|
||||||
the hot path: telemetry never raises, and telemetry never blocks. Plus the thing
|
the hot path: telemetry never raises, and telemetry never blocks. Plus the thing
|
||||||
the feature exists for — that the write-path PLACE arm is now recorded, since
|
the feature exists for — that the write-path PLACE arm is recorded, so a
|
||||||
before this it surfaced snippets while leaving no trace anywhere.
|
snippet it surfaces leaves a trace.
|
||||||
"""
|
"""
|
||||||
from unittest.mock import AsyncMock, MagicMock, patch
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ async def test_update_preference_forwards_what_taught_the_change():
|
|||||||
|
|
||||||
def test_the_rule_door_still_asks_before_writing():
|
def test_the_rule_door_still_asks_before_writing():
|
||||||
"""Half one of the asymmetry. If this fails, the gate was tidied away and
|
"""Half one of the asymmetry. If this fails, the gate was tidied away and
|
||||||
preferences are no longer the exception — they are just the same thing.
|
preferences are not the exception — they are just the same thing.
|
||||||
"""
|
"""
|
||||||
doc = _doc(MODULE, "create_rule").lower()
|
doc = _doc(MODULE, "create_rule").lower()
|
||||||
asks = ("approve", "propose", "ask", "question")
|
asks = ("approve", "propose", "ask", "question")
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ def test_the_reply_carries_a_length_discipline():
|
|||||||
def test_the_reply_gets_a_second_pass_for_what_can_go():
|
def test_the_reply_gets_a_second_pass_for_what_can_go():
|
||||||
"""Cutting is a separate act from writing, and needs saying separately.
|
"""Cutting is a separate act from writing, and needs saying separately.
|
||||||
|
|
||||||
The pre-existing check asked whether anything was MISSING, which a bloated
|
A check that asks only whether anything is MISSING is one a bloated reply
|
||||||
reply passes.
|
passes.
|
||||||
"""
|
"""
|
||||||
text = _flat()
|
text = _flat()
|
||||||
assert "what can go" in text
|
assert "what can go" in text
|
||||||
|
|||||||
@@ -134,9 +134,8 @@ async def test_an_unknown_surface_is_refused_before_anything_is_written():
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@pytest.mark.parametrize("dial", ["threshold", "limit", "", "FLOOR"])
|
@pytest.mark.parametrize("dial", ["threshold", "limit", "", "FLOOR"])
|
||||||
async def test_an_unknown_dial_is_refused(dial):
|
async def test_an_unknown_dial_is_refused(dial):
|
||||||
"""The near-misses are the old vocabulary — `threshold` and `limit` are what
|
"""The near-misses are the generic vocabulary — `threshold` and `limit` are
|
||||||
these were called before this step, so they are exactly what a stale caller
|
what a caller that has not read the dial names will send, and a silent no-op there would be indistinguishable from a
|
||||||
will send, and a silent no-op there would be indistinguishable from a
|
|
||||||
change that did not take."""
|
change that did not take."""
|
||||||
session, ctx = _patches()
|
session, ctx = _patches()
|
||||||
with ctx[0], ctx[1], ctx[2], ctx[3], pytest.raises(ValueError):
|
with ctx[0], ctx[1], ctx[2], ctx[3], pytest.raises(ValueError):
|
||||||
|
|||||||
@@ -446,9 +446,9 @@ async def test_the_prompt_arm_retrieves_against_what_the_operator_SAID():
|
|||||||
Both other arms are keyed on an act — a file write, a command. A rule that
|
Both other arms are keyed on an act — a file write, a command. A rule that
|
||||||
governs what to SAY has no act in front of it: extract intent from loose
|
governs what to SAY has no act in front of it: extract intent from loose
|
||||||
phrasing, raise a conflict before acting, end a finding with an offer all
|
phrasing, raise a conflict before acting, end a finding with an offer all
|
||||||
bind on a response. Before this, the operator's message reached only
|
bind on a response. If the operator's message reached only
|
||||||
`semantic_search_notes`, so no rule had ever been retrieved against a
|
`semantic_search_notes`, no rule would ever be retrieved against a thing
|
||||||
thing the operator actually said.
|
the operator actually said.
|
||||||
"""
|
"""
|
||||||
rec = MagicMock()
|
rec = MagicMock()
|
||||||
search = AsyncMock(return_value=[(0.79, fake_rule(
|
search = AsyncMock(return_value=[(0.79, fake_rule(
|
||||||
|
|||||||
@@ -119,13 +119,13 @@ def test_an_unusable_marker_is_refused_rather_than_sent(tmp_path, body):
|
|||||||
|
|
||||||
|
|
||||||
def test_the_reason_survives_the_command_substitution_that_fetches_it(tmp_path):
|
def test_the_reason_survives_the_command_substitution_that_fetches_it(tmp_path):
|
||||||
"""Regression on a bug this nearly shipped with.
|
"""The reason cannot be a global the function sets.
|
||||||
|
|
||||||
The reason used to be a global the function set, which every caller reads
|
Every caller reads the function through `$( )` — a subshell, so a global
|
||||||
through `$( )` — a subshell, so the assignment died with it and the caller
|
assignment dies with it and the caller sees an UNSET variable. The hooks all
|
||||||
saw an UNSET variable. The hooks all run `set -u`, where reading one aborts
|
run `set -u`, where reading one aborts the script: a directory with a
|
||||||
the script: a directory with a misaddressed marker would have cost the
|
misaddressed marker would cost the session its entire SessionStart context,
|
||||||
session its entire SessionStart context, to fetch a warning about a file.
|
to fetch a warning about a file.
|
||||||
|
|
||||||
So the reason comes back through stdout with the id, and this asserts on
|
So the reason comes back through stdout with the id, and this asserts on
|
||||||
the shape the caller actually uses: read in a subshell, split, and used
|
the shape the caller actually uses: read in a subshell, split, and used
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Ordered straight off a `*_embeddings` table, the planner walks the HNSW index:
|
Ordered straight off a `*_embeddings` table, the planner walks the HNSW index:
|
||||||
it takes ~`hnsw.ef_search` (40) nearest chunks from every owner and project
|
it takes ~`hnsw.ef_search` (40) nearest chunks from every owner and project
|
||||||
and filters them afterwards, so an in-scope record behind 40 nearer ones the
|
and filters them afterwards, so an in-scope record behind 40 nearer ones the
|
||||||
caller cannot see is silently dropped. The fix is one shape, `_rank_scoped`,
|
caller cannot see is silently dropped. The remedy is one shape, `_rank_scoped`,
|
||||||
and these pin that every search goes through it — the rule search was fixed
|
and these pin that every search goes through it — fixed in one search alone,
|
||||||
alone first, and the other three kept the fault because nothing said they
|
the others keep the fault, because nothing says they are the same query.
|
||||||
were the same query.
|
|
||||||
|
|
||||||
The integration lane's crowd tests show the behaviour on a real index; these
|
The integration lane's crowd tests show the behaviour on a real index; these
|
||||||
are the guard that fails on the old shape whatever plan Postgres picks.
|
are the guard that fails on a rank-then-scope query whatever plan Postgres
|
||||||
|
picks.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ def test_every_semantic_search_ranks_through_the_scoped_shape():
|
|||||||
calls = _calls(fn)
|
calls = _calls(fn)
|
||||||
assert "_rank_scoped" in calls, f"{name} does not rank through _rank_scoped"
|
assert "_rank_scoped" in calls, f"{name} does not rank through _rank_scoped"
|
||||||
assert "_scoped_chunks" in calls, f"{name} does not build its scope as chunks"
|
assert "_scoped_chunks" in calls, f"{name} does not build its scope as chunks"
|
||||||
# Its own ORDER BY is the old shape: a distance ordered on the
|
# Its own ORDER BY is the rank-then-scope shape: a distance ordered on the
|
||||||
# embedding table, which the index serves before the scope applies.
|
# embedding table, which the index serves before the scope applies.
|
||||||
assert "order_by" not in calls, f"{name} orders by distance itself"
|
assert "order_by" not in calls, f"{name} orders by distance itself"
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ need only a stand-in and a builder.
|
|||||||
|
|
||||||
The full FK-remapping round-trip needs real Postgres and lives in the
|
The full FK-remapping round-trip needs real Postgres and lives in the
|
||||||
`test_integration_backup_*_roundtrip.py` modules, which drive the real
|
`test_integration_backup_*_roundtrip.py` modules, which drive the real
|
||||||
`restore_full_backup`. That sentence used to read "it is not written yet";
|
`restore_full_backup`.
|
||||||
four of those files exist now, the newest covering the three `code_shapes`
|
|
||||||
columns the import guard below found missing.
|
|
||||||
"""
|
"""
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
@@ -268,18 +266,16 @@ def test_every_column_is_exported_or_declared_excluded(table):
|
|||||||
"""THE COLUMN GUARD — _NOT_INCLUDED's shape, one level down.
|
"""THE COLUMN GUARD — _NOT_INCLUDED's shape, one level down.
|
||||||
|
|
||||||
The table guard below catches a whole table going missing. It cannot catch
|
The table guard below catches a whole table going missing. It cannot catch
|
||||||
a COLUMN going missing from a table it already considers covered, which is
|
a COLUMN going missing from a table it already considers covered, and the
|
||||||
how nine of them vanished from `notes` alone: note_type and task_kind, so
|
cost is large: without note_type and task_kind every snippet and process
|
||||||
every snippet and process restored as a plain note and every issue and
|
restores as a plain note and every issue and spike as `work`; without
|
||||||
spike as `work`; arose_from_id, so every provenance edge went; the
|
arose_from_id every provenance edge goes; without the recurrence pair
|
||||||
recurrence pair, so recurring tasks stopped recurring. Plus milestones.body
|
recurring tasks stop recurring.
|
||||||
— which IS the plan — and repo_bindings.ref.
|
|
||||||
|
|
||||||
Each arrived the same way: added to the model and the migration, both of
|
A column arrives in the model and the migration, both of which fail loudly,
|
||||||
which fail loudly, and never to the serialiser, which fails silently.
|
and is easy to leave out of the serialiser, which fails silently. So a new
|
||||||
|
column must be exported or named in _COLUMN_EXCLUSIONS with a reason, and
|
||||||
A new column must now be exported or named in _COLUMN_EXCLUSIONS with a
|
forgetting is not expressible.
|
||||||
reason. Forgetting is no longer expressible.
|
|
||||||
"""
|
"""
|
||||||
model, helper = _column_guard_targets()[table]
|
model, helper = _column_guard_targets()[table]
|
||||||
[row] = helper([_stand_in(model)])
|
[row] = helper([_stand_in(model)])
|
||||||
@@ -536,8 +532,7 @@ def test_every_table_is_either_backed_up_or_explicitly_excluded():
|
|||||||
)
|
)
|
||||||
|
|
||||||
# And the reverse: a name in either list that no longer exists is a lie the
|
# And the reverse: a name in either list that no longer exists is a lie the
|
||||||
# guard would otherwise keep telling. This half is what caught "embeddings",
|
# guard would otherwise keep telling.
|
||||||
# "invitations" and "password_resets" — three entries that named nothing.
|
|
||||||
phantom = accounted - schema
|
phantom = accounted - schema
|
||||||
assert not phantom, (
|
assert not phantom, (
|
||||||
f"backup lists table(s) that are not in the schema: {sorted(phantom)}. "
|
f"backup lists table(s) that are not in the schema: {sorted(phantom)}. "
|
||||||
|
|||||||
@@ -86,9 +86,8 @@ async def test_build_autoinject_hint_titles_only_with_margin_gate():
|
|||||||
assert "get_note(id)" in out["context"]
|
assert "get_note(id)" in out["context"]
|
||||||
assert "↳" not in out["context"]
|
assert "↳" not in out["context"]
|
||||||
# Telemetry fired for BOTH retrievals this path runs: the scored menu and
|
# Telemetry fired for BOTH retrievals this path runs: the scored menu and
|
||||||
# the reuse-slot query competing against it. The slot's query used to be
|
# the reuse-slot query competing against it. The hit the slot displaces is
|
||||||
# the one unlogged retrieval on this path — the hit it displaced was in
|
# in retrieval_logs, so the query that displaced it must be too.
|
||||||
# retrieval_logs, and the query that displaced it must be too.
|
|
||||||
sources = [c.kwargs["source"] for c in rec.call_args_list]
|
sources = [c.kwargs["source"] for c in rec.call_args_list]
|
||||||
assert sources == ["auto_inject", "reuse_slot", "lesson_slot"]
|
assert sources == ["auto_inject", "reuse_slot", "lesson_slot"]
|
||||||
|
|
||||||
@@ -235,9 +234,9 @@ async def test_a_project_id_that_does_not_resolve_is_reported_not_dropped():
|
|||||||
another instance, or names a deleted project, the session has to be told —
|
another instance, or names a deleted project, the session has to be told —
|
||||||
it cannot infer it from an absence.
|
it cannot infer it from an absence.
|
||||||
|
|
||||||
This used to render nothing whatsoever: the branch hung off `if project_id`
|
The trap is a branch hung off `if project_id` as an `elif`: an id that was
|
||||||
as an `elif`, so an id that was sent and failed took the outer arm, found
|
sent and failed takes the outer arm, finds no project, and falls out of the
|
||||||
no project, and fell out of the block having said nothing at all.
|
block having said nothing at all.
|
||||||
"""
|
"""
|
||||||
from scribe.services.plugin_context import build_session_context
|
from scribe.services.plugin_context import build_session_context
|
||||||
with patch("scribe.services.plugin_context.projects_svc.get_project",
|
with patch("scribe.services.plugin_context.projects_svc.get_project",
|
||||||
|
|||||||
@@ -929,10 +929,10 @@ async def test_a_section_is_complete_only_from_its_latest_contributor(
|
|||||||
def test_a_call_that_returned_nothing_still_records_what_it_nearly_showed():
|
def test_a_call_that_returned_nothing_still_records_what_it_nearly_showed():
|
||||||
"""The whole point, at the payload grain.
|
"""The whole point, at the payload grain.
|
||||||
|
|
||||||
This is the row a threshold is tuned from and the one that used to carry no
|
This is the row a threshold is tuned from: `top_score` and `min_score` are
|
||||||
score at all: `top_score` and `min_score` are both null here, correctly, and
|
both null here, correctly, and without the near-miss a reader cannot tell a
|
||||||
a reader was left unable to tell a bar rejecting 0.71s from one rejecting
|
bar rejecting 0.71s from one rejecting 0.30s. Both render as a zero-result
|
||||||
0.30s. Both render as a zero-result call.
|
call.
|
||||||
"""
|
"""
|
||||||
p = _build_payload(
|
p = _build_payload(
|
||||||
user_id=1, source="pre_tool_rule", query="git push --force",
|
user_id=1, source="pre_tool_rule", query="git push --force",
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ def test_parse_does_not_promote_a_caller_tag_to_language():
|
|||||||
|
|
||||||
|
|
||||||
def test_caller_tag_survives_an_update_round_trip_without_a_language():
|
def test_caller_tag_survives_an_update_round_trip_without_a_language():
|
||||||
# Regression: the tag used to be read back as the language, then dropped
|
# A tag read back as the language would be dropped from the extra-tag set
|
||||||
# from the extra-tag set on re-compose — so it silently disappeared.
|
# on re-compose — and silently disappear.
|
||||||
tags = s.compose_tags("", ["auth"])
|
tags = s.compose_tags("", ["auth"])
|
||||||
fields = s.parse_snippet_fields("n — u", s.compose_body(code="x = 1"), tags)
|
fields = s.parse_snippet_fields("n — u", s.compose_body(code="x = 1"), tags)
|
||||||
extra = [t for t in tags if t not in (s.SNIPPET_TAG, fields["language"])]
|
extra = [t for t in tags if t not in (s.SNIPPET_TAG, fields["language"])]
|
||||||
|
|||||||
@@ -124,8 +124,8 @@ async def test_purge_expired_deletes_across_models_when_positive():
|
|||||||
|
|
||||||
|
|
||||||
def test_owner_clause_scopes_every_model():
|
def test_owner_clause_scopes_every_model():
|
||||||
"""Regression: every trash op must owner-scope, including topics/rules
|
"""Every trash op must owner-scope, topics and rules included — a model
|
||||||
which previously had NO owner check (IDOR across tenants)."""
|
with no owner check is an IDOR across tenants."""
|
||||||
from scribe.services.trash import _owner_clause, _MODEL_FOR
|
from scribe.services.trash import _owner_clause, _MODEL_FOR
|
||||||
from scribe.models.note import Note
|
from scribe.models.note import Note
|
||||||
from scribe.models.rulebook import Rulebook, RulebookTopic, Rule
|
from scribe.models.rulebook import Rulebook, RulebookTopic, Rule
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"""Editing a shared record: an editor grant is enough, a viewer grant is not.
|
"""Editing a shared record: an editor grant is enough, a viewer grant is not.
|
||||||
|
|
||||||
The agent and web paths used to disagree — and worse, within the agent path
|
The agent and web paths must agree, and so must the doors within one path: a
|
||||||
`delete_snippet` honoured editor shares while `update_snippet` did not, so a
|
`delete_snippet` that honours editor shares beside an `update_snippet` that
|
||||||
colleague's snippet could be destroyed through an agent but not improved. Both
|
does not lets a colleague's snippet be destroyed through an agent but not
|
||||||
now resolve the read scope and then require WRITE, which is what the sharing UI
|
improved. Every door resolves the read scope and then requires WRITE, which is what the sharing UI
|
||||||
already promises: viewer / editor / admin are distinct grants.
|
already promises: viewer / editor / admin are distinct grants.
|
||||||
|
|
||||||
A record readable-but-not-writable raises PermissionError rather than reporting
|
A record readable-but-not-writable raises PermissionError rather than reporting
|
||||||
|
|||||||
@@ -33,11 +33,11 @@ def test_something_actually_reads_the_version_endpoint():
|
|||||||
|
|
||||||
|
|
||||||
def test_the_footer_does_not_default_to_a_plausible_version():
|
def test_the_footer_does_not_default_to_a_plausible_version():
|
||||||
"""The regression this readout was built to remove.
|
"""A failed fetch does not render a plausible version.
|
||||||
|
|
||||||
`appVersion` used to start life as the literal `"dev"` and the fetch
|
If `appVersion` starts life as the literal `"dev"` and the fetch swallows
|
||||||
swallowed its own failure, so an instance that could not answer rendered
|
its own failure, an instance that cannot answer renders exactly what a
|
||||||
exactly what a healthy local build renders. Two very different states, one
|
healthy local build renders. Two very different states, one
|
||||||
string, and no way to tell them apart from the page.
|
string, and no way to tell them apart from the page.
|
||||||
|
|
||||||
Pinned as "the ref does not start at a version-shaped literal" rather than
|
Pinned as "the ref does not start at a version-shaped literal" rather than
|
||||||
|
|||||||
@@ -1342,10 +1342,10 @@ async def test_a_pulled_snippet_already_seen_is_evidence_not_menu():
|
|||||||
# ...and the menu carries it, marked.
|
# ...and the menu carries it, marked.
|
||||||
seven = next(line for line in out["context"].splitlines() if "#7" in line)
|
seven = next(line for line in out["context"].splitlines() if "#7" in line)
|
||||||
assert "seen" in seven
|
assert "seen" in seven
|
||||||
# #8 (0.80) now falls OUTSIDE the margin band, because #7 (0.91) anchors it
|
# #8 (0.80) falls OUTSIDE the margin band, because #7 (0.91) anchors it
|
||||||
# at 0.81 instead of being filtered out first. That is the point rather than
|
# at 0.81 instead of being filtered out first. That is the point rather than
|
||||||
# a casualty: the band measures distance from the best answer, and the best
|
# a casualty: the band measures distance from the best answer, and the best
|
||||||
# answer here is #7 — suppressing it used to promote a materially weaker hit
|
# answer here is #7 — suppressing it would promote a materially weaker hit
|
||||||
# into a slot it had not earned, with nothing in the output saying so.
|
# into a slot it had not earned, with nothing in the output saying so.
|
||||||
assert out["note_ids"] == [7]
|
assert out["note_ids"] == [7]
|
||||||
# The stamp saw the pull and the resemblance score for this payload.
|
# The stamp saw the pull and the resemblance score for this payload.
|
||||||
|
|||||||
Reference in New Issue
Block a user