Uh oh!
There was an error while loading. Please reload this page.
fix(spec): DeleteMetaItemResponseSchema declares seq and projectionApplied, matching its two sibling verbs - #13208
Conversation
…lete-response-parity
📓 Docs Drift CheckThis PR changes 1 package(s): 16 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 2 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 126 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 8079764a68875d930b9a911ea37fab8ca02bf4af && git checkout 8079764a68875d930b9a911ea37fab8ca02bf4af
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e452ad54238164dd514e04704b3c1df6dd9c11a5 408da7bd262713b71000c2dbb69de59f4930f3df && git checkout -B drift-repro e452ad54238164dd514e04704b3c1df6dd9c11a5 && git merge --no-ff 408da7bd262713b71000c2dbb69de59f4930f3df
node scripts/docs-audit/affected-docs.mjs --json e452ad54238164dd514e04704b3c1df6dd9c11a5
|
…lete-response-parity
The `api/` bucket moved on both sides of the merge — main widened the analytics and automation route response schemas (+4), this branch widened DeleteMetaItemResponseSchema (+1). The counts file carries a `merge=os-regen` driver, so the merge left it un-text-merged; this is the regenerated value, produced by `pnpm --filter @objectstack/spec gen:strictness-ledger`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4
Uh oh!
There was an error while loading. Please reload this page.
Fixes#13155
DeleteMetaItemResponseSchemanow declaresseqandprojectionApplied— the two keys its own branch has always sent, and the two keys both sibling verbs on the same metadata door already declare. This carries the ruling in #5745 (「[#5563 附带裁决] SaveMetaItemResponseSchema 补齐实现实际返回的字段(version / seq / state / projectionApplied)」) across to the third verb: that decision was made forsaveMetaItem, extended topublishMetaItemby #7294, and never carried here. Declaration only — zero runtime behaviour changes, and nothing here claims the wire was wrong. The wire is right, the two siblings agree with it, and the third declaration was short.Premise check, measured on
origin/mainbefore any editThe card's premise — the delete branch's sent set is expressible in the siblings' shape — holds. Readings:
ObjectStackProtocolImplementation.deleteMetaItem(packages/metadata-protocol/src/protocol.ts:18999)const result = await p.deleteMetaItem(deleteRequest); res.json(result);(rest-server.ts:5941) — verbatim, no route-level mutation, so the protocol return is the wire bodyseq?: numberandprojectionApplied?: MutationProjectionOutcome(:19013-:19020)projectionAppliedshapeinterface MutationProjectionOutcome { success: boolean; error?: string }(:3758) — byte-identical to the inline object both siblings declareseqsourceDeleteResult { seq: number }(packages/metadata-core/src/types.ts:225) — the same integer sequence the siblings declareFour success returns, not one — this is the one place the mirror is a shape mirror rather than a member-list copy:
:19260) —seqalways;projectionAppliedwhen a projector is registered. The only branch carrying either key.:19169) — success/no-op, appends no history event.:19409) — reachable in control-plane bootstrap for a code-only type (saveMetaItem 的 legacy raw-engine 分支在 #5086 的门之后已不可达 —— 一段 grep 找得到、运行时永不执行的代码 #5264, deliberately alive). Writes no history row and emits no watch event, so noseqeven though a row really went away; its receipt message omits the[seq=…]suffix for the same reason.:19377) — the same no-op as (2).So both keys land
.optional()here whileseqis required on both siblings. Declaring it required would make three of the producer's own returns fail their own contract — the #5563 defect in mirror image.seqabsent means "this branch appended no history event", never "nothing was deleted";resetis the key that answers that.Two sibling keys are deliberately not declared, because this branch provably never sends them:
version(a delete mints no new content hash, so there is no ADR-0008 OCC token to echo) andadvisories(the #4463 authoring gate runs on the two write doors by D1 — a delete submits no body for it to judge). Pinned directly, so a later "finish the mirror" cannot add a contract for bytes no producer emits.What is pinned
packages/spec/src/api/protocol.test.ts) — the widened keys parse;seqstays an integer and rejects a fractional one;projectionApplied.successis required once the key is present; the three no-seqbranches parse green; the unknown-key posture is preserved (version/advisoriesare still stripped). Two exported module-scope type pins state the card's cost as a compile-time fact: both keys are now reachable from aDeleteMetaItemResponsewithout anas any. Verified real rather than phantom —tsc -p tsconfig.test.json --listFileslists this file, andcheck:test-typecheckcompiles the layer.packages/objectql/src/delete-meta-response-conformance.test.ts, new) — the sibling gates' exact shape, driving the real protocol against a real ObjectQL engine: nothing is stripped,projectionAppliedis carried through when a projector throws, andseqreally advances past the save's event rather than merely being present.Reverse verification (ablation)
The
objectql→specpair is registered unaliased inKNOWN_UNALIASED_TEST_IMPORTS, i.e. it resolves throughdist/, so both legs were rebuilt and both were proved on disk.Attempt 1 was a false green, and is reported because it is the interesting result. Narrowing the schema alone is a bare deletion against the regenerated
authorable-surface/api.json, sogen:schema's deletion guard failed the build; the marker never reacheddist/, and the suite ran the pre-mutation build and passed 5/5.ablation-dist-preflight --absent/present is what caught it. Recorded as a finding in its own right: the declaration is now guarded by two independent mechanisms, so a future bare deletion of either key cannot land quietly.Attempt 2 reverted the baseline lines in the same mutation so the build agreed with itself:
The one case that stayed green under ablation is the "no overlay row" no-op, which carries neither key — so nothing is stripped there either way. That is the same property the siblings record about their own conditional keys, and it is why the no-op branch needs the direct
seq === undefinedassertion it has rather than relying on the strip check.Restore leg, symmetric: sources restored byte-identical (
git diff HEADempty; blob hashes equal to theirHEADblobs), spec rebuilt,ablation-dist-preflight … --absent→✓ marker absent from all 215 built files, suite green again 5/5.Generated artifacts
Regenerated with the repo's own tooling, never by hand —
check:generated --fixon exactly the two it proved stale (gen:docs,gen:strictness-ledger);authorable-surface/api.jsonwas rewritten bygen:schemaduring the build, which is the expected product. Deltas are minimal and mechanical: twoapi/DeleteMetaItemResponse:*lines,api/strictness sites 444 → 445, and nine lines of reference docs.check:api-surfaceandcheck:export-originswere already green — the change adds no export.type-alias-convention.pin.test.tsis not touched (PR #13186 owns it): the widening mints no new pin, because.optional()widens input and output identically — the precedent that file already records foradvisoriesonSaveMetaItemResponseSchema. Confirmed mechanically rather than by reading:check:spec-parsed-aliasreports 837 pinned isomorphic, exactly the count that file asserts.Nothing in
packages/clientchanges and #13023 is not re-opened — PR #13148 binds the type, so it inherits this fix for free.One in-scope fix inside my own new file:
check:objectql-double-limitfailed it as a NEW limit-blindfinddouble (the two sibling conformance files predate that gate and sit in its shrink-only baseline). Repaired per the gate's own prescription — the bound is applied after the filter, by presence.Verification
Run after the final commit, on
c2b36e4a3(currentorigin/mainmerged in first —api-surface/export-originsare hot files, andpackages/specmoved on the incoming side, so the closure was reinstalled and rebuilt before any of this was read). Exit codes captured before any pipe; each line quotes the gate's own verdict.check:generated(all 14 spec artifacts)✓ All 14 generated artifacts are up to date.pnpm --filter @objectstack/spec typecheckcheck:test-typecheck: OK — @objectstack/spec's test layer compiles under packages/spec/tsconfig.test.jsonpnpm --filter @objectstack/spec testTest Files 443 passed (443)·Tests 11775 passed (11775)objectql)Test Files 3 passed (3)·Tests 28 passed (28)pnpm --filter @objectstack/client testTest Files 30 passed (30)·Tests 402 passed (402)— the corroborating run; nothing in it should change, and nothing didpnpm lint(whole repo,eslint . --no-inline-config)check:objectql-double-limitOK ObjectQL double limit conformance holds·baseline key set verified against c25bfb3: no files added.check:engine-double-contractOK — 705 pinned, 134 in the DEBT ledger, 3 exempt.check:where-matcher315 matcher(s) discovered, 315 answer the combinator battery correctly or refuse it loudly·none newcheck:spec-parsed-alias1528 bare z.input aliases, 837 pinned isomorphic, 691 paired with an XParsed. OKcheck:cross-package-test-inputsOK: 23 package(s) read outside themselves, all declaredcheck:test-source-aliasOK — 72 packages with tests scannedcheck:type-check-coverage,check:query-options-erasure,check:doc-authoring,check:merge-driver,check:published-filescheck:empty-changeset,check:changeset-no-major,check:adr-0087-registration,check:changeset-gate-self-tests,check:objectui-changeset,check:pm-half-states)1 declaring changeset(s) added, nomajorbumpThe gate family was derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no hand-fed paths — the script takes the changeset from the merge base itself).One thing is NOT MEASURED and is reported as such rather than as a pass:
packages/objectql'stypecheckistsc --noEmitagainst atsconfig.jsonwhoseexcludecarries**/*.test.ts, and the package has no sibling test-layer program ortest-typecheck-debt.json.tsc --listFilesreturns 0 hits for the new conformance file, so a green objectql typecheck says nothing about it; its types are exercised by the vitest run above and by CI, not by a static program. That is a pre-existing property of the package, not something this PR changes, and it is stated so the green above is not read as covering more than it does.Tier
The implementation tier was
opusunder the recorded quota-exhaustion exemption (maintainer 2026-08-13, 「fable 如果用完了,可以用 opus」), withneeds:contract-reviewon this PR as the compensating control.This PR was generated by Claude Code in session
session_01LpRNHxWZgSUgVnFT9mQQo4(https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4) — carried as prose because a body PATCH normalises the footer link (#12886).Generated by Claude Code