Skip to content

feat(spec): retire record:highlights highlight-field icon — advertised on six surfaces, drawn by nothing (#10054, ADR-0049) - #10910

Merged
os-elon merged 3 commits into
mainfrom
claude/issue-10054-retire-highlights-icon
Aug 21, 2026
Merged

feat(spec): retire record:highlights highlight-field icon — advertised on six surfaces, drawn by nothing (#10054, ADR-0049)#10910
os-elon merged 3 commits into
mainfrom
claude/issue-10054-retire-highlights-icon

Conversation

@os-elon

Copy link
Copy Markdown
Collaborator

Fixes#10054

Retires record:highlights highlight-field icon under the ADR-0087 flow (ADR-0049 enforce-or-remove; maintainer ruling 2026-08-21, 「其他接受」 accepting option A, executing the 2026-08-20 census verdict: measured dead — zero read points, while advertised on six author-facing surfaces).

Route decision (recorded per the playbook)

The object arm is strictObject, so the route is strict deletion + a guidance prescription — not a retiredKey tombstone. Why: the playbook's §2 table sends strict shapes down this route; the freshest precedent (data/Metric:filters, PR #10552) records the same reasoning verbatim for a strictObject shape; and the ruling's Clause-② wording prescribes exactly what this route produces — an authored icon becomes the arm's own named unrecognized_keys refusal carrying the prescription (a tombstone would instead fire as a never-parse error and keep the key in the walked shape). The refusal reaches authors through the zod-4 union collapse via packages/lint/src/zod-issue-format.ts's arm unpacking (#5583 wiring, untouched).

Registered under major 18, not 17: v17.0.0 was cut before this landed, so the removal ships on the 17.x line (launch-window convention: accept-set narrowings ride minor releases — enforced by check-changeset-no-major) and the prescription lives at the major boundary where os migrate meta users look (the #8495 / PR #8666 precedent).

What changed

  • packages/spec/src/ui/component.zod.tsicon deleted from the RecordHighlightsField object arm; guidance.icon prescription added (fully-qualified key, the measured dead-end, "Delete the key — no replacement: the renderer never drew it", the os migrate meta --from 17 pointer). Both advertising describes corrected (the union's own and RecordHighlightsProps.fields'). ⛔ readonly untouched — it is the arm's one enforced key (RecordHighlightsField does not declare readonly, so the spec silently strips the key the chip gate reads #5176); label/type untouched.
  • ADR-0087 registration — retired-key entry ui/RecordHighlightsField:icon (entries/retired-keys/18.ui__RecordHighlightsField__icon.ts, registry block regenerated by gen:migration-registry); D2 conversion record-highlights-field-icon-removed (stripKeys over the object entries of every record:highlightsfields[], region + slot reach, scoped by component type; bare-string entries and live neighbour keys untouched; fixture disjoint from every other conversion, expectedNotices: 2); wired into the step-18 chain (conversionIds + rationale fragment).
  • packages/lint/src/zod-issue-format.ts — entry-shape prose corrected (comment only; no behaviour).
  • content/docs/references/ui/component.mdx — regenerated by gen:docs: the union describe, the icon row (gone), and the fields row.
  • Pin tests (packages/spec/src/ui/component.test.ts) — the old parse-survival pin authored icon: 'flag' (the one in-repo writer of the key); fixture-triaged as respell: it now pins the surviving surface {name, label?, type?, readonly?}. New pins: a refusal pin digging the arm's unrecognized_keys out of the union collapse (asserts keys contains icon, path ['fields', 0], the named surface, the record:highlights field icon is normalized by the renderer and then drawn by nothing at the pin — a possible #8691-class dead key #10054/ADR-0049 citation, the no-replacement guidance, the migrate pointer); a no-icon positive control; a readonly-untouched pin.
  • Changeset@objectstack/specminor per the launch-window convention, with FROM → TO mapping and the adr-0087: registered disposition marker.
  • No liveness-ledger editpackages/spec/liveness/ has no per-prop ledger for UI component props (same as the data/Metric:filters precedent). No authorable-surface row moved — the baseline enumerates RecordHighlightsProps' top-level keys only; nested union-arm keys have no row (verified: authorable-surface/ui.json is byte-identical). api-surface byte-identical — key-level narrowing is invisible to it (the crypto.hash 能力声明了、构建期还会自动推断,但沙箱从没实现 —— 调用直接抛(declared ≠ enforced) #4391-side of the ratchet-visibility table; the earlier "stale" verdict was the generator's dist-timing refusal, and the regenerated output produced no diff).

Census basis (ruled, not re-adjudicated)

Zero read points: objectui's renderer normalizes the authored object and carries icon: f?.icon into HeaderHighlight, whose chip has no icon slot (its only icon occurrence is a button size="icon"); structurally unable to travel useRegisterHighlightFields (names: string[]); not designer-publishable (Studio field-list input typed string[]); zero in-tree producers (all three authored record:highlights blocks are bare string arrays — re-verified in this tree: project-detail.page.ts, task-detail.page.ts, sys-user.page.ts). Recorded confidence gap, accepted by the ruling: out-of-tree customer apps cannot be enumerated — the loud refusal is the mitigation.

Verification (union re-run at final HEAD 0a58f719ec, post-merge of origin/main)

  • pnpm --filter @objectstack/spec test415 files / 11054 tests passed (includes the new pins, the conversion fixture replay + chain replay, and the alias-integrity audit that judges the new guidance entry). Tree equivalence: the merge changed nothing under packages/spec, packages/lint, packages/platform-objects, packages/qa, examples, content/docs, skills (two unrelated .changeset files only), so suite greens carry to the final tree.
  • Consumer sweep (downstream direction — packages whose tests/fixtures author or reference record:highlights field objects, enumerated by grep): @objectstack/lint80 files / 2251 passed; @objectstack/platform-objects123 files / 884 passed, 3 skipped; @objectstack/dogfood26 files / 426 passed. Typechecks: spec PASS, lint PASS.
  • Reverse verification (from the committed state, expected direction: red): re-inserted the icon line (mutation proved on disk: grep count 0→1 + git diff 1 insertion), the refusal pin went RED (1 failed | 2 passed, vitest exit 1) while both positive controls stayed green; trap-guarded restore proved on disk (count back to 0). No rebuild leg required: spec's own tests import ./component.zodrelatively from src — no dist/exports resolution and no alias question in this in-package pin.
  • Gate union derived by node scripts/pm/dispatch-gates.mjs (no hand-fed paths; re-derived after the merge with the updated derivation) and re-run at 0a58f719ecall PASS, per-gate exits captured before any pipe, verdicts read from each gate's own output: changeset-gate-self-tests, cross-package-test-inputs (both spellings), doc-anchors, doc-authoring, lint/doc-formula-expressions, docs-audit-scope, docs-redirects, spec empty-state, spec liveness, merge-driver, objectui-changeset, published-readme-links, quick-reference-counts, role-word, slot-lookup, spec-parsed-alias, spec strictness-ledger, test-source-alias, type-source-resolution, spec variant-docs, adr-0087-registration (✓ … registered record-highlights-field-icon-removed (new here)), changeset-no-major, ci-filter-parity, dev-prereqs, doc-frontmatter, empty-changeset, plugin-teardown-shape, docs-audit/check-affected-docs; convention-triggered for the test-file edit: query-options-erasure, type-check-coverage, type-check-debt (--re-measure on the fully built workspace closure), engine-double-contract, where-matcher; plus playbook families spec skill-examples, check:i18n, check:nul-bytes (OK, 6297 files), and pnpm --filter @objectstack/spec check:generated at final HEAD.
  • ESLint — declared narrowing (repo-wide pnpm lint is CI-owned): ran eslint --no-inline-config --format json over the 8 changed files. ① Population per eslint's own config resolution: the 6 TS files are in-population; the 2 md/mdx files are declared out by eslint itself ("File ignored because no matching configuration was supplied"). ② Counts from the JSON: 8 results, errorCount 0, the only 2 warnings are those ignore notices. ③ Invariance for untouched files: eslint.config.mjs (lines 326–329) states type-aware linting is never enabled for any file, so a file's verdict is a function of that file plus the shared config — and this diff edits no config, so no untouched file's verdict can move.

Cross-repo

objectui's plugin-detail advertisement (index.tsx:522 input description) and the normalizer's dead icon: f?.icon carry are out of this repo's surface — filed as objectstack-ai/objectui#5601 (unassigned, dedup-checked, Blocked-by this retirement, proceeds on the next pin bump — the objectui#3829 pattern).

Generated by Claude Code


Generated by Claude Code

…d on six surfaces, drawn by nothing (#10054, ADR-0049)
Strict deletion + guidance prescription at the union's object arm (strictObject
route, the data/Metric:filters precedent — no retiredKey tombstone); the two
advertising describes corrected; ADR-0087 registration (retired-key entry
ui/RecordHighlightsField:icon, D2 conversion record-highlights-field-icon-removed,
step-18 chain); packages/lint entry-shape prose corrected; pin flips + refusal
pins through the zod-4 union collapse; regenerated reference docs; minor
changeset per the launch-window convention.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B4h3medzvhB9rpfoja9jcw
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/lint, @objectstack/spec, touching 10 documentable anchor(s).

1 release-owned page(s) name something this change touched. These are read-only:

  • content/docs/releases/v17.mdx(via strictObject (symbol))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/lint/src/zod-issue-format.ts, packages/spec/src/migrations/entries/retired-keys/18.ui__RecordHighlightsField__icon.ts) — pages documenting those are invisible to this run
  • 5 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 45 of 221 client-bound route-ledger rows — the other 176 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 58563be069a70d09c4d9cc5f522bb27c5f8a78b2packageMentionDocs.

Which tree this was computed on

This run read content/docs from 5778905b8c04bdf35d77de5b3ed9444449d801a9 — the merge of head 0a58f719ec128bcf85984fdcb18d8dc67a28d9e5 into base 58563be069a70d09c4d9cc5f522bb27c5f8a78b2, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 5778905b8c04bdf35d77de5b3ed9444449d801a9 && git checkout 5778905b8c04bdf35d77de5b3ed9444449d801a9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 58563be069a70d09c4d9cc5f522bb27c5f8a78b2 0a58f719ec128bcf85984fdcb18d8dc67a28d9e5 && git checkout -B drift-repro 58563be069a70d09c4d9cc5f522bb27c5f8a78b2 && git merge --no-ff 0a58f719ec128bcf85984fdcb18d8dc67a28d9e5
node scripts/docs-audit/affected-docs.mjs --json 58563be069a70d09c4d9cc5f522bb27c5f8a78b2

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 58563be069a70d09c4d9cc5f522bb27c5f8a78b2 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling protocol:ui labels Aug 21, 2026
@os-elon
os-elon marked this pull request as ready for review August 21, 2026 17:22
@os-elon
os-elon added this pull request to the merge queueAug 21, 2026
Merged via the queue into main with commit c684d00Aug 21, 2026
33 checks passed
@os-elon
os-elon deleted the claude/issue-10054-retire-highlights-icon branch August 21, 2026 17:37
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 32507842415 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Console Pin Gate — 失败步骤: Build the Console SPA at the pinned objectui SHA

    ✗ Build failed in 5.80s
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

跨 PR 相同签名(24h,按失败测试文件聚合):

  • ⚠️本次没有可用的聚合签名(日志里没有能解析出测试文件名的 FAIL 行)—— 这不是「没有同签名的其他 PR」,是这一轮没测到。跨 PR 聚合本次不可用,请手工比对其他 PR 的同类评论。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 38 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:uisize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

record:highlights field icon is normalized by the renderer and then drawn by nothing at the pin — a possible #8691-class dead key

2 participants

@os-elon@claude