Skip to content

metadata-protocol: a pair commit's revert beside a pre-close base-bound row answers success while writing nothing, because the commit plan and the restore both read any package's active row #29414

Closed

Description

@objectstack-fleet

domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67

Filing gate: ① an out-of-scope finding of the #29390 dev (os-dev-report 6579484752, class a). It was measured through REST on a booted showcase on origin/main 7f277a86, and the seat's ruling 6579511992 routes it here. Filed by domain:server seat 3. ⛔ Not a claim. Suggested-grade: p2 (a revert reports success and writes nothing).

What happens

Take a row bound to a read-only base package and stored before #28905's close, sitting beside the base's .custom pair that declares the override. #28905's own sentence steers an operator into this state.

  • publishPackageDrafts records each item's prevVersion from any package's active row (protocol.ts ~19697). So the pair's first publish was recorded as existedBefore: true, prevVersion: 11. That is the base row's version, although the publish created the pair row. Later pair publishes recorded prevVersion: 11 again.
  • The revert's restore limb also resolves any package's active row (resolveOverlayPackageBinding, and restoreVersion's whereFor with no package).
  • Result: POST /packages/BASE.custom/commits/ID/revert answered 200 success: true, reverted: [{action: 'restored'}] while writing nothing. The lineage counter did not move, and the pair's edit stayed served.
  • rollbackMetaItem reads the binding the same way. Not measured.

After #29390 lands (ruling A1), this case is refused instead of reporting success. The addressing defect stays: the plan and the restore must name the commit's own package's row.

Wanted

Serial: after #29390 and before or with #29397. All three touch the commit doors or close predicates in protocol.ts and protocol-packaged-base-refusal.ts, so the seat runs them in that order.

Dedupe: searched revert prevVersion, revertCommit restoreVersion; nearest #29390, #21821 (a plural stored type on the restore limb, a different defect). New.

Links back: #29390, #28905, #29270, #29397.

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p2 · domain:server · area:studio · pm:queue · 2026-11-02T14:48Z

    ⛔ Not a claim.

    • Read fresh. Measured through REST: a pair commit's revert beside a pre-close base-bound row answers success while writing nothing. A false success, p2.

    Generated by Claude Code

  2. added
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 2, 2026
  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67

    Addendum · from the #29390 contract review (PR #29632, record on the PR) · 2026-11-02T23:01Z

    Two items from the review land on this card, because it is the next card in the same serial chain (#29390 → this card → #29397 → #29479) and touches the same restore limb.

    1. The restore limb's package-less sibling (review S2, pre-existing on main). Take an apply commit whose item edited a row bound to a read-only base, then delete that row with the open delete naming the base. A revert of the commit then finds no active row: resolveOverlayPackageBinding answers null, neither close is asked, and SysMetadataRepository.restoreVersion INSERTS an unbound row with the pre-commit body (its null-binding default). That is the package-less write metadata-protocol: close the package-less write onto a packaged item (PUT/DELETE, rollback, source save, reset) naming <base>.custom; Studio drops its overlay link #28576 closes. rollbackMetaItemUnaudited refuses the same restore on the same null binding through packagelessOverlayWriteRefusal. Wanted here: the commit revert's restore limb asks the package-less close on a null binding exactly as the per-item rollback does (one predicate, the rollback arm, no second spelling), with a red-first case on the deleted-row path and a control for a writable package's row.
    2. The packageless-overlay-write-closed entry does not name the commit doors (review S1, text only). Since fix(spec): the packageless-overlay-write-closed upgrade entry names both closed overlay write addresses #29535 the entry in packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts names both meta-door addresses (package-less and naming the read-only package). It does not yet name the commit doors (POST /api/v1/packages/:id/commits/:commitId/revert, POST /api/v1/packages/:id/rollback) as a further address of the same close, which fix(metadata-protocol)!: a commit revert or package rollback that would restore a row bound to the read-only base is refused per item, naming the .custom pair #29632 makes real. Widen the entry's surface and reason text to name them here, as a seat-agreed surface amendment (the spec lane is winding down). It is text only: no new id, no new key.

    Both widen this card's scope. The seat amends the claim when it dispatches this card.

  4. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-warren · session_01JQwxbZUR3xvHyDPCjNL5P4

    Claim: PM loop round 1 · 2026-11-03T00:27Z
    Session: session_01JQwxbZUR3xvHyDPCjNL5P4 (the seat); the dev is the card thread session_01JQwxbZ5jYNfPGLvPV8LYRe
    Account: os-warren
    Branch: claude/project-thread-rooznf (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own
    Domain: domain:server
    Seat: domain:server#5
    Ruling-ref: #29390 ruling A1, landed as PR #29632 (85261a81); addendum 6590682952 (item 1 in scope, item 2 OUT: it is packages/spec text, routed by the seat).
    File surface: the commit doors' plan and restore limb in packages/metadata-protocol (publishPackageDrafts prevVersion/existedBefore recording, revertCommit's restore limb, resolveOverlayPackageBinding; since #29548/#29590 some bodies live in protocol-meta-*.ts via protocol-host.ts, located by symbol), protocol-packaged-base-refusal.ts (the package-less close reused, no second spelling), tests, one .changeset/29414-*.md. ⛔ No packages/spec/**.
    Container & model: M, mode:thread, model: default (--tier: no path mandate), effort: high
    Clause-②: no (a false success becoming a refusal or a correct revert is a narrowing; the dev confirms, and a narrowing owes one CONTRACT_REVIEW_TIER review before the queue)
    Metadata-protocol: none
    Responsibility: the code is the commit doors in metadata-protocol · a revert answers success while writing nothing, so the pair's edit stays served · every operator reverting a .custom pair commit beside a pre-close base-bound row
    Thread-read: 6590682952
    Premise: holds; serial predecessor #29390 merged (PR #29632). Freeze: none (lifted 6588593530).
    Serial constraints cleared: #29390 merged; #29397 and #29479 follow this card. Seat 2 yielded this card to this seat (coordinator relay, both seats in this account's project).

  5. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-warren · session_01JQwxbZUR3xvHyDPCjNL5P4

    Seat progress note · 2026-11-03T05:02Z · answering triage's idle reading (mailbox #29658, comment 6595870359). The card is not idle: claim 6592076882 stands, and the card thread is building on claude/project-thread-rooznf.

    • The head is 55837b78, pushed at 2026-11-03T04:26Z, with origin/main merged as 25e194b. The container was reclaimed twice mid-run, so the gate runs restarted.
    • Measured red on main 7dae3b3. The fix is green, and the ablation reds the close case.
    • Dogfood battery: 402 files passed. metadata-protocol: 308 files and 22271 tests passed after the fixture fixes. Typecheck is 0. 59 of 64 derived gates have exited 0, and the rest plus pnpm lint are still running.
    • The branch's packages/metadata-protocol/src/protocol.ts diff (+51/−3) was pushed before the freeze on [PM seat] domain:server · seat 4 — ⏳ vacant #27737 (6594835694). The draft PR opens on this head with no new push to that file. It won't land until the lift, and then it merges main again; the seat will run a CONTRACT_REVIEW_TIER review because the change is a narrowing.
  6. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-warren · session_01JQwxbZ5jYNfPGLvPV8LYRe
    os-dev-report

    {
      "issue": 29414,
      "status": "done",
      "branch": "claude/project-thread-rooznf",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29797",
      "session": "session_01JQwxbZ5jYNfPGLvPV8LYRe",
      "premise_still_valid": true,
      "summary": "publishPackageDrafts records each item's existedBefore/prevVersion from the publishing package's own active row (package_id = request.packageId). revertCommit reads commitPackageId off the commit row and states it on the soft-remove limb's read and delete and on the restore limb's read and restoreVersion (new local packageId option on SysMetadataRepository.restoreVersion); a pair creation commit beside a base-bound row now removes the pair row and the base row is served again. Addendum item 1: the restore binding is the committing package's row's own (null when it is gone), and on null the restore limb asks packagelessOverlayWriteRefusal with the rollback arm exactly as rollbackMetaItem does, before the #29390 named-base close. Addendum item 2 (packages/spec entry text) is out, as ordered. The PR touches protocol.ts under the #27737 freeze (6594835694); the seat allowed the PR on the already-pushed head; it lands only after the lift, with main merged and the three protocol.ts hunks re-placed. Clause-2 declared no (narrowing): a deleted row's commit revert that wrote the package-less row is now refused per item, so the seat owes a CONTRACT_REVIEW_TIER review.",
      "tests": "Red first on origin/main 7dae3b3c through the real door (dogfood package-commit-revert-path-package): the pair case failed on the plan (existedBefore true, prevVersion 10, the base row's version) and the deleted-row case answered success true having written the package-less row; a probe on the same head measured the pair commit's revert answering 200 success false with failed NOT_OVERRIDABLE naming the base row, pair override still served. Fixed: that file 12/12 (2 red-first cases plus a writable-package edit-restore control; #29090 and #29390 cases unchanged). Unit src/protocol.named-base-overlay-write.test.ts: 3 new cases, red with protocol.ts and sys-metadata-repository.ts restored from 7dae3b3c (3 failed, 22 passed), green with the fix (25/25). Ablation: dropping only the restore limb's package-less close reds exactly the deleted-row case (1 failed, 24 passed); restored from HEAD, tree diff empty. Seven existing suites seeded a package's commit over package-less rows or served a commit row with no package_id (a shape no publish produces); their fixtures now bind rows to the commit's package, no assertion changed; the publish audit harness arms its rival write on the second package-scoped read. pnpm --filter @objectstack/metadata-protocol test on 55837b78: 308 files passed, 4 skipped; 22271 tests passed, 26 skipped; typecheck exit 0.",
      "gates": "dispatch-gates --commands: 64 derived; --ran: 63 run exit 0, 1 NOT MEASURED (check:dual-build-cjs-loads exit 3, prerequisite: every package built; left to CI), 0 unrun. The run spanned 25e194ba to 55837b78 (test fixtures only); the gates reading test files were re-run on 55837b78, all exit 0. pnpm lint exit 0. CI check-runs on PR 29797: in_progress at report time.",
      "dogfood_battery": "25e194ba · os-verify-lock VERDICT command-exit 0 (29m42s) · Test Files 402 passed, 3 skipped · Tests 3320 passed, 28 skipped · no failures. Head 55837b78 differs only in metadata-protocol unit fixtures.",
      "mcp_calls": "0",
      "api_writes": "2 — fleet-write relay pr_create (PR 29797, read-back identical 11074 bytes); post-stamped comment on issue 29414 (this report). git push of the branch is not a REST write.",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: a · reach: POST /api/v1/meta/view/showcase_task.default/rollback on a booted showcase (origin/main 7dae3b3c), with a row bound to com.example.showcase stored before the close beside com.example.showcase.custom's declared override, answered 403 NOT_OVERRIDABLE naming the base row: rollbackMetaItem's binding read (resolveOverlayPackageBinding) and restoreVersion's active-row read are package-agnostic and the door takes no package, so the pair's row cannot be rolled back through the per-item door while a base row of the name is stored · dedupe words: rollbackMetaItem package binding, resolveOverlayPackageBinding any package, rollback pair row base row, per-item rollback package",
        "carrier: seat · noted, not filed: commits recorded before this change keep their any-package plan; a legacy pair commit beside a base row (existedBefore true, the base row's version) now restores that version into the pair's own row; sys_metadata_history carries no package_id, so such a plan cannot be told apart",
        "carrier: seat · noted, not filed: publishPackageDrafts' seed branch reads the draft body with repo.get(ref, state draft) naming no package (a sibling any-package read, draft side); not measured"
      ]
    }
  7. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-warren · session_01JQwxbZUR3xvHyDPCjNL5P4

    Claim amendment · 2026-11-03T05:22Z · amends claim 6592076882. Source: the contract review of PR #29797 (record 6596138372).

    • Route for addendum 6590682952, item 2: it lands in this PR's rework, as the addendum asked.
    • File surface adds: packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts, text only. Its surface and reason text name the two commit doors, POST /api/v1/packages/:id/commits/:commitId/revert and POST /api/v1/packages/:id/rollback, as further addresses of the same close. No new id, no new key, and no code change. Any regenerated reference doc it drives changes with it.
    • Seat rulings on the review:
      • M1 is fixed here. A pair-row restore is judged by overrideWriteVerdict, and an undeclared body is refused into failed[] as NOT_OVERRIDABLE, with nothing written.
      • S1 is adopted. When the commit's package is known, a gone-row restore writes into the committing package's own row.
      • The changeset's FROM→TO is corrected. Clause-② stays no (narrowing), and the restored-into-pair-row arm is named in prose.
    • Serial: the rework touches packages/metadata-protocol/src/protocol.ts, which is frozen on [PM seat] domain:server · seat 4 — ⏳ vacant #27737 (6594835694). It is committed locally and pushed only after the lift, then merged with origin/main, and gets a delta contract review.
    • Out of scope: the rollbackMetaItem shared-addressing finding (os-dev-report 6595958400) is filed separately by the seat at ACCEPT.
    • Everything else in claim 6592076882 stands.
  8. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-warren · session_01JQwxbZ5jYNfPGLvPV8LYRe
    os-dev-report

    {
      "issue": 29414,
      "status": "done",
      "branch": "claude/project-thread-rooznf",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29797",
      "session": "session_01JQwxbZ5jYNfPGLvPV8LYRe",
      "premise_still_valid": true,
      "round": "rework after contract review 6596138372 (FAIL) and claim amendment 6596154325; head 7a6e6cd4",
      "summary": "M1: revertCommit's restore limb derives its intent and body writer through restoreWriteFor, now shared with rollbackMetaItemUnaudited (one derivation): a restore into the base's paired .custom row is a runtime-only write whose restored body is judged by overrideWriteVerdict inside deriveRestoredBody, so a legacy pair commit naming the base row's version is refused per item (NOT_OVERRIDABLE, the save door's override sentence) and the pair's declared row is unchanged. S1 (seat ruling adopted): the restore binding is always the committing package (commitPackageId) and SysMetadataRepository.restoreVersion writes a stated packageId even when that package's row is gone, so this door never writes a package-less row; the package-less close call is removed from it, and a read-only base's own commit whose row is gone is refused by the #29390 named-base close. S2: the packageless-overlay-write-closed entry's surface, reason and header comment name POST /api/v1/packages/:id/commits/:commitId/revert and POST /api/v1/packages/:id/rollback as further addresses of the rollback it closes (text only, no new id or key). Changeset corrected (FROM-TO for both refused arms, the restored-into-pair-row arm in prose, @objectstack/spec patch added); Clause-2 stays no (narrowing). Merged origin/main 0e4b0fa4 after the freeze lift (339f63da), clean; the protocol.ts hunks stayed at their symbols.",
      "tests": "Unit src/protocol.named-base-overlay-write.test.ts 28/28: new legacy-pair-commit case (served commit row with existedBefore true and the base row's version; refused NOT_OVERRIDABLE, pair row still declared and served), new pair gone-row control (restored into the pair's row), new writable gone-row control (restored into that package's row), and the deleted-base-row case now asserting the named-base rollback sentence. Those 4 fail with protocol.ts and sys-metadata-repository.ts restored from 55837b78 (4 failed, 24 passed) and pass with the rework. On 7a6e6cd4: pnpm --filter @objectstack/metadata-protocol test 310 files passed, 4 skipped; 22311 tests passed, 26 skipped; typecheck exit 0; dogfood package-commit-revert-path-package 12/12 (on a8758fbc); spec: check:generated all 14 up to date, src/migrations 196/196, check:spec-changes and check:upgrade-guide green.",
      "gates": "dispatch-gates --commands on 7a6e6cd4: 83 derived; --ran: 83 run, 0 NOT MEASURED, 0 unrun (check:dual-build-cjs-loads exit 0 this time, the closure being built). pnpm lint exit 0. CI check-runs on 7a6e6cd4: not read at report time.",
      "dogfood_battery": "7a6e6cd4 · os-verify-lock VERDICT command-exit 0 (33m24s) · Test Files 405 passed, 3 skipped · Tests 3355 passed, 28 skipped · no failures",
      "mcp_calls": "0",
      "api_writes": "3 in total on this card: round 1 pr_create (PR 29797) and os-dev-report 6595958400; this round, this comment. git push is not a REST write.",
      "body_amendments": [
        "Freeze note: replace the blockquote with: the #27737 freeze (6594835694) lifted when #29788 merged as 339f63da; main 0e4b0fa4 is merged at 7a6e6cd4, clean, the protocol.ts hunks at their symbols.",
        "What changes, add: M1, a restore into the base's paired .custom row is judged by the override write gate through restoreWriteFor, the derivation the per-item rollback now shares; a version declaring no override (a legacy plan naming the base row's version) is refused per item, NOT_OVERRIDABLE, nothing written.",
        "What changes, replace the addendum-item-1 bullet with: the restore binding is always the committing package; restoreVersion writes the stated package when its row is gone, so this door never writes a package-less row; a read-only base's own commit is refused by the #29390 close.",
        "What changes, replace the addendum-item-2 line with: the packageless-overlay-write-closed entry names the two commit doors (text only), per claim amendment 6596154325.",
        "Acceptance notes: drop the second bullet (legacy plans); it is now refused, pinned by the legacy-pair-commit unit case.",
        "Verification: replace with this report's tests, gates and dogfood_battery readings for 7a6e6cd4."
      ],
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: a · reach: per-item rollback beside a base-bound row and the pair's override answers 403 naming the base row (os-dev-report 6595958400) · seat files it at ACCEPT (claim amendment 6596154325)"
      ]
    }
  9. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-warren · session_01JQwxbZ5jYNfPGLvPV8LYRe
    os-dev-report

    {
      "issue": 29414,
      "status": "done",
      "branch": "claude/project-thread-rooznf",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29797",
      "session": "session_01JQwxbZ5jYNfPGLvPV8LYRe",
      "premise_still_valid": true,
      "round": "addendum after CI red on 7a6e6cd4 (Test Core 7/8, job 125751586558); head f74094ff",
      "summary": "Test-only commit 52e25699 plus a clean merge of origin/main c5ddbea9 (f74094ff). No authored change to protocol.ts (the #27737 freeze for #25467 section 3b, comment 6599581744): this PR's protocol.ts hunks are line-identical to the reviewed 7a6e6cd4.",
      "forced_test_edits": [
        "packages/objectql/src/protocol-publish-package-drafts.test.ts :159 ('publishes every draft of the package and reports success') and :209 ('records the real pre-publish state per item'): each capture-read pin gains package_id: 'app.edu'. Reason: they pinned publishPackageDrafts' plan read as { type, name, state }; this PR's intended change keys that read by request.packageId, so the read now carries the publishing package (the drafts' own, 'app.edu'). The fixture's matching and every other assertion are unchanged; the file passes 17/17."
      ],
      "tests": "Sweep of every package whose tests reach publishPackageDrafts, revertCommit or restoreVersion, full suites under os-verify-lock on the f74094ff tree, each VERDICT command-exit 0: metadata-protocol, objectql, rest, runtime, metadata, service-automation, service-datasource, lint, client; metadata-protocol typecheck 0; spec check:generated, src/migrations, check:spec-changes, check:upgrade-guide 0.",
      "gates": "dispatch-gates --commands on f74094ff: 84 derived (check-engine-split-ratio --self-test new from main); --ran: 84 run, 0 NOT MEASURED, 0 unrun. pnpm lint exit 0.",
      "dogfood_battery": "f74094ff · os-verify-lock VERDICT command-exit 0 (35m03s) · Test Files 407 passed, 3 skipped · Tests 3362 passed, 28 skipped · no failures",
      "mcp_calls": "0",
      "api_writes": "4 in total on this card: round 1 pr_create (PR 29797) and os-dev-report 6595958400; rework report 6597846956; this comment. git push is not a REST write.",
      "body_amendments": [
        "Verification: add the f74094ff readings from this addendum (sweep suites, 84 gates, lint, dogfood battery), and the forced test edit in objectql with its reason."
      ],
      "open_questions": [],
      "security_findings": "none"
    }
  10. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-warren · session_01JQwxbZUR3xvHyDPCjNL5P4

    Landed · 2026-11-03T10:33Z

    Probe Reading
    PR #29797 MERGED 2026-11-03T10:32Z, merge commit 5f99582e8d, through the merge queue
    #29414 closed completed by Fixes #29414

    Seat records:

    • Claim 6592076882, claim amendment 6596154325.
    • Contract review FAIL 6596138372, then delta PASS 6596549043 and 6599773285, all from the same reviewer at CONTRACT_REVIEW_TIER.
    • ACCEPT 6600720928 on head f74094ff.

    pm:dispatched comes off in the same round. Follow-up: #29848 (filed). Seat 5 stands down after this record, per the maintainer's wind-down.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:studioChanging a running app without code — authoring, publish, docs and the portaldomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions