Repository navigation
docs(spec): Citation records its shipped contract as current - #9132
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
There was a problem hiding this comment.
Approval of this record waits on an engineering owner's decision. FR5 now requires a caller's explicit role or aria-label to reach the Citation root, which reverses the draft's component-owned precedence, and current component records read architecture:public-component-api INV5 both ways. An engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request; the author may do so if they are an engineering owner. Whichever precedence is chosen, two fixes are needed:
- FR5 conflicts with FR1, FR2, AR1, AR2, ORD3 and the Review checks row, which state Citation's role and name with no caller exception; scope those rows as
component:DividerFR2 does, or restore the draft's FR5. - The FR3–FR4 verification row names truncation as the only gap, but no test covers
srcwinning over a legacy stringicon.
[Automated review]
| | FR2 | A missing or rejected destination renders the selected citation presentation in a noninteractive span with no navigation or reference role. The span uses a supported naming role so its translated label reaches assistive technology. Rejection derives no navigation attributes from `source.url`, invents no disabled semantics, and does not hide the citation. | shipped source and tests; `family:navigation-destinations/FR4,FR7`; WAI-ARIA naming rules | | ||
| | FR3 | The label variant presents the source title, an optional decorative source icon, and truncation; the number variant presents the supplied number as the compact visible reference. | shipped source, docs, tests, and stories | | ||
| | FR4 | A node icon takes precedence over `src`, and `src` over a legacy string `icon`. Every icon path remains decorative inside an `aria-hidden` wrapper. | shipped compatibility behavior and tests | | ||
| | FR5 | Both root branches preserve supported DOM props, `className`, `style`, `xstyle`, `data-*` attributes, and the declared `HTMLElement` ref, and a caller's explicit `role` or `aria-label` reaches the root (`architecture:public-component-api` INV5). Implementation gap: the component's role and translated name replace a caller's values (Current-state impact). | `architecture:public-component-api/INV5,INV6,INV8`; regression tests | |
There was a problem hiding this comment.
FR5 now lets a caller's role or aria-label reach the root. FR1 and AR1 (doc-noteref and the translated name), FR2 and AR2 (role="group", no interactive role), ORD3 and the Review checks row still state Citation's own role and name with no caller exception. Scope those rows to apply absent a caller override, as component:Divider FR2 does, or keep the draft's component-owned precedence.
| | Contract | Verification | Representative states | Mutation or failure expectation | Audit section | | ||
| | ---------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | | ||
| | FR1–FR2, AR1–AR2 | `Citation.test.tsx` destination suites and `safeUrl.test.ts` matrix | accepted, missing, blocked schemes, normalized blocked text | A blocked URL creates navigation, an accepted URL loses its destination or native affordances, or an inert reference loses its supported naming role. | `audit:Citation/navigation` | | ||
| | FR3–FR4, AR3 | `Citation.test.tsx` variant and icon suites | label, number, no icon, node icon, `src`, legacy string icon | The wrong visible variant renders, icon precedence changes, or decorative source paint reaches the accessibility tree. Gap: no test covers the label's truncation. | `audit:Citation/behavior` | |
There was a problem hiding this comment.
No test renders src together with a string icon (Citation.test.tsx:267-344), so reversing that order in Citation.tsx:203-204 passes every test. Name it here as a gap beside truncation, or add the test.
4328163 to
879e5f2
Compare
There was a problem hiding this comment.
Approval of this record waits on an engineering owner's decision. FR5 now lets a caller's explicit role or aria-label reach the Citation root, reversing the draft's component-owned precedence, and current component records read architecture:public-component-api INV5 both ways; an engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. Either way, one fix remains:
- FR2, AR2, the missing-or-rejected Representative states row and the Review checks row still forbid an interactive or reference role on an inert citation, which FR5 now lets a caller set; scope those clauses to what Citation itself adds, or restore the draft's FR5.
[Automated review]
| - **AR1 — Linked reference.** An accepted destination exposes one native link with `doc-noteref` and the translated `Citation {number}: {title}` accessible name. | ||
| - **AR2 — Inert reference.** Missing or rejected destinations expose no interactive or navigation role or attributes. The inert span uses `role="group"` so its translated citation label remains an accessible name, including when the visible number has no source text. | ||
| - **AR1 — Linked reference.** An accepted destination exposes one native link with, absent a caller `role` or `aria-label` (FR5), `doc-noteref` and the translated `Citation {number}: {title}` accessible name. | ||
| - **AR2 — Inert reference.** Missing or rejected destinations expose no interactive or navigation role or attributes. Absent a caller `role` or `aria-label` (FR5), the inert span uses `role="group"` so its translated citation label remains an accessible name, including when the visible number has no source text. |
There was a problem hiding this comment.
AR2 still says an inert citation exposes "no interactive or navigation role or attributes", but FR5 (line 106) now lets a caller's role reach the span, so the root-contract test's own role: 'button' input would be required by FR5 and rejected here. FR2's first sentence (line 103), the missing-or-rejected Representative states row (line 122) and the Review checks row (line 48) have the same gap. Scope them to what Citation itself adds, or restore the draft's FR5.
879e5f2 to
0af3cdc
Compare
There was a problem hiding this comment.
Approval of this record waits on an engineering owner's decision: FR5 now lets a caller's explicit role or aria-label reach the Citation root, reversing the draft's component-owned precedence, and current component specs apply the public-component-api INV5 rule both ways. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request (the author may do so as an engineering owner), or FR5 returns to component-owned precedence. Either way the record needs one more commit: reverting FR5 removes the caller exceptions, and keeping it needs these two fixes.
- FR1, FR2, AR1, AR2 and the missing-or-rejected state row drop Citation's role whenever a caller passes
roleoraria-label, so a calleraria-labelalone no longer keeps therole="group"that DEC-1 needs to name the span; condition each value on its own prop. - The FR1–FR2 verification row still counts a lost naming role as a failure even when the caller supplies its own
role.
[Automated review]
| | ID | Invariant | Basis | | ||
| | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | ||
| | FR1 | An accepted `source.url` renders a native anchor with the unchanged destination, `target="_blank"`, `rel="noopener noreferrer"`, and, unless a caller supplies an explicit `role` or `aria-label` (FR5), `role="doc-noteref"` and the translated accessible name; its `title` is the source title, or the number when there is none. | shipped source and tests; `family:navigation-destinations/FR1,FR3–FR5` | | ||
| | FR2 | A missing or rejected destination renders the selected citation presentation in a noninteractive span; Citation adds no navigation or reference role to it. Unless a caller supplies an explicit `role` or `aria-label` (FR5), the span uses a supported naming role so its translated label reaches assistive technology. Rejection derives no navigation attributes from `source.url`, invents no disabled semantics, and does not hide the citation. | shipped source and tests; `family:navigation-destinations/FR4,FR7`; WAI-ARIA naming rules | |
There was a problem hiding this comment.
"Unless a caller supplies an explicit role or aria-label" releases the naming role even when the caller passes only aria-label, but DEC-1 says a roleless span cannot be named, so that label would be lost. Condition the role on a caller role and the translated name on a caller aria-label (the same applies to FR1, AR1, AR2 and the missing-or-rejected state row).
| | All | `Citation.stories.tsx`, component tests, typecheck, lint, and exact-head CI | all documented variants and representative source states | A required state becomes unreachable or gains a build, accessibility, visual, or test regression. | `audit:Citation/testing` | | ||
| | Contract | Verification | Representative states | Mutation or failure expectation | Audit section | | ||
| | ---------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | | ||
| | FR1–FR2, AR1–AR2 | `Citation.test.tsx` destination suites and `safeUrl.test.ts` matrix | accepted, missing, blocked schemes, normalized blocked text | A blocked URL creates navigation, an accepted URL loses its destination or native affordances, or an inert reference loses its supported naming role. | `audit:Citation/navigation` | |
There was a problem hiding this comment.
This failure signal still counts "an inert reference loses its supported naming role" with no caller exception, while FR5 and AR2 now require a caller role (the root-contract test's role: 'button') to replace group. Suggest "…when the caller supplies no role".
0af3cdc to
281730c
Compare
There was a problem hiding this comment.
Approval of this record waits on an engineering owner's decision: FR5 now lets a caller's explicit role or aria-label reach the Citation root, reversing the draft's component-owned precedence, and current component specs apply the public-component-api INV5 rule both ways. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request (the author may do so as an engineering owner), or FR5 returns to component-owned precedence. Each caller prop now replaces only its own value; if caller-wins stays, two rows still need that scoping:
- The linked label and linked number Representative states rows still require the translated accessible name with no exception for a caller
aria-label.
[Automated review]
| | linked label | Native anchor, source title, optional decorative icon, translated accessible name. | Source content and root theme paint. | | ||
| | linked number | Native anchor, numeric visible reference, translated accessible name. | Number and root theme paint. | |
There was a problem hiding this comment.
These two rows still require the "translated accessible name" with no caller exception, while FR1, AR1 and FR5 now let a caller's aria-label replace it. If caller-wins stays, "translated accessible name absent a caller aria-label" keeps them consistent with the missing-or-rejected row below.
281730c to
a921db6
Compare
There was a problem hiding this comment.
Approval of this record waits on an engineering owner's decision: FR5 lets a caller's explicit role or aria-label reach the Citation root, reversing the draft's component-owned precedence, and current component specs apply the public-component-api INV5 rule both ways. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request (the author may do so as an engineering owner), or FR5 returns to component-owned precedence before approval. The caller exceptions are now scoped consistently across FR1, FR2, AR1, AR2, ORD3, the state rows and the Review checks row, and no other change is needed.
[Automated review]
|
/approve-spec a921db6 |
Exact decision needed
Promote
component:Citationfromdrafttocurrent, recording Citation's shipped behavior on main today. No behavior, API, or output change.Existing authority searched
component:Citationon main (draft, template 6): FR1–FR7, AV1–AV3, ORD1–ORD3, PR1–PR2, AR1–AR4, and DEC-1 (inert references userole="group") were re-verified againstCitation.tsxand kept, rewritten in the present tense in template 7. FR5 now records that a caller's explicitroleoraria-labelreaches the root, following the INV5 caller-wins conformance ruling thatcomponent:Avatarrecords; today's replacement, which a root-contract test pins, is an implementation gap. This is a conformance ruling, not a new decision.family:navigation-destinationsandspec:AST-005(current) own the destination decision.pnpm specs:for packages/core/src/Citation/Citation.tsx: directory-wide architecture records.docs/specs/components/move and the schema-5 migration). This record keeps the colocated, schema-3 path main uses today.Decision
packages/core/src/Citation/Citation.spec.md:authority: current,approved_by: cixzhang,approved_at: 2026-10-31,ownersunchanged, template 7, behavioral table headed "Invariant | Basis". DEC-1 is unchanged.Rejected alternatives
None.
Claim scope and non-goals
Covers only Citation as shipped on main. Destination policy, image resource policy, ordering, and surrounding prose stay with their owners. No new behavior is decided.
Evidence and implementation relationship
Citation.test.tsxandsafeUrl.test.ts.themingTargets.test.ts(variant reflection untested).core-citation--label.Current-state impact
roleoraria-labelis replaced by the component's role and translated name (architecture:public-component-apiINV5).Writing rubric results
#+ digits,/pull/,/issues/).Scope
Validation
pnpm check:knowledge: alignedgit diff --check: clean