fix(spec): move the strictObject history sentence to the end of the message (#5955) - #6375
fix(spec): move the strictObject history sentence to the end of the message (#5955)#6375hotlong wants to merge 1 commit into
Conversation
`strictUnknownKeyError` 的消息此前拼成「前置语 → history → 修法」, 每个 surface 自己声明的沿革句(「这个键以前会被静默丢弃」)正好夹在 作者真正需要的两件事之间:哪个键错了,和该写什么。 #5762 把 `flow-time-relative-descriptor-invalid` 升为 error 之后这一点 开始碍事:多个消费方把 finding 打成单行 —— `os validate` 的 `• where: message`、CI 日志,以及 `validateFlowTriggerReadiness` (它刻意把 schema 原文里的换行压平,好让 CLI 的项目符号列表保持对齐)。 `TimeRelativeTriggerSchema` 的 history 句是 224 字符,于是在 #5496 的 描述符上,`Did you mean` 落在 480 字符行的第 443 位,前面挡着一句没有 动作价值的沿革。作者(常常是 AI)只读这行的开头就动手了。 现在沿革句排到最后,两个修法通道(rename 与 guidance 处方)都排在它 前面 —— 一条处方和一次改名一样可执行,不能留在句子后面。 一处拼接点改动,消息长度逐字节不变(480/366/544/598),没有删任何文本、 没有加任何条件分支。新增顺序钉测试 5 条置于 `strict-object.test.ts`; 反向验证:把旧拼接式放回去,其中 3 条转红。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JTSZAjgtL3oR6YcpNDhW3T
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 112 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
|
Merge-main lap (the dispatch's landing-order step), run locally and reported here because the branch could not be updated — this PR had already been marked ready and added to the merge queue, and a queued branch is push-protected ( What the lap found, so nothing is lost: Merge. Pin re-sweep against the merged tree — zero additional flips. The three test files the merged commits added or changed that touch this message are all single-fragment
The only boundary-spanning strings left anywhere in the tree are the two already accounted for in the PR body — the flipped Two new gates arrived with the merge ( Re-verification on the merged tree. One thing worth recording because it read as a real red for a lap: Generated by Claude Code |
Fixes #5955
Direction A of the 2026-08-07 ruling, executed as one concatenation point in
packages/spec/src/shared/suggestions.zod.ts. Quoted verbatim:The change
strictUnknownKeyErrorassembled its message as front matter → history → fix.The
historysentence is now emitted last, after both fix channels.Nothing else changed: no deletion, no conditional (rejected option B), no
structural/API change (rejected option C), no CLI change (rejected option D).
The actual template shape (the PM's mechanism assumption, measured)
The dispatch asked whether the
\n •segments are a per-key multi-errorjoiner, in which case history would belong at the end of each segment. It is
not. Measured on
origin/main:unrecognized_keysissue per rejected object, namingevery offending key in it, so the surface's
historyappears exactly onceper message regardless of key count. That is pinned as its own test.
\n •segments are theguidanceprescriptions — tombstones andwrong-layer pointers — joined inside that single message, not per-key error
segments.
So there are two fix channels, not one, and both had to move ahead of the
sentence: a
guidanceprescription is as actionable as a rename. Puttinghistory after the renames but before the bullets would have re-created the exact
defect for guidance-hit keys — which is measured row 3 of the issue's own table
(
schedule, 779 chars). Final order:Measurement, before and after
TimeRelativeTriggerSchema(the #5762 / #5496 specimen). Its history sentencemeasures 224 characters, not 222 — the issue's count was one em-dash and a
space out; the structural claim is unchanged.
Did you meanatfield+ missingdateField+ scalaroffsetDays)offsetDay)schedule)Message lengths are byte-identical in every case — the strongest available
evidence that this is a reorder and not an edit.
Pin sweep — the number #5593 needs
Repo-wide sweep for assertions that pin the full message in order
(
Unrecognized key(s) on,Did you mean, and every history phrasing:Until #,dropped silently,previously,before ADR), acrosspackages/spec,packages/lint,packages/cli,packages/runtime,packages/rest,packages/objectql,packages/metadata-protocol,packages/services/*,packages/plugins/*:Pinned-message test sites touched: 0. Not a gap in the sweep — a measured
finding, and the reason a new pin was required:
toContain/toMatchon asingle fragment. All of them stayed green with the sentence in the middle
and stay green with it at the end.
packages/lint/src/validate-expressions.test.ts(
/Unrecognized key\(s\) on this object: \validationRules`.*Did you mean …/s`and four siblings) pin front-matter before suggestion — the relation this
change preserves — so they were already order-tolerant in the direction
that moved. Verified green, not edited.
packages/spec/src/automation/etl.test.ts:432assertstoContain('Until #4001'): presence, not position. Green.The one artifact in the repo that did depict the old order is a pending
changeset,
.changeset/action-param-strict-unknown-keys.md, whose workedexample is a full message with
Did you meanbehind the history sentence. Itis flipped here, verbatim, no text dropped — verified against the real parser
output. Its sibling
.changeset/format-zod-error-union-branches.mdwas checkedand deliberately not touched: its example is
argsonActionRefSchema,which has no alias and no guidance, so that message is unchanged by this PR.
The new pin
packages/spec/src/shared/strict-object.test.tsgains amessage order — the fix comes before the history (#5955)block, five cases:rename-before-history, prescription-before-history, both-channels-in-one-message,
history-emitted-exactly-once, and a full-message
toBefor the no-fix case(where the order is genuinely unchanged). Each order case asserts all three
required facts — front matter first, fix before history, history still present
verbatim at the end.
Reverse verification. Direction predicted before running: plain red —
this is a straightforward reorder with a canonical-first chain nowhere in play.
Restoring the old concatenation on top of the new tests:
The two that stay green are honest and intended: "emits the history exactly
once" is an order-independent property, and the no-fix full-message pin is
byte-identical under both orders because there is nothing to put in front.
Verification
Every
check:*step enumerated from.github/workflows/lint.yml, run one byone — all PASS. Two are worth naming because the dispatch predicted them:
check:authorable-surface(zero key drift, anchor untouched) andcheck:docs(nocontent/docs/references/**regen — an error message is notdescribe text).
check:nul-bytesclean, plus a manual control-byte self-scanover every touched file.
Effect on #5593
strictObjectalready delegates tostrictUnknownKeyError, so the reorderlands at the single point both wirings share, and the new pin exercises it
through
strictObject. #5593 is unaffected — neither easier nor harder.Details in the issue report; the number that matters for re-pricing is the one
above: zero pinned-message test sites needed flipping, so the "both pin the
full message text in tests" scheduling concern in the ruling turns out to be
priced at zero from this side.
Generated by Claude Code