refactor(design): retire /design — a surface that could only inspect itself
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 29s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 59s
CI & Build / Build & push image (push) Successful in 40s

The design surface is for the projects an install tracks. /design read the
running app's own stylesheet — names out of a bundled theme.css, values out of
getComputedStyle(document.documentElement) — so it could only ever describe the
instance serving the page. Scribe is one project among the projects Scribe
tracks; it gets no view hardcoded into every install.

The mechanism that makes this a tool rather than a mirror already existed and
already covers Scribe: scripts/check_design_tokens.py runs in CI against a
sheet path it knows nothing about, using check_code_against_tokens — the same
engine behind check_snippets_against_system. /design was redundant even here.

Removed: DesignView, DesignTabs (nothing left to tab between), api/design.ts,
routes/design.py and its blueprint, the /design route, ui_design_system() and
its setting, and the Settings picker that designated "this app's UI".

utils/designTokens.ts and utils/designDrift.ts go with it — between them they
were the browser-reading half. What survives is utils/designValues.ts, which
works on a record rather than a document: valueForMode, modesPresent, and
resolveDeclared.

resolveDeclared gained real isolation in the move. Custom properties inherit
and `all: initial` does not reset them, so a probe sitting in this page would
resolve any reference a record leaves undeclared against the SURROUNDING app's
tokens — previewing another project's system would quietly borrow this one's
palette wherever that system was incomplete, and a token already reported under
unknown_refs would render as though it were fine. Undeclared references are now
blanked on the probe first, so they resolve to nothing, which is what the record
says they are.

Migration 0075 absorbs ui_design_system_id alongside design_rulebook_id rather
than an 0076 undoing it: 0075 has not run anywhere, since dev is unmerged and
deploys come from main. Both keys named a design source for the running
install, and a project already carries its own pointer.

This retires the agreement panel shipped yesterday. It asked whether the sheet
was actually loaded and applied — the one question a record cannot answer about
itself — but only ever about the app you are already inside. Nothing replaces
it; recorded in #2430 rather than quietly dropped.

Step 1 of milestone #274.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs
This commit is contained in:
2026-08-04 10:35:52 -04:00
co-authored by Claude Opus 5
parent 4d2be27935
commit dcd4efcea0
14 changed files with 123 additions and 1428 deletions
@@ -1,28 +1,29 @@
"""retire the design_rulebook_id setting
"""retire the two settings that designated a design source for the app itself
Revision ID: 0075
Revises: 0074
Create Date: 2026-08-03
The /design panel used to compare a design RULEBOOK's prose claims against the
live tokens. That rulebook was imported into the design system and retired, and
the panel now compares the running app against the design system it was
generated from (#2419). Its designation moved with it:
Two keys, retired for the same reason a week apart, so they go in one change
rather than one migration each:
design_rulebook_id -> ui_design_system_id
design_rulebook_id which rulebook described how this app should look
ui_design_system_id which design system this app's own UI was built from
Nothing reads the old key any more, so this deletes the row rather than leaving
an inert one behind (rule #22 — remove the old path, including the setting it
read from). The values are not translatable: a rulebook id and a design system
id are ids in different tables, and guessing a mapping would silently point the
new panel at the wrong system.
Both named a design source for THE RUNNING INSTALL. The design surface is for
the projects an install tracks, and a project already carries its own pointer
(`projects.design_system_id`) — so an install-wide designation had nothing left
to mean. `ui_design_system_id` was introduced by this same migration's first
draft and never reached a deployed database; it is listed here rather than
undone by an 0076 that would reverse a change nobody ran.
Deleting settings rows by key is safe in a way dropping a column is not — the
table is free-form key/value, so an install that never designated one simply has
no row to delete.
Downgrade cannot restore what it never recorded, so it is a no-op rather than a
lie: the operator re-designates in Settings.
lie: the pointer lives on the project now, and always did for anyone who set it
there.
"""
from alembic import op
import sqlalchemy as sa
@@ -33,10 +34,12 @@ down_revision = "0074"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.execute(
sa.text("DELETE FROM settings WHERE key = 'design_rulebook_id'")
sa.text(
"DELETE FROM settings "
"WHERE key IN ('design_rulebook_id', 'ui_design_system_id')"
)
)