Skip to content

docs(knowledge): every component spec lives in docs/specs/components, one folder per component - #9007

Merged
cixzhang merged 4 commits into
mainfrom
docs/specs-components-move
Nov 1, 2026
Merged

cixzhang merged 4 commits into
mainfrom
docs/specs-components-move

Conversation

@cixzhang

@cixzhang cixzhang commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

User impact

Anyone working on a component, on any platform, finds all of its specs in one folder. docs/specs/components/Avatar/ now holds Avatar.spec.md beside Avatar.swift.spec.md; before this change the core record sat in packages/core/src/Avatar/ and the Swift record in packages/native/apple/Sources/Astryx/Avatar/.

Change

  • Move (history kept, git mv). All 194 component and module records go to docs/specs/components/<ComponentRoot>/, structured like the source tree: core 180, Lab 2, Charts 6, and Swift 6 (as <Name>.swift.spec.md). Record ids and text do not change. Module records keep their subdirectories, for example Table/plugins/rowStatus/useTableRowStatus.spec.md.
  • Discovery (.github/scripts/knowledge-paths.cjs, scripts/check-knowledge.mjs, scripts/specs-for-file.mjs):
    • check:knowledge and specs:for read records from docs/specs/components/.
    • Each record resolves its owning package from the source tree, matching exact case. When two packages keep a folder with the same name (Core and Lab both have Chat/), the record belongs to the package that holds its code.
    • Placement compares a file against the source directory the record mirrors, so specs:for returns the same records for every tracked file.
    • A *.spec.md left in a component package's src/ fails, and the error names the path it belongs at.
  • Records. architecture:knowledge-contracts Owning code describes the layout, and DEC-15 records the decision. spec:AST-093 FR1 places a native spec beside the core record, and FR6 counts docs/specs/components/**/<Name>.<language>.spec.md as native-only. architecture:component-theming-surface gets one wording fix.
  • Gate guard. A pull request that still edits a spec at its old path (beside the code or a Swift view) counts as a spec change, so spec-owner approval stays required until it rebases.
  • Other. Package.swift loses its spec exclude: list. Docs, AGENTS.md, native READMEs, and source comments point to the new paths.
  • Release note. A [docs] patch Changeset: component specs are no longer shipped inside the npm package; they live in the repo at docs/specs/components/. astryx swizzle no longer copies the component's spec file into your project. Nothing a consumer imports or renders changes.

Not in this change

  • Swift platform records are still checked by review only, until the schema carries platform_of.
  • Swift shared rules stay in packages/native/specs/.
  • Theme records stay beside their themes.

Testing

  • pnpm check:knowledge passes, and pnpm check:repo passes.
  • The knowledge, lookup, change-scope, spec-owner, a11y-routing, changeset-coverage, and native contract suites pass (644 tests).
  • specs:for --unrouted returns the same paths before and after; the only addition is this pull request's own Changeset. --unreached returns 0. Sampled files (component, member, module, plugin module, Lab, Charts, shared Core/Lab and Lab/Charts roots) route to the same records, one for one.

Writing rubric results

  • R1 narration: none. The records state the layout in the present tense.
  • R2 research: none in the records.
  • R3 evidence names a change: DEC-15's evidence is the checker and lookup tests.
  • R4 rejected alternatives: DEC-15 names one, records beside each package's code, in one line.
  • R5 PR/issue refs: none.
  • R6 ruled open questions: none open.

Posted by Cindy's agent on her behalf.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 30, 2026
@vercel

vercel Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Nov 1, 2026 5:58am UTC

Request Review

@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Oct 30, 2026
@astracat-bot

astracat-bot Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Nov 1, 2026, 6:04 AM UTC

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

🔎 Preview · 12ed676

Storybook · Sandbox · built from this PR's head

Modified components

Component Size LoC
BottomSheet 34.2 kB (0) · 7.4 kB CSS (0) —
BottomSheetSwitcher 14.9 kB (0) · 3.4 kB CSS (0) —
Center · Storybook 4.2 kB (0) · 4.7 kB CSS (0) 118 (0)
31 more components
Component Size LoC
ChatComposer 64.0 kB (0) · 11.1 kB CSS (0) —
ChatComposerDrawer 24.5 kB (0) · 4.7 kB CSS (0) —
ChatComposerInput 58.7 kB (0) · 10.3 kB CSS (0) —
ChatComposerTokenElement 58.7 kB (0) · 10.3 kB CSS (0) —
ChatDictationButton 49.3 kB (0) · 9.8 kB CSS (0) —
ChatLayout 51.9 kB (0) · 10.2 kB CSS (0) —
ChatLayoutScrollButton 49.3 kB (0) · 9.9 kB CSS (0) —
ChatMessage 23.6 kB (0) · 3.7 kB CSS (0) —
ChatMessageBubble 7.1 kB (0) · 3.7 kB CSS (0) —
ChatMessageList 29.8 kB (0) · 7.9 kB CSS (0) —
ChatMessageMetadata 26.5 kB (0) · 4.1 kB CSS (0) —
ChatSendButton 48.6 kB (0) · 9.4 kB CSS (0) —
ChatSystemMessage 6.8 kB (0) · 3.8 kB CSS (0) —
ChatTokenizedText 7.1 kB (0) · 4.0 kB CSS (0) —
ChatToolCalls 32.8 kB (0) · 9.2 kB CSS (0) —
Code · Storybook 3.6 kB (0) · 3.5 kB CSS (0) 81 (0)
Drawer 33.4 kB (0) · 8.2 kB CSS (0) —
DrawerHeader 51.8 kB (0) · 11.5 kB CSS (0) —
Markdown 148.4 kB (0) · 18.2 kB CSS (0) —
Pagination 88.6 kB (0) · 15.7 kB CSS (0) —
PowerSearch 173.5 kB (0) · 20.3 kB CSS (0) —
Selector 80.1 kB (0) · 15.4 kB CSS (0) —
SelectorOption 16.0 kB (0) · 5.4 kB CSS (0) —
Timer · Storybook 9.5 kB (0) · 6.9 kB CSS (0) 186 (0)
Tokenizer · Storybook 73.1 kB (0) · 16.9 kB CSS (0) 1,963 (0)
lab/CommentComposer 97.8 kB (0) · 1.9 kB CSS (0) —
lab/CommentPin · Storybook 50.2 kB (0) · 1.2 kB CSS (0) 413 (0)
lab/Comments 225.5 kB (0) · 2.5 kB CSS (0) —
lab/CommentThread 220.9 kB (0) · 2.4 kB CSS (0) —
lab/EmojiPicker 66.3 kB (0) · 722 B CSS (0) —
richtext/RichTextEditorToolbar 216.6 kB (0) · 454 B CSS (0) —

Package size: @astryxdesign/core 5.64 MB packed (−950.5 kB); no new dependencies.

API changes: core +2 · ~1 · −0 (report stale: run pnpm api:report)

3 API changes (advisory)
Package Where Signature Usage
+ core . · Types · interface SearchContext interface SearchContext<T extends SearchableItem = SearchableItem> import type {SearchContext} from '@astryxdesign/core'
+ core ./Typeahead · Also exported from an earlier entry point · SearchContext SearchContext (.) import {SearchContext} from '@astryxdesign/core/Typeahead'
~ core . · Types · interface SearchSource search: (query: string) => Promise<T[]> | T[] → search: (query: string, context?: SearchContext<T>) => Promise<T[]> | T[] import type {SearchSource} from '@astryxdesign/core'; const x: SearchSource = {search: …}

Not run on this PR: add ci:run-visual, ci:run-a11y or ci:run-rtl to run them.

CI impact: 0 job-min per update · pool 0% · time to green +1.1 min · standard runners · 13 CI action or script files changed (not measured)
Before After Change
Job-minutes per PR update 78.6 78.6 0
Runner pool, runner-minutes a day 36,679 36,679 0 (0%)
Average concurrent jobs 25.5 25.5 0
Time to green (median → this run) 15.5 min 16.6 min +1.1 min
Runs a day 466 466 0

Not measured (the jobs that use these keep their recent minutes above): .github/scripts/a11y-pr-contracts.test.mjs, .github/scripts/analyze-pr.test.mjs, .github/scripts/change-scope.cjs, .github/scripts/change-scope.test.mjs, .github/scripts/ci-interaction-webkit.test.mjs, .github/scripts/component-audit-scope.test.mjs, .github/scripts/component-design-decisions.test.mjs, .github/scripts/knowledge-paths.cjs, .github/scripts/knowledge-paths.test.mjs, .github/scripts/lib/classify-visual.test.mjs, and 3 more.

Assumptions: before is each job's average over the last 20 pull-request runs; after keeps that for unchanged jobs and uses this run's minutes for added or changed ones, weighted by how often each changed job ran in those runs; runs a day averages the seven whole days before today; time to green compares this run with the median of those runs that ran the same jobs.


Generated by PR Enrichment workflow | View full report

@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec a7ec724

@cixzhang

Copy link
Copy Markdown
Contributor Author

Posted by Cindy's agent on her behalf: the approval above applies Cindy's approval of this spec move (component and module records under docs/specs/components/, knowledge-contracts placement decision, AST-093 FR1/FR6).

@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Oct 30, 2026
@cixzhang
cixzhang marked this pull request as ready for review October 30, 2026 16:23
@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Oct 30, 2026
@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec a4007e9

@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Oct 30, 2026
@cixzhang
cixzhang force-pushed the docs/specs-components-move branch from a4007e9 to ca7b641 Compare October 30, 2026 20:22
@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Oct 30, 2026
@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec ca7b641

@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Oct 30, 2026

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The record move itself checks out: lookups from component source files return the same records as before, every moved record resolves to its owning package, and the knowledge and gate suites pass. Two changes are needed before this lands:

  • Keep packages/<pkg>/src/**/<Name>.spec.md classified as a knowledge record in change-scope.cjs (and flip the new expectation at change-scope.test.mjs:183-190), because otherwise the spec-owner gate reports "No knowledge records changed." for the 121+ open pull requests that still edit records at those paths, while merging them carries the edits into docs/specs/components/.
  • Reword "in the subdirectory their code has" in docs/architecture/knowledge-contracts.md and docs/README.md, or move the records, since 12 of the 14 module records (for example Markdown/modules/softBreaks.spec.md, whose code is in Markdown/plugins/) are not there and check:knowledge does not check it.

packages/native/apple/README.md also still lists the moved Swift specs under Sources/Astryx/.

[Automated review]

