Skip to content

Commit 36fe926

Browse files
committed
test(spec): app graduated to strict mid-review (#4165) — the coverage gate did its job
Merge origin/main and record the first live graduation: #4165 made the app shell strict while this PR was in CI. The derivation adapted on its own — app fell out of the lintable set because its parse is now loud — and the pinned expectation list is what turned that shrink into a red test instead of a silent one. That is the exact scenario the pin exists for: a human confirms the shrink is a graduation, not a regression, and moves the name from the covered list to the strict list. Changeset and docs updated to stop hardcoding the count and the strict roster — both now describe the rule (the strict bucket grows as tiers graduate) rather than a snapshot of it. 7133 tests green on the merged base; all six gates PASS; the three example apps stay clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXGj3Z5TmwSV6RK2oGc3cb
1 parent d797118 commit 36fe926

3 files changed

Lines changed: 22 additions & 13 deletions

File tree

.changeset/authoring-key-lint-full-coverage.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ feat(spec): the unknown-authoring-key lint covers every metadata collection, not
66

77
#4148 introduced the lint for `object` and `field` — the two surfaces #4120
88
caught real drift on. Those two were a sample, not the population: of the
9-
authorable metadata types, only four are `.strict()` (`flow` / `permission` /
10-
`position` / `tool`, the #4001 Tier-A set). Every other type strips an
9+
authorable metadata types, only a handful are `.strict()` (`flow` / `permission`
10+
/ `position` / `tool` from #4001 Tier-A, joined mid-review by `app` via #4165).
11+
Every other type strips an
1112
undeclared key exactly the way `field` did — an author who misspells a key on a
1213
`page`, an `agent` or a `dashboard` got the same parse-clean-value-gone silence,
1314
with no lint watching.
1415

15-
`lintUnknownAuthoringKeys` now walks **every metadata collection**17 of them
16-
today: object, app, page, dashboard, report, dataset, action, job, agent, skill,
17-
hook, mapping, datasource, view, email_template, doc, book — and its coverage is
16+
`lintUnknownAuthoringKeys` now walks **every metadata collection**16 today:
17+
object, page, dashboard, report, dataset, action, job, agent, skill, hook,
18+
mapping, datasource, view, email_template, doc, book — and its coverage is
1819
**derived, not listed**: which collections exist comes from `PLURAL_TO_SINGULAR`
1920
(the same boundary map the normalizer uses), which schema judges each comes from
2021
the canonical type→Zod registry, and whether linting is even meaningful is read
@@ -27,7 +28,9 @@ The posture rules keep the lint from ever disagreeing with the parse:
2728
- **strip** (zod default) → lint: the parse drops unknown keys silently, and
2829
that silence is what gets reported.
2930
- **strict** → skip: the parse already rejects loudly with the schema's own
30-
tombstone guidance; a second, possibly disagreeing voice helps nobody.
31+
tombstone guidance; a second, possibly disagreeing voice helps nobody. This
32+
bucket GROWS as #4001 tiers graduate schemas — `app` graduated (#4165) while
33+
this change was in review, and the derivation adapted without an edit.
3134
- **passthrough** → skip: unknown keys survive the parse, nothing is dropped.
3235
- **unions** (`view`) → the union of member keys; lintable only when a member
3336
strips and none passes unknowns through.

content/docs/deployment/validating-metadata.mdx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,11 @@ another package defines.
211211

212212
### 9. Authoring keys the schema never declared
213213

214-
Most metadata schemas are deliberately not strict — of the authorable types,
215-
only `flow`, `permission`, `position` and `tool` reject unknown keys. On every
216-
other type a key the schema does not declare **parses clean and is dropped** on
217-
the way to storage. Nothing fails; the setting simply is not there.
214+
Most metadata schemas are deliberately not strict — only the types the
215+
ADR-0049 tier programme has hardened (`flow`, `permission`, `position`, `tool`,
216+
`app`, …) reject unknown keys. On every other type a key the schema does not
217+
declare **parses clean and is dropped** on the way to storage. Nothing fails;
218+
the setting simply is not there.
218219

219220
```ts
220221
fields: {

packages/spec/src/kernel/metadata-authoring-lint.test.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,18 @@ describe('coverage derivation (#3786 — no third hand-written list)', () => {
3131
// `view` matters doubly: it is a UNION (container | ViewItem | overlay), so
3232
// its presence pins the union half of the posture logic — a regression that
3333
// silently dropped unions would shrink coverage without failing the count.
34-
for (const expected of ['object', 'page', 'app', 'agent', 'dashboard', 'action', 'report', 'hook', 'view']) {
34+
for (const expected of ['object', 'page', 'agent', 'dashboard', 'action', 'report', 'hook', 'view']) {
3535
expect(lintableTypes, `expected '${expected}' to be lint-covered`).toContain(expected);
3636
}
3737
});
3838

39-
it('excludes the strict Tier-A types — the parse is already loud there', () => {
40-
for (const strict of ['flow', 'permission', 'position', 'tool']) {
39+
it('excludes the strict types — the parse is already loud there', () => {
40+
// This list GROWS as the #4001 tier programme hardens schemas, and each
41+
// graduation shrinks the lint's coverage by design — the parse takes over.
42+
// `app` graduated mid-flight (#4165) while this very test was in review:
43+
// the derivation adapted on its own, and the pinned expectation above is
44+
// what forced a human to confirm the shrink was a graduation, not a bug.
45+
for (const strict of ['flow', 'permission', 'position', 'tool', 'app']) {
4146
expect(lintableTypes, `'${strict}' is .strict(); the lint must not double-report`).not.toContain(strict);
4247
}
4348
});

0 commit comments

Comments
 (0)