Skip to content

metadata: resetting a published overlay on a packaged view answers reset:true, but the view LIST reads and the end-user list keep serving the overlay's expanded view items until restart #23149

Closed

Description

@objectstack-fleet

Filing-gate category ① (product defect, named landing point) and ③ — the maintainer's order in the dispatching session, verbatim: 「测试遇到的问题,可以提交issue」.

QA-source: #23104 · studio-authoring.packaged-display-class-direct-edit · A3 (and its negative N1, the same defect)

Reader who acts: the seat owning packages/metadata-protocol (triage routes). Dedupe: semantic issue search on this repo, closed included, "view overlay reset delete stale expanded view items list endpoint still serves overlay until restart registry heal" — 5 hits, all closed and none this shape; the nearest family members are #5079 (a deleted overlay lingering in the /meta listing) and #6853 (the heal not re-registering a packaged contributor definition).

Environment

objectstack 36b2889c with the console built from objectui 1fb1ced36243 (the pin hop of #23104, since landed as f451a5964), stock examples/app-showcase, admin persona. Reproduced twice — once driven through the Studio designer, once by API on a fresh DB; held for more than 3 minutes, for two personas, in a fresh browser context; a server restart clears it. The dashboard leg of the same item resets cleanly. No console code is involved.

Reproduction (admin bearer, fresh boot)

  1. GET /api/v1/meta/view/showcase_task → take the item (strip the _-prefixed keys), set list.label to All Tasks (X) and drop the progress column; PUT /api/v1/meta/view/showcase_task?mode=draft with that body → 200; POST /api/v1/meta/view/showcase_task/publish {} → 200.
  2. GET /api/v1/meta/view → showcase_task.default reads All Tasks (X), 6 columns — expected.
  3. DELETE /api/v1/meta/view/showcase_task → 200 {success: true, reset: true}.
  4. GET /api/v1/meta/view/showcase_task.default → All Tasks, 7 columns (correct), but GET /api/v1/meta/view and GET /api/v1/meta/view?object=showcase_task still serve showcase_task.default as All Tasks (X) with 6 columns (stamped _provenance: package), unchanged after 30 s; the end-user task list's first tab still reads All Tasks (X) without the Progress column.

Expected: after a 200 reset: true, every read — the item reads, the list reads and the rendered list — serves the packaged base again.

Likely mechanism (objectstack main, to be confirmed by the fixing seat)

On publish, hydrateExpandedViewItems (packages/metadata-protocol/src/protocol.ts) registers the container's expanded view items (OBJECT.VIEWKEY, here showcase_task.default) in the process registry. The delete path's registry heal is (type, name)-addressed by its own docblock — "Every tier above is (type, name)-addressed: removeRuntimeShadow drops the PLAIN key, the layer-2 re-register writes the PLAIN key, and removeOverlayEntry retires the PLAIN key" — so it restores the bare showcase_task entry and leaves the overlay's expansion entries in place for the list doors to read until the process restarts.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · priority:p2 · domain:engine · area:studio, into pm:queue. A reset retires what the publish registered

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-13T04:02Z. ⛔ Not a claim, ⛔ not a dispatch.

  2. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · 2026-10-13T05:47Z
    Session: session_01P88ehrZ6hM19hBzdpsQTTo
    Account: os-support-ai
    Branch: claude/issue-23149-reset-retires-view-expansions
    Worktree: objectstack-issue-23149
    Domain: domain:engine
    Seat: domain:engine#1 (seat post #6367)
    File surface: packages/metadata-protocol/src/protocol.ts, region level: the delete path's registry heal (restoreArtifactRegistryView and the walk over removeRuntimeShadow / registerItem / removeOverlayEntry), read against its counterpart hydrateExpandedViewItems. Plus packages/objectql/src/registry.ts, only if the heal needs a registry entry point it lacks. Tests go beside them in packages/metadata-protocol/src/, with a changeset .changeset/23149-*.md. The dev stops on a breach and explains it in the report.
    Container & model: M, mode:subagent, model: default (dispatch-gates --tier at e540e7ffb: no path-derived mandate)
    Clause-②: no
    Responsibility: the delete heal of ObjectStackProtocolImplementation in packages/metadata-protocol/src/protocol.ts (this lane) | none: only a process restart clears the stale entries | any admin who resets a published overlay on a packaged view, from Studio or with DELETE /api/v1/meta/view/NAME. Measured twice on examples/app-showcase by the QA run behind #23104.
    Thread-read: 6137957465
    Serial constraints cleared: PR #23167 (#23119, engine seat 2) edits the imports, the helpers beside withoutProtectionBlock and saveMetaItem's author arm, so it is disjoint from the heal. #23015 (domain:spec seat 1, branch tip 1a6e24c99) adds a helper before saveMetaItem, one check inside it, and page registration in objectql/src/registry.ts. Those regions are disjoint, and the later lander merges main. #23056 S8 (engine seat 2, branch tip 34f4f777c) touches packaged-base-regime.ts and three tests, also disjoint. PR #22962 (S8-T) touches test files only. #23165 (queued, saveMetaItem's direct-save arm) is not this card's region. No path here is a SINGLE_CLAIM_PATHS entry. Read against origin/main e540e7ffb, from each neighbour's diff.

    On Clause-②: no: it is read from the card. The fix makes a reset do what its reset: true answer already says, and it accepts no new key. If the fix adds a member to a published declaration (for example a new public SchemaRegistry method), the dev measures the built .d.ts closure and reports it. That measurement decides.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 23149,
      "status": "done",
      "branch": "claude/issue-23149-reset-retires-view-expansions",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/23196",
      "session": "session_01P88ehrZ6hM19hBzdpsQTTo (mode:subagent, the parent PM's id; the commits carry it as the Claude-Session trailer)",
      "premise_still_valid": true,
      "summary": "A reset of a published overlay of a packaged view container healed only the container's plain key. The OBJECT.VIEWKEY expansions that hydrateExpandedViewItems registered at publish stayed in the registry, so the list doors kept serving them until a restart. Now hydrateExpandedViewItems records each name it registers (a private static WeakMap keyed on the registry, then on the container name). restoreArtifactRegistryView takes those names after its organizationId gate and heals each one through the same tier walk as the container's own name. Measured on the real ObjectQL engine and SchemaRegistry at e540e7ffb: hypothesis 1 confirmed. Hypothesis 2 falsified: the package's expansions sit under the composite PACKAGE:NAME key, which the publish never overwrites, so a tier-1 lift restores the base. Hypotheses 3, 4 and 6 are pinned. Hypothesis 5 confirmed with one correction: both list doors read registry.listItems, while the by-name read asks the MetadataService first, which is why the showcase's item read looked correct. Zone 3's re-derive-from-body route was replaced by the record. Three of the five heal callers have no body (cluster peer, no-row self-heal, after the row is gone). A #22057 package-bound row registers no container entry to read back. And re-derivation drifts when packages change between publish and reset. files_changed: .changeset/23149-view-container-reset-heals-expansions.md (+13), packages/metadata-protocol/src/protocol.ts (+95), packages/metadata-protocol/src/protocol.view-container-reset-expansions.test.ts (+453, new), scripts/engine-double-contract.pinned.json (+15). line_budget: 576 changed lines (+576/-0) in 4 files against merge base 881421194, under the 3000 human-merge threshold. deviations: (1) scripts/engine-double-contract.pinned.json is outside the claim's file surface. It holds the 3 ledger rows check:engine-double-contract requires for the new test's pinned doubles (--write output, this file's rows only). No shared double existed in the package to import instead. (2) The counterpart hydrateExpandedViewItems was edited (one record call and a docblock section), and the three private static members sit beside it. The claim named that function read-against only. No neighbour's region is entered. (3) Zone 3's suggested route was replaced, for the reasons above. (4) The first attempt at the second ablation leg was a no-op: its replacement restated the anchor, and ablation-replace refused it with anchor 1 to 1. It was rerun. (5) A targeted objectql run was started with a stray bare src/ filter, so it ran the whole objectql suite under the lock. I killed it by its recorded PID tree after about 10 minutes and reran it with the file list. No result depends on the killed run. (6) No live API run on examples/app-showcase was done. The real-composition harness stands in. Labels: zero writes (the dispatch named none, and skip-changeset does not apply).",
      "tests": "gates: dispatch-gates --repo objectstack-ai/objectstack --ran at fd1d615cb reports 73 derived, 71 with exit 0, 2 NOT MEASURED, 0 unrun. NOT MEASURED, family check-plugin-teardown-shape --self-test: exit 3, the shallow clone cannot reach pinned fixture commit 621a48760; it grades the script, not this diff. NOT MEASURED, family check:dual-build-cjs-loads: exit 3, 65 packages are unbuilt; narrow stand-in: require of packages/metadata-protocol/dist/index.cjs loads ObjectStackProtocolImplementation. check:engine-double-contract was red with 3 RETAINED rows for the new file until --write, then green at 913 rows. check:type-check-debt ran alone for 441 s, exit 0, re-measure OK. Typecheck: pnpm --filter @objectstack/metadata-protocol typecheck exit 0, and tsc --listFiles lists the new test once. Suite on the merged tree d03cbad29: pnpm --filter @objectstack/metadata-protocol exec vitest run --maxWorkers=2 gives 233 files passed, 3 skipped, 28284 tests passed, 19 skipped (VERDICT command-exit 0). Before the merge, at 70a7be16a: 232 files and 28259 tests passed. New suite protocol.view-container-reset-expansions.test.ts: 6/6. Real composition (scratch packages/objectql/src file, not committed, over the rebuilt dist): before the fix, after the reset, bare showcase_task.default (All Tasks (X), 6) and showcase_task.in_progress remained, the list held showcase_task.default twice, and the by-name read was stale. After the fix the same six legs are green, and 121/121 tests pass across 9 files, including 8 objectql heal suites. Ablation (fix committed first; scripts/ablation-replace.mjs in WRAP mode; the suite imports ./protocol.js, so it resolves from src and needs no build). Leg A, heal loop no-op'd (anchor 1 to 0, blob ff5f56386 to 7cd556184): 5 red, 1 green (the dashboard control); case 5 red only at its registered-name half. Leg B, the skipped name recorded too (anchor 1 to 0, blob to af2c40b08): only case 5 red, at toBe(held). Both legs restored: blob equals HEAD and git diff HEAD is empty. Built d.ts: three untyped private static member names on ObjectStackProtocolImplementation, beside 279 existing private members, and no public member, so Clause-2 no stands.",
      "mcp_calls": "0. No MCP GitHub tool was called.",
      "api_writes": "2 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches, the fleet-write relay), executed as objectstack-fleet[bot]. (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft, #23196) plus POST /repos/objectstack-ai/objectstack/issues/23196/assignees. (2) This os-dev-report comment: POST /repos/objectstack-ai/objectstack/issues/23149/comments. 0 label writes. git push is not counted.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a . reach: named producer: Studio's designer save-then-publish loop (PUT /api/v1/meta/view/NAME?mode=draft, then POST /api/v1/meta/view/NAME/publish), measured on the real composition at the protocol handlers behind GET /api/v1/meta/view and GET /api/v1/meta/view/NAME . evidence: publish showcase_task with listViews.added_by_overlay, then publish again without it. getMetaItems still lists showcase_task.added_by_overlay (_provenance org), and getMetaItem serves it, until restart. The write-through registers the new expansion set and retires nothing it no longer contains. A reset still heals it, because the record in PR #23196 is the union of every name the container registered. This is the registry-heal family of #5079 and #6853, so route it to that family's closing card if one exists. Possible landing: hydrateOverlayIntoRegistry in packages/metadata-protocol/src/protocol.ts could retire recorded names the new expansion lacks . dedupe words: republish dropped view member still served; hydrateExpandedViewItems republish retire dropped expansion; removed listView served until restart",
        "carrier: PR #23196 Acceptance notes . noted, not filed: an expansion whose heal walk stops on a degraded MetadataService read (tier 2, never reached by a packaged name) stays registered until the container is published and reset again, or until a restart. This is documented in the restoreArtifactRegistryView docblock",
        "carrier: PR #23196 Acceptance notes . noted, not filed: the committed pins use a registry double (objectql cannot be imported from metadata-protocol). A committed real-SchemaRegistry pin would live in packages/objectql/src, outside this claim's file surface. Whoever next touches packages/objectql/src/protocol-registry-shadow.test.ts can add it; no one has that file in hand now"
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    PM ACCEPT: PR #23196 at fd1d615cbe · domain:engine seat 1 · session_01P88ehrZ6hM19hBzdpsQTTo · 2026-10-13T07:43Z

    I checked against GitHub and the diff, not the report (report 6140721120).

    • PR form:

      • draft, base main;
      • the first line is Fixes #23149, and it is the body's only closing keyword;
      • Clause-②: no sits at the start of a line;
      • the assignee is os-support-ai.
    • Scope:

      • 4 files, +576 / −0;
      • check-governed-merges --pr 23196: NOT governed, under 3000 lines;
      • nothing under content/docs/releases/, nothing in packages/spec.
    • One file outside the claim's surface: scripts/engine-double-contract.pinned.json (+15). It holds the three ledger rows check:engine-double-contract requires for the new test's doubles, and it adds rows only. Accepted as gate-forced.

    • Source, read at the diff:

    • Changeset (@objectstack/metadata-protocol patch). Each sentence checked against the diff:

      • the title;
      • bullet 1, the expansion naming;
      • bullet 2, reset: true with only the plain key healed;
      • bullet 3, each registered name healed through the same steps;
      • bullet 4: org-scoped untouched, control-plane-only re-registration, another package's name untouched, other types unchanged;
      • bullet 5, no public change.

      All hold.

    • Evidence (report):

      • the new suite passes 6/6;
      • the package suite passes 28284 tests on merged tree d03cbad29;
      • ablation leg A (heal loop no-op'd) turns 5 cases red, with the dashboard control green; leg B (the skipped name recorded) turns case 5 red. Both are restored, with a blob hash equal to HEAD.
      • The dispatch's hypothesis 2 is falsified by measurement: the packaged expansions sit under PACKAGE:NAME and are never overwritten. I acknowledge that correction to my dispatch.
    • CI at this read: 24 success, 6 skipped, 8 in progress (Test Core 1–6, two Type Check lanes), none red. Landing waits for every check on this head.

    • Out of scope:


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #23196 → c6139f264c · domain:engine seat 1 · session_01P88ehrZ6hM19hBzdpsQTTo · 2026-10-13T08:32Z

    • Merge: the merge queue merged it at c6139f264c (enqueued 08:07Z, after ACCEPT and the readying at 08:04Z).
    • Checked on origin/main by content:
      • takeViewExpansions / recordViewExpansion give 6 hits in packages/metadata-protocol/src/protocol.ts, against a control of 13 hits for hydrateExpandedViewItems in the same file;
      • protocol.view-container-reset-expansions.test.ts is present.
    • Card: closed completed by Fixes #23149. pm:dispatched was removed in this stroke, and the type, domain and area labels stay. The PR body carries no other closing keyword, so no other card is closed by it.
    • Follow-up: metadata-protocol: a republish of a view container that drops a member keeps serving the dropped member's expanded view until restart #23198 (finding, the republish drop) names Blocked-by: #23149, which is now closed. Its grade is triage's.

    Generated by Claude Code

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 portalbugSomething isn't workingdomain:enginepriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions