Skip to content

feat(spec)!: retire the view form's visibleOn and the page component's visibility as keys; their ADR-0089 renames leave the load path (F4 of #23543) - #23836

Draft
objectstack-fleet[bot] wants to merge 10 commits into
mainfrom
claude/issue-23543-f4-visibility-keys-retired
Draft

objectstack-fleet[bot] wants to merge 10 commits into
mainfrom
claude/issue-23543-f4-visibility-keys-retired

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Part of #23543
Clause-②: yes (narrowing)

This PR lands family F4 of ruling 6182001882 (item 4) only. F5-http stays on #23543 as its own PR, so #23543 remains open.

What

Ruling F4 A: "visibleOn (view) and visibility (page component) retire as keys: retiredKey tombstones naming visibleWhen with the full spec-property-retirement kit (ADR-0087 markers, baselines, forms, docs, pins); both entries retire in the same change; the three record / strict-exit suites re-point."

  • Three declared positions, measured on main at f606ba2e25 (authorable-surface/ui.json): ui/FormField:visibleOn, ui/FormSection:visibleOn, ui/PageComponent:visibility. There are no others. The inline form field and the page-block section entry in component.zod.ts never declared visibleOn; they already refuse it by name. Each of the three is now a retiredKey() tombstone whose prescription names visibleWhen, for example: "page.components[].visibility was removed in @objectstack/spec 18 (ADR-0089 D2) — … Use visibleWhen. Rename the key; the value (a CEL predicate) is unchanged. Where a component writes both, visibleWhen is the one that was in effect: delete visibility." There is no os migrate meta sentence, on purpose. Both conversions are protocol 15, below MIGRATION_SUPPORT_FLOOR, so the tool lists no edit for them. This is the F5-jsx precedent, and the removal-version pin's --from rule (toMajor − 1 = 14) leaves no legal sentence anyway.
  • The fold is gone. The three .transform(normalizeVisibleWhen) pipes existed only to fold the old keys, so they are removed and all three shapes are plain closed objects. FormField still omits 'visibleOn' from its parsed type, so a reader of parsed output that reaches for the key still fails tsc.
  • The two D2 entries view-visibleOn-to-visibleWhen and page-component-visibility-to-visibleWhen get retiredFromLoadPath: true and retiredAfter: '17.7.0', the stamp F1/F5-jsx/F3/F2 used and still the newest stable (18.0.0-next.2 label). Their "recorded verdict at 18" paragraphs are rewritten as retirement records. Fixtures are unchanged: every includeRetired seam still replays them.
  • ADR-0087 registration: three RETIRED_KEYS_BY_MAJOR[18] entry files, and one D3 entry ui-visibility-alias-keys-retired with no conversionIds (below the floor, the F5-jsx shape). There is no STEP18_RATIONALE fragment, because no step-18 conversion exists. The changeset marker is registered ui-visibility-alias-keys-retired.
  • Pins: the verdict-list pin in conversions.test.ts now keeps one entry, flow-node-http-callout-rename, and lists the two among the retired. view-spelling-walk.test.ts gains a case stating that the stored seam still replays the retired rename. The removal-version JOIN gains rows for both ids (three tombstones). There is a new repo-project pin, ui/visibility-alias-keys-retirement.test.ts, covering:
    • the type channel (@ts-expect-error at each authoring type);
    • the walked shape;
    • defineStack refusals with code + status;
    • load-path retirement;
    • stored view/page rows loading renamed;
    • the ledger rows;
    • a tree-scoped absence walk over the radius @objectstack/spec#test already declares.

维护者速读(草稿)

  • 改了什么: 「何时显示」这个条件只保留一个写法 visibleWhen。表单视图里的旧写法 visibleOn 和页面组件里的旧写法 visibility 不再被悄悄转换,作者写了会当场报错,并告诉他改成 visibleWhen。
  • 为什么改: 这是你 F4 A 的裁决。旧写法已经声明为弃用好几个版本,但解析时仍被默默折叠,等于同一件事有三种拼法;本仓示例和应用里 0 处在写旧拼法。
  • 风险与代价(含回滚): 第三方如果还写旧键,升级后会看到明确报错,改一个键名即可;数据库里存着旧键的视图和页面照常加载(读取时自动改名)。objectui 有读旧键的地方和两三个测试会在它下次升级 spec 时需要跟改,由席位立 objectui 跟进单。回滚就是 revert 本 PR。本 PR 带出一处生成文件 skills/objectstack-ui/references/react-blocks.md(一行类型从 any[] 变成 object[]),所以按受管面走人合。
  • 席位意见: 席位复核通过(domain:spec 席位 1),契约复核第二轮 PASS(6212287597,在当前 head 860f4a18be 上)。第一轮复核 FAIL 只有一处问题:说明文字声称「每个入口都先解析」,其实 os lint 不解析,已按复核意见只改文字并补一条测试。三处旧键都改成报错并提示 visibleWhen,两条旧转换不再在加载时运行,但库里存着的旧行读取时仍会自动改名(复核按代码核实)。CI 全绿。建议批准合并;这是 Tier H(带出 skills/** 的生成文件),席位不会自己翻 ready 或入队。
  • 你要做的: 这是 Tier H,需要你亲手批准或合并。合并后席位会:开 objectui 跟进单(渲染器读旧键的地方和三个会在下次 spec 升级时变红的测试);开一张卡退役已无调用方的 normalizeVisibleWhen / VISIBILITY_ALIAS_KEYS 导出;读合入后第一轮 Spec Shape Gate,若变红就按登记表流程登记。

Mechanism hypotheses, measured

  1. Every declared position: three, listed above.
  2. The parse-time fold: removed (above). On derivedKeys, keeping 'visibleOn' would NOT give two messages: the tombstone aborts the section parse, so the group-reference refinement never runs on a section that wrote it. I measured one issue, at sections.0.visibleOn. The entry was therefore unreachable and is removed, with the group describe updated.
  3. The two entries: stamped '17.7.0' (above). The D3 reason and acceptanceCriteria were tested sentence by sentence against the tool, on a temp project carrying all three old keys, with the built CLI closure:
    • os migrate meta --from 17 exits 0, replays protocol 17 → 18, applies no edit for either key, and prints the three tombstone refusals under "Migrated stack does not yet pass schema validation — 3 refusals left after the chain".
    • It also lists ui-visibility-alias-keys-retired among the manual changes.
    • --write leaves the source byte-unchanged.
  4. The suites that drove these entries live, decided per suite:
    • packages/spec/src/stack-conversions-record.test.ts (found by the full spec run; not in the claim list) gets a synthetic probe, the F2 route. vi.mock('./conversions/registry') appends a test-owned live copy of the page-component rename (stack-conversions-record-probe, toMajor: 15, so every pinned retiresIn: 16 holds). Its subject is the record, not an entry, and no later family has to re-home it.
    • packages/metadata-core/src/artifact-forward-conversion.test.ts FIRING CONTROL 1 is re-pointed to flow-node-http-callout-rename. Its claim is specifically that a NON-retired entry still fires at the artifact door; FIRING CONTROL 2 already covers retired replay. A probe would have to mock the spec registry behind metadata-core's @objectstack/spec import, which is the callee under test. F5-http inherits it; with no live entry left after F5-http, that PR turns it into "the door converts at all" or retires it.
    • packages/cli/src/commands/validate-json-strict-exit.e2e.test.ts and packages/cli/test/stack-conversion-record-door.test.ts are re-pointed to the callout rename: a flow node typed http_request against http. Both spawn the CLI over temp projects that load the built @objectstack/spec through a symlink, outside vitest's module graph, so no probe can reach them. This is the reason F2 gave for the migrate meta suites. The cells are unchanged: the converting member gives text --strict 1, --json --strict 1, --json 0, warnings: [] and one notice; the canonical member gives 0 everywhere. They join the cli suites F5-http already re-homes.
    • In-file: conversions.test.ts's "a live-window conversion still applies at load" is re-pointed to the callout rename, beside the two cases F5-http already owns.
  5. Readers, each fate measured:
    • packages/lint: validate-visibility-predicates.ts (the visibleWhen ?? visibleOn ?? visibility limbs) and validate-predicate-path-refs.ts (PREDICATE_KEYS) now read visibleWhen only.
      • The doors that parse first (os validate, os build, os verify through judgeAuthorTimeRules, defineStack, and the metadata save door, which parses the view body (422, protocol.ts:23082) before its runtime rule pass at :23192) refuse either retired key by name, so a body they hand these rules cannot carry one. os lint hands these readers an UNPARSED stack (cli/src/commands/lint.ts:681-:683). There, a retired key's predicate is unjudged by design, pinned in authoring-rule-input-tier.test.ts as that door's reading beside a canonical control (review patch 860f4a18be, after contract review FAIL 6211507698).
      • An accepted body cannot carry either key, so keeping the limbs would be consumer-side alias tolerance.
      • authoring-rules.ts comments and the rule-explanations.ts text ("(or visibleOn)") are made true.
      • page-envelope-audit.ts is generic (any parse-consumed string re-homed into an envelope), so its code is unchanged. Its one exemplar is gone: a page still writing visibility now fails the parse, which the audit reports on componentParseErrors with the prescription. The negative control is re-pointed to that, plus a canonical control.
    • packages/metadata-core/src/form-predicate-root-policy.ts: FORM_PREDICATE_KEYS = ['visibleWhen']. Its one caller scans result.definition after applyArtifactForwardConversions, which replays the whole chain (retired included) in every window it opens. So a legacy visibleOn is already renamed when it arrives; this is pinned end to end.
    • packages/sdui-parser/src/validate.ts: unchanged. SDUI_BASE_PROPS is the lockstep port of objectui's own BaseSchema node vocabulary for the html/JSX tier, which objectui still reads (SchemaRenderer). It is not the spec's PageComponentSchema, so it moves with objectui's card.
    • packages/metadata-protocol/src/protocol.ts: no read site of either key. Four comments about the view door's generic moved-key projection used the fold as their example and are made true.
  6. Examples and fixtures: no example or app authors either key (absence walk over packages, examples, skills, content, scripts). Fixtures were triaged one by one:
    • Re-spelled: fixtures that used the alias incidentally (editability-boundary, the lint groups walk, the metadata-protocol graft test).
    • Replaced: pins whose subject was the deleted branch ("normalizes visibleOn", "still read through the alias"), each with a refusal or not-read pin plus a canonical control.
    • Deleted: none.
  7. objectui (no edit there): measured at main 63d00da3de and at the pinned daeb77fc84, identical for this question.
    • Writers: no designer or Studio code writes either key into saved metadata.
    • Readers: about 17 form-field/section renderer reads of visibleOn, 6 SDUI-node reads, and 3 renderer reads of page-component visibility. One source type coupling: react/src/SchemaRenderer.tsx:191 visibility?: PageComponent['visibility'].
    • Spec-shape tests that break at objectui's next spec bump: plugin-form/src/sectionFields.spec-parity.test.ts reads SpecFormFieldSchema.in.shape, which no longer exists. types/__tests__/flat-props-refusal-10872.test.ts and SchemaRenderer.expressions.test.tsx:38 were not run.
    • Not imported anywhere in objectui: normalizeVisibleWhen and VISIBILITY_ALIAS_KEYS.
    • Console Pin Gate: it compiles objectui against objectui's installed spec (the showStar reading), so no break is expected there.

Side effects of removing the pipes (measured, each pinned)

  • The dropped-refinements ledger: 13 entries named sites through the pipe's .in. The build printed the corrected entries and they were applied as-is: path moves, plus one site (api/ListInstalledPackagesResponse … pages.element.slots.header.options[0].type). That site is the page type refinement already ledgered on the sibling responses, now reached one level shallower. The header total is 763 → 764.
  • A slotted page node's type refusal (a non-aborting refinement) now arrives at slots.header.type instead of nested in an invalid_union; component.test.ts pins the new, better-located shape. An unknown key still arrives as the union.
  • skills/objectstack-ui/references/react-blocks.md is regenerated by gen:react-blocks: object-form sections is object[] instead of any[], because the pipe had hidden the element type. ⚠️ This is a governed path, so the PR is Tier H. The file stays at 115 → 115 lines, the published SKILL.md set is unchanged, and the change is generated, not hand-authored.
  • Five tests read the old pipe (.in.shape / def.in.shape): component-form-custom-fields-sections-typed.pin, expression-scope-data-root.pin, expression-scope-app-root.pin, component-record-related-list-parent-23134 and metadata-core's form-predicate-root-policy. Each is moved to .shape. buildObjectFormSectionFieldEntry (component.zod.ts) read the pipe at runtime and reads .shape now.
  • Evaluated-slot population goes 34 → 31 positions; the dogfood ADR-0058 D7 ledger drops the three covers.
  • test-typecheck-debt.json: two TS2339 visibleOn entries VANISHED with the deleted reads, re-recorded as the gate asked.

Verification

Readings are on 6e242061, the head before main was merged, unless noted. The merge 24622be1 brought four commits; none touches these files except packages/spec/src/ui/view.test.ts, in other hunks, which merged cleanly through os-regen-merge.sh.

  • @objectstack/spec:

    • on 24622be1: build exit 0; check:generated "All 14 generated artifacts are up to date"; test 684 files / 20896 passed (1 todo), exit 0.
    • on 6e242061: typecheck exit 0; test:repo 64 files / 1082 passed.
  • @objectstack/lint: typecheck exit 0; test 140 files / 6779 passed.

  • @objectstack/metadata-core: typecheck exit 0; test 18 files / 734 passed.

  • @objectstack/metadata-protocol: typecheck exit 0; test 245 files, 20594 passed and 19 skipped after the one re-spelled graft fixture; that file passes 46/46.

  • @objectstack/cli: typecheck exit 0, after building the closure --filter '@objectstack/cli^...'; unit tier 297 files / 4432 passed; stack-conversion-record-door.test.ts 21/21; validate-json-strict-exit.e2e.test.ts (nightly tier, OS_TEST_TIERS=nightly) 7/7.

  • @objectstack/dogfood: expression-conformance.test.ts 7/7.

  • Gates (dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on 24622be1): 123 derived, each run with its exit code captured before any pipe. --ran reports "123 derived famil(ies) accounted for — 119 run, 4 NOT-MEASURED (4 DERIVED from a recorded exit 3)", and all 119 exited 0. The four are prerequisite refusals, declared to CI:

    • check-plugin-teardown-shape --self-test (shallow clone);
    • check:skill-examples (client dist absent);
    • check:dual-build-cjs-loads (full build absent);
    • check:i18n-coverage (@objectstack/connector-slack unbuilt).

    The local-only families check:dev-prereqs, check:skill-frame-freshness and check:nul-bytes exit 0.

  • Ablation, one-time (scripts/ablation-replace.mjs, on 6e242061):

    • Mutation: visibility: retiredKey( → visibility: EvaluatedExpressionInputSchema.optional(), __ablationRetired: retiredKey( in page.zod.ts, which restores the key as live. On-disk proof: anchor 1 → 0, blob b8bce09a → e5d1edef.
    • Result: 5 pins went red — page.test.ts ×2 (the refusal and beside-visibleWhen), the removal-version JOIN row page-component-visibility-to-visibleWhen, and visibility-alias-keys-retirement.test.ts ×2 (the @ts-expect-error type leg and the defineStack page refusal). Direction: red.
    • Restore proven: blob == HEAD b8bce09a, git diff HEAD empty. Spec tests import src, so there is no dist leg.

Acceptance notes

  • normalizeVisibleWhen and VISIBILITY_ALIAS_KEYS stay exported from @objectstack/spec/shared; no caller remains in either repo, and their docblock is made true. Retiring them is an export removal that would make the 18 D3 entry visibility-strict-options-unexported ("which stay exported") false, so it is left to a separate decision.
  • The metadata save door's canonicalization policy for retired spellings is untouched (the seat's separate decision). For view and page the door parses the body as stored, so the old keys meet the refusal there (422).
  • MAJOR_18_CONVERSIONS, every other conversion entry and objectui are untouched.
  • Labels: the write budget names only skip-changeset, and only when nothing publishes. This PR publishes, so it carries no label from me.

Generated by Claude Code

Claude-Session: https://claude.ai/code/session_014ruwnS8CSGGVA5UWfJA8xZ
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/lint, @objectstack/metadata-core, @objectstack/metadata-protocol, @objectstack/spec, touching 24 documentable anchor(s). ⚠️ 8 changed file(s) yielded no anchor (packages/spec/authorable-surface/ui.json, packages/spec/dropped-refinements.baseline.json, packages/spec/src/migrations/entries/retired-keys/18.ui__FormField__visibleOn.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

15 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/automation/flows.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/data-modeling/field-types.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/data-modeling/fields.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/data-modeling/formulas.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/data-modeling/schema-design.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/data-modeling/validation-rules.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/deployment/environment-variables.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/deployment/validating-metadata.mdx (via AUTHORING_RULES (symbol, a top-level const object))
  • content/docs/protocol/objectui/concept.mdx (via FormViewSchema (symbol, a top-level const), visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/protocol/objectui/index.mdx (via FormViewSchema (symbol, a top-level const))
  • content/docs/protocol/objectui/layout-dsl.mdx (via FormFieldSchema (symbol, a top-level const), FormSectionSchema (symbol, a top-level const), FormViewSchema (symbol, a top-level const), PageComponentSchema (symbol, a top-level const), PageSchema (symbol, a top-level const), visibleOn (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line), visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/protocol/objectui/widget-contract.mdx (via FormFieldSchema (symbol, a top-level const))
  • content/docs/ui/pages.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/ui/views.mdx (via visibleOn (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line), visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))

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

  • content/docs/releases/implementation-status.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/releases/v12.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/releases/v15.mdx (via FormFieldSchema (symbol, a top-level const), FormSectionSchema (symbol, a top-level const), PageComponentSchema (symbol, a top-level const), PageTabsProps (symbol, a top-level const object), visibleOn (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line), visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/releases/v16.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/releases/v17/17-0.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/releases/v17/17-2.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/releases/v17/17-3.mdx (via FormViewSchema (symbol, a top-level const))
  • content/docs/releases/v17/17-4.mdx (via visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/releases/v17/17-5.mdx (via retiredAfter (symbol, a field of const object pageComponentVisibilityToVisibleWhen; a field of const object viewVisibleOnToVisibleWhen), retiredFromLoadPath (symbol, a field of const object pageComponentVisibilityToVisibleWhen; a field of const object viewVisibleOnToVisibleWhen), visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/releases/v17/17-6.mdx (via visibleOn (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line), visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))
  • content/docs/releases/v17/17-7.mdx (via PageComponentSchema (symbol, a top-level const), visibleWhen (literal, a string literal in FORM_PREDICATE_KEYS; a string literal in FormSectionSchema; a string literal in PREDICATE_KEYS; a string literal in a comment on a changed line))

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
  • 8 changed file(s) yielded no anchor (packages/spec/authorable-surface/ui.json, packages/spec/dropped-refinements.baseline.json, packages/spec/src/migrations/entries/retired-keys/18.ui__FormField__visibleOn.ts, …) — pages documenting those are invisible to this run
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 143 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 3c080a65c8c17af97403fed1c26cddac05f43a6a → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 92db036a7d81e93e164da603c7d0a690cb5d8a3e — the merge of head 860f4a18bee5da9ea84a43fb40557d1a5300cd2a into base 3c080a65c8c17af97403fed1c26cddac05f43a6a, 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 92db036a7d81e93e164da603c7d0a690cb5d8a3e && git checkout 92db036a7d81e93e164da603c7d0a690cb5d8a3e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3c080a65c8c17af97403fed1c26cddac05f43a6a 860f4a18bee5da9ea84a43fb40557d1a5300cd2a && git checkout -B drift-repro 3c080a65c8c17af97403fed1c26cddac05f43a6a && git merge --no-ff 860f4a18bee5da9ea84a43fb40557d1a5300cd2a

node scripts/docs-audit/affected-docs.mjs --json 3c080a65c8c17af97403fed1c26cddac05f43a6a

⚠️ 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 3c080a65c8c17af97403fed1c26cddac05f43a6a → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Rendered 2026-10-16T08:34Z · adversarial read of PR #23836 for card #23543, family F4 of ruling 6182001882 item 4 · read-only, no local run.

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 24622be1326da4c41cf38936e39e4aba3c76abdc
Local-runs: none
Scope: packages/spec/src/conversions/registry.ts, packages/spec/src/migrations/entries/retired-keys/18.ui__FormField__visibleOn.ts, packages/spec/src/migrations/entries/retired-keys/18.ui__FormSection__visibleOn.ts, packages/spec/src/migrations/entries/retired-keys/18.ui__PageComponent__visibility.ts, packages/spec/src/migrations/entries/semantic/18.ui-visibility-alias-keys-retired.ts, packages/spec/src/shared/visibility-strict-options.ts, packages/spec/src/shared/visibility.ts, packages/spec/src/ui/component.zod.ts, packages/spec/src/ui/page.zod.ts, packages/spec/src/ui/view.zod.ts, skills/objectstack-ui/references/react-blocks.md + packages/spec/src/conversions/apply.ts, packages/spec/src/conversions/stored.ts, packages/spec/src/migrations/chain.ts, packages/spec/src/migrations/types.ts, packages/spec/src/shared/retired-key.ts, packages/spec/src/migrations/entries/semantic/18.visibility-strict-options-unexported.ts, packages/spec/authorable-surface/ui.json, packages/spec/dropped-refinements.baseline.json, packages/lint/src/validate-visibility-predicates.ts, packages/lint/src/validate-predicate-path-refs.ts, packages/lint/src/authoring-rules.ts, packages/lint/src/page-envelope-audit.ts, packages/lint/src/rule-explanations.ts, packages/metadata-core/src/form-predicate-root-policy.ts, packages/metadata-core/src/artifact-forward-conversion.ts, packages/metadata/src/plugin.ts, packages/metadata-protocol/src/protocol.ts, packages/cli/src/utils/author-time-rules.ts, packages/cli/src/commands/lint.ts, packages/cli/src/commands/validate.ts, packages/cli/src/commands/compile.ts, packages/cli/src/commands/migrate/meta.ts, the re-pointed suites (packages/cli/src/commands/validate-json-strict-exit.e2e.test.ts, packages/cli/test/stack-conversion-record-door.test.ts, packages/metadata-core/src/artifact-forward-conversion.test.ts, packages/spec/src/stack-conversions-record.test.ts), the pins (packages/spec/src/conversions/conversions.test.ts, packages/spec/src/conversions/view-spelling-walk.test.ts, packages/spec/src/shared/retired-key-removal-version.pin.test.ts, packages/spec/src/ui/visibility-alias-keys-retirement.test.ts, packages/spec/src/ui/view.test.ts, packages/spec/src/ui/page.test.ts, packages/lint/src/authoring-rule-input-tier.test.ts, packages/lint/src/validate-visibility-predicates.test.ts, packages/lint/src/validate-predicate-path-refs.test.ts, packages/lint/src/page-envelope-audit.test.ts, packages/metadata-core/src/form-predicate-root-policy.test.ts), .changeset/23543-f4-visibility-alias-keys-retired.md. The other 10 files of the 52 (docs, fixtures re-spelled, ledgers) are the lane seat's ACCEPT at TIER_DEFAULT, read here only where a hunk above claims them.

Inputs: card #23543 body and all 26 comments (the ruling 6182001882 items 4 and 7, F5-jsx's record 6189368882, F3's record 6205630082, F2's landing 6209157268, the F4 claim 6209225553, the dev report 6210819298); the PR body and 52-file list; the net diff against main (merge-base 43567a8fcb); the 41 check-runs on the head; the cited code at the head through git show.

Check-runs on the head at this read: 41 total, 36 success, 5 skipped (Console Pin Gate, the dogfood shard matrix, Dogfood Verify CLI, packed-tarball smoke, temporal conformance — all opt-in or path-gated), 0 failed, 0 in progress. Test Core 3/6 and 4/6 were in progress at the first read and completed green by 2026-10-16T08:24Z. Governed Surface Queue Guard, Check Changeset, Spec property liveness, every Lint and Type Check job: success.

① Derived judgments

  1. The accept set moved at exactly three positions, and no other. On main the authorable surface declared the two keys at ui/FormField:visibleOn, ui/FormSection:visibleOn and ui/PageComponent:visibility (base authorable-surface/ui.json lines 550, 562, 1100) and nowhere else; a key-position sweep of every *.ts under packages/spec/src at the head finds no fourth predicate-shaped declaration — the other hits are unrelated value keys (marketplace / registry-config / view-switcher visibility, the earlier-retired agent visibility) or guidance maps that already refuse the spelling (object.zod.ts:1217, app.zod.ts:1088, component.zod.ts:894-895). All three are now retiredKey() tombstones (view.zod.ts:3544, :3829; page.zod.ts:691), so the surface rows read [RETIRED]. The hunks in view.zod.ts and page.zod.ts touch only those keys, the section derivedKeys list and prose; derivedKeys losing 'visibleOn' is right — the tombstone aborts the section parse before the group-reference refinement runs, which section-group-reference.test.ts now pins as exactly one issue at sections.0.visibleOn. Right.
  2. The prescriptions say what the ruling's letter requires. Each names visibleWhen, the removing release (@objectstack/spec 18), ADR-0089 D2, the one-line fix (rename; CEL value unchanged) and the both-written case (delete the old key); the [REMOVED] description channel and the parse channel carry the same text (the removal-version pin's tombstone() renderer reads both and derives 18 from retiredAfter: '17.7.0'). No os migrate meta sentence, correctly: retired-key.ts's house sentence binds a surface a chain step replays, and these entries are toMajor: 15, below MIGRATION_SUPPORT_FLOOR = 16 — the pin's --from rule would demand --from 14, which applyMetaMigrations throws on (chain.ts, MigrationFloorError). The F5-jsx shape. The VISIBILITY_STRICT_OPTIONS prescription no longer offers the two as "still accepted"; view.test.ts pins that. Right.
  3. The pipes are gone and the parsed output of a visibleWhen-only body is unchanged. normalizeVisibleWhen returned its input minus the aliases, so with only visibleWhen present the fold was the identity; FormField still omits visibleOn from the output type; the CONTROL cases in view.test.ts and page.test.ts pin the canonical output carrying no alias key. Readers of the pipe's input side: the five tests and buildObjectFormSectionFieldEntry (component.zod.ts:6727) moved to .shape; a head-wide grep finds no other hard-coded .in read of these three schemas — the remaining def.in readers (component-field-specs-liveness.test.ts, protocol.ts:857, zod-graph.ts) branch on type === 'pipe' and are unaffected. The dropped-refinements baseline moved 13 sites off .in and one site one level shallower (763 → 764), and check:generated is green on the head. A slotted page node's type refusal now arrives at slots.header.type rather than inside an invalid_union — a better-located public error shape, pinned in component.test.ts and named in the changeset. objectui's sectionFields.spec-parity.test.ts reads SpecFormFieldSchema.in.shape and is the follow-up card's, as the dev measured. Right.
  4. The D3 entry is TRUE against the code. applyMetaMigrations replays only step.conversionIds of the hops above --from (chain.ts:218); the two entries sit in no step (the new test pins MIGRATIONS_BY_MAJOR contains neither id), so os migrate meta --from 17 applies no edit and --write writes none; the post-chain refusal listing the entry text describes exists (migrate/meta.ts:725, "Migrated stack does not yet pass schema validation"); the entry carries no relevantWhen, so it is always listed. Stored rows: applyConversionsToStoredItem pins includeRetired: true (stored.ts:107) and the runner skips a retired entry only when !includeRetired (apply.ts:156); the protocol's read path routes every non-flow row through it (protocol.ts:6519); visibility-alias-keys-retirement.test.ts pins a stored view row and a stored page row loading renamed and parsing. The artifact door opens the retired window per entry off retiredAfter (artifact-forward-conversion.ts, idsTheFloorPostdates): a ^17.x floor on an 18 runtime is converted-forward and replays the whole chain; an 18 floor is closed and meets the tombstone — "inside its window" is the exact claim. retiredAfter: '17.7.0' matches the F1 / F5-jsx / F3 / F2 stamps. Right.
  5. The readers — one door is mis-described, and the published text carries the mis-description. detectUnboundFormViewPredicateRoots: its only caller (packages/metadata/src/plugin.ts:1239) runs it on result.definition after applyArtifactForwardConversions and only when FORWARD_WINDOW_OPENED[result.verdict]; an artifact outside every window answers to the strict parse. FORM_PREDICATE_KEYS = ['visibleWhen'] loses nothing; pinned end to end in form-predicate-root-policy.test.ts. Right. validateVisibilityPredicates / validatePredicatePathRefs: os validate (validate.ts:349 parse, exit before :530), os build (compile.ts:408 → :552), os verify (judgeAuthorTimeRules), the per-package pass and the metadata save door (protocol.ts parses the view / page body as submitted, 422 on refusal, and runs assertRuntimeAuthoringRules after; its canonicalization-before-parse is flow-only) all hand them a parse-accepted body. os lint does not. lint.ts:681-683 calls runAuthoringRules('lint', { normalized: stack, parsed: lowered, … }) where lowered is lowerCallables(stack), not a schema parse, and author-time-rules.ts:82 states it: "'lint' never parses — it hands the rules the normalized stack on both tiers". With both D2 entries now retiredFromLoadPath, normalizeStackInput passes the retired key through — the PR's own pin asserts it (authoring-rule-input-tier.test.ts: expect(JSON.stringify(normalized)).toMatch(/"visibleOn"|"visibility"/)). So at the os lint door a raw visibleOn / visibility reaches both readers, and neither reads it: a value defect under the retired key — bare identifier, mis-layered root, non-CEL — that 17.7 reported at os lint (the live rename, the conversion notice, then the finding; the old premise-3 pin measured exactly that) is now reported nowhere at that door — no notice (entry retired), no refusal (door never parses), no finding (limb dropped). The code is still right by the ruling: item 7 leaves no double spelling, Prime Directive 12 forbids a consumer-side alias read, the two gating doors and strict defineStack refuse the key by name, and os lint's standing contract is to judge an unparsed stack where every refused shape is already invisible. What is wrong is the stated mechanism, and it is published: the changeset's @objectstack/lint paragraph says "Every door runs them on a body the spec parse accepted, which cannot carry either retired key"; the CANONICAL docblock in validate-visibility-predicates.ts says "the three CLI commands run the rule stage only after ObjectStackDefinitionSchema passes (judgeAuthorTimeRules)" — judgeAuthorTimeRules is os verify's stage, and its own module says the lint door never parses; validate-predicate-path-refs.ts's PREDICATE_KEYS docblock, the authoring-rule-input-tier.test.ts comment ("the parse every door runs before its rule pass") and the validate-visibility-predicates.test.ts describe comment repeat it. The dev's PR body names judgeAuthorTimeRules, run.parsed, the scaffold door and the save door and never measures os lint. This is the F2 class — text that promises what the tool does not do — in a changeset that becomes CHANGELOG text. Wrong: named defect, below. page-envelope-audit.ts (code unchanged, comments true, the negative control re-pointed to the parse-error channel with a canonical control) and protocol.ts (comments only; the graft fixture re-spelled) are right. packages/sdui-parser/src/validate.ts unchanged is acceptable: SDUI_BASE_PROPS is objectui's node vocabulary, not PageComponentSchema, and moves with the objectui card.
  6. The kept exports' docblock is true. A head-wide grep finds no caller of normalizeVisibleWhen or VISIBILITY_ALIAS_KEYS outside visibility.ts (only the api-surface / export-origins listings and prose); "no schema in this package calls the fold any more" and "remain published, with no caller in this repository" hold. The acceptance note is also true: 18.visibility-strict-options-unexported.ts says the two "stay exported", so retiring them is a separate decision that rewrites that entry. Three analogy mentions survive outside the diff (view.zod.ts:3949, public-auth-features.ts:321, and a stale "fold them via" in visible-when-alias-guidance.test.ts:297); none is load-bearing — the last may ride the patch round. Right.
  7. Re-pointed suites and the verdict-list pin. conversions.test.ts keeps exactly flow-node-http-callout-rename and lists the two among the retired with the stamp; view-spelling-walk.test.ts gains the retired-but-replayed statement; the removal-version JOIN gains the three tombstone rows; no suite outside the stored seam still drives either entry as a live subject (head-wide grep). The probe in stack-conversions-record.test.ts is file-scoped (vi.mock of the registry) with toMajor: 15, so every pinned retiresIn: 16 holds. Right.

② Semver level

@objectstack/spec: minor with feat(spec)! and a BREAKING paragraph — the house form on the 18.0.0-next train that F1 / F5-jsx / F3 / F2 used and check-changeset-no-major requires; @objectstack/lint: patch (two readers narrowed to the canonical key, one explanation string, comments) and @objectstack/metadata-core: patch (one reader narrowed) match what the diff publishes. metadata-protocol changes are comments and a test fixture; cli and qa/dogfood changes are tests and a ledger; the generated skills/objectstack-ui/references/react-blocks.md publishes nothing. Check Changeset is green. The adr-0087: registered ui-visibility-alias-keys-retired marker names the D3 entry file that exists at the head, with no conversionIds and no STEP18_RATIONALE, as a below-floor retirement should. Clause-②: yes (narrowing) agrees across the claim (6209225553, "Clause-②: yes (narrowing) per ruling item 7"), the PR body line 2 and the changeset. Right.

③ Boundary flags

  1. Widened surface (stack-conversions-record probe, page-envelope audit, protocol comments and graft fixture, the baseline, five .in readers, component.zod.ts, ledgers, prose): each a measured reader or live subject, each with its reason in the PR body. Accepted.
  2. Governed path. skills/objectstack-ui/references/react-blocks.md is regenerated (any[] → object[], 115 → 115 lines, SKILL.md set unchanged). The register decides its tier, and one governed hit governs the PR whole: this record lifts nothing on a Tier H path; the owning seat requests the maintainer's word or, if the queue leg regenerates that row byte-exact, lets the guard clear it — the guard's reading on the head is green and is the authority. Not a defect.
  3. The three suites re-pointed to flow-node-http-callout-rename rather than given a probe. The ruling's letter is "the three record / strict-exit suites re-point", and re-point they did: FIRING CONTROL 1's claim is "a non-retired entry fires at this door", which only a live entry can carry without mocking the callee under test; the two cli suites spawn the CLI over a symlinked built spec, outside vitest's module graph (F2's reason for the migrate meta suites); the record suite, whose subject is the record, took the F2 probe route instead. Each reason is true against the code. F5-http inherits the three, which that family's dispatch order must name (open question 2). Matches the ruling.
  4. Open question 1 — retire normalizeVisibleWhen / VISIBILITY_ALIAS_KEYS? Zero callers in this repo (measured here) and, per the dev, in objectui; retiring them is an export removal that rewrites the 18 D3 entry visibility-strict-options-unexported. Escalated to the seat as its own follow-up card, option A recommended; not a rider on this PR.
  5. Open question 2 — the live-window controls after F5-http. Escalated to F5-http's dispatch: that PR either retires the controls whose claim is "a live entry fires" and converts the record-door suites to the refusal-carried record, or keeps a test-owned probe where vi.mock reaches; a test-only entry in the published registry (option B) is refused here as a precedent.
  6. objectui follow-up card: the seat files it at landing (F5-jsx precedent). The dev measured that no designer or Studio writer emits either key, so the ruling's p1 condition reads no; the card names the ~26 renderer reads, SchemaRenderer.tsx:191's type coupling, and the three spec-shape tests that break at the next spec bump. Carried forward.
  7. The metadata save door stays untouched (the seat's separate decision on spec(conversions): eight ADR-0087 D2 conversions stay on the 18 load path after #23487 — five families each need a ruling before they can retire #23543); for view and page it parses the body as submitted, so the old keys meet the 422 there — true at protocol.ts.
  8. Readings on 6e242061 for the non-spec packages: the merge of main changed one file of the 52 against that head (view.test.ts, other hunks), confirmed by git diff --stat; the CI run on the head covers all of them anyway.

Defect (the FAIL)

The published reason for narrowing the two lint readers is false at one door. os lint hands validateVisibilityPredicates and validatePredicatePathRefs the normalized, unparsed stack (packages/cli/src/commands/lint.ts:681-683; packages/cli/src/utils/author-time-rules.ts:82), and with the two D2 entries retired that stack still carries visibleOn / visibility; the readers now ignore it, so a value defect under a retired key is silent at os lint where 17.7 reported it. The code is right by ruling item 7; the text is not, and it ships: .changeset/23543-f4-visibility-alias-keys-retired.md ("Every door runs them on a body the spec parse accepted, which cannot carry either retired key"), packages/lint/src/validate-visibility-predicates.ts (CANONICAL docblock: "the three CLI commands run the rule stage only after ObjectStackDefinitionSchema passes (judgeAuthorTimeRules)"), packages/lint/src/validate-predicate-path-refs.ts (PREDICATE_KEYS docblock), packages/lint/src/authoring-rule-input-tier.test.ts and packages/lint/src/validate-visibility-predicates.test.ts (the describe comments).

Patch round (prose plus one pin, no code change): rewrite those five sites to name os lint as the one door that hands the readers an unparsed stack, and state the posture — there a retired key's predicate is nobody's verdict by design, because os validate, os build, defineStack and the metadata save door refuse the key by name, and os lint already sees no refused shape; keep the changeset sentence to the doors that parse and add the os lint sentence beside it. Give the sentence its pin: the authoring-rule-input-tier.test.ts normalized-tier case already measures the os lint input (validateVisibilityPredicates(normalized) → [] where visibleWhen → one finding) — label it as that door's reading, or add one runAuthoringRules('lint', …) case that says so. Re-review at the patched head renders the PASS; nothing in ① 1-4, 6-7, ② or ③ needs to move.

Implemented-by: claude/issue-23543-f4-visibility-keys-retired
Reviewed-by: session_01HjR8RUwTPQ3TgbfKihmxp9

VERDICT: FAIL


Generated by Claude Code

…lity key's predicate unjudged (F4 of #23543, review patch)

Claude-Session: https://claude.ai/code/session_014ruwnS8CSGGVA5UWfJA8xZ
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Rendered 2026-10-16T09:18Z · re-review at the patched head after FAIL 6211507698 on 24622be132 · PR #23836, card #23543, family F4 of ruling 6182001882 item 4 · read-only, no local run.

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 860f4a18bee5da9ea84a43fb40557d1a5300cd2a
Local-runs: none
Scope: packages/spec/src/conversions/registry.ts, packages/spec/src/migrations/entries/retired-keys/18.ui__FormField__visibleOn.ts, packages/spec/src/migrations/entries/retired-keys/18.ui__FormSection__visibleOn.ts, packages/spec/src/migrations/entries/retired-keys/18.ui__PageComponent__visibility.ts, packages/spec/src/migrations/entries/semantic/18.ui-visibility-alias-keys-retired.ts, packages/spec/src/shared/visibility-strict-options.ts, packages/spec/src/shared/visibility.ts, packages/spec/src/ui/component.zod.ts, packages/spec/src/ui/page.zod.ts, packages/spec/src/ui/view.zod.ts, skills/objectstack-ui/references/react-blocks.md + the six files the patch commit touches (.changeset/23543-f4-visibility-alias-keys-retired.md, packages/lint/src/validate-visibility-predicates.ts, packages/lint/src/validate-predicate-path-refs.ts, packages/lint/src/authoring-rule-input-tier.test.ts, packages/lint/src/validate-visibility-predicates.test.ts, packages/spec/src/shared/visible-when-alias-guidance.test.ts), read against the doors they now describe (packages/cli/src/commands/lint.ts, packages/cli/src/utils/author-time-rules.ts, packages/cli/src/commands/validate.ts, packages/cli/src/commands/compile.ts, packages/metadata-protocol/src/protocol.ts, packages/spec/src/stack.zod.ts, packages/lint/src/authoring-rules.ts), and the whole cited-file list of record 6211507698, which carries unchanged.

Inputs: the FAIL record 6211507698 and its inputs (card #23543 body and every comment, the ruling 6182001882 items 4 and 7, the F5-jsx / F3 / F2 records, the F4 claim 6209225553, the dev reports 6210819298 and this round's 6211629886); the PR body (byte-identical to the first read) and its file list; git diff 24622be132 860f4a18be (one commit, parent 24622be132, 6 files, +47/−22); the 41 check-runs on the new head; the cited code at 860f4a18be through git show.

Check-runs on the head at this read: 41 total, 36 success, 5 skipped (the same opt-in / path-gated five as on the previous head), 0 failed, 0 in progress; the last completed by 2026-10-16T09:14Z. Governed Surface Queue Guard, Check Changeset, every Lint, Type Check and Test Core job: success.

① Derived judgments

  1. The patch touches nothing the previous record's ① 1-4 and 6-7 judged, so those judgments carry. The diff between the two heads is six files: one changeset paragraph, two lint docblocks, two lint test files and one spec test comment. None of the eleven hits paths changed (the tombstones, the registry entries, the D3 entry and the retired-key entries, the two visibility helpers, component.zod.ts, react-blocks.md are byte-identical), nor apply.ts, stored.ts, chain.ts, retired-key.ts, form-predicate-root-policy.ts, the artifact door, plugin.ts, protocol.ts, or any of the pins and re-pointed suites those items cite. The accept set, the prescriptions, the pipes' removal, the D3 entry's truth, the kept exports' docblock and the verdict-list pin stand as judged. Carry.
  2. The five sites now state the doors truly. Each names the doors that parse first — os validate (validate.ts:349, exit before :530), os build (compile.ts:408 → :552), os verify (judgeAuthorTimeRules), defineStack and the metadata save door (protocol.ts, the view / page body parsed as submitted, 422 on refusal, rules after) — as the doors whose bodies cannot carry a retired key, and names os lint as the one door that hands the two readers an unparsed stack, which is what lint.ts:681-683 does (runAuthoringRules('lint', { normalized: stack, parsed: lowered, … }), lowered being lowerCallables(stack)) and what author-time-rules.ts:82 says ("'lint' never parses"). The CANONICAL docblock no longer attributes judgeAuthorTimeRules to "the three CLI commands"; it attributes it to os verify, where it lives. The posture stated at every site — a predicate under a retired key is nobody's verdict at os lint by design, because the parsing doors refuse the key itself and os lint reports no refused shape — is the reading ruling item 7 and Prime Directive 12 leave, and it is now the reading the text gives. One nuance, true as written: defineStack parses in its default strict mode; { strict: false } is the author's documented opt-out ("skips validation", stack.zod.ts:4144), and a stack built that way is exactly the unparsed input the os lint sentence now covers. Right.
  3. The new pin measures that door, non-vacuously. authoring-rule-input-tier.test.ts adds an it.each over the three retired-key sites that runs runAuthoringRules('lint', { normalized: normalizeStackInput(stack) }) and expects no visibility-* finding, with a CONTROL that the same value under visibleWhen at the same door yields exactly ['visibility-bare-identifier']. The shape is faithful: the validateVisibilityPredicates entry declares input: 'normalized' (authoring-rules.ts:1589), which is the tier lint.ts hands as normalized: stack, and runAuthoringRules reads run.parsed ?? run.normalized for the other tier, so passing normalized alone is the lint door's reading for this family. The file imports runAuthoringRules and normalizeStackInput already; CI's Test Core and Lint jobs are green on the head. The rewritten comment beside the earlier normalized-tier case ("the parsing doors refuse the key itself") is now true. Right.
  4. The changeset paragraph. The @objectstack/lint text keeps the parse-first claim to the doors that parse and adds the os lint sentence beside it; the semver header (spec: minor, lint: patch, metadata-core: patch), the BREAKING paragraph, the FROM → TO table and the adr-0087: registered ui-visibility-alias-keys-retired marker are untouched. Right.
  5. The stale analogy in visible-when-alias-guidance.test.ts:297 ("fold them via normalizeVisibleWhen") now reads "folded them until both retired at 18" — true. The two analogy mentions in view.zod.ts:3949 and public-auth-features.ts:321 remain true as function references. Right.

② Semver level

Unchanged by the patch and carries from 6211507698: @objectstack/spec: minor BREAKING (the house form on the 18.0.0-next train), @objectstack/lint: patch, @objectstack/metadata-core: patch; the marker names the D3 entry file at the head; Clause-②: yes (narrowing) agrees across the claim, the PR body line 2 and the changeset. Check Changeset is green on the new head. Right.

③ Boundary flags

  1. The defect of 6211507698 is closed at the five sites plus the pin, with no code change — the patch round the record asked for, and nothing beyond it.
  2. The PR body's Mechanism item 5 still carries the round-1 sentence ("Every door that runs these rules hands them a body the spec parse accepted…"); the body is byte-identical to the first read. A PR-body write is the seat's, not the dev's (the dev proposes the wording in 6211629886's api_writes); it is outside the diff and the changeset and is no defect of the delivery. Carried to the seat: amend item 5 to the patched wording before landing, so the body and the CHANGELOG say the same thing.
  3. Everything else carries: the governed-path note on skills/objectstack-ui/references/react-blocks.md (unchanged by the patch; the register and the guard decide the tier, this record lifts nothing on a Tier H path); the three suites re-pointed to flow-node-http-callout-rename matching the ruling's "re-point" letter; open question 1 (retire the two kept exports — the seat's follow-up card, option A) and open question 2 (the live-window controls after F5-http — that family's dispatch, option A); the objectui follow-up card the seat files at landing; the metadata save door as the seat's separate decision. The dev's dist-gated skips this round (five skipIf lazy-deps cases in an unbuilt worktree) are explained and covered by CI's own run.

Implemented-by: claude/issue-23543-f4-visibility-keys-retired
Reviewed-by: session_01HjR8RUwTPQ3TgbfKihmxp9

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

objectstack-fleet Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT (Tier H, the maintainer's hand) · PR #23836 @ 860f4a18be (#23543, family F4) · domain:spec seat 1 · os-elon-musk · session session_01HjR8RUwTPQ3TgbfKihmxp9 · 2026-10-16T09:23Z

Reviewer of record. Every item below was checked against GitHub and the code, not against the reports (6210819298 and the patch round's 6211629886 on #23543).

  • Shape:
    • Draft, base main. The first line is Part of #23543, with no closing keyword, then Clause-②: yes (narrowing), matching claim 6209225553 and the changeset.
    • 53 files, +1197/−679 at this head, under the 3000-line threshold. Assignee os-elon-musk. (Corrected in place: the first post of this record said 52 files and +1194/−700, which were not read off the PR object.)
  • Tier H: check-governed-merges --pr 23836 reads 1 of 53 paths on the register (skills/**): skills/objectstack-ui/references/react-blocks.md, regenerated by gen:react-blocks (sections: any[] → object[], because the removed pipe had hidden the element type). The generated-surface exception does not lift it. It cannot be split out without reddening check:generated, so the PR lands by the maintainer's hand. ⛔ The seat does not ready, queue or arm it.
  • Ruling item 4, read in the diff:
    • The three positions that declared the old keys on main are retiredKey() tombstones naming visibleWhen: FormFieldSchema.visibleOn, FormSectionSchema.visibleOn and PageComponentSchema.visibility. The three normalizeVisibleWhen pipes are gone. A visibleWhen-only body parses to the same output.
    • RETIRED_KEYS_BY_MAJOR[18] gains the three keys. Both conversions are retiredFromLoadPath with retiredAfter: '17.7.0'.
    • The D3 entry ui-visibility-alias-keys-retired is registered and says only what the tools do: os migrate meta --from 17 applies no edit (protocol 15 is below the floor), and stored rows keep loading through the retired conversions.
    • The changeset is a BREAKING minor with FROM → TO.
    • The three record / strict-exit suites re-point, as item 4 says.
  • Contract review:
    • Round 1, FAIL 6211507698 on 24622be132, with one named defect. The published text claimed every door hands the lint visibility readers a parsed body; os lint does not.
    • Patch round 860f4a18be: prose at five sites plus one pin, no code change.
    • Round 2, PASS 6212287597 on this head. ①, ② and ③ otherwise carry.
  • PR body: the seat amended Mechanism item 5 to the patched wording, as the re-review asked. The 维护者速读 section is filled in, 席位意见 included.
  • The report's open questions, the seat's calls:
    • Retiring the uncalled normalizeVisibleWhen / VISIBILITY_ALIAS_KEYS exports: A. The seat files it as its own card at landing.
    • The live-window controls after F5-http: A, decided in F5-http's dispatch. A test-only registry entry is refused.
  • CI on this head: 36 success, 5 skipped by their filters, 0 failed.
  • Owed at landing:
    • the objectui follow-up card: renderer reads, the SchemaRenderer.tsx:191 type coupling, and three spec-shape tests that red at objectui's next spec bump. The ruling's p1 condition reads NO, because no designer writes either key;
    • the export-retirement card;
    • the first hourly Spec Shape Gate after the merge, read and, if red, registered;
    • Release: back to pm:queue for F5-http.

Generated by Claude Code

This branch has not been deployed

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

Labels

documentation Improvements or additions to documentation protocol:ui size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants