Repository navigation
packages: POST /packages/{id}/revert answers 404 "No metadata items found" for a Studio-authored package that has published items #22090
Description
Activity
- addedbugSomething isn't workingSomething isn't workingpriority:p1High: required for production / M2High: required for production / M2area:studioChanging a running app without code — authoring, publish, docs and the portalChanging a running app without code — authoring, publish, docs and the portal
on Oct 7, 2026 objectstack-fleet commented
on Oct 7, 2026 ContributorAuthorMore actionsPath: write metadata — a builder reverts a package to its published version | 缺项 | P2
Triage: first grade,
bug·priority:p2(re-graded from the filed p1) ·domain:engine·area:studio·pm:queue. Direction:revertPackagefinds a package's members the way the protocol does for stored rowsTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-07T14:57Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in
packages/metadata/src/metadata-manager.ts(revertPackage,:2062) ⇒domain:engine; rationale: the lane table putspackages/metadata*there.- Verified on
main:revertPackagecollects members only from the in-memory registry, bymeta.packageIdormeta.package(:2064–:2069), and throws the 404 when it finds none (:2091–:2098);- a member is reverted to its
publishedDefinition(:2112–:2120); - Studio-authored rows live in
sys_metadataand are served with a_packageIddecoration, as the body reads.
- Why p2, not p1: Revert never works on a Studio-authored package (measured in a browser QA pass). But its effect (each member back to its published version) is what Discard changes, right next to it, already does. No author is left without a path. The North Star grades a capability that runs but is wrong at P2.
- Direction:
revertPackageresolves package membership through the protocol's package-scoped stored rows, the same source Discard changes and the_packageIddecoration read, and reverts those members to their published version. A package with nothing published keeps the declared 409, worded so the UI can show it. - Pins:
- Revert on a Studio package with published members and one draft returns 200, and the draft is gone;
- a never-published Studio package answers 409;
- an unknown package id answers 404 (control);
- a code-shipped package's revert is unchanged (control).
- A foreseen follow-up: objectui's Revert versus Discard changes wording (merge them, or keep both with distinct meanings). The body defers it until the server semantics settle, and triage files it once this lands.
Clause-②: no: the route's contract is unchanged, and it now serves its declared answer. Patch changeset.
- Verified on
- addedpriority:p2Medium: important, M3Medium: important, M3and removedpriority:p1High: required for production / M2High: required for production / M2
on Oct 7, 2026 os-project-manager commented
on Oct 7, 2026 CollaboratorMore actionsDuplicate, superseded: this comment was a second copy of the claim. The relay run was slow, so
post-stampedfell back to a direct write at 15:32Z, and the relay run then posted the same claim as well. The claim of record is 6041193650 (same session, branch, surface and stamp). This copy is not a claim.domain:engine#1·session_017ErfyP2Rx7XWHJA27QjyUi.
Generated by Claude Code
objectstack-fleet commented
on Oct 7, 2026 ContributorAuthorMore actionsClaim: PM loop round 63 · 2026-10-07T15:31Z
Session:session_017ErfyP2Rx7XWHJA27QjyUi
Account:os-project-manager(the seat's linked user, asGET /useranswers it; always the card's assignee)
Branch:claude/issue-22090-revert-package-stored-members
Worktree:objectstack-issue-22090
Domain:domain:engine
Seat:domain:engine#1
Provenance:- Filed from a Studio browser QA pass (session
session_01D76mrPJrSSdaKRxR2rvrMG). Triage graded it p2,pm:queue(6040669927), re-graded from the filed p1. - The lane runs three concurrent claims (the maintainer, verbatim: 「并发3」). feat(objectql,plugin-auth): the Default Organization is load-bearing under
single; an unstamped write is derived there and refused everywhere else (ADR-0131 D3/D9/D11) #15195 and finding(objectql): skipAutomations also skips ObjectQL's own audit stamp — sys_stamp_audit_insert/update are bound through bindHooks, so they carry meta, and a data import with "run automations" unchecked writes rows without created_by/updated_by #22070 hold two slots, and this is the third.
File surface (atorigin/mainb04a5295f7), per triage's grade 6040669927: packages/metadata/src/metadata-manager.ts'srevertPackage(:2062) resolves a package's members the way the protocol does for stored rows: the package-scopedsys_metadatarows that Discard changes and the_packageIddecoration read. It reverts them to their published version.- Today it collects only from the in-memory registry, by
meta.packageIdormeta.package(:2064–:2069), and throws the 404 when none is found (:2091–:2098).
- Today it collects only from the in-memory registry, by
- A package with nothing published keeps the declared 409, worded so the UI can show it.
- Pins:
- Revert on a Studio package with published members and one draft returns 200, and the draft is gone;
- a never-published Studio package answers 409;
- an unknown package id answers 404 (control);
- a code-shipped package's revert is unchanged (control).
- A door-level pin under
packages/qa/dogfood/test/only if measured necessary (domain:cli, declared on [PM seat] domain:cli — 🟢 os-elon-musk · session_01BmsuLyUeuG5CNpZFMH1jzS #6024 first).
.changeset/22090-*.md(@objectstack/metadatapatch).- ⛔ The objectui Revert versus Discard changes wording is triage's follow-up, filed once this lands.
Container & model:M,mode:subagent,model: default(dispatch-gates --tier: no path-derived mandate).
Clause-②: yes (widening) - Revised at hand-back (2026-10-07T17:55Z, seat 1). The dev measured the built entry declarations (
os-dev-report6043377779). The fix landed inmetadata-protocoland theruntimedoor, not inmetadata-manager.ts, and it adds one public method,ObjectStackProtocolImplementation.revertStoredPackage. So@objectstack/metadata-protocolisminorand@objectstack/runtimeispatch, and@objectstack/metadatahas no changeset. The contract review on PR fix(packages): POST /packages/:id/revert reverts a Studio-authored package instead of answering 404 #22112 answered option A (6043636057, PASS). - The line originally read
Clause-②: no: the route's contract is unchanged, and it now serves its declared answer (triage).
Thread-read: 6040669927
Serial constraints cleared: at 2026-10-07T15:31Z: - Open PRs (feat(plugin-security,plugin-auth,verify): sys_user_permission_set gains the permission-set name column, written by every grant writer (ADR-0131 C2 S4a) #22100, feat(cli): the authored public form path /forms/:slug redirects to the console form page when the anonymous door serves it #22098, fix(cli,plugin-auth): one boot line per warning class, each printed once, and the loopback OAuth notice at info #22097, fix(release): judge the major-boundary template stamps on the version-pr lane, and stop prerelease cuts re-dating the last GA #22095, feat(spec)!: retire the flat string-list arm of manifest.permissions — the structured ADR-0025 block is the only form (#13458) #22094, ci(lint): key the PM dispatch-gates family on the files its battery opens #22092, feat(core): one by-name read of the security catalog (ADR-0131 C2, stage S1) #22091, fix(cli): warn at boot when an app's branding names a runtime asset the server will not serve #22089, feat(plugin-email,plugin-auth)!: close the sys_email_template organization door; retire the provenance stamp and the auth SMS seed (ADR-0131 C4, stage 1) #22087, chore(release): enter Changesets pre mode (
next) with onemajormarker, so v18 opens at 18.0.0-next.0 #22084, chore: version packages #21988, feat(spec)!: the build doors refuse a builtin node config value its executor contract refuses, with its location #21974), each file list read byfilename: none touchespackages/metadata/src/metadata-manager.tsor the packages revert door. - This lane's feat(objectql,plugin-auth): the Default Organization is load-bearing under
single; an unstamped write is derived there and refused everywhere else (ADR-0131 D3/D9/D11) #15195 and finding(objectql): skipAutomations also skips ObjectQL's own audit stamp — sys_stamp_audit_insert/update are bound through bindHooks, so they carry meta, and a data import with "run automations" unchecked writes rows without created_by/updated_by #22070 holdobjectql,metadata-protocol's seed loader,runtime's seed order andplugin-auth. No file is shared.
- Filed from a Studio browser QA pass (session
objectstack-fleet commented
on Oct 7, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 22090,
"status": "done",
"branch": "claude/issue-22090-revert-package-stored-members",
"pr": "#22112",
"session": "session_017ErfyP2Rx7XWHJA27QjyUi",
"premise_still_valid": true,
"summary": "POST /packages/:id/revert asked only the metadata service, whose in-memory registry never holds a Studio package's sys_metadata rows, so a published Studio package with a pending draft answered 404 'No metadata items found'. The door (packages/runtime/src/domains/packages.ts) now asks the protocol first. The new ObjectStackProtocolImplementation.revertStoredPackage reads the package's stored rows through the predicate listDrafts uses, now one shared packageScopedRowWhere. It refuses a package with stored rows but no active row with the declared 409. Otherwise it removes the drafts through the per-draft step discardPackageDrafts takes, now shared as discardDraftInItsScope. A package with no stored row answers stored:false, and the unchanged metadata-service branch answers it: code-shipped packages and unknown ids revert exactly as before. Landing moved off metadata-manager.ts, which is unchanged, per measurement (see deviations).",
"hypotheses": {
"H1": "CONFIRMED live at base b04a529 (showcase dev boot, fresh SQLite). The steps: POST /packages com.example.repairs, then PUT /meta/object/repairs_ticket?package=...&mode=draft, then publish-drafts (outcome published), then one more draft edit, then POST /packages/com.example.repairs/revert. The revert answered the 404 quoted at the end of this entry, and GET /meta/_drafts still listed the draft. Published with no draft also answered 404. Call chain at base: dispatcher-plugin.ts:1380 mounts /:id/revert, then http-dispatcher.ts:2238 handlePackages, then domains/packages.ts:1882 revert branch, then :1887 metadataService.revertPackage(id), then metadata-manager.ts:2062, which collects by meta.packageId/meta.package (:2067) and throws the 404 at :2094. The 404 body was code RESOURCE_NOT_FOUND, message "No metadata items found for package 'com.example.repairs'".",
"H2": "CONFIRMED; the seam moved to metadata-protocol plus the runtime door. Discard changes is POST /packages/:id/discard-drafts (base packages.ts:1792), which calls protocol.discardPackageDrafts (base protocol.ts:23716). That calls SysMetadataRepository.listDrafts({packageId}) (base sys-metadata-repository.ts:1220), which filters sys_metadata.package_id for the caller's organization plus env-wide (:1259). That column is what the read path decorates as _packageId. That where is now packageScopedRowWhere (sys-metadata-repository.ts:406), used by listDrafts (:1285) and by revertStoredPackage for active rows (protocol.ts:23853): one copy of the rule. MetadataManager.revertPackage IS reachable from the protocol (the metadata service). The stored rows are NOT reachable from packages/metadata, for two reasons. The dependency runs metadata-protocol to metadata. And a real boot gives the manager no sys_metadata loader: setDataEngine/setDatabaseDriver (metadata-manager.ts:605/:635) have zero in-tree callers, and plugin.ts:538 attaches a FileSystemRepository.",
"H3": "CONFIRMED. A stored member's published version is its active row; the pending change is a separate draft row. publish-drafts promoted the draft (drafts list emptied); Discard removes the draft via deleteMetaItem({state:'draft'}) in the draft's own scope. Revert reuses that step (protocol.ts:23769 discardDraftInItsScope, shared with discardPackageDrafts); no new revert machinery. Stored rows with no active row answer 409 RESOURCE_CONFLICT: "Package '...' has never been published, so there is no published version to revert to. Publish the package first, or discard its drafts to drop them."",
"H4": "CONFIRMED. Control: com.example.showcase. Its two capabilities in examples/app-showcase/src/security/capabilities.ts carry an authored packageId, the key MetadataManager collects by. It has no stored row, so revertStoredPackage answers stored:false and the unchanged branch answers. Live: 409 RESOURCE_CONFLICT "Package 'com.example.showcase' has never been published" at base and head, byte-identical message. The door pin also covers publish-then-edit on the same package: 200, restored from publishedDefinition."
},
"pins": {
"a_published_with_one_draft": "base 404 RESOURCE_NOT_FOUND, draft stays | head 200 {success:true}, drafts [], served object is the published body (live + door test)",
"b_never_published_studio_package": "base 404 RESOURCE_NOT_FOUND | head 409 RESOURCE_CONFLICT, message starts "Package 'com.example.repairs' has never been published", draft untouched (live + door test)",
"c_unknown_id": "base 404 RESOURCE_NOT_FOUND | head 404 RESOURCE_NOT_FOUND, same message (live + door test)",
"d_code_shipped_com.example.showcase": "base 409 "has never been published" | head identical 409 (live + door test); published-then-edited arm 200 (door test)",
"extra_published_no_draft": "base 404 | head 200"
},
"tests": "All at 723afed unless stated. (1) The new door pin packages/runtime/src/package-revert-stored-members.integration.test.ts: 9 cases on a real HttpDispatcher, real ObjectQL on better-sqlite3, the real protocol and a real MetadataManager. It runs together with domains/packages-vetted-org-source.test.ts, which gains the revert door in its org roster: 2 files, 91 tests passed (run at a5a2488, before the main merge; both are inside the full runtime run below). (2) pnpm --filter @objectstack/metadata-protocol exec vitest run: Test Files 221 passed, 3 skipped; Tests 28227 passed, 19 skipped. (3) pnpm --filter @objectstack/runtime exec vitest run --project local: Test Files 333 passed; Tests 4710 passed, 19 skipped. (4) metadata-protocol typecheck exits 0; runtime typecheck exits 0 (tsc --noEmit plus check:test-typecheck OK: 27 files, 190 errors, 68 signatures, ledger unchanged). (5) Reverse verification on committed HEAD 723afed. Done with git restore --source=b04a5295f7 --worktree on packages/runtime/src/domains/packages.ts. Anchor revertStoredPackage went from 4 occurrences to 0, and the blob equalled the base blob d9ece84db4. Predicted 6 red / 3 green, measured 6 red / 3 green. Every red was 'expected 404 to be 200' or 'expected 404 to be 409', the base's 404; the controls (c)(d)(d) stayed green. The restore was done by a trap: git checkout HEAD -- path. Proof: hash-object 2df234a954 equals the HEAD blob, the index blob equals the same, git diff HEAD is 0 bytes, git status is clean. (6) Live door, showcase dev boot at head: (a) 200, drafts [], object served with label 'Repair Ticket' and no 'cost' field; (b) 409 worded; (c) 404; (d) 409 identical to base. (7) ESLint narrowing: the population is the 5 changed TypeScript files; --format json reports 5 files, 0 errors, 0 warnings, exit 0. eslint.config.mjs states no parserOptions.project and no typed rules, so the config is not type-aware and the diff cannot move an untouched file's verdict.",
"gates": "dispatch-gates --commands (no paths) at 723afed: 65 commands, all run; 64 exit 0 directly. check:dual-build-cjs-loads first exited 3 with PREREQUISITE NOT MET (8 unrelated dists missing). It is green after building those (41/41 turbo cache hits): 106 entries, 66 packages, 712 CJS files. --ran reconciliation: 65 derived, 65 run, 0 NOT-MEASURED (a DERIVED zero, all coded). Artifact-roster block: 53 rows, all green. The 4 rows marked as rosters under my paths are green: check-changeset-fixed, authz-resolver, error-code-casing, route-ledger-census. check-partof-closing-keyword was run with the PR body as PR_BODY. check-closing-target-claim ('PR #22112 closes #22090, and each carries a Claim: whose Branch: line names claude/issue-22090-revert-package-stored-members') and check-single-claim-paths were run with PR_NUMBER=22112 after the PR opened. Symbol-anchor sweeps: adr-symbol-anchors, scripts-symbol-anchors, spec-docblock-symbol-anchors and adr-anchors are all exit 0. check-changeset-no-major was run with the PR body as the event payload. Its level axis reads 'Clause-②: yes (widening)', finds @objectstack/metadata-protocol graded minor, and exits 0. Remote CI on 723afed at report time: in_progress (11 success, 3 skipped, 17 in progress, 0 failure). The report is not held for CI.",
"line_budget": "n/a",
"clause_2": "yes (widening) — measured, and it differs from the claim's 'no'. The built @objectstack/metadata-protocol dist/index.d.ts was compared base against head, comments stripped. Base came from the turbo cache artifact of base sources: hash 1bada1a3e3b46703, restored in a throwaway base worktree, since removed. Head gains exactly one public method, revertStoredPackage(request with packageId, optional organizationId, optional actor), resolving to an object with stored (boolean) and discarded (an array of type/name pairs), plus one private member (discardDraftInItsScope). packageScopedRowWhere is not exported from the entry. The @objectstack/runtime dist/index.d.ts is identical base against head, comments stripped (cache hash 055b4c3e0b469299). The route's request and response contract is unchanged. Changesets: metadata-protocol minor (Clause-②: yes (widening)); runtime patch (Clause-②: no). The PR body's second line reads 'Clause-②: yes (widening)'.",
"files_changed": [
"packages/metadata-protocol/src/protocol.ts",
"packages/metadata-protocol/src/sys-metadata-repository.ts",
"packages/runtime/src/domains/packages.ts",
"packages/runtime/src/package-revert-stored-members.integration.test.ts",
"packages/runtime/src/domains/packages-vetted-org-source.test.ts",
".changeset/22090-metadata-protocol-revert-stored-package.md",
".changeset/22090-runtime-revert-studio-package.md"
],
"deviations": [
"Landing: the order expected packages/metadata/src/metadata-manager.ts. The measured producer is metadata-protocol plus the runtime door, so metadata-manager.ts is untouched. The order allowed this under its producer-side clause; the reason is in the PR body and in H2.",
"Changeset packages: the order named @objectstack/metadata patch. It is untouched, so it has no changeset. The changesets are @objectstack/metadata-protocol minor and @objectstack/runtime patch.",
"Clause-② conflict, stated, not silently resolved. The claim (6041193650) declares 'no', and os-dev.md says to copy the claim's Clause-② line. The order says the PR body carries the line 'as measured'. Measured: yes (widening). The PR body carries the measured line and the metadata-protocol changeset is minor. Changing to 'no' is a 1-line body edit plus a changeset level. With 'no', the level axis stands down and gates stay green either way.",
"Commit trailer: the harness reminder's model-named Co-Authored-By was refused by the pre-push hook check:commit-card-trailers. The commit was reworded to the model-free pair before its first push; no published history was rewritten.",
"The PR body's gates bullet says check-closing-target-claim and check-single-claim-paths 'need a PR number, so CI reads them'. Both were then run locally with PR_NUMBER=22112 and are green. The body was not PATCHed (dev writes the body once). The seat can change that bullet to: 'all 53 roster rows green; the two PR-number gates were run with PR_NUMBER=22112'.",
"Base drift: main gained ac9f8bd (feat(spec)!) and aa71c4d after the last merge (bafb58b). aa71c4d edits packages/metadata-protocol/src/protocol-publish-drafts-advisories.test.ts. The branch was not re-merged after the suites ran; CI's merge ref reads both.",
"The door pin was first run at a5a2488, before the main merge. The full runtime and metadata-protocol suites, both typechecks, all gates and the reverse verification ran at 723afed."
],
"mcp_calls": "0",
"api_writes": "3 REST writes through the fleet-write relay (each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): pr_create POST /repos/objectstack-ai/objectstack/pulls (draft PR 22112; read-back identical, 10341 bytes); label-write assign POST /repos//issues/22112/assignees (os-project-manager; read-back matches; labels documentation/size-l/tests/tooling came from the labeler, not this write); this os-dev-report comment POST /repos//issues/22090/comments. Plus git pushes of the branch (not REST). No card assignee write, no label write, no issue created.",
"open_questions": [
{
"question": "Clause-② for this PR: keep the measured 'yes (widening)' (metadata-protocol minor) or restore the claim's 'no'?",
"options": [
"A: keep 'yes (widening)' + metadata-protocol minor, as measured on the built entry declarations",
"B: edit the PR body's second line to 'Clause-②: no' and grade metadata-protocol patch"
],
"recommendation": "A. One added public method on an exported class is a widening, the same act the declinesStoredRow changeset (21986) graded minor with 'yes (widening)'. The seam had to be a protocol verb, because packages/metadata cannot reach the stored rows."
}
],
"out_of_scope_findings": [
"class: a · reach: POST /api/v1/packages/com.objectstack.setup/revert answers 404 RESOURCE_NOT_FOUND "No metadata items found for package 'com.objectstack.setup'" while GET /api/v1/meta/app?package=com.objectstack.setup serves that package's app item. Measured live on a showcase dev boot; the same at base b04a529 and at head (H4 kept the registry branch unchanged). · evidence: MetadataManager.revertPackage and publishPackage collect members by the item's own packageId/package key (metadata-manager.ts:2067, :1840). The platform stamps code-shipped items with _packageId (applyProtection, packages/metadata/src/plugin.ts:1194/:1251), and the protocol scopes registry items by _packageId (protocol.ts:9402). So every code package whose items carry only the stamp is 'not found' by revert and publish. · dedupe words: revertPackage _packageId · No metadata items found code package · MetadataManager package membership key · publishPackage packageId _packageId",
"carrier: triage's foreseen objectui Revert-versus-Discard follow-up (filed once this lands) · noted, not filed. Measured live: a code-shipped package with an org overlay draft (an org-scoped draft of showcase_task.done bound to com.example.showcase) answers 409 'has never been published', at base (registry sentence) and at head (stored-half sentence). The draft stays in both, while discard-drafts drops it (discardedCount 1). Whether a code baseline counts as 'published' for Revert is that follow-up's semantics question. Also for it: on a published stored package, Revert and Discard changes now remove the same drafts, including an item created after the last publish."
],
"cleanup": "Two showcase dev servers were started on ports 43230 and 48729 and stopped by their recorded PID trees; both ports are closed. The throwaway base worktree ../objectstack-issue-22090-base was removed (node_modules first, no --force). Worktree ../objectstack-issue-22090: node_modules removed and the worktree removed after this report is posted. No background process or monitor is left running."
}
Generated by Claude Code
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsLanding record: PR #22112 merged · 2026-10-08T00:20Z
domain:engine#1·session_01EUBvqtauTDmHi2ZgY759p2(os-litant), seated on the take-over of seat 1 (6049493167 on #6367). The predecessor (session_017ErfyP2Rx7XWHJA27QjyUi) landed this card and ran out of tokens before posting this record; this seat writes it from the platform readings.- Merged: PR fix(packages): POST /packages/:id/revert reverts a Studio-authored package instead of answering 404 #22112 at 2026-10-07T18:32Z through the merge queue, squash
a543e244f0onorigin/main. Read by content:revertStoredPackageis onorigin/maininpackages/metadata-protocol/src/protocol.ts(3 hits) andpackages/runtime/src/domains/packages.ts(4 hits). - Review: the dev's report 6043377779; the contract review on PR fix(packages): POST /packages/:id/revert reverts a Studio-authored package instead of answering 404 #22112 answered option A, PASS (6043636057), and the claim's
Clause-②line was revised toyes (widening)at hand-back. - Labels: the card closed
completedwithpm:dispatchedstill on it; removed in this act. The assignee is left as the record of the landing claim. - Out-of-scope finding: the report's class (a) item (code-shipped packages whose items carry only the
_packageIdstamp answer 404 to revert and publish) is finding(metadata): revertPackage and publishPackage find a package's members by the item's own packageId/package key, so a code-shipped package whose items carry only the _packageId stamp answers 404 "No metadata items found" #22113, graded p3 and queued in this lane. - Foreseen follow-up: triage's objectui Revert versus Discard changes wording card, owed by triage now that this has landed (6040669927).
- Merged: PR fix(packages): POST /packages/:id/revert reverts a Studio-authored package instead of answering 404 #22112 at 2026-10-07T18:32Z through the merge queue, squash
- added 3 commits that reference this issue
on Oct 9, 2026
Filing gate ① — product defect with a named location and a reproduction. reach:
POST /api/v1/packages/com.example.repairs/revert→ 404RESOURCE_NOT_FOUND"No metadata items found for package 'com.example.repairs'" on a Studio package holding 4 published items and 1 draft; the Studio package sheet's Revert button sends exactly this.Who acts on it: objectstack triage (metadata service owner); objectui follows for the button wording. ⛔ Not a claim. Found in a manual browser QA pass of Studio on 2026-10-07; filed one card per finding on the maintainer's word: 「你发现的问题全部提交 issue」, and on the one-card-per-finding question 「覆盖规则,逐条立卡」.
What happens
In Studio → package switcher → Package info & settings, Revert on a Studio-created writable package fails with the 404 above and leaves the draft in place. Right next to it, Discard changes (1) does discard the draft ("All pending changes discarded — back to the published version."), so the author sees two near-identical verbs, one of which never works on the packages Studio creates.
Reproduction
com.example.repairs, an object, an app, a flow, a permission set; publish; make one more draft edit.GET /api/v1/meta/_drafts?packageId=com.example.repairsstill lists the draft.Where it comes from (read in source — mechanism inferred, not single-stepped)
revertPackageinpackages/metadata/src/metadata-manager.tscollects items from the in-memory registry whosemeta.packageIdormeta.packageequals the id, and throws the 404 when that list is empty. Studio-authored rows live insys_metadataand are served with a_packageIddecoration, so the collection is plausibly always empty for them.Suggested direction (triage to rule)
Make
revertPackageresolve package membership the way the protocol does forsys_metadatarows; if a package has no snapshot to revert to, answer that in words the UI can show. The objectui half (rename or merge Revert with Discard changes, or disable it with a reason) can follow once the server semantics are settled.Environment
objectstack
879bd38c·examples/app-showcasebooted withobjectstack dev --ui --seed-adminon an isolated port and SQLite file · objectui179f6fe9(HEAD; the framework pin.objectui-shaisa58626c8) served by the console's Vite dev server, perf numbers from avite buildof the same commit · Chromium 141 at 1440×900 · signed in as the seeded platform adminadmin@objectos.aiunless stated.Duplicate check
VERSION_NOT_FOUND: No history row at version 2while/historyshows exactly that row — the revert's lookup key disagrees with the history writer (and the package-level revert route 500s) #7559 (closed) — prior fix of the same route (PR fix(metadata-protocol,metadata): revert reads the history row under the key the writer stored it with (#7559) #7619 turned the 500 into the 404 'No metadata items found'); this card is that 404 firing for a package that HAS published itemspackage_id IS NULL#6215 (closed) — revert-family package scoping bug; different route/stageDedupe words: package revert 404 · No metadata items found for package · revertPackage _packageId · Revert vs Discard changes
Filed by Claude Code (session
session_01D76mrPJrSSdaKRxR2rvrMG) from that QA pass.Generated by Claude Code