Repository navigation
fix(metadata-protocol)!: history and rollback doors refuse a bare two-holder name and address one holder by ?package= - #33193
objectstack-fleet[bot] wants to merge 20 commits into
Conversation
… of a by-package name WIP: the /meta history and rollback doors take ?package=, a bare request of a name two row slots hold is refused naming both, and the repository records whose row each history snapshot is of. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
…ress one holder by package Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
…he holder refusal with candidates Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
…e name Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
…dress Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
📓 Docs Drift CheckThis PR changes 5 package(s): 56 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 17 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 178 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 a1452deeba305b43e2116e76a672a51450492be6 && git checkout a1452deeba305b43e2116e76a672a51450492be6
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b63c894934b5e427b456b997774f48a220edb7e5 952550aea38aba428e045206b01aedc104f49090 && git checkout -B drift-repro b63c894934b5e427b456b997774f48a220edb7e5 && git merge --no-ff 952550aea38aba428e045206b01aedc104f49090
node scripts/docs-audit/affected-docs.mjs --json b63c894934b5e427b456b997774f48a220edb7e5
|
…y it minor Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
|
epic:#31527#3 · os-tesla · session_01L9qibzECS2npZFku4956Um Contract reviewServed-tier: Read: card #32905 and its five comments (triage 6839348119, claim 6867138888, measurement 6867256997, regrade 6867282696 "stays p2", os-dev-report 6869029675); the PR body, file list and net diff against Check-runs on this head when judged: ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implementing files, tried against the brief's four attacks. Can a bare rollback still write one holder's row when two hold the name? Not when both hold a stored active row, whether two pairs or a package beside the package-less row: refused 409 before anything is read. A holder whose row is inactive or deleted is not a slot, so the name is then one-slot and resolves bare; the removed holder's attributed versions are refused by
Does
Does writing Does a one-holder name's bare history and rollback answer exactly as on
Agreement with the contract, remaining sentences.
Findings: F1 must-fix (red Implemented-by: VERDICT: FAIL |
…does Bare history and rollback now also refuse where the bare plain read refuses (a second holder in code counts), and a bare rollback of a one-slot name restores what main restores. `?package=P` addresses the slot of the row a read naming P serves, including the package-less fallback; with no stored row an uninstalled package is refused like a save naming it. `?package=` on a type not stored per package is refused 400 on both doors. The rollback request gets a spec schema. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
…nd which answer as before Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
… collection in the slot pins Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
|
epic:#31527#3 · os-tesla · session_01L9qibzECS2npZFku4956Um Contract reviewServed-tier: Delta review, rendered 2026-11-14T04:03Z, of the patch round on record 6869129898 (head Check-runs on this head when judged. ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Each finding of record 6869129898, against the code.
Red line 3, tried again on this head. A one-holder name, bare: history unfiltered, rollback unchecked, both as Findings on this head: N1 should-fix ( Implemented-by: VERDICT: PASS |
… number of at least 1 The rollback request's spec schema now declares `toVersion` as the door enforces it, and the door reads that declaration in place of an undeclared whole-number parse. The changeset grades every package but spec `patch` under the breaking banner, names the zero-row two-shipper refusal, and states what the bare doors do not yet ask for an action. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
|
epic:#31527#3 · os-tesla · session_01L9qibvyhxPWfA8DsbmVYJ5 Contract-review-claim: session_01L9qibvyhxPWfA8DsbmVYJ5 · head abe8a83 · 2026-11-14T14:21Z The delta review of the N1 round owed by record 6871399466 (the seat that rendered it has signed off); run at |
|
epic:#31527#3 · os-tesla · session_01L9qibvyhxPWfA8DsbmVYJ5 Contract reviewServed-tier: Delta review of the N1 round owed by record 6871399466 (PASS on Check-runs on this head when judged (72 runs, all completed, none running, none failed): 60 success, 11 skipped (opt-in or matrix placeholders: 18.x upgrade harness, Packed-tarball smoke, Temporal Conformance, Dogfood Verify CLI, UI Published Artifacts, Queue verdict, Borrowed queue verdict, Schedule gate, and the skipped twins of Auto Label, Check PR Size and the Dogfood shard template), 1 cancelled ( ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Findings on this head: M1 must-fix (the console's Implemented-by: VERDICT: FAIL |
|
epic:#31527#3 · os-tesla · session_01L9qibvyhxPWfA8DsbmVYJ5 The brief this card thread handed its isolated reviewer for the record above (head Contract review brief — PR #33193 @
|
… stored per package The console sent the item page's package to the history and rollback doors for every type, and the doors refuse it on a type not stored per package, so History broke for an object, view or page opened under a package. The list of by-package types now lives once in spec (`PACKAGE_SLOTTED_METADATA_TYPES`, `isPackageSlottedMetaType`); the metadata store set is built from it and the History panel asks it before naming a package. A pin holds the doors' refusal and the console's predicate to the same list. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
|
epic:#31527#3 · os-tesla · session_01L9qibvyhxPWfA8DsbmVYJ5 Contract reviewServed-tier: Delta review of the M1 round owed by record 6878813627 (FAIL on Check-runs on this head when read (16:18Z, 49 runs created). Success: ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Findings on this head: no must-fix, no should-fix · two nits (stale file-header sentence in Implemented-by: VERDICT: PASS |
|
epic:#31527#3 · os-tesla · session_01L9qibvyhxPWfA8DsbmVYJ5 Regen-provenance: 6880527562 · A merge-forward of Follow-ups the review asked for are filed: #33308 (diff door), #33310 (SDK 2026-11-14T16:28Z |
|
epic:#31527#3 · os-tesla · session_01L9qibvyhxPWfA8DsbmVYJ5 LandingHead-sha:
Ready + auto-merge now. 2026-11-14T17:02Z |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 43619123356 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
|
epic:#31527#3 · os-tesla · session_01L9qibvyhxPWfA8DsbmVYJ5 Merge-queue red: triageQueue build 43619123356 (tree Signature: First diagnosis: load, not this PR. The file's import closure does meet this diff (
The failing case runs in about 0.5 s alone, both ways. Each case of that describe boots the engine, installs two or three packages and restarts, under the 5 s default; on a loaded shard that is the margin that gave out. Disposition: not re-enqueued blind. The fix is in this PR: that describe gets 2026-11-14T17:33Z |
…d shard Each case of that describe boots the engine, installs two or three packages and restarts it: about half a second alone, but a merge-queue shard timed one out at the 5 s default while the file took 63 s. The file's runtime is the same on main's sources and on this branch's, measured back to back, so the budget is room for load, not for a slower product. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
|
epic:#31527#3 · os-tesla · session_01L9qibvyhxPWfA8DsbmVYJ5 Contract reviewServed-tier: Delta review of the hop Check-runs on this head when read (17:57Z, 37 runs created). Success: Auto Label, Check Documentation Links, Check PR Size, Dogfood Regression Gate, Governed Surface Queue Guard, ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
The hop, tried four ways.
Findings on this head: no must-fix, no should-fix · two nits (the "restarts it" clause over-states what ten of the cases do; the local timings rest on the seat's word). The PASS of record 6880527562 stands on Implemented-by: VERDICT: PASS |
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5
|
epic:#31527#3 · os-tesla · session_01L9qibvyhxPWfA8DsbmVYJ5 Regen-provenance: 6881836907 · A second merge-forward of 2026-11-14T18:33Z |
|
epic:#31527#3 · os-tesla · session_01L9qibvyhxPWfA8DsbmVYJ5 Re-landing after the queue redHead-sha:
Auto-merge again. 2026-11-14T19:05Z |
epic:#31527#3 · os-tesla · session_01L9qibvyhxPWfA8DsbmVYJ5
Requested by Tesla · project thread
Fixes #32905
Clause-②: yes (narrowing)
Metadata-protocol: added
Skills-docs: none — no skill under
skills/names the history or rollback door, and the docs that do (content/docs/api/metadata-api.mdx,content/docs/kernel/contracts/metadata-service.mdx,content/docs/plugins/adding-a-metadata-type.mdx,content/docs/permissions/system-context.mdx) list the routes without teaching how a name two packages hold is addressed, so no sentence there turns false; the generated API reference (content/docs/references/api/protocol.mdx) carries the newpackageIdfield.Levels follow the metadata protocol alone (maintainer ruling A, 2026-10-21):
minorfor@objectstack/spec, the one package that carries the added protocol surface (HistoryMetaItemRequestSchema.packageId, the authorable-surface entryapi/HistoryMetaItemRequest:packageId, the newRollbackMetaItemRequestSchemathat declares the rollback door's request with itspackageIdand atoVersionof at least 1, andPACKAGE_SLOTTED_METADATA_TYPES/isPackageSlottedMetaType, the one list of types stored per package that the doors and the console both read), and the levelCheck Changesetdemands forMetadata-protocol: added. Every other package ispatchunder the BREAKING banner above:@objectstack/metadata-protocol(historyMetaItemandRollbackMetaItemRequesttakepackageId),@objectstack/rest(both doors accept?package=; the rollback door readstoVersionthrough its declaration),@objectstack/metadata(new exportshistoryRowAttribution,ENVIRONMENT_HISTORY_MARK;PACKAGE_SLOTTED_STORE_TYPESis now built from the spec list, same members),@object-ui/data-objectstack(history()/rollback()takepackageId),@objectstack/metadata-core(a field description only) and@object-ui/app-shell(no exported signature changes:HistoryPanelandStudioItemToolsare not exported, andMetadataResourceHistoryPagekeeps its props).维护者速读
?package=只读、只回滚那一家的版本。非按包存储的类型带?package=返回 400。控制台的 History 工具会带上条目页的包。packages/metadata-protocol/src/protocol-history-slots.ts(判定规则)和 dogfood 测试history-rollback-two-holders.dogfood.test.ts。pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=1 test/history-rollback-two-holders.dogfood.test.ts,11 条全绿。What changed
The rule (
historySlotOf, newprotocol-history-slots.ts), for a type stored per(package, name)(PACKAGE_SLOTTED_STORE_TYPES), is the oneDatabaseLoaderalready keeps forMetadataManager.getHistory/rollback:?package=): refused409 RESOURCE_CONFLICTwherever the bare plain read refuses the name, before any row is read or written: a name whose active rows are held by two or more row slots (two packages', or a package's beside the package-less row;ambiguous-in-environment,details.candidatesnaming every holder), a name one row holds while a second package ships it in code, and a name two packages ship with no stored row (the registry'sresolveItem, the resolver the plain read asks, throws the read's own refusal, ADR-0150 D5). One slot or none: answers exactly as onmain, its whole log, and a bare rollback restores any of its versions with no ownership check (so a name saved package-less and later taken over by a package still rolls back to its environment versions, the M1 case).?package=P(the/layers?package=spelling): the slot of the row a read namingPserves (findServedOverlayRowwithservedOverride: P's own row, its.custompair's declared override of P, or the package-less row that read falls back to), else P's own slot when P ships the name. A package that neither holds nor ships the name is refused409not-held-by-qualifierwhile another slot holds it; with no row stored, a rollback naming a package that is not installed is refused422 WRITABLE_PACKAGE_REQUIRED(theuninstalledNamedBaseRefusala save gets) instead of minting a slot.404 VERSION_NOT_FOUNDsaying whose it is ("Nothing was restored.").?package=stays refused400as onmain, nowVALIDATION_ERROR(param: package) saying why, on both doors before either door's early answers; never accepted and dropped.Attribution.
SysMetadataRepository(the/metadoors' store) now writessys_metadata_history.package_idfor by-package types, asDatabaseLoaderdoes (sys_metadata= the package-less row).@objectstack/metadataexports that rule ashistoryRowAttribution/ENVIRONMENT_HISTORY_MARKso both writers read one rule.Doors.
GET /meta/:type/:name/historyandPOST /meta/:type/:name/rollbackdeclarepackagein their closed query sets (META_ITEM_HISTORY_PARAMS,META_ITEM_ROLLBACK_PARAMS), refuse it repeated, foldall/empty to no package (metaItemPackageBinding), and render the refusal withdetails.candidates(packageScopedCandidates, as the plain read does). The rollback door's request is now declared in spec (RollbackMetaItemRequestSchema,toVersiona whole number of at least 1), besideHistoryMetaItemRequestSchema.packageId, and the door readstoVersion(body,versionalias and query) through that declaration in place of the undeclared whole-number parse, so the published contract and the door spell a version one way.Console.
MetadataClient.history()/rollback()takepackageId(sent as?package=); the History tool sends its item page's?package=(routed page), the edit page's history sheet sendsownerPackageId(the package its layered read uses), and Studio's item tools send thepackageIdits Layers read already uses — each only for a type stored per package.HistoryPanelasksisPackageSlottedMetaType(@objectstack/spec/api) before naming the package, so an object, view or page opened under a package reads and restores its history bare, as onmain(round 4, record 6878813627 M1: the doors refuse?package=400on those types). The by-package type list lives once in spec,PACKAGE_SLOTTED_METADATA_TYPES;@objectstack/metadata'sPACKAGE_SLOTTED_STORE_TYPESis built from it, andprotocol-history-slots.test.tsholds the doors' refusal and the console's predicate to the same list.Measurement (before the fix)
Posted on the card: 6867256997. On a booted showcase with two
AppPluginpackages each holding flowqa_hist_shared: bare history 200 interleaving both holders' versions;?package=400 (not supported); bare rollback{toVersion: 2}(beta's) 200 and wrote beta's body into alpha's row — a silent pick. Seat regrade: stays p2 (triage 6867291698).After the fix (same boot,
history-rollback-two-holders.dogfood.test.ts)GET …/history, two holdersPOST …/rollback, two holdersGET …/history?package=alpha.customPOST …/rollback?package=alpha.customto beta's versionPOST …/rollback?package=alpha.customto an unattributed versionPOST …/rollback?package=alpha.customto its own version?package=com.qa.hist32905.alpha(the base)?package=a stranger packagenot-held-by-qualifier?package=onview(not stored per package), both doorsVALIDATION_ERROR, saying the type is not stored per package (r1 of this PR had accepted and dropped it)rollback?package=an uninstalled packageWRITABLE_PACKAGE_REQUIRED;?package=the installed pair restores (200)?package=history lists and restores the environment v2Red lines
/layersand every other door are untouched; the new refusals only narrow.CONTROL: a name one package holds keeps its whole log and its bare rollback, as before(dogfood), CONTROL — no package, 'all' and the empty string send no 'packageId' (REST), CONTROL: no 'packageId' sends no 'package' key on either door (client) and CONTROL: no '?package=' sends no 'packageId' on either door (console) hold the one-holder and bare spellings unchanged;rest-server-meta-member-selector.test.tsstays green with only its measured-set pin takingpackage.the bare history of the two-holder name is refused 409 naming both holders,the bare rollback of the two-holder name is refused 409 naming both, and neither holder's row changes,?package= restores only that holder's row; the other holder's version and an unattributed one are refused, writing nothing,a package that holds nothing of the name is refused naming the holders(dogfood); the protocol's two-holder refusal is a 409 naming every holder in 'details.candidates' (REST). An unattributed legacy version is never assigned to a holder while two hold the name. Round 2 adds, at the dogfood boot,a name two packages ship, only one customized, is refused bare at both doors, as the bare plain read refuses it,?package= on a type not stored per package is refused 400 at both doors, never accepted and dropped,a rollback naming no installed package, of a name no row holds, is refused and stores nothing; an installed holder restoresandCONTROL (M1): a name the environment saved and a package took over restores the environment's versions, bare and addressed, as before; inprotocol-history-slots.test.ts(13 cases) each arm ofhistorySlotOfalone, both F5 arms (P ships the name / does not, the read falls back to the package-less row: the environment's slot); and in the REST file, through the real protocol, the400on aviewat both doors. Round 3 addsa name two packages ship and neither customized, no row stored, is refused bare at both doors…(dogfood) andRollbackMetaItemRequestSchema declares what the rollback door enforces(spec:0,-1,1.5,'2',truerefused;1with every optional member preserved).package_idalready existed), no dependency on another child's branch; the bare one-holder behaviour is unchanged so it ships alone.Ablations
Each leg mutated one source line with
scripts/ablation-replace.mjs(anchor must hit once; blob change proved; restore to HEAD proved), rebuilt@objectstack/metadata-protocol, proved the mutation live indist/withscripts/ablation-dist-preflight.mjs(exit 0, tree clean), and ranhistory-rollback-two-holders.dogfood.test.ts. L1, L2 and L5 ran on r1 and their anchors are unchanged; L3 and L4 were re-anchored onto the rewrittenisVersionOf(L3b, L4b) and re-run on this head with L6–L10. After the last leg the rebuild was proved clean:ablation-dist-preflight … __ablate32905 --absentexit 0 (22 built files), tree clean, and the file is 11/11 green.historySlotOfwhen no package is namedthe bare history of the two-holder name is refused 409 naming both holders:expected 200 to be 409(also the base-package case's bare-409 line)historySlotOfwhen no package is namedthe bare rollback … neither holder's row changes:{"success":true,…"restoredFromVersion":1} expected 200 to be 409; the cascade reddens the two?package=cases (the bare write grew alpha's log:expected [ 1, 3, 6 ] to have a length of 2)isVersionOfaccepts another holder's attributed version?package= reads that holder's versions alone…:expected [ 1, 2, 3, 4 ] to have a length of 2 but got 4;?package= restores only that holder's row…:expected 200 to be 404; also the base-package caseisVersionOfaccepts an unattributed version under two holders?package= reads…:expected [ 1, 3, 5 ] to have a length of 2 but got 3;?package= restores…:expected 200 to be 404; also the stranger-package casenot-held-by-qualifierrefusal disableda package that holds nothing of the name is refused naming the holders:{"events":[]} expected 200 to be 409a name two packages ship, only one customized, is refused bare at both doors…: bare historyexpected 200 to be 409?package=on a type not stored per package let through (F4)?package= on a type not stored per package is refused 400 at both doors…:{"events":[]} expected 200 to be 400a rollback naming no installed package, of a name no row holds, is refused…:expected 404 to be 422CONTROL (M1) … restores the environment's versions, bare and addressed:expected [ 4, 5 ] to include 2CONTROL (M1) …: bare rollback to the environment v1expected 404 to be 200HistoryPanelnames the package for every type again (addressedPackage = packageId)a type not stored per package reads and restores bare under ?package=…(history called withpackageId), Studio'sdataandinterfacespillars (object,pagehistory withpackageId), and Studio's object rollbackRollbackMetaItemRequestSchema.toVersionloosened back toz.number()(spec rebuilt, marker proved inpackages/spec/dist)rest-server-query-number-census.test.ts, 8 of 35 red:body { toVersion: 1.5 },0,-1, theversionalias1.5/0, and?toVersion="1.5"/"0"/"-1"now reach the protocol instead of answering400— the door reads the declarationVerification
Round 4, on head
248bc6bec(after one more merge oforigin/main), each command run with</dev/nulland its exit code landed on disk first:dispatch-gates --commands: 123 commands, all exit 0;dispatch-gates --ran: 123 derived, 123 run, 0 NOT-MEASURED.pnpm lint: exit 0.@objectstack/metadata-protocol,rest,spec,dogfoodexit 0;rest check:test-typecheckexit 0;@object-ui/data-objectstackand@object-ui/app-shelltype-checkexit 0 (after building their dependency closure).data-objectstack120 files / 1386 passed; app-shell item-tools, Studio item-tools and ResourceEditPage 65 files / 312 passed.check:generated(spec): exit 0 after--fixregeneratedapi-surfaceandexport-originsfor the two new exports.Acceptance notes
com.qa.hist.{alpha,beta}.customeachPUT /meta/flow/qa_hist_runtime?package=<pair>(nooverrides) made the bareGET /meta/flow/qa_hist_runtimeanswer 200 with alpha.custom's flow when measured; metadata-protocol: a name held only by stored rows of two enabled packages answers the bare read by save order while /layers refuses 409 (ADR-0150 D3) #32607 (closed by PR fix(metadata-protocol)!: a name two or more enabled packages hold only as stored rows, and no package ships, is refused by the read naming no package instead of served by save order (ADR-0150 D3) #33182, merged 2026-11-14) makes that read refuse 409. This PR's history and rollback doors refuse that name bare either way (they judge the row slots).GET /meta/:type/:name/diff?from=&to=still reads the name's versions across holders (measured: diffs alpha's v1 against beta's v2; it writes nothing), and@objectstack/client'sgetHistory(the SDK) sends no package; the console usesdata-objectstack's client. Filed as metadata-protocol: the history diff door compares two package holders' versions of one name as if they were one item's (ADR-0150 D4, after #32905) #33308 (the diff door) and client: meta.getHistory, rollbackItem and diffItem cannot name a package, so a two-holder item's history is unreachable through the SDK (ADR-0150 D3, after #32905) #33310 (the SDK). Other package-scoped types not stored per(package, name)(page, dashboard, …) are not addressed by ADR-0150 D3 here and their history door answers as before.action): the bare doors judge an action name by its row slots and the registry'sresolveItemonly, not by the bare read's own action judges (refuseAmbiguousObjectLessAction,judgeBareActionRead). Measured on the objectql: narrow the phase-1 one-holder refusal from environment to namespace for action (ADR-0150 child B, action family) #31697 artifact boot (two packages shippingqa_shared_digestobject-less andqa_shared_boundeach on its own object, no stored row): bare read409, bare history200 {"events":[]}, bare rollback404 VERSION_NOT_FOUND(nothing restored). The changeset says so. Filed as metadata-protocol: bare history and rollback of an action two packages ship answer 200 empty and 404 where the bare read answers 409 (ADR-0150 D4, after #32905) #33311.HistoryPanelthreads nomembertoday (pre-existing); unchanged.packages/rest/src/rest-server.ts(the two closed query sets live there, route rule 5);packages/spec/src/api/protocol.zod.tsplus its generatedauthorable-surface/api.jsonandcontent/docs/references/api/protocol.mdx(HistoryMetaItemRequestis the protocol's request type);packages/metadata-core/src/objects/sys-metadata-history.object.ts(doc ofpackage_idonly);ui/…/StudioItemTools.tsxandResourceEditPage.tsx(one prop each: the History tool's other two mounts), andStudioDesignSurface.itemTools-28239.test.tsx(its history/rollback call pins now carry thepackageIdits Layers pin already did).meta-item-history-routes.ts— 5865708652 (letter B, three sites), 5856774816 (three sites), ruling record 6787890900 (two sites), maintainer ruling 2026-08-22 option B;rest-server-meta-member-selector.test.ts— 6116165299 (Q2 i, closed sets). No ruling line is edited or removed by this diff (git diffofruling|maintainer|⛔lines: two added⛔comments, none removed).Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01L9qibvyhxPWfA8DsbmVYJ5