Skip to content

docs(spec): ActionSchema.params states the same-name param rule - #28752

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-qaqibu
Nov 1, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-qaqibu

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Fixes #28732

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:ActionSchema.params 的描述多了一句同名参数规则:参数名(name,或字段型参数的 field)只能为同一个输入的按门控分身重复(visible / requiresFeature 不同);只有每一处声明都必填时才算必填;值形状按第一处声明(type、multiple、options)。生成的参考文档随之更新。
  • 为什么改:fix(platform-objects): Setup Create User marks Email required while phone sign-in is off #28707 让 validateActionParams 按这条规则合并同名声明,但规则只写在校验函数的注释里。写动作元数据的作者和 AI 读的是 schema 描述,看不到它。
  • 风险与回滚:极低。只改描述文字,没有新键、没有新门禁、没有改变任何接受或拒收的值。回滚即撤销本 PR。
  • 先看:packages/spec/src/ui/action.zod.ts(params 的 .describe() 那一行);content/docs/references/ui/action.mdx(生成的参考表)。
  • 两分钟验证:grep -n "may repeat only for per-gate variants" content/docs/references/ui/action.mdx,预期两行(Action 与 InlineAction 两张表的 params 行)。
  • 需要你做的:无,席位落地。

What changes

  • packages/spec/src/ui/action.zod.ts: the params member's .describe() keeps its first sentence and gains one more, stating the rule validateActionParams (packages/spec/src/ui/action-params.zod.ts) has applied since fix(platform-objects): Setup Create User marks Email required while phone sign-in is off #28707: a param name (its name, or the field a field-backed param reads) may repeat only for per-gate variants of ONE input, declarations that differ in visible / requiresFeature; the params check treats the name as required only when EVERY declaration requires it, and judges the value by the FIRST declaration's shape (type, multiple, options), so that shape should stay identical across them.
  • Generated references, regenerated by pnpm --filter @objectstack/spec gen:schema && gen:docs, no hand edits: content/docs/references/ui/action.mdx (two rows: Action and InlineAction), ui/component.mdx, data/object.mdx, kernel/metadata-plugin.mdx. The JSON Schema under packages/spec/json-schema/ is build output, not tracked, and carries the same text after a build.
  • Changeset .changeset/28732-action-params-same-name-rule.md: @objectstack/spec patch. The description ships: it is in dist (the zod .describe()), in json-schema/ and in src/**/*.zod.ts, all in spec's files[].
  • ⛔ No lint rule, gate or refusal, as the order ruled.

Tests

No behaviour moved, so there is no new test and no ablation (os-dev: a description is not tested behaviour). The suites that read the schema stay green at head 157212e3:

  • pnpm --filter @objectstack/spec test: Test Files 785 passed (785), Tests 23206 passed | 1 todo (23207).
  • pnpm --filter @objectstack/spec typecheck: exit 0, check:test-typecheck: OK with the ledger held.
  • eslint packages/spec/src/ui/action.zod.ts --no-inline-config: exit 0.

Acceptance notes

  • Wording reach. The order's sentence named name; the only repeated name in the tree, sys_user.create_user's email, is declared through field ({ field: 'email', … } twice). The resolver keys by name ?? field (ResolvedActionParam.name: "inline name, or the resolved field name"), so the sentence names both spellings. Without it an author reading "a param name may repeat" could not tell a field-backed repeat falls under the same rule.
  • No case for a drift refusal. The order asked for an open question only if a real case exists for refusing same-name declarations whose value shapes differ. fix(platform-objects): Setup Create User marks Email required while phone sign-in is off #28707's census found no repeated name other than create_user's email, and its two declarations read the same field, so their shape cannot differ. No producer writes a drifting pair, so no open question is raised (「新增门禁默认否」).
  • Clause-②: no, Metadata-protocol: none, measured: check:authorable-surface and check:api-surface are green with no regenerated artifact moving beyond the description text, and no accepted value changes.

Verification

Local gates at head 157212e

Derived by node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (97 families), each exit captured before any pipe.
Re-runs: check:skill-examples and check:lean-entry-closure first exited 3 (PREREQUISITE NOT MET: client-react / objectql dist absent in this fresh worktree), then 0 after turbo run build of objectql, client and client-react. check:dual-build-cjs-loads stays NOT MEASURED (needs every package's dist); CI runs it.

node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-adr-links.mjs :: exit 0
node scripts/check-adr-links.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dev-prereqs.mjs --self-test :: exit 0
node scripts/check-doc-route-spelling.mjs --advisory :: exit 0
node scripts/check-doc-route-spelling.mjs --self-test :: exit 0
node scripts/check-docs-section-name.mjs :: exit 0
node scripts/check-docs-section-name.mjs --self-test :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-section-landing-index.mjs :: exit 0
node scripts/check-section-landing-index.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0
pnpm --filter @objectstack/spec run check:api-surface :: exit 0
pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0
pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0
pnpm --filter @objectstack/spec run check:docs :: exit 0
pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm --filter @objectstack/spec run check:empty-state :: exit 0
pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0
pnpm --filter @objectstack/spec run check:export-origins :: exit 0
pnpm --filter @objectstack/spec run check:exported-any :: exit 0
pnpm --filter @objectstack/spec run check:generated :: exit 0
pnpm --filter @objectstack/spec run check:liveness :: exit 0
pnpm --filter @objectstack/spec run check:llms-txt :: exit 0
pnpm --filter @objectstack/spec run check:skill-examples :: exit 0
pnpm --filter @objectstack/spec run check:skill-refs :: exit 0
pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0
pnpm --filter @objectstack/spec run check:variant-docs :: exit 0
pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:corpus-claim-drift :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:doc-anchors :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:doc-frontmatter :: exit 0
pnpm check:docs-audit-scope :: exit 0
pnpm check:docs-redirects :: exit 0
pnpm check:docs-single-h1 :: exit 0
pnpm check:docs-spec-enumerations :: exit 0
pnpm check:docs-transcript-drift :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 3
pnpm check:gitlink-declared :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:merge-driver :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:pm-prior-rulings :: exit 0
pnpm check:published-files :: exit 0
pnpm check:published-readme-links :: exit 0
pnpm check:react-page-adapter-contract :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:role-word :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:spec-docblock-symbol-anchors :: exit 0
pnpm check:spec-parsed-alias :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:vendor-version-stamps :: exit 0
pnpm check:watch-hint-literal :: exit 0

dispatch-gates --ran: 97 derived, 96 run, 1 NOT-MEASURED (check:dual-build-cjs-loads), 0 UNRUN.


Generated by Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
…tion

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
… same-name sentence

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation protocol:ui tooling labels Nov 1, 2026
@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 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; 100 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 — 168 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 7e6e8a464799af6271410ec6b4b3e119716e2f3a → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 878b64eed05f134b2b1395196a54af2159bbf599 — the merge of head 157212e35382baefae19cf88da5c942d587fcaf5 into base 7e6e8a464799af6271410ec6b4b3e119716e2f3a, 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 878b64eed05f134b2b1395196a54af2159bbf599 && git checkout 878b64eed05f134b2b1395196a54af2159bbf599
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7e6e8a464799af6271410ec6b4b3e119716e2f3a 157212e35382baefae19cf88da5c942d587fcaf5 && git checkout -B drift-repro 7e6e8a464799af6271410ec6b4b3e119716e2f3a && git merge --no-ff 157212e35382baefae19cf88da5c942d587fcaf5

node scripts/docs-audit/affected-docs.mjs --json 7e6e8a464799af6271410ec6b4b3e119716e2f3a

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 157212e35382baefae19cf88da5c942d587fcaf5
Local-runs: none
Scope: packages/spec/src/ui/action.zod.ts + packages/spec/src/ui/action-params.zod.ts (validateActionParams, ResolvedActionParam), packages/runtime/src/action-execution.ts (resolveDeclaredActionParams, the caller that keys a field-backed param), packages/platform-objects/src/identity/sys-user.object.ts (create_user, the one repeated name in the tree) and its sys-user-create-user-email-required.test.ts, packages/spec/src/ui/action-params.test.ts (the #28280 pins), objectui packages/fields/src/resolveActionParams.ts (paramName, the dialog's key). The remaining paths (.changeset/28732-action-params-same-name-rule.md, the four regenerated content/docs/references/**/*.mdx) are the lane seat's ACCEPT at TIER_DEFAULT; they were read to judge the hits file, not reviewed.

Inputs read: card #28732 body and its three comments (triage 6560352773, claim 6560638974, os-dev-report 6561191307); PR #28752 body and file list (6 files); the net diff 8bceb6a2…157212e3 (+16/−6, 4 commits); the check-runs on 157212e3 (see ① / ③).

① Derived judgments

  • Accept-set: unchanged — right. The only source hunk replaces the string inside .describe() on ActionSchema.params (action.zod.ts:1431). No key, refinement, transform or error map moves; z.array(ActionParamSchema).optional() is the same schema before and after. A repeated name still parses silently, exactly as the card asked (no new gate, 「新增门禁默认否」). The accept-set the dev names in the PR body (nothing accepted or refused differently) matches the tree.
  • Public surface: the description text only — right. .describe() ships three ways, each the same sentence: dist (the zod schema), json-schema/ (gen:schema, build output, untracked) and the generated references. All four .mdx rows carrying the params member (Action and InlineAction in ui/action.mdx, ActionButtonProps in ui/component.mdx, the embedded action tables in data/object.mdx and kernel/metadata-plugin.mdx) carry the new sentence and no stale copy of the old one remains (git grep at head: 5 files, every occurrence of the first sentence is followed by the new one). check:generated / check:docs on the head are the gate for that; their conclusions are in ③.
  • Shared description reaches InlineAction / ActionButtonProps — right, with one reading noted in ④. The description lives on the one actionObject() factory, so the sentence is also served on the inline page-button form. That is where the first sentence already lived; the dev did not fork it, correctly.
  • Wording widened from the order's literal (name) to name or field — right. resolveDeclaredActionParams (runtime action-execution.ts:2801) keys every declaration by p?.name ?? fieldRef, ActionParamSchema.name's own docblock says "Defaults to field when field is set", the ai.paramHints refine keys by p.name ?? p.field (action.zod.ts:2218) and objectui's paramName reads param.name ?? param.field. The one repeated name in the tree, sys_user.create_user's email, is declared twice through field (sys-user.object.ts:331-332), so a sentence naming only name would not have covered the case that motivated the rule.
  • Clause-②: no, Metadata-protocol: none — right. No authored key, value or default changes; nothing stored moves. check:authorable-surface and check:api-surface are not touched by .describe() text.

② Semver level

@objectstack/spec patch — matches. The change is description text inside a shipped schema (dist, json-schema/, src/**/*.zod.ts are all in spec's files[]), so a changeset is owed and patch is the floor and the ceiling: no type, no accepted value, no refusal moves. The changeset carries Clause-②: no and Metadata-protocol: none, in agreement with the PR body and with the diff. Check Changeset on the head: success.

Clause-②: no — judged from the tree, not from the PR body: the authoring input type of ActionSchema (what defineAction publishes) is unchanged, and the only moved bytes are inside a string literal passed to .describe().

③ Boundary flags

  • Deviation 1 — "Description names name or field (the order's sentence named the name only)." Answered: right, see ①. The widening is not a vocabulary change; it names the two spellings the resolver already folds into one request key.
  • Deviation 2 — "test:repo not run locally; CI Test Core runs it." Answered by the head's check-runs, which are the gate verdicts for this review: 65 check-runs on 157212e3, all completed, 57 success and 8 skipped (opt-in / matrix-template / schedule-gate rows), 0 failure, 0 cancelled. Among the successes: Test Core (1/8–8/8), Test UI (1/16–16/16), Build Core, Build Docs, Build & E2E UI, Live E2E UI, Lint · ESLint, Lint · docs & code guards, Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · PM dispatch-gates, Lint · scoped (×3), Type Check · workspace / source gates / consumer gates / debt ledger, Type Check UI, Check Changeset, Check PR Size, Check Documentation Links, Spec property liveness, Governed Surface Queue Guard, PR-body guards, Dogfood Regression Gate, Flag docs affected by code changes. The locally NOT-MEASURED check:dual-build-cjs-loads rides the Lint gate jobs, which are green.
  • open_questions: []. The card offered one conditional open question: a parse-time refusal of drifting shapes (same name, different type), to be raised only with a real case. The dev raised none. Agreed on the tree: create_user's two email declarations read the same field and declare no type, so their resolved shape cannot differ; action-params.test.ts:51-58 pins FIRST-wins over a deliberately drifting pair as the contract, so the validator's behaviour on drift is already a decided answer, not an unknown. No producer in the tree writes a drifting pair. Not escalated.
  • out_of_scope_findings: []. Nothing to add.
  • Local-runs: none. Nothing built, run or re-run here; node scripts/pm/record-recognisers.mjs --template was read for the record's shape only.

④ Design judgment

packages/spec/src/ui/action.zod.ts — judged against the ruling it implements (card #28732 "Wanted": the params description says a name may repeat only for per-gate variants of one input, required holds only when every declaration requires it, the first declaration's type wins; no new gate) and against its first consumer, the validator the sentence describes (validateActionParams, action-params.zod.ts:185-230) as fed by resolveDeclaredActionParams on the one real instance (sys_user.create_user).

Each clause of the sentence, read against the code:

  1. "A param name (its name, or the field a field-backed param reads)" — holds. The resolver's key is p?.name ?? fieldRef; ResolvedActionParam.name is "inline name, or the resolved field name". The parenthetical reads as "name, else field", which is the resolver's precedence (a field-backed param that also sets name is keyed by name), so it does not invert it.
  2. "may repeat only for per-gate variants of ONE input (declarations that differ in visible / requiresFeature, e.g. one optional and one required)" — holds as the card's norm, unenforced by design. requiresFeature is lowered into visible at parse time (lowerRequiresFeature, action.zod.ts:588; its describe says so), so naming both spellings is the author-side vocabulary, not two mechanisms. The example is the tree's only instance (sys-user.object.ts:331-332: required: false, requiresFeature: 'phoneNumber' then required: true, visible: 'has(features.phoneNumber) && features.phoneNumber != true').
  3. "the params check treats the name as required only when EVERY declaration of it requires it" — holds: declared.set(p.name, first ? { ...first, required: Boolean(first.required && p.required) } : p) (action-params.zod.ts:206-207); pinned by action-params.test.ts:36-48 in both orders.
  4. "judges its value by the FIRST declaration's shape (type, multiple, options)" — holds: the spread keeps the first declaration's type/multiple/options and only required is folded; valueSchemaFor({ type, multiple, options }) at :221 is the whole shape the check reads, and ResolvedActionParam carries no other shape key, so the three-word list is complete, not a sample. Pinned by action-params.test.ts:51-58.
  5. "so keep that shape identical across them" — the right advisory for a rule the schema deliberately does not enforce: the console (filterVisibleParams, per the create_user test header) renders whichever declaration's gate says yes, so a second declaration with a different type would be collected by the dialog under its own shape and then judged by the first's; the sentence tells the author exactly which side wins.

Attempts to break it:

  • Does the sentence promise an enforcement that does not exist? No. It says "may repeat only…" (the norm) and "the params check treats… judges…" (what runs). It never says the schema refuses anything, and nothing in ActionSchema was given a refusal.
  • Does it misstate the key for a field-backed param that also sets name? No (clause 1 above).
  • Does it reach surfaces where the params check does not run? The sentence is also served on InlineAction / ActionButtonProps (same factory). On a page button of type:'api' whose target is a foreign endpoint the ADR-0104 D2 check is not the thing judging the body. The first sentence of the description has always been shared the same way, the rule is still the platform's own and the advisory ("keep that shape identical") is harmless there. Noted, not a finding.
  • Does the generated reference drift from the source? No: five rows across four .mdx files, all byte-identical to the .describe() text (one \' unescaped by the generator), and no stale copy remains.

Findings:

  • should-fix (minor): "the params check" is unnamed. Every other description on this schema that cites an enforcer names it (e.g. "refused by @objectstack/lint (action-dispatch-contract-mismatch)"). Naming the check — validateActionParams (ADR-0104 D2), applied on the platform action route — would let an author or an AI reading the generated reference find the behaviour it describes, and would also tell an inline-api-button author that the rule is the platform's dispatch check, not their foreign target's. Text only; can follow in a later description PR or at the seat's discretion.
  • No must-fix. The sentence states the rule the validator applies, with the same precedence and the same shape list, and the one repeated name in the tree is exactly the case it describes.

Implemented-by: claude/project-thread-qaqibu
Reviewed-by: session_01FEkQ5kE5BrPUPNMMyv6oCH

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH

席位核对 · ACCEPT · PR #28752 · 2026-11-01T17:32Z

Head-sha: 157212e35382baefae19cf88da5c942d587fcaf5

维护者速读(席位终稿)

核对六项

  1. 形态:draft → main;Fixes #28732,合并关卡正确。文件 6 个(1 处 .describe()、4 个生成的参考 mdx、changeset),在认领 6560638974 申报面内。
  2. 前提实测:dev 实测树内唯一重复参数名是 sys_user.create_user 的 email(经 field),无生产者写出形状漂移的同名对;复核独立核对一致。
  3. 门禁:本 head check-runs 57 success、8 skipped(路径未命中)、0 失败或待跑。dev 本地 97 条派生门禁中 96 条 exit 0,check:dual-build-cjs-loads 本地未测,由 CI Build Core(success)读取;spec 测试 785/785 文件、typecheck 与 eslint 绿。
  4. 接受的偏差:句中写"name 或 field"(比卡片多一个 field),因为 create_user 经 field 重复 email,树内所有取键处都用 name ?? field,复核认定正确。复核一条 should-fix 仅措辞:句中"参数检查"可点名 validateActionParams(ADR-0104 D2),不挡落地。
  5. 范围外去向:无。open_questions 为空(无漂移形状的生产者)。
  6. 落地条款:Clause-②: no,但 packages/spec 路径 ⇒ CONTRACT_REVIEW_TIER 复核 PASS(6561409355,Head-sha 与现 head 一致)。Metadata-protocol: none,spec patch,与 changeset 一致(Check Changeset 本 head success)。check-governed-merges --pr 28752:NOT governed,22 行。落地授权,维护者原话:「按照 pm-dispatch 规则复核过的都授权」(2026-10-25T11:35Z)。

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 1, 2026 17:34
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 1, 2026 17:34
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 1, 2026
@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

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

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

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

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

  • Type Check UI — 失败步骤: Run type-check over the ui packages(日志不可读,点进 job 看)

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

    × on a read-only base, the editor is handed the surface's customization: the base, its version and the door 86ms
    FAIL   dom  packages/app-shell/src/views/studio-design/StudioDesignSurface.metadataArea-28561.test.tsx > the area's form editor in the builder (objectstack#28569) > on a read-only base, the editor is 
      ↳ 失败原因: AssertionError: expected { base: 'com.vendor.hotcrm', …(5) } to deeply equal { base: 'com.vendor.hotcrm', …(4) }
    

↳ 失败原因 是判读的关键:超时(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)

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 protocol:ui size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec: ActionSchema.params accepts a repeated param name silently; the same-name rule (#28280) is stated only in the validator docblock

1 participant