Skip to content

fix(spec): put the fix before the explanation in the three hand-written unrecognized_keys error maps (#6416) - #6453

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-6416-handwritten-errmap-reorder
Aug 7, 2026
Merged

fix(spec): put the fix before the explanation in the three hand-written unrecognized_keys error maps (#6416)#6453
os-project-manager merged 1 commit into
mainfrom
claude/issue-6416-handwritten-errmap-reorder

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#6416

Direction 1 only, per the 2026-08-07T17:54Z triage ruling: three in-place reorders applying the shape PR #6375 landed for the shared template to the three sites that ruling's scope missed. Direction 2 (folding them into guidance with a set-keyed form) is not touched — it stays a separate card. The two clean sibling maps named in the issue (ai/tool.zod.tsstrictToolError, object.zod.tsstrictCapabilitiesError) are untouched.

Premise re-anchored on fresh origin/main

Read at 9bc846bff (the issue's anchors were read at bbd2d8d3d, before #6279 / #6423 / #6364 landed). All three hand-written maps are still present and still carry the mid-message sentence; the premise holds in full.

sitesymbolanchor at bbd2d8d3danchor at 9bc846bff
packages/spec/src/shared/visibility.tsstrictVisibilityError94-11294-111 (unchanged text)
packages/spec/src/ui/dashboard.zod.tsstrictWidgetAnalyticsError145-190145-190 (unchanged text)
packages/spec/src/data/object.zod.tsstrictTenancyError428-438425-438 (unchanged text; #6423 edited a different stanza)

What changed

Every message now emits front matter → every fix channel → the explanatory sentence last, matching strictUnknownKeyError's emission order exactly:

  • strictVisibilityError — the visibleWhen alias pointer moves ahead of the "Before ADR-0089 D3a these were dropped silently…" sentence.
  • strictWidgetAnalyticsError — all three prescription branches (the ADR-0021 dataset migration, the objectui component/data quarantine, the drillDown 是 objectui 读的活能力,却不是 spec 声明的任何键 —— #3752 的迁移处方曾指向它 #5022 drill near-key answer) move ahead of the "Undeclared top-level keys were dropped silently…" sentence.
  • strictTenancyError — the per-key tombstone bullets move ahead of the two-modes explainer, which is now appended to the last bullet with a single space. This is byte-for-byte the shape strictUnknownKeyError uses for history after its own bullets.

Nothing is deleted and nothing becomes conditional. The acceptance surface does not move: these are $ZodErrorMaps shaping the text of an already-failing parse, and visibility.ts's alias tables are untouched.

Byte parity: measured, not asserted

The ruling asked for ideally byte-identical lengths. All seven message variants are identical in both length and character multiset — a pure permutation, no joiner space gained or lost anywhere:

OK visibility alias branch: old=386 new=386 delta=0 multiset-identical=true
OK visibility no-fix branch: old=189 new=189 delta=0 multiset-identical=true
OK dashboard legacy branch: old=467 new=467 delta=0 multiset-identical=true
OK dashboard quarantine branch:old=353 new=353 delta=0 multiset-identical=true
OK dashboard drill branch: old=817 new=817 delta=0 multiset-identical=true
OK dashboard no-fix branch: old=215 new=215 delta=0 multiset-identical=true
OK tenancy multi-key: old=648 new=648 delta=0 multiset-identical=true

Pins

13 new order pins, placed next to each surface's existing test conventions:

  • packages/spec/src/ui/view.test.ts — next to ADR-0089 D3a — strict view form schemas, exercised through the real FormFieldSchema / FormSectionSchema doors.
  • packages/spec/src/ui/dashboard.test.ts — after DashboardWidgetSchema (dataset-bound), one order pin per prescription branch.
  • packages/spec/src/data/object.test.ts — after TenancyConfigSchema — #2763 strategy/crossTenantAccess removal.

Each asserts all three facts the ruling names: front matter first (startsWith), every fix channel strictly before the explanatory sentence (indexOf < indexOf), and the sentence still present verbatim at the end (endsWith), plus an "emitted exactly once whatever the key count" pin and a full-message toBe for the branch with no fix to offer.

Reverse verification

Direction predicted before running: red — restoring the old concatenation must fail exactly the new order pins, while every pre-existing assertion on these three surfaces stays green, because the reorder deletes nothing and those are all toContain fragment pins.

Method: the three source files were reverted to origin/main with git checkout -- while the new tests stayed in place; the patch was re-applied afterwards.

$ git checkout -- packages/spec/src/shared/visibility.ts \
packages/spec/src/ui/dashboard.zod.ts \
packages/spec/src/data/object.zod.ts
$ npx vitest run --maxWorkers=2 src/ui/view.test.ts src/ui/dashboard.test.ts \
src/data/object.test.ts src/ui/page.test.ts src/ui/chart.test.ts
× src/ui/dashboard.test.ts > strictWidgetAnalyticsError message order … > legacy inline-analytics branch: the ADR-0021 dataset prescription comes first
× src/ui/dashboard.test.ts > strictWidgetAnalyticsError message order … > quarantine branch: the objectui-internal verdict comes first
× src/ui/dashboard.test.ts > strictWidgetAnalyticsError message order … > drill branch (#5022): the "AUTOMATIC" answer comes first
× src/ui/dashboard.test.ts > strictWidgetAnalyticsError message order … > keeps the whole drill answer ahead of the history, not just its opening
× src/ui/dashboard.test.ts > strictWidgetAnalyticsError message order … > emits the history exactly once, whatever the key count
× src/data/object.test.ts > strictTenancyError message order … > names the wrong key first, then the tombstone bullet, then the explainer
× src/data/object.test.ts > strictTenancyError message order … > keeps EVERY per-key bullet ahead of the explainer, not just the first
× src/data/object.test.ts > strictTenancyError message order … > is a full-message pin for the plain unknown-key case
× src/ui/view.test.ts > strictVisibilityError message order … > names the wrong key first, then the alias pointer, then the history
× src/ui/view.test.ts > strictVisibilityError message order … > emits the history exactly once, whatever the key count
Test Files 3 failed | 2 passed (5)
Tests 10 failed | 545 passed (555)

Prediction confirmed. The representative failure shows the defect precisely:

AssertionError: expected 'Unrecognized key(s) on this view/page…' to contain '`visibleWhenn`. If this is the condit…'
Received: "Unrecognized key(s) on this view/page schema: `visibleWhenn`. Before ADR-0089 D3a
these were dropped silently, shipping inert metadata; a mis-layered or stale key is now
a loud parse error. If this is the conditional-visibility predicate, the canonical key is
`visibleWhen` (ADR-0089) — …"

Two honest notes on the transcript, rather than a tidier number:

  1. 545 pre-existing tests stayed green under the old order — including every toContain on these three messages. That is the point of the exercise: fragment pins cannot carry an ordering fact, which is exactly why the issue asked for order pins.
  2. 3 of the 13 new pins are green in BOTH directions, by design, and I did not "fix" them to go red. Two are full-message toBe pins on the no-fix branch of strictVisibilityError / strictWidgetAnalyticsError: that branch emits front matter + sentence and has no fix channel, so there is genuinely nothing to reorder and the message is byte-identical before and after. Their job is the complementary one — proving the reorder did not perturb the branch it should not touch. The third (still emits the whole alias table after the reorder) is a deliberate presence check guarding against a reorder that quietly drops one of the three alias spellings, which the order assertions alone would not catch. Reported rather than manufactured into a red.

After re-applying the patch: Test Files 5 passed (5) / Tests 555 passed (555).

Verification

Gate list enumerated from .github/workflows/lint.yml (both jobs), run one by one — no from-memory list:

  • ESLint job (30 steps)pnpm lint, then check:slot-lookup, query-options-erasure, nul-bytes, doc-authoring, docs-audit-scope, role-word, quick-reference-counts, adr-anchors, org-identifier, authz-resolver, service-providers, route-envelope, error-code-casing, wildcard-fallthrough, meta-type-normalized, init-service-contract, durability-log-level, startup-registry-verdict, objectui-changeset, release-notes, release-body, node-version, workflow-status-functions, shard-attestation, published-files, engine-double-contract, resume-authority-declared, merge-driver, spec-parsed-aliasall PASS.
  • TypeScript Type Check jobcheck:type-check-coverage, check:driver-conformance, check:stall-guard, tsc --noEmit (spec), check:generated --reconcile-only, check:skill-frame-sync, check:skill-compatibility, workspace turbo build (66/66), workspace turbo typecheck (120/120), check:type-check-debt, examples typecheck, downstream-contract typecheck, check:api-surface, check:exported-any, check:dual-source-exports, check:skill-examplesall PASS.
  • Generated artifactspnpm --filter @objectstack/spec check:generated after a real build: all 10 up to date, nothing regenerated. This confirms the dispatch's mechanism assumption: these strings are not .describe() inputs, so content/docs/references/** sees zero regen, and authorable-surface.base.json is byte-identical (never hand-edited).
  • Tests@objectstack/spec 339 files / 8652 tests pass; @objectstack/lint 62 / 1541 pass; @objectstack/cli 91 / 928 pass (the two message consumers named in strictObject 的 history 句夹在「哪个键错了」与「该写什么」之间,在单行 error 显示位上把修法推到 222 字符之后(#5762 实测) #5955os validate's renderer and validateFlowTriggerReadiness).
  • Byte disciplinenode scripts/check-nul-bytes.mjs OK over 6082 files, plus a widened self-scan (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]') over every changed file: clean.

content/docs/releases/ untouched. File-disjoint from in-lane PR #6447.


🤖 Generated with Claude Code

https://claude.ai/code/session_018ffcE95NaMJcL9XJ9VDYgk


Generated by Claude Code

`strictVisibilityError` (shared/visibility.ts)、`strictWidgetAnalyticsError`
(ui/dashboard.zod.ts)、`strictTenancyError` (data/object.zod.ts) 都是独立的
`$ZodErrorMap`,既不经过 `strictUnknownKeyError`(所以 #5955 的修法够不到),
也不在 #5593 迁移的 44 个直接调用点里。三者都复现了 #5955 针对的形状:一句
不可操作的说明句夹在「哪个键错了」与修复它的处方之间 —— 在多个消费方的单行
渲染上(`os validate` 的 `• where: message`、CI 日志、会主动抹平换行的
`validateFlowTriggerReadiness`),处方被推出作者真正会读的那一段。
现在统一为 PR #6375 为共享模板落地的顺序:前置语(哪个键错了)→ 全部修复
通道(`visibleWhen` 别名指针;ADR-0021 dataset / objectui 隔离 / #5022 drill
三个分支;`tenancy` 的逐键 tombstone bullets)→ 说明句放最后。没有删除任何
文本,也没有让任何句子变成有条件的:七个消息变体在长度与字符多重集上与原拼接
逐字节相同。接受面不动 —— 这些 map 只塑造一个已经失败的 parse 的文本,
`visibility.ts` 的别名表原样未改。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ffcE95NaMJcL9XJ9VDYgk
@vercel

vercelBot commented Aug 7, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 7, 2026 9:44pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

112 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/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/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/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)

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.

@os-project-manager
os-project-manager marked this pull request as ready for review August 7, 2026 22:02
@os-project-manager
os-project-manager added this pull request to the merge queueAug 7, 2026
Merged via the queue into main with commit bf0ae99Aug 7, 2026
26 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-6416-handwritten-errmap-reorder branch August 7, 2026 22:18
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:dataprotocol:uisize/mteststooling

Projects

None yet

2 participants

@os-project-manager@claude