feat(design-systems): declare what to write instead, rather than what not to
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / Python tests (push) Successful in 43s
CI & Build / Build & push image (push) Successful in 38s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / Python tests (push) Successful in 43s
CI & Build / Build & push image (push) Successful in 38s
Milestone #254 step 6, first half (#2295) — and this reframes the task rather than answering it. The operator's call: "in this case we should declare what should be used in place of pure white, it's not a prohibition it's what should be used in its place." None of the three options on the table (a constraints record / the panel reads both sources / negative token rows) was right, because all three kept the prohibition as a KIND OF THING. It isn't one. "Pure white is never text" is the shadow cast by a positive fact — text is Parchment — and a design system that stores what things ARE has no row for a ban because it never needed one. So `design_tokens` gains `supersedes`: the literal values this token should be written instead of. `--color-text-on-action` supersedes `#fff` / `#ffffff`. Same fact as the rule, stated forwards, and now actionable — a finding can say what to write rather than only objecting. It has to be DECLARED, not derived, and that is the crux: `#fff` and Parchment `#E8E4D8` are different colours, so no value-matching check could ever have connected them. That mismatch is precisely why the prohibition looked unrepresentable until it was turned around. `supersedes` cascades on EMPTINESS rather than on None. A child overriding a colour says nothing about which literals it replaces, and blanking the family's declaration there would silently disarm the check for every app that customises the token — while a child that states its own list replaces it wholesale. Two things this deliberately does NOT do: - It does not feed the drift panel. Superseded literals live in component CSS, which `designDrift.ts` cannot see and already documents as a blind spot. This is input for the source lint (#2277). Declaring it with nothing consuming it yet is honest; wiring it to a panel that cannot check it would not be. - It does not remove the panel's `prohibited_color` arm yet — that happens when the panel is repointed at a resolved system, which needs #2288 first. The declaration also exposes a missing token: most of the 67 hardcoded `color: #fff` (#2275) are text on a coloured action button, and the system has no token for that role at all. Every view hardcodes it. Declaring the token that was never there is the first real output of the operator's framing.
This commit is contained in:
@@ -189,3 +189,24 @@ async def test_resolve_returns_serialised_tokens_with_their_provenance():
|
||||
{"system_id": 2, "value": "#5b4a8a"},
|
||||
{"system_id": 1, "value": "#6b2118"},
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_token_can_clear_supersedes_with_an_empty_list():
|
||||
"""`[]` means "this token replaces nothing after all" — a real edit. Guarded
|
||||
on `is not None` so it isn't swallowed as "unchanged", the same trap #2077
|
||||
recorded for update_snippet."""
|
||||
with patch("scribe.mcp.tools.design_systems.ds_svc") as svc:
|
||||
svc.update_token = AsyncMock(return_value=_fake_token())
|
||||
from scribe.mcp.tools.design_systems import update_design_token
|
||||
await update_design_token(token_id=9, supersedes=[])
|
||||
assert svc.update_token.await_args.kwargs["supersedes"] == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_token_leaves_supersedes_alone_when_omitted():
|
||||
with patch("scribe.mcp.tools.design_systems.ds_svc") as svc:
|
||||
svc.update_token = AsyncMock(return_value=_fake_token())
|
||||
from scribe.mcp.tools.design_systems import update_design_token
|
||||
await update_design_token(token_id=9, purpose="text on action surfaces")
|
||||
assert "supersedes" not in svc.update_token.await_args.kwargs
|
||||
|
||||
Reference in New Issue
Block a user