Comment thread .github/scripts/change-scope.test.mjs Outdated
{filename: 'packages/core/src/Selector/Selector.spec.md'},
]);
expect(result.specOnly).toBe(false);
expect(result.touchesKnowledgeRecords).toBe(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once this is on main, spec-owner-gate.yml runs this classifier from the default branch, so touchesKnowledgeRecords: false here means an open pull request that still edits packages/core/src/<X>/<X>.spec.md gets spec-owner-approval success ("No knowledge records changed.") while git's rename detection lands the edit in docs/specs/components/<X>/ on merge. At least 121 open pull requests edit records at their old paths. Classifying non-ignored packages/<registered package>/src/**/<Name>.spec.md as knowledge records (not spec-only) keeps the gate failing closed until those branches rebase; this assertion would then expect true.

public name normally matches the root; a parent/member exception requires an
exact top-level or full inline consumer-doc entry in that root. Public
semantic module records are nested at least one directory beneath the same
folder as `<PublicName>.spec.md`, in the subdirectory their code has; the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

12 of the 14 module records this pull request moves are not in the subdirectory their code has: for example Markdown/modules/softBreaks.spec.md governs packages/core/src/Markdown/plugins/softBreaks.ts, and DropdownMenu/modules/useMenuPress.spec.md governs packages/core/src/hooks/useMenuPress.ts. check:knowledge accepts a module record in any subdirectory, so this clause (and docs/README.md:43) is neither true today nor enforced. Rewording it, for example "mirroring the code's subdirectory where the code has one under the component folder", or moving the records and adding the check would resolve it.

cixzhang pushed a commit that referenced this pull request Oct 31, 2026
…h lookup gets its own test budget (#9073)

## Maintainer impact

In full node-project runs on a busy machine, `.github/scripts/a11y-pr-contracts.test.mjs` › changedSinceMergeBase › "lists the branch's own changes and never makes a full clone shallow" times out at 30 s. Alone it takes under a second.

The case ran about 35 git processes in one budget. It built an origin and a full clone (about 14 git calls, plus the clone's and the push's own processes), then made four lookups of about 5 calls each, one of them a fetch that fails. On a loaded runner, starting a process is what costs.

## Intended invariant

Unchanged: against a local or a remote base, the branch's own changes are listed; a complete clone is never made shallow; a base that can't be found, or no base, gives no list.

## Change

This is test only:
- The two repositories are built once, in `beforeAll`.
- Each lookup is its own case: a local base; the remote base, with the clone still not shallow; a missing or absent base.

The repositories, the lookups and the assertions are the same. No budget changes.

**Tradeoff:** building the repositories is now the largest step, under the hook's own 30 s budget; the cases depend on that shared fixture (they don't change it). **Rejected:** a larger budget only moves the threshold.

## Evidence

A repeatable load: the test pinned to one CPU (`taskset`) shared with 40 busy loops.
- **Before** (main 45b112c): the case times out at 30 s (it needed 38 s).
- **After:** the three cases take 4, 5 and 3 s.
- Unloaded, the whole file passes (46 tests).
- **Product:** no source file changes.

## Scope

- [x] No intended public API, product behavior, visual direction, or policy change.
- [x] Product changes discovered during the work were removed or split.
- [x] Public text and artifacts contain no internal Meta context.

## Testing

1. `pnpm exec vitest run --project node .github/scripts/a11y-pr-contracts.test.mjs`
2. The load: `for i in $(seq 40); do taskset -c N node -e 'for(;;){}' & done`, then step 1 under `taskset -c N` with `-t changedSinceMergeBase`, on main and on this branch.

Prior art: #9007 edits other cases in this file (spec-record paths), with no overlap; no other open pull request touches it (searched the changed files of all open pull requests).

Local gates at 1fdb412: ESLint, Prettier and `pnpm check:repo` are clean; the file passes (46 tests); the full node project passes apart from two lock tests in `cache.test.mjs`, which fail on main on this machine too (#8520 fixes them).
@cixzhang
cixzhang force-pushed the docs/specs-components-move branch from ca7b641 to dc1278d Compare October 31, 2026 16:15
@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Oct 31, 2026
@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec dc1278d

@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec 5353227

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested. The record move and its tooling check out: every tracked non-record file routes to the same records as before, a spec left at an old path stays owner-gated, the spec text is approved at this head, and CI is green. One fix from the last review remains. Under the Chart root that Lab and Charts share, resolveComponentPackage (.github/scripts/knowledge-paths.cjs:215-224) looks for a module's code only in src/Chart/. So a Charts module record such as docs/specs/components/Chart/modules/markColor.spec.md resolves to no package (and useChartColors resolves to Lab), and check:knowledge rejects every placement. Resolve a module record under a shared root to its parent component record's package, or search a flat package's src/ for the stem, and add a Charts-beside-Lab fixture to knowledge-paths.test.mjs.

[Automated review]

@cixzhang
cixzhang force-pushed the docs/specs-components-move branch 2 times, most recently from 7483fd1 to 9ead7ac Compare November 1, 2026 00:01
@cixzhang

cixzhang commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

/approve-spec 9ead7ac

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks ready to merge: the component-spec move and its tooling check out on this head, the rewritten placement records carry an exact-head spec approval, and the Charts shared-root placement issue from the last review is fixed. One optional cleanup: the description still says 193 records (Core 179) move and that lookups return the same records for every tracked file, but the rebase also moved InfoTip's record, so 194 move and the three Lab InfoTip files now route to component:InfoTip as well. The red CI results are unrelated to this change: test-node (4/4) fails in a theme build --watch timing test (this pull request touches no CLI or theme code), and the spec owner gate run hit a GitHub API rate limit, so re-running both should clear them.

[Automated review]

Generated by a one-off move: git mv of every component record (core, lab,
charts) and every Swift component spec, then path rewrites.
check:knowledge and specs:for read component and module records from
docs/specs/components/<ComponentRoot>/, resolve the owning package from the
source tree (by exact case, and by the records own code when two packages
share a root), and compare placement against the source directory a record
mirrors. A spec file left in a component packages src fails with the path it
belongs at. Platform records (<Name>.swift.spec.md) sit beside the core record
and stay review-checked until the schema carries platform_of.
@cixzhang
cixzhang force-pushed the docs/specs-components-move branch from 9ead7ac to 12ed676 Compare November 1, 2026 05:47
@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Nov 1, 2026
@cixzhang

cixzhang commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

/approve-spec 12ed676

1 similar comment
@cixzhang

cixzhang commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

/approve-spec 12ed676

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks ready to merge: the component-spec move and its tooling check out on this head, and the rewritten placement records carry an engineering-owner /approve-spec for this exact commit. One optional fix: the description says specs:for returns the same records for every tracked file, but the three Lab InfoTip files now also reach component:InfoTip and Package.swift loses its AST-093 FR1 route, so that line could say so.

[Automated review]

@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Nov 1, 2026
@cixzhang
cixzhang merged commit e0a0376 into main Nov 1, 2026
52 checks passed
cixzhang added a commit that referenced this pull request Nov 1, 2026
cixzhang added a commit that referenced this pull request Nov 1, 2026
cixzhang added a commit that referenced this pull request Nov 1, 2026
…ate the card, row or field around it (#9252)

* fix(Tooltip, HoverCard): a press inside an open surface stays in the surface

component:Tooltip FR11 and component:HoverCard FR11: a press inside an
open surface acts only within it, never on the trigger, a clickable
container around it, or the control a label names. React bubbles a click
on the surface to the trigger's ancestors even when the surface is
portaled, so a click on a FileInput status tooltip's text opened the file
picker. Each surface's content now stops the click.

Tests: a click on tooltip or card content does not reach a clickable
container (the trigger's own click still does); a card's own button keeps
working; inside a label the control is not toggled; FileInput compact and
dropzone status tooltips open no picker while a surface click still does.
Browser: FileInputTooltipPress.a11y.chromium.spec.ts checks the same with
real file-chooser events.

* fix(Tooltip, HoverCard): a middle-button press on the surface stays too; FR11 rows have three cells

useClickableContainer opens a ClickableCard's href in a new tab on a
middle-button mouseup, so the surface content stops mouseup as well as
click. Tests: a middle mouseup on tooltip or card text reaches no
container. The FR11 rows lose a fourth cell their three-column tables
never rendered.

* test(FileInput): the tooltip-press browser spec also runs when Tooltip changes

* fix(Tooltip, HoverCard): the stopPropagation helper sits below the hook so the hook keeps its JSDoc

* Merge main (af268d3, with #8207's FR5) into the surface-press fix

# Conflicts:
#	packages/core/src/HoverCard/HoverCard.spec.md
#	packages/core/src/Tooltip/Tooltip.spec.md

* Merge main (e0a0376, with #9007's spec move)

* fix(Tooltip, HoverCard): mark a surface press instead of stopping it, so document listeners still see it

Stopping the click at the surface also ended the native event at the React
root, so a Typeahead inside an open HoverCard no longer opened. The
surfaces now mark the press (a module-private WeakSet), and
useClickableContainer — which FileInput, ClickableCard and Item rows use —
leaves a marked click or middle-button mouseup alone. FR11 states that
page-level listeners still receive the press.

Tests: ClickableCard with onClick and with href ignores a click or middle
mouseup on tooltip or hover-card content; document listeners still get the
click; FileInput opens no picker from tooltip text. With main's
useClickableContainer, six of these fail.

* fix(Tooltip, HoverCard): the shared interactive-ancestor walk treats an open surface as something else

Only useClickableContainer read the per-event mark, so Item rows with
onClick, TreeList rows and Table row-click rows still ran on a click on
tooltip text. The surfaces now register their content element, and
hasInteractiveAncestor, which every one of those containers already calls,
counts a registered surface as not the container's. Item's own guard asks
isInLayerSurface. The event keeps propagating to page-level listeners.

Tests: an Item row with onClick ignores a click on tooltip text in its
endContent. With main's useClickableContainer and Item, seven surface-press
cases fail.

* fix(Token, Item): their own row guards walk with isLayerSurface; one walk for the surface rule

Token's onClick guard checked only button and a, so a click on an open
tooltip in its icon or endContent still ran it; it now asks
innermostInteractive(target, row, isLayerSurface) like Item. The separate
isInLayerSurface walk is gone. The changeset names the surfaces covered.

Test: a Token with onClick ignores a click on tooltip text in its
endContent.

* docs(useClickableContainer): hasInteractiveAncestor's JSDoc names the open-surface case

* fix(HoverCard, Tooltip): the layer root around a surface's content counts too

A caller's style, xstyle or className (padding) lands on the layer root,
so a press on a padded HoverCard's edge still activated the container.
registerLayerSurface also registers the content element's popover root.
Test: a padded HoverCard's own edge does not activate a ClickableCard
(fails without the root registration).

* docs(spec): container records cross-reference Tooltip FR11; FR11 cites DEC-1 by id

Token FR4, TreeList FR7, ClickableCard FR2 and SelectableCard FR2 each say
a press inside an open Tooltip or HoverCard surface does not activate the
container (component:Tooltip FR11). Tooltip FR11's basis cites
component:Tooltip DEC-1 instead of an unnamed tap strategy.

* chore(changeset): name the containers covered and credit the author
@github-actions
github-actions Bot deleted the docs/specs-components-move branch November 2, 2026 06:04

This branch was successfully deployed

1 active deployment
Preview — 12ed676e Deployed Nov 1, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant