Repository navigation
Commit 9ae6384
fix(metadata-protocol): the draft promotion answers NO_DRAFT before a permission-set name collision (#27825)
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
<!-- claude-projects-attribution: {"github_login":"os-sales"} -->
_Requested by **Sales** · [project
thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuPMZ8BHWtPf6nBTp51qSM64)_
Fixes #27707
Clause-②: no
Metadata-protocol: none
Skills-docs: none — a refusal-order change with no new authorable
capability; nothing in `skills/**` teaches the promotion's refusal
order.
## 维护者速读
- **改了什么:** 草稿发布(promotion)时,权限集"重名"检查改为只在确实读到草稿时才执行,与其他三个同类检查一致;另在
objectql 注册表的私有成员上加一行注释,注明 metadata-protocol 按名字调用它。
- **为什么改:** 以前没有草稿却去发布一个别人已占用名字的权限集,会先报 `NAMESPACE_CONFLICT`(别人的事),而不是
`NO_DRAFT`(调用方自己没草稿);现在先告诉调用方它自己的状态。
- **风险与回滚:** 极低——只是"拒绝换拒绝"的顺序调整,无草稿时之前和现在都拒绝,有草稿时检查与顺序不变;回滚即 revert 本
PR。
- **先看:** `packages/metadata-protocol/src/protocol.ts`(promotion 处 `if
(draftForGate)`
一处);`packages/metadata-protocol/src/protocol.permission-set-name-cross-package.test.ts`(新增一条
pin);`packages/objectql/src/registry.ts`(仅注释)。
- **两分钟验证:** `pnpm --filter @objectstack/metadata-protocol exec vitest
run --maxWorkers=2
src/protocol.permission-set-name-cross-package.test.ts`,预期 18/18 通过。
- **需要你做的:** 无,席位落地。
## What changes
- `packages/metadata-protocol/src/protocol.ts`, the draft promotion
(`publishMetaItem`'s promotion step, which `publishPackageDrafts` also
goes through): the `permissionSetNameCollisionRefusal` call now sits
under `if (draftForGate)`, like its siblings `savedItemNameRefusal`,
`permissionSetOwnerBindingRefusal`, `defaultRecordPageSaveRefusal` and
`externalBindingSaveRefusal`. Before, it ran in a bare block.
- **Refusal before/after.** If you promote with no draft while another
owner holds the name, the answer used to be `NAMESPACE_CONFLICT` / 422
and is now `NO_DRAFT` / 404, the repository's answer from
`repo.promoteDraft`.
- **The accept set does not change.** With `draftForGate` null,
`repo.promoteDraft` gets `expectedDraftHash: null`. It then answers
`NO_DRAFT` when no draft row exists, or `DraftConflictError` when a
draft was saved after the gate read. A promotion with no judged draft
never succeeds, before or after this change. With a draft, the same
refusals run in the same order.
- `packages/objectql/src/registry.ts`: the doc comment of the private
`packageHeldSecurityCatalogNames` now says that
`protocol-save-door-refusals.ts` (`registryPackageHoldersOf`) reads it
by name. Renaming the member means updating that caller and
`protocol-permission-set-name-registry-reading.test.ts`. This is a
comment only.
- Changeset: `patch` for `@objectstack/metadata-protocol`. objectql gets
none because its change is a comment, so nothing published changes.
## Tests
- New pin, through the real promotion door (`publishMetaItem`, using the
harness the PR #27535 tests use): B holds the active name and A has no
draft. Promoting as A answers `NO_DRAFT` / 404 and leaves only B's
active row stored.
- Control, a draft plus a collision: the existing pins still answer
`NAMESPACE_CONFLICT` / 422, for both `publishMetaItem` and
`publishPackageDrafts`.
- Control, a clean promote: the existing CONTROL pin still succeeds, for
a first publish and a re-publish.
## Acceptance notes
- None out of scope.
## Verification
<details><summary>Red before the fix (test committed first,
83e4daa)</summary>
```
× with no draft at the address, the caller is told about its own state first: the repository's NO_DRAFT (404), not the collision another owner's name would raise
Expected: "NO_DRAFT"
Received: "NAMESPACE_CONFLICT"
Tests 1 failed | 17 passed (18)
```
</details>
<details><summary>Ablation (scripts/ablation-replace.mjs) on the fix
commit</summary>
Anchor `if (draftForGate) {` + newline + `const nameCollision` was
replaced by `if (true /* ablation: guard removed */) {`, landing x1 to
x0, blob 5fb0787 to 2d3ef72c.
```
× with no draft at the address ... Expected: "NO_DRAFT" Received: "NAMESPACE_CONFLICT"
Tests 1 failed | 17 passed (18)
ok restored: blob == HEAD (5fb0787) and `git diff HEAD` is empty
```
The first ablation attempt used a replacement that was a substring of
the anchor. The tool refused it before running anything and restored the
file (blob == HEAD), so that attempt was a no-op and is not counted as a
reading.
</details>
<details><summary>Local gates at 98bfa9d (after merging origin/main
8f25cee)</summary>
- `@objectstack/metadata-protocol` test: 280 files passed, 4 skipped;
21803 tests passed, 26 skipped. Exit 0.
- `@objectstack/objectql` test: 490/490 files, 9244/9244 tests. Exit 0.
- typecheck for both packages: exit 0.
- eslint `--no-inline-config` on `packages/metadata-protocol` and
`packages/objectql`: exit 0 each.
- `dispatch-gates --commands`: 63 families derived, and every one exits
0. `check:dual-build-cjs-loads` and `check:type-check-debt` exited 3
(PREREQUISITE NOT MET) the first time and exited 0 when re-run after the
build. `--ran` reconciliation: 63 run, 0 NOT-MEASURED (a derived zero).
- Overlap check: `git merge-tree` against #27780 (the other open PR on
`protocol.ts`) is clean. No other open PR touches these files.
- Dogfood: not owed, because the title has no `!` and there is no
`(narrowing)` arm.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L6bhgUCizoYWQVJWjhdR6F)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L6bhgUCizoYWQVJWjhdR6F
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent ddab8b9 commit 9ae6384
4 files changed
Lines changed: 33 additions & 1 deletion
File tree
- .changeset
- packages
- metadata-protocol/src
- objectql/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
524 | 524 | | |
525 | 525 | | |
526 | 526 | | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
527 | 541 | | |
528 | 542 | | |
529 | 543 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23062 | 23062 | | |
23063 | 23063 | | |
23064 | 23064 | | |
23065 | | - | |
| 23065 | + | |
| 23066 | + | |
| 23067 | + | |
| 23068 | + | |
23066 | 23069 | | |
23067 | 23070 | | |
23068 | 23071 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3499 | 3499 | | |
3500 | 3500 | | |
3501 | 3501 | | |
| 3502 | + | |
| 3503 | + | |
| 3504 | + | |
| 3505 | + | |
| 3506 | + | |
3502 | 3507 | | |
3503 | 3508 | | |
3504 | 3509 | | |
| |||
0 commit comments