Skip to content

fix(metadata-protocol,app-shell): serve a pair's mapping override, let a pair remove any override it holds, name a working remedy for an excluded type (#30062) - #30226

Merged
objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/project-thread-yenpdr
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/project-thread-yenpdr

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

维护者速读

  • 改了什么: 客户在定制包(BASE.custom)里声明覆盖的 mapping 现在真的生效(导入用的是改过的映射);定制包里任何可覆盖类型的覆盖都能删掉,删掉后基包原件回来(doc、mapping 以前报 403);给不可覆盖类型加 overrides 时,拒绝信息引用该类型自己的依据并给出一条走得通的出路;「全部元数据」里的类型瓷砖在只读包上显示「可定制」当且仅当 Studio 真的给了「定制」入口(flow、app 以前误显示「只读」)。
  • 为什么改: ADR-0146 D2 原文:定制包里的项替换基包的项,删掉它就回到基包的项;ADR-0146 的 revert(「恢复包原件」)就是删掉 .custom 里的覆盖项。旧行为对 doc/mapping 拒绝了这条路,mapping 覆盖保存发布后不起作用,拒绝文案一律叫人「新建同类」而有些类型根本不能新建。
  • 风险与回滚: 删除只对定制包自己的行放宽(点名 ?package=BASE.custom);点名基包的删除仍是原答案,覆盖照旧生效(消融 A3 为证)。错误码与状态不变,无 spec 改动。回滚即撤 PR。
  • 先看: packages/metadata-protocol/src/protocol-packaged-base-refusal.ts(isPairRowRemoval 的放宽与 excludedTypeRemedy),packages/metadata-protocol/src/protocol-package-bindings.ts(SEALED_ROW_SERVED_OVERRIDE_TYPES 加 mapping),ui/packages/app-shell/src/views/metadata-admin/PageShell.tsx(studioOffersCustomize)。
  • 两分钟验证: pnpm --filter @objectstack/objectql exec vitest run src/protocol-custom-override-bare-slot-envelope.test.ts -t 30062:每个可覆盖类型在两种内核上,删除定制包的覆盖后所有读都回到基包原件;点名基包的删除不动任何东西。
  • 需要你做的: 无,席位落地。第 3 项(翻译与覆盖自身 label 的优先级)不在本 PR,见 Acceptance notes。

Refs #30062 (items 1, 2, 4, 5)

Clause-②: no — ADR-0146 D2 ("an item in the customization package replaces the base's item, and removing it brings the base's back") and ADR-0146's revert ("Restore package original" = delete the overriding item from .custom, so the base's document is served again) already admit both the served mapping override and the pair's removal of its own doc or mapping override; the 403 and the unserved row refused that text. A removal naming the base keeps today's answer, and no accepted write is refused that was accepted before.
Metadata-protocol: semantic
Skills-docs: follow-up #30229 — skills/objectstack-platform/SKILL.md's ADR-0146 paragraph says the read honours a pair's declared override "today only for a view, dashboard, report, email_template, translation or flow"; that list predates page, app, dataset, doc and book, and now mapping, and it says nothing of the removal. skills/** is outside this card's order, so the seat carries the edit.

What changes

Item 1 — a mapping override is served. packages/metadata-protocol/src/protocol-package-bindings.ts: mapping joins SEALED_ROW_SERVED_OVERRIDE_TYPES (page, app, dataset, doc, book) and BORROWED_ENVELOPE_DECLINED_TYPES. A mapping is sealed (allowEnvironmentOverlay: false) and open for override (ADR-0146 D2, no D9 condition), and its pair row was declined with every other stored row of a name a managed package ships, so a saved and published mapping override answered the base's body on every read. Now the by-name read (no ?package and ?package=BASE), both lists, the layered read and the pair's per-package layer serve it. The import door (@objectstack/rest import-mapping.ts:63) and the connector pull (service-automation connector-pull.ts:244) resolve a named mapping through that package-less getMetaItem, so they get the pair's mapping. The served arm was chosen, not "the save refuses loudly", so Clause-②: no does not narrow. Comment-only updates in protocol-lock-state.ts, protocol-registry-hydration.ts and protocol.ts name the sixth type.

