From 491d4eeddb2b58381d5661424a3b5926fd175d1b Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 00:55:01 -0700 Subject: [PATCH 01/17] docs(spec): documented component props agree with the source (AST-039 FR12) --- docs/specs/AST-039/spec.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index a38717061bda8..491a00cf07885 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -117,6 +117,15 @@ change. MUST NOT change a file its siblings share; only the integration manifest and `package.json` may change, to declare a new root. Review MUST reject a change that adds a central catalog, and writer tests MUST fail on one. +- **FR12 — Documented component props agree with the source.** + `astryx doctor integration components` MUST compare every prop table a + component descriptor documents with the props of the export it describes, + read with the TypeScript the project installs. It MUST warn about a + documented prop the code lacks, a prop the package's own source declares + that the table omits, a `required` flag the code contradicts, and a + documented type that does not match the code under the loose comparison the + command documents. Without the project's TypeScript it MUST say that the + comparison was skipped and why, and MUST NOT report a pass. ### Compatibility transition @@ -153,14 +162,15 @@ metadata stays outside Core runtime authoring, consistent with ## Verification -| Contract | Verification | Representative states | Mutation or failure expectation | -| -------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -| FR1–FR4 | authoring parser, discovery, and writer tests | valid pair; missing/duplicate source; bad identity; malformed/missing doc | a source without its typed same-stem descriptor becomes visible, or duplicated metadata can win | -| FR5–FR7 | contribution inventory, pack-check, materialization, and real-world integration tests | nested files; symlink/escape; missing import; local vs packed; duplicate package slug | a nested file is omitted, an escaped file is copied, or two surfaces disagree on ownership | -| FR6 | static source-graph and runtime-export tests | direct export; re-export; type-only export; source side effect | discovery executes source or accepts no runtime binding | -| FR8–FR9 | integration-add snapshots and compatibility fixtures | new writer output; released template/codemod/agent guidance input | a writer emits an untyped or non-`.doc.mjs` item, or a compatibility reader becomes the new canonical form | -| FR10 | bundled-theme, integration-theme, and provider-to-consumer tests | all bundled themes; multi-theme package; list/add/build; no JSON catalog | removing a theme doc still passes, or restoring `manifest.json` becomes necessary | -| FR11 | integration-add writer tests; catalog codemod tests | second item of a kind; leftover 0.6 catalog | adding an item writes a shared file, or a leftover catalog is read instead of refused | +| Contract | Verification | Representative states | Mutation or failure expectation | +| -------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| FR1–FR4 | authoring parser, discovery, and writer tests | valid pair; missing/duplicate source; bad identity; malformed/missing doc | a source without its typed same-stem descriptor becomes visible, or duplicated metadata can win | +| FR5–FR7 | contribution inventory, pack-check, materialization, and real-world integration tests | nested files; symlink/escape; missing import; local vs packed; duplicate package slug | a nested file is omitted, an escaped file is copied, or two surfaces disagree on ownership | +| FR6 | static source-graph and runtime-export tests | direct export; re-export; type-only export; source side effect | discovery executes source or accepts no runtime binding | +| FR8–FR9 | integration-add snapshots and compatibility fixtures | new writer output; released template/codemod/agent guidance input | a writer emits an untyped or non-`.doc.mjs` item, or a compatibility reader becomes the new canonical form | +| FR10 | bundled-theme, integration-theme, and provider-to-consumer tests | all bundled themes; multi-theme package; list/add/build; no JSON catalog | removing a theme doc still passes, or restoring `manifest.json` becomes necessary | +| FR11 | integration-add writer tests; catalog codemod tests | second item of a kind; leftover 0.6 catalog | adding an item writes a shared file, or a leftover catalog is read instead of refused | +| FR12 | prop-drift unit tests; `doctor integration components` tests; a repository test over every shipped component descriptor | each finding kind; a type written differently but equal; props declared in another package; no TypeScript installed | a removed or renamed prop or a changed union passes unreported, or a missing compiler reads as a pass | ## Decision log From f57e7fbcd888e233e205ddc2c5fde90c7244218b Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 00:55:02 -0700 Subject: [PATCH 02/17] docs(spec): integration components are found by the names their docs document (AST-039 FR4) --- docs/specs/AST-039/spec.md | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index 491a00cf07885..0e4029ffdbe1f 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -69,10 +69,17 @@ change. - **FR4 — Pair identity is structural.** Discovery MUST derive the primary source path and any source-bound runtime export identity from the shared descriptor/source stem when that relationship is structural. The descriptor - owns the stable public item identity. Discovery MUST reject a missing source, - more than one eligible same-stem source, an invalid inferred export name, or - a descriptor whose authored identity conflicts with its required directory - identity. + owns the stable public item identity. A component descriptor's `name` is the + runtime export it documents: when the same-stem source has no runtime export + named after the stem, that `name` MAY name another runtime export of the + source, and discovery records the component under that name. Each full entry + a component descriptor lists in `components` is a component under its own + name when the module the descriptor's import names exports it. Every surface + (FR7) lists, finds, and verifies these components by those names, and a + source that exports its stem keeps the stem. Discovery MUST reject a missing + source, more than one eligible same-stem source, an invalid inferred export + name, or a descriptor whose authored identity conflicts with its required + directory identity. - **FR5 — Multi-file items have one confined ownership boundary.** A kind whose item needs nested source, token, palette, asset, or receipt files MUST define one item-local copy and pack boundary. Discovery and authoring MUST enumerate @@ -162,15 +169,16 @@ metadata stays outside Core runtime authoring, consistent with ## Verification -| Contract | Verification | Representative states | Mutation or failure expectation | -| -------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -| FR1–FR4 | authoring parser, discovery, and writer tests | valid pair; missing/duplicate source; bad identity; malformed/missing doc | a source without its typed same-stem descriptor becomes visible, or duplicated metadata can win | -| FR5–FR7 | contribution inventory, pack-check, materialization, and real-world integration tests | nested files; symlink/escape; missing import; local vs packed; duplicate package slug | a nested file is omitted, an escaped file is copied, or two surfaces disagree on ownership | -| FR6 | static source-graph and runtime-export tests | direct export; re-export; type-only export; source side effect | discovery executes source or accepts no runtime binding | -| FR8–FR9 | integration-add snapshots and compatibility fixtures | new writer output; released template/codemod/agent guidance input | a writer emits an untyped or non-`.doc.mjs` item, or a compatibility reader becomes the new canonical form | -| FR10 | bundled-theme, integration-theme, and provider-to-consumer tests | all bundled themes; multi-theme package; list/add/build; no JSON catalog | removing a theme doc still passes, or restoring `manifest.json` becomes necessary | -| FR11 | integration-add writer tests; catalog codemod tests | second item of a kind; leftover 0.6 catalog | adding an item writes a shared file, or a leftover catalog is read instead of refused | -| FR12 | prop-drift unit tests; `doctor integration components` tests; a repository test over every shipped component descriptor | each finding kind; a type written differently but equal; props declared in another package; no TypeScript installed | a removed or renamed prop or a changed union passes unreported, or a missing compiler reads as a pass | +| Contract | Verification | Representative states | Mutation or failure expectation | +| -------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| FR1–FR4 | authoring parser, discovery, and writer tests | valid pair; missing/duplicate source; bad identity; malformed/missing doc | a source without its typed same-stem descriptor becomes visible, or duplicated metadata can win | +| FR4 | discovery, lookup, search, Doctor, and pack-check tests | a doc naming another export of its file; a file exporting its stem; a family member the import exports; one it does not | one surface misses a component's name, or a doc named after its file reads differently | +| FR5–FR7 | contribution inventory, pack-check, materialization, and real-world integration tests | nested files; symlink/escape; missing import; local vs packed; duplicate package slug | a nested file is omitted, an escaped file is copied, or two surfaces disagree on ownership | +| FR6 | static source-graph and runtime-export tests | direct export; re-export; type-only export; source side effect | discovery executes source or accepts no runtime binding | +| FR8–FR9 | integration-add snapshots and compatibility fixtures | new writer output; released template/codemod/agent guidance input | a writer emits an untyped or non-`.doc.mjs` item, or a compatibility reader becomes the new canonical form | +| FR10 | bundled-theme, integration-theme, and provider-to-consumer tests | all bundled themes; multi-theme package; list/add/build; no JSON catalog | removing a theme doc still passes, or restoring `manifest.json` becomes necessary | +| FR11 | integration-add writer tests; catalog codemod tests | second item of a kind; leftover 0.6 catalog | adding an item writes a shared file, or a leftover catalog is read instead of refused | +| FR12 | prop-drift unit tests; `doctor integration components` tests; a repository test over every shipped component descriptor | each finding kind; a type written differently but equal; props declared in another package; no TypeScript installed | a removed or renamed prop or a changed union passes unreported, or a missing compiler reads as a pass | ## Decision log From e5b534f5f360d62fc527307943c3ab60af8dc81e Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 00:55:02 -0700 Subject: [PATCH 03/17] docs(spec): every integration descriptor satisfies its published authoring type (AST-039 FR13) --- docs/specs/AST-039/spec.md | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index 0e4029ffdbe1f..58a3fe84c82b3 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -133,6 +133,17 @@ change. documented type that does not match the code under the loose comparison the command documents. Without the project's TypeScript it MUST say that the comparison was skipped and why, and MUST NOT report a pass. +- **FR13 — Every descriptor satisfies its published authoring type.** + `astryx doctor integration validate` MUST check each descriptor an + integration's roots hold, and each translation a descriptor carries, against + the type `@astryxdesign/cli/authoring` publishes for that kind, as the + TypeScript the project installs checks a value written with that type. It + MUST warn about every unknown field, missing required field, and value + outside its field's type, naming the field and the change that fixes it, + including a field the compiler reaches only once an earlier unknown field is + gone. A value the loader accepts and the CLI reads MUST satisfy the published + type. Without the project's TypeScript it MUST say that the descriptors were + not type-checked and why, and MUST NOT report a pass. ### Compatibility transition @@ -169,16 +180,17 @@ metadata stays outside Core runtime authoring, consistent with ## Verification -| Contract | Verification | Representative states | Mutation or failure expectation | -| -------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -| FR1–FR4 | authoring parser, discovery, and writer tests | valid pair; missing/duplicate source; bad identity; malformed/missing doc | a source without its typed same-stem descriptor becomes visible, or duplicated metadata can win | -| FR4 | discovery, lookup, search, Doctor, and pack-check tests | a doc naming another export of its file; a file exporting its stem; a family member the import exports; one it does not | one surface misses a component's name, or a doc named after its file reads differently | -| FR5–FR7 | contribution inventory, pack-check, materialization, and real-world integration tests | nested files; symlink/escape; missing import; local vs packed; duplicate package slug | a nested file is omitted, an escaped file is copied, or two surfaces disagree on ownership | -| FR6 | static source-graph and runtime-export tests | direct export; re-export; type-only export; source side effect | discovery executes source or accepts no runtime binding | -| FR8–FR9 | integration-add snapshots and compatibility fixtures | new writer output; released template/codemod/agent guidance input | a writer emits an untyped or non-`.doc.mjs` item, or a compatibility reader becomes the new canonical form | -| FR10 | bundled-theme, integration-theme, and provider-to-consumer tests | all bundled themes; multi-theme package; list/add/build; no JSON catalog | removing a theme doc still passes, or restoring `manifest.json` becomes necessary | -| FR11 | integration-add writer tests; catalog codemod tests | second item of a kind; leftover 0.6 catalog | adding an item writes a shared file, or a leftover catalog is read instead of refused | -| FR12 | prop-drift unit tests; `doctor integration components` tests; a repository test over every shipped component descriptor | each finding kind; a type written differently but equal; props declared in another package; no TypeScript installed | a removed or renamed prop or a changed union passes unreported, or a missing compiler reads as a pass | +| Contract | Verification | Representative states | Mutation or failure expectation | +| -------- | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| FR1–FR4 | authoring parser, discovery, and writer tests | valid pair; missing/duplicate source; bad identity; malformed/missing doc | a source without its typed same-stem descriptor becomes visible, or duplicated metadata can win | +| FR4 | discovery, lookup, search, Doctor, and pack-check tests | a doc naming another export of its file; a file exporting its stem; a family member the import exports; one it does not | one surface misses a component's name, or a doc named after its file reads differently | +| FR5–FR7 | contribution inventory, pack-check, materialization, and real-world integration tests | nested files; symlink/escape; missing import; local vs packed; duplicate package slug | a nested file is omitted, an escaped file is copied, or two surfaces disagree on ownership | +| FR6 | static source-graph and runtime-export tests | direct export; re-export; type-only export; source side effect | discovery executes source or accepts no runtime binding | +| FR8–FR9 | integration-add snapshots and compatibility fixtures | new writer output; released template/codemod/agent guidance input | a writer emits an untyped or non-`.doc.mjs` item, or a compatibility reader becomes the new canonical form | +| FR10 | bundled-theme, integration-theme, and provider-to-consumer tests | all bundled themes; multi-theme package; list/add/build; no JSON catalog | removing a theme doc still passes, or restoring `manifest.json` becomes necessary | +| FR11 | integration-add writer tests; catalog codemod tests | second item of a kind; leftover 0.6 catalog | adding an item writes a shared file, or a leftover catalog is read instead of refused | +| FR12 | prop-drift unit tests; `doctor integration components` tests; a repository test over every shipped component descriptor | each finding kind; a type written differently but equal; props declared in another package; no TypeScript installed | a removed or renamed prop or a changed union passes unreported, or a missing compiler reads as a pass | +| FR13 | doc type unit tests; `doctor integration validate` tests; a repository test over every shipped descriptor and translation | each finding kind; a field hidden behind an unknown field; a descriptor in another kind's root; an annotation naming another type; no TypeScript installed | an unknown, missing, or mistyped field passes unreported, or a missing compiler reads as a pass | ## Decision log From 285583e2100869b88164f095053a72a747f0d94d Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 00:55:02 -0700 Subject: [PATCH 04/17] docs(spec): one command checks an integration and can gate its build (AST-039 FR14) --- docs/specs/AST-039/spec.md | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index 58a3fe84c82b3..1151589d28d09 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -144,6 +144,21 @@ change. gone. A value the loader accepts and the CLI reads MUST satisfy the published type. Without the project's TypeScript it MUST say that the descriptors were not type-checked and why, and MUST NOT report a pass. +- **FR14 — One command checks an integration, and can gate its build.** The + `astryx integration check` command MUST run the `validate`, `components`, + `templates`, and `docs` checks against one package, local or installed, report + each check's own result and a summary, and exit 1 exactly when one of those + checks would exit 1 on its own. With `--strict` it MUST also exit 1 on any + warning and on any check, or comparison within a check, that did not run, such + as a package with no integration manifest or a comparison that needs the + project's TypeScript, reporting each as an error from the same findings the + default run reports (`architecture:cli-surface` INV32). An item a comparison + lists as unchecked, such as a prop type it cannot read, does not fail + `--strict`. Its exit codes and JSON shape are a compatibility contract for the + builds that run it: a check, or a finding that fails it under `--strict`, MAY + join it only in a minor release that names the addition, and a patch release + MUST NOT make a passing package fail. Each `doctor integration` subcommand, + exit code, and JSON MUST stay unchanged. ### Compatibility transition @@ -180,17 +195,18 @@ metadata stays outside Core runtime authoring, consistent with ## Verification -| Contract | Verification | Representative states | Mutation or failure expectation | -| -------- | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -| FR1–FR4 | authoring parser, discovery, and writer tests | valid pair; missing/duplicate source; bad identity; malformed/missing doc | a source without its typed same-stem descriptor becomes visible, or duplicated metadata can win | -| FR4 | discovery, lookup, search, Doctor, and pack-check tests | a doc naming another export of its file; a file exporting its stem; a family member the import exports; one it does not | one surface misses a component's name, or a doc named after its file reads differently | -| FR5–FR7 | contribution inventory, pack-check, materialization, and real-world integration tests | nested files; symlink/escape; missing import; local vs packed; duplicate package slug | a nested file is omitted, an escaped file is copied, or two surfaces disagree on ownership | -| FR6 | static source-graph and runtime-export tests | direct export; re-export; type-only export; source side effect | discovery executes source or accepts no runtime binding | -| FR8–FR9 | integration-add snapshots and compatibility fixtures | new writer output; released template/codemod/agent guidance input | a writer emits an untyped or non-`.doc.mjs` item, or a compatibility reader becomes the new canonical form | -| FR10 | bundled-theme, integration-theme, and provider-to-consumer tests | all bundled themes; multi-theme package; list/add/build; no JSON catalog | removing a theme doc still passes, or restoring `manifest.json` becomes necessary | -| FR11 | integration-add writer tests; catalog codemod tests | second item of a kind; leftover 0.6 catalog | adding an item writes a shared file, or a leftover catalog is read instead of refused | -| FR12 | prop-drift unit tests; `doctor integration components` tests; a repository test over every shipped component descriptor | each finding kind; a type written differently but equal; props declared in another package; no TypeScript installed | a removed or renamed prop or a changed union passes unreported, or a missing compiler reads as a pass | -| FR13 | doc type unit tests; `doctor integration validate` tests; a repository test over every shipped descriptor and translation | each finding kind; a field hidden behind an unknown field; a descriptor in another kind's root; an annotation naming another type; no TypeScript installed | an unknown, missing, or mistyped field passes unreported, or a missing compiler reads as a pass | +| Contract | Verification | Representative states | Mutation or failure expectation | +| -------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| FR1–FR4 | authoring parser, discovery, and writer tests | valid pair; missing/duplicate source; bad identity; malformed/missing doc | a source without its typed same-stem descriptor becomes visible, or duplicated metadata can win | +| FR4 | discovery, lookup, search, Doctor, and pack-check tests | a doc naming another export of its file; a file exporting its stem; a family member the import exports; one it does not | one surface misses a component's name, or a doc named after its file reads differently | +| FR5–FR7 | contribution inventory, pack-check, materialization, and real-world integration tests | nested files; symlink/escape; missing import; local vs packed; duplicate package slug | a nested file is omitted, an escaped file is copied, or two surfaces disagree on ownership | +| FR6 | static source-graph and runtime-export tests | direct export; re-export; type-only export; source side effect | discovery executes source or accepts no runtime binding | +| FR8–FR9 | integration-add snapshots and compatibility fixtures | new writer output; released template/codemod/agent guidance input | a writer emits an untyped or non-`.doc.mjs` item, or a compatibility reader becomes the new canonical form | +| FR10 | bundled-theme, integration-theme, and provider-to-consumer tests | all bundled themes; multi-theme package; list/add/build; no JSON catalog | removing a theme doc still passes, or restoring `manifest.json` becomes necessary | +| FR11 | integration-add writer tests; catalog codemod tests | second item of a kind; leftover 0.6 catalog | adding an item writes a shared file, or a leftover catalog is read instead of refused | +| FR12 | prop-drift unit tests; `doctor integration components` tests; a repository test over every shipped component descriptor | each finding kind; a type written differently but equal; props declared in another package; no TypeScript installed | a removed or renamed prop or a changed union passes unreported, or a missing compiler reads as a pass | +| FR13 | doc type unit tests; `doctor integration validate` tests; a repository test over every shipped descriptor and translation | each finding kind; a field hidden behind an unknown field; a descriptor in another kind's root; an annotation naming another type; no TypeScript installed | an unknown, missing, or mistyped field passes unreported, or a missing compiler reads as a pass | +| FR14 | `integration check` text and JSON tests, with and without `--strict`; a test that lists every finding that fails it; a search test | a clean package; a warning only; an error in one check; no manifest; a skipped comparison; a search for ci | the default exit differs from a check's own, a warning or a skipped comparison passes `--strict`, text and JSON exit differently, a new failing finding is added without the list changing, or a search for ci does not find the command first | ## Decision log From fcfca0ad79785e404012dbd744a92d4132df9e52 Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 00:55:02 -0700 Subject: [PATCH 05/17] docs(cli-surface): warnings fail a command only under --strict (INV32, DEC-4) --- docs/architecture/cli-surface.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/architecture/cli-surface.md b/docs/architecture/cli-surface.md index 72a69e734201e..77846baa069cf 100644 --- a/docs/architecture/cli-surface.md +++ b/docs/architecture/cli-surface.md @@ -121,6 +121,18 @@ and remains available through explicit Core package selection. - **INV8 — Exit codes do not depend on the output mode.** The same condition exits the same way with and without `--json`, so a command works as a gate without parsing stdout. +- **INV32 — Warnings fail a command only under `--strict`.** Without + `--strict`, a command exits 1 for an error, never for a finding it reports + as a warning, so a warning cannot turn a passing build red. A condition a + command's own mode exists to detect, such as stale outputs under + `theme build --check`, is an error, not a warning. A command that can gate a + build on its warnings offers `--strict`, which changes only how the same + findings end the run: it reads exactly the data the default run reports and + adds no check, reports each warning, and each check that did not run, as an + error in the command's own issue format with the finding's own code, and + exits 1, the same way in text and JSON (INV8). A command offers `--strict` + only through a record that names it; `astryx integration check` + (`spec:AST-039` FR14) is the one that does (DEC-4). - **INV9 — A command that cannot support `--json` is rejected before any side effect**, not part-way through the work. - **INV10 — Every write is confined.** Output paths pass `assertWithin`, which @@ -336,6 +348,8 @@ updated in the same pull request when it moves an invariant: package.json mutation policy, or public subpath spelling; - changing what `integration verify` executes, resolves, or proves about the tarball; +- adding `--strict`, or any other way to fail on warnings, to a command + (INV32); - changing local, configured, or autolinked integration precedence; - changing an integration item descriptor, the theme directory boundary, or the consumer copy contract. @@ -418,6 +432,20 @@ Rejected: a patch warning with the refusal held for a minor (the warning prints after the file is replaced); and letting every current invariant outrank released documentation (any correction could then claim the patch tier). +### DEC-4 — A command fails on warnings only when its caller asks, with `--strict` + +**Decider:** `josephfarina`, `2026-10-16` + +A warning that changes an exit code turns every build that runs the command +red as soon as a release adds a check or a finding, so no command fails on a +warning by default. A package that wants its build held to its warnings asks +for it with `--strict`, which reports the same findings as errors in the +format its failures already use: a strict run never knows something the +default run does not, and reading a strict failure needs nothing new. +Rejected: a command that fails on warnings by default (a CLI upgrade turns +green builds red); a gate flag or command with another name for each case +(each one a new contract to keep). + ## Verification | Invariant | Evidence | Failure signal | @@ -442,6 +470,7 @@ released documentation (any correction could then claim the patch tier). | INV25 | `foundation/discovery/cli-self-docs.test.mjs`, `api/doctor/doctor.test.mjs` | A CLI doc with no namespace, a namespace no topic reads, or no section in its topic passes doctor. | | INV28 | `test/inv28-package-provenance.test.mjs`, `clients/cli/commands/text-json-parity.test.mjs`, `cli-api-types-verify` | A result about one artifact has no envelope `package`, a listed artifact has no `package`, or text omits the package the envelope names. | | INV29 | `api/doctor/doctor.test.mjs` (configured-integrations: not installed, no manifest, manifest throws; self-named authored package, direct and through a symlinked `node_modules` or `cwd`; provider-conflict set-aside; an astryx.config that imports but cannot load the project → failed `config` check)| A configured integration that cannot load leaves `doctor` at exit 0, or an installed-but-unnamed one fails it | +| INV32 | `test/cli-strict-guard.test.mjs` lists every command offering `--strict` and every command whose exit depends on warnings; `api/integration/integration-check.test.mjs` compares a strict run with the default run | A second command offers `--strict`, a command exits 1 on a warning alone without `--strict`, or a strict run reports a finding the default run does not or outside the command's issue format. | ## Open questions From f5a630cde76e806880f88f6172a330ddb34a26b7 Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 12:46:52 -0700 Subject: [PATCH 06/17] docs(spec): a name discovery records from a doc never takes a lookup Core answers (AST-039 FR4) --- docs/specs/AST-039/spec.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index 1151589d28d09..33bd41aea9d92 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -72,14 +72,17 @@ change. owns the stable public item identity. A component descriptor's `name` is the runtime export it documents: when the same-stem source has no runtime export named after the stem, that `name` MAY name another runtime export of the - source, and discovery records the component under that name. Each full entry - a component descriptor lists in `components` is a component under its own - name when the module the descriptor's import names exports it. Every surface - (FR7) lists, finds, and verifies these components by those names, and a - source that exports its stem keeps the stem. Discovery MUST reject a missing - source, more than one eligible same-stem source, an invalid inferred export - name, or a descriptor whose authored identity conflicts with its required - directory identity. + source, and discovery records the component under that name. Each full entry a + component descriptor lists in `components` is a component under its own name + when the module the descriptor's import names exports it. Every surface (FR7) + lists, finds, and verifies these components by those names, and a source that + exports its stem keeps the stem. A name discovery records only this way MUST + NOT take an unscoped lookup that Core answers: such a lookup returns the Core + component unless a declared replacement claims the name, a lookup scoped to + the package returns the package's component, and Doctor reports the shared + name. Discovery MUST reject a missing source, more than one eligible same-stem + source, an invalid inferred export name, or a descriptor whose authored + identity conflicts with its required directory identity. - **FR5 — Multi-file items have one confined ownership boundary.** A kind whose item needs nested source, token, palette, asset, or receipt files MUST define one item-local copy and pack boundary. Discovery and authoring MUST enumerate @@ -198,7 +201,7 @@ metadata stays outside Core runtime authoring, consistent with | Contract | Verification | Representative states | Mutation or failure expectation | | -------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | FR1–FR4 | authoring parser, discovery, and writer tests | valid pair; missing/duplicate source; bad identity; malformed/missing doc | a source without its typed same-stem descriptor becomes visible, or duplicated metadata can win | -| FR4 | discovery, lookup, search, Doctor, and pack-check tests | a doc naming another export of its file; a file exporting its stem; a family member the import exports; one it does not | one surface misses a component's name, or a doc named after its file reads differently | +| FR4 | discovery, lookup, search, Doctor, and pack-check tests | a doc naming another export of its file; a file exporting its stem; a family member the import exports; one it does not; a recorded name Core also has | one surface misses a component's name, a doc named after its file reads differently, or an unscoped lookup Core answers returns another package's component | | FR5–FR7 | contribution inventory, pack-check, materialization, and real-world integration tests | nested files; symlink/escape; missing import; local vs packed; duplicate package slug | a nested file is omitted, an escaped file is copied, or two surfaces disagree on ownership | | FR6 | static source-graph and runtime-export tests | direct export; re-export; type-only export; source side effect | discovery executes source or accepts no runtime binding | | FR8–FR9 | integration-add snapshots and compatibility fixtures | new writer output; released template/codemod/agent guidance input | a writer emits an untyped or non-`.doc.mjs` item, or a compatibility reader becomes the new canonical form | From 7efb1248f78d059767f6f0061831fbee7d37b16e Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 12:46:52 -0700 Subject: [PATCH 07/17] docs(cli-surface): integration components are found by the names their docs document (INV24) --- docs/architecture/cli-surface.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/architecture/cli-surface.md b/docs/architecture/cli-surface.md index 77846baa069cf..60ca36bec9883 100644 --- a/docs/architecture/cli-surface.md +++ b/docs/architecture/cli-surface.md @@ -280,7 +280,13 @@ and remains available through explicit Core package selection. locates roots and integration-level capabilities but never catalogs items: there is no catalog file under a root and no per-item map or list in the manifest (`spec:AST-039/FR11`). A released alternate reader is an isolated - compatibility path, not a second authoring convention. + compatibility path, not a second authoring convention. A component + descriptor's `name` is the runtime export it documents: discovery records + the component under that name when its same-stem source exports it and not + the stem, and records each full entry a descriptor lists in `components` + under its own name when the module the descriptor imports exports it + (`spec:AST-039/FR4`). A name recorded only this way never takes an unscoped + lookup Core answers: Core keeps it unless a declared replacement claims it. - **INV25 — Every CLI doc names the group that reads it.** Each command, API function, schema, and enum doc the CLI ships declares a `namespace`. The docs tree adopts `cli/commands` and `cli/api` docs, one route each under From fe9fec35acb6108925e0e9262ccfcd73991e53cb Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 13:51:52 -0700 Subject: [PATCH 08/17] docs(spec): integration check's release promise names what a patch can still raise (AST-039 FR14) --- docs/specs/AST-039/spec.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index 33bd41aea9d92..6a2a7c864ffa4 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -158,10 +158,11 @@ change. default run reports (`architecture:cli-surface` INV32). An item a comparison lists as unchecked, such as a prop type it cannot read, does not fail `--strict`. Its exit codes and JSON shape are a compatibility contract for the - builds that run it: a check, or a finding that fails it under `--strict`, MAY - join it only in a minor release that names the addition, and a patch release - MUST NOT make a passing package fail. Each `doctor integration` subcommand, - exit code, and JSON MUST stay unchanged. + builds that run it: a new check, or a new failure code, MAY join it only in a + minor release that names the addition. A name a patch release adds to Core or + the CLI (a component, template, or topic) MAY raise a conflict with a + package's own name. Each `doctor integration` subcommand, exit code, and JSON + MUST stay unchanged. ### Compatibility transition From be9ad5e220d9185768f5fb429d79daa835ddbfb8 Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 13:53:07 -0700 Subject: [PATCH 09/17] docs(spec): integration check checks the package in the current folder (AST-039 FR14) --- docs/specs/AST-039/spec.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index 6a2a7c864ffa4..d5fc862283398 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -148,21 +148,21 @@ change. type. Without the project's TypeScript it MUST say that the descriptors were not type-checked and why, and MUST NOT report a pass. - **FR14 — One command checks an integration, and can gate its build.** The - `astryx integration check` command MUST run the `validate`, `components`, - `templates`, and `docs` checks against one package, local or installed, report - each check's own result and a summary, and exit 1 exactly when one of those - checks would exit 1 on its own. With `--strict` it MUST also exit 1 on any - warning and on any check, or comparison within a check, that did not run, such - as a package with no integration manifest or a comparison that needs the - project's TypeScript, reporting each as an error from the same findings the - default run reports (`architecture:cli-surface` INV32). An item a comparison - lists as unchecked, such as a prop type it cannot read, does not fail - `--strict`. Its exit codes and JSON shape are a compatibility contract for the - builds that run it: a new check, or a new failure code, MAY join it only in a - minor release that names the addition. A name a patch release adds to Core or - the CLI (a component, template, or topic) MAY raise a conflict with a - package's own name. Each `doctor integration` subcommand, exit code, and JSON - MUST stay unchanged. + `astryx integration check` command, which takes no package argument, MUST run + the `validate`, `components`, `templates`, and `docs` checks against the + integration package in the current folder, report each check's own result and + a summary, and exit 1 exactly when one of those checks would exit 1 on its + own. With `--strict` it MUST also exit 1 on any warning and on any check, or + comparison within a check, that did not run, such as a package with no + integration manifest or a comparison that needs the project's TypeScript, + reporting each as an error from the same findings the default run reports + (`architecture:cli-surface` INV32). An item a comparison lists as unchecked, + such as a prop type it cannot read, does not fail `--strict`. Its exit codes + and JSON shape are a compatibility contract for the builds that run it: a new + check, or a new failure code, MAY join it only in a minor release that names + the addition. A name a patch release adds to Core or the CLI (a component, + template, or topic) MAY raise a conflict with a package's own name. Each + `doctor integration` subcommand, exit code, and JSON MUST stay unchanged. ### Compatibility transition From dad5fd1c002eb04ffcd7193e3b2f74aeb387c005 Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 14:10:43 -0700 Subject: [PATCH 10/17] docs(spec): a name a doc records never takes a lookup that a file-named component answers (AST-039 FR4, cli-surface INV24) --- docs/architecture/cli-surface.md | 3 ++- docs/specs/AST-039/spec.md | 13 +++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/architecture/cli-surface.md b/docs/architecture/cli-surface.md index 60ca36bec9883..3602fa8503b6a 100644 --- a/docs/architecture/cli-surface.md +++ b/docs/architecture/cli-surface.md @@ -286,7 +286,8 @@ and remains available through explicit Core package selection. the stem, and records each full entry a descriptor lists in `components` under its own name when the module the descriptor imports exports it (`spec:AST-039/FR4`). A name recorded only this way never takes an unscoped - lookup Core answers: Core keeps it unless a declared replacement claims it. + lookup that Core or a component named after its source answers: that + component keeps it unless a declared replacement claims it. - **INV25 — Every CLI doc names the group that reads it.** Each command, API function, schema, and enum doc the CLI ships declares a `namespace`. The docs tree adopts `cli/commands` and `cli/api` docs, one route each under diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index d5fc862283398..bcd11499ea4a1 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -77,12 +77,13 @@ change. when the module the descriptor's import names exports it. Every surface (FR7) lists, finds, and verifies these components by those names, and a source that exports its stem keeps the stem. A name discovery records only this way MUST - NOT take an unscoped lookup that Core answers: such a lookup returns the Core - component unless a declared replacement claims the name, a lookup scoped to - the package returns the package's component, and Doctor reports the shared - name. Discovery MUST reject a missing source, more than one eligible same-stem - source, an invalid inferred export name, or a descriptor whose authored - identity conflicts with its required directory identity. + NOT take an unscoped lookup that Core or a component named after its source + answers: such a lookup returns that component unless a declared replacement + claims the name, a lookup scoped to the package returns the package's + component, and Doctor reports a name Core shares. Discovery MUST reject a + missing source, more than one eligible same-stem source, an invalid inferred + export name, or a descriptor whose authored identity conflicts with its + required directory identity. - **FR5 — Multi-file items have one confined ownership boundary.** A kind whose item needs nested source, token, palette, asset, or receipt files MUST define one item-local copy and pack boundary. Discovery and authoring MUST enumerate From 53f6a1af7a8a090ebd436d3a28a5104543ad0cbe Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 14:49:21 -0700 Subject: [PATCH 11/17] docs(spec): an exact name beats Core's keyword fallback, and a recorded doc's stem yields too (AST-039 FR4) --- docs/specs/AST-039/spec.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index bcd11499ea4a1..221e7eb4d0324 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -76,14 +76,16 @@ change. component descriptor lists in `components` is a component under its own name when the module the descriptor's import names exports it. Every surface (FR7) lists, finds, and verifies these components by those names, and a source that - exports its stem keeps the stem. A name discovery records only this way MUST - NOT take an unscoped lookup that Core or a component named after its source - answers: such a lookup returns that component unless a declared replacement - claims the name, a lookup scoped to the package returns the package's - component, and Doctor reports a name Core shares. Discovery MUST reject a - missing source, more than one eligible same-stem source, an invalid inferred - export name, or a descriptor whose authored identity conflicts with its - required directory identity. + exports its stem keeps the stem. A name discovery records only this way, and + the stem of a descriptor recorded under another name, MUST NOT take an exact + unscoped lookup that Core or a component named after its source answers: such + a lookup returns that component unless a declared replacement claims the name, + a lookup scoped to the package returns the package's component, and Doctor + reports a recorded name Core shares. An exact name, including one discovery + records this way, takes precedence over Core's keyword fallback. Discovery + MUST reject a missing source, more than one eligible same-stem source, an + invalid inferred export name, or a descriptor whose authored identity + conflicts with its required directory identity. - **FR5 — Multi-file items have one confined ownership boundary.** A kind whose item needs nested source, token, palette, asset, or receipt files MUST define one item-local copy and pack boundary. Discovery and authoring MUST enumerate From 5b4a30793e3ff396131060b869aea1e44ae09ef0 Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 17:12:03 -0700 Subject: [PATCH 12/17] docs(cli-surface): INV32's evidence names the integration check's test where it lives --- docs/architecture/cli-surface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/cli-surface.md b/docs/architecture/cli-surface.md index 3602fa8503b6a..fc03fcbc1c648 100644 --- a/docs/architecture/cli-surface.md +++ b/docs/architecture/cli-surface.md @@ -477,7 +477,7 @@ green builds red); a gate flag or command with another name for each case | INV25 | `foundation/discovery/cli-self-docs.test.mjs`, `api/doctor/doctor.test.mjs` | A CLI doc with no namespace, a namespace no topic reads, or no section in its topic passes doctor. | | INV28 | `test/inv28-package-provenance.test.mjs`, `clients/cli/commands/text-json-parity.test.mjs`, `cli-api-types-verify` | A result about one artifact has no envelope `package`, a listed artifact has no `package`, or text omits the package the envelope names. | | INV29 | `api/doctor/doctor.test.mjs` (configured-integrations: not installed, no manifest, manifest throws; self-named authored package, direct and through a symlinked `node_modules` or `cwd`; provider-conflict set-aside; an astryx.config that imports but cannot load the project → failed `config` check)| A configured integration that cannot load leaves `doctor` at exit 0, or an installed-but-unnamed one fails it | -| INV32 | `test/cli-strict-guard.test.mjs` lists every command offering `--strict` and every command whose exit depends on warnings; `api/integration/integration-check.test.mjs` compares a strict run with the default run | A second command offers `--strict`, a command exits 1 on a warning alone without `--strict`, or a strict run reports a finding the default run does not or outside the command's issue format. | +| INV32 | `test/cli-strict-guard.test.mjs` lists every command offering `--strict` and every command whose exit depends on warnings; `api/integration/check/check.test.mjs` compares a strict run with the default run | A second command offers `--strict`, a command exits 1 on a warning alone without `--strict`, or a strict run reports a finding the default run does not or outside the command's issue format. | ## Open questions From f7b7565501f4fe8b1fdee689595faabece183915 Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 18:14:25 -0700 Subject: [PATCH 13/17] docs(spec): a legacy docs package keeps a name it documents over a recorded one (AST-039 FR4, cli-surface INV24) --- docs/architecture/cli-surface.md | 4 ++-- docs/specs/AST-039/spec.md | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/architecture/cli-surface.md b/docs/architecture/cli-surface.md index fc03fcbc1c648..afa77b9c820a6 100644 --- a/docs/architecture/cli-surface.md +++ b/docs/architecture/cli-surface.md @@ -286,8 +286,8 @@ and remains available through explicit Core package selection. the stem, and records each full entry a descriptor lists in `components` under its own name when the module the descriptor imports exports it (`spec:AST-039/FR4`). A name recorded only this way never takes an unscoped - lookup that Core or a component named after its source answers: that - component keeps it unless a declared replacement claims it. + lookup that Core, a legacy docs package, or a component named after its + source answers: that owner keeps it unless a declared replacement claims it. - **INV25 — Every CLI doc names the group that reads it.** Each command, API function, schema, and enum doc the CLI ships declares a `namespace`. The docs tree adopts `cli/commands` and `cli/api` docs, one route each under diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index 221e7eb4d0324..8bd2670fea5ec 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -78,14 +78,14 @@ change. lists, finds, and verifies these components by those names, and a source that exports its stem keeps the stem. A name discovery records only this way, and the stem of a descriptor recorded under another name, MUST NOT take an exact - unscoped lookup that Core or a component named after its source answers: such - a lookup returns that component unless a declared replacement claims the name, - a lookup scoped to the package returns the package's component, and Doctor - reports a recorded name Core shares. An exact name, including one discovery - records this way, takes precedence over Core's keyword fallback. Discovery - MUST reject a missing source, more than one eligible same-stem source, an - invalid inferred export name, or a descriptor whose authored identity - conflicts with its required directory identity. + unscoped lookup that Core, a legacy docs package, or a component named after + its source answers: such a lookup returns that component unless a declared + replacement claims the name, a lookup scoped to the package returns the + package's component, and Doctor reports a recorded name Core shares. An exact + name, including one discovery records this way, takes precedence over Core's + keyword fallback. Discovery MUST reject a missing source, more than one + eligible same-stem source, an invalid inferred export name, or a descriptor + whose authored identity conflicts with its required directory identity. - **FR5 — Multi-file items have one confined ownership boundary.** A kind whose item needs nested source, token, palette, asset, or receipt files MUST define one item-local copy and pack boundary. Discovery and authoring MUST enumerate From a94cf0b2d184ca486e30ac5578600da8fd9fba3a Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 18:52:37 -0700 Subject: [PATCH 14/17] docs(spec): a renamed descriptor's stem yields to Core and file-named components, not to legacy docs (AST-039 FR4) --- docs/specs/AST-039/spec.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index 8bd2670fea5ec..c0a7da9dcff0e 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -76,14 +76,15 @@ change. component descriptor lists in `components` is a component under its own name when the module the descriptor's import names exports it. Every surface (FR7) lists, finds, and verifies these components by those names, and a source that - exports its stem keeps the stem. A name discovery records only this way, and - the stem of a descriptor recorded under another name, MUST NOT take an exact - unscoped lookup that Core, a legacy docs package, or a component named after - its source answers: such a lookup returns that component unless a declared - replacement claims the name, a lookup scoped to the package returns the - package's component, and Doctor reports a recorded name Core shares. An exact - name, including one discovery records this way, takes precedence over Core's - keyword fallback. Discovery MUST reject a missing source, more than one + exports its stem keeps the stem. A name discovery records only this way MUST + NOT take an exact unscoped lookup that Core, a legacy docs package, or a + component named after its source answers, and the stem of a descriptor + recorded under another name yields the same way to Core and to a component + named after its source: such a lookup returns that owner's component unless a + declared replacement claims the name, a lookup scoped to the package returns + the package's component, and Doctor reports a recorded name Core shares. An + exact name, including one discovery records this way, takes precedence over + Core's keyword fallback. Discovery MUST reject a missing source, more than one eligible same-stem source, an invalid inferred export name, or a descriptor whose authored identity conflicts with its required directory identity. - **FR5 — Multi-file items have one confined ownership boundary.** A kind whose From 32c5467677a416b960922b169fccbb0ba2d8cf5a Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 19:18:34 -0700 Subject: [PATCH 15/17] docs(spec): a prop type the comparison cannot judge is listed as unchecked (AST-039 FR12) --- docs/specs/AST-039/spec.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index c0a7da9dcff0e..5cceeb05a3e7d 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -133,13 +133,14 @@ change. that adds a central catalog, and writer tests MUST fail on one. - **FR12 — Documented component props agree with the source.** `astryx doctor integration components` MUST compare every prop table a - component descriptor documents with the props of the export it describes, - read with the TypeScript the project installs. It MUST warn about a - documented prop the code lacks, a prop the package's own source declares - that the table omits, a `required` flag the code contradicts, and a - documented type that does not match the code under the loose comparison the - command documents. Without the project's TypeScript it MUST say that the - comparison was skipped and why, and MUST NOT report a pass. + component descriptor documents with the props of the export it describes, read + with the TypeScript the project installs. It MUST warn about a documented prop + the code lacks, a prop the package's own source declares that the table omits, + a `required` flag the code contradicts, and a documented type that does not + match the code under the loose comparison the command documents. It MUST list + a documented type it cannot judge as unchecked rather than report a match or a + mismatch. Without the project's TypeScript it MUST say that the comparison was + skipped and why, and MUST NOT report a pass. - **FR13 — Every descriptor satisfies its published authoring type.** `astryx doctor integration validate` MUST check each descriptor an integration's roots hold, and each translation a descriptor carries, against From eebb912930bf2d10c15b0a4cc5aaaa2a7384308c Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 23:13:13 -0700 Subject: [PATCH 16/17] docs(spec): integrations get their own authoring type where they may author more than Core's (AST-039 FR13) --- docs/specs/AST-039/spec.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index 5cceeb05a3e7d..881d76b56c0e5 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -144,14 +144,17 @@ change. - **FR13 — Every descriptor satisfies its published authoring type.** `astryx doctor integration validate` MUST check each descriptor an integration's roots hold, and each translation a descriptor carries, against - the type `@astryxdesign/cli/authoring` publishes for that kind, as the - TypeScript the project installs checks a value written with that type. It - MUST warn about every unknown field, missing required field, and value - outside its field's type, naming the field and the change that fixes it, - including a field the compiler reaches only once an earlier unknown field is - gone. A value the loader accepts and the CLI reads MUST satisfy the published - type. Without the project's TypeScript it MUST say that the descriptors were - not type-checked and why, and MUST NOT report a pass. + the type `@astryxdesign/cli/authoring` publishes for that kind in an + integration, as the TypeScript the project installs checks a value written + with that type. It MUST warn about every unknown field, missing required + field, and value outside its field's type, naming the field and the change + that fixes it, including a field the compiler reaches only once an earlier + unknown field is gone. A value the loader accepts and the CLI reads MUST + satisfy that type. Where an integration may author more than Core's type for a + kind allows, such as any template `category`, `@astryxdesign/cli/authoring` + MUST publish an additional integration type for that kind, and Core's type + MUST stay unchanged. Without the project's TypeScript it MUST say that the + descriptors were not type-checked and why, and MUST NOT report a pass. - **FR14 — One command checks an integration, and can gate its build.** The `astryx integration check` command, which takes no package argument, MUST run the `validate`, `components`, `templates`, and `docs` checks against the From db3ef54bb7eef559271ea519ef11e1f40c24fb74 Mon Sep 17 00:00:00 2001 From: Joey Farina Date: Sat, 17 Oct 2026 23:34:48 -0700 Subject: [PATCH 17/17] docs(spec): verify compares lifecycle-built family members after the pack (AST-039 FR4) --- docs/specs/AST-039/spec.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/specs/AST-039/spec.md b/docs/specs/AST-039/spec.md index 881d76b56c0e5..487efd30bed86 100644 --- a/docs/specs/AST-039/spec.md +++ b/docs/specs/AST-039/spec.md @@ -74,19 +74,22 @@ change. named after the stem, that `name` MAY name another runtime export of the source, and discovery records the component under that name. Each full entry a component descriptor lists in `components` is a component under its own name - when the module the descriptor's import names exports it. Every surface (FR7) - lists, finds, and verifies these components by those names, and a source that - exports its stem keeps the stem. A name discovery records only this way MUST - NOT take an exact unscoped lookup that Core, a legacy docs package, or a - component named after its source answers, and the stem of a descriptor - recorded under another name yields the same way to Core and to a component - named after its source: such a lookup returns that owner's component unless a - declared replacement claims the name, a lookup scoped to the package returns - the package's component, and Doctor reports a recorded name Core shares. An - exact name, including one discovery records this way, takes precedence over - Core's keyword fallback. Discovery MUST reject a missing source, more than one - eligible same-stem source, an invalid inferred export name, or a descriptor - whose authored identity conflicts with its required directory identity. + when the module the descriptor's import names exports it. When the pack + lifecycle writes the file that import names, `integration verify` compares + that descriptor's members as discovery lists them after the pack. Every + surface (FR7) lists, finds, and verifies these components by those names, and + a source that exports its stem keeps the stem. A name discovery records only + this way MUST NOT take an exact unscoped lookup that Core, a legacy docs + package, or a component named after its source answers, and the stem of a + descriptor recorded under another name yields the same way to Core and to a + component named after its source: such a lookup returns that owner's component + unless a declared replacement claims the name, a lookup scoped to the package + returns the package's component, and Doctor reports a recorded name Core + shares. An exact name, including one discovery records this way, takes + precedence over Core's keyword fallback. Discovery MUST reject a missing + source, more than one eligible same-stem source, an invalid inferred export + name, or a descriptor whose authored identity conflicts with its required + directory identity. - **FR5 — Multi-file items have one confined ownership boundary.** A kind whose item needs nested source, token, palette, asset, or receipt files MUST define one item-local copy and pack boundary. Discovery and authoring MUST enumerate