Compare commits

...
4 Commits
Author SHA1 Message Date
bvandeusenandClaude Opus 5.5 79655fd19f docs(6207): the remaining migrations state their rationale in terms of the data
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 59s
CI & Build / integration (push) Successful in 1m32s
CI & Build / Python tests (push) Successful in 2m33s
CI & Build / Build & push image (push) Successful in 26s
Record, milestone, phase and plan citations go from every migration header;
the "end before start" fix (0043) and the suppressed-count column (0095)
state the condition they prevent rather than the incident. 0053 and 0055
pointed at design specs under docs/superpowers that are not in the repo;
those pointers are removed.

Kept: alembic's generated "Create Date:" headers (template metadata, not
rationale), 0121's ARM_BORN justification beside the constant, and 0051's
illustrative "update task 42" format example.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:49:32 -04:00
bvandeusenandClaude Opus 5.5 79059ca1e7 docs(6207): migrations 0079, 0082, 0087, 0104, 0111, 0112, 0116-0118 drop milestone and record citations
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:47:43 -04:00
bvandeusenandClaude Opus 5.5 8c9bb000fa docs(6207): migrations 0101, 0105, 0106, 0108, 0120, 0121 drop citations and the operator quote
0121 keeps the ARM_BORN date beside the constant it justifies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:47:14 -04:00
bvandeusenandClaude Opus 5.5 9e1da4af3d 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>
2026-10-21 20:46:36 -04:00
56 changed files with 132 additions and 167 deletions
@@ -1,13 +1,11 @@
"""replace events.end_dt with duration_minutes (Fable #160)
"""replace events.end_dt with duration_minutes
Revision ID: 0043
Revises: 0042
Create Date: 2026-04-29
Structural fix for the "end before start" bug class observed on prod
2026-04-29: an event landed with end_dt 32 days before start_dt due
to a tool-call mishap, then disappeared from upcoming-list filters.
Storing duration instead of end_dt makes the invalid state
Structural fix for the "end before start" bug class: an event whose end_dt
precedes its start_dt disappears from upcoming-list filters. Storing duration instead of end_dt makes the invalid state
inexpressible at the schema level (duration_minutes >= 0).
Backfill rules:
@@ -5,7 +5,7 @@ Revises: 0043
Create Date: 2026-05-13
Adds two columns to the ``notes`` table to support the task-as-durable-record
design (spec 2026-05-13):
design:
- ``description``: user-stated goal / initial context. Meaningful when
``is_task=true``; left NULL on knowledge notes.
@@ -4,7 +4,7 @@ Revision ID: 0048
Revises: 0047
Create Date: 2026-05-22
Phase 2 of the conversation+curator architecture (Fable #172). The
Part of the conversation+curator architecture. The
scheduler runs every 15 minutes and processes any journal conversation
with messages newer than its `last_curator_run_at` timestamp. NULL
means "never run; process all of today on first sweep."
@@ -4,7 +4,7 @@ Revision ID: 0049
Revises: 0048
Create Date: 2026-05-22
Phase 3 of the conversation+curator architecture (Fable #172). The
Part of the conversation+curator architecture. The
curator's final summary line (≤ 240 chars) is persisted here so the
NEXT chat turn can include it in the system prompt. This is the
feedback loop that closes the architecture — without it, the chat
@@ -4,13 +4,11 @@ Revision ID: 0053
Revises: 0052
Create Date: 2026-05-27
Phase 9 of the MCP-first pivot. The Python models for these tables were
deleted in Phase 8 (commit 91bafb6); this migration drops the orphan
Part of the MCP-first pivot. The Python models for these tables are gone;
this migration drops the orphan
SQL tables and cleans up dead per-user setting rows.
Hard-cutover: existing rows in these tables are discarded. The
accompanying spec at docs/superpowers/specs/2026-05-26-mcp-first-pivot-design.md
explicitly accepted this loss.
Hard-cutover: existing rows in these tables are discarded, deliberately.
"""
from alembic import op
+1 -1
View File
@@ -4,7 +4,7 @@ Revision ID: 0054
Revises: 0053
Create Date: 2026-05-27
The image cache was wired into the LLM image-search tool (removed in Phase 8).
The image cache was wired into the LLM image-search tool, which is gone.
With no producer or consumer left, the table is dropped here.
"""
from alembic import op
+2 -2
View File
@@ -6,8 +6,8 @@ Create Date: 2026-05-27
Adds the Scribe Rulebook hierarchy as a fourth top-level entity, sibling
to Project. Rules carry a structural (statement, why, how_to_apply)
triple. Projects subscribe to Rulebooks (many-to-many). See the design
doc at docs/superpowers/specs/2026-05-27-scribe-rulebook-design.md.
triple. Projects subscribe to Rulebooks (many-to-many; migration 0101
later drops subscriptions).
"""
from alembic import op
import sqlalchemy as sa
+1 -1
View File
@@ -4,7 +4,7 @@ Revision ID: 0066
Revises: 0065
Create Date: 2026-06-14
T3 of plan #819. The milestone becomes the plan container: its `body` holds
The milestone becomes the plan container: its `body` holds
the design/intent/purpose (markdown), `description` stays the one-liner, and
individual steps live as first-class child tasks (milestone_id) instead of
checkboxes crammed into a kind=plan task body. start_planning is reworked to
+2 -3
View File
@@ -4,8 +4,7 @@ Revision ID: 0074
Revises: 0073
Create Date: 2026-07-31
The operator's shape for #254: prose doesn't live as one-offs, it lives centrally
on the system. Two fields rather than one per category:
Prose doesn't live as one-offs; it lives centrally on the system. Two fields rather than one per category:
design_systems.guidance the narrative a token table cannot hold — aesthetic,
voice and tone, what is deliberately out of scope.
@@ -17,7 +16,7 @@ on the system. Two fields rather than one per category:
Free-form rather than a column per category on purpose. A schema with `voice`,
`aesthetic` and `scope` columns would bake one rulebook's table of contents into
every install (rule #115), and the next install's design system would have three
every install, and the next install's design system would have three
empty columns and nowhere to put what it actually cares about.
Both nullable: a design system with no prose at all is complete, not a draft.
+2 -2
View File
@@ -4,7 +4,7 @@ Revision ID: 0076
Revises: 0075
Create Date: 2026-08-07
Step 1 of milestone #278. Structure only — nothing reads or writes the new
Structure only — nothing reads or writes the new
table yet, and nothing behaves differently after this runs.
## What the table is for
@@ -39,7 +39,7 @@ and not for notes, so the column looked like the notes-side half of that
feature, modelled and abandoned.
It is dropped rather than repurposed for supersession, and the distinction is
the point (#2483): consolidation folds several records into one survivor and
the point: consolidation folds several records into one survivor and
destroys the originals. Merging two snippets is lossless — one helper, several
call sites. Folding two dev-logs means writing a summary and losing what each
actually said. Supersession is the opposite act: both records survive, and the
+1 -1
View File
@@ -1,4 +1,4 @@
"""Chunked embeddings: one note_embeddings row per chunk (#280)
"""Chunked embeddings: one note_embeddings row per chunk
Revision ID: 0077
Revises: 0076
+2 -2
View File
@@ -1,4 +1,4 @@
"""Forge connections move to the user level (#2778)
"""Forge connections move to the user level
Revision ID: 0078
Revises: 0077
@@ -12,7 +12,7 @@ keyring, with an optional per-project pin (projects.forge_connection_id).
The data move carries the existing admin config into a connection row for the
first admin user (host parsed from the base URL), then deletes the old
setting keys outright — no legacy dual-read (rule #22). The env-var channel
setting keys outright — no legacy dual-read. The env-var channel
(FORGE_KIND/FORGE_BASE_URL/FORGE_TOKEN) is untouched by this migration; it
survives as an implicit keyring entry for admin users only.
"""
+3 -3
View File
@@ -1,13 +1,13 @@
"""The shape ledger: code_shapes (#2787, milestone 294)
"""The shape ledger: code_shapes
Revision ID: 0079
Revises: 0078
Create Date: 2026-08-19
The accounting half of the pattern system (governing note 2786): the snippet
The accounting half of the pattern system: the snippet
library records canon (small); this table accounts for EVERY shape the
coverage extractor finds in a bound repo (total). Rows arrive `unclassified`
from the coverage sync (step 2) and gain judgments — canonical / instance /
from the coverage sync and gain judgments — canonical / instance /
variant / exempt — from audits, hooks, and the mechanical proposer.
Unclassified IS the todo list.
"""
@@ -1,4 +1,4 @@
"""Shape fingerprints + the mechanical proposer's columns (#2792, milestone 294)
"""Shape fingerprints + the mechanical proposer's columns
Revision ID: 0080
Revises: 0079
@@ -1,4 +1,4 @@
"""Shape history, recheck, and the divergence flag (#2793, milestone 294)
"""Shape history, recheck, and the divergence flag
Revision ID: 0081
Revises: 0080
+5 -5
View File
@@ -1,13 +1,13 @@
"""Per-binding ref — the branch a project's ledger follows (#2873, milestone 294)
"""Per-binding ref — the branch a project's ledger follows
Revision ID: 0082
Revises: 0081
Create Date: 2026-08-21
A repo binding used to imply the repo's default branch; the shape ledger
therefore only saw work after a merge to main, while the operator's work
lands on dev (rule 1). `ref` names the branch the coverage refresh reads —
NULL keeps today's behaviour (the forge's default branch).
Without a ref, a repo binding implies the repo's default branch, so the shape
ledger only sees work after a merge — while a project may do its work on a
dev branch. `ref` names the branch the coverage refresh reads; NULL means the
forge's default branch.
"""
import sqlalchemy as sa
from alembic import op
@@ -1,4 +1,4 @@
"""Exempt/variant reason codes — a small fixed catalogue beside the prose (#2874, milestone 294)
"""Exempt/variant reason codes — a small fixed catalogue beside the prose
Revision ID: 0083
Revises: 0082
+1 -1
View File
@@ -1,4 +1,4 @@
"""code_shape_uses — consumption edges, separate from conformance (#2870, milestone 294)
"""code_shape_uses — consumption edges, separate from conformance
Revision ID: 0084
Revises: 0083
+1 -1
View File
@@ -1,4 +1,4 @@
"""Project inception: the decision record + always-on rulebook exclusions (milestone 297)
"""Project inception: the decision record + always-on rulebook exclusions
Revision ID: 0085
Revises: 0084
@@ -1,4 +1,4 @@
"""code_shape_consumers — the CSS consumer map (milestone 302, note 2917)
"""code_shape_consumers — the CSS consumer map
Revision ID: 0086
Revises: 0085
+3 -3
View File
@@ -1,12 +1,12 @@
"""canonical_systems — the global area vocabulary, promoted from a constant
to a table (milestone 307 step 1, decision note 3026)
to a table
Revision ID: 0087
Revises: 0086
Create Date: 2026-08-26
The eight standard area names already existed as `STANDARD_SYSTEMS`, a tuple in
services/systems.py that milestone 297 seeds into a project at inception. A
services/systems.py that is seeded into a project at inception. A
constant cannot be referenced: a rule that applies across projects has nothing
to point at, because `systems.project_id` is NOT NULL and a family rule cannot
be chained to one project's row. This makes the vocabulary a table so it can be
@@ -35,7 +35,7 @@ depends_on = None
# service computes (canonical_slug: lowercase, "&" -> "and", non-alphanumerics
# collapsed to "-"). Charters stay generic on purpose: a project refines its
# own System's description, never this one. Nothing here names an app, a repo,
# a vendor or a house convention — the catalog ships to every install (rule 115).
# a vendor or a house convention — the catalog ships to every install.
_SEED = (
("CI & Release", "ci-and-release",
"How the project is verified and shipped: pipelines, runners, image/artifact builds, release tagging and rollback."),
@@ -1,5 +1,4 @@
"""rules gain a trigger, a tier, canon tags and typed edges (milestone 307
step 3, decision note 3026)
"""rules gain a trigger, a tier, canon tags and typed edges
Revision ID: 0088
Revises: 0087
@@ -31,8 +30,8 @@ down_revision = "0087"
branch_labels = None
depends_on = None
# Kept in one place so upgrade and the CHECK agree by construction (rule 36:
# a whitelisted value means DROP + ADD CONSTRAINT in the same migration —
# Kept in one place so upgrade and the CHECK agree by construction (a
# whitelisted value means DROP + ADD CONSTRAINT in the same migration —
# there is no prior constraint here, so the pair is created together).
_TIERS = ("always_on", "conditional")
_RELATION_KINDS = ("co_surfaces", "overrides", "elaborates")
+1 -2
View File
@@ -1,5 +1,4 @@
"""rule_embeddings — rules become findable by meaning (milestone 307 step 4,
decision note 3026)
"""rule_embeddings — rules become findable by meaning
Revision ID: 0089
Revises: 0088
+4 -6
View File
@@ -1,5 +1,4 @@
"""a rule can carry its own check — verify_with, expires_when, verified_at
(milestone 312 step 1)
Revision ID: 0090
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
that goes false.
Milestone 307's rulebook audit found nine stale sites. Every one was a
constraint; not one norm had rotted. One of them had been telling every
session to skip database-backed tests for weeks while the integration lane
sat green in the workflow.
What goes stale in a rulebook is the constraints, not the norms — and a
stale constraint keeps instructing every session that loads it, long after
the world it described has moved.
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."
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
invent a trigger.
"""
+2 -3
View File
@@ -1,5 +1,4 @@
"""task_kind gains 'spike' — the investigation, not the change
(milestone 312 step 5)
Revision ID: 0091
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
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
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.
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
0065 did when it introduced 'issue'. Adding the value and constraining it
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
(milestone 317 step 1)
Revision ID: 0092
Revises: 0091
@@ -32,8 +31,7 @@ Three nullable columns:
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,
non-snippet records are OFFERED them (milestone 317 decisions 1 and 2, gated
at the service in step 2): a task's decay is its status, and a snippet
non-snippet records are OFFERED them (gated at the service): a task's decay is its status, and a snippet
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
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
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.
"""
import sqlalchemy as sa
@@ -69,9 +67,8 @@ def upgrade() -> None:
#
# 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
# on every note write to serve one operator-initiated query. If step 3's
# live acceptance measures otherwise, add it there against a real plan
# rather than guessing here.
# on every note write to serve one operator-initiated query. If a real
# plan says otherwise, add it then rather than guessing here.
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
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
edit is snapshotted, and the design-system note calls that history "the
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.
Rescoping rule 79 on 2026-08-29 is what surfaced it. The superseded statement
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.
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
somewhere. The more consequential record had the weaker protection.
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:
@@ -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.
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
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.
+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
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
the weaker version of it.
WHAT IT MEASURES. The write-path standing-rule arm is the only retrieval
surface in Scribe whose usefulness cannot be observed, and — not coincidentally
— the only one that has never declined to fire. Over 30 days it took 296 calls,
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`
WHAT IT MEASURES. A standing-rule arm that returns something on every call
and clears its threshold every time 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
hint was any use.
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
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`
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 —
@@ -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
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
a column that is already gated; it does not require gating one that never was,
No CHECK on `event`, matching the note twin. DROP + ADD governs adding a value
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
is not where that discipline earns its cost.
@@ -1,4 +1,4 @@
"""add retrieval_logs.suppressed_count — tell a ranker decline from a repeat (#3497)
"""add retrieval_logs.suppressed_count — tell a ranker decline from a repeat
Revision ID: 0095
Revises: 0094
@@ -20,9 +20,8 @@ the dropped rows never come back and there is nothing to count.
NULLABLE, AND THE NULL IS THE POINT. A surface that does not measure
suppression stores NULL, not 0, and the readout renders it as "not measured"
rather than "none". Defaulting to 0 would make an unmeasured surface look like
a perfectly clean one — the exact substitution of an artifact for a
measurement that #3311 made and that #3497 exists to correct. Doing it again,
in the migration that fixes it, would be its own small joke.
a perfectly clean one — the substitution of an artifact for a measurement
that this column exists to prevent.
No backfill for the same reason: existing rows genuinely do not know, and
saying so is the honest state. `retrieval_logs` is not restored from backup,
@@ -1,4 +1,4 @@
"""add retrieval_logs.best_available_score — the score the bar rejected (#3670)
"""add retrieval_logs.best_available_score — the score the bar rejected
Revision ID: 0096
Revises: 0095
@@ -34,7 +34,7 @@ NULLABLE, AND UNBACKFILLED, for the reason 0095 spells out: a row written
before this shipped genuinely does not know what its best rejected candidate
scored, and saying so is the honest state. A 0.0 default would read as "the
corpus had nothing remotely relevant" — an artifact standing in for a
measurement, which is the whole defect this milestone corrects.
measurement, which is the defect this column corrects.
`retrieval_logs` is not restored from backup, so no importer changes.
@@ -1,4 +1,4 @@
"""add retrieval_logs.best_available_id — WHICH record the bar refused (#3807)
"""add retrieval_logs.best_available_id — WHICH record the bar refused
Revision ID: 0097
Revises: 0096
+2 -2
View File
@@ -1,4 +1,4 @@
"""rules gain a `kind` — a preference is a rule that does not bind (#3849)
"""rules gain a `kind` — a preference is a rule that does not bind
Revision ID: 0098
Revises: 0097
@@ -37,7 +37,7 @@ install upgrades and every existing row keeps the force it had. That is the
same reasoning 0088 used for `tier`, and it is the reason both are safe to
apply without reading the data first.
The CHECK is created with the column (rule 36: there is no prior constraint,
The CHECK is created with the column (there is no prior constraint,
so the pair is created together — a value added to it LATER does DROP + ADD
in one migration).
@@ -7,7 +7,7 @@ Create Date: 2026-09-11
`pre_tool_rule` retrieves against the RAW COMMAND TEXT and `write_path_rule`
against the code being written, so whatever was on the command line or in the
buffer is what `record_retrieval` wrote into `retrieval_logs.query`. A command
that exported a token stored the token (#3925).
that exported a token stored the token.
`services/retrieval_telemetry.scrub_secrets` closes that going forward — the
value never reaches the column. It cannot reach BACKWARDS, and this does: it
+2 -2
View File
@@ -4,14 +4,14 @@ Revision ID: 0100
Revises: 0099
Create Date: 2026-09-11
Milestone 394. Every rule now reaches a session by retrieval — because
Every rule reaches a session by retrieval — because
something it is about to do made the rule relevant — and the machinery that
delivered rules unconditionally goes with it.
WHAT GOES, AND WHERE IT CAME FROM
- ``rules.tier`` and its ``ck_rules_tier`` CHECK (migration 0088). Dropping
the column takes the constraint with it. Rule 36 is about ADDING a value to
the column takes the constraint with it. DROP + ADD is about ADDING a value to
a live whitelist, which needs DROP + ADD in the same migration; it does not
speak to removing the column outright, and saying so here is cheaper than
the next reader wondering whether it was forgotten.
@@ -4,12 +4,12 @@ Revision ID: 0101
Revises: 0100
Create Date: 2026-09-15
Milestone 414. A rule lives in a rulebook topic, where it is GLOBAL, or on one
project, and retrieval reads that home directly (step 1). Subscriptions were
the last thing that pretended a rulebook reached some projects and not others,
and after milestone 394 they changed nothing a session received — only what a
project's rule LISTING showed. Operator, 2026-09-15: "we have global and
project scoped rules, we don't need the subscriptions now."
A rule lives in a rulebook topic, where it is GLOBAL, or on one project, and
retrieval reads that home directly. Subscriptions were the last thing that
pretended a rulebook reached some projects and not others, and with no
always-on tier they changed nothing a session received — only what a
project's rule LISTING showed. Global and project-scoped rules cover what
subscriptions were for.
WHAT GOES
@@ -19,8 +19,8 @@ WHAT GOES
there is nothing to mute; a project that departs from a global rule writes
a project rule with an ``overrides`` relation, which says why.
- The ``subscribe_rulebooks`` key inside ``projects.inception.choices``, and
the ``exclude_always_on_rulebooks`` key milestone 394 left behind in the
same place. Both describe decisions that can no longer be made; a stored
the ``exclude_always_on_rulebooks`` key the retired always-on tier left
in the same place. Both describe decisions that can no longer be made; a stored
record carrying them would be read back as a choice the product offers.
IRREVERSIBLE, AND THE DOWNGRADE SAYS SO
@@ -1,4 +1,4 @@
"""milestone_embeddings — a plan becomes findable by meaning (milestone 415)
"""milestone_embeddings — a plan becomes findable by meaning
Revision ID: 0102
Revises: 0101
@@ -9,8 +9,8 @@ lives in — could not be found at all. So "is there already a plan for this?"
had no tool, and a project whose roadmap was written as milestones had every
later plan opened as a new milestone beside the one that already described it.
The sibling of rule_embeddings (0089), for the reasons its model docstring and
note 3163 give. The vectors are DERIVED: nothing is backfilled here, the startup
The sibling of rule_embeddings (0089), for the reasons its model docstring
gives. The vectors are DERIVED: nothing is backfilled here, the startup
backfill writes them.
"""
import sqlalchemy as sa
@@ -1,16 +1,12 @@
"""retrieval_tuning_events — why a floor is where it is (#4102)
"""retrieval_tuning_events — why a floor is where it is
Revision ID: 0103
Revises: 0102
Create Date: 2026-09-17
Milestone 416 stops shipping similarity thresholds as values somebody has to
defend, and hands the adjustment to the model that reads the surface's own
telemetry. The operator's decision:
"the floor should be chosen and adjusted by the model using it… the user
should be able to touch it but the model should be the thing handling it 9
times out of 10."
Similarity thresholds are not values somebody has to defend: the model that
reads the surface's own telemetry adjusts them, and the operator can still
touch them.
The number itself already has a home — the generic settings table. What has no
home is the ARGUMENT, and once the values move on their own the argument is the
@@ -1,10 +1,10 @@
"""retrieval_tuning_events carries the space each number was measured in (#4104)
"""retrieval_tuning_events carries the space each number was measured in
Revision ID: 0104
Revises: 0103
Create Date: 2026-09-17
Milestone 416 step 6. A retrieval floor is a cosine distance in ONE embedding
A retrieval floor is a cosine distance in ONE embedding
model's geometry, computed over documents cut one particular way. Change the
model and every score moves at once; change the chunker and the same record
embeds different text. Either way a number chosen before the change is a
@@ -16,7 +16,7 @@ merely unknown.
the startup backfill re-embeds precisely what is stale instead of wiping the
table. These two columns are that idea applied to the tuned numbers.
TWO COLUMNS, NOT ONE (rule 149). A reader has to be able to say WHICH half
TWO COLUMNS, NOT ONE. A reader has to be able to say WHICH half
moved: a new embedding model and a re-cut document shape invalidate the same
numbers for different reasons, and a fused `"<model>@<n>"` could only report
that something changed.
@@ -1,20 +1,19 @@
"""note_usage_events records WHICH project the reader was in (#4196, #3735)
"""note_usage_events records WHICH project the reader was in
Revision ID: 0105
Revises: 0104
Create Date: 2026-09-20
Milestone 385. `RetrievalLog` has carried `project_id` from the start, so
`RetrievalLog` has carried `project_id` from the start, so
"this record was SURFACED on project B" has always been answerable. The usage
table never had one, so "this record was OPENED on project B" never was — and
the two cannot be joined to recover it, because there is no session identity
server-side (see the NoteUsageEvent docstring, which rules that out on
purpose).
That gap sits exactly on the question milestone 385 exists to answer. A
lesson's whole claim is that it reaches a session on a project it was not
written on; step 8's acceptance is "retrieved on a different project, at the
moment it applies, AND opened". Surfacing cross-project: answerable. Opening:
That gap sits exactly on the question lessons exist to answer. A lesson's
whole claim is that it reaches a session on a project it was not written on:
"retrieved on a different project, at the moment it applies, AND opened". Surfacing cross-project: answerable. Opening:
answerable. Both at once: not, until this column.
WHICH PROJECT THIS IS, because the name is ambiguous and the wrong reading
@@ -1,10 +1,10 @@
"""rule_usage_events carry an outcome, not just a read (#4212, milestone 419)
"""rule_usage_events carry an outcome, not just a read
Revision ID: 0106
Revises: 0105
Create Date: 2026-09-20
Milestone 419's first step. `rule_usage_events` can say a rule was SURFACED
`rule_usage_events` can say a rule was SURFACED
and that it was PULLED. It cannot say what happened next, so these two
sessions leave identical telemetry:
@@ -12,16 +12,14 @@ sessions leave identical telemetry:
- a rule surfaced, opened, and silently ignored.
The second is the more urgent by a distance, and it is the one the readout
cannot name. That is the whole of what this milestone is about, measured on a
session where three of seven misses were caught by the operator and none by
the system.
cannot name, and it is what an outcome column is for.
TWO NEW EVENT VALUES, AND NO CHECK MIGRATION. `event` was created in 0094 as
plain `sa.Text()` with no constraint — checked in the migration itself, not
assumed from the model — so `applied` and `departed` join `surfaced` and
`pulled` without a DROP/ADD pair. Rule 36 governs CHECK-whitelisted columns
`pulled` without a DROP/ADD pair. That pair is for CHECK-whitelisted columns
and this is not one; noted explicitly because the next reader will reach for
rule 36 here, and should be able to see in one place why it does not bite.
it here, and should be able to see in one place why it does not apply.
THE THIRD STATE IS DERIVED, AND THAT IS NOT A SHORTCUT. Read-and-silently-
unchanged is the absence of an outcome, and it has to be: an agent that knew
@@ -29,8 +27,8 @@ it was ignoring a rule would not be ignoring it. There is no honest way to ask
for that event, so nothing here tries. `applied` and `departed` are reported;
the third state is what is left over when a rule was pulled and neither
arrived. A schema that offered an `ignored` value would collect nothing and
read as though it had measured something, which is the #3311 failure — a
statistic that cannot vary being mistaken for a finding.
read as though it had measured something — a statistic that cannot vary
being mistaken for a finding.
`detail` CARRIES THE WHY OF A DEPARTURE, and is the reason this is a column
rather than two more bare event strings. A departure without its reason is
+2 -3
View File
@@ -1,4 +1,4 @@
"""system_embeddings — a charter becomes an ANSWER, not just a filter (#4251)
"""system_embeddings — a charter becomes an ANSWER, not just a filter
Revision ID: 0107
Revises: 0106
@@ -13,8 +13,7 @@ to one, and an agent asking where a record belonged had to read every charter
or guess.
The fourth sibling of note_embeddings (0067), rule_embeddings (0089) and
milestone_embeddings (0102), and for the same reason note 3163 gives about the
third: the row could be shared, the search cannot. "Where does this belong?"
milestone_embeddings (0102), and for the same reason as the third: the row could be shared, the search cannot. "Where does this belong?"
is a different question from "what prior art is there?", and no note or rule
search can answer it, because a charter is not a note.
@@ -1,17 +1,16 @@
"""trigger_leaves_the_stored_title — a snippet's and lesson's title is its name (milestone 427)
"""trigger_leaves_the_stored_title — a snippet's and lesson's title is its name
Revision ID: 0108
Revises: 0107
Create Date: 2026-09-23
Snippets and lessons stored their title as `subject — trigger`, because the
join is what makes these kinds rank on the situation they apply to (#2485) and
join is what makes these kinds rank on the situation they apply to and
the stored title WAS the embedded one. Every surface that shows a title then
showed the trigger too: menu lines, lists and search rows ran to 1,500–3,000
characters, and an injected repeat spent all of it again.
The trigger's home is `data` (decision #4157), and since this milestone the
join happens at embed time (`embeddings.document_title`). This revision
The trigger's home is `data`, and the join happens at embed time (`embeddings.document_title`). This revision
rewrites the rows already stored.
EXACT-SUFFIX, READ FROM THE ROW'S OWN MIRROR. A title is only rewritten when
@@ -1,4 +1,4 @@
"""embeddings_record_their_model — a vector says whose space it lives in (#4132)
"""embeddings_record_their_model — a vector says whose space it lives in
Revision ID: 0109
Revises: 0108
+1 -1
View File
@@ -1,4 +1,4 @@
"""task_claims — a task can say a session is working it (milestone 381 step 2)
"""task_claims — a task can say a session is working it
Revision ID: 0110
Revises: 0109
+2 -3
View File
@@ -1,5 +1,4 @@
"""lesson_rule_links — a lesson points at the rule it is an instance of
(milestone 440 step 1, #4196)
Revision ID: 0111
Revises: 0110
@@ -21,8 +20,8 @@ down_revision = "0110"
branch_labels = None
depends_on = None
# One place, so the CHECK and the model's LINK_STATES cannot drift (rule 36:
# a new value later means DROP + ADD CONSTRAINT in the same migration).
# One place, so the CHECK and the model's LINK_STATES cannot drift (a new
# value later means DROP + ADD CONSTRAINT in the same migration).
_STATES = ("suggested", "confirmed", "rejected")
+1 -2
View File
@@ -1,5 +1,4 @@
"""lesson_no_rule — "no rule fits" is a recorded answer (milestone 440 step 2,
#4631)
"""lesson_no_rule — "no rule fits" is a recorded answer
Revision ID: 0112
Revises: 0111
@@ -1,5 +1,4 @@
"""system_path_patterns — a System names the files that are its area
(milestone 444 step 3, #4756)
Revision ID: 0113
Revises: 0112
+1 -1
View File
@@ -1,5 +1,5 @@
"""system_usage_events — were an area's rulings read once its files were
touched? (milestone 444 step 4, #4757)
touched?
Revision ID: 0114
Revises: 0113
@@ -1,5 +1,4 @@
"""retrieval_judgments — a reviewer's verdict on each line of a logged menu
(#4772)
Revision ID: 0115
Revises: 0114
+2 -2
View File
@@ -1,5 +1,5 @@
"""moment_mappings — an install's own corrections to which actions reach which
moment (milestone 458 step 2)
moment
Revision ID: 0116
Revises: 0115
@@ -8,7 +8,7 @@ Create Date: 2026-10-05
The moments and their default actions are code; this table holds what an
install adds ("make ship" is a deliver here) and the defaults it switches off.
User-owned configuration, so it cascades with the user. No CHECK on `effect`,
which the service validates (rule 36).
which the service validates.
"""
import sqlalchemy as sa
from alembic import op
+2 -2
View File
@@ -1,4 +1,4 @@
"""rule_moments — the moments of work a rule arrives at (milestone 458 step 3)
"""rule_moments — the moments of work a rule arrives at
Revision ID: 0117
Revises: 0116
@@ -28,7 +28,7 @@ def upgrade() -> None:
sa.Column("moment", sa.Text(), primary_key=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
)
# The delivery read (step 4) asks "which rules are on this moment?", the
# The delivery read asks "which rules are on this moment?", the
# opposite direction from the primary key.
op.create_index("ix_rule_moments_moment", "rule_moments", ["moment"])
@@ -1,5 +1,4 @@
"""rule_moment_judgments — whether a rule belongs on a moment, and who said so
(milestone 458 step 7, #4925)
Revision ID: 0118
Revises: 0117
@@ -23,8 +22,8 @@ branch_labels = None
depends_on = None
# One place each, so the CHECKs and the model's JUDGMENT_STATES / SOURCES
# cannot drift (rule 36: a new value later means DROP + ADD CONSTRAINT in the
# same migration).
# cannot drift (a new value later means DROP + ADD CONSTRAINT in the same
# migration).
_STATES = ("suggested", "confirmed", "rejected")
_SOURCES = ("pass", "signal", "edit")
@@ -1,5 +1,5 @@
"""rule_moment_judgments.misfire — a mount reported as arriving where it does
not apply (milestone 458 step 7b, #4955)
not apply
Revision ID: 0119
Revises: 0118
+3 -3
View File
@@ -1,5 +1,5 @@
"""family canon — platforms, family ideas, the adoption ledger and its decision
log (milestone 463 step 1)
log
Revision ID: 0120
Revises: 0119
@@ -13,7 +13,7 @@ reason), or owed. These tables hold that.
- `platforms` — a GLOBAL catalog, the canonical_systems shape: no owner, slug
is the match key. Seeded with generic technology names only. Nothing here
names an app, a repo or a house convention: the catalog ships to every
install (rule 115).
install.
- `project_platforms` — which platforms a project is (declared / detected /
rejected). Membership is what makes "is this in family?" a lookup.
- `family_ideas` — a NOTE's family state. No new record type: any notes row
@@ -39,7 +39,7 @@ branch_labels = None
depends_on = None
# One place per whitelist, so each CHECK and the model's tuple cannot drift
# (rule 36: a new value later means DROP + ADD CONSTRAINT in one migration).
# (a new value later means DROP + ADD CONSTRAINT in one migration).
_MEMBERSHIP = ("declared", "detected", "rejected")
_IDEA_STATUSES = ("candidate", "canon", "retired")
_ADOPTION_STATUSES = ("unassessed", "adopted", "variant", "exempt", "owed")
@@ -1,4 +1,4 @@
"""retire the report_preference arm's rows (milestone 500 step 4)
"""retire the report_preference arm's rows
Revision ID: 0121
Revises: 0120
@@ -6,8 +6,8 @@ Create Date: 2026-10-09
The fixed-question arm that searched for completion-report preferences when a
task closed is gone: the reply shapes and the preferences mounted beside them
now arrive on the moments (milestone 500 step 3). Its rows go with it (rule
22 — no legacy to preserve), because each one would otherwise mean something
now arrive on the moments. Its rows go with it (there is no legacy to
preserve), because each one would otherwise mean something
different once the arm is no longer registered:
- `retrieval_logs` / `retrieval_judgments` — a source missing from the
@@ -21,11 +21,10 @@ different once the arm is no longer registered:
- `settings` — the arm's floor and budget keys, which nothing reads.
COST IS BOUNDED BY AN INDEX, NOT BY THE TABLE. A migration runs at boot
against the live tables, which CI never has (lesson #5221). `retrieval_logs`
against the live tables, which CI never has. `retrieval_logs`
and `retrieval_judgments` are indexed on `source`. `rule_usage_events` is not,
so its delete is also bounded by `created_at` (indexed): the arm shipped with
milestone 409 step 4, decided 2026-09-14, so no row of it predates
`ARM_BORN`, and the scan covers weeks rather than the table's whole history.
so its delete is also bounded by `created_at` (indexed): no row of the arm
predates `ARM_BORN`, and the scan covers weeks rather than the table's whole history.
Not reversible: the arm is not coming back, so neither are its rows.
"""
+1 -1
View File
@@ -1,4 +1,4 @@
"""Seal forge-connection tokens at rest (milestone 523)
"""Seal forge-connection tokens at rest
Revision ID: 0122
Revises: 0121