Skip to content

feat(schema): close prose-and-shape drift gate; regenerate from Rust#380

Merged
BartWaardenburg merged 7 commits into
mainfrom
feat/issue-338-phase8-prose
May 17, 2026
Merged

feat(schema): close prose-and-shape drift gate; regenerate from Rust#380
BartWaardenburg merged 7 commits into
mainfrom
feat/issue-338-phase8-prose

Conversation

@BartWaardenburg
Copy link
Copy Markdown
Collaborator

Summary

Phase 8 of issue #338. The strict structural drift gate is no longer #[ignore]d: every type in derived_definition_names() is regenerated from Rust, with descriptions sourced from /// doc comments and per-envelope titles from #[schemars(title = "...")]. Editing the committed schema by hand on any in-scope definition now fails the default cargo test invocation.

Base branch is feat/issue-338-phase5-envelopes (stacked PR). Phase 5 (10 commits deriving envelope structs from Rust) is the prerequisite; this PR's diff is only the 1 commit closing the gate on top. Merge Phase 5 first, then rebase this onto main.

What landed

  • Strict gate flipped on at crates/cli/src/bin/schema_emit.rs::drift_tests::committed_definitions_match_derived_structurally.
  • Augmentation fix: augment_finding_definition now pushes "actions" into the per-finding required array, matching the wire (which always emits the array, possibly empty).
  • Production normalize_schema mirrors the test-side strip: drops schemars cosmetic output (default, examples, format, minimum/maximum/exclusive*) and collapses single-arm allOf ref wrappers so the regenerated document stays clean.
  • Each envelope in crates/cli/src/output_envelope.rs carries #[schemars(title = "fallow <command> --format json")] so the Mintlify docs renderer keeps its section headings.
  • HealthActionsMeta is a typed struct under crates/cli/src/health_types/ flowing through HealthReport.actions_meta and HealthGroup.actions_meta. The previous actions_meta AUGMENTATION_KEYS escape hatch is retired; the runtime still injects the breadcrumb post-pass on the serde_json::Value tree because the suppression context lives inside the report builder, but the schema now documents the field.
  • Latent invalid CoverageSetupOutput.package_manager shape in the committed schema (type: "string", enum: [null, ...], rejected by ajv strict) is replaced by the proper anyOf: [{$ref}, {type: null}] form via the regen.
  • docs/output-schema.json regenerated from Rust as the source of truth.
  • CONTRIBUTING.md updated to reflect the closed escape hatch.
  • VS Code + npm typed contract files (output-contract.d.ts) regenerated via pnpm run codegen:types.

What stays hand-maintained

  • Top-level metadata ($schema, title, description, oneOf), preserved verbatim by merge_with_committed.
  • Two non-strict drift tests (committed_property_refs_match_derived_property_refs covering $ref-target drift, plus committed_definitions_match_derived_property_keys covering property-key + required drift). They complement the strict gate rather than duplicating it.

Wire compatibility

SCHEMA_VERSION stays at 6. The wire shape is unchanged: actions_meta already appeared on the wire via post-pass injection, actions already appeared as an array on every finding, and the four flagged Option<T> fields (ReviewReconcileOutput.target, CoverageSetupOutput.package_manager, .dockerfile_snippet, .config_written) already emitted as null rather than absent. Phase 8 only changes the schema document's encoding (inlined enums become $refs to registered definitions, descriptions flow from ///); JSON instances validate identically against both forms.

Test plan

  • cargo test --workspace --all-targets (all 7 schema drift tests including the previously-#[ignore]d strict gate)
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo fmt --all -- --check
  • pnpm run check:codegen (TypeScript contracts in lock-step with regenerated schema)
  • bash action/tests/run.sh (200 jq tests)
  • bash ci/tests/run.sh (205 jq tests)

Refs #338.

BartWaardenburg added a commit that referenced this pull request May 16, 2026
…/ SvelteKit

Two issues caught by parallel review on PR #380:

1. BLOCK: CoverageSetupFramework variants NestJs / SvelteKit landed as
   `nest_js` / `svelte_kit` in the regenerated schema (and downstream TS
   contracts). The runtime emits `nestjs` / `sveltekit` (per
   crates/cli/src/coverage/mod.rs:106-108 and crates/cli/src/explain.rs:958),
   so the schema rejected every real NestJS / SvelteKit setup output under
   ajv strict validation. The Rust enum had `rename_all = "snake_case"`
   without explicit per-variant renames for NestJs / SvelteKit; NextJs had
   a per-variant rename that worked correctly, masking the gap.

   Root cause was twofold:
   - `CoverageSetupFramework` (and its sibling helper enums) are NOT in
     `derived_definition_names()`, so `merge_with_committed` treats them
     as transitively-referenced helpers handled by the second loop.
   - That second loop carried a `if definitions.contains_key(name) { continue; }`
     guard that silently preserved the committed schema's stale helper
     definitions on every regen. Any change to a serde rename / schemars
     attribute on a helper was invisible until the helper was deleted by
     hand from `docs/output-schema.json`.

   Fix: invert the guard so derived helpers always overwrite the committed
   entry, with the `derived_definition_names()` in-scope set skipped because
   those are processed by the first loop with augmentation. After Phase 8
   every helper in `docs/output-schema.json` is a derived artifact, so the
   "preserve hand-authored helpers" rationale no longer applies. Added
   `#[serde(rename = "nestjs")]` to `NestJs` and `#[serde(rename = "sveltekit")]`
   to `SvelteKit` to match the wire and the established `NextJs` pattern.

2. LOW: stale comments in `crates/cli/src/bin/schema_emit.rs`. The
   `finding_definition_names()` docstring still said "requiredness is
   decided by the committed schema and the augmentation is non-opinionated",
   but the augmentation now unconditionally pushes `actions` into required.
   The `committed_definitions_match_derived_property_keys` test still
   described `actions_meta` as injected via post-pass on
   `serde_json::Value` rather than modeled on the typed envelope, but
   Phase 8 typed it as `Option<HealthActionsMeta>` on `HealthReport`.
   Both rewritten to reflect current behaviour.

Verified:
- Strict drift gate still passes (7 schema tests).
- `cargo test --workspace --all-targets` green.
- `cargo clippy --workspace --all-targets -- -D warnings` clean.
- `pnpm run check:codegen` clean (TS contracts regenerated).
- `CoverageSetupFramework` definition now emits `nextjs` / `nestjs` /
  `nuxt` / `sveltekit` / `astro` / `remix` / `vite` / `plain_node` /
  `unknown`, matching the runtime and `_meta.enums`.
BartWaardenburg added a commit that referenced this pull request May 16, 2026
…/ SvelteKit

Two issues caught by parallel review on PR #380:

1. BLOCK: CoverageSetupFramework variants NestJs / SvelteKit landed as
   `nest_js` / `svelte_kit` in the regenerated schema (and downstream TS
   contracts). The runtime emits `nestjs` / `sveltekit` (per
   crates/cli/src/coverage/mod.rs:106-108 and crates/cli/src/explain.rs:958),
   so the schema rejected every real NestJS / SvelteKit setup output under
   ajv strict validation. The Rust enum had `rename_all = "snake_case"`
   without explicit per-variant renames for NestJs / SvelteKit; NextJs had
   a per-variant rename that worked correctly, masking the gap.

   Root cause was twofold:
   - `CoverageSetupFramework` (and its sibling helper enums) are NOT in
     `derived_definition_names()`, so `merge_with_committed` treats them
     as transitively-referenced helpers handled by the second loop.
   - That second loop carried a `if definitions.contains_key(name) { continue; }`
     guard that silently preserved the committed schema's stale helper
     definitions on every regen. Any change to a serde rename / schemars
     attribute on a helper was invisible until the helper was deleted by
     hand from `docs/output-schema.json`.

   Fix: invert the guard so derived helpers always overwrite the committed
   entry, with the `derived_definition_names()` in-scope set skipped because
   those are processed by the first loop with augmentation. After Phase 8
   every helper in `docs/output-schema.json` is a derived artifact, so the
   "preserve hand-authored helpers" rationale no longer applies. Added
   `#[serde(rename = "nestjs")]` to `NestJs` and `#[serde(rename = "sveltekit")]`
   to `SvelteKit` to match the wire and the established `NextJs` pattern.

2. LOW: stale comments in `crates/cli/src/bin/schema_emit.rs`. The
   `finding_definition_names()` docstring still said "requiredness is
   decided by the committed schema and the augmentation is non-opinionated",
   but the augmentation now unconditionally pushes `actions` into required.
   The `committed_definitions_match_derived_property_keys` test still
   described `actions_meta` as injected via post-pass on
   `serde_json::Value` rather than modeled on the typed envelope, but
   Phase 8 typed it as `Option<HealthActionsMeta>` on `HealthReport`.
   Both rewritten to reflect current behaviour.

Verified:
- Strict drift gate still passes (7 schema tests).
- `cargo test --workspace --all-targets` green.
- `cargo clippy --workspace --all-targets -- -D warnings` clean.
- `pnpm run check:codegen` clean (TS contracts regenerated).
- `CoverageSetupFramework` definition now emits `nextjs` / `nestjs` /
  `nuxt` / `sveltekit` / `astro` / `remix` / `vite` / `plain_node` /
  `unknown`, matching the runtime and `_meta.enums`.
@BartWaardenburg BartWaardenburg force-pushed the feat/issue-338-phase8-prose branch from ea06c88 to a82e072 Compare May 16, 2026 17:51
BartWaardenburg added a commit that referenced this pull request May 16, 2026
…/ SvelteKit

Two issues caught by parallel review on PR #380:

1. BLOCK: CoverageSetupFramework variants NestJs / SvelteKit landed as
   `nest_js` / `svelte_kit` in the regenerated schema (and downstream TS
   contracts). The runtime emits `nestjs` / `sveltekit` (per
   crates/cli/src/coverage/mod.rs:106-108 and crates/cli/src/explain.rs:958),
   so the schema rejected every real NestJS / SvelteKit setup output under
   ajv strict validation. The Rust enum had `rename_all = "snake_case"`
   without explicit per-variant renames for NestJs / SvelteKit; NextJs had
   a per-variant rename that worked correctly, masking the gap.

   Root cause was twofold:
   - `CoverageSetupFramework` (and its sibling helper enums) are NOT in
     `derived_definition_names()`, so `merge_with_committed` treats them
     as transitively-referenced helpers handled by the second loop.
   - That second loop carried a `if definitions.contains_key(name) { continue; }`
     guard that silently preserved the committed schema's stale helper
     definitions on every regen. Any change to a serde rename / schemars
     attribute on a helper was invisible until the helper was deleted by
     hand from `docs/output-schema.json`.

   Fix: invert the guard so derived helpers always overwrite the committed
   entry, with the `derived_definition_names()` in-scope set skipped because
   those are processed by the first loop with augmentation. After Phase 8
   every helper in `docs/output-schema.json` is a derived artifact, so the
   "preserve hand-authored helpers" rationale no longer applies. Added
   `#[serde(rename = "nestjs")]` to `NestJs` and `#[serde(rename = "sveltekit")]`
   to `SvelteKit` to match the wire and the established `NextJs` pattern.

2. LOW: stale comments in `crates/cli/src/bin/schema_emit.rs`. The
   `finding_definition_names()` docstring still said "requiredness is
   decided by the committed schema and the augmentation is non-opinionated",
   but the augmentation now unconditionally pushes `actions` into required.
   The `committed_definitions_match_derived_property_keys` test still
   described `actions_meta` as injected via post-pass on
   `serde_json::Value` rather than modeled on the typed envelope, but
   Phase 8 typed it as `Option<HealthActionsMeta>` on `HealthReport`.
   Both rewritten to reflect current behaviour.

Verified:
- Strict drift gate still passes (7 schema tests).
- `cargo test --workspace --all-targets` green.
- `cargo clippy --workspace --all-targets -- -D warnings` clean.
- `pnpm run check:codegen` clean (TS contracts regenerated).
- `CoverageSetupFramework` definition now emits `nextjs` / `nestjs` /
  `nuxt` / `sveltekit` / `astro` / `remix` / `vite` / `plain_node` /
  `unknown`, matching the runtime and `_meta.enums`.
@BartWaardenburg BartWaardenburg force-pushed the feat/issue-338-phase8-prose branch from a82e072 to edc8933 Compare May 16, 2026 18:00
BartWaardenburg added a commit that referenced this pull request May 16, 2026
…/ SvelteKit

Two issues caught by parallel review on PR #380:

1. BLOCK: CoverageSetupFramework variants NestJs / SvelteKit landed as
   `nest_js` / `svelte_kit` in the regenerated schema (and downstream TS
   contracts). The runtime emits `nestjs` / `sveltekit` (per
   crates/cli/src/coverage/mod.rs:106-108 and crates/cli/src/explain.rs:958),
   so the schema rejected every real NestJS / SvelteKit setup output under
   ajv strict validation. The Rust enum had `rename_all = "snake_case"`
   without explicit per-variant renames for NestJs / SvelteKit; NextJs had
   a per-variant rename that worked correctly, masking the gap.

   Root cause was twofold:
   - `CoverageSetupFramework` (and its sibling helper enums) are NOT in
     `derived_definition_names()`, so `merge_with_committed` treats them
     as transitively-referenced helpers handled by the second loop.
   - That second loop carried a `if definitions.contains_key(name) { continue; }`
     guard that silently preserved the committed schema's stale helper
     definitions on every regen. Any change to a serde rename / schemars
     attribute on a helper was invisible until the helper was deleted by
     hand from `docs/output-schema.json`.

   Fix: invert the guard so derived helpers always overwrite the committed
   entry, with the `derived_definition_names()` in-scope set skipped because
   those are processed by the first loop with augmentation. After Phase 8
   every helper in `docs/output-schema.json` is a derived artifact, so the
   "preserve hand-authored helpers" rationale no longer applies. Added
   `#[serde(rename = "nestjs")]` to `NestJs` and `#[serde(rename = "sveltekit")]`
   to `SvelteKit` to match the wire and the established `NextJs` pattern.

2. LOW: stale comments in `crates/cli/src/bin/schema_emit.rs`. The
   `finding_definition_names()` docstring still said "requiredness is
   decided by the committed schema and the augmentation is non-opinionated",
   but the augmentation now unconditionally pushes `actions` into required.
   The `committed_definitions_match_derived_property_keys` test still
   described `actions_meta` as injected via post-pass on
   `serde_json::Value` rather than modeled on the typed envelope, but
   Phase 8 typed it as `Option<HealthActionsMeta>` on `HealthReport`.
   Both rewritten to reflect current behaviour.

Verified:
- Strict drift gate still passes (7 schema tests).
- `cargo test --workspace --all-targets` green.
- `cargo clippy --workspace --all-targets -- -D warnings` clean.
- `pnpm run check:codegen` clean (TS contracts regenerated).
- `CoverageSetupFramework` definition now emits `nextjs` / `nestjs` /
  `nuxt` / `sveltekit` / `astro` / `remix` / `vite` / `plain_node` /
  `unknown`, matching the runtime and `_meta.enums`.
@BartWaardenburg BartWaardenburg force-pushed the feat/issue-338-phase8-prose branch from edc8933 to 8648df6 Compare May 16, 2026 20:18
Base automatically changed from feat/issue-338-phase5-envelopes to main May 16, 2026 21:14
BartWaardenburg added a commit that referenced this pull request May 16, 2026
…/ SvelteKit

Two issues caught by parallel review on PR #380:

1. BLOCK: CoverageSetupFramework variants NestJs / SvelteKit landed as
   `nest_js` / `svelte_kit` in the regenerated schema (and downstream TS
   contracts). The runtime emits `nestjs` / `sveltekit` (per
   crates/cli/src/coverage/mod.rs:106-108 and crates/cli/src/explain.rs:958),
   so the schema rejected every real NestJS / SvelteKit setup output under
   ajv strict validation. The Rust enum had `rename_all = "snake_case"`
   without explicit per-variant renames for NestJs / SvelteKit; NextJs had
   a per-variant rename that worked correctly, masking the gap.

   Root cause was twofold:
   - `CoverageSetupFramework` (and its sibling helper enums) are NOT in
     `derived_definition_names()`, so `merge_with_committed` treats them
     as transitively-referenced helpers handled by the second loop.
   - That second loop carried a `if definitions.contains_key(name) { continue; }`
     guard that silently preserved the committed schema's stale helper
     definitions on every regen. Any change to a serde rename / schemars
     attribute on a helper was invisible until the helper was deleted by
     hand from `docs/output-schema.json`.

   Fix: invert the guard so derived helpers always overwrite the committed
   entry, with the `derived_definition_names()` in-scope set skipped because
   those are processed by the first loop with augmentation. After Phase 8
   every helper in `docs/output-schema.json` is a derived artifact, so the
   "preserve hand-authored helpers" rationale no longer applies. Added
   `#[serde(rename = "nestjs")]` to `NestJs` and `#[serde(rename = "sveltekit")]`
   to `SvelteKit` to match the wire and the established `NextJs` pattern.

2. LOW: stale comments in `crates/cli/src/bin/schema_emit.rs`. The
   `finding_definition_names()` docstring still said "requiredness is
   decided by the committed schema and the augmentation is non-opinionated",
   but the augmentation now unconditionally pushes `actions` into required.
   The `committed_definitions_match_derived_property_keys` test still
   described `actions_meta` as injected via post-pass on
   `serde_json::Value` rather than modeled on the typed envelope, but
   Phase 8 typed it as `Option<HealthActionsMeta>` on `HealthReport`.
   Both rewritten to reflect current behaviour.

Verified:
- Strict drift gate still passes (7 schema tests).
- `cargo test --workspace --all-targets` green.
- `cargo clippy --workspace --all-targets -- -D warnings` clean.
- `pnpm run check:codegen` clean (TS contracts regenerated).
- `CoverageSetupFramework` definition now emits `nextjs` / `nestjs` /
  `nuxt` / `sveltekit` / `astro` / `remix` / `vite` / `plain_node` /
  `unknown`, matching the runtime and `_meta.enums`.
@BartWaardenburg BartWaardenburg force-pushed the feat/issue-338-phase8-prose branch from 8648df6 to 766addf Compare May 16, 2026 21:15
Phase 8 of issue #338. The strict structural drift gate is no longer
`#[ignore]`d: every type in `derived_definition_names()` is regenerated
from Rust, with descriptions sourced from `///` doc comments and
per-envelope titles from `#[schemars(title = "...")]`. Editing the
committed schema by hand on any in-scope definition now fails the
default `cargo test` invocation.

What landed:
- Strict gate flipped on at
  `crates/cli/src/bin/schema_emit.rs::drift_tests::committed_definitions_match_derived_structurally`.
- Augmentation fix: `augment_finding_definition` now pushes "actions"
  into the per-finding `required` array, matching the wire (which
  always emits the array, possibly empty).
- Production `normalize_schema` mirrors the test-side strip: drops
  schemars cosmetic output (`default`, `examples`, `format`,
  `minimum`/`maximum`/`exclusive*`) and collapses single-arm `allOf`
  ref wrappers so the regenerated document stays clean.
- Each envelope in `crates/cli/src/output_envelope.rs` carries
  `#[schemars(title = "fallow <command> --format json")]` so the
  Mintlify docs renderer keeps its section headings.
- `HealthActionsMeta` is a typed struct under `crates/cli/src/health_types/`
  flowing through `HealthReport.actions_meta` and
  `HealthGroup.actions_meta`. The previous "actions_meta"
  AUGMENTATION_KEYS escape hatch is retired; the runtime still injects
  the breadcrumb post-pass on the `serde_json::Value` tree because the
  suppression context lives inside the report builder, but the schema
  now documents the field.
- The latent invalid `CoverageSetupOutput.package_manager` shape in the
  committed schema (`type: "string", enum: [null, ...]`, rejected by
  ajv strict) is replaced by the proper `anyOf: [{$ref}, {type: null}]`
  form via the regen.
- `docs/output-schema.json` is regenerated from Rust as the source of
  truth.
- `CONTRIBUTING.md` updated to reflect the closed escape hatch.
- VS Code + npm typed contract files (`output-contract.d.ts`)
  regenerated via `pnpm run codegen:types`.

What stays hand-maintained:
- Top-level metadata (`$schema`, `title`, `description`, `oneOf`),
  preserved verbatim by `merge_with_committed`.
- Two non-strict drift tests
  (`committed_property_refs_match_derived_property_refs` covering
  $ref-target drift, plus `committed_definitions_match_derived_property_keys`
  covering property-key + required drift). They complement the strict
  gate rather than duplicating it.
…/ SvelteKit

Two issues caught by parallel review on PR #380:

1. BLOCK: CoverageSetupFramework variants NestJs / SvelteKit landed as
   `nest_js` / `svelte_kit` in the regenerated schema (and downstream TS
   contracts). The runtime emits `nestjs` / `sveltekit` (per
   crates/cli/src/coverage/mod.rs:106-108 and crates/cli/src/explain.rs:958),
   so the schema rejected every real NestJS / SvelteKit setup output under
   ajv strict validation. The Rust enum had `rename_all = "snake_case"`
   without explicit per-variant renames for NestJs / SvelteKit; NextJs had
   a per-variant rename that worked correctly, masking the gap.

   Root cause was twofold:
   - `CoverageSetupFramework` (and its sibling helper enums) are NOT in
     `derived_definition_names()`, so `merge_with_committed` treats them
     as transitively-referenced helpers handled by the second loop.
   - That second loop carried a `if definitions.contains_key(name) { continue; }`
     guard that silently preserved the committed schema's stale helper
     definitions on every regen. Any change to a serde rename / schemars
     attribute on a helper was invisible until the helper was deleted by
     hand from `docs/output-schema.json`.

   Fix: invert the guard so derived helpers always overwrite the committed
   entry, with the `derived_definition_names()` in-scope set skipped because
   those are processed by the first loop with augmentation. After Phase 8
   every helper in `docs/output-schema.json` is a derived artifact, so the
   "preserve hand-authored helpers" rationale no longer applies. Added
   `#[serde(rename = "nestjs")]` to `NestJs` and `#[serde(rename = "sveltekit")]`
   to `SvelteKit` to match the wire and the established `NextJs` pattern.

2. LOW: stale comments in `crates/cli/src/bin/schema_emit.rs`. The
   `finding_definition_names()` docstring still said "requiredness is
   decided by the committed schema and the augmentation is non-opinionated",
   but the augmentation now unconditionally pushes `actions` into required.
   The `committed_definitions_match_derived_property_keys` test still
   described `actions_meta` as injected via post-pass on
   `serde_json::Value` rather than modeled on the typed envelope, but
   Phase 8 typed it as `Option<HealthActionsMeta>` on `HealthReport`.
   Both rewritten to reflect current behaviour.

Verified:
- Strict drift gate still passes (7 schema tests).
- `cargo test --workspace --all-targets` green.
- `cargo clippy --workspace --all-targets -- -D warnings` clean.
- `pnpm run check:codegen` clean (TS contracts regenerated).
- `CoverageSetupFramework` definition now emits `nextjs` / `nestjs` /
  `nuxt` / `sveltekit` / `astro` / `remix` / `vite` / `plain_node` /
  `unknown`, matching the runtime and `_meta.enums`.
The Phase 8 merge-logic fix (commit c9c4df0) made `merge_with_committed`
always overwrite transitive helpers from the derived schema, which fixed
the NestJs / SvelteKit rename gap but also revealed that two helper
fields had richer prose in the previously-committed schema than in
their Rust `///` doc comments.

Affected:
- `DuplicationStats.clone_groups`: lost "Matches `clone_groups[].length`
  post `minOccurrences` filtering; the count of groups hidden by the
  filter is exposed in `clone_groups_below_min_occurrences`."
- `DuplicationStats.clone_instances`: lost "Matches the sum of
  `clone_groups[].locations[].length` post `minOccurrences` filtering."

Both prose blocks describe the relationship between the count fields
and the `minOccurrences` filter (the same one that gates the new
`clone_groups_below_min_occurrences` field). Without them, JSON
consumers reading the schema lose the context that the count fields
reflect POST-filter totals, not raw corpus totals.

Restored by enriching the `///` doc comments in
`crates/core/src/duplicates/types.rs` to match the prior committed
schema text. The regen now emits the full prose; tests + clippy + fmt
clean.

The Phase 8 regen surfaced ~130 other prose regressions on in-scope
types (HealthScore.grade lost the score-banding, HealthFinding.severity
lost the threshold defaults, etc.). They are pre-existing in
`ed07ef6c` (the Phase 8 commit) and tracked as a follow-up.
The original Phase 8 regen (commit `ed07ef6c`) replaced the
previously-published schema's hand-authored descriptions with the
matching Rust `///` doc comments, which were systematically terser. The
loss surfaced once `merge_with_committed` started overwriting transitive
helpers (commit `c9c4df08`); 141 field and top-level descriptions across
42 definitions read materially less than the version on `origin/main`:

- `HealthScore.grade`: lost the score banding (`A: score >= 85, B:
  70-84, C: 55-69, D: 40-54, F: below 40`)
- `HealthScore.score`: lost the reproducibility note (`100 -
  sum(penalties) == score`)
- `HealthFinding.severity`: lost the threshold defaults (`cognitive
  25/40, cyclomatic 30/50`)
- `HealthFindingAction.type`: lost the multi-action / action-selection
  prose explaining when each action variant fires
- `AuditOutput.attribution`: lost the `audit.gate` interaction note
- `CheckOutput.unused_catalog_entries`: lost the catalog-spec prose
- `RuntimeCoverageReport`: lost descriptions on 10 / 13 fields
- `HealthScorePenalties`: lost descriptions on 10 / 11 fields
- `RuntimeCoverageSummary`: lost descriptions on 10 / 12 fields
- ... plus 130+ more terser fields

Restored by enriching the `///` doc comments in the source structs to
match the previously-published prose verbatim, then regenerating
`docs/output-schema.json` plus the npm + VS Code typed contracts. The
augmented `schema_version` description on `RuntimeCoverageReport` (which
lives in `schema_emit.rs::augment_runtime_coverage_report` rather than
on a typed field) was also re-extended.

Files touched:
- `crates/types/src/envelope.rs`, `crates/types/src/output_health.rs`,
  `crates/types/src/results.rs`
- `crates/core/src/duplicates/types.rs`
- `crates/cli/src/health_types/{mod,scores,coverage,trends,vital_signs,
  targets,runtime_coverage,grouped}.rs`
- `crates/cli/src/output_envelope.rs`
- `crates/cli/src/bin/schema_emit.rs` (augmented schema_version
  description text)
- `docs/output-schema.json`, `editors/vscode/src/generated/
  output-contract.d.ts`, `npm/fallow/types/output-contract.d.ts`
  (regenerated)

After this commit, the OLD `origin/main` schema and the regenerated
schema agree on prose: zero field descriptions differ by more than 30
characters, and the structural alignment from commits `ed07ef6c`,
`c9c4df08`, `f9ccdca4` stays in place.

Verified:
- `cargo test --workspace --all-targets` green.
- `cargo clippy --workspace --all-targets -- -D warnings` clean.
- `cargo fmt --all -- --check` clean.
- `pnpm run check:codegen` clean.
- Programmatic comparison of every field description vs `origin/main`
  shows zero remaining > 30-char regressions.
Phase 8 of issue #338 closed the prose-and-shape escape hatch, flipped
the strict structural drift gate out of `#[ignore]`, fixed the
augmentation to add `actions` to `required`, modeled `actions_meta` as a
typed `Option<HealthActionsMeta>`, taught the production normalizer to
strip schemars cosmetic output, preserved envelope titles via
`#[schemars(title)]`, fixed the merge-logic gap that silently froze
transitive helpers across regens (which surfaced a `nest_js` /
`svelte_kit` rename mismatch on `CoverageSetupFramework`), corrected the
latent `package_manager` invalid-enum shape, and restored 141 prose
descriptions across 42 definitions that the initial regen had reduced
to terser `///` text.

Adds a second bullet under `[Unreleased] / Internal` covering Phase 8's
work as a follow-up to the existing Phase 5 envelope-derive entry. Both
bullets reference #338. The first bullet describes what Phase 5 shipped
(the migration path); the second describes what Phase 8 shipped (closing
the gate + the cleanup it forced).

Refs #338.
The Phase 8 stack rebased onto Phase 5's tip (which absorbed v2.75.0
from main) needs one more schema + TS contract regeneration so the
committed artifacts reflect the merged Rust source.

Refs #338.
@BartWaardenburg BartWaardenburg force-pushed the feat/issue-338-phase8-prose branch from 766addf to 2f6666e Compare May 16, 2026 21:35
The prose-restoration commit pulled `<name>`, `<hash>`, `<anonymous>`,
`<template>` literally into `///` doc comments, which rustdoc strict
mode parses as unclosed HTML tags. The Documentation CI job runs with
`RUSTDOCFLAGS=-D warnings`, so the warnings became errors.

Wrap each placeholder in backticks so the markdown renderer treats the
text as inline code instead of an HTML opening tag. The schema-emit
binary regenerates descriptions verbatim from the `///` text, so the
JSON-schema output and TypeScript contracts regenerate automatically.

Five sites:
- `crates/types/src/results.rs:93` — `<name>` in
  `UnresolvedCatalogReference.catalog:<name>` doc
- `crates/cli/src/health_types/runtime_coverage.rs:319` — `<hash>` in
  `RuntimeCoverageFinding.id` doc
- `crates/cli/src/health_types/scores.rs:208-209` — `<anonymous>` and
  `<template>` in `HealthFinding.name` doc
- `crates/cli/src/health_types/scores.rs:402` — `<anonymous>` in
  `LargeFunctionEntry.name` doc

Refs #338.
@BartWaardenburg BartWaardenburg merged commit 0ac1a80 into main May 17, 2026
20 checks passed
@BartWaardenburg BartWaardenburg deleted the feat/issue-338-phase8-prose branch May 17, 2026 06:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant