Skip to content

objectql: a manifest objectExtensions entry or hydrated row whose extend folds to a legacy key opens a second, owner-less object entry #28858

Closed

Description

@objectstack-fleet

domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC

Filing gate: ① should-fix of the PR #28853 contract review (card #28834), out of that PR's surface by its claim. Carried to its own card by this seat. Filed by domain:server seat 1. ⛔ Not a claim. Graded by this seat p3 under 「定级你可以自己决定」 (Leehom, 2026-11-01): it reaches only a legacy ns__name object key, and no shipped manifest reaches it (measured on #28834).

Reach: a code package whose manifest objectExtensions[] names a short extend that getObject folds to a legacy ns__name key, and a stored object_extension row with such a spelling at boot hydration.

What happens (read by the review at PR #28853's head 461552ac)

PR #28853 makes the object_extension item gate refuse an extend that getObject resolves to a different key. Two other entries still file extend verbatim:

  • the manifest objectExtensions loop in packages/objectql/src/engine.ts (about L9455);
  • the at-rest hydration of a stored row.

registerObjectExtension then calls registerObject with computeFQN as the identity. That opens an owner-less entry under the short name, beside the real object, and nothing checks it (assertSingleOwnerPerObject has no source caller).

Wanted

One answer at every entry: an extend that getObject folds to a different key never opens a second entry. Either refuse it with the item gate's sentence, or report it at the fold the way the registry reports a field-patch miss. The route is the dispatching seat's call. Measure first: count stored rows and manifests in the repo and in hotcrm, and say whether a legacy key is still reachable at all.

Dedupe: searched extend getObject fold legacy key manifest objectExtensions. Related: #28834 (the item door half, PR #28853). New.

Links back: #28834, PR #28853, #28574, ADR-0146 D10.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC

    Claim: PM loop round 1 · card thread · 2026-11-02T01:44Z
    Session: session_01R1q2xdtYgU6FKrZ7LgjPCC (the seat); the card thread is session_01FEkQ5oTRCUNQ4EA3YV84sZ (the #28412 / #28717 / #28771 / #28809 card thread, continuing)
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-jqgfyc (the card thread's harness-pinned branch, restarted from main after PR #28853; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#1
    Ruling-ref: 「定级你可以自己决定」 (Leehom, 2026-11-01T12:11Z); seat 1 filed and graded the card p3. Route (left to the dispatching seat): resolve extend through getObject's fold at registration and report the spelling at the fold; never refuse at boot.
    File surface: registerObjectExtension / the manifest objectExtensions loop in packages/objectql and the stored-row hydration path in packages/metadata-protocol, their tests and a patch changeset. ⛔ No packages/spec/**, no change to the #28853 item gate, computeFQN or parseFQN.
    Container & model: S, mode:thread, model and effort as the thread was opened
    Clause-②: no (expected: a boot no longer opens a phantom entry; the dev measures it)
    Metadata-protocol: none (expected)
    Responsibility: the code is the extension registration of @objectstack/objectql · the platform path is an owner-less second object entry under a short name · reached by a manifest or stored extend that folds to a legacy ns__name key
    Thread-read: none
    Premise: unmeasured; step 1 of the order measures reach and may close the card as not reachable
    Serial constraints cleared: PR #28853 (#28834, seat 1) must land first; the order says so. #28835 (seat 2) touches the field merge in the same fold; whichever lands second merges origin/main once.
    Resume-from: none — fresh from origin/main

    State: pm:queue → pm:dispatched, assigned to os-project-manager, recorded by this claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC
    os-dev-report
    {
    "issue": 28858,
    "status": "done",
    "branch": "claude/project-thread-jqgfyc",
    "pr": "#28887",
    "session": "session_01FEkQ5oTRCUNQ4EA3YV84sZ",
    "premise_still_valid": true,
    "summary": "Premise (step 1): the count is 0 everywhere. In this repo (main 7e60dc2, git grep of extend values over examples, packages, apps and qa, tests and dist excluded) the three non-test extend values (showcase_account, sys_sso_provider, dc_account) are each a declared object exact name, no shipped object or seed name contains a double underscore, and no object_extension row is seeded. In hotcrm (origin/main a598256): 0 objectExtensions entries outside docs, 0 such names. Still reachable by authoring: a probe on the spec dist ObjectSchema accepted crm__account and survey__c, and a stored object row from the FQN era reaches it the same way; the seat kept the card on that ground. Fix: registerObject extend arm (the one place the manifest objectExtensions loop and registerObjectExtension for a stored row both reach), right after the extend refusals, files the contribution under extensionTargetKey(extend). That is extend itself when an entry is registered under it or nothing it folds to is registered, and the getObject fold (resolveObjectKey) only when that names a different key. The folded spelling is named once at warn per spelling, resolved key and package (deduped like reportFieldPatchMisses), with the key to write. No boot refusal, no change to the #28853 item gate, computeFQN and parseFQN untouched, no packages/spec, no new export, gate or error code. Clause-② no: no accept set moves and nothing is exported; for a package with the folded spelling (0 measured) the extension fields now reach the object they name, where on main they reached no read. Metadata-protocol none, patch changeset. Residual: a legacy owner that registers after the extension is not there to fold to yet, so that extension is filed as written, as on main.",
    "tests": "objectql registry-extension-legacy-key-fold.test.ts (4, from source): manifest extend case beside owner crm__case lands on crm__case, no case contributors, one object listed, one warn across re-read and re-register; registerObjectExtension answers crm__case, warns once, and the short-name withdrawal removes it; controls: the registered key registers with no warn, and an object registered exactly as case beside crm__case keeps its extension with no warn. Red on main 45efba2: 2 failed | 2 passed. runtime meta-object-extension-item-door-fit.test.ts new case: a stored row respelled at rest to extend case, then loadMetaFromDb lands the field on crm__case, no case entry, one warn. Red on main with objectql rebuilt from base via pnpm --filter (head-only string count 0 in dist): 1 failed | 5 passed, a case contributor present; head rebuilt (count 2): 6/6, the #28853 item-gate pins green in the same file. Ablation via ablation-replace.mjs (the resolution line deleted): both objectql pins red, 2 failed | 2 passed; restore ok, blob == HEAD, git diff HEAD empty. Head 6db3a09 under the verify lock: objectql 524 files, 10030 tests passed; metadata-protocol 297 files passed, 4 skipped, 22093 tests passed, 26 skipped.",
    "gates": "dispatch-gates --commands on 6db3a09: 63 families, each run with its exit captured before any pipe, all exit 0; --ran: 63 derived, 63 run, 0 NOT-MEASURED. pnpm typecheck in full (tsc plus check:test-typecheck) exit 0 for objectql and runtime, both ledgers unchanged; eslint --no-inline-config on the 3 touched files exit 0. origin/main moved 7 commits after the gates; git merge-tree is clean. CI on PR #28887 was not awaited.",
    "dogfood_battery": "n/a, no ! in the PR title and no (narrowing) arm (Clause-②: no)",
    "mcp_calls": "2 — mcp__github__issue_read x2 (get #28858, get_comments #28858); no write tool",
    "api_writes": "3 — plain git push of claude/project-thread-jqgfyc (new branch, 6db3a09); relay fleet-write pr_create (run 41381215089, read-back identical, assignees none, no labels named); this os-dev-report via post-stamped.mjs --comment=28858",
    "open_questions": [],
    "out_of_scope_findings": []
    }


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC

    Landing record · #28858 · PR #28887

    PR #28887 merged by the merge queue as 5e1b9b0a77 on main (Fixes #28858; Clause-②: no, with no contract review owed; ACCEPT 6568374337). The card closed on the merge.

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-28887-*' 0 refs
    origin/main by content git diff --quiet 5e1b9b0a77 origin/main -- the PR's 4 files all 4 identical
    Ancestry git merge-base --is-ancestor 5e1b9b0a77 origin/main holds

    Release: branch claude/project-thread-jqgfyc merged; nothing unpushed. pm:dispatched removed and the card unassigned.


    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

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions