You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
objectql: a tenant overlay copy of an extension's validation rule runs BESIDE the extension's original in the fold — should the overlay replace it (ADR-0029 D9.2a says validations stay additive)? #27949
Ruled: 6545641964 · letter B · 2026-10-31T15:54Z
Ruling-ref: 6545641964
Ruled: 6524229156 · letter C′ (merge by name; message and active to the tenant, condition to the package; a changed condition refused at build) · 2026-10-30T13:42Z
Ruling-ref: 6524229156
Filing gate: ③ split by the seat from #27559 on contract review 6515204227 (must-fix 1) of PR #27932. ⛔ Not a claim. No grade: triage sets the lane and grade, and routes the decision.
What happens today (measured on #27559's probe, card body)
A tenant overlays a packaged object and rewords the message of a validation rule that a second package contributes through objectExtensions, keeping the rule's name. The registry fold then holds TWO rules of that name: the overlay's reworded copy and the extender's appended original (mergeObjectDefinitions concatenates extenders' validations; subtractExtenderContributions removes only byte-equal extender entries). Both are enforced, so a refused write reports both sentences. After PR #27932 (the one-packaged-rule-set half of #27559) the tenant's sentence is served as written and the extension's original stays translated, but both still fire.
The question (a product decision, not the lane's)
Should a tenant overlay copy of an extension's rule replace the extension's rule in the fold (the rule then runs once, as the tenant wrote it), or keep running beside it as today?
Replacing it reverses ADR-0029 D9.2a's recorded sentence that the fold still runs additively for validations, and ADR-0126 regime E ("additive, upgrade-safe"). It needs a dated ADR amendment (Prime Directive 13; Tier H).
It also means a later package fix to that rule no longer reaches a tenant who overlaid it — the overlay freezes the enforced rule.
Keeping it beside means a tenant cannot reword an extension's rule without the original also firing.
PR #27932's first head carried a replace-when-the-owner-does-not-declare-the-name design (tenantCarriedRuleNames / withoutRulesNamed in packages/objectql/src/registry.ts); the contract review failed it on the missing ADR amendment, and the seat split it off here.
Wanted (route is triage's)
Route the replace-or-beside question to the maintainer. If "replace": one PR with the registry fold change, its pins, and the ADR-0029 amendment.
⛔ Not a claim. This reverses or keeps ADR-0029 D9.2a (validations stay additive), so it is the maintainer's.
Options:
A. Keep it additive: both rules run. Today's behaviour.
B. The tenant overlay copy of a same-named extension rule replaces it.
C. Additive by default, but an overlay copy whose condition is byte-equal to the extension's, and which only rewords the message, replaces it. The rule runs once, with the tenant's sentence.
Triage's lean: C. It fixes the double sentence without letting a tenant overlay weaken a packaged rule. A changed condition still runs beside the original, so D9.2a's protection holds.
Director seat, summon #54 continuation 2 (ledger objectstack#12708, opening mark 6523423511). Presented to the maintainer in batch #431 as item 2 of 3 — triage 6515331119 routes a product rule to the maintainer: does a tenant's overlay copy of an extension-contributed validation rule replace the extension's rule, or run beside it.
Seat increment (re-read at origin/main59547058, 2026-10-30T13:02Z):
Prior rulings: ADR-0029 D9.2a (additive validations — the protection against a tenant overlay weakening a packaged rule); ADR-0126 regime E (additive, upgrade-safe); contract review 6515204227 must-fix 1 (a replace design needs an ADR amendment first). check-prior-rulings.mjs --card 27949 names ADR-0029 D9.2 / D9.4 / D9.5 / D9.7, ADR-0020 D1, ADR-0060 D3, ADR-0076 D4–D8 — the relevant ones are the two above.
Facets: ① the invariant worth keeping is "a tenant cannot weaken a packaged rule by overlaying it"; the double sentence is a symptom of treating a message-only reword as a second rule. C keeps the invariant (a changed condition still runs beside the original) and removes the duplicate (byte-equal condition, reworded message ⇒ one rule, the tenant's sentence); B replaces unconditionally and freezes the enforced rule against later package fixes; A leaves every reword firing twice. ② measured on i18n: a tenant's rewrite of an objectExtensions-contributed validation rule message still loses to that package's translation, on the refusal and on /meta #27559's probe; any tenant who rewords an extension's rule message hits it. ③ C is decidable mechanically from the two rule bodies (condition bytes equal or not); an author who changes the condition gets the additive behaviour, loudly visible as two sentences, never a silently weakened rule. ④ C adds no key; one fold rule plus a dated ADR-0029 note that D9.2a's additivity is kept for conditions and clarified for message-only overlays (Tier H).
Seat's letter: C — additive by default; an overlay copy whose condition is byte-equal to the extension's rule and differs only in message replaces it in the fold (the rule runs once, with the tenant's sentence); a changed condition runs beside the original as today. Not A (every reword fires twice), not B (a tenant overlay can weaken or freeze a packaged rule, reversing D9.2a).
Workload of C: domain:server · 1 PR · S–M (the fold rule in packages/objectql/src/registry.ts, pins for the byte-equal and the changed-condition cases; the dated ADR-0029 note under D9.2a — docs/adr/**, so the PR is Tier H) · Clause-②: no, Metadata-protocol: none (what an author can write is unchanged; how two same-named rules fold is).
After the ruling: needs-user-decision → pm:queue (domain:server, p2); the server lane dispatches with Ruling-ref:.
Director seat, summon #54 continuation 2 (ledger objectstack#12708, opening mark 6523423511, brief 6523479993). The maintainer asked in the summon chat 「校验规则有 ID 的概念吗?扩展是否建议就是覆盖,其他类似平台是怎么设计的」; the seat answered (rules carry a declared-unique name and an active flag, but the registry fold concatenates validations instead of keying them by name; Salesforce managed packages lock the formula and let the subscriber change message and active; Dynamics and ServiceNow replace by identity and freeze on upgrade) and revised its letter to C′; the maintainer answered, verbatim: 「同意」.
What the ruling binds (letter C′, the Salesforce managed-package model):
Identity. A validation rule's identity within an object is its name (validation.zod.ts L143, "Unique rule name"). The registry fold merges validationsby name across layers (base → extenders → tenant overlay), the way fields merge by key, instead of concatenating the arrays (registry.ts L560). Two rules of one name in one object are one rule.
What a same-named overlay may change. A tenant overlay copy of a packaged or extension-contributed rule may change message and active (including active: false, an explicit, visible deactivation); it may not change the condition (nor the rule type): the condition belongs to the package. The rule then runs once, with the tenant's sentence, or not at all if deactivated.
A changed condition is refused at build, loudly, with the prescription "to change the condition, add a rule under a new name, or change it in the package" — never two rules running beside each other, never a silent replacement.
Different names stay additive, as ADR-0029 D9.2a protects: an extender's or a tenant's new rule is added. D9.2a gains a dated note: "validations stay additive" means rules of different names add; a same-named rule merges by name with the condition owned by the package (Prime Directive 13: a recorded decision is amended by an ADR edit).
Not taken: A (every reword fires twice), B (whole-rule replacement freezes the enforced rule against later package fixes — the Dynamics / ServiceNow cost), C (byte-equal dedupe with two rules running on a changed condition, a confusing outcome), C″ (message only, no deactivation — the maintainer took C′ with active).
Governing text: validation.zod.ts L143 / L148 (name, active); registry.ts L503 / L560 (the concatenation this ruling replaces); ADR-0029 D9.2 / D9.2a; ADR-0126 regime E; contract review 6515204227 must-fix 1 (an ADR amendment before a replace design). Premises re-read at origin/main59547058 (brief 6523479993).
Workload of the letter:domain:server · 1 PR · S–M (the by-name merge in packages/objectql/src/registry.ts with subtractExtenderContributions kept consistent; a build-time refusal with its prescription for a same-named overlay that changes condition or type; three pins — reword merges to one rule, condition change refused, active: false deactivates; the dated ADR-0029 D9.2a note — docs/adr/**, so the PR is Tier H) · Clause-②: no (narrowing) — a same-named overlay with a different condition that parses today is refused at build ⇒ one contract-review-tier review before the queue · Metadata-protocol: none (no key changes; what a tenant may write for an existing key is narrowed).
Execution:needs-user-decision → pm:queue in this act (domain:server, priority:p2, area:records as triage set them); the server lane dispatches with Ruling-ref: naming this comment.
os-decision-facets
① 长远:名字即身份,按名字合并,条件归包、措辞与开关归租户,与 Salesforce 托管包同形;B 冻结、C 并存
② 拉动:#27559 探针实测;任何改写扩展规则措辞的租户都撞上
③ 防错:改条件在构建时响亮拒绝;停用是显式可见的动作,不是静默放松
④ 不扩散:不加键;折叠按既有唯一名合并,兑现一条已声明未兑现的约束
Claim: PM loop · 2026-10-30T13:49Z · ruling 6524229156 (letter C′, maintainer 「同意」 13:40Z)
Ruling-ref: 6524229156
Session: session_016WV4d1cS6VY12DcqQYpdvA (card thread; seat session_016WV4d1Np3hU7RQnA13YZcL)
Account: os-elon-musk
Branch: claude/project-thread-hrozea
Worktree: objectstack-issue-27949
Domain: domain:server
Seat: domain:server#4
File surface: packages/objectql/src/registry.ts (the validations fold by name, subtractExtenderContributions kept consistent, the build-time refusal), its tests, docs/adr/0029-kernel-object-ownership-and-platform-objects-decomposition.md (the dated D9.2a note only), .changeset/*; ⛔ no packages/objectql/src/engine.ts (hot file, #25522), no packages/spec key change
Container & model: M, mode:thread, model: default (TIER_DEFAULT), effort: high; contract review owed (narrowing), Tier H for the ADR edit (GOVERNED_APPROVERS)
Clause-②: no (narrowing) — a same-named overlay with a different condition or type that parses today is refused at build; the dev confirms from the diff
Metadata-protocol: narrowed — provisional: AGENTS.md step 3 reads "a value refused" as narrowed (the ruling's workload line says none); the dev and the contract review settle it from the diff
Responsibility: a tenant overlay copy of a packaged or extension-contributed validation rule runs beside the original instead of replacing its message; the platform path that covers it is the registry fold merging validations by name with the condition owned by the package; reached by every tenant that rewords or deactivates an extension's rule.
Thread-read: 6524229156
Premise: holds (card at origin/main121d1a5f; registry.ts concatenates validations)
Serial constraints cleared: no open PR on packages/objectql/src/registry.ts or ADR-0029 right now.
Release: session_016WV4d1Np3hU7RQnA13YZcL · 2026-10-31T05:37Z · seat 4 signs off by maintainer order; the claim (6524414507) ends here, and the card goes to the decision inbox (needs-user-decision) because it waits on the maintainer's ruling on where the "refused loudly" of ruling C′ (6524229156) sits: A, at overlay load, or B, at the save door, with the load reporting only (recommended, presented as 总监决裁 3 in the project's decision thread).
Resume-from: branch claude/project-thread-hrozea @ 2a499b92 (pushed, no PR; B-shaped, based on main6a648ac2), one commit with the fold, load report, predicate, pins, ADR note and changeset. Measured on that head: 12 ablations red as designed; objectql 496 files / 9641 tests green; dispatch-gates --ran 70/70 exit 0; pnpm lint 0. The drafted PR body has placeholders for the ruling. Under A the resuming seat adds the SchemaRegistry.tenantRuleCopyRefusal throw to registerObject for overlays, switches Metadata-protocol to narrowed, and re-runs the delta gates. Under B, file the save-door follow-up card (behind the protocol.ts serial queue on #6367). Both trees: merge main, contract review (owed) and Tier H governed approval (ADR note) before landing.
State: pm:dispatched → needs-user-decision; assignee cleared in this stroke.
Director seat (the maintainer's decisions thread, 2026-10-31). This card is presented to the maintainer in batch #433 (five cards: #27884, #27401, #27949, #28117, #28220 — the in-flight-blocked ones first) with the latest four-facet letter on this card as the seat's reading; the seat's increment and the maintainer's letter follow in the ruling comment.
Ruling: batch #433 item 3 · letter B · maintainer 「同意」 2026-10-31T15:49Z
Director seat (the maintainer's decisions thread). Presented in batch #433 (five in-flight-blocked cards, detailed letters in the project's decision doc; brief-claimed on this card at 15:16Z–15:18Z). The maintainer answered the whole batch at 2026-10-31T15:47Z, verbatim: 「同意」 = every item at the seat's recommendation.
What the ruling binds (letter B): under ruling C′ (6524229156), the loud refusal of a same-named tenant overlay that changes a validation rule's condition or type sits at the save door; the overlay load path only reports such a copy (no throw) and runs the rule with the package's condition, so an upgrade never fails on stored data. The branch claude/project-thread-hrozea @ 2a499b92 (by-name fold, load report, predicate, three pins, dated ADR-0029 D9.2a note, changeset) is the shape to land; the save-door half is a follow-up card queued behind the protocol.ts serial queue (#6367), Blocked-by: the landing PR. Metadata-protocol: none stays (nothing an author can write changes at load). Not taken: A (refuse at overlay load — turns the upgrade into the failure point for operators, not authors).
Workload of the letter:domain:server · the existing branch, 1 PR · S–M · Clause-②: no (narrowing) ⇒ contract review before the queue · the ADR note makes it Tier H (governed approval) · plus one follow-up card (save door).
Execution: server seat 1 (this project) takes the branch over with Resume-from above and Ruling-ref: naming this comment, merges main, fills the PR body's ruling placeholders, and files the save-door card in the same stroke; needs-user-decision → pm:dispatched is the seat's stroke. Analysis read: 6539646736.
Claim: re-dispatch under ruling B · card thread · 2026-10-31T15:59Z
Session: session_01L9qibz3ZUbZ8Qcn1rDw9yc (the seat); the card thread is session_01L9qibx3A7AeHSnSr39oxaV
Account: os-tesla (the seat's linked user; the card's assignee)
Branch: claude/project-thread-9ii7md (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#1
Ruling-ref: 6545641964 (letter B, Tesla 「同意」 2026-10-31T15:47Z, batch #433 item 3). Under ruling C′ 6524229156, the refusal sits at the save door (follow-up card), and the overlay load only reports.
File surface: as seat 4's claim 6524414507. That is packages/objectql/src/registry.ts (the validations by-name fold, subtractExtenderContributions, the load report), its tests, docs/adr/0029-… (the dated D9.2a note only), and .changeset/*. ⛔ No packages/objectql/src/engine.ts (#25522) and no packages/spec key change.
Container & model: S–M, mode:thread, model: opus, effort high. Contract review is owed (narrowing). Tier H: the ADR note needs os-zhuang's approval.
Clause-②: no (narrowing)
Metadata-protocol: none
Thread-read: 6545641964
Premise: seat 4's B-shaped head 2a499b92 was measured on main6a648ac2: 12 ablations red as designed, objectql 9641 tests green, gates 70/70. The dev re-measures after merging origin/main.
Serial constraints cleared: the save-door half, which waits in the protocol.ts queue on #6367, is NOT in this PR
Resume-from: claude/project-thread-hrozea @ 2a499b92 (seat 4's release 6539646736), carried onto the new thread branch, with origin/main merged in
Order: /mnt/project-files/server-seat-1/r2026-10-31/dispatch-27949.md (project file, not public)
State: pm:queue → pm:dispatched, assigned to os-tesla, recorded by this claim.
Report at 2026-10-31T17:09Z for PR #28445, head e5e62b28.
{
"issue": 27949,
"status": "done",
"branch": "claude/project-thread-9ii7md",
"pr": "https://github.com/objectstack-ai/objectstack/pull/28445",
"session": "session_01L9qibx3A7AeHSnSr39oxaV",
"premise_still_valid": true,
"summary": "Resumed seat 4's claude/project-thread-hrozea@2a499b92 (by-name validations fold, load report, tenantRuleCopyRefusal predicate, pins, ADR-0029 D9.2a note, changeset) and merged main 630b4078 cleanly (no conflict). Aligned two things with ruling B 6545641964: the changeset declares Metadata-protocol: none at patch (was semantic / minor), and the ADR note names ruling B and places the loud refusal at the save door, the load path only reporting. Under B registerObject needs no build-time refusal and none is added; tenantRuleCopyRefusal is left uncalled for the save-door card #28448.",
"tests": "head e5e62b28: objectql vitest Test Files 507/507, Tests 9847/9847 (lock VERDICT command-exit 0); objectql typecheck exit 0 (check:test-typecheck OK). 12 ablations via scripts/ablation-replace.mjs on registry.ts, run over registry-validation-rule-by-name + engine-validation-tenant-rewrite (18 cases, source import, no dist): A01 concat 8 failed; A02 no tenant message 8; A03 no tenant active 1; A04 tenantRuleSettings undefined 7; A05 withPackagedOwnerRules no-op 1; A06 tenant condition carried 2; A07 predicate never refuses 2; A08 packagedRules skips extenders 5; A09 no schema defaults 1; A10 load report not called 1; A11 once-guard removed 1; A12 settings last-wins 1. Each restored to the HEAD blob, git diff HEAD empty. First attempts of A05/A06/A08 were tool-refused no-ops and were re-anchored.",
"gates": "local: dispatch-gates --commands on e5e62b28c3 derived 70; 70 run with stdin /dev/null, 70 exit 0 (list: server-seat-1/r2026-10-31/out/27949-gates-ran-e5e62b28.list); --ran: 70 accounted, 0 NOT-MEASURED, exit 0. pnpm lint scoped (root lint command over packages/objectql) exit 0. CI on e5e62b28 at report time: Lint & Repo Gates success, TypeScript Type Check success, Build Core success, Governed Surface Queue Guard success, Check Changeset success, Live E2E UI success, Test UI success, Test Core 7/8 shards success and 5/8 in_progress; UI lanes skipped by filter.",
"dogfood_battery": "e5e62b28 · os-verify-lock VERDICT command-exit 0 (held 1297s) · Test Files 361 passed / 3 skipped (364) · Tests 2917 passed / 28 skipped (2945) · no failures",
"line_budget": "759 changed lines (+705 / -54), 5 files, under the 3000 threshold",
"files_changed": [
".changeset/27949-validation-rules-merge-by-name.md",
"docs/adr/0029-kernel-object-ownership-and-platform-objects-decomposition.md",
"packages/objectql/src/engine-validation-tenant-rewrite.test.ts",
"packages/objectql/src/registry-validation-rule-by-name.test.ts",
"packages/objectql/src/registry.ts"
],
"deviations": [
"Seat 4's commit subject carries `!` (kept, history not rewritten); the changeset level follows Metadata-protocol: none = patch per ruling A.",
"os-zhuang reviewer request not sent by the dev (FRAME forbids review requests); the seat sends it at pr_ready.",
"Workspace build excluded docs and @object-ui/* (no derived gate needed them; all 70 exit 0)."
],
"mcp_calls": "4 — reads only: issue_read get, issue_read get_comments (#27949), pull_request_read get, pull_request_read get_check_runs (#28445); 0 writes",
"api_writes": "2 — relay pr_create (run 41221405790, POST pulls → #28445) and this os-dev-report comment (POST issues/27949/comments); git push is not a REST write",
"open_questions": [],
"out_of_scope_findings": []
}
Activity
objectstack-fleet commentedon Oct 30, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 能用的表单 | none | P②
Triage: p2 ·
domain:server·area:records·needs-user-decision· 2026-10-30T04:56Z⛔ Not a claim. This reverses or keeps ADR-0029 D9.2a (validations stay additive), so it is the maintainer's.
Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
director#1 · os-bill · session_01Utp6CbQLcy8yg6vhJVXXi8
Brief-claimed-by: session_01Utp6CbQLcy8yg6vhJVXXi8 · batch #431 · 2026-10-30T13:02Z
Director seat, summon #54 continuation 2 (ledger objectstack#12708, opening mark 6523423511). Presented to the maintainer in batch #431 as item 2 of 3 — triage 6515331119 routes a product rule to the maintainer: does a tenant's overlay copy of an extension-contributed validation rule replace the extension's rule, or run beside it.
Seat increment (re-read at
origin/main59547058, 2026-10-30T13:02Z):mergeObjectDefinitions(registry.ts L546) folds extenders additively andsubtractExtenderContributions(L585) removes only byte-equal extender entries, so a reworded same-named rule leaves two rules in the fold; ADR-0029 D9.2 / D9.2a (L455 / L481) record that the overlay replaces the base layer while extenders fold on top andvalidationsstay additive; PR fix(i18n): a tenant's rewrite of an extension-contributed validation rule message is served as written, on the refusal and on /meta #27932 (the one-packaged-rule-set half of i18n: a tenant's rewrite of an objectExtensions-contributed validation rule message still loses to that package's translation, on the refusal and on /meta #27559) is merged, so the tenant's sentence is served as written and both rules still fire; i18n: a tenant's rewrite of an objectExtensions-contributed validation rule message still loses to that package's translation, on the refusal and on /meta #27559 is closed.check-prior-rulings.mjs --card 27949names ADR-0029 D9.2 / D9.4 / D9.5 / D9.7, ADR-0020 D1, ADR-0060 D3, ADR-0076 D4–D8 — the relevant ones are the two above.domain:server· 1 PR · S–M (the fold rule inpackages/objectql/src/registry.ts, pins for the byte-equal and the changed-condition cases; the dated ADR-0029 note under D9.2a —docs/adr/**, so the PR is Tier H) ·Clause-②: no,Metadata-protocol: none(what an author can write is unchanged; how two same-named rules fold is).needs-user-decision→pm:queue(domain:server, p2); the server lane dispatches withRuling-ref:.Prior rulings read: overlay, extension, validation rule, additive, D9.2a → ADR-0029 D9.2 / D9.2a, ADR-0126 regime E, contract review 6515204227; thread: 6515331119; repo: objectstack-ai/objectstack
objectstack-fleet commentedon Oct 30, 2026
director#1 · os-bill · session_01Utp6CbQLcy8yg6vhJVXXi8
Ruling: batch #431 item 2 · letter C′ · maintainer 「同意」 2026-10-30T13:40Z
Director seat, summon #54 continuation 2 (ledger objectstack#12708, opening mark 6523423511, brief 6523479993). The maintainer asked in the summon chat 「校验规则有 ID 的概念吗?扩展是否建议就是覆盖,其他类似平台是怎么设计的」; the seat answered (rules carry a declared-unique
nameand anactiveflag, but the registry fold concatenatesvalidationsinstead of keying them by name; Salesforce managed packages lock the formula and let the subscriber change message and active; Dynamics and ServiceNow replace by identity and freeze on upgrade) and revised its letter to C′; the maintainer answered, verbatim: 「同意」.What the ruling binds (letter C′, the Salesforce managed-package model):
name(validation.zod.ts L143, "Unique rule name"). The registry fold mergesvalidationsby name across layers (base → extenders → tenant overlay), the way fields merge by key, instead of concatenating the arrays (registry.ts L560). Two rules of one name in one object are one rule.messageandactive(includingactive: false, an explicit, visible deactivation); it may not change thecondition(nor the ruletype): the condition belongs to the package. The rule then runs once, with the tenant's sentence, or not at all if deactivated.Not taken: A (every reword fires twice), B (whole-rule replacement freezes the enforced rule against later package fixes — the Dynamics / ServiceNow cost), C (byte-equal dedupe with two rules running on a changed condition, a confusing outcome), C″ (message only, no deactivation — the maintainer took C′ with
active).Governing text: validation.zod.ts L143 / L148 (
name,active); registry.ts L503 / L560 (the concatenation this ruling replaces); ADR-0029 D9.2 / D9.2a; ADR-0126 regime E; contract review 6515204227 must-fix 1 (an ADR amendment before a replace design). Premises re-read atorigin/main59547058(brief 6523479993).Workload of the letter:
domain:server· 1 PR · S–M (the by-name merge inpackages/objectql/src/registry.tswithsubtractExtenderContributionskept consistent; a build-time refusal with its prescription for a same-named overlay that changesconditionortype; three pins — reword merges to one rule, condition change refused,active: falsedeactivates; the dated ADR-0029 D9.2a note —docs/adr/**, so the PR is Tier H) ·Clause-②: no (narrowing)— a same-named overlay with a different condition that parses today is refused at build ⇒ one contract-review-tier review before the queue ·Metadata-protocol: none(no key changes; what a tenant may write for an existing key is narrowed).Execution:
needs-user-decision→pm:queuein this act (domain:server,priority:p2,area:recordsas triage set them); the server lane dispatches withRuling-ref:naming this comment.os-decision-facets
① 长远:名字即身份,按名字合并,条件归包、措辞与开关归租户,与 Salesforce 托管包同形;B 冻结、C 并存
② 拉动:#27559 探针实测;任何改写扩展规则措辞的租户都撞上
③ 防错:改条件在构建时响亮拒绝;停用是显式可见的动作,不是静默放松
④ 不扩散:不加键;折叠按既有唯一名合并,兑现一条已声明未兑现的约束
objectstack-fleet commentedon Oct 30, 2026
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL
Claim: PM loop · 2026-10-30T13:49Z · ruling 6524229156 (letter C′, maintainer 「同意」 13:40Z)
Ruling-ref: 6524229156
Session:
session_016WV4d1cS6VY12DcqQYpdvA(card thread; seatsession_016WV4d1Np3hU7RQnA13YZcL)Account:
os-elon-muskBranch:
claude/project-thread-hrozeaWorktree:
objectstack-issue-27949Domain:
domain:serverSeat:
domain:server#4File surface:
packages/objectql/src/registry.ts(thevalidationsfold by name,subtractExtenderContributionskept consistent, the build-time refusal), its tests,docs/adr/0029-kernel-object-ownership-and-platform-objects-decomposition.md(the dated D9.2a note only),.changeset/*; ⛔ nopackages/objectql/src/engine.ts(hot file, #25522), nopackages/speckey changeContainer & model:
M,mode:thread,model: default (TIER_DEFAULT),effort: high; contract review owed (narrowing), Tier H for the ADR edit (GOVERNED_APPROVERS)Clause-②: no (narrowing) — a same-named overlay with a different
conditionortypethat parses today is refused at build; the dev confirms from the diffMetadata-protocol: narrowed — provisional: AGENTS.md step 3 reads "a value refused" as
narrowed(the ruling's workload line saysnone); the dev and the contract review settle it from the diffResponsibility: a tenant overlay copy of a packaged or extension-contributed validation rule runs beside the original instead of replacing its message; the platform path that covers it is the registry fold merging
validationsbynamewith the condition owned by the package; reached by every tenant that rewords or deactivates an extension's rule.Thread-read: 6524229156
Premise: holds (card at
origin/main121d1a5f;registry.tsconcatenatesvalidations)Serial constraints cleared: no open PR on
packages/objectql/src/registry.tsor ADR-0029 right now.objectstack-fleet commentedon Oct 31, 2026
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL
Release:
session_016WV4d1Np3hU7RQnA13YZcL· 2026-10-31T05:37Z · seat 4 signs off by maintainer order; the claim (6524414507) ends here, and the card goes to the decision inbox (needs-user-decision) because it waits on the maintainer's ruling on where the "refused loudly" of ruling C′ (6524229156) sits: A, at overlay load, or B, at the save door, with the load reporting only (recommended, presented as 总监决裁 3 in the project's decision thread).Resume-from: branch
claude/project-thread-hrozea@2a499b92(pushed, no PR; B-shaped, based onmain6a648ac2), one commit with the fold, load report, predicate, pins, ADR note and changeset. Measured on that head: 12 ablations red as designed; objectql 496 files / 9641 tests green;dispatch-gates --ran70/70 exit 0;pnpm lint0. The drafted PR body has placeholders for the ruling. Under A the resuming seat adds theSchemaRegistry.tenantRuleCopyRefusalthrow toregisterObjectfor overlays, switchesMetadata-protocoltonarrowed, and re-runs the delta gates. Under B, file the save-door follow-up card (behind theprotocol.tsserial queue on #6367). Both trees: mergemain, contract review (owed) and Tier H governed approval (ADR note) before landing.State:
pm:dispatched→needs-user-decision; assignee cleared in this stroke.objectstack-fleet commentedon Oct 31, 2026
director#2 · os-tesla · session_01L9qibxdiybU8ehPKBroYE3
Brief-claimed-by: session_01L9qibxdiybU8ehPKBroYE3 · batch #433 · 2026-10-31T15:17Z
Director seat (the maintainer's decisions thread, 2026-10-31). This card is presented to the maintainer in batch #433 (five cards: #27884, #27401, #27949, #28117, #28220 — the in-flight-blocked ones first) with the latest four-facet letter on this card as the seat's reading; the seat's increment and the maintainer's letter follow in the ruling comment.
objectstack-fleet commentedon Oct 31, 2026
director#2 · os-tesla · session_01L9qibxdiybU8ehPKBroYE3
Ruling: batch #433 item 3 · letter B · maintainer 「同意」 2026-10-31T15:49Z
Director seat (the maintainer's decisions thread). Presented in batch #433 (five in-flight-blocked cards, detailed letters in the project's decision doc; brief-claimed on this card at 15:16Z–15:18Z). The maintainer answered the whole batch at 2026-10-31T15:47Z, verbatim: 「同意」 = every item at the seat's recommendation.
What the ruling binds (letter B): under ruling C′ (6524229156), the loud refusal of a same-named tenant overlay that changes a validation rule's
conditionortypesits at the save door; the overlay load path only reports such a copy (no throw) and runs the rule with the package's condition, so an upgrade never fails on stored data. The branchclaude/project-thread-hrozea@2a499b92(by-name fold, load report, predicate, three pins, dated ADR-0029 D9.2a note, changeset) is the shape to land; the save-door half is a follow-up card queued behind theprotocol.tsserial queue (#6367),Blocked-by:the landing PR.Metadata-protocol: nonestays (nothing an author can write changes at load). Not taken: A (refuse at overlay load — turns the upgrade into the failure point for operators, not authors).Workload of the letter:
domain:server· the existing branch, 1 PR · S–M ·Clause-②: no (narrowing)⇒ contract review before the queue · the ADR note makes it Tier H (governed approval) · plus one follow-up card (save door).Execution: server seat 1 (this project) takes the branch over with
Resume-fromabove andRuling-ref:naming this comment, mergesmain, fills the PR body's ruling placeholders, and files the save-door card in the same stroke;needs-user-decision→pm:dispatchedis the seat's stroke. Analysis read: 6539646736.objectstack-fleet commentedon Oct 31, 2026
domain:server#1 · os-tesla · session_01L9qibz3ZUbZ8Qcn1rDw9yc
Claim: re-dispatch under ruling B · card thread · 2026-10-31T15:59Z
Session:
session_01L9qibz3ZUbZ8Qcn1rDw9yc(the seat); the card thread issession_01L9qibx3A7AeHSnSr39oxaVAccount:
os-tesla(the seat's linked user; the card's assignee)Branch:
claude/project-thread-9ii7md(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:serverSeat:
domain:server#1Ruling-ref:
6545641964(letter B, Tesla 「同意」 2026-10-31T15:47Z, batch #433 item 3). Under ruling C′6524229156, the refusal sits at the save door (follow-up card), and the overlay load only reports.File surface: as seat 4's claim
6524414507. That ispackages/objectql/src/registry.ts(thevalidationsby-name fold,subtractExtenderContributions, the load report), its tests,docs/adr/0029-…(the dated D9.2a note only), and.changeset/*. ⛔ Nopackages/objectql/src/engine.ts(#25522) and nopackages/speckey change.Container & model:
S–M,mode:thread,model: opus, efforthigh. Contract review is owed (narrowing). Tier H: the ADR note needs os-zhuang's approval.Clause-②: no (narrowing)
Metadata-protocol: none
Thread-read: 6545641964
Premise: seat 4's B-shaped head
2a499b92was measured onmain6a648ac2: 12 ablations red as designed, objectql 9641 tests green, gates 70/70. The dev re-measures after mergingorigin/main.Serial constraints cleared: the save-door half, which waits in the
protocol.tsqueue on #6367, is NOT in this PRResume-from:
claude/project-thread-hrozea@2a499b92(seat 4's release6539646736), carried onto the new thread branch, withorigin/mainmerged inOrder:
/mnt/project-files/server-seat-1/r2026-10-31/dispatch-27949.md(project file, not public)State:
pm:queue→pm:dispatched, assigned toos-tesla, recorded by this claim.objectstack-fleet commentedon Oct 31, 2026
domain:server#1 · os-tesla · session_01L9qibx3A7AeHSnSr39oxaV
os-dev-report
Report at 2026-10-31T17:09Z for PR #28445, head
e5e62b28.{ "issue": 27949, "status": "done", "branch": "claude/project-thread-9ii7md", "pr": "https://github.com/objectstack-ai/objectstack/pull/28445", "session": "session_01L9qibx3A7AeHSnSr39oxaV", "premise_still_valid": true, "summary": "Resumed seat 4's claude/project-thread-hrozea@2a499b92 (by-name validations fold, load report, tenantRuleCopyRefusal predicate, pins, ADR-0029 D9.2a note, changeset) and merged main 630b4078 cleanly (no conflict). Aligned two things with ruling B 6545641964: the changeset declares Metadata-protocol: none at patch (was semantic / minor), and the ADR note names ruling B and places the loud refusal at the save door, the load path only reporting. Under B registerObject needs no build-time refusal and none is added; tenantRuleCopyRefusal is left uncalled for the save-door card #28448.", "tests": "head e5e62b28: objectql vitest Test Files 507/507, Tests 9847/9847 (lock VERDICT command-exit 0); objectql typecheck exit 0 (check:test-typecheck OK). 12 ablations via scripts/ablation-replace.mjs on registry.ts, run over registry-validation-rule-by-name + engine-validation-tenant-rewrite (18 cases, source import, no dist): A01 concat 8 failed; A02 no tenant message 8; A03 no tenant active 1; A04 tenantRuleSettings undefined 7; A05 withPackagedOwnerRules no-op 1; A06 tenant condition carried 2; A07 predicate never refuses 2; A08 packagedRules skips extenders 5; A09 no schema defaults 1; A10 load report not called 1; A11 once-guard removed 1; A12 settings last-wins 1. Each restored to the HEAD blob, git diff HEAD empty. First attempts of A05/A06/A08 were tool-refused no-ops and were re-anchored.", "gates": "local: dispatch-gates --commands on e5e62b28c3 derived 70; 70 run with stdin /dev/null, 70 exit 0 (list: server-seat-1/r2026-10-31/out/27949-gates-ran-e5e62b28.list); --ran: 70 accounted, 0 NOT-MEASURED, exit 0. pnpm lint scoped (root lint command over packages/objectql) exit 0. CI on e5e62b28 at report time: Lint & Repo Gates success, TypeScript Type Check success, Build Core success, Governed Surface Queue Guard success, Check Changeset success, Live E2E UI success, Test UI success, Test Core 7/8 shards success and 5/8 in_progress; UI lanes skipped by filter.", "dogfood_battery": "e5e62b28 · os-verify-lock VERDICT command-exit 0 (held 1297s) · Test Files 361 passed / 3 skipped (364) · Tests 2917 passed / 28 skipped (2945) · no failures", "line_budget": "759 changed lines (+705 / -54), 5 files, under the 3000 threshold", "files_changed": [ ".changeset/27949-validation-rules-merge-by-name.md", "docs/adr/0029-kernel-object-ownership-and-platform-objects-decomposition.md", "packages/objectql/src/engine-validation-tenant-rewrite.test.ts", "packages/objectql/src/registry-validation-rule-by-name.test.ts", "packages/objectql/src/registry.ts" ], "deviations": [ "Seat 4's commit subject carries `!` (kept, history not rewritten); the changeset level follows Metadata-protocol: none = patch per ruling A.", "os-zhuang reviewer request not sent by the dev (FRAME forbids review requests); the seat sends it at pr_ready.", "Workspace build excluded docs and @object-ui/* (no derived gate needed them; all 70 exit 0)." ], "mcp_calls": "4 — reads only: issue_read get, issue_read get_comments (#27949), pull_request_read get, pull_request_read get_check_runs (#28445); 0 writes", "api_writes": "2 — relay pr_create (run 41221405790, POST pulls → #28445) and this os-dev-report comment (POST issues/27949/comments); git push is not a REST write", "open_questions": [], "out_of_scope_findings": [] }