Feature: Shaping conventions files, including specs.yaml - #392
Draft
nathanacurtis wants to merge 2 commits into
Draft
Feature: Shaping conventions files, including specs.yaml#392nathanacurtis wants to merge 2 commits into
nathanacurtis wants to merge 2 commits into
Conversation
… block
`platforms.<id>` holds facts about one implementation, and two members filed
under `figma` were not. `states` names a prop and an enum value that exist in
api.yaml — a transform reading only the spec applies it, and the CSS transform
does exactly that without touching a Figma file. The prop conventions naming
which prop carries an accessible name or a value are the same shape of fact.
Compare the members that genuinely are Figma facts: `glyphs.match` names a layer
pattern, `codeOnlyProps.match` a frame, `subcomponents.scope` a page.
Adds `Conventions.specs`, a sibling of `platforms` and `primitives` rather than
a member of `platforms`. `primitives` is the precedent — it already sits outside
for the same reason — and the spec is the hub, so making `specs` a platform key
would name the hub as one of its own spokes.
conventions/specs.yaml
props:
states: { disabled: { prop: isDisabled } }
accessibility: { label: { prop: a11yLabel } }
value: { prop: progress }
Prop conventions are objects, not bare names, so one can gain fields without a
break — the same argument that made `actions` an array of objects. That also
retires `propRoles`, a name that borrowed a word meaning something specific and
unrelated on anatomy elements.
`roleValidation` moves to `Settings.spec`, beside `roles`. It describes how
strict a run should be, which is a fact about neither the design tool nor the
target, and its on-switch was already in settings — one feature's two knobs were
split across two files.
ADRs 067 and 073 are amended in place rather than superseded, since neither has
shipped. Schema and types only; no transform or CLI code changes, so the loader
still reads the old paths until the follow-up lands.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…abulary closed Three revisions to the spec-conventions shape. The `props` wrapper is gone. Everything the file holds describes props, so the group earned nothing and read badly against the leaf it contained — `props.states.disabled.prop` says "prop" twice. A grouping level is worth adding when a second kind arrives, not before. `indeterminate` is no longer folded into the state concepts. Those are a governed vocabulary: each resolves to a canonical selector, and `indeterminate` there means a checkbox's mixed state. A progress bar with no known value is a different fact wearing the same word — it suppresses aria-valuenow and has no selector at all. Widening a governed vocabulary to absorb a prop binding is what the governance exists to prevent. It is modelled as `value.indeterminate` instead, a property of the value, which is what it is: the prop that says this value is unknown. `conventions/primitives.yaml` becomes `conventions/figma.primitives.yaml`. Its `source` keys name Figma style properties and its `values` keys name Figma tokens, so the file is Figma-scoped even though the components it produces are not. That is the same test Decision 4 applies to every conventions file, and leaving it unqualified said it was scope-free. Recorded as ADR 073 Decision 5, which reverses part of Decision 2's reasoning — that reasoning held for the table's output and was over-applied to the file. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Schema and ADRs only. No transform or CLI code changes, so the loader still reads the old paths — this describes the destination, and the implementation follows separately.
Both ADRs are amended in place rather than superseded, since neither has shipped.
What moves, and why
platforms.<id>holds facts about one implementation. Two members filed underfigmawere not facts about Figma:statesnames a prop and an enum value that exist inapi.yaml. A transform reading only the spec applies it — thecsstransform does exactly that, and never opens a Figma file.Compare the members that genuinely are Figma facts:
glyphs.matchnames a layer pattern,codeOnlyProps.matcha frame,subcomponents.scopea page. Those describe the design tool.statesdoes not, and filing it underfigmasaid it did.Conventions.platforms.figma.statesConventions.specs.statesConventions.platforms.figma.propRoles.accessibleNameConventions.specs.accessibility.label.propConventions.platforms.figma.propRoles.valueConventions.specs.value.propConventions.platforms.figma.propRoles.indeterminateConventions.specs.value.indeterminateConventions.platforms.figma.roleValidationSettings.spec.roleValidationPropRoleNameFour decisions worth reviewing
specsis a sibling ofplatforms, not a member of it.primitivesis the precedent — it already sits outside for the same reason — and the spec is the hub, so makingspecsa platform key would name the hub as one of its own spokes. The loader precedent exists too:PRIMITIVES_FILEis already a reserved basename inconfig/conventions/, so the new file needs a second reserved name and no new mechanism.indeterminateis not folded into the state concepts. Those are a governed vocabulary — each resolves to a canonical selector, andindeterminatethere means a checkbox's mixed state. A progress bar with no known value is a different fact wearing the same word: it suppressesaria-valuenowand has no selector at all. Widening a governed vocabulary to absorb a prop binding is what the governance exists to prevent. It is modelled asvalue.indeterminateinstead — a property of the value, which is what it is.Prop conventions are objects, not bare names.
accessibility.label.proprather thanaccessibility.label: a11yLabel, so a convention can gain fields without a break. The known disclosure gap needs one that pairs a label with the state selecting it. This also retirespropRoles, a name that borrowed a word meaning something specific and unrelated on anatomy elements.roleValidationbecomes a setting. It describes how strict a run should be — a fact about neither the design tool nor the target — and its on-switch (spec.roles) was already in settings. One feature's two knobs were split across two files.Also here
conventions/primitives.yaml→conventions/figma.primitives.yaml(ADR 073, Decision 5). Itssourcekeys name Figma style properties and itsvalueskeys name Figma tokens, so the file is Figma-scoped even though the components it produces are not. This partly reverses Decision 2, whose platform-neutrality reasoning held for the table's output and was over-applied to the file.Follow-up, not in this PR
The implementation lands separately and must go in one commit, because the loader and its consumers agree on a shape:
ConfigLoader— reserve thespecsbasename, addresolveSpecs, drop three keys fromresolvePlatform, changePRIMITIVES_FILETransformCommand/AnalyzeCommandreact-from-specsandwebcomponents-from-specsde-libraryandegsplitfigma.yamlin twosettings/states.md— it is no longer a settingTransformerContext.processingStateskeeps its name. The config surface moved; the transform surface did not.The dominant risk is silence.
resolvePlatformbuilds from an explicit allowlist, so an unlisted key is dropped with no error — components lose their state classification, still transform, and look plausible. That mechanism already swallowedpropRolesonce during this work. Worth a fixture assertion thatde-libraryemits:disabledselectors.Verification
Types and type-tests compile clean; all three schema files parse. The worktree has no
node_modules, so the vitest suite has not run against these changes — worth running once before merge.