feat(design): offer starter token ROLES at creation, never values
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / integration (push) Successful in 29s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Failing after 46s
CI & Build / Build & push image (push) Skipped
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / integration (push) Successful in 29s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Failing after 46s
CI & Build / Build & push image (push) Skipped
A literal gets written into a stylesheet when there is no role to reach for. This codebase demonstrated it: the house style had no "text on a filled colour" role, so 76 call sites wrote a pure-white literal — not out of defiance, but because nothing existed to write instead. The correction was not a better ban list; it was declaring the missing role (#2275, #2349). So the useful moment is creation. A system whose roles are named on day one never presents the occasion. Ten groups, ~40 roles: surface, text, action, semantic, border, accent, radius, space, motion, state. Operator's call was one flat list, every group individually skippable — presets keyed to app shape (web / CLI / docs) were rejected because they need the product to hold opinions about app categories, and a wrong category is worse than a list someone prunes once. TWO BOUNDARIES THIS HAS TO HOLD, both rule #115: - The ROLES ship; the VALUES never do. Every seeded token has an empty value_by_mode, so a fresh system is a set of named, deliberately-unanswered questions. A test asserts no hex appears anywhere in the module — not just that tokens are blank, but that no palette hides in a comment waiting to be pasted in. - The PREFIX is the install's. `--fs-` is FabledSword's convention, not the product's; the default is a neutral `--ds-` and callers pass their own. Valueless roles are already legible downstream — render_stylesheet emits them as commented-out declarations and stylesheet_for_system reports them under `valueless` (#2299) — so "declared but undecided" reads correctly with nothing new built. Both surfaces, per rule #33: MCP gains starter_role_groups/token_prefix plus list_starter_role_groups(); REST gains the same on POST plus GET /api/design-systems/starter-roles. The parity enumeration is extended rather than loosened. Note create_design_system treats None and [] alike (seed nothing), while starter_tokens treats None as "all". Deliberate: creation must never write 40 rows into a system whose caller never asked, and the everything-checked default belongs in the UI where the operator can see it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs
This commit is contained in:
@@ -22,6 +22,11 @@ from __future__ import annotations
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import design_systems as ds_svc
|
||||
from scribe.services.design_systems import DesignSystemCycle
|
||||
from scribe.services.design_starter_roles import (
|
||||
ALL_GROUPS,
|
||||
DEFAULT_TOKEN_PREFIX,
|
||||
describe_groups,
|
||||
)
|
||||
|
||||
|
||||
async def create_design_system(
|
||||
@@ -29,6 +34,8 @@ async def create_design_system(
|
||||
description: str = "",
|
||||
guidance: str = "",
|
||||
parent_id: int = 0,
|
||||
starter_role_groups: list[str] | None = None,
|
||||
token_prefix: str = "",
|
||||
) -> dict:
|
||||
"""Create a design system, optionally inheriting from another.
|
||||
|
||||
@@ -41,20 +48,47 @@ async def create_design_system(
|
||||
parent_id: Inherit from this system — it holds the defaults this one
|
||||
overrides. Omit (0) for a top-level "family" system, which is what
|
||||
a first design system usually is.
|
||||
starter_role_groups: Seed the system with named but VALUELESS token
|
||||
roles, so there is something to reach for before a literal gets
|
||||
written instead. Call list_starter_role_groups() for the catalogue.
|
||||
Pass ["all"] for every group. Omit for none — a system with three
|
||||
hand-written tokens is a legitimate design system.
|
||||
token_prefix: Naming convention for the seeded roles, e.g. "--fs-".
|
||||
Defaults to a neutral "--ds-"; pass the install's own if it has one.
|
||||
Ignored when no starter groups are requested.
|
||||
"""
|
||||
uid = current_user_id()
|
||||
groups = starter_role_groups
|
||||
if groups and len(groups) == 1 and groups[0] == "all":
|
||||
groups = list(ALL_GROUPS)
|
||||
system = await ds_svc.create_design_system(
|
||||
uid,
|
||||
title=title,
|
||||
description=description or None,
|
||||
guidance=guidance or None,
|
||||
parent_id=parent_id or None,
|
||||
starter_role_groups=groups,
|
||||
token_prefix=token_prefix or DEFAULT_TOKEN_PREFIX,
|
||||
)
|
||||
if system is None:
|
||||
raise ValueError(f"parent design system {parent_id} not found or not writable")
|
||||
return system.to_dict()
|
||||
|
||||
|
||||
async def list_starter_role_groups() -> dict:
|
||||
"""The starter token ROLES offered at design-system creation.
|
||||
|
||||
Roles, not values. Every group is a set of named questions — "page
|
||||
background, the deepest surface" — that the operator answers with their own
|
||||
palette. Nothing here carries a colour, because a default palette would be
|
||||
one install's taste shipped as product.
|
||||
|
||||
Reach for this before create_design_system so the choice is informed, and
|
||||
pass the group names you want as `starter_role_groups`.
|
||||
"""
|
||||
return {"groups": describe_groups(), "default_prefix": DEFAULT_TOKEN_PREFIX}
|
||||
|
||||
|
||||
async def list_design_systems() -> dict:
|
||||
"""List your design systems. An empty list is normal — most installs have none."""
|
||||
uid = current_user_id()
|
||||
@@ -350,6 +384,7 @@ async def set_project_design_system(project_id: int, design_system_id: int = 0)
|
||||
def register(mcp) -> None:
|
||||
for fn in (
|
||||
create_design_system,
|
||||
list_starter_role_groups,
|
||||
list_design_systems,
|
||||
get_design_system,
|
||||
resolve_design_system,
|
||||
|
||||
Reference in New Issue
Block a user