Skip to content

metadata-protocol, objectql: the two non-blocking contract-review notes on #27535 — guard the promotion door's permission-set name judge with draftForGate, and note the metadata-protocol caller on the registry member #27707

Closed

Description

@objectstack-fleet

domain:devx#1 · sunhaolin · session_017xRBcVy4VXmkQjn2MGeaWk

Filing gate: ③ a maintainer-directed task. The maintainer, in the PM session session_017xRBcVy4VXmkQjn2MGeaWk on 2026-10-29, said (verbatim): 「其他任务正常提交 issue」. This card carries the two non-blocking notes the record left on PR #27535 (merged), filed as a normal card.

Business reason: #27535 landed with two notes its contract review (comment 6501292842, PASS) marked non-blocking and the seat's ACCEPT (comment 6502611799) set aside "for a follow-up, not held against landing". One makes the promotion door's refusals follow the order its sibling judges already use; the other makes a future rename of a private registry member fail at its source.
Reader: the dev who takes this card from the queue (packages/metadata-protocol/src/protocol.ts and packages/objectql/src/registry.ts).
Dedupe: searched the open and closed issues (GitHub issue search scoped to objectstack-ai/objectstack) for "permission set name collision promotion draftForGate registry packageHeldSecurityCatalogNames" (6 hits) and "NAMESPACE_CONFLICT permission set publish NO_DRAFT refusal order follow-up 27535" (24 hits). None covers either note.

What to change

Note 2: the promotion-door ordering

The ACCEPT names it: "Note 2: the promotion-door ordering, a refusal-for-refusal change with no accept-set effect." The contract review (should-fix 2, "Refusal order at the promotion"): "permissionSetNameCollisionRefusal at :23044 runs unguarded, while its three siblings (permissionSetOwnerBindingRefusal, defaultRecordPageSaveRefusal, the name judge) run under if (draftForGate), and repo.promoteDraft's NO_DRAFT (sys-metadata-repository.ts:1552) comes after. […] Refusal for refusal, no accept-set effect; guard it with draftForGate like the siblings so the first fact the caller is told is the one about its own state, or record in the comment why this judge runs first."

At origin/main a75ddba1, packages/metadata-protocol/src/protocol.ts:

  • :22935: draftForGate is read.
  • :23019 (name judge) and :23031 (permissionSetOwnerBindingRefusal): each runs under if (draftForGate).
  • :23046–23051: the permissionSetNameCollisionRefusal call sits in a bare { … } block, not under if (draftForGate).
  • :23058: defaultRecordPageSaveRefusal runs under if (draftForGate).
  • packages/metadata-protocol/src/sys-metadata-repository.ts:1570: promoteDraft sets NO_DRAFT (the review cited :1552 at the PR head).

The ask: put the :23046 block under if (draftForGate) like its siblings, so a promotion with no draft at the address gets the repository's NO_DRAFT answer first. If there is a reason it must run first, record that reason in the comment above it (:23037–23045) instead. Either way, add one pin for the order: a per-item publish with no draft answers NO_DRAFT, with the existing collision pins still green.

Note 3: a one-line comment on the registry member

The ACCEPT names it: "Note 3: a one-line comment on the registry member." The contract review (should-fix 3, "Private member reached structurally"): "packageHeldSecurityCatalogNames is private on SchemaRegistry, and the door reaches it by duck-typing (read.call(registry)), as the exported findPackageHeldSecurityCatalogNames itself does with bracket access. […] a one-line note on the registry member naming the metadata-protocol caller would make a future rename red at the source as well as in the test."

At origin/main a75ddba1:

  • packages/objectql/src/registry.ts:3503: private packageHeldSecurityCatalogNames(); its doc comment (:3491–3502) names findPackageHeldSecurityCatalogNames (:2370–2374) as its exporter but not the metadata-protocol caller.
  • packages/metadata-protocol/src/protocol-save-door-refusals.ts:283 and :296–297: the caller that reads the member by name.

The ask: add one line to the doc comment at registry.ts:3491–3502 naming protocol-save-door-refusals.ts (registryPackageHoldersOf) as a by-name reader, so renaming the member means updating that caller and its objectql test.

Out of scope: any change to what the doors accept or refuse. Both notes are ordering and comment work only.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 真挡得住的权限 | none | P②

    Triage: p2 · domain:server · area:access · 2026-10-29T19:55Z

    ⛔ Not a claim.

    • Read fresh. Both notes are server lane (metadata-protocol, objectql). Guard permissionSetNameCollisionRefusal with draftForGate like its three siblings: a refusal-for-refusal reorder with no accept-set effect, pinned by the order the caller is told. The registry member gets its caller note.
    • protocol.ts is a hot file, so run the in-flight intersection check.
    • pm:queue.

    Generated by Claude Code

  2. added
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    domain:serverDomain lane — seat card indexed by label:pm:seat
    and removed on Oct 29, 2026
  3. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F

    Claim: PM loop round 4
    Session: session_01L6bhgUCizoYWQVJWjhdR6F
    Account: os-sales
    Branch: claude/issue-27707-promotion-guard-order
    Worktree: objectstack-issue-27707
    Domain: domain:server
    Seat: domain:server#2
    File surface: packages/metadata-protocol/src/protocol.ts (the promotion door's permissionSetNameCollisionRefusal call only), packages/objectql/src/registry.ts (a caller note on the private member only), their tests, .changeset/* (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: default (dispatch-gates --tier: no path-derived mandate), effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: metadata-protocol promotion door refusal order (this lane) | the three sibling judges already run under draftForGate | every caller promoting a permission set
    Thread-read: 6506884031
    Premise: holds (read on main 6d9419bd 2026-10-29T23:00Z: permissionSetNameCollisionRefusal runs outside the if (draftForGate) block its siblings use)
    Serial constraints cleared: protocol.ts is also edited by this seat's #27444 (draft-verdict wiring, a different hunk); git merge-tree against it before pr_create.

  4. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
    os-dev-report

    {
      "issue": 27707,
      "status": "done",
      "branch": "claude/issue-27707-promotion-guard-order",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27825",
      "session": "session_01L6bhgUCizoYWQVJWjhdR6F",
      "premise_still_valid": true,
      "summary": "The draft promotion in packages/metadata-protocol/src/protocol.ts now runs permissionSetNameCollisionRefusal under if (draftForGate), like its four sibling draft judges. Promoting with no draft while another owner holds the name used to answer NAMESPACE_CONFLICT/422 and now answers NO_DRAFT/404. The accept set does not change: with no judged draft, repo.promoteDraft gets expectedDraftHash null and answers NO_DRAFT or DraftConflictError, so it never succeeds either way; with a draft, the same refusals run in the same order (Clause-②: no confirmed). The private SchemaRegistry.packageHeldSecurityCatalogNames doc comment in packages/objectql/src/registry.ts now names its by-name metadata-protocol caller (registryPackageHoldersOf) and the objectql test. This is a comment only. The card assignee was not touched. Head 98bfa9de includes one merge of origin/main 8f25cee2.",
      "tests": "New pin in protocol.permission-set-name-cross-package.test.ts through the real publishMetaItem door. Red first at 83e4daac (Expected NO_DRAFT, Received NAMESPACE_CONFLICT; 1 failed, 17 passed), green after the fix (18/18). Controls: the existing draft-plus-collision pins (publishMetaItem and publishPackageDrafts answer NAMESPACE_CONFLICT) and the clean-promote CONTROL stay green. Ablation via scripts/ablation-replace.mjs: anchor x1 to x0, blob 5fb0787b to 2d3ef72c, the new pin goes red, restored blob == HEAD 5fb0787b with git diff HEAD empty. The first ablation attempt was refused by the tool before running (the replacement was a substring of the anchor), so it was a no-op and is not a reading. At 98bfa9de: metadata-protocol test 280 files passed, 4 skipped, 21803 tests passed, 26 skipped, exit 0; objectql test 490/490 files, 9244/9244 tests, exit 0; typecheck for both packages exit 0.",
      "gates": "At 98bfa9de: dispatch-gates --commands derived 63 families. 61 exited 0 on the first sweep. check:dual-build-cjs-loads and check:type-check-debt exited 3 (PREREQUISITE NOT MET: no dist, and lock queue-timeout); both exited 0 when re-run after the full build. --ran reconciliation: 63 run, 0 NOT-MEASURED (a derived zero, every line carries an exit code). eslint --no-inline-config on packages/metadata-protocol and packages/objectql exited 0 each. git merge-tree against #27780 (claude/issue-27444-draft-verdict-package-prefix) is clean; no other open PR touches these files. CI check-runs on 98bfa9de: in_progress when this report was written, not awaited per contract.",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2 — relay pr_create (repository_dispatch to fleet-write, created PR #27825 as a draft with no assignees, reviewers or labels); post-stamped comment POST /repos/objectstack-ai/objectstack/issues/27707/comments (this report). git push is not counted.",
      "line_budget": "33 insertions, 1 deletion across 4 files",
      "deviations": [
        "Commit trailers use the model-free pair AGENTS.md requires, not the harness model-named Co-Authored-By.",
        "post-stamped requires the seat signature as the first line, so the os-dev-report marker is on line two.",
        "The objectql change is comment-only and its suite ran in full, but the objectql test:repo project was not run locally; that is left to CI Test Core."
      ],
      "files_changed": [
        "packages/metadata-protocol/src/protocol.ts",
        "packages/metadata-protocol/src/protocol.permission-set-name-cross-package.test.ts",
        "packages/objectql/src/registry.ts",
        ".changeset/27707-promotion-no-draft-before-name-collision.md"
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "none — carrier: none"
      ]
    }
  5. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F

    ACCEPT → PR #27825 · head 98bfa9de · 2026-10-30T01:21Z · landing sent.

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

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain: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