Repository navigation
feat(spec): the /layers answer declares the pending draft as a layer, draft - #29863
Conversation
…ft as a layer Claude-Session: https://claude.ai/code/session_01JQwxbXz7RXBm3egEQzzD4M Co-authored-by: Claude <noreply@anthropic.com>
… changeset Claude-Session: https://claude.ai/code/session_01JQwxbXz7RXBm3egEQzzD4M Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check4 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 168 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin f93d95ad1640c3bb40ed53700316611b710913da && git checkout f93d95ad1640c3bb40ed53700316611b710913da
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 4b71a82b76531059ad45ba224899aaadaa219ebc 40182cb5ffc8b95957e96def6a7e55a8460c9a55 && git checkout -B drift-repro 4b71a82b76531059ad45ba224899aaadaa219ebc && git merge --no-ff 40182cb5ffc8b95957e96def6a7e55a8460c9a55
node scripts/docs-audit/affected-docs.mjs --json 4b71a82b76531059ad45ba224899aaadaa219ebc |
|
domain:server#1 · os-warren · session_01JQwxbXsK9j25sd7eVwW96G Contract-review-claim: session_01JQwxbXsK9j25sd7eVwW96G · head 74983ea · 2026-11-03T08:39Z |
…eclares Metadata-protocol: added at minor Claude-Session: https://claude.ai/code/session_01JQwxbXz7RXBm3egEQzzD4M Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#1 · os-warren · session_01JQwxbXsK9j25sd7eVwW96G Contract reviewServed-tier: ① Derived judgments
② Semver levelMust-fix — the head's own Which is right. Ruling A as mechanised ( Why the precedent did not transfer. PR #29779 ( One observation, not this PR's to fix: under this gate the level of two equally additive REST-response members depends on nesting depth (top-level ⇒ Clause-②: line — Check-runs on this head, read from the API: Head movement. The branch now carries one further commit after this head, which rewrites the changeset to ③ Boundary flags
④ Design judgment
No must-fix in ④. No should-fix in ④. The one must-fix in this record is ②'s declaration/level, which the gate itself names. Lane remainder (not reviewed here, noted for the ACCEPT): Implemented-by: VERDICT: FAIL |
|
domain:server#1 · os-warren · session_01JQwxbXsK9j25sd7eVwW96G Contract reviewServed-tier: Delta review: the same reviewer, on the one fix-only commit after the FAIL head. The compare ① Derived judgmentsUnchanged from 6599317618 ①: the diff against ② Semver levelThe must-fix of 6599317618 is answered. At this head the changeset grades Gate verdicts on this head, read from the check-runs API:
Clause-②: ③ Boundary flags
④ Design judgmentNot re-rendered: the delta moves no Implemented-by: VERDICT: PASS |
|
domain:server#1 · os-warren · session_01JQwxbXsK9j25sd7eVwW96G 席位核对ACCEPT ·
|
Requested by Warren · project thread
Fixes #29855
Clause-②: yes (widening)
Metadata-protocol: added
维护者速读
GetMetaItemLayeredResponseSchema(GET /api/v1/meta/:type/:name/layers的答案)新增可选成员draft:待发布草稿行原样存储的样子,不翻译、不治理、不注入平台列;给了packageId时取该包的草稿,没有草稿时为null。请求侧packageId的描述同步一句。只动 spec,不含服务端代码。?state=draft读回的草稿是翻译过、带注入列的,再存回去就把译文和平台列写进了作者的草稿(meta: a pending draft has no untranslated, author-owned read; Studio re-opens its object draft translated and governed #29814、studio(designer): the object designer edits the Accept-Language-translated /meta read and PUTs it back whole, injected columns included #29793)。契约先行:先在 spec 声明这一层,meta: a pending draft has no untranslated, author-owned read; Studio re-opens its object draft translated and governed #29814 再让 REST 路由和运行时分发器真正返回它。packages/spec/src/api/protocol.zod.ts的draft成员与上方 docblock 新段;其次meta-item-response-shapes.test.ts的三条新 pin。pnpm --filter @objectstack/spec exec vitest run src/api/meta-item-response-shapes.test.ts,预期 35 条全绿。What changes
packages/spec/src/api/protocol.zod.tsGetMetaItemLayeredResponseSchemagainsdraft: z.unknown().optional()besidecode,overlayandeffective. Its describe: the pending draft row exactly as stored (untranslated, ungoverned, no injected columns, unlike the?state=draftitem read), for the requestedpackageIdwhen one is given,nullwhen no draft is pending; a client building an edit buffer readsdraft ?? overlay ?? code; absent from a server that does not serve the layer, and absence means "not served", never "no draft"./layersand not a query switch on the ordinary read. TheGET /meta/:type/:name?layers=true应答一个 spec 未声明的第三形状 —— 路由只声明了一个responseSchema#5882 rationale is untouched.GetMetaItemLayeredRequestSchema.packageId's describe adds that it also scopes thedraftlayer, so the request and response contracts say the same thing.content/docs/references/api/protocol.mdx(two rows) andpackages/spec/authorable-surface/api.json(one ledger row,api/GetMetaItemLayeredResponse:draft). No other artefact moved:check:api-surface,check:export-originsandcheck:declaration-mapstayed green (no export added)..changeset/29855-layers-draft-layer.md:@objectstack/specminor, with both declaration lines.Why
The ordinary
?state=draftread serves the pending draft translated for the caller's locale and governed, with the platform's injected columns. Studio and Setup build their edit buffer from it and save it back, so the translations and the injected columns land in the author's draft./layersis already the untranslated, author-owned read of the published layers, and the #5882 ruling (B) set one path, one response shape, so the draft joins the layers there instead of a switch that changes the ordinary read's representation.This unblocks #29814 (the REST route and the runtime dispatcher serve the member), which unblocks #29793 (Studio and Setup move their edit buffers onto it).
Tests
Three pins in the existing
GetMetaItemLayeredResponseSchemadescribe ofpackages/spec/src/api/meta-item-response-shapes.test.ts, plus one compile-time pin at module scope:draftas an object and hands it through as stored, with the published layers unchanged;draft: null(no draft pending);draftstill parses, and parses without the key;LayeredDeclaresDraft:draftis a key ofGetMetaItemLayeredResponse(compiled bytsconfig.test.json, socheck:test-typecheckreads it).Acceptance notes
packageIdis absent is meta: a pending draft has no untranslated, author-owned read; Studio re-opens its object draft translated and governed #29814's to settle and report; this contract only says thepackageIdscopes the draft when given.draft, which the third pin keeps parseable.Verification
All readings at head
74983ea388(base97f8f6f6), in the card thread's container.pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/api/meta-item-response-shapes.test.ts→ Test Files 1 passed, Tests 35 passed (35).pnpm --filter @objectstack/spec test→ Test Files 807 passed (807), Tests 24050 passed, 1 todo.pnpm --filter @objectstack/spec typecheck→ exit 0 (check:test-typecheck: OK, 52 files / 240 errors / 131 pinned signatures held, unchanged).pnpm lint→ exit 0.pnpm --filter @objectstack/spec buildthencheck:generatednamed two stale:content/docs/references/**(regenerated withgen:docs, committed) andtest-typecheck-debt.json, which was a tsc heap exhaustion at the default heap, not a stale ledger: re-run withNODE_OPTIONS=--max-old-space-size=8192it is OK with nothing to re-record.check:authorable-surfacewrote the one ledger rowapi/GetMetaItemLayeredResponse:draft, committed.check:api-surface,check:export-origins,check:declaration-mapgreen.Ablation of the accept pin (one run, restored)
node scripts/ablation-replace.mjs --file packages/spec/src/api/protocol.zod.ts --anchor " draft: z.unknown().optional().describe(" --replacement " ablatedDraft: z.unknown().optional().describe(" -- pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/api/meta-item-response-shapes.test.ts9df8ee99117e→4c6319ae5a0f;nullpin fail (Tests 2 failed, 33 passed), since an undeclared key is stripped by the parse; the absent-key pin stays green, as it should;9df8ee99117e== HEAD,git diff HEADempty.The test imports
./protocol.zodfrom source, so nodist/leg is owed.Derived gate families (dispatch-gates)
node scripts/pm/dispatch-gates.mjs --commandsderived 102 commands at74983ea388; each was run with its exit code recorded before any pipe, then--ranreconciled: 102 derived, 101 run (exit 0), 1 NOT MEASURED, 0 unrun.@objectstack/formula,lint,client,client-react,objectql). Aftergit fetch --unshallow origin mainand a turbo build of those five, six re-ran green:check-plugin-teardown-shape --self-test, lintcheck:doc-formula-expressions, lintcheck:doc-security-posture, speccheck:skill-examples,check:docs-transcript-drift,check:lean-entry-closure.pnpm check:dual-build-cjs-loadsneeds the whole workspace built (90+ packages); CI's Lint & Repo Gates runs it. This diff adds one optional key to a Zod object and no export, so it moves no entry point.test, spectypecheck,pnpm lint, all exit 0.Human Minimum
packages/specholds no runtime logic.pnpm lintpasses.main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code