Skip to content

feat(spec)!: unexport normalizeVisibleWhen and VISIBILITY_ALIAS_KEYS from @objectstack/spec/shared; read visibleWhen directly - #24004

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-23877-visibility-fold-unexported
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-23877-visibility-fold-unexported

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23877

Clause-②: yes

What

@objectstack/spec/shared stops exporting normalizeVisibleWhen and VISIBILITY_ALIAS_KEYS. They were the ADR-0089 fold of the retired visibleOn / visibility keys onto visibleWhen, and the list of those two keys. Since the two keys became retiredKey() tombstones at 18, no schema called the fold. The replacement is to read visibleWhen directly.

  • packages/spec/src/shared/visibility.ts is deleted. The two exports were all it held (plus the private WithVisibilityAliases type and the docblocks that describe them), so the export * from './visibility' line leaves the shared barrel too.
  • api-surface/shared.json and export-origins/shared.json were regenerated with gen:api-surface / gen:export-origins. Each loses exactly the two rows. api-surface-signatures.json and declaration-map/ never listed either name, so they did not move. check:authorable-surface did not move either, because no schema key changed.
  • ADR-0087 D3: the new semantic entry visibility-alias-fold-unexported follows the shape of visibility-strict-options-unexported, with no conversionIds (a TS surface, never stored). That older entry's replacement said the two "stay exported". It now points at the new entry.
  • Comments that named a removed symbol now name the mechanism instead: kernel/public-auth-features.ts (the requiresFeature lowering) and ui/view.zod.ts (the groups → sections fold).
  • A pin, shared/visibility-fold-unexported.test.ts: the shared barrel's runtime namespace does not carry either name. A surviving export (lazySchema) is the lit control.
  • The changeset is BREAKING minor on @objectstack/spec with Clause-②: yes (narrowing), a FROM → TO table and the marker registered visibility-alias-fold-unexported. It also says it supersedes the pending F4 note's "stay exported" sentence (see Acceptance notes).

Not touched: the stored-row conversions view-visibleOn-to-visibleWhen and page-component-visibility-to-visibleWhen, any schema's accept set, and objectui.

Zero pull, measured

  • objectstack @ 2e1562091c (git grep over the whole tree, CHANGELOGs and dist excluded): there are no call sites. The only hits were the definition, the two baselines, the two precedent comments, the D3 entry 18.visibility-strict-options-unexported.ts:16-17, and one sentence of the pending F4 changeset (see Acceptance notes).
  • objectui @ the .objectui-sha pin f8267df93a: there is one hit, a comment (packages/core/src/utils/normalize-list-view.ts:374), and no call. The positive control visibleWhen hits in the same tree.

Ablation

The fix was committed first. The mutation went through scripts/ablation-replace.mjs with a trap restore. It re-adds export const VISIBILITY_ALIAS_KEYS = … to shared/index.ts. Result: the anchor went 1 → 0, the blob went e2d66ff65e0b → eee4d0b060de, and no longer exports VISIBILITY_ALIAS_KEYS went red (AssertionError: expected true to be false, 1 failed | 2 passed). After the restore, the blob equals HEAD and git diff HEAD is empty. The first attempt was a no-op because its replacement still contained the anchor; the tool refused it before running anything. The pin imports ./index from source, so no rebuild applies.

Verification (head 9d59f55e, after merging origin/main; the final head 5626f603 adds two changeset lines on top of it)

  • pnpm --filter @objectstack/spec build passes. check:generated reports all 14 artifacts up to date (before regeneration it flagged exactly api-surface/ and export-origins/, ./shared − the two names).
  • pnpm --filter @objectstack/spec typecheck exits 0.
  • pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2: 755 files, 22018 passed, 1 todo.
  • node scripts/check-adr-0087-registration.mjs passes: [BREAKING+bang+clause-②-narrowing] registered visibility-alias-fold-unexported (new here).
  • node scripts/check-platform-checklist.mjs --changed-from origin/main reports OK: 24 anchors judged, 0 lost.
  • dispatch-gates --commands derived 88 families, all run, with exit codes captured before any pipe. 87 exited 0. One was NOT MEASURED (check-plugin-teardown-shape --self-test, exit 3: its fixture commit is beyond this shallow clone). --ran reconciled 88/88.
  • Re-run on 5626f603 for the changeset text: check-adr-0087-registration, check-changeset-no-major, check-empty-changeset, check:doc-authoring, check:issue-citations, check:nul-bytes, check:pm-changeset-deadline-census, check:objectui-changeset, check:published-files. All exited 0.

Acceptance notes

  • packages/spec/src/data/field.zod.ts:10 had a comment saying editability-boundary's only runtime import was shared/visibility.ts. In fact it is shared/visibility-strict-options.ts, and the deleted file would have left the comment pointing nowhere. The comment was re-pointed in place (one line, comment only). That file is outside the claim's listed surface, which is why this note exists.
  • The pending (unreleased, not in pre.json) changeset .changeset/23543-f4-visibility-alias-keys-retired.md:42 says normalizeVisibleWhen and VISIBILITY_ALIAS_KEYS "stay exported", which this PR makes false for the same release. Correcting it in place makes check-empty-changeset red by design (it classes this as a deliberate correction to another PR's release note, which needs a person to confirm), and the file is outside this claim's surface. So this PR leaves it as authored, and its own changeset says it supersedes that sentence. If the seat wants it corrected in place, the one-line rewrite is: drop the first clause, and say the two leave @objectstack/spec/shared in a separate change of the same release (D3 entry visibility-alias-fold-unexported).
  • packages/spec/scripts/build-skill-references.ts:274 mentions visibility.ts in a historical note about leaked transitive imports. It names no symbol and stays as is.

Generated by Claude Code

…from @objectstack/spec/shared (WIP: baselines pending)

Claude-Session: https://claude.ai/code/session_01T2YHPgcuCRcypzyAuxvAap
Co-authored-by: Claude <noreply@anthropic.com>
…elines; add the BREAKING changeset

Claude-Session: https://claude.ai/code/session_01T2YHPgcuCRcypzyAuxvAap
Co-authored-by: Claude <noreply@anthropic.com>
…'s 'stay exported'

Claude-Session: https://claude.ai/code/session_01T2YHPgcuCRcypzyAuxvAap
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 1 package(s): @objectstack/spec, touching 7 documentable anchor(s). ⚠️ 6 changed file(s) yielded no anchor (packages/spec/api-surface/shared.json, packages/spec/export-origins/shared.json, packages/spec/src/data/field.zod.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

12 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 (symbol, a field of type WithVisibilityAliases))
  • content/docs/automation/flows.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/data-modeling/field-types.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/data-modeling/fields.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/data-modeling/formulas.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/data-modeling/schema-design.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/data-modeling/validation-rules.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/deployment/environment-variables.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/protocol/objectui/concept.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/protocol/objectui/layout-dsl.mdx (via visibleOn (symbol, a field of type WithVisibilityAliases), visibleWhen (symbol, a field of type WithVisibilityAliases), visibleOn (literal, a string literal in VISIBILITY_ALIAS_KEYS; a string literal in normalizeVisibleWhen; a string literal on a changed line))
  • content/docs/ui/pages.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/ui/views.mdx (via visibleOn (symbol, a field of type WithVisibilityAliases), visibleWhen (symbol, a field of type WithVisibilityAliases), visibleOn (literal, a string literal in VISIBILITY_ALIAS_KEYS; a string literal in normalizeVisibleWhen; a string literal on a changed line))

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

  • content/docs/releases/implementation-status.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/releases/v12.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/releases/v15.mdx (via visibleOn (symbol, a field of type WithVisibilityAliases), visibleWhen (symbol, a field of type WithVisibilityAliases), visibleOn (literal, a string literal in VISIBILITY_ALIAS_KEYS; a string literal in normalizeVisibleWhen; a string literal on a changed line))
  • content/docs/releases/v16.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/releases/v17/17-0.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/releases/v17/17-2.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/releases/v17/17-4.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/releases/v17/17-5.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))
  • content/docs/releases/v17/17-6.mdx (via visibleOn (symbol, a field of type WithVisibilityAliases), visibleWhen (symbol, a field of type WithVisibilityAliases), visibleOn (literal, a string literal in VISIBILITY_ALIAS_KEYS; a string literal in normalizeVisibleWhen; a string literal on a changed line))
  • content/docs/releases/v17/17-7.mdx (via visibleWhen (symbol, a field of type WithVisibilityAliases))

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
  • 6 changed file(s) yielded no anchor (packages/spec/api-surface/shared.json, packages/spec/export-origins/shared.json, packages/spec/src/data/field.zod.ts, …) — pages documenting those are invisible to this run
  • 4 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 — 142 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 19955fb73fe58d7971a1f0f1fc9c473f2dfdf179 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 916130d4e432f1abcbd5da55b0157335ec5ad760 — the merge of head ca04d47854bbde1abaf80803e85fc07aeb158e7d into base 19955fb73fe58d7971a1f0f1fc9c473f2dfdf179, 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 916130d4e432f1abcbd5da55b0157335ec5ad760 && git checkout 916130d4e432f1abcbd5da55b0157335ec5ad760
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 19955fb73fe58d7971a1f0f1fc9c473f2dfdf179 ca04d47854bbde1abaf80803e85fc07aeb158e7d && git checkout -B drift-repro 19955fb73fe58d7971a1f0f1fc9c473f2dfdf179 && git merge --no-ff ca04d47854bbde1abaf80803e85fc07aeb158e7d

