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
[Decision] 「是否提前」 for three domain:spec protocol cards: #24720 (refuse parent in validations[]), #24690 (stored_expression dropped-field reason), #24808 (user field with a foreign reference) #24854
Filing gate ②: a maintainer's decision. These are three protocol-class cards in the domain:spec lane. Under the standing rule 6261447528 (「后续的任务如果有涉及到spec元数据协议相关的修改,也可以和我沟通是否提前」), each one waits for the maintainer's 「是否提前」 answer, and the rule's "several at once go out as one decision card" applies.
user 字段可以写任意 reference,解析和 os validate 都通过,运行时就指向那个对象了。规范里写的是固定指向 sys_user
A 拒绝非 sys_user(收紧)/ B 改成"默认指向 sys_user 的关联字段"(放宽,改说明)
A 在 GA 后要等 19.0,或降成警告;B 随时能发
未派。第一步先看现有包里有没有人这么写
A:先测有没有人这么写,没有就拒绝;有的话先给警告
请回: 例如「24720 进,24690 进,24808 A 进」。每行可以单独定,也可以说「都不提前」。
Per card (read on origin/main)
#24720: PR #24763, head 8aa8af7310, 304 lines, not governed, merge-clean.
validationRuleParentIssue in packages/lint/src/validate-expressions.ts refuses at error. It runs at os validate / os build and at the object save door.
DroppedFieldsEvent.reason gains stored_expression. objectql reports it on insert, batch, update by id and predicate update, only when the caller's value differs from the value stored.
Under strictReadonlyWrites such a write is refused. That contract's coverage follows what onFieldsDropped reports.
Field.user spreads ...config after reference: 'sys_user' (field.zod.ts:3201), and referenceTargetOf (field-value.zod.ts:212) returns the authored carrier first.
Nothing refuses a foreign target, except for tree (object.zod.ts:1656).
Producers authoring user with a foreign reference have not been measured. That measurement is A's first act.
Dedupe
The open needs-user-decision cards in domain:spec were read: #24692 is on hold and #24798 was ruled. None covers these three.
Director delta, batch #378 item 1 (three rows) — director seat, summon #46, session_013414Vtz3Z8PrXFetUziHoh, 2026-10-20T11:12Z. Read on objectstack origin/main, fetched in this act. Thread: no comments. The card carries no four-facet block, so this delta supplies one. Only what this seat re-checked or adds is below.
Recommendation: all three into 18.0.#24720 进; #24690 进 (the pin bump first); #24808 A 进. Narrowings that miss the major wait for 19.0 or degrade to warnings. 只看①:三行都进;②③④ 是否翻转:否。Workload: #24720 = the PR as it stands; #24690 = an objectstack console pin bump plus the PR as it stands; #24808 = domain:spec · 1 PR (S: the measurement, the refusal, pins, and converting any in-repo producer) · precedent the tree foreign-reference refusal (object.zod.ts:1656).
Maintainer ruling 「甲」 recorded (#60156292005854). 18.0.0 has shipped, so 「提前」 is no longer open. The question each row now answers is 18.x versus 19.0. Triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-20T13:31Z
The rule: a narrowing that an ADR-0087 conversion carries ships in 18.x. A narrowing no conversion can carry ships its loud half in 18.x, and its refusal waits for 19.0.
Triage's derivation per row (the domain:spec seat confirms each at dispatch and records it on the card):
18.x: the DroppedFieldsEvent.reason widening ships. That is the loud half: the caller is told. 19.0: the strictReadonlyWrites refusal, since it is runtime behaviour with no metadata to convert. Landing order is unchanged: the objectstack .objectui-sha bump past objectui PR #13233 first, then PR #24778
The first act is unchanged: measure the producers. If an ADR-0087 conversion can rewrite such a field (for example, to a lookup on that object), A's refusal goes into 18.x with it. Otherwise 18.x warns, and the refusal waits for 19.0
State:needs-user-decision → pm:queue. The spec seat lands, re-levels or dispatches each row.
Activity
objectstack-fleet commentedon Oct 20, 2026
Director delta, batch #378 item 1 (three rows) — director seat, summon #46,
session_013414Vtz3Z8PrXFetUziHoh, 2026-10-20T11:12Z. Read on objectstackorigin/main, fetched in this act. Thread: no comments. The card carries no four-facet block, so this delta supplies one. Only what this seat re-checked or adds is below.Premises, re-run
validations[]rule whose condition readsparentpassesos validate, but novalidations[]rule bindsparent, so every write under it is refused as unevaluable #24720 — PR fix(lint): os validate, os build and the object save door refuse a validations[] predicate that reads parent #24763: open, draft, head8aa8af7310,mergeable_stateclean, +302/−2. Check-runs on the head: 39 success, 8 skipped, 0 failure. Holds.DroppedFieldsEvent.reasonhas no value for "written, but recomputed", soonFieldsDropped/droppedFieldsstay silent #24690 — PR feat(spec,objectql): report a caller value a stored expression replaced, as DroppedFieldsEvent reason stored_expression #24778: open, draft, head7b1b1fd8bc, +683/−69. Check-runs: 36 success, 5 skipped, 0 failure. The premise has moved. app-shell: writeWarningToast needs astored_expressionrow before objectstack#24778 widensDroppedFieldsEvent.reason, or the pin bump turns red objectui#13178 closedcompletedat 11:02 UTC today through objectui PR Widencodehelperto the object-literal stamp position — the blast radius is now measured, and 4 undischargeableunresolvedfindings are the blocker #13233. The objectstack console pin (.objectui-sha616df6598b42, moved by PR chore(console): move the objectui pin 12dc7b47c340 → 616df6598b42, past the filterableFields reader's removal (objectui#13169) #24829 at 09:44 UTC today) predates that merge. So the landing order now reads: the pin bump past PR Widencodehelperto the object-literal stamp position — the blast radius is now measured, and 4 undischargeableunresolvedfindings are the blocker #13233, then PR feat(spec,objectql): report a caller value a stored expression replaced, as DroppedFieldsEvent reason stored_expression #24778. The condition "objectui#13178 lands before GA" is met.userfield accepts any authoredreference({ type: "user", reference: "probe_account" }parses and validates) and the runtime resolves to it, whileField.userandIMPLICIT_REFERENCE_TARGETSsay the target is fixed tosys_user#24808 —packages/spec/src/data/field.zod.ts:3201-3205:Field.userwritesreference: 'sys_user'and then spreads...config, so an authoredreferenceoverrides it.field-value.zod.ts:212(referenceTargetOf) returns the authored carrier first.object.zod.ts:1656refuses a foreign target fortreeonly. Holds.Governing text, re-run
git grep -n -iE "stored_expression|strictReadonlyWrites|IMPLICIT_REFERENCE_TARGETS|fixed.*sys_user" origin/main -- AGENTS.md docs/adr packages/spec/src→field.zod.ts:3188(theusertype's docblock: "target fixed to thesys_usersystem object"),field-value.zod.ts:163/171,contracts/data-engine.ts:127/202(strictReadonlyWrites), anderror-code-ledger.zod.ts:768(ERR_READONLY_FIELD_REJECTED). AGENTS.md: 0. docs/adr: 0.Director increments
userfield accepts any authoredreference({ type: "user", reference: "probe_account" }parses and validates) and the runtime resolves to it, whileField.userandIMPLICIT_REFERENCE_TARGETSsay the target is fixed tosys_user#24808's direction is set by the protocol. The spec docblock declares theusertarget fixed tosys_user, and parsing does not enforce it. Under the standing rule (spec declaration > implementation), that is an implementation gap: A enforces the declaration. B would amend the protocol so that auserfield becomes a second spelling oflookup. The user picker andcurrent_userdefaults assume a user, so B breaks the type's meaning. Only the timing goes to the maintainer.userfield accepts any authoredreference({ type: "user", reference: "probe_account" }parses and validates) and the runtime resolves to it, whileField.userandIMPLICIT_REFERENCE_TARGETSsay the target is fixed tosys_user#24808: change "warn first if producers exist" to "fix them in the same PR". A's first act is still to measure producers that authoruserwith a foreignreference(repository examples and apps, plus any reachable app repository). Producers found in our own trees are converted toField.lookupin the same PR. A warning-first stage is warranted only on named external-user evidence (startup rule: no staged transition without it).validations[]rule whose condition readsparentpassesos validate, but novalidations[]rule bindsparent, so every write under it is refused as unevaluable #24720 is safe under either answer to showcase: a new line can be inserted on a paid invoice, so its total moves — readonlyWhen is insert-exempt by design, and nothing else locks a paid header against new lines #24684. If showcase: a new line can be inserted on a paid invoice, so its total moves — readonlyWhen is insert-exempt by design, and nothing else locks a paid header against new lines #24684 later bindsparentinvalidations[], lifting the refusal is a widening, which can ship in any minor. So refusing now costs nothing later.DroppedFieldsEvent.reasonhas no value for "written, but recomputed", soonFieldsDropped/droppedFieldsstay silent #24690: only the conditional row's condition changed (met, see premises). The narrowing is confined to callers who opted intostrictReadonlyWrites.os-decision-facets
validations[]rule whose condition readsparentpassesos validate, but novalidations[]rule bindsparent, so every write under it is refused as unevaluable #24720 拒绝永远失败的写法;spec(data): once PR #24673 lands, a client value for a stored-expression field is overwritten with no report —DroppedFieldsEvent.reasonhas no value for "written, but recomputed", soonFieldsDropped/droppedFieldsstay silent #24690 让「字段被丢弃」的通知说出真实原因;spec(data): auserfield accepts any authoredreference({ type: "user", reference: "probe_account" }parses and validates) and the runtime resolves to it, whileField.userandIMPLICIT_REFERENCE_TARGETSsay the target is fixed tosys_user#24808 兑现「user 固定指向 sys_user」),都不新增特例。validations[]rule whose condition readsparentpassesos validate, but novalidations[]rule bindsparent, so every write under it is refused as unevaluable #24720 今天写这种规则的人每次写入都失败;spec(data): once PR #24673 lands, a client value for a stored-expression field is overwritten with no report —DroppedFieldsEvent.reasonhas no value for "written, but recomputed", soonFieldsDropped/droppedFieldsstay silent #24690 是客户端值被存储表达式静默覆盖;spec(data): auserfield accepts any authoredreference({ type: "user", reference: "probe_account" }parses and validates) and the runtime resolves to it, whileField.userandIMPLICIT_REFERENCE_TARGETSsay the target is fixed tosys_user#24808 未实测有无作者这么写,A 的第一步就是测。os build/保存时当场看到。userfield accepts any authoredreference({ type: "user", reference: "probe_account" }parses and validates) and the runtime resolves to it, whileField.userandIMPLICIT_REFERENCE_TARGETSsay the target is fixed tosys_user#24808 取 A 而不是把user放宽成第二种查找字段;不新增门禁。Prior rulings read: stored_expression,strictReadonlyWrites,sys_user,validations parent → field.zod.ts:3188 docblock; standing rule 6261447528; ADR none; thread: none; repo: objectstack-ai/objectstack
Recommendation: all three into 18.0. #24720 进; #24690 进 (the pin bump first); #24808 A 进. Narrowings that miss the major wait for 19.0 or degrade to warnings. 只看①:三行都进;②③④ 是否翻转:否。Workload: #24720 = the PR as it stands; #24690 = an objectstack console pin bump plus the PR as it stands; #24808 =
domain:spec· 1 PR (S: the measurement, the refusal, pins, and converting any in-repo producer) · precedent thetreeforeign-reference refusal (object.zod.ts:1656).Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Maintainer ruling 「甲」 recorded (#6015
6292005854). 18.0.0 has shipped, so 「提前」 is no longer open. The question each row now answers is 18.x versus 19.0. Triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-20T13:31ZThe rule: a narrowing that an ADR-0087 conversion carries ships in 18.x. A narrowing no conversion can carry ships its loud half in 18.x, and its refusal waits for 19.0.
Triage's derivation per row (the
domain:specseat confirms each at dispatch and records it on the card):parentinvalidations[])parent. 18.x:validationRuleParentIssuelands atwarn, noterror(PR #24763 is re-levelled). 19.0:errorstored_expressiondropped-field reason)DroppedFieldsEvent.reasonwidening ships. That is the loud half: the caller is told. 19.0: thestrictReadonlyWritesrefusal, since it is runtime behaviour with no metadata to convert. Landing order is unchanged: the objectstack.objectui-shabump past objectui PR #13233 first, then PR #24778userfield with a foreignreference)lookupon that object), A's refusal goes into 18.x with it. Otherwise 18.x warns, and the refusal waits for 19.0State:
needs-user-decision→pm:queue. The spec seat lands, re-levels or dispatches each row.Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Closing · epic PM
session_01Y5583bs2RDd53MH6kM1ca3(os-steve) · maintainer 「24854 这个卡已经没必要了」 2026-10-20T22:57ZThe maintainer said in the epic seat's chat, verbatim: 「24854 这个卡已经没必要了」 ("this card is no longer needed"). Closed as completed.
The question is already answered: ruling 「甲」 was recorded in 6292049030 (#6015
6292005854). The three rows live on their own cards:validations[]rule whose condition readsparentpassesos validate, but novalidations[]rule bindsparent, so every write under it is refused as unevaluable #24720: closed.userfield accepts any authoredreference({ type: "user", reference: "probe_account" }parses and validates) and the runtime resolves to it, whileField.userandIMPLICIT_REFERENCE_TARGETSsay the target is fixed tosys_user#24808: closed.DroppedFieldsEvent.reasonhas no value for "written, but recomputed", soonFieldsDropped/droppedFieldsstay silent #24690:pm:dispatched, unchanged. It stays with thedomain:speclane under 甲. The widening ships in 18.x, and thestrictReadonlyWritesrefusal waits for 19.0.Nothing else depends on this card: none of the three carries a
Blocked-byline naming it.