Skip to content

test(spec): the ADR-0010 envelope gate covers UNREGISTERED_KIND_SCHEMAS (#6931) - #7116

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-6931-envelope-gate-unregistered-kinds
Aug 9, 2026
Merged

test(spec): the ADR-0010 envelope gate covers UNREGISTERED_KIND_SCHEMAS (#6931)#7116
os-zhuang merged 1 commit into
mainfrom
claude/issue-6931-envelope-gate-unregistered-kinds

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes the blind spot recorded in #6931: metadata-type-schemas.test.ts holds the ADR-0010 envelope-declaration invariant, its debt list is empty and commented as the end state — and the three real parse doors bound in UNREGISTERED_KIND_SCHEMAS were never in its iteration.

Premise, verified on origin/main (f5a9bc2) before writing anything

# Premise Result
a metadata-type-schemas.test.ts holds the ADR-0010 invariant; UNDECLARED_ENVELOPE is empty and its comment calls that "the end state" Holdsconst UNDECLARED_ENVELOPE = new Set<string>([]), plus "This list is now EMPTY, and that is the end state — not a reason to delete it."
b The iteration (listMetadataTypeSchemaTypes()) deliberately excludes UNREGISTERED_KIND_SCHEMAS (webhook / connector / sharing_rule) Holds — measured: the function returns 26 names, none of the three among them; the exclusion is documented in the trailing [#6245] comment
c All three currently declare the envelope (post-#6900), so a new assertion is GREEN on arrival — the value is prospective Holds — structurally walked on origin/main: webhook, connector, sharing_rule each resolve 1 object shape and each declares _packageId

Premise alive, so the work proceeded.

What changed

A second it.each in the same file, over listUnregisteredKindSchemaTypes(), asserting envelope posture and nothing else: the schema declares the protection-fields spread, or sits on its own explicit debt-list constant (UNDECLARED_ENVELOPE_UNREGISTERED — empty, kept separate from the registered list on purpose). It carries the same reverse pin as its sibling, so the debt list cannot outlive the debt.

Walker reuse, measured rather than assumed. Both iterations live in the same file and call the same objectShapes structural walker and the same rejectedEnvelopeKeys probe — no second copy exists to drift. Only the SET each walks differs, which is the whole point of the split.

One new export, listUnregisteredKindSchemaTypes() (@objectstack/spec/kernel) — the issue's direction 2, so the check enumerates the map rather than hand-listing three names (a hand-list would not inherit a fourth entry, which is the entire prospective value). It returns names and grants nothing else.

The fence

Triage ruling on #6931, quoted verbatim:

Scope fence: the fix enrolls them in the test's iteration only (assert envelope posture or an explicit debt entry); it must NOT grant them KIND status — #2657's B/C decision stays open, exactly as the schemas file's comment intends.

Held, and checked item by item:

  • listMetadataTypeSchemaTypes() output is byte-identical. Measured before and after; both runs return the same 26 names in the same order — ["action","agent","api","app","book","capability","dashboard","dataset","datasource","doc","email_template","field","flow","hook","job","mapping","object","page","permission","position","report","seed","skill","tool","translation","view"]. The function body is untouched.
  • No KIND-obligation count changed. #4001 — reports the campaign number still asserts 25 closed / 26 total and is green — including in reverse-verification run 2, where a fake fourth unregistered entry existed and the count did not move. metadata-create-seeds.test.ts (a create seed per registered member), capability-metadata-kind.test.ts and metadata-type-api-registration.test.ts are green; MetadataTypeSchema and DEFAULT_METADATA_TYPE_REGISTRY are not in the diff at all (metadata-plugin.zod.ts untouched), and check:stack-collection-maps / check:meta-type-normalized pass.
  • Deliberately no unknown-key/posture case in the new block: sharing_rule is strict and connector is not, and choosing between them is Studio metadata coverage gaps: surface remaining types + promote un-typed concepts #2657's call, not this suite's.
  • A pinned case keeps it that waystays OUT of the registered-kind set fails if any of the three ever appears in listMetadataTypeSchemaTypes(). It is a signpost, not a veto: promotion remains available, it just cannot happen invisibly.

Reverse verification — predictions written before each run

Run 1 — remove ...MetadataProtectionFields from ConnectorSchema (connector.zod.ts:880), then restore

Prediction Measured
New case connector DECLARES the protection envelopeRED, naming 'connector' ✅ RED — 'connector' does not declare _packageId, so the envelope is dropped on every parse through PUT /meta — silently, if the schema is not strict.
New case connector does not REJECT the protection envelope → GREEN (non-strict ⇒ silent strip, no 422) ✅ GREEN
Both ORIGINAL suites stay fully GREENconnector is not in their iteration ✅ all 107 original cases green; total 1 failed | 120 passed (121), the single failure in the new block

That last row is the blind spot exhibited: the gate that exists to catch "declares no envelope" reports success while a bound parse door silently strips all seven keys.

Run 2 — add a fake fourth entry (fake_kind: z.object({ name: z.string() }), no spread), then remove

Prediction Measured
fake_kind DECLARES the protection envelopeRED ✅ RED, with the same message naming 'fake_kind'
fake_kind resolves-schema / walker-understands / does-not-REJECT → GREEN ✅ all three GREEN
Original suites GREEN, campaign count still 25 / 26 ✅ green, count unmoved
Totals → 125 cases, 1 failed 1 failed | 124 passed (125)

Both edits were reverted; git status clean of them before the commit.

Gates

Every gate .github/workflows/lint.yml runs, one by one — all green:

  • pnpm lint, pnpm --filter @objectstack/spec exec tsc --noEmit, turbo run typecheck (121/121), examples typecheck, @objectstack/downstream-contract typecheck.
  • The 45 root pnpm check:* gates. Three (check:app-nav-i18n, check:i18n, check:i18n-coverage) first reported "nothing was measured — build first"; after turbo run build (70/70) all three pass.
  • The spec-filtered gates, plus check:type-check-coverage / check:type-check-debt (33 ledger entries re-measured, none above its recorded number).
  • check:export-origins — NEW today, and the brief's expectation did not hold. A purely test-only change would not touch it; this change is not purely test-only, because the fenced fix needs the names export. So the one added export made both api-surface/kernel.json and export-origins/kernel.json stale (0 breaking (removed/narrowed), 1 added). Regenerated exactly those two via gen:api-surface + gen:export-originsone line added to each file, nothing else — and check:generated then reports all 11 artifacts up to date. Flagging this explicitly rather than letting it read as an untouched gate.
  • node scripts/check-adr-0087-registration.mjs --base origin/mainthis PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen) — nothing owed.

Changeset: included (@objectstack/spec: patch). The recent test-only analog that added a checked-in artifact plus a gate — #7090, test(spec): export-surface pins compare a build-time baseline instead of running tsc — carried exactly that, and this PR has the stronger reason: it adds a public export to a published package, so the skip-changeset "releases nothing" route does not apply.

Tests: full suite green — 134/134 turbo tasks, zero failures. @objectstack/lint was run separately (68 files / 1770 tests, all pass): two earlier full-suite passes hit 5s and 30s vitest timeouts in its corpus sweeps under parallel container load, on cases unrelated to this diff, and the package is green in isolation.

Fixes #6931


Generated by Claude Code

…AS (#6931)

`metadata-type-schemas.test.ts` holds the invariant that every metadata type
either declares `...MetadataProtectionFields` or sits on an explicit debt list,
and that debt list is empty with a comment calling it the end state — so the
gate read as total coverage. It was not. The walk iterates
`listMetadataTypeSchemaTypes()`, which deliberately excludes the three non-KIND
stack collections bound in `UNREGISTERED_KIND_SCHEMAS` (webhook / connector /
sharing_rule). Those three are real parse doors — #6245 wired them to
`PUT /api/v1/meta/:type/:name` — so the one automated check for "declares no
envelope" never ran over any of them, and all three were judged one at a time by
hand: sharing_rule surfaced as a hard 422, connector only after a silent
seven-key strip and a separate card a day later (#6362 / PR #6900), and webhook
was fine by accident of #4001 batch 11 with nothing verifying it.

A second `it.each` asserts the envelope property — and only that property — over
`listUnregisteredKindSchemaTypes()`, sharing the same `objectShapes` walker and
`rejectedEnvelopeKeys` probe as the registered walk so the two iterations cannot
drift into judging the property differently. All three pass today, so this closes
no live bug; the value is prospective, for a fourth entry.

Reverse-verified in both directions, predicted before measured. Removing the
spread from ConnectorSchema turns the new `connector DECLARES` case red while
every case of the two original suites stays green — the blind spot, exhibited.
Adding a fourth entry with no envelope turns its `DECLARES` case red while the
#4001 campaign count is unmoved.

Scope fence, per the triage ruling on #6931: this enrolls the three in the test's
iteration only. `listUnregisteredKindSchemaTypes()` returns names and grants
nothing — no MetadataTypeSchema membership, no DEFAULT_METADATA_TYPE_REGISTRY
entry, no create seed, no authorization verdict, no place in the campaign count.
`listMetadataTypeSchemaTypes()` output is byte-identical, and a pinned case keeps
it that way, so #2657's B/C decision stays open and unprejudged.

Fixes #6931

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJpiZS4AgkrDYwZ2Amh5mw
@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 9, 2026 5:23pm

Request Review

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/permissions/system-context.mdx (via packages/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

7 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding: the ADR-0010 envelope-declaration gate cannot see UNREGISTERED_KIND_SCHEMAS — the blind spot that let #6362 survive #6245

2 participants