node scripts/docs-audit/affected-docs.mjs --json 19955fb73fe58d7971a1f0f1fc9c473f2dfdf179

⚠️ 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 19955fb73fe58d7971a1f0f1fc9c473f2dfdf179 → 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

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5626f603058f7a0b61fa840670f2ffbc79215630
Local-runs: none
Scope: packages/spec/src/data/field.zod.ts, packages/spec/src/kernel/public-auth-features.ts, packages/spec/src/migrations/entries/semantic/18.visibility-alias-fold-unexported.ts, packages/spec/src/migrations/entries/semantic/18.visibility-strict-options-unexported.ts, packages/spec/src/shared/index.ts, packages/spec/src/shared/visibility.ts, packages/spec/src/ui/view.zod.ts + .changeset/23877-visibility-alias-fold-unexported.md, .changeset/23543-f4-visibility-alias-keys-retired.md, .changeset/pre.json, packages/spec/api-surface/shared.json, packages/spec/export-origins/shared.json, packages/spec/src/shared/visibility-fold-unexported.test.ts, packages/spec/src/shared/editability-boundary.ts, packages/spec/src/shared/visibility-strict-options.ts, packages/spec/src/ui/page.zod.ts, packages/spec/src/api/automation-api.zod.ts, packages/spec/src/conversions/registry.ts, packages/spec/src/conversions/conversions.test.ts, packages/spec/src/index.ts, packages/spec/package.json, packages/spec/tsup.config.ts, packages/spec/scripts/build-migration-registry.ts, scripts/check-adr-0087-registration.mjs, scripts/check-empty-changeset.mjs, docs/adr/0089-unify-visibility-predicate-naming.md, objectui packages/core/src/utils/normalize-list-view.ts at the pin f8267df93a

Read-only: the net diff is git diff adce192974..5626f603 (merge-base of the head with origin/main, 11 files, +107/−86, four commits on the branch of which 9d59f55e is the merge of origin/main); the card's body and its three comments (6215941821, 6220012215, 6221253902); the F4 ruling 6182001882 item 4 and F4's landed changeset; the check-runs on the head, read at 2026-10-16T18:27Z: 41 runs, 33 success, 8 skipped, 0 failed or in progress — Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core and Governed Surface Queue Guard all success. Every gate family a local run could answer is answered by those conclusions.

① Derived judgments

  1. Exactly the two names leave @objectstack/spec/shared, and nothing else — RIGHT. api-surface/shared.json loses exactly two rows (VISIBILITY_ALIAS_KEYS (const), normalizeVisibleWhen (function)) and gains none; export-origins/shared.json loses the same two, both resolving to src/shared/visibility.ts. At the merge base no other baseline shard listed either name (api-surface-signatures.json, declaration-map/, the root api-surface shard): the root entry src/index.ts re-exports shared members by name (lines 224-228, 284-294), never wholesale, and neither name is among them. The deleted module held exactly those two exports plus the unexported type WithVisibilityAliases; shared/index.ts:32 was its only re-export in the tree.
  2. Deleting the module and its barrel line removes nothing else a consumer reads — RIGHT. packages/spec/package.json publishes ./shared as dist/shared/index.* and no deeper subpath; tsup.config.ts:162 names src/shared/index.ts as the entry, never visibility.ts; no ADR anchor under scripts/adr-anchors/ names shared/visibility. A git grep over the whole head tree (dist and CHANGELOGs excluded) finds no importer of the module and no reference to either name outside the two changesets, the two D3 entries and the new pin; the one mention of the filename left is a historical note at packages/spec/scripts/build-skill-references.ts:274, which names no symbol. The barrel line's removal is forced rather than a widening of the claim: an empty module behind export * is TS2306.
  3. No schema's accept set moves — RIGHT. The three src edits outside shared/ are comment-only hunks (field.zod.ts:10, public-auth-features.ts:319-323, view.zod.ts:3958-3961); no *.zod.ts shape, tombstone or strictObject option changes; check:authorable-surface did not move and the job carrying it is green.
  4. The stored-row conversions are untouched — RIGHT. view-visibleOn-to-visibleWhen (conversions/registry.ts:535) and page-component-visibility-to-visibleWhen (:646) are not in the diff, and neither is the pin at conversions/conversions.test.ts:443-444 that holds both at retiredFromLoadPath: true, retiredAfter: '17.7.0' (the verdict list the F4 ruling's item 7 names). The new D3 entry carries no conversionIds, which is the right shape for a TS surface no stored row ever held.
  5. The re-worded comments say only what is true — RIGHT. public-auth-features.ts: "the same schema-boundary fold onto one canonical key that ADR-0089 ran for visibleOn / visibility → visibleWhen through 17" matches ADR-0089 D2 (aliases "normalized at parse" by a zod transform) and the F4 changeset ("through 17 … the parse folded onto visibleWhen"). view.zod.ts: "the whole-array counterpart of the per-node … fold ADR-0089 ran at the schema boundary through 17" rests on the same two facts. field.zod.ts:10: editability-boundary.ts:60-62 carries two import type lines and one runtime import, of ./visibility-strict-options, whose only import is import type { StrictObjectOptions } — so "its only runtime import is shared/visibility-strict-options.ts, which imports nothing at runtime" is true, and the base text naming shared/visibility.ts was already false at the merge base. The new D3 entry's claims check against the tree: the three tombstones exist (view.zod.ts:3555, :3840, page.zod.ts:690), the three RETIRED_KEYS_BY_MAJOR[18] rows exist, and "a stored row is renamed by the rehydration conversions" is what the F4 changeset says. The rewritten sibling entry now states the removal instead of "stay exported".
  6. The registration holds — RIGHT. build-migration-registry.ts discovers entries by readdirSync of the directory (:267) and requires the filename to equal 18. + id + .ts (:182, :246): 18.visibility-alias-fold-unexported.ts with id: 'visibility-alias-fold-unexported' satisfies it, and the semantic:18 region exists. check-adr-0087-registration's registered rule requires every named id to resolve at the head and at least one to be new in the diff; this entry is new in the diff, so registered visibility-alias-fold-unexported is the honest disposition, and the route is the one the sibling removal took (visibility-strict-options-unexported, changeset pre/5082-…) rather than type-surface-only or runtime-interface-only.
  7. The pin is adequate. visibility-fold-unexported.test.ts reads ./index through the runtime namespace, uses lazySchema as the lit control, and asserts name in shared is false for both names; the type-level absence is held by check:api-surface against the regenerated baseline.
  8. Zero pull in objectui — RIGHT. .objectui-sha is f8267df93a at the head and on origin/main; at that commit the only hit for either name is one docblock sentence (packages/core/src/utils/normalize-list-view.ts:374), no import; the positive control visibleWhen hits 381 files. No objectui change and no pin bump are owed, and Console Pin Gate is not at risk.

② Semver level

The changeset declares '@objectstack/spec': minor with a bang title, Clause-②: yes (narrowing), a FROM → TO table, the one-line fix, the failure mode, and the adr-0087 marker registered visibility-alias-fold-unexported. That is what the diff publishes: a BREAKING TS/API narrowing of @objectstack/spec/shared and nothing from any other released package. yes takes at least minor, (narrowing) is BREAKING, and check-changeset-no-major forbids major — the F4 precedent is minor for the same reason. The PR body's Clause-②: yes and the changeset's Clause-②: yes (narrowing) agree; the arm lives in the changeset, where the gate reads it. .changeset/pre.json is mode: pre, tag next, with no consumed changesets, so this changeset and F4's land in the same @objectstack/spec version — the "same release" the changeset's supersede sentence asserts.

③ Boundary flags

  • Deviation: shared/index.ts loses its barrel line. Accepted — forced by TS2306 once the module is empty; ① item 2 shows nothing else leaves.
  • Deviation: field.zod.ts:10 comment re-pointed. Accepted — verified true in ① item 5; the old text pointed at a file this PR deletes and was already wrong.
  • Deviation: the shared checkout's branch was detached, deleted and recreated in a worktree. Process only; the branch was at origin/main when deleted, and the net diff carries no residue of it.
  • Deviation: the PR body ends with the session-URL footer alone. That is the form the rules prescribe for a PR body. Accepted.
  • Deviation: a local revert attempt that did not land. Verified from the remote: the F4 changeset is byte-identical to the merge base at the head (empty diffstat), and the four branch commits net to the 11-file diff the body describes. Nothing wrong reached the remote.
  • Open question 1 — the F4 changeset's "stay exported" sentence. Judged: option A stands, and no reader is stranded either way. What the reader of the 18 notes sees: both changesets are consumed by the same changeset version run (pre mode, nothing consumed yet), so the @objectstack/spec CHANGELOG block for that version carries both bodies verbatim — F4's "Not part of it: … stay exported" and, in the same block, this changeset's "This supersedes the sentence in this release's visibleOn / visibility retirement note … they leave in the same release"; a reader who greps either name lands on both entries in one version and the second resolves the first. The central release notes are compiled by hand from the changesets and the ADR-0087 registries, where the D3 entry states the removal outright. Under option B the F4 note would read cleanly, but check-empty-changeset's foreign changeset rule refuses any M row on a changeset present at the merge base and not added by the PR, naming the DELIBERATE CORRECTION class whose remedy is "get it confirmed on the PR" — a person's confirmation, not the dev's act, on a file outside the claim's surface. The rule against errata in a later entry binds released entries; F4's is unreleased and lands in the same release, so A does not create the stranded-reader case that rule exists for. Escalated as optional, not blocking: if the seat wants the cleaner F4 note, it is a maintainer-confirmed correction in its own act; the dev's one-line rewrite in the Acceptance notes is the right text for it.
  • Open question 2 — delete the module or keep its header as an empty module. A (delete) is right. An empty module kept for prose is a workaround; the ADR-0089 history lives in the ADR, the three retired-key entries and the two D3 entries, and the deleted header described only the two removed exports.
  • Out-of-scope note (build-skill-references.ts:274). Confirmed: a historical filename mention, no symbol, no action.
  • Advisory, not a condition of this verdict — two sentences of consumer-facing changeset text overreach. (a) "since 18 every schema that carries it refuses visibleOn and visibility by name": the three shapes that ever had the aliases do (tombstones, and strictObject for the other key), but api/automation-api.zod.ts:231 screenFieldSpecShape is a plain z.object carrying visibleWhen, a wire shape that never had the aliases, so an unknown visibleOn there is stripped rather than refused; "every schema that had them refuses them by name" is the true sentence. (b) "a plain-JS import resolves to undefined" holds for a CommonJS require read; an ES-module named import of a missing export fails at link time with a SyntaxError. Neither moves the contract or the upgrade instruction (delete the import, read visibleWhen); both are one-sentence precision fixes on this PR's own changeset (an A row, so the foreign changeset rule does not bite) — the seat's call.

Verdict reason: the diff publishes exactly the narrowing the card and the F4 ruling's item 4 leave room for — two zero-pull exports gone from @objectstack/spec/shared, registered as a D3 entry, counted by the two regenerated baselines, pinned at runtime, described by a BREAKING minor changeset with its migration — and touches no schema, no conversion and no objectui file. Every claim its hunks make was read true in the cited code, and every gate the head answers is green.

Implemented-by: claude/issue-23877-visibility-fold-unexported
Reviewed-by: session_01HjR8RUwTPQ3TgbfKihmxp9

VERDICT: PASS


Generated by Claude Code

…rate the ESM and CommonJS failure modes

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

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · PR #24004 @ ca04d47854 (#23877) · domain:spec seat 1 · os-elon-musk · session session_01HjR8RUwTPQ3TgbfKihmxp9 · 2026-10-16T19:18Z

Reviewer of record. Every item below was checked against GitHub and the code, not against the reports (6221253902, 6222159859 on #23877).

  • Shape: Draft, base main. The first line is Fixes #23877, the body's only closing keyword, then Clause-②: yes, matching claim 6220012215. 11 files, about +109/−88. Not governed. Assignee os-elon-musk.
  • What it lands: normalizeVisibleWhen and VISIBILITY_ALIAS_KEYS leave @objectstack/spec/shared. shared/visibility.ts held only them and a private type, so it is deleted with its barrel line, and an empty module behind export * would not compile. The shared api-surface and export-origins shards each lose exactly those two rows. A D3 entry visibility-alias-fold-unexported registers the removal, and visibility-strict-options-unexported's "stay exported" text is rewritten. Three comments now name the mechanism instead of a removed symbol. An export-absence pin is added. No schema's accept set moves, and the stored-row conversions are untouched.
  • Contract review: PASS 6222103558 on 5626f60305 (CONTRACT_REVIEW_TIER). Zero pull re-measured in objectstack and in objectui at the pin f8267df93a: one docblock hit, no import.
  • The patch round (6222126702) changed the PR's own changeset text only, which is a prose face, so the PASS record still governs the head. The diff 5626f60305..ca04d47854 is that one file, +2/−2. The two sentences now read true: "every schema that had the two old keys (the form field, the form section and the page component) refuses them by name"; and an ESM named import fails at link time while a CommonJS require reads undefined.
  • Semver: a BREAKING minor on @objectstack/spec, Clause-②: yes (narrowing), marker registered visibility-alias-fold-unexported.
  • The report's open questions, the seat's calls: (1) F4's unreleased "stay exported" sentence: A. Both changesets land in the same @objectstack/spec version block, and this one says it supersedes that line. (2) Delete the module rather than keep an empty one: A.
  • CI on this head: 41 check-runs, 35 success, 6 skipped by their filters, 0 failed.

The PR goes to the merge queue in this act. The cloud dev session is archived at this ACCEPT.


Generated by Claude Code

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

Labels

Projects

None yet

2 participants