Repository navigation
决策:check-spec-symbol-derivation 要不要从符号粒度收紧到成员粒度?(实测全树 3 条悬空引用,零 allowlist) #7513
Description
Activity
- addeddomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Sep 3, 2026 Maintainer ruling recorded — A′ + b: tighten precision rule 4 from symbol to member granularity now; rule 2's member-attached widening (B) and the bare
alignsphrase (a) each owe one measurement first.needs-user-decision→pm:queuein the same stroke.Director seat, summon 14 (
session_01LsEjuNMPitCHwEfYftZ1um, GitHubos-warren). Provenance: maintainer, live PM chat, 2026-09-04, decision batch #29, verbatim 「同意」 on the presented recommendation (A′ + b; fallback C). Premises re-verified onorigin/main24e027eat 05:24Z:scripts/check-spec-symbol-derivation.mjsstill carries the stalehasExportModifierprose at:143/:164beside theNo export filter (objectui#6291)comments at:1216/:1322; the phrase table at:78requiresaligns with.The ruling
- A — member-granularity dangling references. A comment citing
SpecSymbol.keywherekeyis not in that symbol's shape is an error, as a tightening of the existing precision rule 4 (objectui#4607), ⛔ not a second tool. Measured population: 3 hits, 0 allowlist entries — the guard's own header standard. - B deferred, measurement owed. Widening rule 2 to member-attached doc comments is a different population (57 member docs carrying claim phrases); the first claimant runs that scan in a throwaway and posts the counts here. Non-zero real findings ⇒ re-presented as its own item; ⛔ not landed on A's PR.
- b — bare
aligns/alignedstays out ofCLAIM_PATTERNSuntil its false-positive surface on a heuristic already producing 19 unbacked claims is measured; same claimant, same throwaway, counts posted here. - The two stale docblock paragraphs (
:143,:164) are corrected in the same PR — a correction, not a widening, needs no ruling (already agreed on the card).
Execution constraints, for the
domain:uiseat- One PR: rule-4 tightening + its self-test cases (the three known sites as fixtures:
SelectOptionMetadata×2 already repaired by PR fix(types,fields): correct three false spec-alignment claims and pin the real boundary #7510,DashboardSchema.titlerepaired by PR docs(app-shell): DashboardView's header comment stops attributingtitleto the spec #7520 — the gate must red on the pre-repair text and green onmain) + the docblock correction + the two measurement reports as comments on this card. S/M. Clause-②: no (repo gate). - ⛔ The 22 Zod method-surface hits (
.safeParse/.parse/.shape) are excluded by rule, not by allowlist entry — the exclusion is a documented predicate in the script. - Serial constraints: any open PR on
scripts/check-spec-symbol-derivation.mjs(finding(types/app-shell): 8 more hand-written select-option shapes remain after #6887 — and the derivation gate is structurally blind to the whole class #7014's remaining Q1/Q2 work, per its 5528139486).
Ruling authority: maintainer 2026-09-04 (batch #29). Related: #7014 / PR #7510 · #7509 / PR #7520 · #6291 · #4607 · #5899.
Generated by Claude Code
- A — member-granularity dangling references. A comment citing
The two owed measurements — B and b
Taken in a throwaway on
1bae75bbagainst@objectstack/spec@17.2.0, over the guard's own
jurisdiction (packages/*/src, minusSKIP_PATH_SEGMENTS, tests and stories excluded), using the
realCLAIM_PATTERNS/CLAIM_WINDOW/normalizeDocimported from the script rather than a
re-implementation. Neither landed on the PR.B — widening rule 2 to member-attached doc comments
reading value member docs carrying a claim phrase within CLAIM_WINDOWof a spec mention, same sentence52 ...of those, the member is TIED to a spec import, so rule 2 would pass it 7 ...UNTIED — the NEW rule-2 failures the widening would produce 45 The card's figure was 57; 52 is the same census re-taken after PR #7510 and PR #7520, and the
delta is those repairs plus the phrase-table/proximity discipline applied faithfully (a bare
co-occurrence count is higher).By package:
packages/types37 ·packages/plugin-form5 ·packages/core2 ·
packages/app-shell1. Concentration is the finding:packages/types/src/objectql.tsand
form.tsalone carry 30 of the 45, nearly all of them one repeated shape — a per-key
Aligns with @objectstack/spec FormField.KEYline on a member of a hand-declared mirror.So B is not 45 independent judgement calls; it is mostly two files and one habit. But 45 new reds
in one commit is well past this guard's own header standard, and the burn-down would land in
packages/types, not inscripts/**. ⇒ Still a separate item, and it wants a ledger seeded by
--claim-ledger, not an allowlist.b — bare
aligns/alignedadded toCLAIM_PATTERNSreading value comments containing a bare aligns/aligned(not followed bywith) anywhere55 ...that claim within CLAIM_WINDOWof a spec mention, same sentence5 ...that are NEW — not already matched by today's phrase table 0 The measured false-positive surface is zero, and so is the yield — the widening is a no-op on
this tree today. All 5 bare-verb comments near a spec mention already match today's table
through another phrase (canonical,mirrors, ...), so adding the bare verb changes no verdict.⭐ Positive control, because a zero that is never checked is the one reading you must not trust.
The verbatim pre-repairSelectOptionMetadatacomment — the specimen that motivated b —- under today's table:
findClaim()returnsnull(reproduces the card's observation) - under the widened table: hits, phrase
Aligns
So the instrument does see the shape it exists to see. The zero is real, and its cause is that
PR #7510 repaired the only site that had it. Worth putting in front of whoever rules on b: the
question has changed shape since the card was written — it is no longer "how much noise does this
buy" but "is a rule with no live population worth carrying".
Generated by Claude Code
- under today's table:
os-dev-report
{ "issue": 7513, "status": "done", "branch": "claude/issue-7513-spec-symbol-member-granularity", "pr": "https://github.com/objectstack-ai/objectui/pull/7617", "premise_still_valid": true, "summary": "Tightened precision rule 4 of check:spec-symbols from symbol to member granularity, per the A-prime + b ruling: a comment citing SpecSymbol.key where the spec's shape has no `key` is now an error. No allowlist and no ledger of its own — the measured population is 0 on the base commit, so a hit is always fresh. Zod's API is excluded by two documented predicates (call form + a five-name introspection vocabulary), never by ALLOW entries. Also corrected the two stale hasExportModifier docblock paragraphs and, as a declared bounded in-place fix, deleted the hasExportModifier helper itself (measured 0 call sites repo-wide — correcting the prose while leaving a live-looking helper to grep leaves the trap half-set). Three deviations the PM should read: (1) my re-measurement does NOT reproduce the card's 27 raw / 22 Zod figures — I measure 136 raw citations, 73 within window, 9 excluded as Zod API, 0 dangling; the card's snapshot came from a different, unrecorded extraction, and mine is documented in the script header with commit and spec version. (2) A structural 'any property of the Zod type' exclusion silently swallowed SelectOptionSchema.description — the very citation the rule exists for — which is why the predicate is a named vocabulary plus call form, with the authored key set answering FIRST. (3) The member set had to be the union over the spec's symbol family taken on the AUTHORING side: FormField.visibleOn is a real authorable key that the spec's output type deliberately Omits, and reading the output type alone reported that correct comment as dangling. B and b did not land; their counts are posted as their own comment.", "tests": "All at 3ce032c0 unless noted. (1) `node scripts/check-spec-symbol-derivation.mjs` exit 0, its own verdict lines: '✅ spec symbol derivation: 1343 files scanned against 4959 spec export names; 20 declared dialects, 14 untriaged collisions in 7 packages.' / '✅ spec alignment claims: 2 declared deliberate copies, 19 unbacked claims in 5 packages.' / '✅ spec member citations: no comment cites a key its spec symbol does not declare.' (2) `pnpm exec vitest run scripts/__tests__/` from the REPO ROOT, through objectstack/scripts/pm/os-verify-lock.sh (VERDICT command-exit 0, held 83s, waited 500s): 'Test Files 100 passed (100)' / 'Tests 2958 passed (2958)'. An earlier attempt returned exit 99 queue-timeout = NOT MEASURED, not a red; slot issue-7513-dev kept the place and the retry acquired it. `vitest list` confirms all 15 new tests are collected in the [unit] project, so they ran rather than being silently skipped. (3) `pnpm run type-check:scripts` exit 0; `tsc --listFiles` confirms BOTH edited files are in the tsconfig.scripts.json program, so this is not a NOT-MEASURED. (4) check:control-bytes exit 0 ('scanned 6228 tracked text file(s)'), check:entry-guard exit 0 ('56 export bindings, 56 of them inert on import' — this PR adds two exports), check-changeset-presence exit 0 ('no changeset is owed'). (5) eslint over the WHOLE repo, 4244 files (count from --format json): both changed files 0 errors / 0 warnings. Run exit 1 from 93 PRE-EXISTING errors in 79 untouched files; that is a raw `eslint .`, not the repo's `lint` script (turbo run lint). No type-aware linting is configured, so this diff cannot move an untouched file's verdict. (6) RED/GREEN pair: RED on the VERBATIM pre-repair comments copied from PR #7510 and PR #7520's removed lines — SelectOptionSchema.description@5, FieldSchema.rows@3 and @13 on two MEMBER docblocks, DashboardSchema.title@4 inside a function body; GREEN on main and on the repaired wording. (7) ABLATION: no rebuild applies — the guard is a plain .mjs consumed directly from source, there is no dist and no exports-resolved package boundary, so no build step exists to skip; I state that rather than claiming a rebuild I did not do. Implementation was COMMITTED first (3ce032c0), then the findings.push was neutered. Mutation confirmed ON DISK by anchored text counts in both directions plus a changed blob hash: removed-text count 0 (want 0), injected marker count 1 (want 1), hash 60b46c3f != HEAD blob 9caf395e. Under the ablation all three RED fixtures returned the empty list (baseline: all three returned findings), so those assertions genuinely fail without the rule. Restore leg proven by `git diff HEAD` empty AND worktree blob hash 9caf395e identical to the HEAD blob, with an absolute-path `trap ... EXIT INT TERM` armed throughout. Errors were captured by redirect-then-read, never through a pipe.", "mcp_calls": "1 — one add_issue_comment for the claim; every other GitHub read and write went through repo-scoped REST (probe green, 15000/hr installation token), and the card body plus comments were read from the public single-card JSON payload at zero quota.", "open_questions": [], "out_of_scope_findings": [] }Notes that do not fit the machine fields
- Serial constraint re-measured as the ruling required. All 6 open PRs enumerated to
completion (page 2 empty; PR chore: release packages #5400's 633 files paged out in full, 633 = itschanged_files):
zero touchscripts/check-spec-symbol-derivation.mjsor its test. finding(types/app-shell): 8 more hand-written select-option shapes remain after #6887 — and the derivation gate is structurally blind to the whole class #7014 has no open PR.
PR fix(types,layout,mobile,core): re-home the breakpoint vocabulary, delete the two dead responsive implementations #7598's two ALLOW entries are onmainand were read from the file's current state. - Nothing filed as out of scope. The one candidate —
FormField.visibleOnin
packages/types/src/form.ts— was investigated and is correct: the spec'sFormFieldSchema
acceptsvisibleOnat authoring (probed bysafeParse), and only the parsed OUTPUT type omits
it. Filing it would have been a false report; instead it became the measurement that shaped the
family-union member set. - ⛔ Not flipped ready, not enqueued, no assignee written. Landing is the PM's.
Generated by Claude Code
- Serial constraint re-measured as the ruling required. All 6 open PRs enumerated to
从 #7014 的执行中生出。⛔ 本席不裁,原因是先例而不是难度:这把工具的每一次加宽都先经裁定(objectui#6291),无裁而建第三次,正是它自己存在来防的那类载体缺陷。两个相关问题合并成一张卡上呈——分开呈会让维护者对同一件事裁两次。
背景:gate 看不见它本该看见的那一类
#7014 落地时(PR #7510)修掉了三条实测为假的规范断言:注释声称
@objectstack/spec声明某个键,而 spec 实际按名拒收它。逐条测过,每条都配了会点亮的对照:FieldSchema把 select 字段的options路由进那个.strict()的 option schema,所以在 option 上写一个description让整个字段 422INVALID_METADATA。那三条注释当时正在邀请这个写法。check:spec-symbols在这次修复前后打印逐字相同的输出(diff 验证)。三条真缺陷被移除,gate 一无所知。这是发现,不是通过。它为什么看不见——三个各自独立、今天都还活着的原因
attachedDoc(stmt, text))。三条断言全挂在成员上;SelectOptionMetadata自己的 docblock 只有/** Select option */。aligns with。 真实注释写的是裸Aligns——实测findClaim()对真实文本返回null,对同一文本插入with后返回一条 claim。(同一文件field-types.ts:336写的是Aligns with,gate 看得见——同文件里的对照。)SelectOptionSchema是活的 spec 导出,所以悬空引用精度规则(objectui#4607)不发火,尽管.description根本不是它的键。⭐ 还有第四个,更糟:卡片的假机制是从工具自己的 docblock 抄来的
#7014 说 gate 之所以瞎,是因为「both rules skip any declaration without an
exportmodifier (hasExportModifier, applied once per scanner)」。今天这已经不成立——两半过滤器都在 #6291 被删了。独立复现::143逐字描述被删掉的过滤器,:164描述两个 scanner 会「re-run withhasExportModifierforced true」。卡片作者不是凭空编的,是读了工具自己的头注释。 这不是文档陈旧,是一份还在教下一个读者假规则的说明书。⇒ 订正这两处 docblock 不需要裁定(是订正,不是加宽),已并入执行。下面的选项只关于要不要加宽 gate 本身。
尺寸——重新量过,卡片的旧数字是错的
#7014 用 #5899 的「47 对 18」给这件事定尺寸。⚠️ 那个数字属于已经落地的导出面加宽(#6291),不是这个洞。重测:
DashboardSchema.title(已另立 #7509).safeParse/.parse/.shape)三条,不是三十条。零 allowlist,恰好是这把 guard 自己头注释立的标准:「an ALLOW map with 38 entries is not a guard」。
选项
A —— 只加成员粒度引用检查。 注释引用
SpecSymbol.key而key不在该符号的 shape 里时报错。这是把已有的精度规则 4 从符号粒度收紧到成员粒度,不是第二把工具。B —— 同时把规则 2 加宽到读成员挂载的注释,而不只是声明挂载的。⚠️ 这是另一个总体:57 份成员 doc 带 claim 短语。dev 与本席同判——该单独测量后再裁,不应搭在 A 上。
C —— 都不做,继续一卡一卡地订正站点。
A′ —— 先 A,B 欠一次测量再议。 (dev 的推荐,也是本席的倾向。)
附问:裸
aligns要不要进短语表?规则 2 的短语表要求
aligns with,真实注释常写裸Aligns——这正是SelectOptionMetadata那条连规则 2 本可触及的部分都没看见的原因。aligns/aligned加进CLAIM_PATTERNS。dev 推荐 b 暂不动,但欠一次测量,本席同意并把理由记下:在一个已经产出 19 条 unbacked claim 的启发式上加一个高频裸动词,假阳性面没量过就不该裁——dev 明确说了它没量,而不是量了很小。这条测量便宜,谁先动这张卡谁顺手跑。
本席倾向:A′ + b(欠测量)
理由:(a) 真实需求——3 条实测悬空引用,其中两条正在邀请 422;(b) 长期健全——A 是收紧既有规则的粒度,不新增工具面;(c) AI 不易做错——把「引用一个不存在的成员」变成红,正是本轮三条假断言的成因;(d) 创业期尺度——零 allowlist、三个站点,而 B 的 57 份成员 doc 是没量过的总体。
⛔ 但先例说了算:#6291 是先裁后建的。请裁。
派工状态
⛔
needs-user-decision,不带pm:queue——本卡在裁定前不进队列(#6853 点名的缺陷:卡片自己说不可派工而标签仍在招手)。不受本卡阻塞:PR #7510(已落确定的一半,Clause-② no);#7509(
DashboardSchema.title,第三条悬空引用);#7014 仍开,余下 Q1(Tier-1 收敛)与 Q2(patchOptions守卫)已由本席裁明,见 #7014 评论 5528139486。