Skip to content

metadata-protocol: after a restart, two stored rows of one name that each name a different holder leave only the last one loaded, for every package-slotted type (tool, webhook, …) #33127

Open

Description

@objectstack-fleet

triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

Filing gate: ① a product defect with a named location, measured on main. It is filed on the ADR-0150 epic seat's ruling 5 (red-line read 6840825374): #32692 keeps its webhook-only scope, and this widening goes to a card of its own, as asked in 6838938411. The epic seat carried it to triage (6857956853), and PR #33124's body names it as carried.

What happens

Two packages hold one name of a package-slotted type, for example a tool that each ships. The environment has saved one stored row per holder, each row naming its own holder. After the server restarts, the boot hydration (loadMetaFromDb) keeps only the last row loaded. The other holder's stored customization is no longer served: that package runs its shipped item, with no error or warning. The row is still in the database.

Measured on main by the epic red-line read and by os-dev report 6857853868 (PR #33124, scratch merges with a888c5c9ef and 2c98485db): two stored rows, each naming a holder, leave only the last loaded after hydration. This holds for tool as well as webhook, and by the same code for every PACKAGE_SLOTTED_STORE_TYPES type. Since #32741, a package-less row is no longer grafted onto a holder; that half is fixed. This card is the remaining case.

Why it matters

Whoever customised the losing holder's item (an admin, or an agent through MCP: a tool's runtime create is open, so the row need not come from an older release) sees the customization silently revert after every restart. This is the "silently wrong" class #32997 grades as p1. It needs two installed packages holding the same name, both with a stored row.

Do

Also carried here: the heal path (added by triage 2026-11-13 evening, from PR #33149 / #32820, review#1 record 6864058135)

Same hydration/heal family, in packages/objectql/src/registry.ts, which is outside #32820's surface. Both items are main's answer and were not moved by that PR. Weight p3 (transient or rare), done after this card's main case.

  • Heal 1 (③): a holder that is installed (not re-enabled) after the hydration leaves a package-less overlay row in the other holder's slot until the next unscoped hydration.
  • Heal 2 (n1): when no holder is served at the enable, the heal rebuilds through the lookup that names no package and drops its record. If that lookup answers a disabled holder's artifact, the next enable has nothing to rebuild, as the hydrator itself would leave it.

Reader

domain:server. ADR-0150's red lines apply: resolution is never loosened, ambiguity is reported and never guessed, and the change is independently releasable.

Dedupe

boot hydration two stored rows holder: only #32692 (webhook scope, by ruling). PACKAGE_SLOTTED_STORE_TYPES hydration: 0 open. loadMetaFromDb last loaded: 0 open.

Activity

  1. added
    pm:queueReady for the PM dispatch loop
    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterate
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 13, 2026
  2. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Path: none

    Triage: grade · domain:server · p2 · pm:queue · 2026-11-13T15:54Z

    ⛔ Not a claim.

  3. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#31527#3 · os-tesla · session_01L9qibzECS2npZFku4956Um

    Scope note: #32692's half 2 ("one row per holder") is carried by this card · 2026-11-14T01:02Z

    ⛔ Not a claim. This seat (epic:#31527#3) owns this card's line (the maintainer's order 6866786021, batch 2).


    Generated by Claude Code

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 14, 2026
  5. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cy1y7UNVbT2QAdmgdn
    Claim: area-lease pilot #33059, area E1 · 2026-11-14T16:05Z
    Branch: claude/area-E1-33127
    Account: os-elon-musk
    Clause-②: yes (narrowing)
    Metadata-protocol: semantic

    Coupled set in one PR: this card (lead), with #32900, #32663 and #32662; #32692's pair-row rule is read, not changed. Owned files beyond the E1 lease: packages/objectql/src/registry.ts (the stored-row slot model) and packages/metadata-protocol/src/protocol-registry-hydration.ts. No E1 hot file is edited. Open E2b PRs (#33271, #33274) touch neither file.

    The narrowing arm: an action held only as stored rows by two packages was served by the read naming no package after a restart (the row hydrated last); it now refuses 409 naming both, as every other type the registry reads by package already did.

  6. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cy1y7UNVbT2QAdmgdn
    os-dev-report

    {
     "issue": 33127,
     "status": "done",
     "branch": "claude/area-E1-33127",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/33513",
     "session": "session_016WV4cy1y7UNVbT2QAdmgdn",
     "premise_still_valid": true,
     "summary": "SchemaRegistry kept ONE bare slot per name for a stored row, so the row saved or hydrated last took it; every symptom in #33127/#32663/#32662/#32900 was that slot. A stored row bound to a package (its own, or its .custom pair's by the pair-row rule) of a PACKAGE_SLOTTED_STORE_TYPES type (less flow, permission) now registers in that package's own overlay slot beside the composite entry; every bare-slot reader sees the slots, ranked per package, and the exact read admits a pair address at its stored-row base. Each address serves its own row in every registration/save/row order, before and after a reboot, on writer and peer; the bare read of a two-holder name stays 409 naming both (#32607). One narrowing: an action held only as stored rows by two packages, bare read after a restart, 200 (last hydrated) -> 409 naming both; ADR-0087 semantic entry stored-row-own-package-slot. The slot model also moves #33131's carried face (registry action item with both overrides declared -> each its own override); that pin moved to its TO and #33131 is NOT closed. One PR, ~760 changed lines (no split).",
     "tests": "Head 8d85d2b1af, all under os-verify-lock. Targeted: objectql 4 files Tests 144 passed (144); runtime meta-disabled-holder-served-stack Tests 36 passed (36); metadata-protocol register-item-call-sites Tests 5 passed (5). Typecheck objectql+metadata-protocol+runtime+spec TC_EXIT=0. pnpm lint LINT_EXIT=0. turbo run test over the 83 dependents of objectql/metadata-protocol/runtime + @objectstack/spec: Tasks 186 successful, 188 total; failed: @object-ui/example-schema-catalog#test 'sh: 1: vitest: not found' (box install, NOT MEASURED) and @objectstack/dogfood#test 2 'Test timed out in 5000ms' under contention ([#31985] bare-name-one-holder-ai-family, [#29414] control package-commit-revert-path-package), both green re-run alone under the lock (VERDICT command-exit 0, Test Files 2 passed, Tests 39 passed). Ablations (ablation-replace.mjs; mp rebuilt; dist-preflight marker present, then restore + rebuild + --absent exit 0; whole tree clean): overlaySlot forced off (ABLATION_33127_SLOT) objectql 27 failed/117 passed = [#33127] boot 16 + [#32663] 6 + [#33131] moved 4 + [#32900] pair-first 1, runtime 2 failed (#33127 doors); storedRowBases off: runtime 4 failed (#32662 pair addresses); one-holder served arm off (ABLATION_33127_PLAINB): runtime 3 failed (#32662 (b) x2 + #32607 M1); isBundleAddition row-only exemption off: objectql 1 failed ([#28802] dependent's member at the same locale). CONTROLs green throughout. First slot/plainb pass used false-&& markers the build folded away (dist preflight exit 1) - re-run with globalThis markers, preflight exit 0.",
     "gates": "Local: dispatch-gates --commands (102 families on the final diff) all run; exit 0 except check:dual-build-cjs-loads exit 3 'Run pnpm build first' (NOT MEASURED, CI); check:engine-double-contract and check:objectql-double-limit exit 1 on first pass (new unbounded find double) -> fixed + ledger --write -> both exit 0. --ran reconciliation: after the last batch, 0 unrun (the 7 families the ledger edit added were run, all 0). check:adr-0087-registration exit 0 (registered stored-row-own-package-slot). check-nul-bytes OK. PR #33513 check-runs at report time: 19 in_progress (Check Changeset, Lint x8, Type Check x4, Spec property liveness, PR-body guards, Governed Surface Queue Guard, Check PR Size, Flag docs, filter), 5 skipped; not waited on.",
     "dogfood_battery": "8d85d2b1af · turbo run test (dogfood#test, no shard, no path) Test Files 2 failed | 463 passed | 3 skipped (468) · Tests 2 failed | 3930 passed | 42 skipped (3974) · failures: bare-name-one-holder-ai-family [#31985] and package-commit-revert-path-package [#29414] control, both 'Test timed out in 5000ms' on a contended box; re-run alone under the lock VERDICT command-exit 0, 2/2 files, 39/39 tests",
     "mcp_calls": "0 this segment (issue_read / pull_request_read reads only, before the context summary); no MCP write tool",
     "api_writes": "4 — POST issues/33127/comments (claim, via post-stamped relay), git push (branch), pr_create #33513 (fleet-write relay), POST issues/33127/comments (os-dev-report, via post-stamped relay)",
     "open_questions": [
      {
       "question": "#33127's body also lists heal 1, heal 2 and S1. The slot model makes restoreArtifactRegistryView retire the removed package's slot, but those three items are not separately pinned here, and the PR body's closing line for this card closes it on merge. Keep Fixes or downgrade to Part of?",
       "options": [
        "A keep the closing line; file heal 1/heal 2/S1 as a follow-up sub-issue if the seat finds them still open",
        "B change the closing line to a Part-of line (seat-written edit) and leave the card open for those items"
       ],
       "recommendation": "B if the seat reads heal 1/heal 2/S1 as separate acceptance criteria (the body has no pins for them); A otherwise"
      },
      {
       "question": "The coordinator's pre-push rule (tests of touched packages plus all dependents, pnpm lint) conflicts with os-dev's 'repo-scale runs are CI's'. I followed the coordinator but narrowed the dependents to the 83 reached through code (objectql/metadata-protocol/runtime) plus spec, leaving the 39 packages reached only through the new spec migration entry to CI. Accept the narrowing?",
       "options": [
        "A accept (declared narrowing)",
        "B require the full 122-package affected set locally"
       ],
       "recommendation": "A: the spec change is one semantic-entry file read only by spec's migration registry, and the 83+spec run held the lock 3h30m on a shared box"
      }
     ],
     "security_findings": "none",
     "out_of_scope_findings": [
      "carrier: #32663 residual (a) · dashboard stays converged by name in the registry (one bare slot); #32451 CONTROL kept; awaits its ruling · noted, not filed",
      "carrier: #33131 · its carried face (registry action item with both overrides) is now each holder's own override; pin moved to TO; seat to judge whether the card closes · noted, not filed",
      "carrier: none · getMetaItemLayered at a pair address is unchanged (only the plain read adopts storedRowBase) · Acceptance notes only",
      "carrier: none · dogfood [#31985] bare-name-one-holder-ai-family ran 5011ms under contention against vitest's 5000ms default; green alone · Acceptance notes only"
     ],
     "schema_registry_symbols_changed": "registerItem (5th arg options.storedRowPackage), bareSlotEntriesOf, rankedForPackage (new, private), orderedOverlayTier (new, private), removePackageOverlaySlots (new, public), getItem, getPackageScopedItem, overlaySlotBinding, boundServedActions, unregisterItem, packageScopedHoldersOf, ownersHoldingAName, isBundleAddition (3rd arg codeMembersOf); module helpers OVERLAY_SLOT_MARK, overlaySlotKey, parseOverlaySlotKey, overlaySlotEntriesOf, storedRowPackageOf. E2b overlap: none — open E2b PRs #33271 (plugin.ts, action-handler-coordinate.ts) and #33274 (runtime action-execution.ts) touch no file of this PR; #33271 reads composite keys, which this change leaves as they were.",
     "deviations": "Coordinator pre-push rule followed with the declared narrowing above. Branch was not pushed empty first (the coordinator rule gates every push on the dependents' run); the first push carried the finished, tested head. A first ablation pass for two arms was void (marker folded by the build) and re-run, both legs recorded."
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:serverDomain lane — seat card indexed by label:pm:seatlease:E1pm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions