Skip to content

fix(app-shell): an expression field shows the draft's server verdict at its own path - #26999

Merged
objectstack-fleet[bot] merged 13 commits into
mainfrom
claude/project-thread-0i0ziw
Oct 28, 2026
Merged

objectstack-fleet[bot] merged 13 commits into
mainfrom
claude/project-thread-0i0ziw

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Warren · project thread

Fixes #26858

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:表达式字段(RLS 的 USING/CHECK、流程边条件)按自己的路径读取草稿 _diagnostics 的服务端裁决;该路径有错误时显示服务端消息,不再显示 "Valid CEL"。lint 的边条件 expression-invalid 发现补上 flows[N].edges[M].condition 路径。
  • 为什么改:fix(metadata-protocol): a draft's _diagnostics reports what its publish will refuse #26856 之后横幅已显示发布裁决,但字段仍打印自己的客户端结论,两者矛盾(裁定 p2)。
  • 风险与回滚:只读路径相等匹配,不复制任何发布规则,不改任何规则的裁决或消息;回滚即 revert 本 PR。
  • 先看:ui/packages/app-shell/src/views/metadata-admin/draftVerdict.ts、CelPredicateField.tsx、packages/lint/src/validate-expressions.ts。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/metadata-admin/serverVerdictAtPath-26858.test.tsx 与 pnpm --filter @objectstack/lint exec vitest run src/validate-expressions.edge-condition-path.test.ts。
  • 需要你做的:评审通过后合并;无需其他操作。

What changes

  • lint: ExprIssue gains an optional path. A top-level flow edge's condition finding carries flows[N].edges[M].condition beside its unchanged where; authoring-rules maps it as path: i.path ?? i.where. The served draft verdict therefore lands at edges.M.condition (via the existing itemRelativeIssuePath). Rule, verdict and message are unchanged; the runtime gate's differential fingerprint compares the same written flow on both sides.
  • app-shell: DraftVerdictContext plus useServerErrorsAt(path) in draftVerdict.ts, an equality-only lookup on the served path, never a prefix.
    • CelPredicateField takes verdictPath; when the served verdict has errors at it, the field shows the server message, sets aria-invalid, and withholds "Valid CEL". RLS clauses pass rowLevelSecurity.N.using / .check.
    • FlowEdgeInspector shows served errors at edges.N.condition under the condition.
    • Providers: ResourceEditPage (its served diagnostics), PermissionMatrixEditPage (its own draft read, refreshed after each save), and the Studio Automations page (its own flow draft read, refreshed after each save).

Why

Ruling p2 on #26858: after #26856 the expression field still printed its own client verdict where the draft's server verdict at that path said otherwise (an RLS predicate over a formula field, a flow edge condition). The field now shows the _diagnostics verdict at its own path whenever one exists. No publish rule is copied into the client.

Acceptance notes

Noted by the implementer, not filed (seat-applied body amendment):

  • Class a candidate (read inference only, reach not measured at a public door): Studio Automations (AutomationsPillar in StudioDesignSurface.tsx) shows no draft-level publish verdict in its banner or canvas Problems panel. fix(metadata-protocol): a draft's _diagnostics reports what its publish will refuse #26856 wired that for the metadata-admin ResourceEditPage only; this PR adds only the field-level provider.
  • The field-permission when CEL field in PermissionMatrixEditor gets no verdictPath: no served rule was measured at such a path.
  • The lint-flow-patterns flow-default-edge-with-condition finding has no key path, so its served verdict cannot land on an edge field.

Verification

  • New tests: lint 6 (edge path, null member, second flow, map-form no path, start-node no path, runtime gate path); UI 13 across serverVerdictAtPath-26858, PermissionMatrixEditor.serverVerdict-26858, ResourceEditPage.serverVerdict-26858, AutomationsPillar.serverVerdict-26858. They cover a refused RLS predicate (server message, no "Valid CEL"), a refused edge condition, the no-verdict control, the sibling-path no-leak case, and the lint finding's edges.N.condition.
  • Ablations (each restored, blob equal to HEAD): dropping the context read in CelPredicateField turns 4 field tests red; in FlowEdgeInspector, 3; deleting the lint path assignment, 4; mapping i.where only, 1. Controls stay green in each.
  • @objectstack/lint full suite 7515 passed plus typecheck; @object-ui/app-shell type-check; related app-shell suites 1129 passed; after merging main, the touched suites 199 passed.
  • dispatch-gates --commands: 63 families run, each exit 0; --ran reconciles 63 derived, 63 run, 0 NOT-MEASURED.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (spec untouched)
  • 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, except the dispatch-required Chinese maintainer brief above.
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JQwxbYiz7aUf8wSAnnRyMx

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Oct 28, 2026
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint, touching 3 documentable anchor(s).

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

  • content/docs/deployment/validating-metadata.mdx (via AUTHORING_RULES (symbol, a top-level const object))
What this run could not see
  • the SDK route bridge reached 56 of 208 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 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; 97 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 — 4 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 50b1b7d81e753c7e79f039c5d53c95e44ed99f6d → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 34c4643db8560186553c354510b9c2251276149f — the merge of head 5aa7e9722995abe0a6c970f86f1166b07fd86985 into base 50b1b7d81e753c7e79f039c5d53c95e44ed99f6d, 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 34c4643db8560186553c354510b9c2251276149f && git checkout 34c4643db8560186553c354510b9c2251276149f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 50b1b7d81e753c7e79f039c5d53c95e44ed99f6d 5aa7e9722995abe0a6c970f86f1166b07fd86985 && git checkout -B drift-repro 50b1b7d81e753c7e79f039c5d53c95e44ed99f6d && git merge --no-ff 5aa7e9722995abe0a6c970f86f1166b07fd86985

node scripts/docs-audit/affected-docs.mjs --json 50b1b7d81e753c7e79f039c5d53c95e44ed99f6d

⚠️ 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 50b1b7d81e753c7e79f039c5d53c95e44ed99f6d → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · seat domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · head 814230c7 · 2026-10-28T01:05Z

席位意见:表达式字段现在按自己的路径读服务端 _diagnostics 判决(只认等值路径),有错误时显示服务端原文并不再写 "Valid CEL";客户端没有复制任何发布规则。

  1. Shape: Fixes #26858; 15 files, +927/−45. Source: CelPredicateField.tsx +35, draftVerdict.ts +34 (DraftVerdictContext, serverErrorsAt, useServerErrorsAt), PermissionMatrixEditor.tsx +54/−21, StudioDesignSurface.tsx +50/−15 (the Automations provider), FlowEdgeInspector.tsx +9, ResourceEditPage.tsx and PermissionAdvancedFacets.tsx (two verdictPath props), and on the lint side validate-expressions.ts +22 and authoring-rules.ts (path: i.path ?? i.where). Four new test files (13 UI tests) plus one lint test file (6), and a patch changeset for @objectstack/lint (Clause-②: no).
  2. Premise measured: the seat read the source diff on the head. serverErrorsAt matches by equality only, so a sibling policy's or edges.10's error does not leak in. The lint finding gets flows[N].edges[M].condition only for a top-level edge of an array-form flows, indexed over the list as authored (a null member does not renumber), and where, message and verdict are unchanged. The seat traced the server side on main: itemRelativeIssuePath in metadata-protocol/src/metadata-diagnostics.ts strips the gate snapshot's flows[0] prefix, so the served path is edges.M.condition. That is the same index FlowEdgeInspector reads, because its edges is the authored draft list. Each provider takes the verdict off a read (load, and a sequence-guarded re-read after each draft save) and never computes one. Dev ablations: four, each turning its pinned tests red (4, 3, 4 and 1), all restored to HEAD.
  3. Gates / check-runs: PR check-runs on 814230c7: 56 success, 9 skipped, 0 failure; mergeable_state clean. Dev's local run: 63 derived, 63 run, 0 NOT-MEASURED (two prerequisite exit-3s fixed by unshallow and build, then re-run exit 0); @objectstack/lint 7515 tests and app-shell type-check exit 0.
  4. Accepted deviations: the surface widened by measurement beyond the card's sketch. RLS lives in PermissionMatrixEditPage, which has its own draft read, so it provides the verdict. The edge editor is FlowEdgeInspector, and the refusing rule is expression-invalid, not lint-flow-patterns. One origin/main merge resolved a PermissionMatrixEditor.tsx conflict keeping both sides. The seat checked a trial merge with open PR fix(app-shell): the delegated-admin scope facet shows the spec default for an unset key #26990 (PermissionAdvancedFacets.tsx): no conflict.
  5. Out of scope: the three noted findings are in the PR body's Acceptance notes (seat-applied amendment), none filed. Skills-docs: none — UI field + lint finding path.
  6. Landing: not Tier H (dispatch-gates --tier hits none of the declared globs); the seat readies and arms auto-merge.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 28, 2026 01:06
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 28, 2026 01:06
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 28, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 28, 2026
…-0i0ziw

# Conflicts:
#	ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · addendum (queue kick, re-merge)

ACCEPT stands · seat domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · head fc69ac6c · 2026-10-28T02:35Z

The merge queue removed this PR for MERGE_CONFLICT at 2026-10-28T01:56Z, against #26994 (a8ee8a22) in StudioDesignSurface.tsx. The dev merged origin/main as a merge commit, with no rebase or force.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

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

  • Test Core (8/8) — 失败步骤: Run this shard's tests

    @objectstack/rest:test:  FAIL   local  src/data-nested-object-door.test.ts > [#20802] the nested-relation form at the public door — sqlite > the cap: a condition matching more related records than the
      ↳ 失败原因: @objectstack/rest:test: Error: Test timed out in 5000ms.
    

↳ 失败原因 是判读的关键:超时(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 队列共有 16 个失败构建(不含本次)。

分诊清单:

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

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

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

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

  • Test Core (8/8) — 失败步骤: Run this shard's tests

    @objectstack/rest:test:  FAIL   local  src/data-nested-object-door.test.ts > [#20802] the nested-relation form at the public door — sqlite > the cap: a condition matching more related records than the
      ↳ 失败原因: @objectstack/rest:test: Error: Test timed out in 5000ms.
    

↳ 失败原因 是判读的关键:超时(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 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 19 个失败构建(不含本次)。

分诊清单:

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

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

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

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

  • Test Core (8/8) — 失败步骤: Run this shard's tests

    @objectstack/rest:test:  FAIL   local  src/data-nested-object-door.test.ts > [#20802] the nested-relation form at the public door — sqlite > the cap: a condition matching more related records than the
      ↳ 失败原因: @objectstack/rest:test: Error: Test timed out in 5000ms.
    

↳ 失败原因 是判读的关键:超时(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 已在队列失败 2 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 21 个失败构建(不含本次)。

分诊清单:

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

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

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

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

  • Test Core (8/8) — 失败步骤: Run this shard's tests

    @objectstack/rest:test:  FAIL   local  src/data-nested-object-door.test.ts > [#20802] the nested-relation form at the public door — sqlite > the cap: a condition matching more related records than the
      ↳ 失败原因: @objectstack/rest:test: Error: Test timed out in 5000ms.
    

↳ 失败原因 是判读的关键:超时(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 已在队列失败 3 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 23 个失败构建(不含本次)。

分诊清单:

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

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

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

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

  • Test Core (8/8) — 失败步骤: Run this shard's tests

    @objectstack/rest:test:  FAIL   local  src/data-nested-object-door.test.ts > [#20802] the nested-relation form at the public door — sqlite > the cap: a condition matching more related records than the
      ↳ 失败原因: @objectstack/rest:test: Error: Test timed out in 5000ms.
    

↳ 失败原因 是判读的关键:超时(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 已在队列失败 4 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 24 个失败构建(不含本次)。

分诊清单:

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

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

…ix for #27037)

The case seeds RELATION_FILTER_ID_CAP (1,000) rows and deletes 1,001: 2.3 s
alone, past the default 5 s on a loaded full-suite queue shard, which
ejected #26960, #27011 and #27020 (#27037). The slowdown itself is
#27004's (since #26930 a 1,001-row predicate delete is about 18x slower);
this only budgets for it.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QUs3Bshfmr8srPY21BVS4u
(cherry picked from commit 6674ba8)
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Ported fix for #27037, not this PR's change.

The merge queue dequeued this PR on CI_FAILURE: packages/rest/src/data-nested-object-door.test.ts, the "the cap" case, timed out at 5000 ms. This PR does not touch packages/rest; the slowdown is tracked in #27004. Commit ef55ac5 cherry-picks 6674ba8 from #27020 (test-only, +2/−1: that one case gets a 30_000 budget). It no-ops once main carries it.

Local: that test file passes (the cap case 1.8 s); dispatch-gates --ran 63 derived, 63 run, 0 NOT-MEASURED on head ef55ac5.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · addendum (queue flake, ported fix)

ACCEPT stands · seat domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · head ef55ac59 · 2026-10-28T05:24Z

The merge queue removed this PR for CI_FAILURE at 2026-10-28T04:14Z. Every queue red was packages/rest/src/data-nested-object-door.test.ts "the cap" timing out at 5000 ms: p0 anchor #27037, cause #27004 (server lane), not this PR. The dev merged origin/main (8fd39d48, a merge commit, no rebase or force) and cherry-picked the ported fix 6674ba8e09 from #27020 as ef55ac59 (test-only, +2/−1, that one case gets a 30_000 budget; it no-ops once main carries it).

  • Delta measured by the seat: all 15 of the PR's own files have identical hunks against the merge base before (fc69ac6c) and after (ef55ac59). The only new file is the ported packages/rest test line.
  • Gates / check-runs: PR check-runs on ef55ac59: 54 success, 7 skipped, 0 failure; mergeable_state clean. Dev: the rest test file passes (the cap case 1.8 s); dispatch-gates --ran 63/63.
  • The record of comment 6457887063 continues to govern; the seat re-arms auto-merge.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 28, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 28, 2026
…-0i0ziw

# Conflicts:
#	ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx
…the package door now reads through it

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQwxbYiz7aUf8wSAnnRyMx
…overlaid read

Both new package-door tests landed beside #26840, whose load now reads the
package's objects through MetadataClient.withPreviewDrafts; their fake
clients lacked it, which turned main red on Test UI.

Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 039a875)
…e-read after the save

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQwxbYiz7aUf8wSAnnRyMx
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Not this PR's · seat domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · 2026-10-28T06:44Z

Test UI (10/16) failed on 5aa7e972 in ui/packages/app-shell/src/views/__tests__/AppReportIndex.navGates-26892.test.tsx:359, case "(f) CONTROL: offers it, once, to a member granted that permission": expected ['Alpha','Beta','Gamma'] to deeply equal ['Alpha','Beta','Delta','Gamma']. This PR touches nothing the report index or the command palette reads. The test waits only until Gamma appears, then asserts the full list at once, so on a loaded shard Delta can still be missing. It is a race in the test itself, already a queue-flake anchor (#27116, also hit in #27083's queue build), and main is green. No fix exists yet; the race is reported to that card's owner (seat domain:ui#5, #26892). The seat re-runs the failed job once.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · addendum (queue conflict, re-merge)

ACCEPT stands · seat domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · head 5aa7e972 · 2026-10-28T07:01Z

The merge queue removed this PR for MERGE_CONFLICT at 2026-10-28T06:09Z, against #27064 (76e90df6) and #27080 (dc87dfb5) in PermissionMatrixEditor.tsx. The dev merged origin/main (59d89d61, a merge commit, no rebase or force) and added three commits.


Generated by Claude Code

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.

Studio: an expression field that says "Valid CEL" should read the draft's server verdict at its own path (RLS predicate, flow edge condition)

2 participants