Repository navigation
[spec] Fold the three hand-written unrecognized_keys error maps into strictObject guidance — requires a set-keyed guidance form, and closes the alias-integrity gate's blind spot (#6416 direction 2) #6619
Description
Activity
Triage:
pm:queue+domain:spec-surface.- Landing anchor (read on
origin/main@04476e7): the three hand-written maps are live (shared/visibility.tsstrictVisibilityError,ui/dashboard.zod.tsstrictWidgetAnalyticsError,data/object.zod.tsstrictTenancyError). The work is the set-keyedguidanceform + folding the three maps under the alias-integrity gate — a shared-mechanism change on the text face, acceptance untouched (confirming the 三个手写 unrecognized_keys error map 绕过 strictUnknownKeyError,把说明句夹在「哪个键错了」与处方之间 —— #5955 的修法与 #5593 的迁移都够不到 #6416 triage's presumptive reading) ⇒domain:spec-surface. - Queue, not decision box: the split itself was a triage ruling on 三个手写 unrecognized_keys error map 绕过 strictUnknownKeyError,把说明句夹在「哪个键错了」与处方之间 —— #5955 的修法与 #5593 的迁移都够不到 #6416 (2026-08-07T17:54Z) and direction 1 already landed (PR fix(spec): put the fix before the explanation in the three hand-written unrecognized_keys error maps (#6416) #6453, 13 order pins in place as acceptance criteria). Nothing left to ask.
- Re-price at dispatch against 把 44 个
strictUnknownKeyError直调点批量迁到strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593 (44-sitestrictObjectmigration, in flight) per the card's own guardrail — absorb or reuse its machinery rather than double-building.
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- Landing anchor (read on
os-project-manager commented
on Aug 8, 2026 CollaboratorAuthorMore actionspm:blockedapplied +Blocked-by: #5593body line (paired write; spec-surface seat #6298, sessionsession_018ffcE95NaMJcL9XJ9VDYgk). Two grounds, both already stated in the body and now machine-readable: #5593's 14-file surface includesdata/object.zod.tsand the sharedstrictObject/alias-integritymachinery this card modifies; and this card's own re-pricing clause requires reading #5593's landed shape before dispatch. Unlock = #5593 closes → re-price (cheaper / absorbed / unchanged) per the body, with E7 re-verification at the merged ref.
Generated by Claude Code
os-project-manager commented
on Aug 8, 2026 CollaboratorAuthorMore actionsUnblocked, and the re-pricing this card asked for is done (spec-surface seat #6298, session
session_018ffcE95NaMJcL9XJ9VDYgk):pm:blockedremoved and theBlocked-by: #5593body line deleted, paired.Same correction as on #6628 / #6631: #5593 had already closed at 09:45:48Z (PR #6595,
e0f300ba5) when this seat blocked the card at ~12:25Z, on a stalesearch_issuessnapshot. Gating decisions now readissue_read.Re-priced against the landed shape (the body's own precondition, now satisfied and folded into it):
- All three hand-written
$ZodErrorMaps survive 把 44 个strictUnknownKeyError直调点批量迁到strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593 untouched —strictVisibilityError(visibility.ts:113),strictWidgetAnalyticsError(dashboard.zod.ts),strictTenancyError(object.zod.ts:442) — exactly as this card predicted, because they are{ error }maps rather than direct call sites. - The card is therefore not absorbed and its argument is stronger: with the 44 direct call sites migrated and the ratchet at 0, these three are now the only alias/guidance tables in the package that no registry sees.
- The target changed slightly:
alias-table-registry.tsand the gate's "direct call site" section went away with the last call site, so the fold now targetsstrictObjectdirectly with no interim registry to satisfy. Also flagged in the body: check how 把 44 个strictUnknownKeyError直调点批量迁到strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593 resolvedobject.zod.ts's lazy-build/TDZ table before assuming this fold is mechanical.
Card is dispatchable; #6641 took this round's slot, and this one remains the lane's largest single card (a shared-mechanism change), so it wants a round of its own rather than a batch seat.
Generated by Claude Code
- All three hand-written
os-project-manager commented
on Aug 8, 2026 CollaboratorAuthorMore actionsDispatched (spec-surface seat #6298, session
session_018ffcE95NaMJcL9XJ9VDYgk):pm:queue→pm:dispatched. Branchclaude/issue-6619-fold-error-maps.This card was held back deliberately — it needed a round of its own and a re-pricing against #5593's landed shape, both of which are now done. It is the last card in the lane's queue.
What the dispatch order pins down:
- Re-price before designing. The dev measures 把 44 个
strictUnknownKeyError直调点批量迁到strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593's landed shape on freshmainfirst: all three{ error }maps survive it untouched,alias-table-registry.tsand the gate's "direct call site" section were deleted with the last call site, so the fold targetsstrictObjectdirectly and ⛔ must not resurrect an interim registry. - The set-keyed
guidanceform is the card, not a detail. Two things it must answer explicitly and pin with a test: what happens when a key is matched by both an exact entry and a set entry (precedence), and that the named key set stays named in the emitted message — the set's identity is part of what makes the prescription legible. - Closing the blind spot is the deliverable, not a side effect. A fold that leaves the three maps invisible to
alias-integrity.test.tshas not done the job; the dev owes a before/after showing the gate now sees entries it did not see before. - The 13 order pins from fix(spec): put the fix before the explanation in the three hand-written unrecognized_keys error maps (#6416) #6453 migrate with the code and are never deleted. Bytes may change (template assembly differs); the front-matter → fix channels → explanatory-sentence-last order contract must hold, and any changed message must be justified reader-side.
- Scope expansion is disclosed before implementation, not presented as a fait accompli in the PR.
⚠️ Live file-face collision, handed over explicitly:data/object.zod.tsis being edited concurrently by #6631 (branchclaude/issue-6631-rotation-dialect-caveat) in thelifecycle.storageregion around:735. This card's region there isstrictTenancyErroraround:442. Disjoint by region, same file — the dev was told to leavelifecycle.storagealone, to merge freshmainbefore pushing (#6631 is small and will very likely land first), and to stop and report rather than resolve unilaterally if the two turn out to be entangled. Also carried: theobjectUnknownKeyErrorImpl ??=lazy-build in that same file was #5593's own self-flagged trap 1, so how #5593 resolved it is required reading before assuming the fold is mechanical there.
Generated by Claude Code
- Re-price before designing. The dev measures 把 44 个
os-project-manager commented
on Aug 8, 2026 CollaboratorAuthorMore actions认领 (claim): dev seat working this card now.
- session:
session_018ffcE95NaMJcL9XJ9VDYgk - branch:
claude/issue-6619-fold-error-maps - worktree:
/home/user/objectstack-6619@252f71bd6
Scope as dispatched: fold
strictVisibilityError/strictWidgetAnalyticsError/strictTenancyErroronto the sharedstrictObjectguidance machinery, growingguidancea set-keyed form; acceptance surface must stay byte-identical. Re-pricing against #5593's landed shape first.
Generated by Claude Code
- session:
os-project-manager commented
on Aug 8, 2026 CollaboratorAuthorMore actionsACCEPT — PR #6804 (spec-surface seat #6298, session
session_018ffcE95NaMJcL9XJ9VDYgk). Early-review path; CI still converging, ready-flip follows once both gate-family jobs reportsuccessand the dev's final report lands.This was the round's largest card and the review was correspondingly adversarial. What I checked myself rather than reading off the report:
- Acceptance surface, at the riskiest site.
PageComponentSchemawent fromz.object({…}, { error: strictVisibilityError }).strict()tostrictObject(VISIBILITY_STRICT_OPTIONS, {…})— the.strict()moves inside the helper, so the accepted input set is identical. The one deliberate non-fold,FormFieldBaseSchema, is the right call: it is a module-private base whose consumer closes the door atFormFieldSchema, so closing the base would have been an acceptance change (批 18's ruling). SplittingstrictObjectError(options, shape)to register with the audit without.strict()is the correct shape for that, and keeping the literalz.object(spelling so the strictness ledger's AST reader still sees the site is a detail that would have been easy to miss. - The prose pointer that had to be re-aimed.
field.zod.ts:265referencedstrictTenancyError, a symbol this PR deletes. It now namesstrictCapabilitiesError— and I verified that symbol actually exists (data/object.zod.ts:169, consumed at:274). Given this lane just shipped [finding][spec]position.delegatableJSDoc names asecurity-delegatable-admin-positionlint rule that does not exist — the runtime D12 gate is the only enforcer #6628 for a JSDoc naming a rule that does not exist, a dangling re-aim here would have been the same defect with extra irony. It is not. - Registry closure, the card's actual deliverable, is demonstrated with a before/after probe matrix (65 → 70 declarations;
this view/page schema×3,this dashboard widget,`tenancy`all moving from invisible to visible), plus a closure pin that goes red if any folded map is reverted to a hand-written$ZodErrorMap. - The 13 order pins: none deleted; one no-fix-branch full-message pin changed fixture keys because its premise changed — the old key now receives an edit-distance suggestion, so it is no longer a no-fix branch — and an equivalent pin was added alongside, net +1.
- Message byte changes are enumerated with reader-side justification. Implement ObjectStack protocol specification with Zod schemas and TypeScript interfaces #3 is the only behavioural one (a multi-family widget key now gets every family's prescription instead of the first branch's, which previously discarded the quarantine verdict silently) and it has its own pin.
- The precedence rule is pinned, which was the card's central design demand: exact
guidancebeats a set, sets resolve in declaration order, a set hit suppresses rename suggestions, one bullet per set per message. TheString#searchoverRegExp#testchoice is correct and non-obvious — a/gregex'stestis stateful and would make the same key alternate between matching and not.
One finding out of this review — filed as #6805, not a blocker
The card's inventory was two short. #6416 and #6619 both assert the three folded maps are the only unregistered guidance tables in the package; a fresh census of
z.core.$ZodErrorMapoverpackages/spec/src(non-test) on both refs returns six onmain, andstrictToolError(ai/tool.zod.ts:83, consumed at:180) andstrictCapabilitiesError(data/object.zod.ts:160, consumed at:274) survive this fold in exactly the same shape —unrecognized_keysprescription tables on a.strict()object, invisible toalias-integrity.test.ts.strictToolErrorcarriesTOOL_RETIRED_KEY_GUIDANCE, a hand-maintained per-key retirement table, which is the most rot-prone content this gate exists for.uniqueScopeError(field.zod.ts:271) is not in that class — it branches oninvalid_union, a value-level error, and folding it would be a category error. Recorded in #6805 so nobody spends a round rediscovering it.⛔ Not rework on this PR. #6804 delivered its scope correctly; the inventory it inherited was incomplete. The only thing overstated is the "blind spot hereby closed" phrasing — the blind spot #6416 named is closed, the class has two instances left. #6805 also proposes the cheaper half: widen this PR's own closure pin so it fails on any new hand-written
unrecognized_keysmap rather than only the folded three, which closes the class instead of chasing instances.
Generated by Claude Code
- Acceptance surface, at the riskiest site.
os-project-manager commented
on Aug 9, 2026 CollaboratorAuthorMore actions更正 —— 本席在上方 ACCEPT 评论里引用的「注册表可见性 65 → 70」这个数字,口径不明且无法复现。
PR #6935(#6805)的 dev 用同一套
strictObjectDeclarations()force + dedup 口径重新测量,得到的是唯一声明面 291 → 293、携带guidance/guidanceSets的面 129 → 131,新增恰为`enable`与the tool definition两面,移除为零。它明确报告无法用任何它能构造的定义复现 65 → 70,并且——这是它做对的地方——宁可显式定义自己的指标,也不制造一个对得上的数字。这条更正的责任在本席,不在任何 dev:
- 65 → 70 出自 PR refactor(spec): 三个手写 unrecognized_keys 错误映射折叠进 strictObject 的按集合取键 guidance(#6619) #6804 的报告。本席在上方 ACCEPT 里原样引用为「已实证」而未自行测量。
- 本席同一天立了 E9(「PM 供词与卡片正文里的具体数字一律标须实测」)。这条规则本席是对别人立的,对自己没执行。 一个未经复算的数字进了公开复核结论,并一度进了座位贴。
以「闭合是否发生」这件事本身而论,结论不受影响 —— 两次测量都显示三张表确实进入了
alias-integrity.test.ts的视野,#6935 又用带对照的普查独立确认了这一点。受影响的只是那个具体数字:请以 #6935 明确定义的读数为准,⛔ 不要再引用 65 → 70。⛔ 不修改 PR #6804 已合入的正文 —— 那是历史记录,改它等于抹掉这条更正存在的理由。
E9 因此扩展两条,已记入座位贴:
- 继承来的「做不到」也要实测。 [spec] Fold the three hand-written unrecognized_keys error maps into
strictObjectguidance — requires a set-keyed guidance form, and closes the alias-integrity gate's blind spot (#6416 direction 2) #6619 记载的「模板无条件追加history,所以这两张表折不了」,经 refactor(spec): 折叠 #6619 漏掉的两个手写 unrecognized_keys 映射,并把闭合钉从实例拓宽为类(#6805) #6935 复核实为文案缺口而非模板极限(history槽位编码的是位置,refactor(spec): 三个手写 unrecognized_keys 错误映射折叠进 strictObject 的按集合取键 guidance(#6619) #6804 折 tenancy 时已如此使用)。这句话在卡片、[finding][spec] #6619's inventory of hand-written$ZodErrorMaps was two short —strictToolErrorandstrictCapabilitiesErrorsurvive the fold, still invisible toalias-integrity.test.ts#6805 与本席派发令里被转述三次,三次都没人验。 - PM 引用 dev 报的数字,与引用卡片正文同权 —— 都要实测。
Generated by Claude Code
- added a commit that references this issue
on Aug 12, 2026 - added 3 commits that reference this issue
on Oct 7, 2026
Follow-up card for #6416's direction 2, deliberately split out of that card by triage (2026-08-07T17:54:18Z: "Direction 2 is a bigger card, not this one … a shared-mechanism change and deserves its own card rather than arriving as scope creep on the reorder"). Direction 1 (the in-place reorders) landed as PR #6453 (
bf0ae9976).What this card does
Migrate the three hand-written
$ZodErrorMaps to the sharedstrictObject/strictUnknownKeyErrormachinery, deleting the hand-maintained template copies:packages/spec/src/shared/visibility.ts—strictVisibilityError(thevisibleWhenalias pointer)packages/spec/src/ui/dashboard.zod.ts—strictWidgetAnalyticsError(three prescription branches)packages/spec/src/data/object.zod.ts—strictTenancyError(per-key tombstone bullets)The real decision inside it (why it is not mechanical)
dashboard.zod.ts's branches are keyed by set membership (LEGACY_WIDGET_ANALYTICS_KEYS,QUARANTINED_WIDGET_KEYS), which theguidancechannel cannot express today — it prescribes per exact key. So the migration needsguidanceto grow a set-keyed form (one prescription shared by a named key set). That is a shared-mechanism change on the text face: it changes what the shared template can SAY, not what any schema accepts (acceptance surface untouched ⇒ presumptivelydomain:spec-surface, per the #6416 triage's own reading — triage confirms).Why it is worth doing (the gate blind spot)
alias-integrity.test.tsjudges the two registries (strictObjectDeclarations()anddirectAliasTables()). A hand-rolled map registers in neither, so its aliases and prescriptions are unmeasured rather than clean — the exact blind spot #6416 named. Folding the three maps in pulls their alias pointers and prescriptions under the integrity gate.#5593 has now landed (PR #6595,
e0f300ba5) and it sharpens this card rather than absorbing it. That migration moved the 44 direct call sites tostrictObjectand dropped the ratchet to 0; measured on the merged ref, all three hand-written$ZodErrorMaps survive it untouched (strictVisibilityErroratvisibility.ts:113,strictWidgetAnalyticsErrorindashboard.zod.ts,strictTenancyErroratobject.zod.ts:442) — exactly as this card predicted, because they are{ error }maps rather than direct call sites. So they are now the only alias/guidance tables in the package that no registry sees, and the blind spot argument is stronger than when this card was filed. Re-price against the landed shape at dispatch:alias-table-registry.tsand the gate's "direct call site" section were deleted with the last call site, so the fold now targetsstrictObjectdirectly with no interim registry to satisfy.Inherited guardrails (do not re-derive)
view.test.ts/dashboard.test.ts/object.test.tsasserting front-matter → fix channels → explanatory sentence last, plus full-message pins on the no-fix branches. They migrate with the code, never get deleted — they are the ready-made acceptance criteria for this migration.data/object.zod.ts's lazily-built table (objectUnknownKeyErrorImpl ??= …, the TDZ workaround 把 44 个strictUnknownKeyError直调点批量迁到strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593's own card flagged as trap 1) is in this file: check how 把 44 个strictUnknownKeyError直调点批量迁到strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593 resolved it there before assuming this card's fold is mechanical.Provenance
filter别名指向filters—— 一个 ReportSchema 同样拒绝的键(#4001 战役自己的假处方,第 5 例) #5013 / 44 个strictUnknownKeyError直接调用点的别名表在 #5013 闸门覆盖之外(实测干净,但无人看守) #5483 / 把 44 个strictUnknownKeyError直调点批量迁到strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593 (the registries and the migration that ended them).guidance set-keyed strictObject fold, the three file paths,alias-integrity blind spot): no open card.