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.ts strictToolError, object.zod.ts strictCapabilitiesError) 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.

site symbol anchor at bbd2d8d3d anchor at 9bc846bff
packages/spec/src/shared/visibility.ts strictVisibilityError 94-112 94-111 (unchanged text)
packages/spec/src/ui/dashboard.zod.ts strictWidgetAnalyticsError 145-190 145-190 (unchanged text)
packages/spec/src/data/object.zod.ts strictTenancyError 428-438 425-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

vercel Bot commented Aug 7, 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 7, 2026 9:44pm

Request Review

@github-actions github-actions Bot added the size/m label Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

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.

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

2 participants