Skip to content

feat(cli): doctor integration components compares documented props with the code - #7676

Merged
josephfarina merged 12 commits into
mainfrom
josephfarina/doctor-prop-drift-components
Oct 19, 2026
Merged

josephfarina merged 12 commits into
mainfrom
josephfarina/doctor-prop-drift-components

Conversation

@josephfarina

@josephfarina josephfarina commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

User need

#7663 adds a check that compares each component doc's props with the component's TypeScript props, on #7681, which reads the package's TypeScript project. Nothing runs it yet, so an integration still learns about a stale prop table only when someone reads the code.

Problem and solution fit

astryx doctor integration components now runs #7663's comparison on every component doc. It first finds each doc's prop tables:

  • a doc's own props document the export its name names, even when another file beside it holds that export;
  • each entry of a family doc documents its own export, and a member record compares that entry against its own source;
  • two docs that each have a table for one component are both compared;
  • a hook entry (use…) documents parameters and returns, not props, and isn't a table.

It prints each finding as a warning that names the component, the prop, the doc and source files, and the documented type. It lists the components it can't read, each with its reason. It prints the closing [ok] only when at least one prop table was compared, never beside a finding or after a failed check. Without TypeScript, it prints one line saying the comparison was skipped and why. It never reports a pass it didn't check.

Core gets the same comparison as a repository test, packages/cli/test/core-prop-docs.test.mjs. The 102 mismatches Core has today, over 182 prop tables, are listed in core-prop-docs.known-drift.json. The list only shrinks: a new mismatch fails the test, and so does a fixed one until its line is removed.

Current specification

Implements architecture:cli-surface INV33, "Documented component props agree with the source" (AST-039 FR12, which now points to it; DEC-5), with #7663, #7684 and #7667. Its evidence row names the prop-drift, type-text, doctor-integration and core-prop-docs tests, which this PR's tests satisfy.

Public delta and caller burden

  • astryx doctor integration components prints new warnings, then a line that says how many components were compared and with which TypeScript: in the summary beside the findings, as [ok] Documented prop names and required flags match the code in N component(s) (TypeScript x.y). when nothing differs, or as [info] Props compared in N component(s) (TypeScript x.y). when a failed check holds the [ok] back. Without a usable TypeScript it prints [skip] Props not compared: …, naming the installed version when that TypeScript has no JavaScript compiler API (TypeScript 7). Exit codes are unchanged, because findings are warnings.
  • JSON gains one additive field, data.propDrift = {status: 'checked' | 'skipped', reason?, typescript?, components, findings[], unchecked[]}. It is present when the manifest loads and declares a components root.
    • Each finding is {component, prop, kind, severity: 'warning', message, doc, source, docType?, codeType?, docRequired?, codeRequired?}, and the text shows every field it carries.
    • Nothing else in the envelope changes.
  • No new command, flag, option or environment variable. A package needs nothing beyond the typescript it already has.

Example: an Acme package's AcmeButton declares label: string. Its doc leaves label optional and lists an elevation prop the code doesn't have:

severity:  [warn]
component: AcmeButton
prop:      elevation
kind:      not_in_code
message:   "elevation" is documented, but AcmeButton's props have no "elevation".
doc:       components/AcmeButton.doc.mjs
source:    components/AcmeButton.tsx
docType:   number

severity:     [warn]
component:    AcmeButton
prop:         label
kind:         required_mismatch
message:      "label" is required in the code, but the doc does not mark it required.
doc:          components/AcmeButton.doc.mjs
source:       components/AcmeButton.tsx
docRequired:  false
codeRequired: true

2 prop(s) differ between the docs and the code in 1 of 1 component(s) compared (TypeScript 6.0.3). These are warnings: update the doc, or the code if the doc is right.

The command still exits 0.

