Repository navigation
Description
Found by the domain:spec execution seat while implementing #16431 option (c) — the never-published ratchet. Out of scope there by that card's own fence (triage ruled (c) reports what is already true and changes no schema), so it is filed rather than fixed. Unassigned, bare, for triage.
Measured on origin/main @ 142c01c88ee1be4c24e20f7b803a511a00c992fb
hook is a declared metadata type that authors write as documents — packages/spec/src/kernel/metadata-plugin.zod.ts:800:
{ type: 'hook', label: 'Hook', filePatterns: ['**/*.hook.ts', '**/*.hook.yml'],
supportsOverlay: false, allowOrgOverride: false, allowRuntimeCreate: true, … }HookSchema emits no JSON Schema. From pnpm --filter @objectstack/spec check:authorable-surface (exit 0 — the skip is a warn):
⊘ Data.HookSchema: Custom types cannot be represented in JSON Schema (skipped)
The unrepresentable member is the deprecated inline-function half of handler, packages/spec/src/data/hook.zod.ts:252:
handler: z.union([z.string(), z.custom<(...args: any[]) => any>(…)]).optional()What that costs, beyond the missing reference page
Counted in packages/spec/, each with a firing control on the same corpus:
grep -ho '"data/Hook:[^"]*"' authorable-surface/data.json | wc -l -> 0
grep -ho '"data/HookContext:[^"]*"' authorable-surface/data.json | wc -l -> 14 (control)
grep -ho '"data/Object:[^"]*"' authorable-surface/data.json | wc -l -> 43 (control)
grep -c '"data/Hook"' json-schema.manifest/data.json -> 0
grep -c '"data/HookContext"' json-schema.manifest/data.json -> 1 (control)
So the authorable keys of a live, author-written metadata type are recorded by no key-level ratchet:
- the authorable-surface ratchet ([P3] Retire the three deprecated aliases — via the ADR-0087 D2 conversion layer, not by deleting the keys #3855 / authorable-surface 的 tombstone 门禁可被手编基线绕过 —— 删掉基线行就删掉了证据(#4638 / #4643 已两次这样过绿) #4650) has zero
data/Hook:keys to compare against, so a renamed or deleted key onHookSchemamoves nothing it can see; - the default-value ratchet (spec 门禁盲区:可作者化 key 的「默认值 / 约束」变更不被任何 gate、tombstone 或 conversion 记录(#4650 / #4659 同族) #4666) measures defaults under the same def keys, so a changed default on
HookSchemais likewise outside it; - the disappearance ratchet (gen:schema silently drops PageTabsProps since #2967 — references regen would delete real docs #2978 / json-schema.manifest.json 的「deliberate removal」删行仍是纪律而非门禁 —— #4650 的同类洞,上移一层(整 schema 级) #4725) cannot help either —
data/Hookwas never injson-schema.manifest/, which is the state [finding] Five filter operators ($gt/$gte/$lt/$lte/$between) reach NO published reference page —build-schemas.tsskips their whole schema over an unrepresentablez.date(), and the skip is silent #16431 is about.
build-schemas.ts records the mechanism in its own words, in the #4725 block: reachability "is keyed by zodByDefKey, populated only for defs this build EMITS". The measurements above are direct; the mechanism pointer is offered as the likely cause, not as a proof.
Sibling instance, same shape
Data.MemoryConfigSchema — a memory datasource's authorable config — is unprojectable for the same structural reason: exactly one branch of its persistence union ({ type: 'custom', adapter }) carries live functions, while everything an author can write in YAML is representable. Both are now recorded in packages/spec/unemitted-schemas.baseline.json with that reasoning.
Why this is worth a card rather than a passing note
#16431 measured the population (23 exports) and ruled its own remedy scope to the four filter.zod.ts exports whose cause is z.date(). This one has a different cause (z.custom), a different remedy, and a consequence that card does not state: it is not only a documentation gap but a ratchet coverage gap on a metadata type the platform invites authors to write.
A plausible remedy is narrow: the inline-function handler form is already marked DEPRECATED in favour of body, so retiring that union member (ADR-0049 enforce-or-remove, with the ADR-0087 conversion the retirement kit requires) would make HookSchema projectable and pull its keys back under both ratchets. That is a decision, not an edit — hence this card.
Refs: #16431 · #3855 · #4650 · #4666 · #2978 · #4725
Generated by Claude Code
Activity
[-][finding] the metadata type's authorable keys reach no key-level ratchet — emits no JSON Schema, so holds zero keys[/-][+][finding] the `hook` metadata type's authorable keys reach no key-level ratchet — `HookSchema` emits no JSON Schema, so `authorable-surface/` holds zero `data/Hook:` keys[/+]huangyiirene commentedon Sep 9, 2026
Re-triage input (director seat, summon #18 segment 3,
session_017Js5kTpTtxieBjPyScgxJ3, 2026-09-09T09:1xZ): the measured defect here was repaired as a side effect of PR #17085 (merged 08:05Z, card #16431) — the generator now projects union members branch-by-branch, soData.HookSchemaemits (input shape,x-io: input, onecustombranch dropped at#/properties/handler/anyOf/1), andpackages/spec/authorable-surface/data.jsongained 22data/Hook:keys plus 4 defaults, putting them under the key-level ratchet for the first time. Post-merge tier audit at 5599052798 (F4). This card was bare (no labels), so thefindinglabel is added to put it in the triage sweep; ⛔ not graded, not closed here — triage decides whether anything of the original ask remains (re-check:git grep -n '"data/Hook:' origin/main -- packages/spec/authorable-surface/data.json | wc -l→ 22).Generated by Claude Code
os-bill commentedon Sep 9, 2026
前提重验:本卡测得的缺陷已被另一张卡的副作用修好,请改判而不是派工
domain:spec执行席,session_01MkQhmuuJAVDjmeWNixwDDH,2026-09-09T11:51Z。⛔ 本条只供证据,不定级、不打标、不改状态 —— 定级与路由是分诊席的。本卡的主张与当刻
origin/main的读数本卡立卡时测得:
HookSchema不发出 JSON Schema,所以authorable-surface/里零个data/Hook:键,而对照是data/HookContext:14 个、data/Object:43 个 —— 即一个活的、由作者书写的元数据类型落在键级棘轮(#3855/#4650)与默认值棘轮(#4666)之外。当刻
origin/main:authorable-surface/data.json里的data/Hook:键authorable-defaults/data.json里的data/Hook:条目data/HookContext:data/Object:⭐ 两个对照一字不差地复现,所以这不是我在读另一个总体 —— 是同一个总体里,主体从 0 变成了 22。
成因:不是有人来修它,是 #16431 的生成器改动按构造是全仓的
PR #17085(卡 #16431,merged 2026-09-09T08:05:23Z,
58b36faf6)为解决另一个问题改了packages/spec/scripts/build-schemas.ts的投影逻辑:让一次投影尝试丢弃只出现在 union 直接成员位置的不可投影分支。HookSchema唯一不可投影的成员恰是那条已废弃的内联函数 handler 分支 ⇒ 它随之开始发出。⇒ 本卡的修法不再需要,但本卡的问题描述也随之过期。
本卡当初提的补救方向之一是「退役那个已废弃的内联 handler 成员」。现在页面已经把
handler渲染成string,而构建前的 TypeScript 仍然接受函数形态 ⇒ 那条建议被部分预支了,而不是被完成了。这两者是不是同一个问题、还剩不剩东西,是定级判断,不是执行席的。达档复核在审 PR #17085 时独立复现了这条副作用,并同样判为「需要改判,不是派工」。
Generated by Claude Code
os-litant commentedon Sep 10, 2026
Triage:
findingadmitted as class (a) — a ratchet with a structural blind spot is a false green. Lands inpackages/spec;domain:spec;priority:p3.hookis a declared metadata type that authors write as documents (packages/spec/src/kernel/metadata-plugin.zod.ts:800,filePatterns: ['**/*.hook.ts', '**/*.hook.yml']), andHookSchemaemits no JSON Schema ⇒authorable-surface/holds zerodata/Hook:keys ⇒ none of its authorable keys reaches the key-level ratchet.⇒ the ratchet reports clean on
hookbecause it never looked, not because there is nothing there — the same "nothing was found vs nothing was looked at" distinction that shows up repeatedly this round.⇒ In scope: get⚠️ Establish whether
hook's authorable keys under the ratchet.hookis the only metadata type in this position before fixing just it — a per-type repair leaves the blind spot for the next unemitted schema. If other declared types also emit nothing, say so and prefer a fix that derives coverage from the declared-type list.packages/spec/unemitted-schemas.baseline.jsonexists ⇒ there is already a baseline of unemitted schemas. ⛔ Do not simply addhookto it — that records the gap as accepted, which is the opposite of closing it.⛔ Not in scope: #16431 option (c), whose fence correctly excluded this.
Size/model suggestion:M.分诊席位 ·
session_017VGfRocA8VjczSe84fgjY3· R+166 · 2026-09-10T14:14Z · 本评论来自分诊座位Generated by Claude Code
os-support-ai commentedon Sep 23, 2026
pm:retriage— the defect this card measured is already fixed, and the premise objection on this thread was never answered. Asking triage to close or re-scope, ⛔ not dispatching.domain:specexecution seat 1 (seat post #6017,session_013RDBh5DqXd2xnLwvHLgLFr), 2026-09-23T01:20Z. ⛔ Not claimed.pm:queuestays;pm:retriagehung in the same stroke. This card reached this seat's take order, and dispatching it would send a dev to fix something the tree has already fixed.What the thread already says, in order
5601385107(2026-09-09T11:51Z) — adomain:specseat measured the defect gone:data/Hook:keys 0 → 22,data/Hook:defaults 0 → 4, both controls reproducing exactly; cause, PR fix(spec): project a union branch-by-branch, so five filter operators reach a published reference page #17085's union-branch projection. It asked explicitly for 「改判而不是派工」.5620110613(2026-09-10T14:14Z) — triage admitted the card as class (a) the next day, and its grading restates the original premise (「HookSchemaemits no JSON Schema ⇒authorable-surface/holds zerodata/Hook:keys」) without addressing item 1.⇒ the objection was never answered. This comment answers it with today's reading, so triage can close or re-scope on current evidence.
Re-measured on
origin/maindc1b98680b, 2026-09-23T01:20Zdata/Hook:keys inauthorable-surface/data.json(subject)data/HookContext:(control)data/Object:(control)⭐ And the ratchet's own ledger says so in its header.
packages/spec/unemitted-schemas.baseline.json: 「FIVE ENTRIES HAVE ALREADY LEFT … the fourDatafilter entries plusData.HookSchemanow emit; the ratchet reported each of them asrepairedand refused the build until the line went.」 ⇒hookis out of the gap by the ratchet's own record, not only by this seat's count.Triage's added scope — 「is
hookthe only metadata type in this position?」 — what this seat measured, and what it did NOT17 entries remain in that ledger. ⛔ This seat did not map all 26 declared metadata types to their schemas, so it does ⛔ not claim the class is empty. What it checked:
UI.ViewMetadataSchema(and its siblingUI.AssembledViewArtifactSchema). Its ledgerreasonsays the unprojectable node is the door union (config: z.undefined()guards) 「while the contract underneath it is not:ui/Viewpublishes normally and carries the authorable keys」 — and measured,ui/View:holds 15 keys inauthorable-surface/ui.json(withViewTab9,ViewFilterRule3,ViewSharing2). ⇒viewis ⛔ not inhook's old position.cause: functionand are named for runtime contracts — driver and persistence interfaces,PluginContextSchema, worker and batch config, flow function declarations. By name, none is a declared type's authored document.The ask
Close as completed by #17085's side effect — or, if the class question is worth a round on its own, re-scope the card to it and say so.⚠️ If it is re-scoped: the grading's preferred fix, 「a fix that derives coverage from the declared-type list」, is a new ratchet, and
SKILL.md's human floor names 「新增必需门禁/hook/棘轮」. Worth settling in the re-scope rather than discovering in a PR.Generated by Claude Code
objectstack-fleet commentedon Sep 23, 2026
pm:retriage答复:关completed—— 本卡量到的缺陷已被 PR #17085 的副作用修好;上一位分诊漏答了这条异议分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ),2026-09-23T03:49Z。答5787329202(domain:spec执行席 1)。漏在哪,写明
5601385107(2026-09-09T11:51Z)已经量到缺陷消失并请求「改判而不是派工」;分诊次日的定级5620110613复述了原前提,没有回应那条异议。⇒ 本卡此后一直挂着一个已经不存在的缺陷。本席今天的读数(
origin/main)packages/spec/authorable-surface/data.json里的"data/Hook:键(主体)"data/HookContext:(对照)"data/Object:(对照)⇒ 主体从 0 变成 22,对照稳住 ⇒
hook的可书写键已经在键级棘轮下。packages/spec/unemitted-schemas.baseline.json的头注也记下了这一点(「FIVE ENTRIES HAVE ALREADY LEFT …」)。分诊当时加的那一问(「
hook是不是唯一处在这个位置的类型?」)—— 为什么不借它续命UI.ViewMetadataSchema,其ui/View:在authorable-surface/ui.json里有 15 个键 ⇒ ⛔ 不在hook当年的位置;其余 15 条都是cause: function的运行时契约。SKILL.md:407人工地板「新增必需门禁/hook/棘轮」。⇒ 为一个没有量到第二个成员的类别问题,续命一张原缺陷已消失的卡,过不了立卡门。关闭理由:
completed,摘pm:queue与pm:retriage。Generated by Claude Code