docs(6207): migrations 0088 and 0090-0094 state their rationale in terms of the data

Milestone/step labels, record ids, the rescoped-rule date and the 296-call
measurement go; each header states what the change protects. "rule 36"
citations become the DROP + ADD practice they named.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 20:46:36 -04:00
co-authored by Claude Opus 5.5
parent 40f603ba28
commit 9e1da4af3d
6 changed files with 25 additions and 38 deletions
@@ -1,5 +1,4 @@
"""rules gain a trigger, a tier, canon tags and typed edges (milestone 307 """rules gain a trigger, a tier, canon tags and typed edges
step 3, decision note 3026)
Revision ID: 0088 Revision ID: 0088
Revises: 0087 Revises: 0087
@@ -31,8 +30,8 @@ down_revision = "0087"
branch_labels = None branch_labels = None
depends_on = None depends_on = None
# Kept in one place so upgrade and the CHECK agree by construction (rule 36: # Kept in one place so upgrade and the CHECK agree by construction (a
# a whitelisted value means DROP + ADD CONSTRAINT in the same migration — # whitelisted value means DROP + ADD CONSTRAINT in the same migration —
# there is no prior constraint here, so the pair is created together). # there is no prior constraint here, so the pair is created together).
_TIERS = ("always_on", "conditional") _TIERS = ("always_on", "conditional")
_RELATION_KINDS = ("co_surfaces", "overrides", "elaborates") _RELATION_KINDS = ("co_surfaces", "overrides", "elaborates")
+4 -6
View File
@@ -1,5 +1,4 @@
"""a rule can carry its own check — verify_with, expires_when, verified_at """a rule can carry its own check — verify_with, expires_when, verified_at
(milestone 312 step 1)
Revision ID: 0090 Revision ID: 0090
Revises: 0089 Revises: 0089
@@ -11,10 +10,9 @@ know they did. A CONSTRAINT is a fact about someone else's software: a
runner's shell, a bot's config, a tool that exists. Nobody is present when runner's shell, a bot's config, a tool that exists. Nobody is present when
that goes false. that goes false.
Milestone 307's rulebook audit found nine stale sites. Every one was a What goes stale in a rulebook is the constraints, not the norms — and a
constraint; not one norm had rotted. One of them had been telling every stale constraint keeps instructing every session that loads it, long after
session to skip database-backed tests for weeks while the integration lane the world it described has moved.
sat green in the workflow.
Three nullable columns, so a rule can say how to check itself: Three nullable columns, so a rule can say how to check itself:
@@ -31,7 +29,7 @@ none of them. A null `verify_with` is not an omission — it is the honest
marker of "this one is a decision, and there is nothing to go and check." marker of "this one is a decision, and there is nothing to go and check."
That signal only works if the field stays empty wherever it belongs empty. That signal only works if the field stays empty wherever it belongs empty.
No CHECK constraint is involved, so rule 36 does not apply here. Nothing is No CHECK constraint is involved. Nothing is
backfilled: a migration cannot invent a check any more than 0088 could backfilled: a migration cannot invent a check any more than 0088 could
invent a trigger. invent a trigger.
""" """
+2 -3
View File
@@ -1,5 +1,4 @@
"""task_kind gains 'spike' — the investigation, not the change """task_kind gains 'spike' — the investigation, not the change
(milestone 312 step 5)
Revision ID: 0091 Revision ID: 0091
Revises: 0090 Revises: 0090
@@ -12,12 +11,12 @@ end of it. "Find out whether the runner can be given a bash shell" is not
work, and filing it as work makes a finished investigation look like an work, and filing it as work makes a finished investigation look like an
abandoned change. abandoned change.
It is the record a failed check asks for. Milestone 312 gave rules a It is the record a failed check asks for. Rules carry a
`verify_with`; when one of those fails, the rule is wrong and the next move `verify_with`; when one of those fails, the rule is wrong and the next move
is often to go and find out what replaced it. `notes.arose_from_id` already is often to go and find out what replaced it. `notes.arose_from_id` already
exists (0065), so that constraint -> spike link needs no further schema. exists (0065), so that constraint -> spike link needs no further schema.
Rule 36: `task_kind` is gated by a CHECK whitelist, so the value and the `task_kind` is gated by a CHECK whitelist, so the value and the
widened constraint land in the SAME migration — DROP then ADD, exactly as widened constraint land in the SAME migration — DROP then ADD, exactly as
0065 did when it introduced 'issue'. Adding the value and constraining it 0065 did when it introduced 'issue'. Adding the value and constraining it
later leaves a window where the database accepts anything. later leaves a window where the database accepts anything.
+4 -7
View File
@@ -1,5 +1,4 @@
"""a note can carry its own check — verify_with, expires_when, verified_at """a note can carry its own check — verify_with, expires_when, verified_at
(milestone 317 step 1)
Revision ID: 0092 Revision ID: 0092
Revises: 0091 Revises: 0091
@@ -32,8 +31,7 @@ Three nullable columns:
WHICH ROWS THESE ARE FOR. `notes` is one table holding notes, tasks, WHICH ROWS THESE ARE FOR. `notes` is one table holding notes, tasks,
snippets and processes, so these columns land on all of them. Only non-task, snippets and processes, so these columns land on all of them. Only non-task,
non-snippet records are OFFERED them (milestone 317 decisions 1 and 2, gated non-snippet records are OFFERED them (gated at the service): a task's decay is its status, and a snippet
at the service in step 2): a task's decay is its status, and a snippet
already carries a richer, location-aware verdict in `data.verification`. The already carries a richer, location-aware verdict in `data.verification`. The
columns exist on the other rows and stay null there; a gate that lives in columns exist on the other rows and stay null there; a gate that lives in
the schema would have meant a partial index or a CHECK across three columns the schema would have meant a partial index or a CHECK across three columns
@@ -44,7 +42,7 @@ value of the sweep is that its output is short. A null `verify_with` is not
an omission; it is the honest marker of "this one is a decision, and there an omission; it is the honest marker of "this one is a decision, and there
is nothing to go and check." is nothing to go and check."
No CHECK constraint is involved, so rule 36 does not apply. Nothing is No CHECK constraint is involved. Nothing is
backfilled: a migration cannot invent a check. backfilled: a migration cannot invent a check.
""" """
import sqlalchemy as sa import sqlalchemy as sa
@@ -69,9 +67,8 @@ def upgrade() -> None:
# #
# Still a sequential scan's job at this size, and an index on # Still a sequential scan's job at this size, and an index on
# (verified_at) filtered to `verify_with IS NOT NULL` would be maintained # (verified_at) filtered to `verify_with IS NOT NULL` would be maintained
# on every note write to serve one operator-initiated query. If step 3's # on every note write to serve one operator-initiated query. If a real
# live acceptance measures otherwise, add it there against a real plan # plan says otherwise, add it then rather than guessing here.
# rather than guessing here.
def downgrade() -> None: def downgrade() -> None:
+5 -8
View File
@@ -1,4 +1,4 @@
"""rules gain an edit history — rule_versions (milestone 323 step 1) """rules gain an edit history — rule_versions
Revision ID: 0093 Revision ID: 0093
Revises: 0092 Revises: 0092
@@ -7,12 +7,9 @@ Create Date: 2026-08-29
The sibling `note_versions` has had for a long time. A note's every meaningful The sibling `note_versions` has had for a long time. A note's every meaningful
edit is snapshotted, and the design-system note calls that history "the edit is snapshotted, and the design-system note calls that history "the
changelog". A RULE — which binds behaviour on every session that loads it — changelog". A RULE — which binds behaviour on every session that loads it —
had nothing: an edit destroyed what it used to say, with no record anywhere. had nothing: an edit destroyed what it used to say, with no record anywhere,
and keeping a superseded statement meant a person remembering to copy it
Rescoping rule 79 on 2026-08-29 is what surfaced it. The superseded statement somewhere. The more consequential record had the weaker protection.
had to be hand-copied into a task log to survive the edit (#3237), which is
not a process, it is a person remembering. The more consequential record had
the weaker protection.
Three things are deliberately NOT copied from note_versions, and each is a Three things are deliberately NOT copied from note_versions, and each is a
guard that exists there for a reason that does not hold here: guard that exists there for a reason that does not hold here:
@@ -31,7 +28,7 @@ guard that exists there for a reason that does not hold here:
CASCADE: deleting a user must not erase the history of the rules they edited. CASCADE: deleting a user must not erase the history of the rules they edited.
The edit still happened and the rule still binds because of it. The edit still happened and the rule still binds because of it.
No CHECK constraint, so rule 36 does not apply. Nothing is backfilled — a No CHECK constraint is involved. Nothing is backfilled — a
migration cannot invent the text a rule used to have, and inventing "the migration cannot invent the text a rule used to have, and inventing "the
current text, as of now" would be worse than an empty history, because it current text, as of now" would be worse than an empty history, because it
would look like a record of an edit that never occurred. would look like a record of an edit that never occurred.
+7 -10
View File
@@ -1,4 +1,4 @@
"""add rule_usage_events — was a surfaced rule ever read? (milestone 333 step 1) """add rule_usage_events — was a surfaced rule ever read?
Revision ID: 0094 Revision ID: 0094
Revises: 0093 Revises: 0093
@@ -9,18 +9,15 @@ table to arrive after `rule_embeddings` and `rule_versions` — each one added
because the rule side kept inheriting machinery built for notes and getting because the rule side kept inheriting machinery built for notes and getting
the weaker version of it. the weaker version of it.
WHAT IT MEASURES. The write-path standing-rule arm is the only retrieval WHAT IT MEASURES. A standing-rule arm that returns something on every call
surface in Scribe whose usefulness cannot be observed, and — not coincidentally and clears its threshold every time is either a perfectly tuned surface or a
— the only one that has never declined to fire. Over 30 days it took 296 calls, bar it cannot fail to clear, and `retrieval_logs`
returned something on every one, and cleared its threshold 100% of the time,
while every other surface declines most of the time (#3311). That is either a
perfectly tuned surface or a bar it cannot fail to clear, and `retrieval_logs`
cannot tell them apart: it records what the ranker scored, never whether the cannot tell them apart: it records what the ranker scored, never whether the
hint was any use. hint was any use.
WHY NOT A rule_id COLUMN ON note_usage_events. The row shares no note-specific WHY NOT A rule_id COLUMN ON note_usage_events. The row shares no note-specific
fields and the aggregate readout is the same shape, which is the strongest case fields and the aggregate readout is the same shape, which is the strongest case
for sharing that note #3163 admits. What decides against it is identity at for sharing there is. What decides against it is identity at
RESTORE: `note_usage_events`'s importer maps `note_id` through `note_id_map` RESTORE: `note_usage_events`'s importer maps `note_id` through `note_id_map`
and drops what does not resolve. A rule id parked in that column would come and drops what does not resolve. A rule id parked in that column would come
back from a backup silently reattached to whatever note took that number — back from a backup silently reattached to whatever note took that number —
@@ -34,8 +31,8 @@ and dies with it; telemetry outlives the row it describes. Deleting a rule must
not erase the evidence that it was surfaced forty times and opened never, since not erase the evidence that it was surfaced forty times and opened never, since
that evidence is exactly the case for having deleted it. that evidence is exactly the case for having deleted it.
No CHECK on `event`, matching the note twin. Rule 36 governs adding a value to No CHECK on `event`, matching the note twin. DROP + ADD governs adding a value
a column that is already gated; it does not require gating one that never was, to a column that is already gated; it does not require gating one that never was,
and a two-member enum whose members are written by two functions in one module and a two-member enum whose members are written by two functions in one module
is not where that discipline earns its cost. is not where that discipline earns its cost.