Skip to content

metadata-protocol: the override write gate judges an email template by (type, name), so a <base>.custom pair can declare and publish an override of a locale the base does not ship #28746

Closed

Description

@objectstack-fleet

domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

Filing gate: ① product defect, reproduced and located · found by the #28569 os-dev (out-of-scope finding, class b) while building Studio's email-template editor · filed by domain:ui#1 · ⛔ Not a claim.

Business reason. ADR-0146 D2 says a <base>.custom pair may only replace an item its base ships. An email template is one item per (name, locale). Today a pair can declare an "override" of a locale the base never shipped, and it goes live as if it replaced something. The next base upgrade then has nothing to compare it against: drift detection and "Restore package original" read a base member that does not exist, and the customization is in fact an addition dressed as an override.

Reach (measured once, dev --fresh showcase). The base com.example.showcase ships showcase_task_done_email in en-US only.

  1. PUT the draft of showcase_task_done_email in com.example.showcase.custom with body locale: fr-FR and overrides: { package: com.example.showcase, basedOn: 0.1.0 } → 200.
  2. Publish with ?member=fr-FR → 200. The fr-FR "override" is served.

(?member= on the PUT itself is 400; the member is the body's locale.)

Located (origin/main).

  • packages/metadata-protocol/src/protocol-packaged-base-refusal.ts:459: the gate asks shippedArtifactsOf(request.type, request.name), which answers for the name across all members, so "the base ships this item" holds for any locale.
  • :339-340: declared_package_ships_no_such_item is the refusal that should fire for an unshipped member.
  • :202 OverrideWriteSubject carries type, name and package, and no member, so the gate cannot ask per member.
  • The read side is already per member (findServedOverlayRow adds the member to its where before isServedOverrideRowOf; the layered read passes the member), so only the write gate is coarser.

Wanted. The override write gate judges a bundle type's subject by (type, name, member): a declared override of a member the base does not ship is refused with declared_package_ships_no_such_item. A new member in the pair without overrides is unaffected.

Reader. The domain:server seat that owns metadata-protocol, through triage.

Dedup. declared_package_ships_no_such_item → 1 hit (#28569, the finder); override unshipped locale → 7 hits, none about this gate (#28413 closed: os migrate; the rest unrelated). Studio's editor (#28569) never writes such a member, so it carries no workaround.

Activity

  1. added
    bugSomething isn't working
    domain:serverDomain lane — seat card indexed by label:pm:seat
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    priority:p1High: required for production / M2
    on Nov 1, 2026
  2. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 发布并装进一个环境 | none | P④

    Triage: p1 · bug · domain:server · area:studio · pm:queue · 2026-11-01T16:59Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

    Claim: PM loop round 4 · card thread · 2026-11-01T17:36Z
    Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01FEkQ5oDNN263d7ffSwgjsY
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-4dpgpx (the card thread's harness-pinned branch; 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#2
    Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」. With 2026-11-01T12:11Z 「定级你可以自己决定」; graded p1 by triage (2026-11-01T16:59Z), child of epic #28236.
    File surface: packages/metadata-protocol/src/protocol-packaged-base-refusal.ts (the override write verdict), packages/cli/src/utils/override-declarations.ts, packages/cloud-connection/src/marketplace-install-local-plugin.ts (its shippers read), their tests, one dogfood test; changeset
    Container & model: M, mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: high
    Clause-②: yes (narrowing) or no (narrowing) expected — a declared override of an unshipped member is now refused; the dev measures and declares
    Metadata-protocol: narrowed (expected; the dev measures and declares)
    Responsibility: the dev makes the one override write gate member-aware for bundle types at every door that asks it, with red-first, controls and ablation; the seat runs the CONTRACT_REVIEW_TIER review and lands
    Thread-read: none
    Premise: reproduced on the showcase by the #28569 dev; located from code
    Serial constraints cleared: the dev re-reads open PRs on the three files before pr_create (#28564 / PR #28728 near the install door)
    Resume-from: none — fresh from origin/main

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


    Generated by Claude Code

  4. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_01FEkQ5oDNN263d7ffSwgjsY

    os-dev-report
    {
    "issue": 28746,
    "status": "done",
    "branch": "claude/project-thread-4dpgpx",
    "pr": "#28777",
    "session": "session_01FEkQ5oDNN263d7ffSwgjsY",
    "premise_still_valid": true,
    "summary": "The override write gate now counts shippers at the bundle member the body addresses. metadata-protocol gains overrideWriteMemberOf (ITEM_KEY_DISCRIMINATORS via itemDiscriminator; email_template is the only entry at 7a48bf7) and the exported overrideWriteIdentityOf; overrideWriteItemsOf spells OverrideWriteItem.identity through it (so os validate and install-local's incoming items), install-local's listedIdentity returns it, and overrideWriteVerdict (save door, per-item publish, package publish) looks the registry up at the member via a new optional member argument on the private shippedArtifactsOf and drops other-member artifacts. The declared_package_ships_no_such_item sentence names the locale for a bundle type; no new code or gate. PREMISE HALF FALSIFIED: the order's 'unaffected' addition (an undeclared fr-FR in the pair) was refused undeclared_same_name on main at the save door, os validate and install-local; under the ruled identity it is admitted now, so the PR declares Clause-2 yes (narrowing). A dependent's template of the base's name at an unshipped locale is likewise admitted (pinned in the CLI file); flagged for the contract review. Surface beyond the claim's file list: protocol-artifact-lookup.ts, protocol.ts (private method signature + docblock), index.ts (one export), and the lookupArtifactItem call-site pin.",
    "tests": "Red first at base 7a48bf7 (dists from base): dogfood email-template-override-unshipped-locale 3 failed/1 passed (fr-FR override PUT draft 200 and publish ?member=fr-FR 200; fr-FR addition 403 undeclared_same_name; en-US control green); CLI override-declarations 2 failed/7 passed; install-local override-gate 2 failed/6 passed. Green after the fix: 4/4, 10/10, 8/8. Ablation from committed c5b0757 (ablation-replace anchor 1->0, blob 5b2921c51bef->162d86a93c62, metadata-protocol + cloud-connection rebuilt, dist-preflight marker ABLATE_28746 in 2 built files): CLI 2 failed, install-local 2 failed, dogfood 3 failed (refused cases and the addition controls; the en-US control stayed green); restored blob = HEAD, git diff HEAD empty, rebuilt, preflight --absent exit 0 tree clean. At-rest probe (temporary, not committed): a pre-gate fr-FR override row is served with _packageId com.example.showcase and layers code null; its next declared save is refused with the locale sentence; an undeclared re-save publishes as the pair's own fr-FR. Final head e30f9fc (merged origin/main 5493b88): metadata-protocol 294 files/22022 tests passed; cloud-connection 66/805; cli unit 365/5178; typecheck metadata-protocol, cloud-connection, cli, dogfood exit 0; objectql consumer files 3/42 (pre-merge c5b0757).",
    "gates": "dispatch-gates --commands at e30f9fc: 69 families, all exit 0 locally; three first answered PREREQUISITE NOT MET (exit 3: teardown self-test pinned commit not in the shallow clone; check:type-check-debt queued behind the dogfood lock; check:dual-build-cjs-loads before a full build) and were re-run green (teardown after git fetch --depth=1 of 621a487; type-check-debt under the lock VERDICT command-exit 0; dual-build-cjs-loads 115 entry points / 70 packages). dispatch-gates --ran: 69 derived, 69 run, 0 NOT-MEASURED, 0 UNRUN. Per-family exits: pm-orders/server/28746-gates-ran.txt. CI check-runs on PR #28777: in_progress at report time.",
    "dogfood_battery": "e30f9fc8 · os-verify-lock VERDICT command-exit 0 (held 48m13s, shared with the package suites) · Test Files 375 passed / 3 skipped (378) · Tests 3038 passed / 28 skipped (3066) · no failures",
    "mcp_calls": "0",
    "api_writes": "2 — fleet-write relay pr_create (#28777, read-back identical, no assignees); this os-dev-report comment via post-stamped.mjs on #28746",
    "open_questions": [
    {
    "question": "A dependent package's undeclared email template that reuses its base's name at a locale the base does not ship is now admitted (on main it was refused undeclared_same_name). Keep it, or refuse a dependent adding a member to a base's bundle?",
    "options": [
    "A keep: identity is (type, name, member) as ruled, the dependent's fr-FR replaces nothing",
    "B refuse: a dependent may not add members to a base's bundle name; needs a rule beyond the member-aware identity"
    ],
    "recommendation": "A, because it is the mechanical reading of the card's ruling and of ITEM_KEY_DISCRIMINATORS, and B would be a second rule the card ruled out ('No second gate')"
    }
    ],
    "out_of_scope_findings": [
    "carrier: none · noted, not filed: a declared override row of an unshipped member stored BEFORE this gate is still served under the base's _packageId with the layered read answering code null (measured on the showcase); dormant once the gate holds, so recorded in Acceptance notes only",
    "carrier: seat · Skills-docs follow-up owed: skills/objectstack-platform/SKILL.md's ADR-0146 customization paragraph does not say an email_template override names a locale its base ships"
    ]
    }

  5. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

    Landing record · #28746 · PR #28777

    domain:server seat 2 · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-11-01T19:49Z

    Reading Probe Result
    Merged PR #28777 state merged, merge commit 5bb7834a
    On main git merge-base --is-ancestor 5bb7834a origin/main yes
    Queue refs git ls-remote origin refs/heads/gh-readonly-queue/* matching 28777 0
    Content git grep -c overrideWriteIdentityOf origin/main -- packages/metadata-protocol/src/protocol-packaged-base-refusal.ts 5

    Release: claim 6561473652 (session_01FEkQ5oDNN263d7ffSwgjsY, branch claude/project-thread-4dpgpx) · landed.

    • State: pm:dispatched removed; assignee cleared; the card closed on merge.
    • Resume-from: none.

    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

    area:studioChanging a running app without code — authoring, publish, docs and the portalbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatfindingpriority:p1High: required for production / M2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions