Repository navigation
Commit 87956dc
spec(data): the name-only reference value a refused reader is served has a named schema, and referenceNameVisible says which name it carries (#29623)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8PrTYh9NTZ1gpojcZYPdiSm)_
Fixes #29579
Clause-②: yes (widening)
Metadata-protocol: added
## 维护者速读
- 改了什么:`referenceNameVisible` 的说明改为写明下发的名字是 ADR-0079 显示名(`nameField`,否则
`displayNameField`,否则推导的标题字段),并列全五种下发 `Record #` 兜底的情形;下发值 `{ id, name,
readRefused: true }` 有了具名 schema,并入展开后 lookup 值的联合;引擎的类型改为从它派生。
- 为什么改:这个值发给读不到目标记录的人,公开文字必须准确说明暴露了什么;旧说明只写 `nameField`、只列了三种兜底,下发形状在
spec 里没有名字。
- 风险与回滚:不改任何行为;联合里原有的开放记录成员本就接受这个值,所以没有值的判定变化。回滚即 revert 本 PR。
- 先看:`packages/spec/src/data/field-value.zod.ts`(新 schema
与联合);`packages/spec/src/data/field.zod.ts`(说明文字);`packages/objectql/src/engine-reference-name-visible.ts`(类型派生)。
- 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run
src/data/field-value.test.ts`,预期全绿,含新用例「a reader refused the target of a
referenceNameVisible field reads the NAMED name-only value」。
- 需要你做的:批准两个名字后再合并:新导出 `ReferenceNameOnlyValueSchema` /
`ReferenceNameOnlyValue`,以及 `readRefused` 作为声明的键(triage 6587041711
要求合并前获维护者批准)。
## What changes
- **`packages/spec/src/data/field-value.zod.ts`.** New closed
`ReferenceNameOnlyValueSchema` (`strictObject`): `id` (a
`ReferenceIdValueSchema` record id), `name` (non-empty string),
`readRefused: true`. Type `ReferenceNameOnlyValue` is its `z.input`
type. `valueSchemaFor(def, 'expanded')` for the reference types is now
`ReferenceIdValueSchema | ReferenceNameOnlyValueSchema |
z.record(z.string(), z.unknown())`. It is a read form only; the stored
form stays `ReferenceIdValueSchema`.
- **`packages/spec/src/data/field.zod.ts`.** The `referenceNameVisible`
JSDoc and `.describe()`:
- item 1: the name is the ADR-0079 D4 display name
`resolveRecordDisplayName` answers (`nameField`, else
`displayNameField`, else the derived title field), no longer "the target
object's `nameField` value";
- item 2: the `Record #` floor cases are now all five that objectql's
`servableNameField` serves (field-level security, `maskingRule`, formula
hop, an explicit pointer (or its `displayNameField` alias) at a column
neither title-typed nor autonumber, no usable field-level answer), plus
the ADR-0079 floor every reader gets (no resolvable or no non-empty
name);
- the describe names `ReferenceNameOnlyValueSchema`.
- **`packages/objectql/src/engine-reference-name-visible.ts`.** The
hand-written `ReferenceNameOnlyValue` interface is gone; the module
imports the type from `@objectstack/spec/data` (the module already
imported `@objectstack/spec/data` at run time, so the edge exists). No
behaviour change.
- **Regenerated.** `content/docs/references/**` (`field.mdx`,
`object.mdx`, `system/migration.mdx` carry the field describe;
`field-value.mdx` gains the section; `index.mdx` counts 1604 → 1605),
`api-surface`, `export-origins`, `declaration-map`,
`json-schema.manifest`, `authorable-surface/data.json`, and the
strictness-ledger counts.
- **Ledgers.** `dropped-refinements.baseline.json` gains
`data/ReferenceNameOnlyValue` site `id` (the build named it: the
`ReferenceIdValueSchema` refine does not project, the same as the
`data/ReferenceIdValue` row); `type-alias-convention.pin.test.ts` 818 →
819 isomorphic pins; `strictness-ledger.test.ts` field-value sites 4 →
5.
- **Changeset.** `@objectstack/spec` minor. `@objectstack/objectql`
carries none: built `dist/` holds 0 hits for the new import comment
against 1 for a sibling string (positive control); the type is erased
and the module is not re-exported.
## Declarations, measured
- **Clause-②: yes (widening).** The PR adds two exports
(`ReferenceNameOnlyValueSchema`, `ReferenceNameOnlyValue`) and three
authorable-surface rows, which is a widening of the published surface
(precedent `FileRefusedValueSchema`, 02d9f69). No value's parse verdict
moves: the expanded reference union already held `z.record(z.string(),
z.unknown())`, which accepts `{ id, name, readRefused: true }`.
- **Metadata-protocol: added.** The build writes three new rows into the
authorable-surface shard `packages/spec/authorable-surface/data.json`:
`data/ReferenceNameOnlyValue:id`, `:name`, `:readRefused`. That is the
claim's measurement and its stated arm. The value is served, never
written: the stored form refuses it (test `bad({ type: 'lookup' }, { id,
name, readRefused: true })`).
## Tests
All at head `0d181d41`, against a spec `dist/` rebuilt from this tree.
- `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2
src/data/field-value.test.ts
src/data/field-reference-name-visible.test.ts
src/type-alias-convention.pin.test.ts scripts/strictness-ledger.test.ts`
→ `Test Files 4 passed (4)`, `Tests 69 passed (69)`.
- `pnpm --filter @objectstack/spec typecheck`, `pnpm --filter
@objectstack/objectql typecheck` → exit 0.
- `pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2` →
`Test Files 541 passed (541)`, `Tests 10493 passed (10493)`.
- `pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2
src/data-reference-name-visible.test.ts` (the served-value end-to-end
suite of PR #29576) → `Tests 23 passed (23)`.
<details><summary>Reverse verification: the engine type follows the spec
schema</summary>
One-shot, not kept. `node scripts/ablation-replace.mjs` changed the spec
schema's `name: z.string().min(1)` to `name: z.number()` (anchor 1 → 0,
blob `e91212ba468a` → `e122c5983b7e`), then rebuilt spec and
type-checked objectql:
```text
spec-build-exit 0
objectql-typecheck-exit 2
src/engine-reference-name-visible.ts(147,26): error TS2322: Type 'string' is not assignable to type 'number'.
```
Line 147 is the `served.set(id, { id, name:
resolveRecordDisplayName(...), readRefused: true })` call, so the engine
reads the type from the rebuilt spec `.d.ts`. Restored (blob hash equals
HEAD, `git diff HEAD` empty), spec rebuilt, objectql typecheck exit 0,
`check:generated` all 14 current.
</details>
## Acceptance notes
- `packages/objectql/src/engine.ts` (the private `serveReferenceNames`
docblock, ~L13860) still says the floor is served "when field-level
security keeps that field from the caller", one case of five. It is a
private method's comment that does not ship (0 hits in objectql
`dist/index.d.ts`), and the module header of
`engine-reference-name-visible.ts` already lists all of them. Not in
this claim's file surface; left as is.
## Verification
Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at `0d181d41` (19 paths vs merge base
`81cd9eb0e`).
- 107 derived, 106 run with exit 0, 1 NOT MEASURED. `--ran`
reconciliation: `✓ dispatch-gates --ran: 107 derived famil(ies)
accounted for — 106 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit
3).`
- NOT MEASURED: `pnpm check:dual-build-cjs-loads` (exit 3, PREREQUISITE
NOT MET: it needs every package built). Left to CI.
- `node scripts/check-plugin-teardown-shape.mjs --self-test` first
exited 3 (shallow clone could not read its pinned positive control);
after `git fetch origin 621a487…` it exits 0 (`48 cases pass`).
- `pnpm --filter @objectstack/spec check:generated` → `✓ All 14
generated artifacts are up to date`.
- `node scripts/check-platform-checklist.mjs --changed-from origin/main`
(an exported interface left a module) → `OK — 2 anchored file(s) in the
change set, 9 anchor(s) judged, 0 lost.`
- `origin/main` at `5bd615eff` merges cleanly (`git merge-tree` exit 0);
none of its commits since the base touch this diff's files.
- Families CI runs (lint, `Test Core`, type-check lanes) are CI's run on
this PR.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` is CI's run.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
## Maintainer approval
Warren approved the new public names `ReferenceNameOnlyValueSchema`,
`ReferenceNameOnlyValue` and `readRefused` as named, with
`Metadata-protocol: added` kept: 「全部批准」 on the decision card
「六个命名/措辞全部按推荐批准吗?」 in the project thread "Decide pending spec name
approvals", 2026-11-03 00:08Z. Recorded on this PR in seat comment
6591825483.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01JQwxbahxR9HAJnTs7UKhmn
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JQwxbahxR9HAJnTs7UKhmn)_
<!-- signed: domain:spec#3 · os-warren ·
session_01JQwxbahe4v7GBD94QeSnir -->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent c779611 commit 87956dc
21 files changed
Lines changed: 215 additions & 70 deletions
File tree
- .changeset
- content/docs/references
- data
- system
- docs/audits/2026-07-unknown-key-strictness-ledger.counts
- packages
- objectql/src
- spec
- api-surface
- authorable-surface
- declaration-map
- export-origins
- json-schema.manifest
- scripts
- src
- data
- ui/packages/fields/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
45 | | - | |
| 44 | + | |
| 45 | + | |
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| |||
193 | 193 | | |
194 | 194 | | |
195 | 195 | | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
78 | 78 | | |
79 | 79 | | |
80 | 80 | | |
81 | | - | |
| 81 | + | |
82 | 82 | | |
83 | 83 | | |
84 | 84 | | |
| |||
0 commit comments