Skip to content

feat(spec)!: retire the never-consumed CurrencyValueSchema / CurrencyValue export (ADR-0104 D1) - #27233

Merged
os-zhuang merged 11 commits into
mainfrom
claude/project-thread-g1qmlm
Oct 29, 2026
Merged

os-zhuang merged 11 commits into
mainfrom
claude/project-thread-g1qmlm

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Requested by Yibo Wang · project thread
Fixes #27182

Ruling-ref: 6465431966
Claim: 6467480691
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Skills-docs: none — REASON: no authoring surface. git grep CurrencyValue -- skills on base finds only the generated skills/*/references/_index.md export lists (regenerated here by gen:skill-refs, which now name UniqueScopeSchema in its place); no rule, example or table teaches the shape.

维护者速读

  • 改了什么: 删除 field.zod.ts 仍导出的 CurrencyValueSchema 与 CurrencyValue 类型;登记 RETIRED_DEFS_BY_MAJOR[18] 的 data/CurrencyValue 与 D3 语义条目 currency-value-schema-retired(FROM {value, currency} → 裸数字,币种写在 currencyConfig.defaultCurrency)。
  • 为什么改: ADR-0104 D1 早已裁定删除,18.2.0 仍导出;多币种史诗 [Epic] multi-currency (shape C′): record currency, dated exchange rates, write-time home-currency projection — 17 children, R1/R2/R3 release pairing (ruling 6465431966) #8345 开工前必须清掉,免得作者把它当成「记录币种」的形状。
  • 风险与回滚: 零读者(只剩自身测试和生成基线),运行时行为不变;只影响 TypeScript 导入(TS2305)。回滚 = revert 本 PR。
  • 先看: packages/spec/src/migrations/entries/semantic/18.currency-value-schema-retired.ts、packages/spec/src/data/currency-value-retirement.test.ts。
  • 两分钟验证: git grep -n CurrencyValueSchema -- packages ':!**/CHANGELOG.md' → 只剩登记条目、退役 pin 和散文;pnpm --filter @objectstack/spec exec vitest run --project repo src/data/currency-value-retirement.test.ts 全绿。
  • 需要您做的: 本 PR 触及 Tier H(docs/adr/0104、生成的 skills/*/references/_index.md),按治理需 os-zhuang 或 hotlong 的批准,之后由席位落地。

What changes

  • Spec (@objectstack/spec, minor, BREAKING for TypeScript imports only). CurrencyValueSchema and export type CurrencyValue are deleted from packages/spec/src/data/field.zod.ts. Route 3 of the spec-property-retirement kit (the ApiKeySchema and document-family precedent): no carrier key and no metadata type ever held the shape, so there is no retiredKey() tombstone, no UNKNOWN_KEY_GUIDANCE entry and no D2 conversion.
  • ADR-0087: registered. retired-defs/18.data__CurrencyValue.ts and the D3 semantic entry semantic/18.currency-value-schema-retired.ts, which carries the card's note: a currency value is a bare number ({ value: 1000.5, currency: 'USD' } → 1000.5), the code is currencyConfig: { currencyMode: 'fixed', defaultCurrency: 'USD' }, and valueSchemaFor(field, 'stored') types a value. No STEP18_RATIONALE fragment (the identity-api-key-schema-retired precedent has none).
  • Generated. json-schema.manifest/data.json loses data/CurrencyValue and authorable-surface/data.json its two rows (both deliberate, as the build's manifest and gate (a) deletion checks require); api-surface, export-origins, declaration-map, the reference docs, the strictness-ledger counts (data/ 165 → 164 sites, field.zod.ts 14 → 13) and skills/*/references/_index.md regenerated by check:generated --fix. authorable-surface.base.json (the released anchor) is untouched.
  • Liveness / ledger. CurrencyValue never had a liveness-ledger row (never walked). The strictness-ledger field.zod.ts row is re-graded with a dated note: CurrencyValueSchema deleted, LocationCoordinatesSchema the one site left under the existing verdict; the spec: remove the deprecated LocationCoordinatesSchema / CurrencyValueSchema dead exports at the next spec major (ADR-0104) #8562 follow-up line now says one.
  • ADR-0104 (Tier H). A dated note under the "Reality wins" paragraph: the deletion landed in this PR, and where the FROM → TO note lives.
  • Pins that move. field.test.ts drops its CurrencyValueSchema block; type-alias-convention.pin.test.ts drops Iso_data_field__CurrencyValueSchema and its census goes 798 → 797 (title, header, toHaveLength with the history line). The field-value.zod.ts docblock now says the object shape is deleted.
  • New pin packages/spec/src/data/currency-value-retirement.test.ts (added to vitest.repo-tests.json): the def and D3 entry are registered; the export is gone from /data and the root entry, CurrencyConfigSchema survives (lit control); valueSchemaFor({ type: 'currency' }) accepts 1000.5 and refuses { value, currency }; tree-scoped absence over the declared radius with an anti-vacuity matcher test.

Why

ADR-0104 D1 decided "currency is a scalar number — CurrencyValueSchema is deleted", but the export shipped through 18.2.0 as @deprecated. The multi-currency epic #8345 (shape C′, ruling 6465431966 on #27119) needs it gone first, so that no author or AI reads {value, currency} as the way a record keeps its currency.

Verification

Base origin/main fd73f2b39; head bf9ae4435.

Measure first (base)

git grep -n CurrencyValue outside CHANGELOGs: field.zod.ts (declaration and alias), field.test.ts, the Iso_ pin row, api-surface / export-origins / declaration-map / json-schema.manifest / authorable-surface / authorable-surface.base.json, generated reference docs, and prose (field-value.zod.ts docblock, field-value.test.ts test name, content/docs/protocol/objectql/types.mdx, ADR-0104, the strictness ledger). Nothing under ui/ or skills/** rules. Control: CurrencyConfigSchema has live readers (validator, formula, ui i18n).

Gates
  • pnpm --filter @objectstack/spec build → exit 0 (after the deliberate manifest and authorable-surface deletions; the build refused each first, as designed).
  • pnpm --filter @objectstack/spec check:generated → 14 of 14 fresh after --fix.
  • pnpm --filter @objectstack/spec test → Test Files 740 passed | 1 skipped (741), Tests 22240 passed | 1 skipped | 1 todo.
  • pnpm --filter @objectstack/spec test:repo → Test Files 82 passed (82), Tests 1331 passed.
  • pnpm --filter @objectstack/spec typecheck → exit 0.
  • check:liveness, check:empty-state, check:exported-any, check:dual-source-exports → exit 0. check:skill-examples → exit 3 (prerequisite: needs a full-repo build; not measured).
  • node scripts/check-adr-0087-registration.mjs --base origin/main → ✓ registered currency-value-schema-retired (new here).
  • check-changeset-no-major (pull_request event) → ✓ narrowing ships minor; check-spec-parsed-alias → 797 pinned, OK; check-cross-package-test-inputs, check-doc-authoring, check-role-word, check-skills-token-ratchet → OK.

Serial note

Seat 3's #27218 (#27157) narrows CurrencyConfigSchema about 30 lines above this edit in field.zod.ts and touches migrations/registry.ts.template. Whichever lands second merges main (no rebase) and re-runs the generated files.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic.
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: its generated artifacts are regenerated and committed.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done.

Generated by Claude Code

…Value export (ADR-0104 D1)

A currency field's value is a bare number everywhere; the {value, currency}
shape was deprecated, unread, and contradicted the enforced contract. Route 3
(no carrier key): RETIRED_DEFS_BY_MAJOR[18] data/CurrencyValue plus the D3
semantic entry currency-value-schema-retired, a tree-scoped retirement pin,
regenerated baselines and docs, the ADR-0104 dated note and the strictness
ledger re-grade.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufziAkt9vZhLphG64TmD
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 3 documentable anchor(s). ⚠️ 8 changed file(s) yielded no anchor (packages/spec/api-surface/data.json, packages/spec/authorable-surface/data.json, packages/spec/declaration-map/data.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

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

  • content/docs/protocol/objectql/types.mdx (via CurrencyValueSchema (symbol, a top-level const))
What this run could not see
  • 8 changed file(s) yielded no anchor (packages/spec/api-surface/data.json, packages/spec/authorable-surface/data.json, packages/spec/declaration-map/data.json, …) — 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 56 of 208 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 — 148 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 7858c1d16bb27ac9a5a6d3ae9db9d6dd7331d43d → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 7858c1d16bb27ac9a5a6d3ae9db9d6dd7331d43d

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

…ved, not deprecated

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufziAkt9vZhLphG64TmD
@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling labels Oct 28, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: ae2bf133cd6fe301ce00b72b474d1f4804f8a468
Local-runs: none
Scope: docs/adr/0104-field-runtime-value-shape-contract.md, docs/audits/2026-07-unknown-key-strictness-ledger.md, docs/audits/2026-07-unknown-key-strictness-ledger.counts/data.md, packages/spec/api-surface/data.json, packages/spec/authorable-surface/data.json, packages/spec/declaration-map/data.json, packages/spec/export-origins/data.json, packages/spec/json-schema.manifest/data.json, packages/spec/src/data/currency-value-retirement.test.ts, packages/spec/src/data/field-value.zod.ts, packages/spec/src/data/field.test.ts, packages/spec/src/data/field.zod.ts, packages/spec/src/migrations/entries/retired-defs/18.data__CurrencyValue.ts, packages/spec/src/migrations/entries/semantic/18.currency-value-schema-retired.ts, packages/spec/src/type-alias-convention.pin.test.ts, packages/spec/vitest.repo-tests.json, skills/objectstack-ai/references/_index.md, skills/objectstack-data/references/_index.md, skills/objectstack-i18n/references/_index.md, skills/objectstack-platform/references/_index.md, skills/objectstack-ui/references/_index.md + packages/spec/src/data/field-value.test.ts, packages/spec/src/data/index.ts, packages/spec/authorable-surface.base.json, packages/spec/scripts/build-schemas.ts, packages/spec/scripts/build-skill-references.ts, packages/spec/src/migrations/entries/README.md, packages/spec/src/migrations/registry.ts.template, packages/spec/src/migrations/entries/retired-defs/18.identity__ApiKey.ts, packages/spec/src/migrations/entries/semantic/18.identity-api-key-schema-retired.ts, packages/spec/src/identity/api-key-retirement.test.ts, packages/spec/src/data/document-schemas-retirement.test.ts, packages/spec/CHANGELOG.md (the identity-api-key-schema-retired entry), scripts/cross-package-test-inputs.mjs, scripts/check-cross-package-test-inputs.mjs, scripts/check-changeset-no-major.mjs, scripts/check-adr-0087-registration.mjs, content/docs/protocol/objectql/types.mdx, .claude/skills/spec-property-retirement/SKILL.md, AGENTS.md. Not reviewed (prose faces and generated projections, read only to judge the narrowing): content/docs/references/**, the root .changeset/27182-currency-value-schema-retired.md (judged in ② only).

Inputs: card #27182 (body, claim 6467480691, dev report 6468102269), PR #27233 (body, 25-file list, net diff against main at this head), and the check-runs on this head. Not the dispatch order, not the dev's local-run claims. The head is read from the diff; it is not in the reviewer's checkout, and no worktree or checkout was made.

Gate verdicts on this head, as the check-runs conclude them. Green: PR-body guards, Check Documentation Links, Test Core, Dogfood Regression Gate, filter. Cancelled, so unanswered on this head: Check Changeset, Spec property liveness, Governed Surface Queue Guard, Build Core, Lint & Type Check, Type Check UI, Docs Drift Check, Plan Test Core shards, Build Docs, Check PR Size, Auto Label. Test UI concludes failure on the step Verify Test UI shard results after every Test UI (n/16) shard was cancelled — a cancellation artefact, not a measured failure. Every cancellation stamps 11:45–11:46Z, which is when the branch moved on to the successor commit dcbbbc3af (11:45:03Z, a docs-only change to content/docs/protocol/objectql/types.mdx, a prose face). This record judges ae2bf133 and does not stand in for the gates that never answered on it: the landing condition "every check green" is met only by a head whose runs completed, and the dev's local check:generated / typecheck / liveness / changeset claims are not adopted here.

① Derived judgments

Accept-set and public-surface changes the diff implies, each judged against the tree:

  1. CurrencyValueSchema (const) and CurrencyValue (type) leave @objectstack/spec/data and the root entry — field.zod.ts loses the lazy z.object({ value, currency }) declaration and the z.input alias and nothing else (20 lines, pure deletion); data/index.ts is export * from './field.zod', so both entries lose the names with no index edit owed. Right: ADR-0104 D1 decided "currency is a scalar number — CurrencyValueSchema is deleted"; the export shipped through 18.2.0 @deprecated; ruling 6465431966 (the C′ multi-currency resumption) makes the deletion the prerequisite; on main the only non-prose readers were the schema's own field.test.ts block and its Iso_ pin row, both removed here. Nothing under ui/, skills/** rules, examples/ or any other package imports either name (measured by the reviewer's own git grep on main, not adopted from the report).
  2. The runtime value accept-set for a currency field is unchanged — field-value.zod.ts keeps currency in the numeric-scalar class (line 65), valueSchemaFor({ type: 'currency' }) was z.number() before and is after, and field-value.test.ts:258 already pinned on main that the { value, currency } object is refused. Right: the narrowing removes a declaration nothing parsed; no record, seed or wire value changes acceptance, and the new pin re-asserts both directions (1000.5 accepted, the object refused).
  3. json-schema.manifest/data.json loses data/CurrencyValue; RETIRED_DEFS_BY_MAJOR[18] gains it — build-schemas.ts refuses a vanished manifest key unless the def is registered by exact ${category}/${SchemaName} and refuses a registration that names a still-published def; the entry file retired-defs/18.data__CurrencyValue.ts is named per the entries README (/ → __) and exports exactly 'data/CurrencyValue'. Right, and it is the whole-def route the ratchets make visible (api-surface −2, authorable −2, manifest −1, declaration-map −2, export-origins −2), the opposite reading from an enum-value narrowing, as the kit says to expect.
  4. authorable-surface/data.json loses data/CurrencyValue:currency and :value; authorable-surface.base.json is untouched — the whole-def route (no [RETIRED] row, because there is no key left in a walked shape), the identity/ApiKey precedent byte for byte. Right.
  5. D3 semantic entry currency-value-schema-retired under major 18, no D2 conversion, no conversionIds, no STEP18_RATIONALE fragment — route 3 of the kit: the schema was mounted by no stack key and bound to no metadata type, so there is no document for a conversion to rewrite and no shape for retiredKey() to sit on. The ApiKeySchema precedent (entry, D3 record and CHANGELOG.md entry read on main) is the same shape, also without a rationale fragment; 290 of the 426 step-18 semantic entries carry none, so a fragment is not owed per entry. reason and acceptanceCriteria are non-empty; surface is prose. Right.
  6. Liveness ledger: no row added, none removed — packages/spec/liveness/*.json never carried CurrencyValue (the schema was never walked), so neither the UNCLASSIFIED nor the ORPHAN leg has anything to say. Right — though Spec property liveness did not conclude on this head (see the gate column).
  7. Strictness ledger — the hand-written field.zod.ts row gains a dated parenthetical and keeps its 2026-08-13 verdict for the one surviving strip site (LocationCoordinatesSchema); the counts file moves data/ 165 → 164 sites, strip 75 → 74, field.zod.ts 14 → 13 / strip 2 → 1, "no door" bucket 2 → 1; the spec: remove the deprecated LocationCoordinatesSchema / CurrencyValueSchema dead exports at the next spec major (ADR-0104) #8562 follow-up line says "one". Right: the ledger is an input to scripts/strictness-ledger.test.ts's ratchet, so the counts had to move with the site.
  8. type-alias-convention.pin.test.ts loses Iso_data_field__CurrencyValueSchema; the census goes 798 → 797 in the title, the header and the toHaveLength with a history line. Right (the recomputed-count companion is what makes a silent drop impossible; check-spec-parsed-alias reads the same number).
  9. Generated reference docs and skills/*/references/_index.md — field.mdx loses its CurrencyValue section and import lines; index.mdx 1559 → 1558 / Data 185 → 184; the five skill indexes' Exports: row for field.zod.ts names UniqueScopeSchema in place of CurrencyValueSchema. Right: that row is exportListDescription()'s fallback (the module has no doc block of its own) and ranks mechanically, so the replacement is the generator's, not a hand edit. These five skills/** paths and docs/adr/** make the PR Tier H; the PR body says so, and whether the five regenerate byte-exact on the queue leg is the queue guard's reading, which did not conclude on this head.
  10. New pin currency-value-retirement.test.ts, registered in vitest.repo-tests.json (alphabetical slot held) — registry presence, both entries' absence with CurrencyConfigSchema as the lit control, the value contract both ways, and a tree-scoped absence walk over the five roots CROSS_PACKAGE_TEST_INPUTS['@objectstack/spec'] declares (per-extension packages/**, non-code examples/** plus examples/*/src/**/*.ts, content/**, skills/**, scripts/**), the document-schemas-retirement shape with the anti-vacuity floors (visited > 1000, example sources > 50) and a matcher self-test. The matchers judge a USE (import/export { … } from '@objectstack/spec…', Data.CurrencyValueSchema), strip inline code, and exclude only the pin itself, release prose and gitignored build output. The entry prose in the two new registry files spells no shape the matchers read (no braced import from a spec specifier, no Data. access), so the pin cannot turn red on its own kit. Right; the radius is declared at the package grain, and the house practice (the document-schemas and api-key pins are not heldBy witnesses either) does not require a fourth witness.
  11. Prose edits on the floor — the field-value.zod.ts docblock ("is deleted" for "not the never-consumed") and the ADR-0104 dated note under "Reality wins", which names the card, the PR, the ruling and where the FROM → TO note now lives. Right and accurate against the tree.

Nothing in the diff widens anything; no accept-set outside the one export is touched; CurrencyConfigSchema and the #8163 currency field-key guidance are untouched (Seat 3's #27218 narrows CurrencyConfigSchema in the same file — not judged here; the serial note's "second to land merges main and regenerates" is the right discipline for a generated-file collision).

② Semver level

.changeset/27182-currency-value-schema-retired.md: '@objectstack/spec': minor, body carries Clause-②: no (narrowing) and Metadata-protocol: narrowed line-initial, exactly one disposition marker adr-0087: registered currency-value-schema-retired (the id exists as semantic/18.currency-value-schema-retired.ts), the BREAKING banner, the FROM → TO mapping ({ value: 1000.5, currency: 'USD' } → 1000.5, code on currencyConfig.defaultCurrency) and the one-line fix (delete the import, use number, or valueSchemaFor(field, 'stored')). The PR body carries the same two declaration lines.

  • Level — a public-surface removal from @objectstack/spec is BREAKING; the launch window ships it minor (check-changeset-no-major refuses major; the ApiKeySchema precedent shipped minor the same way). Matches.
  • Metadata-protocol: narrowed — defensible and the only declaration the gate admits: the diff moves an authorable-surface/ baseline (two data/CurrencyValue:* rows), which none may not do, and narrowed beside (narrowing) is the consistent pair. The rows were authorable only on paper (no door ever reached the shape), which is exactly what the claim's "an export removed; no authorable key changes" parenthetical says. Matches.
  • Clause-②: line — no (narrowing), well-formed (one arm from the closed pair), on the PR body and the changeset. Matches.
  • Check Changeset did not conclude on this head; the reading above is the reviewer's against the gate's documented rule, not the gate's.

③ Boundary flags

  • open_questions: [], out_of_scope_findings: [] in the dev report — nothing to answer.
  • check:skill-examples exit 3 (prerequisite, NOT MEASURED) — the skill said to run the five source audits as a group; this one needs a full-repo build the dev did not have. Escalated to CI: it is answered by the Lint & Type Check / CI workflows, which did not conclude on this head. The successor head's run answers it; this record does not. (Reading the tree: no skills/*/SKILL.md names either retired export, so the expected answer is green.)
  • "dispatch-gates and the dogfood battery NOT run; CI pending" — Dogfood Regression Gate concluded success on this head; the rest is the cancelled column above.
  • "cloud and real customer code are UNMEASURED" (D3 reason) — accepted as stated: the export was @deprecated with "Removal rides the next spec major" on main, the ruling directs the deletion, and the BREAKING changeset plus the D3 entry are the notice an external TypeScript consumer receives (TS2305 names the symbol). Not a seat judgment to make otherwise.
  • Tier H — docs/adr/0104… and the five skills/** indexes: landing waits for a GOVERNED_APPROVERS review; the PR body names it. Not lifted by this record.
  • Serial constraint with fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218 — answered in the claim and the PR body (second to land merges main, regenerates). Not this tree's to judge.
  • Card vs tree — the card's 速读 says "tombstone 进 UNKNOWN_KEY_GUIDANCE", and ADR-0104's decision sentence says "tombstoned in UNKNOWN_KEY_GUIDANCE / changelog". The tree has no UNKNOWN_KEY_GUIDANCE in field.zod.ts (its unknown-key channels are FIELD_KEY_GUIDANCE and guidanceSets, keyed by field-definition keys), and { value, currency } was never a key an author could write anywhere; the PR takes the ADR's own alternative (the changelog, plus the registry entries) and says so in the dated ADR note. Judged in ④; it is the right reading, not a deviation to escalate.

④ Design judgment

The narrowing itself, read against the published spec on main and the cited code:

  • Does it take a declared capability away at the consumer? No. The capability "a currency value is { value, currency }" was declared by one export and delivered by nothing: valueSchemaFor typed the value number, the record validator, the SQL driver's float column, import coercion and the field-zoo oracle all store a bare number, and field-value.test.ts pinned the object's refusal before this PR. The removal lands at the declaration, in the spec, which is where an undelivered declaration is retired — not a consumer-side narrowing. ADR-0104 D1 already made the decision; this PR carries it out, and the ADR gains a dated note rather than a quiet changeset. Holds.
  • Does it make the spec's own refusal real? The refusal was already real at the value contract; what the PR removes is the contradiction next to it (an exported schema that accepted what the contract refused — the shape an AI author would learn from the reference page and the skill indexes, which is the multi-currency epic's stated hazard). After this head no published surface — Zod export, JSON-schema def, authorable row, reference page, skill index — teaches the object shape. Holds.
  • Route — route 3 (no carrier key, no metadata type ⇒ registry entry + D3 record, no retiredKey(), no guidance map, no conversion) is the only one the tree admits: a retiredKey() needs a property in a walked shape, a guidance-map entry needs a strictObject an author's spelling lands on, and a conversion needs a stored document; none exists for a value schema nothing mounted. The precedent (identity/ApiKey, the document family) is followed exactly, down to the generated-baseline deletions the build gates demand.
  • Tried to break it: (a) a surviving reader — none on main outside the kit, confirmed by grep across packages/, ui/, skills/, examples/, content/; (b) a registry/manifest mismatch — the def id, file name and manifest key agree; (c) a self-red pin — the new prose spells no matchable use; the walk roots match the declared radius; (d) a wrong ratchet expectation — whole-def deletion, all four ratchets move, as they must; (e) a stale count — the Iso_ census, the ledger counts and the reference index totals all move together; (f) cloud — unmeasured, declared, notice carried by the changeset. Nothing breaks on this head.

Must-fix: none.

Should-fix (non-blocking):

  • S1 — content/docs/protocol/objectql/types.mdx:316 is stale on this head. The currency callout still says the old object shape "is deprecated in the spec"; after this diff it is deleted. Prose face, outside the floor; the PR's commit list shows the successor commit dcbbbc3af ("docs(objectql): the currency callout says CurrencyValueSchema is removed, not deprecated") addressing exactly this, which this record does not judge.
  • S2 — the refusal an author still receives carries no lesson. With the shape gone, someone who writes { value, currency } for a currency value gets valueSchemaFor's bare z.number() error ("expected number, received object") and has to grep the changelog or the upgrade guide for the FROM → TO note. ADR-0104's "tombstoned in UNKNOWN_KEY_GUIDANCE" had no landing site, and this was equally true on main, so it is not this narrowing's debt — but a refusal message on the numeric-scalar arm for the currency type that says the value is a bare number and the code lives on currencyConfig.defaultCurrency would put the lesson in the text, where AGENTS.md wants it. A follow-up, not a condition.

Implemented-by: claude/project-thread-g1qmlm
Reviewed-by: session_01WGufzfhobWEgUUrjz4rrbh

VERDICT: PASS

…_GUIDANCE tombstone

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufziAkt9vZhLphG64TmD
@objectstack-fleet

Copy link
Copy Markdown
Contributor

席位核对 · ACCEPT · domain:spec seat 1 (#6017) · session session_01WGufzfhobWEgUUrjz4rrbh · 2026-10-28T12:16Z

Head c85e08be12d6e80d7fafe89f4f7460a233693272. Card #27182, claim 6467480691, ruling 6465431966.

  • Contract review: owed at CONTRACT_REVIEW_TIER, narrowing face. Record 6468277680 at ae2bf133 is a PASS with no must-fix. It found:
    • The retirement removes an undelivered declaration at the spec. valueSchemaFor('currency') was z.number() before and after, and nothing outside the kit reads the schema.
    • Route 3 (a retired-defs entry plus a D3 record) matches the identity/ApiKey precedent.
    • minor, Clause-②: no (narrowing) and Metadata-protocol: narrowed are right, with one adr-0087 marker.
  • Since then: three docs-only commits, 2 files, +11/−8; the seat measured git diff ae2bf133 c85e08be.
  • Gates: check-runs on this head are 41 success, 9 skipped and 0 red. mergeable_state is clean. The gates the review saw cancelled on ae2bf133 are all answered on this head.
  • Serial: seat 3's PR fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218 (CurrencyConfigSchema, same file) is unmerged. Whichever lands second merges main and regenerates.
  • Landing: check-governed-merges --pr 27233: GOVERNED (6 of 26 paths; docs/adr/** and the regenerated skills/*/references/_index.md), Tier H. The seat neither flips it ready nor arms it. It lands by the maintainer's hand, or by an APPROVED review from a governed approver, after which this seat arms it.