Item 2 — the pair's removal of its own override works for every overridable type. Measured: the doc and mapping 403 came from refusePackagedBaseRemoval (protocol-write-gates.ts: a sealed managed item on a kernel that does not merge the overlay at read), which deleteMetaItemUnaudited skips only when isPairRowRemoval admits the row, and isPairRowRemoval admitted only CONDITIONS_DELIVERED = {flow}. protocol-packaged-base-refusal.ts: isPairRowRemoval now admits the pair's row for every type isOverridableMetadataType opens (isOpenOverrideType, so action still waits for D9); the pair check is unchanged (the package the removal names must be the base's customization package). The repository's delete gate then takes the runtime-only intent the declared save wrote, which every overridable type's allowRuntimeCreate admits. No spec flag moved. A removal naming the base (?package=BASE) is untouched: on the host-config kernel it answers { success: true, reset: false } with "No row … remains" and removes nothing; on the environment kernel a doc or mapping still answers 403 NOT_OVERRIDABLE.

Item 4 — the All metadata tile agrees with the Customize door. ui/packages/app-shell/src/views/metadata-admin/PageShell.tsx: new studioOffersCustomize(type) = overridable and offered by a Studio door (isStudioCustomizeType: view, dashboard, report, app; AREA_FORM_EDITOR_TYPES: email_template, translation; and the flow header). On a read-only package typeStateWord reads it instead of allowEnvironmentOverlay, so flow and app read 可定制, and doc, mapping, page, dataset and book read 只读 until a door exists. StudioCustomizeControls.tsx is not touched (seat ruling): a door added to its list moves the tile with it.

Item 5 — an excluded type's refusal names a remedy that works for it. judgeOverrideWrite's declared_type_not_overridable sentence cited ADR-0146 D6 and told every type to "create your own TYPE under a new name". It now cites the type's own basis from OVERRIDE_TYPE_CLASSIFICATION (EXCLUSION_BASIS_CITATION: D6 for the security catalog and code-only types, D1 for a datasource, D9 for a behavioural switch, D10 for a field, ADR-0126 §3 for one-shot types, D2 otherwise) and excludedTypeRemedy prescribes, in order: Setup › Datasources for a datasource; the object extension for a field (OBJECT_EXTENSION_ITEM_DOOR); "create your own TYPE under a new name" only where the registry admits a runtime create; the type's ADR-0126 Regime C prescription where it has a row (packagedBaseRegimePrescription: a sharing rule names POST /api/v1/sharing/rules and its switch); Setup › Queues for a queue; otherwise "an X is defined only in package source, so it changes only with a new version of 'BASE'". articleFor fixes "a api". Code (NOT_OVERRIDABLE), status (403) and rule are unchanged, and the permission sentence is byte-identical (pinned by runtime meta-object-extension-item-door-fit.test.ts:161).

Docs (head 5663983b, added by the card thread after the CI Docs Drift Check, comment 6648033732). content/docs/concepts/metadata-lifecycle.mdx: a declared override is served for every type of OVERRIDABLE_METADATA_TYPES but action (which waits for ADR-0146 D9's conditions); the import door and the connector pull run the pair's mapping; a new bullet, removing the override (DELETE …?package=hotcrm.custom, "Restore package original") brings the base's back. content/docs/api/metadata-api.mdx: a capability is not copied under a new name (its registry entry has allowRuntimeCreate: false); it changes only with a new version of the package that declares it, and an overrides on a type that takes none is refused 403 NOT_OVERRIDABLE with that type's own route. This PR made the old sentences false, so they are fixed here.

Tests

  • packages/objectql/src/protocol-custom-override-bare-slot-envelope.test.ts: mapping joins the sealed served types (by-name, lists, layered, per-package, after a restart, with and without a metadata service). New describe [#30062] a pair's removal of its own override brings the base's item back, over view, dashboard, report, email_template, page, app, dataset, doc, book, mapping, flow and translation, on both kernels: (1) the removal naming the pair succeeds, the pair row is gone, and every read serves the base's item again; (2) a removal naming the base keeps main's exact answer per kernel and type, removes nothing, and the override stays served.
  • packages/objectql/src/protocol-flow-custom-override.test.ts: the old flow-only admission pins (a mapping pair-row delete 403, a view delete override-artifact) are rewritten to the new behaviour (removed; runtime-only). They pinned the service-automation: a declared override in <base>.custom is the packaged flow — armed over the code package, rebound in one reload, one ledger switch #28412 flow-only scope (contract review 6559358939), which item 2 supersedes.
  • packages/metadata-protocol/src/protocol.packaged-base-refusal.test.ts: each of the 17 excluded non-D10 types cites its own basis, never reads "a" before a vowel, names a create only where the registry admits one, and the named remedies (sharing rule route and switch, Setup › Queues, Setup › Datasources, object extension, new version of the base) appear where they belong.
  • packages/metadata-protocol/src/protocol-package-bindings.sealed-override.test.ts: the served set includes mapping.
  • ui/packages/app-shell/src/views/metadata-admin/DirectoryPage.stateVocabulary-29705.test.tsx: for every overridable type plus permission, object and api, under both overlay flags, the tile reads 可定制 exactly when a door offers Customize; flow and app read 可定制, doc and mapping 只读, a writable flow 可新增; the flow list header renders 可定制.

Ablations

scripts/ablation-replace.mjs (wrap mode); for objectql legs the mutated metadata-protocol was rebuilt and scripts/ablation-dist-preflight.mjs proved the marker in dist/ before the run, then absent after restore. Each leg restored with blob == HEAD and git diff HEAD empty.

# Mutation Result
A1 isPairRowRemoval back to flow-only 7 / 78 red: doc and mapping pair removals on both kernels, the mapping pair-row delete, the view intent
A2 mapping out of SEALED_ROW_SERVED_OVERRIDE_TYPES 6 / 71 red: mapping served with and without a metadata service, mapping removal and base-named control on both kernels
A3 the pair check widened to admit a base-named removal 2 / 71 red: doc and mapping, environment kernel, base-named removal
A4 typeStateWord back to reading allowEnvironmentOverlay 15 / 114 red: every per-type agreement row, flow/app, the flow header
A5 the refusal back to the old "a TYPE … (ADR-0146 D6) … create your own" sentence 16 / 87 red: 14 per-type citation rows, the named-remedy row, the code-only row

Acceptance notes

Verification

Head 69812d8212 (base b7075d99, origin/main 474c030b merged in once, no conflict, no rebase). The affected closure was rebuilt after the merge under the shared lock.

  • pnpm --filter @objectstack/metadata-protocol test: 312 files passed (4 skipped), 22 375 tests passed (26 skipped). typecheck: exit 0.
  • pnpm --filter @objectstack/objectql test: 548 / 548 files, 10 910 / 10 910 tests. typecheck: exit 0.
  • @objectstack/runtime src/domains/meta-object-extension-item-door-fit.test.ts: 6 / 6 (it pins the permission refusal sentence byte for byte).
  • ui: the six test files that import PageShell or DirectoryPage (DirectoryPage.stateVocabulary-29705, .diagnosticsLinkTone-12768, .environmentOverlay-12141, PageShell.narrowHeader-12306, metadataAreaHost.customizations-28568, metadataAreaHost.scope-28561): 149 / 149. turbo run type-check --filter=@object-ui/app-shell: 68 / 68 tasks. eslint on the two ui files: 0 errors (one more react-refresh/only-export-components warning, for the new studioOffersCustomize export beside the already-exported typeStateWord).
  • pnpm lint: exit 0. node scripts/check-nul-bytes.mjs: exit 0.
Gates: dispatch-gates --commands, each exit code captured before any pipe

95 derived families at head 5663983b (the docs commit added 29 docs families and 7 doc-reading families): 94 exit 0, 1 NOT MEASURED. dispatch-gates --ran exit 0. (At 69812d82: 65 derived, 64 exit 0, 1 NOT MEASURED.)

  • NOT MEASURED: pnpm check:dual-build-cjs-loads exit 3, "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/" (32 unbuilt packages outside this diff's closure); left to CI.
  • node scripts/check-plugin-teardown-shape.mjs --self-test first exited 3 on the shallow clone (pinned fixture commit unreachable); after git fetch origin 621a4876… it exits 0 (48 cases).
  • pnpm check:type-check-debt first exited 3 (lock queue timeout, nothing measured); re-run under the lock: exit 0, "0 raw tsc error(s) total, none above its recorded number".
  • Also run, outside the derivation: check:authz-resolver, check:error-code-casing (both keep a roster under packages/), check:overlay-whitelist-table, check:changeset-fixed: all exit 0.

Change set: 15 paths, +461 / −66 against origin/main (13 code and test paths +448 / −61, plus the two docs pages).

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: n/a.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the maintainer brief is Chinese by the seat's convention).

🤖 Generated with Claude Code


Generated by Claude Code

…ry open type's pair-row removal

Claude-Session: https://claude.ai/code/session_01Utp6CaHjE2N5aEp4ogQnWY
Co-authored-by: Claude <noreply@anthropic.com>
…base-named removal's unchanged answer

Claude-Session: https://claude.ai/code/session_01Utp6CaHjE2N5aEp4ogQnWY
Co-authored-by: Claude <noreply@anthropic.com>
… own record and names a remedy that works for it

Claude-Session: https://claude.ai/code/session_01Utp6CaHjE2N5aEp4ogQnWY
Co-authored-by: Claude <noreply@anthropic.com>
…for every overridable type, excluded-type remedies, tile agrees with the door

Claude-Session: https://claude.ai/code/session_01Utp6CaHjE2N5aEp4ogQnWY
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the size/l label Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/metadata-protocol, touching 25 documentable anchor(s).

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/concepts/metadata-lifecycle.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v16.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class))
  • content/docs/releases/v17/17-0.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 103 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 10 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 31260ca8e446fa4fbe94ff8fd2a9030ddfb53a9a → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 586b70389c2e1c7fe5d2ef9366825d65128f3a9c — the merge of head c503347163e4d6a786e5723d3a20e1276bd46429 into base 31260ca8e446fa4fbe94ff8fd2a9030ddfb53a9a, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 586b70389c2e1c7fe5d2ef9366825d65128f3a9c && git checkout 586b70389c2e1c7fe5d2ef9366825d65128f3a9c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 31260ca8e446fa4fbe94ff8fd2a9030ddfb53a9a c503347163e4d6a786e5723d3a20e1276bd46429 && git checkout -B drift-repro 31260ca8e446fa4fbe94ff8fd2a9030ddfb53a9a && git merge --no-ff c503347163e4d6a786e5723d3a20e1276bd46429

node scripts/docs-audit/affected-docs.mjs --json 31260ca8e446fa4fbe94ff8fd2a9030ddfb53a9a

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 31260ca8e446fa4fbe94ff8fd2a9030ddfb53a9a → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…dable type but action, and removing it brings the base's back; a capability changes only with a new version

Claude-Session: https://claude.ai/code/session_01Utp6CaHjE2N5aEp4ogQnWY
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

席位核对 · ACCEPT · head 5663983b · 2026-11-05T03:11Z

Head-sha: 5663983

Item Reading
Shape Draft, base main, author objectstack-fleet[bot] (relay pr_create). Body: attribution block, then Refs #30062 (items 1, 2, 4, 5); no closing keyword anywhere in the body, so the merge leaves #30062 open by design.
Premise Re-read by the seat at origin/main 0fd363d1 before dispatch: mapping absent from SEALED_ROW_SERVED_OVERRIDE_TYPES; isPairRowRemoval admitted CONDITIONS_DELIVERED = {flow} only. The diff widens it to isOpenOverrideType (D9-undelivered action still refused).
Gates Check-runs on 5663983b: 60 success, 9 skipped, 0 failure, 0 pending; mergeable_state clean. Dev's local reconciliation: 95 derived, 94 run, 1 NOT MEASURED (check:dual-build-cjs-loads, no local dist; covered by CI).
Declarations Clause-②: no, citing ADR-0146 D2 and the chartered revert; the served arm of item 1 was chosen, so nothing narrows. Metadata-protocol: semantic; changesets are minor on @objectstack/metadata-protocol and the app-shell package. Skills-docs: follow-up #30229.
Docs (read sentence by sentence) concepts/metadata-lifecycle.mdx: "for every type of OVERRIDABLE_METADATA_TYPES … but action" matches the diff's isOpenOverrideType; the removal bullet matches isPairRowRemoval. api/metadata-api.mdx: the capability sentence was checked against metadata-plugin.zod.ts:1269 (allowRuntimeCreate: false).
Accepted deviations Item 3 not in this PR: its spec half is #30193 (spec lane, in flight); the server half (getPackaged*Base accessors for page, dataset, app, book and the PACKAGED_BASE_ACCESSORS rows) stays on #30062. Item 4 is UI-only in PageShell.tsx; #30063 / #30064 were told of the tile expectation (6646961716, 6646971486).
Out of scope filed #30193 (item 3 spec half) · filed #30229 (skills paragraph) · ablations A1–A5 red then restored, per the report.
Landing Not governed (0 of 15 paths), 527 changed lines (< 3000). Seat lands: pr_ready + automerge_enable. At MERGED, #30062 is released with item 3's server half as pm:blocked, Blocked-by: #30193.

@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 41960860949 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test UI (6/16) — 失败步骤: Run the ui tests (shard 6/16)

    × a read-only package: overlay types can be customized, never 可写; the create-only and closed types are read-only 52ms
    × a read-only package: the view list header reads 可定制, the page list header 只读 6ms
    × typeStateWord: the mapping in every context 2ms
    FAIL   dom  packages/app-shell/src/views/metadata-admin/DirectoryPage.stateVocabulary-29705.test.tsx > All metadata tiles: one state word, in the package context (objectstack#29705) > a read-only pack
      ↳ 失败原因: AssertionError: expected '可定制' to be '只读' // Object.is equality
    FAIL   dom  packages/app-shell/src/views/metadata-admin/DirectoryPage.stateVocabulary-29705.test.tsx > the list header draws the tile word (objectstack#29705) > a read-only package: the view list head
      ↳ 失败原因: (这条 FAIL 之后 12 行内没有可识别的原因行 —— 点进 job 看)
    FAIL   dom  packages/app-shell/src/views/metadata-admin/DirectoryPage.stateVocabulary-29705.test.tsx > the state vocabulary census (objectstack#29705) > typeStateWord: the mapping in every context
      ↳ 失败原因: AssertionError: expected [ Array(4) ] to deeply equal [ 'customizable', 'readOnly', …(2) ]
    

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 2 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

席位核对 · ACCEPT (re-affirmed) · head c5033471 · 2026-11-05T04:12Z

Head-sha: c503347

Supersedes the head pin of 6648642607; every other row there stands.

Item Reading
Queue round Dequeued (CI_FAILURE, aggregate #30253): three #29705 pins in DirectoryPage.stateVocabulary-29705.test.tsx hard-coded page 只读. Cause: #30063 (page door) and #30064 (dataset door, AREA_CUSTOMIZE_EDITOR_TYPES) landed on main; with item 4 the page tile reads 可定制 by rule. This is the "whichever lands second flips those" case the body predicted.
Fix Merged origin/main bb86ff96c; studioOffersCustomize also reads AREA_CUSTOMIZE_EDITOR_TYPES (otherwise the dataset tile would still read 只读); the three pins now expect page 可定制, the read-only control row is api, and the agreement DOORS include the new constant. These are expectation changes for a behaviour item 4 deliberately changes, not weakened pins.
Gates Dev: 29705 file 114/114, the 6 PageShell/DirectoryPage files 150/150, app-shell type-check 0, ui eslint 0, the 5 ui-reading gates green. CI on c5033471 running; the merge-group full suite decides.
Body Acceptance notes updated by the seat: the queue-round line and the #30106 serial note (landed as #30207).
Landing automerge_enable re-sent.

@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 5, 2026 04:12
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 15f808a Nov 5, 2026
69 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-yenpdr branch November 5, 2026 05:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant