@astryxdesign/cli 0.6.4-canary.06c8fa3 → 0.6.4-canary.0e1fbdb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/README.md +49 -40
  2. package/api/build/build.doc.mjs +6 -1
  3. package/api/build/build.test.mjs +22 -0
  4. package/api/build/kit/kit.mjs +44 -5
  5. package/api/component/component.doc.mjs +14 -7
  6. package/api/docs/_adapter.d.mts +8 -3
  7. package/api/docs/_adapter.mjs +14 -6
  8. package/api/docs/docOverlays.test.mjs +27 -1
  9. package/api/docs/docs.doc.mjs +2 -2
  10. package/api/doctor/doctor.doc.mjs +17 -8
  11. package/api/doctor/doctor.type.d.mts +1 -1
  12. package/api/doctor/doctor.type.mjs +1 -1
  13. package/api/gap-report/gap-report.doc.mjs +19 -10
  14. package/api/hook/hook.doc.mjs +6 -3
  15. package/api/index.d.mts +2 -0
  16. package/api/index.mjs +3 -1
  17. package/api/init/init.doc.mjs +17 -12
  18. package/api/integration/add-theme.mjs +22 -1
  19. package/api/integration/add-theme.test.mjs +34 -0
  20. package/api/integration/authoring-checks.mjs +2 -2
  21. package/api/integration/integrationPackCheck.doc.mjs +3 -3
  22. package/api/integration/pack-check.lifecycle-output.test.mjs +2 -0
  23. package/api/integration/pack-check.mjs +54 -6
  24. package/api/integration/pack-check.test.mjs +90 -0
  25. package/api/integration/pack-check.type.mjs +1 -1
  26. package/api/json/assertResponse.doc.mjs +1 -1
  27. package/api/json/index.ts +1 -0
  28. package/api/json/isError.doc.mjs +1 -1
  29. package/api/layout/_adapter.d.mts +34 -0
  30. package/api/layout/_adapter.mjs +148 -0
  31. package/api/layout/check/check.d.mts +16 -0
  32. package/api/layout/check/check.mjs +40 -0
  33. package/api/layout/expand/expand.d.mts +22 -0
  34. package/api/layout/expand/expand.mjs +155 -0
  35. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  36. package/api/layout/grammar/grammar.d.mts +13 -0
  37. package/api/layout/grammar/grammar.mjs +87 -0
  38. package/api/layout/layout.d.mts +6 -0
  39. package/api/layout/layout.mjs +17 -0
  40. package/api/layout/layout.test.mjs +297 -0
  41. package/api/layout/layout.type.d.mts +89 -0
  42. package/api/layout/layout.type.mjs +103 -0
  43. package/api/layout/layoutCheck.doc.d.mts +11 -0
  44. package/api/layout/layoutCheck.doc.mjs +85 -0
  45. package/api/layout/layoutExpand.doc.d.mts +11 -0
  46. package/api/layout/layoutExpand.doc.mjs +107 -0
  47. package/api/layout/layoutGrammar.doc.d.mts +11 -0
  48. package/api/layout/layoutGrammar.doc.mjs +57 -0
  49. package/api/search/search.d.mts +27 -1
  50. package/api/search/search.doc.mjs +2 -2
  51. package/api/search/search.mjs +228 -16
  52. package/api/swizzle/swizzle.doc.mjs +7 -5
  53. package/api/template/copy/copy.mjs +1 -1
  54. package/api/template/copy/copy.test.mjs +9 -0
  55. package/api/template/template-integration.test.mjs +65 -1
  56. package/api/template/template.doc.mjs +2 -1
  57. package/api/template/template.mjs +1 -1
  58. package/api/theme/generateTonalPalette.doc.mjs +1 -2
  59. package/api/theme/listThemes.doc.mjs +1 -1
  60. package/api/theme/themeAdd.doc.mjs +9 -10
  61. package/api/theme/themeBuild.doc.mjs +13 -13
  62. package/api/theme/themeList.doc.mjs +1 -1
  63. package/api/theme/themeListAvailable.doc.mjs +2 -1
  64. package/api/theme/themePaletteGenerate.doc.mjs +15 -8
  65. package/api/theme/themeTargets.doc.mjs +3 -2
  66. package/api/theme/themeTemplate.doc.mjs +2 -1
  67. package/api/upgrade/run/run.mjs +1 -1
  68. package/api/upgrade/upgrade.doc.mjs +24 -22
  69. package/assets/docs/README.md +4 -2
  70. package/assets/docs/browser-support.doc.mjs +11 -11
  71. package/assets/docs/color.doc.mjs +8 -2
  72. package/assets/docs/elevation.doc.mjs +6 -4
  73. package/assets/docs/getting-started.doc.mjs +5 -16
  74. package/assets/docs/icons.doc.mjs +2 -21
  75. package/assets/docs/illustrations.doc.mjs +7 -15
  76. package/assets/docs/layout.doc.dense.mjs +130 -82
  77. package/assets/docs/layout.doc.mjs +133 -77
  78. package/assets/docs/migration.doc.mjs +19 -21
  79. package/assets/docs/motion.doc.mjs +16 -3
  80. package/assets/docs/principles.doc.dense.mjs +5 -5
  81. package/assets/docs/principles.doc.mjs +8 -0
  82. package/assets/docs/principles.doc.zh.mjs +6 -6
  83. package/assets/docs/shape.doc.mjs +8 -3
  84. package/assets/docs/spacing.doc.mjs +7 -2
  85. package/assets/docs/styling-libraries.doc.mjs +6 -2
  86. package/assets/docs/styling.doc.mjs +19 -23
  87. package/assets/docs/theme.doc.dense.mjs +58 -18
  88. package/assets/docs/theme.doc.mjs +56 -46
  89. package/assets/docs/theme.doc.zh.mjs +9 -8
  90. package/assets/docs/tokens.doc.dense.mjs +2 -2
  91. package/assets/docs/tokens.doc.mjs +389 -8
  92. package/assets/docs/tokens.doc.zh.mjs +2 -2
  93. package/assets/docs/tree/add-a-component.doc.mjs +75 -0
  94. package/assets/docs/tree/add-a-theme.doc.mjs +85 -0
  95. package/assets/docs/tree/add-a-topic.doc.mjs +144 -0
  96. package/assets/docs/tree/agent-guidance.doc.mjs +138 -0
  97. package/assets/docs/tree/block-template.doc.mjs +130 -0
  98. package/assets/docs/tree/build-the-template.doc.mjs +28 -0
  99. package/assets/docs/tree/building-blocks.doc.mjs +46 -0
  100. package/assets/docs/tree/check-your-docs.doc.mjs +137 -0
  101. package/assets/docs/tree/checks.doc.mjs +119 -0
  102. package/assets/docs/tree/codemods.doc.mjs +147 -0
  103. package/assets/docs/tree/component-family.doc.mjs +113 -0
  104. package/assets/docs/tree/component-imports.doc.mjs +69 -0
  105. package/assets/docs/tree/components.doc.mjs +23 -0
  106. package/assets/docs/tree/configuration.doc.mjs +23 -0
  107. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +182 -0
  108. package/assets/docs/tree/define-the-theme.doc.mjs +118 -0
  109. package/assets/docs/tree/describe-the-component.doc.mjs +57 -0
  110. package/assets/docs/tree/docs.doc.mjs +21 -0
  111. package/assets/docs/tree/document-the-template.doc.mjs +28 -0
  112. package/assets/docs/tree/document-the-theme.doc.mjs +68 -0
  113. package/assets/docs/tree/export-template-assets.doc.mjs +147 -0
  114. package/assets/docs/tree/extend-or-replace.doc.mjs +103 -0
  115. package/assets/docs/tree/fonts-and-assets.doc.mjs +106 -0
  116. package/assets/docs/tree/generate-a-palette.doc.mjs +66 -0
  117. package/assets/docs/tree/grade-template-with-agent.doc.mjs +105 -0
  118. package/assets/docs/tree/help.doc.mjs +16 -0
  119. package/assets/docs/tree/integrations.doc.mjs +25 -470
  120. package/assets/docs/tree/links.doc.mjs +98 -0
  121. package/assets/docs/tree/package-and-test.doc.mjs +32 -0
  122. package/assets/docs/tree/page-template.doc.mjs +71 -0
  123. package/assets/docs/tree/publishing.doc.mjs +111 -0
  124. package/assets/docs/tree/quick-start.doc.mjs +272 -0
  125. package/assets/docs/tree/replace-a-core-component.doc.mjs +104 -0
  126. package/assets/docs/tree/replace-a-core-template.doc.mjs +172 -0
  127. package/assets/docs/tree/sections-and-placement.doc.mjs +108 -0
  128. package/assets/docs/tree/see-it-in-an-app.doc.mjs +59 -0
  129. package/assets/docs/tree/ship.doc.mjs +16 -0
  130. package/assets/docs/tree/short-and-findable.doc.mjs +108 -0
  131. package/assets/docs/tree/single-component.doc.mjs +165 -0
  132. package/assets/docs/tree/start-a-template.doc.mjs +143 -0
  133. package/assets/docs/tree/subcomponent.doc.mjs +115 -0
  134. package/assets/docs/tree/template-assets.doc.mjs +64 -0
  135. package/assets/docs/tree/template-doc-overview.doc.mjs +109 -0
  136. package/assets/docs/tree/template-fonts.doc.mjs +102 -0
  137. package/assets/docs/tree/template-grading-rubric.doc.mjs +452 -0
  138. package/assets/docs/tree/template-icons.doc.mjs +97 -0
  139. package/assets/docs/tree/template-images-media.doc.mjs +127 -0
  140. package/assets/docs/tree/template-styles.doc.mjs +93 -0
  141. package/assets/docs/tree/templates.doc.mjs +34 -0
  142. package/assets/docs/tree/test-in-an-app.doc.mjs +115 -0
  143. package/assets/docs/tree/test-template-in-app.doc.mjs +128 -0
  144. package/assets/docs/tree/themes.doc.mjs +39 -0
  145. package/assets/docs/tree/troubleshooting.doc.mjs +149 -0
  146. package/assets/docs/tree/upgrading.doc.mjs +103 -0
  147. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +51 -0
  148. package/assets/docs/tree/verify-packed-template.doc.mjs +77 -0
  149. package/assets/docs/tree/versioning.doc.mjs +161 -0
  150. package/assets/docs/tree/write-good-templates.doc.mjs +64 -0
  151. package/assets/docs/tree/write-the-template-file.doc.mjs +154 -0
  152. package/assets/docs/typography.doc.mjs +24 -4
  153. package/assets/docs/working-with-ai.doc.mjs +30 -22
  154. package/authoring/config/config.doc.mjs +2 -2
  155. package/authoring/config/type.ts +2 -2
  156. package/authoring/doctypes/_schema.d.mts +3 -2
  157. package/authoring/doctypes/_schema.mjs +6 -0
  158. package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
  159. package/authoring/doctypes/base/type.ts +4 -2
  160. package/authoring/doctypes/command/command.doc.mjs +1 -1
  161. package/authoring/doctypes/command/type.ts +1 -1
  162. package/authoring/doctypes/component/component.doc.mjs +6 -0
  163. package/authoring/doctypes/component/type.ts +8 -0
  164. package/authoring/doctypes/reference/reference.doc.mjs +7 -0
  165. package/authoring/doctypes/reference/type.ts +5 -0
  166. package/authoring/doctypes/schema/schema.doc.mjs +2 -2
  167. package/authoring/doctypes/template/template.doc.mjs +1 -1
  168. package/authoring/doctypes/template/type.ts +2 -2
  169. package/authoring/integration/integration.doc.mjs +12 -10
  170. package/clients/cli/command-result-coverage.test.mjs +7 -7
  171. package/clients/cli/commands/component.doc.mjs +4 -3
  172. package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
  173. package/clients/cli/commands/docs.doc.mjs +1 -1
  174. package/clients/cli/commands/docs.mjs +60 -17
  175. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -2
  176. package/clients/cli/commands/doctor-integration.test.mjs +53 -0
  177. package/clients/cli/commands/doctor.doc.mjs +3 -1
  178. package/clients/cli/commands/doctor.mjs +49 -5
  179. package/clients/cli/commands/gap-report.doc.mjs +10 -9
  180. package/clients/cli/commands/init.doc.mjs +9 -6
  181. package/clients/cli/commands/integration-add.doc.mjs +9 -9
  182. package/clients/cli/commands/integration-authoring.test.mjs +61 -10
  183. package/clients/cli/commands/integration-pack.doc.mjs +5 -9
  184. package/clients/cli/commands/integration-real-world.test.mjs +1 -1
  185. package/clients/cli/commands/integration-verify.doc.mjs +22 -0
  186. package/clients/cli/commands/integration.doc.mjs +4 -4
  187. package/clients/cli/commands/integration.mjs +74 -43
  188. package/clients/cli/commands/layout-check.doc.mjs +65 -0
  189. package/clients/cli/commands/layout-expand.doc.mjs +83 -0
  190. package/clients/cli/commands/layout-grammar.doc.mjs +30 -0
  191. package/clients/cli/commands/layout.doc.mjs +34 -0
  192. package/clients/cli/commands/layout.error-codes.test.mjs +66 -0
  193. package/clients/cli/commands/layout.exit-parity.test.mjs +41 -0
  194. package/clients/cli/commands/layout.mjs +275 -0
  195. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  196. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  197. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  198. package/clients/cli/commands/manifest.doc.mjs +1 -1
  199. package/clients/cli/commands/search.doc.mjs +10 -3
  200. package/clients/cli/commands/search.mjs +21 -2
  201. package/clients/cli/commands/search.test.mjs +21 -4
  202. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  203. package/clients/cli/commands/template.doc.mjs +1 -1
  204. package/clients/cli/commands/text-json-parity.test.mjs +24 -1
  205. package/clients/cli/commands/theme-add.doc.mjs +1 -1
  206. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -2
  207. package/clients/cli/commands/theme-palette.doc.mjs +1 -2
  208. package/clients/cli/commands/theme-targets.doc.mjs +2 -2
  209. package/clients/cli/commands/theme.doc.mjs +2 -1
  210. package/clients/cli/commands/upgrade.doc.mjs +62 -3
  211. package/clients/cli/index.mjs +32 -6
  212. package/clients/cli/lib/define-command.mjs +28 -4
  213. package/clients/cli/lib/define-command.test.mjs +54 -0
  214. package/clients/cli/lib/exit-codes.test.mjs +25 -2
  215. package/clients/cli/lib/json-shim.test.mjs +20 -6
  216. package/clients/cli/lib/manifest.mjs +23 -5
  217. package/foundation/agent-docs/agent-docs.mjs +1 -1
  218. package/foundation/discovery/authoring-self-docs.test.mjs +6 -2
  219. package/foundation/discovery/cli-self-docs.mjs +16 -2
  220. package/foundation/discovery/cli-self-docs.test.mjs +20 -0
  221. package/foundation/discovery/docs-discovery.mjs +5 -1
  222. package/foundation/discovery/docs-discovery.test.mjs +21 -0
  223. package/foundation/discovery/docs-section-key.d.mts +1 -1
  224. package/foundation/discovery/docs-section-key.mjs +1 -1
  225. package/foundation/discovery/template-adapter.mjs +1 -1
  226. package/foundation/doc-compiler/doc-loads.test.mjs +15 -2
  227. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  228. package/foundation/doc-compiler/tree.d.mts +4 -0
  229. package/foundation/doc-compiler/tree.mjs +6 -1
  230. package/foundation/integrations/cli-requirement.d.mts +26 -6
  231. package/foundation/integrations/cli-requirement.mjs +46 -11
  232. package/foundation/integrations/cli-requirement.test.mjs +7 -2
  233. package/foundation/integrations/contribution-inventory.mjs +1 -1
  234. package/foundation/response/error-codes.doc.mjs +6 -8
  235. package/foundation/response/error-codes.test.mjs +30 -5
  236. package/foundation/response/response-types.doc.d.mts +4 -3
  237. package/foundation/response/response-types.doc.mjs +42 -6
  238. package/foundation/response/response.doc.mjs +11 -10
  239. package/foundation/xle/browser.d.mts +3 -3
  240. package/foundation/xle/browser.mjs +3 -3
  241. package/foundation/xle/expand.mjs +2 -2
  242. package/foundation/xle/parse.mjs +1 -1
  243. package/foundation/xle/print.mjs +2 -2
  244. package/foundation/xle/splice.mjs +1 -1
  245. package/package.json +9 -9
  246. package/api/docs/docs.test.mjs +0 -245
  247. package/api/docs/integration-tree.test.mjs +0 -555
  248. package/api/docs/integrationDocs.test.mjs +0 -314
  249. package/api/search/search.test.mjs +0 -530
  250. package/assets/docs/tree/integrations.test.mjs +0 -62
  251. package/assets/docs/tree/writing-docs.doc.mjs +0 -286
  252. package/clients/cli/commands/docs.test.mjs +0 -323
  253. package/foundation/agent-docs/agent-docs.test.mjs +0 -1159
  254. package/foundation/doc-compiler/tree.test.mjs +0 -606