Design and alternatives

  • In the integration subject's adapter. The comparison reads files and loads the project's TypeScript, so it runs from the new api/integration/_adapter.mjs (cli-surface INV21). authoring-checks.mjs only attaches the report.
  • Warnings first. Existing packages already have drift (Core has 102 cases), so an error would break them on upgrade. A build that wants to fail on drift opts in with astryx integration check --strict (feat(cli): integration check runs every source check for a package's build, with --strict #7665).
  • Rejected: a separate command (the components check is where an author already looks); a flag or environment variable to turn the comparison on.

Evidence

  • doctor-integration.test.mjs: text, JSON, the skip line, no [ok] when nothing was compared and none beside findings, and a test that compares the JSON with the text for each finding kind and each component not compared, field by field (test-utils/text-projection.mjs).
  • prop-drift.test.mjs (4 tests of comparePropDocs): a family doc's entries and hooks, member records, a doc whose name names an export in another file, and a table for one component in each of two docs.
  • authoring-checks.test.mjs, and the Core ratchet test above.
  • Core: 182 prop tables, compared in about 6.5 s. There are 102 findings: 73 not_in_doc, 26 required_mismatch and 3 not_in_code.

Since the first automated review

A member with no source file

  • A family member that no file in the package declares has no source to read: for example, one only another folder or built output defines. A record like that would have crashed the comparison with a TypeError. It is now listed as not compared ("no source file for "), and the rest of the doc is still compared. A test covers it and failed before. Discovery doesn't produce such records on main yet; a family-member change in review will.

Props a mapped type builds

Authoring docs

  • A new guide, astryx docs cli/integrations/building-blocks/components/describe-the-component/check-the-props, shows how to run the props check and read its output. It says what each finding means and how to fix it, and why a component can be listed as not compared, with the fix for each reason. Its examples are real output.
  • The component guides link to it. The single-component guide adds doctor integration components to the commands to run after a change, and the checks guide points to it for fixing a props finding.
  • A fresh agent with no context got a package with all four kinds of finding and only the CLI's docs. It found the guide by search, fixed the doc correctly, and left the source alone.
  • That test also found the summary line wrong: "N documented prop(s) differ from the code" counted a prop the doc leaves out. It now reads "N prop(s) differ between the docs and the code". The guide's sample output is now a real run with one finding, so its summary agrees with the finding shown.

Every props table

  • Two kinds of table were never compared, so a stale one passed under [ok]. A family doc whose name names another export of its file compared only its own table, and its other entries only where discovery recorded a member: a hiddenComponents entry, or one whose source isn't built, was skipped. And a doc with hidden: true never reached the comparison, because discovery leaves it out.
  • Now the doc's own record compares every entry no member record compares, and an entry whose source isn't found is listed as not compared with the reason. The command adds a record, under the doc's file name, for each doc discovery leaves out: a hidden one, or one whose source or own file doesn't load.
  • Tests cover a family doc named after another export, with a hidden entry (compared) and an unbuilt one (not compared, with the reason). A command test covers a package whose only tables are a hidden doc and an unrecorded family entry: it reports their drift and never prints [ok]. Both failed before.
  • The props guide says hidden docs and every family entry are compared, a prop the package's defaultProps fills counts as optional (feat(cli): compare documented component props with the TypeScript source #7663, which reads it through memo and lazy too), and a deprecated prop is still asked for. Core and Lab are unchanged.
  • An independent audit then found two more gaps, each fixed with a test that failed first:
    • The summary counted components by name, so two docs of one name (in two folders, or a hidden older one) read as one: "3 prop(s) differ … in 1 of 2". It counts each doc's component now.
    • A .doc.ts beside a .doc.mjs of the same name was left out, though both ship and validate checks both. It gets its own record now.

After #7663's review of mapped props

  • Some fields moved here from feat(cli): compare documented component props with the TypeScript source #7663, with their printing and tests, to make room there for the mapped-props rule. They are the fields each finding carries for this command to show: docType, codeType, docRequired and codeRequired. The printing gives the code's type as the source writes it, a union's members in source order, a method prop's function type rather than its return type, and an intersection's narrowed type. They are in this PR's first commit, the one that adds them to the JSON and the text. Core and Lab output is byte-identical.
  • A family doc whose own source is missing. Discovery drops the doc's own record when its same-stem source is missing (already an invalid_component error). It still records the members its package's entry exports, and those records carry the doc's path, so the doc counted as compared. Its own table and its hiddenComponents entries were then neither compared nor listed. comparePropDocs now compares the own table and every entry no member record compares for such a doc, or lists them as not compared ("no source file for "). A command test with that package shape failed before.

After #7663's review of mapped-prop ownership

  • feat(cli): compare documented component props with the TypeScript source #7663 now traces each mapped prop's key to where it is written. It lists props whose keys it can't trace in propsNotChecked. This PR prints them under [info] Props not checked for a doc entry, with the reason and the props, and the [ok] line then says "apart from the props not checked". The JSON docs, the README row and the props guide say so. A command test covers it and failed before.
  • Four of the comparison's tests moved here from feat(cli): compare documented component props with the TypeScript source #7663 to keep it under 2,000 lines: a declaration file or type package that doesn't resolve, a config with errors, a name through an index signature, and props through React's component types. Core and Lab output is byte-identical.

After the review of hidden entries in sibling files

  • A family entry with no record of its own, such as a hiddenComponents entry, was read only from <Entry>.tsx, the family's own file or the folder's index. When its export is declared in another file of the folder (say components/menu-parts.tsx, re-exported from the package's entry), it was listed as not compared, while the same entry, not hidden, was compared from the source discovery finds. It now falls back to the .tsx in the doc's folder that declares it, the same lookup discovery uses for a member. A test with a hidden entry in a sibling file failed before. Core and Lab output is byte-identical.

After the review of the Core test

  • The not-checked line covers both reasons now: [info] Props not fully checked in N component(s), counting each component once, and the [ok] line says it leaves those props out. feat(cli): compare documented component props with the TypeScript source #7663 now lists a prop only some variants require there, and the props guide's required_mismatch row says so, asking authors to name the variants that need it in its description. The command test covers both reasons.
  • The repository test over Core's docs read only the 180 docs beside a same-stem .tsx. It now reads all 238, as the command reads a package's docs, so six more tables are compared. Resizable's two existing mismatches join the known list, and the four docs whose own export has no same-stem source are pinned as listed, not compared. A new mismatch anywhere in Core now fails the test. It failed before without the two known entries.
  • Two more of the comparison's tests moved here from feat(cli): compare documented component props with the TypeScript source #7663, which is at its size limit. The typed-any test adds forwardRef components whose props are Omit or Partial of any, which feat(cli): compare documented component props with the TypeScript source #7663 now lists; they failed before.

After the round-21 review

  • feat(cli): compare documented component props with the TypeScript source #7663 now reads a prop that only some union members require as optional. Core's ContextMenu.items and DropdownMenu.items, documented as required, are flagged and join the known list. The props guide's required_mismatch row says such a prop counts as optional and its description should say which variants need it. It also says each prop of an entry that names several is listed, and asks authors to give each prop its own entry. The JSON docs say the same, and the command test's second not-checked reason is now a two-name entry.
  • The README's integration authoring table says that doctor integration components also checks each props table against the TypeScript props.
  • The defaultProps test moved here from feat(cli): compare documented component props with the TypeScript source #7663 and sits where it was, with this PR's check of the finding's fields.

After the round-22 review

  • With the multi-name listing gone, the not-checked line reads as it did before round 21, [info] Props not checked for a doc entry in N component(s), and the JSON docs and the command test match. The props guide's required_mismatch row says an entry that names several props makes the same claim about each, and to give props separate entries when their flags differ.

After main moved

Restacked

Restacked

Restacked

After the latest review

Merge order

The order is required:

  1. feat(cli): read a package's TypeScript project the way tsc reads it #7681 (reading the package's TypeScript project), then feat(cli): compare documented component props with the TypeScript source #7663 (the prop comparison, stacked on feat(cli): read a package's TypeScript project the way tsc reads it #7681), then feat(cli): doctor integration components compares documented props with the code #7676 (props in doctor integration components, stacked on feat(cli): compare documented component props with the TypeScript source #7663), then feat(cli): read two written TypeScript types as one when they differ only in how they are written #7684 (reading two written types as one, stacked on feat(cli): doctor integration components compares documented props with the code #7676), then feat(cli): doctor integration components judges documented prop types against the code #7667 (prop types, stacked on feat(cli): read two written TypeScript types as one when they differ only in how they are written #7684).
  2. feat(cli): check each authored doc against its published authoring type #7664 (the doc type check), after feat(cli): read a package's TypeScript project the way tsc reads it #7681: once feat(cli): read a package's TypeScript project the way tsc reads it #7681 merges, feat(cli): check each authored doc against its published authoring type #7664 is rebased onto main, which takes out the 65 lines the two share and brings it to 1,960, and reviewed again. Then feat(cli): doctor integration validate checks each doc against its published authoring type #7673 (doc types in validate, stacked on feat(cli): check each authored doc against its published authoring type #7664).
  3. fix(cli): doc links to a component, page, or block resolve #7668 (doc links to components and templates) any time before feat(cli): integration check runs every source check for a package's build, with --strict #7665.
  4. feat(cli): integration check runs every source check for a package's build, with --strict #7665 (integration check) last, after a rebase that adds these PRs' fields and codes to its failures list. Merged earlier, it breaks main.

This PR must ship in the same release as integration check. If it slips past that release, it can ship only in a minor, because its findings fail integration check --strict.

Overlap with open PRs

Size

1,978 changed lines. 104 of them are packages/cli/test/core-prop-docs.known-drift.json, a data file the Core test reads.

Scope

  • One capability is added.
  • Unrelated fixes, cleanup, and policy changes were removed or split.
  • Consumer docs, focused tests, and migration evidence ship with the feature.
  • Public text and artifacts contain no internal Meta context.

Testing

The CI test jobs run only for pull requests into main, so this PR's tests run in CI once it is retargeted after #7663 merges. On main 9b8b723 with #7681, #7663 and this branch: copyright, sync-exports, the published ./api type check, readme:check, the four CLI typechecks (strict, json-api, authoring-contract, template-docs), check:changesets, the lint job's changeset coverage check (run on the merge commit GitHub builds), check:cli-structure, check:repo, eslint on the changed files, prettier on the new files, and the full pnpm vitest run --project node suite: 10,321 tests pass. The 21 failures are in the npm pack, packed real-world package, theme resolution, CSS graph and browser accessibility tests, which fail the same way on main 9b8b723 on the same machine (they need a package registry and a browser it doesn't reach). Earlier in review, with all nine PRs merged into main, the four doctor integration subcommands were run in text and JSON on 64 package shapes, against main: templates output is byte-identical everywhere; validate and components only add their new section and JSON field; docs differs only where the doc-links PR fixes a link warning. integration check exits 1 exactly when one of the four would, each of its sections is byte-identical to that subcommand's output, and only integration check's three list tests fail until its planned rebase.

@vercel

vercel Bot commented Oct 18, 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 Oct 19, 2026 10:45pm UTC

Request Review

@astracat-bot

astracat-bot Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🔄 In progress (for maintainers only) Oct 19, 2026, 10:46 PM UTC

@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

Previews ready: Storybook and Sandbox were built with this PR's docsite. CI evidence will be added when available.

View Storybook for this PR · View Sandbox for this PR

@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 PR can't be approved yet because the behavior it adds has no current spec on main: AST-039 is still a draft there and ends at FR11, and the FR12 this PR implements exists only in draft spec PR #7658. A maintainer needs to approve that requirement, or identify another current claim, settling whether doctor integration components should compare documented props with the TypeScript props as warnings with a data.propDrift field, and whether Core should enforce it through the known-drift ratchet test. Separately, please make the text output carry every propDrift field: docRequired and codeRequired appear in the JSON but never print, and with any finding the compared count and TypeScript version disappear (see propDriftBlocks in doctor.mjs), which AST-017 FR13 requires.

[Automated review]

Comment on lines +193 to +203
fields: [
'severity',
'component',
'prop',
'kind',
'message',
'doc',
'source',
'docType',
'codeType',
],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

docRequired and codeRequired are missing from this list, so a required_mismatch finding (and a not_in_doc finding whose requiredness is known) prints without them while --json carries them. AST-017 FR13 requires every field in the text projection, and the PR description says the text shows each field a finding carries. Listing both keys is enough, because the formatter prints false:

fields: ['severity', 'component', 'prop', 'kind', 'message', 'doc', 'source', 'docType', 'codeType', 'docRequired', 'codeRequired'],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed at e7ce3a1. The findings show docRequired and codeRequired, and the compared count and TypeScript version print whether or not anything differs (in the summary beside findings, the [ok], or an [info] line when a failed check holds the [ok] back). clients/cli/commands/doctor-integration.test.mjs › "components prints every field of the props comparison that JSON carries" compares the JSON with the text field by field for each finding kind and each component not compared.

@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 PR can't be approved yet because the behavior it adds still has no current spec on main: AST-039 is a draft there and ends at FR11, and the FR12 this PR implements exists only in draft spec PR #7658. A maintainer needs to approve that requirement, or identify another current claim, settling whether doctor integration components should compare documented props with the TypeScript props as warnings with a data.propDrift field, and whether Core should enforce it through the known-drift ratchet test. The earlier text-output finding is fixed: findings now print docRequired and codeRequired, and the compared count and TypeScript version print whenever the comparison runs. Nothing else in this review blocks approval.

[Automated review]

@josephfarina
josephfarina force-pushed the josephfarina/doctor-prop-drift branch from 33b99ca to 6ac81a6 Compare October 18, 2026 05:37
@josephfarina
josephfarina force-pushed the josephfarina/doctor-prop-drift-components branch from e7ce3a1 to 59d4101 Compare October 18, 2026 05:37

@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 PR can't be approved yet because the behavior it adds still has no current spec on main: AST-039 is a draft there and ends at FR11, and the FR12 this PR implements exists only in draft spec PR #7658. A maintainer needs to approve that requirement as current, or identify another current claim, settling whether doctor integration components and integrationComponentConflicts() should compare documented props with the TypeScript props as warning-only findings with an optional data.propDrift field and a [skip] line when no usable TypeScript is installed, and whether Core should enforce the same comparison through the known-drift ratchet test. The implementation itself checks out on this revision: no defects were found, exit codes are unchanged, and the text output carries every propDrift field.

[Automated review]

@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 PR can't be approved yet because the behavior it adds still has no current spec on main: AST-039 is a draft there and ends at FR11, and both the FR12 this PR implements and the FR4 rule that a component doc's name is the export it documents exist only in draft spec PR #7658. A maintainer needs to approve those requirements as current, or identify another current claim, settling whether doctor integration components and integrationComponentConflicts() should compare documented prop tables with the TypeScript props as warning-only findings in an optional data.propDrift field, which export a doc's own table is compared with when its name differs from its file, and whether Core should enforce the comparison through the known-drift ratchet test. The implementation itself checks out on this revision: no defects were found, exit codes are unchanged, and the text output carries every propDrift field.

[Automated review]

@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 PR can't be approved yet because the behavior it adds still has no current spec on main: AST-039 is a draft there and ends at FR11, and both the FR12 this PR implements and the FR4 rule that a component doc's name is the export it documents exist only in draft spec PR #7658. A maintainer needs to approve those requirements as current, or identify another current claim, settling whether doctor integration components and integrationComponentConflicts() should compare documented prop tables with the TypeScript props as warning-only findings in an optional data.propDrift field, which export a doc's own table is compared with when its name differs from its file, and whether Core should enforce the comparison through the known-drift ratchet test. The implementation itself checks out on this revision: no defects were found, exit codes are unchanged, the text output carries every propDrift field, and Core still yields exactly the 99 listed mismatches.

[Automated review]

@josephfarina
josephfarina force-pushed the josephfarina/doctor-prop-drift branch from 1a7383c to 722fcd3 Compare October 18, 2026 11:16

@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 is ready to approve: doctor integration components compares every props table a package ships with the component's TypeScript props, as architecture:cli-surface INV33 requires, keeps exit codes unchanged, and never asks a doc for the props authors are told to skip. The rebase onto main holds: the ContextMenu entry that main fixed is gone from the known-drift list, Core yields exactly the 102 listed findings, and the CLI tests and checks pass. Nothing else is needed here; it can land after its base PRs, in the order the description gives, and should ship in the same release as integration check.

[Automated review]

@josephfarina
josephfarina force-pushed the josephfarina/doctor-prop-drift branch from c0b2461 to 2ed0495 Compare October 19, 2026 15:35
@josephfarina
josephfarina force-pushed the josephfarina/doctor-prop-drift-components branch from 515eab6 to 788903f Compare October 19, 2026 15:35

@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 is ready to approve. doctor integration components now compares every props table a package ships with the component's TypeScript props, as architecture:cli-surface INV33 requires: it never asks a doc for the props authors are told to skip, says when it skipped the comparison, and keeps exit codes unchanged. The restack onto the base PR's new head leaves this change as it was, and the CLI tests, typechecks and repository checks pass on this revision, with Core yielding exactly the 102 listed findings. Nothing else is needed here: it can land after its base PRs in the order the description gives, and should ship in the same release as integration check.

[Automated review]

@josephfarina
josephfarina force-pushed the josephfarina/doctor-prop-drift branch from 2ed0495 to 332eff7 Compare October 19, 2026 16:40
@josephfarina
josephfarina force-pushed the josephfarina/doctor-prop-drift-components branch 2 times, most recently from 788903f to e0bf813 Compare October 19, 2026 16:40

@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 is ready to approve: doctor integration components now compares every props table a package ships with the component's TypeScript props, as architecture:cli-surface INV33 requires, never asks a doc for the props authors are told to skip, says when it skipped the comparison, and keeps exit codes unchanged. The restack changes only one test here, and the CLI tests, typechecks and published type check pass on this revision, with Core yielding exactly the 102 listed findings. Nothing else is needed here; it can land after its base PRs, in the order the description gives.

[Automated review]

@josephfarina
josephfarina force-pushed the josephfarina/doctor-prop-drift branch from 332eff7 to 02eeac1 Compare October 19, 2026 18:38
@josephfarina
josephfarina force-pushed the josephfarina/doctor-prop-drift-components branch from e0bf813 to ec0c4fa Compare October 19, 2026 18:38

@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 is ready to approve: doctor integration components now compares every props table a package ships with the component's TypeScript props, as architecture:cli-surface INV33 requires, never asks a doc for the props authors are told to skip, says when it skipped the comparison, and keeps exit codes unchanged. The restack leaves this change as it was, and its tests, the CLI test suite, the typechecks and the published type check pass on this revision, with Core yielding exactly the 102 listed findings. One optional follow-up: because the exempt-props test moved up to #7667, nothing here fails if key drops out of PROPS_NOT_DOCUMENTED, so declaring an own key?: string in one command-test fixture would guard it until #7667 lands. It can land after its base PRs, in the order the description gives.

[Automated review]

);
fs.writeFileSync(
path.join(tmpDir, 'components', 'AcmeBadge.tsx'),
"export function AcmeBadge(props: {label: string; tone?: 'neutral' | 'info'}) { return null; }\n",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Optional, until #7667 lands: since the restack moved the exempt-props test up to #7667, nothing at this revision fails if 'key' drops out of PROPS_NOT_DOCUMENTED, because Core declares no own key. Declaring one in this fixture, for example key?: string, would make this test guard INV33's key exemption: it should draw no finding.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in bc10598: the fixture declares its own key?: string, and the test fails if key leaves PROPS_NOT_DOCUMENTED.

@josephfarina
josephfarina force-pushed the josephfarina/doctor-prop-drift branch from 02eeac1 to dbbf90d Compare October 19, 2026 19:45

@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 approve: doctor integration components now compares every props table a package ships with the component's TypeScript props, as architecture:cli-surface INV33 requires, warns about each difference without changing the exit code, and says when it skipped the comparison. The new key?: string fixture closes the last test gap, since the command test now fails if key leaves the exempt list. Nothing else is needed here; it can land after its base PRs in the order the description gives, and should ship no later than the release that adds integration check.

[Automated review]

…th the code

`astryx doctor integration components` now runs the foundation prop
comparison on each component doc, read with the project's own
`typescript`, and warns about a documented prop the code lacks, a prop the
package declares that the doc omits, and a wrong `required`. Components
whose props cannot be read are listed with the reason, and the closing
`[ok]` prints only when at least one prop table was compared. Without
TypeScript it prints one line saying the comparison was skipped. Findings
are warnings; JSON gains data.propDrift. Each finding carries both sides
where they apply: the documented type of a prop the code lacks
(`docType`), the code's type for a prop the doc omits as the source writes
it, a union's members in the order the source declares them (`codeType`),
and both flags of a wrong `required` (`docRequired`, `codeRequired`).
The README's integration authoring table names the props check.

Core's own component docs get the same comparison as a repository test,
with the drift Core already has listed in core-prop-docs.known-drift.json;
the list only shrinks.
…ps comparison

The text view now shows each finding's docRequired and codeRequired, and
says how many components were compared and with which TypeScript whether
or not anything differs: in the summary beside findings, in the [ok] when
nothing differs, and in an [info] line when a failed check holds the [ok]
back. A test compares the JSON with the text for each finding kind and
each component not compared.
`astryx docs cli/integrations/building-blocks/components/describe-the-component/check-the-props`
shows how to run `doctor integration components`, what each finding means
and how to fix it, and why a component can be listed as not compared.
The component guides and the checks guide link to it, and the
single-component guide adds the check to the commands to run after a
change.
…props

`doctor integration components` said "N documented prop(s) differ from the
code", but N counts every finding, including a prop the doc leaves out. It
now says "N prop(s) differ between the docs and the code". The guide's
sample output is now a real run with one finding, so its summary agrees
with the finding shown.
Two kinds of table were never compared, so a stale one passed under
`[ok]`. A family doc whose `name` names another export of its file
compared its own table, and its other entries only where discovery
recorded a member: a `hiddenComponents` entry, or one whose source
isn't built yet, was skipped. And a doc with `hidden: true` never
reached the comparison, because discovery leaves it out. Now the doc's
own record compares every entry no member record compares, and an entry
whose source isn't found is listed as not compared with the reason. The
command adds a record, under the doc's file name, for each doc discovery
leaves out: a hidden one, or one whose source or own file doesn't load.
The props guide says hidden docs and every family entry are compared, a
prop a mapped type of the package's own builds is asked for, a prop the
package's `defaultProps` fills counts as optional, and a deprecated prop is
still asked for.
…ide a .doc.mjs

The summary counted components by name, so two docs of one name (in two
folders, or a hidden older one) read as one component: "3 prop(s) differ
... in 1 of 2". It counts each doc's component now. A `.doc.ts` beside a
`.doc.mjs` of the same name was left out, though both ship and validate
checks both; every doc discovery leaves out gets its own record now.
… source is missing

A family doc whose same-stem source is missing gets no record of its own,
but discovery still records the members its package exports, and those
records carry the doc's path. The doc then counted as compared, so its own
table and its `hiddenComponents` entries were neither compared nor listed.
Such a doc now has its own table and every entry no member record compares
compared, or listed as not compared ("no source file for <Name>").
…for a doc entry

A prop a mapped type builds from keys the comparison can't trace to the
package's source (a template literal key, say) draws no `not_in_doc`. The
command now lists such props under `[info] Props not checked for a doc
entry`, with the reason, and JSON adds `propDrift.propsNotChecked`
({component, doc, reason, props}); the `[ok]` line then says it leaves
them out. The props guide says which mapped props are asked for: not one
whose keys another package defines, behind an alias or not. Four of the
comparison's tests move here, from the base, to keep it under 2,000 lines.
…ry finds a member's

A family entry discovery gives no record of its own, such as a
`hiddenComponents` entry, was read only from `<Entry>.tsx`, the family's
own file or the folder's index. When its export is declared in another
file of the folder (`components/menu-parts.tsx`, re-exported from the
package's entry), it was listed as not compared, while the same entry,
not hidden, was compared from the source discovery finds. It now falls
back to the `.tsx` in the doc's folder that declares it, as discovery
does for a member.
… optional

The comparison under this reads a prop that only some members of a union
require as optional, since a caller can leave it out, and holds every prop
a doc entry names to that entry's `required`. The props guide's
`required_mismatch` row says both: name the variants that need such a
prop in its description, and give props separate entries when their flags
differ. The typed-`any` test adds `forwardRef` components whose props are
`Omit` or `Partial` of `any`, which the base now lists, and a polymorphic
component whose props React's `ComponentPropsWithoutRef<E>` names, which
stays listed. Three of the
comparison's tests move here, from the base, to keep it under 2,000 lines
(the `defaultProps` one with the first commit).
The repository test over Core's own docs read only docs beside a
same-stem `.tsx`, so a doc whose `name` names an export in another file
(`Resizable.doc.mjs`) was never compared, and a new mismatch there would
pass. It now reads every `.doc.mjs` under Core's `src`, as the command
reads a package's docs: Resizable's two existing mismatches join the
known list, and the four docs whose own export has no same-stem source
are pinned as listed, not compared.
The test of the exempt props moved up to the prop types PR, so nothing
here failed if `key` left the list of props a doc never needs. A fixture
component in the command test now declares its own `key`, and the test's
exact findings would gain `key not_in_doc` without it.

This branch was successfully deployed

1 active deployment
Preview — a6638ab9 Deployed Oct 19, 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