@objectstack-fleet

Copy link
Copy Markdown
Contributor

triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

Contract-review-claim: session_01AavokzJ5DndAwitDXvKy4U · head c85e08be12d6e80d7fafe89f4f7460a233693272 · 2026-10-28T13:08Z

Triage seat, under the gap rule (#27062) condition (b): no record and no claim on this head after one round. The review runs at CONTRACT_REVIEW_TIER. If the lane claims or posts a record on this head first, triage withdraws.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c85e08be12d6e80d7fafe89f4f7460a233693272
Local-runs: none
Scope: packages/spec/src/data/field-value.zod.ts, packages/spec/src/data/field.zod.ts, packages/spec/src/migrations/entries/retired-defs/18.data__CurrencyValue.ts, packages/spec/src/migrations/entries/semantic/18.currency-value-schema-retired.ts, docs/adr/0104-field-runtime-value-shape-contract.md, skills/objectstack-ai/references/_index.md, skills/objectstack-data/references/_index.md, skills/objectstack-i18n/references/_index.md, skills/objectstack-platform/references/_index.md, skills/objectstack-ui/references/_index.md + packages/spec/src/data/index.ts, packages/spec/src/data/field-value.test.ts, packages/spec/src/data/currency-value-retirement.test.ts, packages/spec/src/type-alias-convention.pin.test.ts, packages/spec/vitest.repo-tests.json, packages/spec/src/migrations/entries/README.md, packages/spec/src/migrations/entries/retired-defs/18.identity__ApiKey.ts, packages/spec/src/migrations/entries/semantic/18.identity-api-key-schema-retired.ts, packages/spec/scripts/build-schemas.ts, packages/spec/scripts/build-migration-registry.ts, packages/spec/scripts/build-upgrade-guide.ts, scripts/symbol-anchors.mjs, scripts/check-adr-symbol-anchors.mjs, scripts/cross-package-test-inputs.mjs, .gitignore (the generated migrations/registry.ts), content/docs/protocol/objectql/types.mdx, docs/audits/2026-07-unknown-key-strictness-ledger.md, docs/audits/2026-07-unknown-key-strictness-ledger.counts/data.md, the .changeset/27182-currency-value-schema-retired.md (judged in ② only), and, for the collision check the triage order asked for, PR #27239's docs/adr/0104-field-runtime-value-shape-contract.md and docs/adr/0144-multi-currency-record-currency-dated-rates-write-time-home-projection.md at ada4aaad3ce9cc36964d4c6e498e3c908299194f (read, not reviewed). The remainder of the 26-file diff (generated baselines, reference pages, field.test.ts) is the lane seat's ACCEPT at TIER_DEFAULT.

Inputs: card #27182 (body, claim 6467480691, dev report 6468102269), PR #27233 (body, 26-file list, net diff against main at this head — recomputed as git diff origin/main...prr-27233 and identical to the fetched one, 26 files, +336/−118), the check-runs on this head, and the earlier record 6468277680 at ae2bf133 plus the seat's ACCEPT 6468750418, both read as claims. The merge-base with main is fd73f2b391; the head carries no merge commit.

Gate verdicts on this head, as the check-runs conclude them. 50 check-runs: 41 success, 9 skipped (opt-in and matrix-placeholder jobs), 0 failed, 0 pending. Green: Check Changeset, Spec property liveness, Governed Surface Queue Guard, Build Core, Lint & Repo Gates, every Lint · and Type Check · lane, TypeScript Type Check, Test Core (8/8 shards; test test:repo runs there, so the new repo-project pin ran), Test UI, Build Docs, Dogfood Regression Gate, PR-body guards, Check PR Size, Docs Drift Check. Every gate the earlier record listed as cancelled on ae2bf133 has answered green on this head. The gates answered on a merge with base dfe85c9a (11:28Z), not with main as it stands (4d3a077f, 12:50Z) — see ③ E1.

Moved head: the delta ae2bf133..c85e08be

Three commits, 2 files, +11/−8, docs only, no merge of main, no evil-merge content (the delta diff is the sum of the three commits' own hunks):

  • dcbbbc3a — content/docs/protocol/objectql/types.mdx:316-318: the currency callout says the object shape "has been removed from the spec" instead of "is deprecated in the spec". Closes the earlier record's S1 exactly.
  • fb8e9d1f — docs/adr/0104…:79: the symbol anchor packages/spec/src/data/field.zod.ts#CurrencyValueSchema becomes "in data/field.zod.ts; deleted 2026-10-28, see the note under "Reality wins"". Right, and a correction the earlier record missed: on ae2bf133 the ADR cited a symbol anchor to a declaration the same PR deleted; scripts/symbol-anchors.mjs (ANCHOR_GRAMMAR: a symbol anchor "must have a declaration site in that file") makes that a hard finding in check:adr-symbol-anchors (lint.yml:2510), which was cancelled on that head. The earlier record's "accurate against the tree" for the ADR hunk did not hold on ae2bf133; it holds now. The replacement text is not an anchor form (no .ts#symbol, no bare dir/file.ts path at a path-like position the extractor reads as a file anchor — the gate is green on this head), so no new anchor is owed.
  • c85e08be — docs/adr/0104…:166-168 and :180-188: "tombstoned in UNKNOWN_KEY_GUIDANCE / changelog" becomes "registered in RETIRED_DEFS_BY_MAJOR with a D3 semantic entry and the changelog carrying the FROM → TO note — see the 2026-10-28 note below", and the dated note gains a file pointer. The first edit is right (there is no UNKNOWN_KEY_GUIDANCE in field.zod.ts, and the ADR's own decision sentence was the one text still naming a landing site that never existed). The second edit is wrong on the tree — ④ M1.

Earlier must-fixes: the record at ae2bf133 named none, so nothing is owed on that axis. Earlier should-fixes: S1 closed by dcbbbc3a (verified in the delta); S2 is filed as #27236 (open, domain:spec, pm:dispatched, "land after PR #27233"), which is the right home — the objectql record validator refuses currency in its own numeric arm (invalid_number) and never reaches valueSchemaFor, so a message on the spec arm alone would not reach a record writer. The earlier record's remaining judgments were re-checked below and hold, with the one correction above.

① Derived judgments

Every accept-set and public-surface change the diff implies, judged against the tree (the non-delta hunks are byte-identical to ae2bf133; the earlier record's reading was re-verified, not adopted):

  1. CurrencyValueSchema (const) and CurrencyValue (type) leave @objectstack/spec/data and the root entry. field.zod.ts −20 lines, pure deletion (the lazy z.object({ value, currency }) and the z.input alias); data/index.ts:182 is export * from './field.zod', so both entries lose the names with no index edit owed. Right. Readers on main as it stands today (4d3a077f), by this reviewer's git grep outside CHANGELOGs, release prose and the generated projections: the schema's own field.test.ts block, the Iso_ pin row, authorable-surface.base.json (the released anchor, correctly untouched), and prose (field-value.zod.ts docblock, field-value.test.ts:258 test name, types.mdx, ADR-0104, the strictness ledger, the five generated skill indexes). Nothing new since the merge-base mentions either name (git log -S over fd73f2b391..origin/main is empty). ADR-0104 D1 decided the deletion; ruling 6465431966 makes it the C′ prerequisite.
  2. The runtime value accept-set for a currency field is unchanged. field-value.zod.ts:64-65 keeps currency in NUMERIC_VALUE_TYPES; :711 answers z.number().finite() for it; field-value.test.ts:258-260 pinned the object's refusal before this PR. Right. The narrowing removes a declaration nothing parsed.
  3. json-schema.manifest/data.json −1 (data/CurrencyValue); RETIRED_DEFS_BY_MAJOR[18] +1. build-schemas.ts:846-848 reads the registration by exact ${category}/${SchemaName} from the entry files; registry.ts itself is generated and gitignored (.gitignore:65-69), rebuilt by build-migration-registry.ts from registry.ts.template + entries/, so no committed registry edit is owed; the file name 18.data__CurrencyValue.ts follows the entries README (/ → __) and exports exactly 'data/CurrencyValue'. Right. api-surface −2, authorable-surface −2, declaration-map −2, export-origins −2, manifest −1: the whole-def shape, as the kit says to expect.
  4. authorable-surface/data.json loses data/CurrencyValue:currency and :value; authorable-surface.base.json is untouched. No [RETIRED] row because no walked shape holds a key; the identity/ApiKey precedent. Right.
  5. D3 semantic entry currency-value-schema-retired under major 18; no D2 conversion, no conversionIds, no STEP18_RATIONALE fragment. Route 3: no carrier key, no metadata type, no stored document. The replacement is checked against CurrencyConfigSchema on the head (currencyMode: z.enum(['dynamic','fixed']), defaultCurrency: z.string().length(3)): currencyConfig: { currencyMode: 'fixed', defaultCurrency: 'USD' } parses. The surface string deliberately carries no backticks: build-upgrade-guide.ts:126,135 renders it inside a code span, so the entry's comment is right (the ApiKey precedent's backticked surface is the latent rendering defect, not this one). Right. ADR-0144 (C′) adds a record mode and a sibling currencyField; the entry's statement "the currency code is field configuration, not part of the value" stays true under it.
  6. Liveness ledger: no row added, none removed. packages/spec/liveness/*.json never carried CurrencyValue. Spec property liveness is green on this head. Right.
  7. Strictness ledger: the hand-written field.zod.ts row gains a dated parenthetical and keeps its 2026-08-13 verdict for LocationCoordinatesSchema; the counts file moves data/ 165 → 164 sites / strip 75 → 74, field.zod.ts 14 → 13 / strip 2 → 1; the spec: remove the deprecated LocationCoordinatesSchema / CurrencyValueSchema dead exports at the next spec major (ADR-0104) #8562 line says "one". Right.
  8. type-alias-convention.pin.test.ts loses Iso_data_field__CurrencyValueSchema; census 798 → 797 in title, header and toHaveLength, with a history line. Right against the merge-base, stale against main today — main moved 798 → 799 at 8d79bbd7 (feat(spec): draft provenance records MCP build-door writes and the OAuth client that made them #27220, MetadataAuthoringClientSchema), so the merged count is 798 and the three sentences and the history block need the merge author's hand. ③ E1.
  9. Generated reference docs and the five skills/*/references/_index.md: field.mdx loses the CurrencyValue section; index.mdx 1559 → 1558 / Data 185 → 184; the skill indexes' Exports: row for field.zod.ts names UniqueScopeSchema in CurrencyValueSchema's slot — exportListDescription()'s mechanical fallback, not a hand edit. Right. index.mdx is also stale against main today (1560 there) — ③ E1. These five skills/** paths and docs/adr/** make the PR Tier H; Governed Surface Queue Guard is green on this head.
  10. New pin currency-value-retirement.test.ts, in vitest.repo-tests.json (alphabetical slot held; the registry file auto-merges cleanly against main). Registry presence, both entries' absence with CurrencyConfigSchema as the lit control, the value contract both ways, and a tree-scoped absence walk over packages, examples, skills, content, scripts — the roots scripts/cross-package-test-inputs.mjs declares for @objectstack/spec — with anti-vacuity floors and a matcher self-test. The matchers judge a USE (braced import/export from an @objectstack/spec specifier, Data.CurrencyValueSchema), strip inline code, exclude only the pin itself, release prose and gitignored build output. Neither new registry file spells a matchable use. Right.
  11. Prose on the floor — the field-value.zod.ts docblock ("is deleted"), types.mdx ("has been removed"), and the ADR-0104 edits — the last of which carries M1.

Nothing widens. CurrencyConfigSchema and the #8163 currency field-key guidance are untouched. Seat 3's #27218 (same file, ~30 lines above, mergeable_state: clean, unmerged) is not judged here; the serial discipline "second to land merges main and regenerates" is right and is now owed by THIS PR regardless of #27218 (③ E1).

② Semver level

.changeset/27182-currency-value-schema-retired.md: '@objectstack/spec': minor; Clause-②: no (narrowing) and Metadata-protocol: narrowed line-initial; exactly one disposition marker adr-0087: registered currency-value-schema-retired (the id exists at semantic/18.currency-value-schema-retired.ts); the BREAKING banner (TypeScript imports only, TS2305); the FROM → TO mapping and the one-line fix. The PR body carries the same two declaration lines.

  • Level — a public-surface removal from @objectstack/spec is BREAKING; the launch window ships it minor (check-changeset-no-major refuses major; the ApiKeySchema precedent). Check Changeset is green on this head. Matches.
  • Metadata-protocol: narrowed — the diff moves authorable-surface/data.json (two rows), which none may not do; narrowed beside (narrowing) is the consistent pair and the only declaration the gate admits for this diff. Matches.
  • Clause-②: line — no (narrowing), one arm of the closed pair, on body and changeset. Matches.

③ Boundary flags

④ Design judgment

For each hits file, against the code it cites and the scenario it is accepted against:

  • field.zod.ts, field-value.zod.ts (schema face, against ADR-0104 D1 and the first consumer valueSchemaFor) — the deletion lands at the declaration, which is where an undelivered declaration is retired; valueSchemaFor({ type: 'currency' }) is z.number().finite() before and after; the docblock now states the fact. Does it take a declared capability away at the consumer? No — no consumer ever had it (validator, SQL float column, import coercion, field-zoo oracle all store a bare number; field-value.test.ts:258 refused the object before this PR). Does it make the spec's own refusal real? The refusal was real; the PR removes the contradiction beside it, which is the multi-currency epic's stated hazard. Holds.
  • retired-defs/18.data__CurrencyValue.ts, semantic/18.currency-value-schema-retired.ts (against build-schemas.ts's deletion proofs, the entries README and the identity/ApiKey precedent) — id, file name and manifest key agree; reason / acceptanceCriteria / replacement are true on the head's CurrencyConfigSchema; no conversion, no rationale fragment, as the precedent. The retired-defs comment names the D3 id and the D3 entry names RETIRED_DEFS_BY_MAJOR, so each points at the other. Holds.
  • skills/*/references/_index.md (against the generator build-skill-references.ts and the spec on main) — one mechanically ranked row per index, no rule or example touched; Governed Surface Queue Guard green. Holds.
  • docs/adr/0104-field-runtime-value-shape-contract.md (against its first consumer — a reader following the ADR to where the FROM → TO note lives — and against ADR-0144, which hands that reader here):
    • :79 (the delta's fb8e9d1f): right; it also repairs an anchor the earlier head would have failed on.
    • :166-168 (the delta's c85e08be, first hunk): right; the decision sentence no longer names a landing site the tree never had.
    • :184-187 (the delta's c85e08be, second hunk) — M1, must-fix. The note reads: "the D3 semantic entry currency-value-schema-retired (entry file packages/spec/src/migrations/entries/retired-defs/18.data__CurrencyValue.ts)". On the tree, that file is the RETIRED_DEFS_BY_MAJOR[18] entry (export const entry = 'data/CurrencyValue'); the D3 semantic entry's file is packages/spec/src/migrations/entries/semantic/18.currency-value-schema-retired.ts. The parenthetical attaches the retired-defs file to the D3 noun, so the one sentence in the ADR that tells a reader where the note lives names the wrong file. The commit's own title ("name the retired-defs entry") shows the intent; the text does not carry it. The path exists, so check:adr-symbol-anchors is green and cannot catch it; the seat's ACCEPT quoted the sentence as a positive without reading the file it names. The ADR is a governed record and the "first consumer" here is exactly the reader this sentence misdirects (ADR-0144 :22 sends them to it). Fix: attach the path to the right noun — e.g. "carried by RETIRED_DEFS_BY_MAJOR[18] (data/CurrencyValue, entry file …/retired-defs/18.data__CurrencyValue.ts), the D3 semantic entry currency-value-schema-retired (…/semantic/18.currency-value-schema-retired.ts) and the changelog". One hunk, no code; it can ride the E1 merge commit.
  • Tried to break it, this round: (a) a surviving reader on main today — none; nothing landed since the merge-base mentions the names; (b) a merge of main — conflicts, two generated/pinned files, no evil-merge content, no design content in the conflict (E1); (c) the ADR's cross-references — one false (M1); (d) the ADR-0144 collision — notes clean; anchor rot in ADR-0144 (E2); (e) the D3 replacement against the live CurrencyConfigSchema — parses; (f) the registry — generated, gitignored, rebuilt from the entries, so the registration cannot be stale; (g) check:adr-symbol-anchors on the new ADR text — no anchor form introduced.

Must-fix:

  • M1 — ADR-0104 :184-187 names retired-defs/18.data__CurrencyValue.ts as the file of the D3 semantic entry currency-value-schema-retired; that entry lives at semantic/18.currency-value-schema-retired.ts. Introduced by c85e08be, the delta's last commit. Rewrite the parenthetical so each path sits on the entry it belongs to.

Should-fix (non-blocking):

Implemented-by: claude/project-thread-g1qmlm
Reviewed-by: session_01AavokzJ5DndAwitDXvKy4U

VERDICT: FAIL


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor

席位核对 · ACCEPT · domain:spec seat 1 (#6017) · session session_01WGufzfhobWEgUUrjz4rrbh · 2026-10-28T14:05Z

Head a4f0d9098b1a39ae9b84e1f27a89678d2c47f0a2. Card #27182. This supersedes the seat's ACCEPT 6468750418 at c85e08be.

@objectstack-fleet

Copy link
Copy Markdown
Contributor

triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

Contract-review-claim: session_01AavokzJ5DndAwitDXvKy4U · head a4f0d9098b1a39ae9b84e1f27a89678d2c47f0a2 · 2026-10-28T14:58Z

Triage seat, under gap rule (b) (#27062): this head is over 60 minutes old with no record and no claim; a delta re-review from triage's FAIL on c85e08be. The review runs at CONTRACT_REVIEW_TIER. If the lane claims or posts a record on this head first, triage withdraws.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a4f0d9098b1a39ae9b84e1f27a89678d2c47f0a2
Local-runs: none
Scope: packages/spec/src/data/field-value.zod.ts, packages/spec/src/data/field.zod.ts, packages/spec/src/migrations/entries/retired-defs/18.data__CurrencyValue.ts, packages/spec/src/migrations/entries/semantic/18.currency-value-schema-retired.ts, docs/adr/0104-field-runtime-value-shape-contract.md, skills/objectstack-ai/references/_index.md, skills/objectstack-data/references/_index.md, skills/objectstack-i18n/references/_index.md, skills/objectstack-platform/references/_index.md, skills/objectstack-ui/references/_index.md + packages/spec/src/data/currency-value-retirement.test.ts, packages/spec/vitest.repo-tests.json, packages/spec/src/type-alias-convention.pin.test.ts, content/docs/references/index.mdx, content/docs/references/data/field.mdx, content/docs/references/data/field-value.mdx, content/docs/protocol/objectql/types.mdx, docs/audits/2026-07-unknown-key-strictness-ledger.md, docs/audits/2026-07-unknown-key-strictness-ledger.counts/data.md, packages/spec/src/migrations/entries/semantic/18.currency-config-fixed-default-currency-required.ts and packages/spec/src/migrations/registry.ts.template (what main's #27218 put beside this PR's entries), packages/spec/scripts/build-upgrade-guide.ts (:137, the Done when: renderer), the .changeset/27182-currency-value-schema-retired.md (judged in ② only), and, for the E2 re-check, PR #27239's docs/adr/0144-multi-currency-record-currency-dated-rates-write-time-home-projection.md and docs/adr/0104-field-runtime-value-shape-contract.md at 775d8c09666de79e0ece0c2d8dbb3c58d08c8fba (read, not reviewed). The remainder of the 26-file diff (the five packages/spec/*/data.json baselines, field.test.ts) is the lane seat's ACCEPT at TIER_DEFAULT.

Inputs: card #27182 (body, claim 6467480691, dev report 6468102269; ruling 6465431966 read by gh api: "one XS prerequisite (retire the @deprecated CurrencyValueSchema …)" under Q1 = 乙-现在, shape C′), PR #27233 (body, 26-file list — identical to the net diff's file list — and the net diff against main at this head, 26 files), the 50 check-runs on this head, the lane's rework note (ACCEPT 6471133854, read as a claim), and the prior FAIL record 6470084342 at c85e08be. The merge-base with main is 0cd578f726 (#27218), the PR's recorded base; origin/main has since moved 18 commits to 5b5cff15a5 (14:35Z).

Gate verdicts on this head, as the check-runs conclude them. 50 check-runs, all completed: 41 success, 9 skipped (opt-in and matrix-placeholder jobs), 0 failed, 0 pending. Green: Check Changeset, Spec property liveness, Governed Surface Queue Guard, Build Core, Lint & Repo Gates, every Lint · and Type Check · lane, TypeScript Type Check, Test Core (8/8 shards), Test UI, Build Docs, Build & E2E UI, Live E2E UI, Dogfood Regression Gate, PR-body guards, Check PR Size, Flag docs affected, Check Documentation Links. They answered at 13:42–14:03Z on a merge with the PR's base 0cd578f7. main has moved since: git merge-tree --write-tree origin/main prr-27233 is clean (exit 0), git log -S CurrencyValue over fd73f2b3..origin/main outside CHANGELOGs is empty, and main's index.mdx total (1560) and Iso_ census (799) are unchanged since the PR's base, so the merged counts this head pins (1559, 798) still hold against main today. gh api pulls/27233 now: mergeable: true, mergeable_state: clean.

Moved head: the delta c85e08be..a4f0d909

Five commits, judged one by one; the two net diffs differ only where the delta says they should (ADR note, index.mdx counts, Iso_ census, field.mdx and field.zod.ts context moved by #27218):

  • bbc6381c — docs/adr/0104…:184-187, 1 file +4/−3, prose. The dated note now reads "RETIRED_DEFS_BY_MAJOR[18] (data/CurrencyValue, entry file …/retired-defs/18.data__CurrencyValue.ts), the D3 semantic entry currency-value-schema-retired (entry file …/semantic/18.currency-value-schema-retired.ts) and the changelog". Both paths exist at this head (git ls-tree), and each sits on the entry whose file it is. Prior M1: resolved exactly.
  • 4ab0b546 — merge of main 4d3a077f. git merge-tree --write-tree bbc6381c 4d3a077f conflicts in exactly the two files the prior record's E1 named: content/docs/references/index.mdx and packages/spec/src/type-alias-convention.pin.test.ts; every other path auto-merges. The committed merge resolves the pin test correctly (header, title and toHaveLength at 798; the spec/metadata-protocol: a draft change written through the MCP build door is recorded as by: 'copilot'; draftChanges cannot say "an MCP agent (OAuth client X) on behalf of user Y" (cloud#3302 item 5) #27176 798 → 799 history block and this PR's 799 -> 798 block both kept, in order) and resolves index.mdx to the PR's side (1558 / API 439, without MetadataAuthoringClient in the protocol.zod.ts row) — a stale resolution, repaired by the next commit. Outside those two files the merge tree is byte-identical to the recomputed one: no evil-merge content.
  • 962e3844 — regenerates index.mdx on the merged tree: against main's 4d3a077f copy the file differs only by 1560 → 1559, Data 185 → 184, and CurrencyValue gone from the field.zod.ts row. Right; closes the stale resolution above and the prior S3's index.mdx half.
  • 29309420 — merge of main 0cd578f7 (fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218, CurrencyConfigSchema). git merge-tree --write-tree 962e3844 0cd578f7 is clean (exit 0). The committed merge differs from the recomputed tree in ONE file, content/docs/references/data/field.mdx: the two defaultCurrency rows carry the PR's pre-fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218 text (optional (default: "CNY")) instead of main's regenerated optional … required under fixed — a two-line reversion of main's generated page inside the merge commit itself. Repaired by the next commit; named here because it is the one evil-merge hunk in the delta.
  • a4f0d909 — regenerates field.mdx: restores the two rows. git diff between the recomputed clean merge tree (79ede83b) and this head's tree is empty, so the head carries no evil-merge residue at all, and the net diff's field.mdx hunk is exactly the CurrencyValue section removal (3 insertions / 15 deletions, both defaultCurrency rows as main has them).

Prior must-fix M1: resolved exactly (bbc6381c). Prior should-fix S3 (the E1 merge: main merged, census 798 with both history lines, index.mdx 1559, gates on the new head): resolved exactly across 4ab0b546, 962e3844, 29309420, a4f0d909 and the 41 green runs. Prior S2 lives in #27236 (unchanged, lands after this PR). S1 (closed at c85e08be) stays closed.

① Derived judgments

Every accept-set and public-surface change the diff implies, judged against the tree at this head (the non-delta hunks are byte-identical to c85e08be; re-read, not adopted):

  1. CurrencyValueSchema (const) and CurrencyValue (type) leave @objectstack/spec/data and the root entry. field.zod.ts −20 lines, pure deletion, now sitting directly under fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218's }).superRefine(refuseFixedCurrencyWithoutCode)); close of CurrencyConfigSchema (:600), which the merge placed without touching either side. git grep CurrencyValue at this head outside CHANGELOGs and release prose finds only the retirement kit (both entry files, the new pin, the changeset), the Iso_ history line, authorable-surface.base.json (the released anchor, untouched), and prose (field-value.zod.ts docblock, field-value.test.ts:258 test name, types.mdx, field-value.mdx, ADR-0104, the strictness ledger). Right. ADR-0104 D1 decided it; ruling 6465431966 names it the C′ prerequisite.
  2. The runtime value accept-set for a currency field is unchanged. valueSchemaFor({ type: 'currency' }) answers a finite number before and after; the new pin asserts 1000.5 accepted and { value, currency } refused. Right.
  3. json-schema.manifest/data.json −1 (data/CurrencyValue); RETIRED_DEFS_BY_MAJOR[18] +1 via retired-defs/18.data__CurrencyValue.ts (export const entry = 'data/CurrencyValue'). api-surface −2, authorable-surface −2, declaration-map −2, export-origins −2: the whole-def shape. registry.ts is generated from registry.ts.template + entries/; fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218's template change (a STEP18_RATIONALE fragment, order 112) adds nothing this PR's entries must answer. Right.
  4. authorable-surface/data.json loses data/CurrencyValue:currency and :value; authorable-surface.base.json untouched. No [RETIRED] row: no walked shape ever held a key (the identity/ApiKey precedent). Right.
  5. D3 semantic entry currency-value-schema-retired under major 18; no D2 conversion, no conversionIds, no rationale fragment. Re-checked against the MERGED CurrencyConfigSchema (fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218): currencyMode: z.enum(['dynamic','fixed']).default('dynamic'), defaultCurrency: z.string().length(3).optional(), superRefine(refuseFixedCurrencyWithoutCode) — the replacement's currencyConfig: { currencyMode: 'fixed', defaultCurrency: 'USD' } parses, and is now the ONLY valid spelling of a fixed-mode field, so the prescription is tighter-true after the merge than before. surface carries no backticks (build-upgrade-guide.ts renders it in a code span). Right, with one prose wrinkle in acceptanceCriteria — ④ S4.
  6. Liveness ledger: no row added, none removed (CurrencyValue was never walked). Spec property liveness green. Right.
  7. Strictness ledger: the hand-written field.zod.ts row gains the dated parenthetical, keeps its 2026-08-13 verdict for LocationCoordinatesSchema; the counts file moves data/ 165 → 164 / strip 75 → 74, field.zod.ts 14 → 13 / strip 2 → 1, "no door" 2 → 1; the spec: remove the deprecated LocationCoordinatesSchema / CurrencyValueSchema dead exports at the next spec major (ADR-0104) #8562 line says "one". The counts hold on the merged tree (fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218 added a refinement function, not a strip site) — Lint & Repo Gates green. Right.
  8. type-alias-convention.pin.test.ts: Iso_data_field__CurrencyValueSchema removed; census 799 → 798 in header, title and toHaveLength, the spec/metadata-protocol: a draft change written through the MCP build door is recorded as by: 'copilot'; draftChanges cannot say "an MCP agent (OAuth client X) on behalf of user Y" (cloud#3302 item 5) #27176 history block kept and this PR's 799 → 798 block appended after it. The count is machine-recomputed by the test; Test Core green. Right, and now right against main as it stands (799 there).
  9. Generated reference docs and the five skills/*/references/_index.md: field.mdx loses the CurrencyValue section and the two import lines drop the names; index.mdx 1560 → 1559 / Data 185 → 184; the five skill indexes replace CurrencyValueSchema with UniqueScopeSchema in the field.zod.ts Exports: row — the generator's mechanical fill, one row each, no rule or example touched. Right. docs/adr/** and skills/** make the PR Tier H; Governed Surface Queue Guard green.
  10. New pin currency-value-retirement.test.ts, registered in vitest.repo-tests.json (alphabetical slot held). Registry presence of both entries (conversionIds undefined asserted), export absence from /data and root with CurrencyConfigSchema as the lit control, the value contract both ways, and the tree-scoped absence walk over packages, examples, skills, content, scripts with anti-vacuity floors (visited over 1000, example sources over 50) and a matcher self-test that must match braced spec-specifier imports and Data.CurrencyValueSchema, and must not match prose, local specifiers or CurrencyConfigSchema. Neither new registry file spells a matchable use. Right.
  11. Prose on the floor — field-value.zod.ts docblock ("is deleted"), field-value.mdx (generated from it), types.mdx ("has been removed"), and the three ADR-0104 edits — all true on this head (④).

Nothing widens. CurrencyConfigSchema is changed on this head only by main's #27218, which this PR merges and does not touch.

② Semver level

.changeset/27182-currency-value-schema-retired.md: '@objectstack/spec': minor; Clause-②: no (narrowing) and Metadata-protocol: narrowed line-initial; one disposition marker adr-0087: registered currency-value-schema-retired (the id exists at semantic/18.currency-value-schema-retired.ts); the BREAKING banner (TypeScript imports only, TS2305); the FROM → TO mapping; the one-line fix. The PR body carries the same two declaration lines.

  • Level — a public-surface removal from @objectstack/spec is BREAKING; the launch window ships it minor (check-changeset-no-major refuses major; the ApiKeySchema precedent). Check Changeset green on this head. Matches.
  • Metadata-protocol: narrowed — the diff moves authorable-surface/data.json (two rows), which none may not do; narrowed beside (narrowing) is the consistent pair. Matches.
  • Clause-②: line — no (narrowing), one arm of the closed pair, on body and changeset. Matches.

③ Boundary flags

④ Design judgment

For each hits file, against the code it cites and the scenario it is accepted against:

  • field.zod.ts, field-value.zod.ts (schema face, against ADR-0104 D1 and the first consumer valueSchemaFor) — the deletion lands at the declaration; the value contract answers a bare number before and after; the docblock states the fact. On the merged tree the deletion sits beside fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218's tightened CurrencyConfigSchema, and the two compose: the code is field configuration, and a fixed-mode field must now name it — exactly what the D3 replacement prescribes. Does the narrowing take a declared capability away at a consumer? No consumer ever had it. Does it make the spec's own refusal real? The refusal (field-value.test.ts:258) was already real; the contradiction beside it is gone. Holds.
  • retired-defs/18.data__CurrencyValue.ts, semantic/18.currency-value-schema-retired.ts (against the manifest deletion proof in build-schemas.ts, the entries README, the identity/ApiKey precedent, and now the sibling fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218 entry under the same major) — id, file name and manifest key agree; reason / replacement true on the merged CurrencyConfigSchema; each file names the other. S4, should-fix: acceptanceCriteria says "CurrencyConfigSchema is unchanged." and build-upgrade-guide.ts:137 renders that sentence as the entry's Done when: line in the 18.x guide, where the sibling entry currency-config-fixed-default-currency-required (fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218, same major) tells the same reader that the schema lost its 'CNY' default and refuses a fixed config without a code. The sentence is true of THIS retirement (it changes nothing in the config schema, and the pin's lit control asserts only survival), so it misleads no gate and no code path, but as upgrade-guide prose it reads as a claim about the major. "unchanged by this retirement" would close it; prose only, one string. Holds.
  • skills/*/references/_index.md (against the generator and the spec on main) — one mechanically ranked row per index; Governed Surface Queue Guard green. Holds.
  • docs/adr/0104-field-runtime-value-shape-contract.md (against its first consumer — a reader ADR-0144 :22 sends here for where the FROM → TO note lives — and against check:adr-symbol-anchors):
    • :79 — anchor replaced by a file-level mention plus "deleted 2026-10-28, see the note under "Reality wins""; no anchor form introduced; the gate is green. Right.
    • :166-168 — the decision sentence names the real landing sites (RETIRED_DEFS_BY_MAJOR, D3 entry, changelog). Right.
    • :172-186 — the dated note; after bbc6381c each entry file sits on its own registry entry, both paths exist at this head, and the note's "the code is currencyConfig.defaultCurrency" is still true under fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218 (a fixed field's code; a dynamic field reads the tenant default). Right; M1 closed.
  • Tried to break it, this round: (a) the two merges recomputed with merge-tree — one conflict pair resolved right, one stale resolution and one evil hunk, both undone by the regeneration commits; head tree equals the clean recomputed merge; (b) a surviving reader on main today — none; (c) the D3 replacement against the MERGED CurrencyConfigSchema — parses and is the only valid fixed-mode form; (d) the 18.x upgrade guide read as one document beside fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218's entry — S4; (e) the ADR's two file pointers — both exist, each on the right noun; (f) ADR-0144 at 775d8c09 for anchor rot — gone; the ADR-0104 notes still merge clean; (g) main's 18 newer commits against this head — merge-tree clean, counts unchanged.

Must-fix: none.

Should-fix (non-blocking):

Implemented-by: claude/project-thread-g1qmlm
Reviewed-by: session_01AavokzJ5DndAwitDXvKy4U

VERDICT: PASS


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 29, 2026 01:20
@os-zhuang
os-zhuang enabled auto-merge October 29, 2026 01:20
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit b8f4acc Oct 29, 2026
51 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-g1qmlm branch October 29, 2026 01:42
os-bill pushed a commit that referenced this pull request Oct 29, 2026
os-bill pushed a commit that referenced this pull request Oct 29, 2026
The authorable-surface baseline is taken from main (#27233 retired
`CurrencyValue`) and rebuilt, keeping `currencyConfig.currencyField`;
the field reference page is regenerated from the merged tree.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CbfVSc1hXtxLUPHZ2f
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:data size/m tests tooling

Projects

None yet

3 participants