@@ -74,7 +74,7 @@ export function templateCopy(match, {targetPath, cwd, overwrite = false}) {
74
74
  if (!overwrite && fs.existsSync(outputFilePath)) {
75
75
  const rel = path.relative(cwd, outputFilePath) || outputFilePath;
76
76
  throw new AstryxError(
77
- `Refusing to overwrite existing file ${rel}. Re-run with overwrite to replace it.`,
77
+ `Refusing to overwrite existing file ${rel}. Re-run with --overwrite (or -f) to replace it.`,
78
78
  undefined,
79
79
  ERROR_CODES.ERR_FILE_EXISTS,
80
80
  );
@@ -35,6 +35,15 @@ describe('template.copy — overwrite + path safety', () => {
35
35
  expect(fs.readFileSync(path.join(dir, 'mine.tsx'), 'utf-8')).toBe('USER CODE');
36
36
  }, SLOW);
37
37
 
38
+ it('names the flag that replaces the file, as swizzle and theme add do', async () => {
39
+ fs.writeFileSync(path.join(dir, 'mine.tsx'), 'USER CODE');
40
+ await expect(
41
+ template('blank', {targetPath: './mine.tsx', cwd: dir}),
42
+ ).rejects.toThrow(
43
+ 'Refusing to overwrite existing file mine.tsx. Re-run with --overwrite (or -f) to replace it.',
44
+ );
45
+ }, SLOW);
46
+
38
47
  it('overwrites when overwrite:true is passed', async () => {
39
48
  fs.writeFileSync(path.join(dir, 'mine.tsx'), 'USER CODE');
40
49
  const res = await template('blank', {targetPath: './mine.tsx', overwrite: true, cwd: dir});
@@ -19,6 +19,7 @@ import {
19
19
  } from './template.mjs';
20
20
  import {search} from '../search/search.mjs';
21
21
  import {build} from '../build/build.mjs';
22
+ import {layoutExpand} from '../layout/layout.mjs';
22
23
  import {runCli} from '../../test-utils/run-cli.mjs';
23
24
 
24
25
  let tmpDir;
@@ -517,7 +518,53 @@ describe('integration template discovery', () => {
517
518
  });
518
519
  });
519
520
 
520
- it('resolves chained block aliases consistently in template', async () => {
521
+ it('resolves a replacement block through the layout alias', async () => {
522
+ const pkgDir = installWidgets(tmpDir);
523
+ writeTemplate(pkgDir, 'acme-card-callout', {
524
+ kind: 'block',
525
+ source:
526
+ 'export default function AcmeCardCallout() { return <span>Acme replacement block</span>; }\n',
527
+ });
528
+ declareReplaces(pkgDir, {
529
+ 'acme-card-callout': 'CardCallout',
530
+ });
531
+
532
+ const accidental = path.join(tmpDir, 'node_modules', '@acme', 'extra');
533
+ fs.mkdirSync(path.join(accidental, 'templates'), {recursive: true});
534
+ fs.writeFileSync(
535
+ path.join(accidental, 'package.json'),
536
+ JSON.stringify({name: '@acme/extra', version: '1.0.0'}),
537
+ );
538
+ fs.writeFileSync(
539
+ path.join(accidental, 'astryx.integration.mjs'),
540
+ `export default {templates: './templates'};\n`,
541
+ );
542
+ writeTemplate(accidental, 'CardCallout', {
543
+ kind: 'block',
544
+ body: `export default {type: 'block', name: 'ZZZ accidental block', description: 'collision'};\n`,
545
+ source:
546
+ 'export default function Accidental() { return <span>Accidental block</span>; }\n',
547
+ });
548
+ fs.writeFileSync(
549
+ path.join(tmpDir, 'astryx.config.mjs'),
550
+ `export default { integrations: ['@acme/widgets', '@acme/extra'] };\n`,
551
+ );
552
+
553
+ const result = await layoutExpand('C{card-callout}', {
554
+ name: 'ReplacementLayout',
555
+ cwd: tmpDir,
556
+ });
557
+
558
+ expect(result.data.code).toContain('Acme replacement block');
559
+ expect(result.data.code).not.toContain('Accidental block');
560
+ expect(result.data.blocksReferenced).toEqual(
561
+ expect.arrayContaining([
562
+ expect.objectContaining({name: 'CardCallout', mode: 'splice'}),
563
+ ]),
564
+ );
565
+ }, 30_000);
566
+
567
+ it('resolves chained block aliases consistently in template and layout', async () => {
521
568
  const [firstTarget, secondTarget] = (await discoverCoreTemplates()).filter(
522
569
  candidate => candidate.type === 'block',
523
570
  );
@@ -545,12 +592,29 @@ describe('integration template discovery', () => {
545
592
  cwd: tmpDir,
546
593
  });
547
594
  expect(firstTemplate.data.source).toContain('First chain replacement');
595
+ const layoutId = id =>
596
+ id.replace(/([a-z0-9])([A-Z])/gu, '$1-$2').toLowerCase();
597
+ const firstLayout = await layoutExpand(
598
+ `C{${layoutId(firstTarget.dirName)}}`,
599
+ {
600
+ cwd: tmpDir,
601
+ },
602
+ );
603
+ expect(firstLayout.data.code).toContain('First chain replacement');
604
+ expect(firstLayout.data.code).not.toContain('Final chain replacement');
548
605
 
549
606
  const secondTemplate = await template(secondTarget.dirName, {
550
607
  show: true,
551
608
  cwd: tmpDir,
552
609
  });
553
610
  expect(secondTemplate.data.source).toContain('Final chain replacement');
611
+ const secondLayout = await layoutExpand(
612
+ `C{${layoutId(secondTarget.dirName)}}`,
613
+ {
614
+ cwd: tmpDir,
615
+ },
616
+ );
617
+ expect(secondLayout.data.code).toContain('Final chain replacement');
554
618
  }, 30_000);
555
619
 
556
620
  it('keeps the old discovery behavior when no replacement is declared', async () => {
@@ -102,6 +102,7 @@ export const doc = {
102
102
  type: 'string',
103
103
  description:
104
104
  'Directory to discover templates and resolve the target path from.',
105
+ default: 'process.cwd()',
105
106
  },
106
107
  ],
107
108
  returns: [
@@ -134,7 +135,7 @@ export const doc = {
134
135
  throws: [
135
136
  {
136
137
  code: 'ERR_UNKNOWN_TEMPLATE',
137
- when: 'the named template does not exist, or --skeleton is run without a name',
138
+ when: 'the named template does not exist, or options.skeleton is set without a name',
138
139
  },
139
140
  {
140
141
  code: 'ERR_AMBIGUOUS_TEMPLATE',
@@ -8,7 +8,7 @@
8
8
  * the requested one, and routes to a leaf (list/show/skeleton/copy/cdn). The shared
9
9
  * discovery/IO + cross-command helpers live in `foundation/discovery/template-adapter.mjs` and are
10
10
  * RE-EXPORTED here so external import paths (`api/template/template.mjs`) —
11
- * used by component, search, init, discover, Doctor integration, and
11
+ * used by component, layout, search, init, discover, Doctor integration, and
12
12
  * lib/project — keep resolving unchanged.
13
13
  *
14
14
  * @position api/template — the template dispatcher + barrel; leaves live under
@@ -29,7 +29,7 @@ export const doc = {
29
29
  name: 'input',
30
30
  type: 'TonalPaletteGenerationInput',
31
31
  description:
32
- 'Families and seeds plus optional modes, shared stops, anchors, vibrancy from 0 to 100 (default 50), and neutral profile. Only generate an accent family when one is explicitly requested; clarify whether an ambiguous accent means one theme value or a tonal family.',
32
+ 'Families and seeds plus optional modes, shared stops, anchors, vibrancy from 0 to 100 (default 50), and neutral profile.',
33
33
  required: true,
34
34
  },
35
35
  ],
@@ -72,6 +72,5 @@ export const doc = {
72
72
  code: "generateTonalPalette({stops: [12.5, 50], families: [{id: 'blue', seed: '#0074e2'}]});",
73
73
  },
74
74
  ],
75
- command: 'theme palette generate',
76
75
  related: ['themePaletteGenerate'],
77
76
  };
@@ -13,7 +13,7 @@ export const doc = {
13
13
  displayName: 'listThemes()',
14
14
  summary: 'Read the CLI bundled-theme descriptors.',
15
15
  description:
16
- 'Reads the typed same-stem descriptors under templates/themes and returns normalized entries synchronously. This low-level helper keeps its historical bundled-only contract; project-aware themeList() and themeAdd() also discover source themes from installed integrations.',
16
+ "Synchronously returns the themes bundled with the CLI, including each one's entry file, export name and file list. Bundled themes only; use themeListAvailable() to include themes from installed integrations.",
17
17
  importPath: '@astryxdesign/cli/api',
18
18
  signature: 'listThemes(): BundledTheme[]',
19
19
  keywords: ['theme', 'themes', 'descriptor', 'bundled', 'adapter', 'list'],
@@ -44,6 +44,7 @@ export const doc = {
44
44
  type: 'string',
45
45
  description:
46
46
  'Project directory used for integration discovery and target paths.',
47
+ default: 'process.cwd()',
47
48
  },
48
49
  {
49
50
  name: 'options.package',
@@ -57,11 +58,6 @@ export const doc = {
57
58
  description:
58
59
  'Copy receipt with slug, displayName, maintained flag, owner package, outputDir, entry, exportName, and files.',
59
60
  },
60
- {
61
- type: 'theme.list',
62
- description:
63
- 'The CLI list affordance routes a bare `astryx theme add` or `--list` to themeListAvailable() and returns every available theme with its owner.',
64
- },
65
61
  ],
66
62
  throws: [
67
63
  {
@@ -74,7 +70,10 @@ export const doc = {
74
70
  when: 'the selected installed package has a blocking integration or theme-descriptor error',
75
71
  },
76
72
  {code: 'ERR_PATH_TRAVERSAL', when: 'the target path escapes cwd'},
77
- {code: 'ERR_NO_SOURCE', when: 'a theme file to copy is missing'},
73
+ {
74
+ code: 'ERR_NO_SOURCE',
75
+ when: 'the bundled theme descriptors cannot be read, or a theme file to copy is missing',
76
+ },
78
77
  {
79
78
  code: 'ERR_FILE_EXISTS',
80
79
  when: 'a destination exists and overwrite is not set',
@@ -82,12 +81,12 @@ export const doc = {
82
81
  {code: 'ERR_WRITE_FAILED', when: 'writing files fails'},
83
82
  ],
84
83
  examples: [
85
- {label: 'Copy a bundled theme', code: "await themeAdd('ocean');"},
84
+ {label: 'Copy a bundled theme', code: "await themeAdd('butter');"},
86
85
  {
87
- label: 'Copy an integration theme',
88
- code: "await themeAdd('ocean', {package: '@acme/themes'});",
86
+ label: 'Name the owner package and the destination',
87
+ code: "await themeAdd('butter', {package: '@astryxdesign/cli', targetPath: 'src/brand-theme'});",
89
88
  },
90
89
  ],
91
90
  command: 'theme add',
92
- related: ['themeList', 'listThemes'],
91
+ related: ['themeListAvailable', 'themeTemplate', 'listThemes'],
93
92
  };
@@ -14,14 +14,13 @@ export const doc = {
14
14
  name: 'themeBuild',
15
15
  namespace: 'cli/api',
16
16
  displayName: 'themeBuild()',
17
- summary: 'Compile a defineTheme file to CSS + JS + type declarations.',
17
+ summary:
18
+ 'Compile a defineTheme() file to scoped CSS, a JS module, and type declarations, or check committed outputs for drift in CI.',
18
19
  description:
19
- 'The compiler behind `astryx theme build`. Reads a file that calls defineTheme() and, ' +
20
- "via @astryxdesign/core's shared generator (the single source of truth, so the build " +
21
- 'emits the exact CSS the <Theme> runtime does), writes a scoped CSS file, a JS module ' +
22
- 'that re-exports the built theme, and a .d.ts (plus an optional .variants.d.ts when the ' +
23
- 'theme adds custom prop values). When another build step emits the icon registry, ' +
24
- '{iconsSpecifier} declares the fully specified module path for the generated JS import. ' +
20
+ 'The compiler behind `astryx theme build`. Reads a file that calls defineTheme() and ' +
21
+ 'writes a scoped CSS file, a JS module that re-exports the built theme, and a .d.ts ' +
22
+ '(plus an optional .variants.d.ts when the theme adds custom prop values). It uses ' +
23
+ "@astryxdesign/core's own generator, so the CSS matches what the <Theme> runtime emits. " +
25
24
  'With {check: true} it writes nothing and instead compares ' +
26
25
  'each output against disk, returning the drift: the CI guard for committed, generated theme CSS.',
27
26
  importPath: '@astryxdesign/cli/api',
@@ -48,7 +47,7 @@ export const doc = {
48
47
  name: 'options.out',
49
48
  type: 'string',
50
49
  description:
51
- 'Override the output CSS path; the sibling .js and .d.ts derive from it. A relative path must stay within cwd.',
50
+ 'Override the output CSS path. The .js, .d.ts and any .variants.d.ts are written in the same directory, named after the theme (<name>.js), not after the CSS file. A relative path must stay within cwd.',
52
51
  },
53
52
  {
54
53
  name: 'options.check',
@@ -61,13 +60,14 @@ export const doc = {
61
60
  name: 'options.iconsSpecifier',
62
61
  type: 'string',
63
62
  description:
64
- 'Override the icon-registry import specifier in the generated JS module, for example ./icons.mjs. When omitted, the source specifier is preserved.',
63
+ 'Override the import specifier of the icon registry in the generated JS module, for example ./icons.mjs. Takes effect only when the theme sets icons: to a named import; when omitted, the source specifier is kept.',
65
64
  },
66
65
  {
67
66
  name: 'ctx.cwd',
68
67
  type: 'string',
69
68
  description:
70
- 'Directory the theme file and @astryxdesign/core resolve against.',
69
+ 'Directory the theme file, a relative out path, and the returned output paths resolve against.',
70
+ default: 'process.cwd()',
71
71
  },
72
72
  ],
73
73
  returns: [
@@ -79,7 +79,7 @@ export const doc = {
79
79
  {
80
80
  type: 'theme.build.check',
81
81
  description:
82
- 'The {check: true} receipt: theme name, an upToDate flag, the stale outputs (each {path, reason: "missing" | "outdated"}), and the full list of checked paths. Writes nothing.',
82
+ 'The {check: true} receipt: theme name, an upToDate flag, the stale outputs (each {path, reason: "missing" | "outdated"}), and the full list of checked paths. Writes nothing. Resolves to null, like a normal build, when the theme produces no CSS.',
83
83
  },
84
84
  ],
85
85
  throws: [
@@ -102,7 +102,7 @@ export const doc = {
102
102
  },
103
103
  {
104
104
  code: 'ERR_CORE_INCOMPATIBLE',
105
- when: 'the selected theme carries ordered-adaptation intent but the installed @astryxdesign/core does not export generateAdaptationCSS (upgrade core)',
105
+ when: 'the installed @astryxdesign/core does not export generateAdaptationCSS and the theme either declares ordered adaptations or has lineage whose adaptation use could not be observed (upgrade core)',
106
106
  },
107
107
  {
108
108
  code: 'ERR_WRITE_FAILED',
@@ -124,5 +124,5 @@ export const doc = {
124
124
  },
125
125
  ],
126
126
  command: 'theme build',
127
- related: ['themeAdd', 'themeList', 'listThemes'],
127
+ related: ['themeTemplate', 'themeAdd', 'themeListAvailable', 'listThemes'],
128
128
  };
@@ -13,7 +13,7 @@ export const doc = {
13
13
  displayName: 'themeList()',
14
14
  summary: 'List themes bundled with this CLI build.',
15
15
  description:
16
- 'Projects the bundled typed theme descriptors into a synchronous theme.list envelope. This preserves the original programmatic API contract. The CLI command uses themeListAvailable() so installed integrations also appear.',
16
+ 'Synchronous list of the themes bundled with the CLI. It does not include themes from installed integrations; use themeListAvailable() for the list `astryx theme list` shows.',
17
17
  importPath: '@astryxdesign/cli/api',
18
18
  signature: 'themeList(): ThemeListResponse',
19
19
  keywords: ['theme', 'list', 'themes', 'bundled', 'available'],
@@ -13,7 +13,7 @@ export const doc = {
13
13
  displayName: 'themeListAvailable()',
14
14
  summary: 'List bundled and installed integration themes.',
15
15
  description:
16
- 'Loads Project for the requested directory, combines the CLI bundle with source themes from installed integrations, and projects each entry with its owner package. An unreadable project configuration degrades to the bundled descriptors.',
16
+ 'Lists the bundled themes plus source themes from integrations installed in cwd, each with its owner package. If the project configuration cannot be read, it falls back to the bundled themes.',
17
17
  importPath: '@astryxdesign/cli/api',
18
18
  signature:
19
19
  'themeListAvailable(options?: {cwd?: string, package?: string}): Promise<ThemeListResponse>',
@@ -24,6 +24,7 @@ export const doc = {
24
24
  type: 'string',
25
25
  description:
26
26
  'Project directory whose installed integrations contribute themes.',
27
+ default: 'process.cwd()',
27
28
  },
28
29
  {
29
30
  name: 'options.package',
@@ -27,30 +27,34 @@ export const doc = {
27
27
  {
28
28
  name: 'configPath',
29
29
  type: 'string',
30
- description: 'JSON generation request, resolved within cwd.',
30
+ description:
31
+ 'Path to a JSON file holding a TonalPaletteGenerationInput (the object generateTonalPalette() takes), resolved within cwd.',
31
32
  required: true,
32
33
  },
33
34
  {
34
35
  name: 'options.out',
35
36
  type: 'string',
36
37
  description:
37
- 'Optional candidate JSON destination. A sibling .receipt.json path is derived from it.',
38
+ 'Where to write the candidate: a path ending in .ts (a TypeScript module) or .json. A sibling <name>.receipt.json is written next to it.',
38
39
  },
39
40
  {
40
41
  name: 'options.preview',
41
42
  type: 'string',
42
- description: 'Optional path for a self-contained HTML review artifact.',
43
+ description:
44
+ 'Optional path, ending in .html, for a self-contained HTML review page.',
43
45
  },
44
46
  {
45
47
  name: 'options.overwrite',
46
48
  type: 'boolean',
47
- description: 'Replace existing candidate and receipt files.',
49
+ description:
50
+ "Replace existing candidate, receipt and preview files. Without it, if any target exists, nothing is written and the result has written: false, reason: 'exists'.",
48
51
  default: 'false',
49
52
  },
50
53
  {
51
54
  name: 'ctx.cwd',
52
55
  type: 'string',
53
56
  description: 'Directory used to resolve the input and output paths.',
57
+ default: 'process.cwd()',
54
58
  },
55
59
  ],
56
60
  returns: [
@@ -64,17 +68,20 @@ export const doc = {
64
68
  {code: 'ERR_FILE_NOT_FOUND', when: 'the config file does not exist'},
65
69
  {
66
70
  code: 'ERR_PALETTE_GENERATION',
67
- when: 'the request, seed, stop layout, mode, or anchor constraint is invalid',
71
+ when: 'the config is not valid JSON; the request, seed, stop layout, mode or anchor is invalid; out does not end in .ts or .json; or preview does not end in .html',
68
72
  },
69
73
  {
70
74
  code: 'ERR_PATH_TRAVERSAL',
71
75
  when: 'an input or output path escapes cwd, or output would replace input',
72
76
  },
73
- {code: 'ERR_WRITE_FAILED', when: 'the candidate pair cannot be written'},
77
+ {
78
+ code: 'ERR_WRITE_FAILED',
79
+ when: 'the candidate, receipt or preview file cannot be written',
80
+ },
74
81
  ],
75
82
  examples: [
76
83
  {
77
- label: 'Preview a candidate',
84
+ label: 'Generate a candidate without writing files',
78
85
  code: "themePaletteGenerate('palette.config.json');",
79
86
  },
80
87
  {
@@ -83,5 +90,5 @@ export const doc = {
83
90
  },
84
91
  ],
85
92
  command: 'theme palette generate',
86
- related: ['themeBuild', 'themeTemplate'],
93
+ related: ['generateTonalPalette', 'themeBuild', 'themeTemplate'],
87
94
  };
@@ -22,7 +22,7 @@ export const doc = {
22
22
  'Same source as the Theming table `astryx component <Name>` prints ' +
23
23
  '(the component docs), so the list cannot drift from the components, and `theme build` ' +
24
24
  'validates overrides against this exact set. A filter naming a component gives that ' +
25
- "component's set; anything else is a substring search over the keys.",
25
+ "component's set; anything else is a case-insensitive substring search over each target's key, class and component.",
26
26
  importPath: '@astryxdesign/cli/api',
27
27
  signature:
28
28
  'themeTargets(filter?: string, ctx?: {cwd?: string}): Promise<ThemeTargetsResponse>',
@@ -41,13 +41,14 @@ export const doc = {
41
41
  name: 'filter',
42
42
  type: 'string',
43
43
  description:
44
- 'A component name (exact, case-insensitive) or a substring of a target key. Omit for the whole surface.',
44
+ 'A component name (exact, case-insensitive), or a substring of a target key, class or component. Omit for the whole surface.',
45
45
  },
46
46
  {
47
47
  name: 'ctx.cwd',
48
48
  type: 'string',
49
49
  description:
50
50
  "Directory the project's @astryxdesign/core is resolved from.",
51
+ default: 'process.cwd()',
51
52
  },
52
53
  ],
53
54
  returns: [
@@ -43,6 +43,7 @@ export const doc = {
43
43
  name: 'options.cwd',
44
44
  type: 'string',
45
45
  description: 'Directory the target path resolves against.',
46
+ default: 'process.cwd()',
46
47
  },
47
48
  ],
48
49
  returns: [
@@ -64,5 +65,5 @@ export const doc = {
64
65
  },
65
66
  ],
66
67
  command: 'theme template',
67
- related: ['themeAdd', 'themeBuild', 'themeList'],
68
+ related: ['themeAdd', 'themeBuild', 'themeListAvailable', 'themeTargets'],
68
69
  };
@@ -114,7 +114,7 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
114
114
  // Resolve the source dir against the API's cwd (not process.cwd()) so a
115
115
  // programmatic caller in another directory scans the right tree. Confine it to
116
116
  // cwd: --apply rewrites files in place, so a `..`-escaping or out-of-tree
117
- // absolute --path must be rejected (parity with template/theme/swizzle,
117
+ // absolute --path must be rejected (parity with template/theme/swizzle/layout,
118
118
  // and this is the most destructive command). allowAbsolute permits an absolute
119
119
  // path that still resolves inside cwd.
120
120
  let path_;
@@ -13,19 +13,16 @@ export const doc = {
13
13
  name: 'upgrade',
14
14
  namespace: 'cli/api',
15
15
  displayName: 'upgrade()',
16
- summary: 'Run version migrations and reconcile copied compositions.',
16
+ summary:
17
+ 'After bumping @astryxdesign/core, migrate project source with codemods and update copied compositions.',
17
18
  description:
18
- 'Migrates project source from a previous Astryx version to the currently ' +
19
- 'installed one by running the registered codemods, and compares the fully ' +
20
- 'rendered managed agent-docs block on every migration path, including ' +
21
- 'same-Core integration guidance changes; list and registry-only modes do not ' +
22
- 'run migration reconciliation. Dry-run previews without writing; `apply` ' +
23
- 'writes the prepared block only after selected codemods and hooks succeed. ' +
24
- 'Core codemods run before ' +
25
- 'the config is loaded so a config codemod can repair an otherwise-invalid ' +
26
- 'astryx.config. Copied compositions carry adjacent receipts with exact canonical and format-specific install bases; upgrade ' +
27
- 'compares those installed bases with the matching registry release, updates pristine ' +
28
- 'files, merges non-overlapping edits, and leaves conflicting originals untouched.',
19
+ 'Runs the codemods between `from` and the installed Core version, then refreshes the ' +
20
+ 'managed agent-docs block. Dry-run by default; `apply` writes changes only after the ' +
21
+ 'selected codemods and hooks succeed. Config codemods run before astryx.config is ' +
22
+ 'loaded, so they can repair an invalid config. Also updates copied compositions from ' +
23
+ 'their install receipts: unchanged files are updated, non-overlapping edits are merged, ' +
24
+ 'and conflicts are left untouched. `list` only lists codemods; `registry` only updates ' +
25
+ 'copied compositions.',
29
26
  importPath: '@astryxdesign/cli/api',
30
27
  signature:
31
28
  'upgrade(options?: UpgradeOptions, ctx?: {cwd?: string}): Promise<UpgradeListResponse | UpgradeRegistryResponse | UpgradeStatusResponse | UpgradeRunResponse>',
@@ -42,7 +39,7 @@ export const doc = {
42
39
  name: 'options.from',
43
40
  type: 'string',
44
41
  description:
45
- 'Version before the dependency bump. Required unless `list` or `registry` is set.',
42
+ 'Version before the dependency bump; the target is the installed @astryxdesign/core (or legacy @xds/core). Required unless `list` or `registry` is set.',
46
43
  },
47
44
  {
48
45
  name: 'options.apply',
@@ -55,11 +52,13 @@ export const doc = {
55
52
  type: 'boolean',
56
53
  description:
57
54
  'Run codemods even when `from` is at/after the installed version.',
55
+ default: 'false',
58
56
  },
59
57
  {
60
58
  name: 'options.codemod',
61
59
  type: 'string',
62
- description: 'Run a single named transform instead of the full set.',
60
+ description:
61
+ 'Run only this codemod. Optional codemods run only when named here. Setting it also skips copied-composition reconciliation.',
63
62
  },
64
63
  {
65
64
  name: 'options.skipCodemod',
@@ -83,23 +82,26 @@ export const doc = {
83
82
  type: 'boolean',
84
83
  description:
85
84
  'Install jscodeshift when it is missing; otherwise a missing jscodeshift throws ERR_DEP_MISSING.',
85
+ default: 'false',
86
86
  },
87
87
  {
88
88
  name: 'options.registry',
89
89
  type: 'boolean',
90
90
  description:
91
- 'Reconcile copied compositions from their install receipts without requiring `from`.',
91
+ 'Only reconcile copied compositions from their install receipts; `from` is not required. Cannot be combined with `list`, `from`, `force`, `codemod`, `skipCodemod`, `integration` or `installDeps`.',
92
92
  default: 'false',
93
93
  },
94
94
  {
95
95
  name: 'options.list',
96
96
  type: 'boolean',
97
97
  description: 'Return the available codemods instead of running any.',
98
+ default: 'false',
98
99
  },
99
100
  {
100
101
  name: 'ctx.cwd',
101
102
  type: 'string',
102
103
  description: 'Directory to run the upgrade in.',
104
+ default: 'process.cwd()',
103
105
  },
104
106
  ],
105
107
  returns: [
@@ -116,36 +118,36 @@ export const doc = {
116
118
  {
117
119
  type: 'upgrade.status',
118
120
  description:
119
- 'A short-circuit outcome (no codemods executed): `up_to_date` (`from` is at/after the installed target and no `force`), `no_codemods` (none apply to the range), or `config_fixable` (dry-run preview that a pending config codemod would repair an invalid astryx.config). Each carries the agent-docs summary and, when found, the copied-composition registry summary.',
121
+ 'A short-circuit outcome (no codemods executed): `up_to_date` (`from` is at/after the installed target and no `force`), `no_codemods` (none apply to the range), or `config_fixable` (dry-run preview that a pending config codemod would repair an invalid astryx.config). up_to_date and no_codemods carry the agent-docs summary and, when receipts are found, the copied-composition summary; config_fixable carries configError, configCodemods, suggestedCommand, message, note, and the agent-docs summary.',
120
122
  },
121
123
  {
122
124
  type: 'upgrade.run',
123
125
  description:
124
- 'The terminal run receipt: from/to versions, codemod count, integrations processed, agent-docs and registry summaries, modifiedFiles, protectedFiles, declinedCandidates, and completion state. A protected required change returns complete: false with ERR_CODEMOD_PROTECTED; the CLI exits nonzero while preserving the structured receipt.',
126
+ 'The terminal run receipt: from, to, codemods (count), integrations, agentDocs, agentDocsRefreshed, registryCompositions (when receipts are found), filesChanged, transformsApplied, modifiedFiles, protectedFiles, declinedCandidates, errors, and complete. When a protected file still requires a change, complete is false and errorCode is ERR_CODEMOD_PROTECTED; the CLI exits nonzero while preserving the structured receipt.',
125
127
  },
126
128
  ],
127
129
  throws: [
128
130
  {
129
131
  code: 'ERR_INVALID_ARGUMENT',
130
- when: '`from` is missing (and neither `list` nor `registry` is set), or the project config fails strict validation and no pending config codemod can repair it',
132
+ when: '`from` is missing (and neither `list` nor `registry` is set); `list` and `registry` are both set; `registry` is combined with `from`, `force`, `codemod`, `skipCodemod`, `integration` or `installDeps`; an `integration` specifier is invalid or not installed; or astryx.config fails to load or validate and no pending config codemod repairs it',
131
133
  },
132
134
  {code: 'ERR_INVALID_VERSION', when: '`from` is not a valid semver string'},
133
135
  {code: 'ERR_PATH_TRAVERSAL', when: '`path` resolves outside cwd'},
134
136
  {
135
137
  code: 'ERR_VERSION_DETECT',
136
- when: 'the installed @astryxdesign/core version cannot be detected',
138
+ when: 'neither @astryxdesign/core nor legacy @xds/core is installed in cwd; with `registry`, only when copied-composition receipts exist and @astryxdesign/core is not installed',
137
139
  },
138
140
  {
139
141
  code: 'ERR_DEP_MISSING',
140
- when: 'jscodeshift is required but could not be installed',
142
+ when: 'jscodeshift is missing and `installDeps` is not set, or installing it failed',
141
143
  },
142
144
  {
143
145
  code: 'ERR_UNKNOWN_CODEMOD',
144
- when: 'a `codemod` name matches no registered codemod',
146
+ when: 'the version range has codemods but none remain selected: `codemod` names no codemod in the range, or `skipCodemod` excludes all of them',
145
147
  },
146
148
  {
147
149
  code: 'ERR_CODEMOD_FAILED',
148
- when: 'one or more codemods failed, or a post-codemod hook failed',
150
+ when: 'one or more codemods failed, or a post-codemod hook failed, and no protected file still needs a change (otherwise an upgrade.run receipt with complete: false is returned)',
149
151
  },
150
152
  {
151
153
  code: 'ERR_CODEMOD_PROTECTION_SOURCE',
@@ -21,13 +21,15 @@ Someone building a product with Astryx. Their questions:
21
21
  ## Tells that you are writing for us instead
22
22
 
23
23
  - second person aimed at the wrong reader — "reviewers should…", "before promoting a component…", "attach evidence for…"
24
- - **rubric, readiness, gate, audit, checklist, sign-off, promotion, evidence** as things the reader must produce
24
+ - an internal rubric, readiness gate, audit checklist, or sign-off that the reader must satisfy for Astryx maintainers
25
25
  - a table of things to verify rather than things to use
26
26
  - anything about lab → core, which is our lifecycle, not theirs
27
27
  - Storybook, Playwright, CI or the Simulator named as tools the reader runs
28
28
 
29
29
  One subtlety: a statement about the **system's behavior** is caller-facing even when it sounds like process. "A component's theme targets are stable once published" tells a caller what they can rely on; "reviewers must check that theme targets are stable" is ours. Same fact, different reader — **rewrite it rather than move it**.
30
30
 
31
+ A public authoring-quality rubric is also caller-facing when it helps someone evaluate an artifact they create through Astryx. It must be complete and actionable from public inputs. It must not include Astryx's internal approval, promotion, evidence-publication, or CI process. A current system spec must assign the shipped guide as the rubric's owner.
32
+
31
33
  ## Where the rest goes
32
34
 
33
35
  The material is usually good; the finding is placement, not quality. It goes in the [wiki](https://github.com/facebook/astryx/wiki) — **as a section on the page that already covers it, not a new page.** The wiki is at nearly 60 pages, several of them overlapping, because every stray section got its own.
@@ -55,5 +57,5 @@ Worked example: a responsive-and-interaction readiness rubric is grading criteri
55
57
  one section by its key. A section's key is its `id`, or a key derived from its
56
58
  title when it has none. Give a section an `id` when its title may change, since
57
59
  readers and extensions link to the key. Two sections in one topic cannot share
58
- a key. Keep each section small enough to read on its own: `astryx doctor` fails
60
+ a key. Keep each section small enough to read on its own: `astryx doctor` warns on
59
61
  any section over 32 KB.