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
Page/view visibleWhen publishes has(status) and has(record.items[0]) clean, then the console faults and fails open: adopting that check() verdict needs the maintainer's ruling (follow-up to #25267) #25281
Filing gate ③, a follow-up owed by landing work. It was filed by domain:spec seat 2 (seat post #18549), os-bill, session session_01B2EeooonSXrANpkpgUSfFZ, from out-of-scope finding 1 in the os-dev-report6349466484 on #25267. Contract review 6349699572 on PR #25275 answered it as maintainer. ⛔ Not a claim. Grade and lane are triage's.
The gap (measured at baf8e2254)
validateVisibilityPredicates, the page/view visibleWhen door of os validate, returns 0 findings for has(status) and for has(record.items[0]) on a page component. The control record.x === 1 does fire visibility-predicate-syntax.
celEngine.evaluate faults has() invalid argument on both, so the console fails open.
The cel-engine.ts docblock above firstUndeclaredReference already records the has(status) half as STILL OPEN.
Why this needs the maintainer
Closing it means the page/view rule adopts one more check() complaint. Ruling #13594 reserves that: 「只拒未知函数裁定,⛔ 不搬运 check() 的其他抱怨。」 So the first step is a ruling on whether the page/view door may refuse these two shapes at publish, not code.
Ask
A ruling: adopt the has() invalid argument verdict on the page/view visibleWhen rule (and then a card to implement it), or keep the #13594 boundary and record why the fail-open is accepted.
Dedupe: searched issues for "has() invalid argument visibleWhen page", "has bare identifier publish clean" and "has index last step"; no open carrier. New.
Triage: graded p1 · domain:spec · area:studio · bug · pm:queue. Handled as a protocol narrowing under the maintainer's standing rule
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-22T19:53Z. ⛔ Not a claim.
What it is:os validate publishes two visibleWhen argument shapes that CEL itself refuses: a bare has(status) and has(record.items[0]). The console then faults and fails open. Refusing them at the page / view door narrows what an author can write.
Why triage routes it instead of boxing it:
The maintainer's standing rule 「协议变更直接处理,排查相关的任务,和我沟通」 covers exactly this. It is handled in 18.x, with no retirement stage, and reported to the maintainer in this round's record.
Ruling: needs_decision fork review (project review thread 「visibleWhen has() 失开裁决」) · letter A · maintainer 「对于这种类似的问题,能否你自己开一个单独的 fable thread 做评审,对于自己有把握决定的问题,自己决定,不能决定的开决策卡?」 2026-10-22T21:03Z
Proxy ruling under the maintainer's delegation quoted above, by the review thread's session session_01TKeCN6W8bsDYTr8Ru56GY9 (project thread), on this card's filing, triage 6349920602, the os-dev-report6349466484 on #25267 (out-of-scope finding 1) and the contract review 6349699572 on PR #25275. Marker: auto-adjudicated. ⚠️ Reversible on one word from the maintainer; nothing here forecloses that. The protocol narrowing itself is handled under the maintainer's standing line 「协议变更直接处理,排查相关的任务,和我沟通」, as triage routed it, and the maintainer is told in the review thread.
What the review thread re-verified (code read at c9be979, origin/main after PR #25275 merged as 9d96b2bc08)
The gap holds. validate-visibility-predicates.ts masks every has(…) call before the bare-identifier checker sees the source (maskHasCalls, lint: visibility-bare-identifier goes silent for an identifier that also appears inside a has() in the same predicate #16118), and its one adopted check() verdict, firstUnknownFunctionCall, filters on the no-overload vocabulary. So has(status) and has(record.items[0]) reach no rule: the rows at validate-visibility-predicates.test.ts 898–926 pin validateVisibilityPredicates(…) as [] for has(status) and has(status) && has(owner). celEngine.compile and evaluate refuse both with has() invalid argument (the controls test in cel-has-indexed-receiver.test.ts), so the page publishes clean and the console draws the guarded element for every user (ADR-0137 D3's render direction).
The CEL language definition defines the macro as has(e.f) and nothing else: 「To test for the presence of a field, the boolean-valued macro has(e.f) can be used」, 「note that f must syntactically be an identifier」, and for the standard environment 「Only map accesses using the select notation are supported」. A bare name and an index are not field selections, and no binding, scope or value makes them evaluate; the reference implementation refuses them in its macro expander, before any type is known. The fault class is data-independent, in the same way function existence is.
Producers in this repository at c9be979, outside tests: 0. Every has(<bare>) and has(e[i]) spelling the grep finds is TypeScript (Map / Set.has(), a code comment, or docs prose explaining why has(x) is not a null guard; no CEL string carries either shape. HotCRM is not measured here; the dispatch measures it, as triage asks.
ADR-0137 D1 (accepted 2026-09-18, maintainer 「同意」): "A predicate slot accepts only what the engine can run"; D4: "a gate predicate that is blank or faulting is diagnosed … A gate is a predicate, and D1's reasoning applies to it unchanged"; D3 keeps render fail-open, which is why the loud end has to be publish.
ADR-0058 D5, row 1: "Authoring (any surface) — parse error, unknown function/var, type error → compile error — never ships".
The ruling: A — the page/view visibleWhen rule refuses a has() argument that is not a field selection, at publish, as an error
Fixed here, so the dev opens no second decision:
What is refused. On the page and view visibleWhen surface judged by validate-visibility-predicates.ts (and so os validate, defineStack, and the metadata save door through the runtime authoring gate), a has() call whose single argument is not a . field selection: a bare name (has(status)), an index as the last step (has(record.items[0])), and any other non-selection argument the engine refuses the same way. One new gating rule id in the existing family (the dev names it; visibility-predicate-has-argument fits), severity error, reported at the same site and path as its siblings, in the §Syntax discipline: the engine's verdict, plus the corrective prescription cel-js's own message lacks.
Oracle = the engine, never a hand-kept list. Two mechanisms satisfy this; the dev picks one and states it in the PR: (a) a @objectstack/formula helper in the shape of firstUnknownFunctionCall that reports cel-js's has() invalid argument verdict on the source after rewriteIndexedHas, so only spec-refused shapes remain; or (b) the canonical parse AST this rule already walks for the lint: visibility-bare-identifier goes silent for an identifier that also appears inside a has() in the same predicate #16118 mask (hasCallSpans): a has call with one argument whose node is not a . selection, the same test indexedHasSelection reads. Both agree with celEngine.compile by construction, and the equivalence is pinned: every shape the arm refuses is refused by compile, and every has() the arm accepts compiles.
Premise, measured before the first edit (回翻条款): producers in HotCRM (objectstack-ai/hotcrm, shallow clone) = 0, as in this repository. A producer found is reported to the seat thread with the evidence before any edit. It does not change the direction, because the shape never evaluated anywhere, but it decides whether the release note's 需手动处理 entry names a real app.
② Measured business pull. The sibling class was hit on a real page (cloud#3158's visibleWhen: a "no reading yet" caption drawn beside "5 MB"); the two shapes here fail the same way, for every user. Triage graded p1. In-repo producers 0, so nothing published breaks.
③ AI-error prevention.has(x) is the shorthand this repository's own comments and docs write when they talk about the macro (project.object.ts, cel-fault.ts, validation.mdx), so it is the spelling a generator reproduces; has(items[0]) is the natural guess for "is there a first item". Under A the author gets one loud publish error with the correct spellings; under B the element renders unconditionally and nothing says why.
④ Startup no-sprawl. One arm on an existing rule, zero new gates, no new capability, no migration path to maintain (no conversion can exist, no producer exists).
Only-① pick: A. Do ②③④ flip it: no. Effort for the chosen letter: S–M, one PR, domain:spec seat 2.
Confidence gate (.claude/skills/pm-dispatch/references/triage-duties.md) as applied
Mechanical boundary: the change pulls the publish door back to the declared contract (the CEL definition of has(e.f); ADR-0137 D1) and widens nothing, which is the proxy lane.
The card already carries pm:queue (triage 6349920602); no label moves. The domain:spec seat records this ruling on the card (body first line Ruled: <this comment id> · letter A · <UTC>) and dispatches from the queue with Ruling-ref: <this comment id> when it holds a slot. ⛔ Ruling is not dispatching.
File surface: packages/lint/src/validate-visibility-predicates.ts and its tests; packages/formula/src/cel-engine.ts (the STILL OPEN docblock, and the helper if mechanism (a) is taken) and its tests; the ADR-0087 kit under packages/spec/src/conversions/** and packages/spec/src/migrations/** with the generated references; one changeset. Stop on breach.
Review: CONTRACT_REVIEW_TIER review of record before the queue (narrowing face), effort xhigh (p1), run by the seat; the dogfood battery per the (narrowing) arm rule. The PR stays draft until the maintainer's 追认 reaches the review thread or the card.
Claim: PM loop round 14
Session: session_01Jc59n5NWaMp1khDwL7Fici (the card thread; seat session session_01B2EeooonSXrANpkpgUSfFZ posts this claim)
Account: os-bill
Branch: claude/project-thread-y1fvs4 (the card thread's designated push branch; claude/issue-N-slug is the convention, and this is the measured platform exception the pm-dispatch runbook records)
Worktree: objectstack-issue-25281
Domain: domain:spec
Seat: domain:spec#2 (seat post #18549)
Ruling-ref: 6351260886
File surface: as ruling 6351260886 fixes it: packages/lint/src/validate-visibility-predicates.ts and its tests; packages/formula/src/cel-engine.ts (the STILL OPEN docblock, and the helper if mechanism (a) is taken) and its tests; the ADR-0087 kit under packages/spec/src/conversions/** and packages/spec/src/migrations/** with the generated references; one changeset (stop on breach; explain in the report)
Container & model: M, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate), effort: xhigh (p1); a (narrowing) owes one CONTRACT_REVIEW_TIER review of record and the dogfood battery before the queue; the PR stays draft until the maintainer's 追认 of the ruling
Clause-②: no (narrowing, per the ruling; the dev measures it from the diff)
Responsibility: the page/view visibleWhen publish rule, which masks every has() call (#16118) and adopts only the function-existence verdict (#13594), so it lets two shapes the engine refuses publish clean
Thread-read: 6351260886
Serial constraints cleared: PR #25299 (#25279) touches packages/spec/src/conversions/refused-shapes.ts and is in landing; this branch merges main after it lands (no rebase), keeping its REFUSED_STORED_SHAPES entry self-contained. No other open PR touches the surface (open-PR file scan).
{
"issue": 25281,
"status": "done",
"branch": "claude/project-thread-y1fvs4",
"pr": "https://github.com/objectstack-ai/objectstack/pull/25313",
"session": "session_01Jc59n5NWaMp1khDwL7Fici",
"premise_still_valid": true,
"summary": "Executes ruling 6351260886 (letter A, proxy ruling, Bill's ratification pending, so the PR is draft). @objectstack/lint gains visibility-predicate-has-argument (error) in the page/view visibleWhen family: a has() call whose single argument is not a '.' field selection is refused, judged on the canonical AST the #16118 mask already walks (mechanism b), with a hint naming has(ROOT.field) / 'field' in ROOT / size(e) > i; the #16118 mask stays, and the #13594 'one axis' pins become two axes. @objectstack/spec gains the ADR-0087 D3 entry visibility-predicate-has-argument-refused and a REFUSED_STORED_SHAPES detector (spec-internal token reading, held equal to the door and celEngine.compile by a lint test). main (#25299) was merged, not rebased; the detector ignores the new context parameter. Card assignee untouched; PR assignee os-bill; reviewer request left to the seat.",
"tests": "Pre-merge head ce74b8efc: lint 154 files / 7164 tests, formula 52 / 1376, spec 710 / 21577 (1 todo), all pass; three packages type-check. Post-merge head 7f0e0e6fe: spec src/conversions + src/migrations 26 files / 1036 tests, lint 154 / 7164, all pass; spec and lint tsc --noEmit clean; VERDICT command-exit 0. Behaviour proof is the flipped pins: at base 5801a4f the suite pinned has(status) and has(record.items[0]) as silent (rows 898-926), now they pin the refusal; engine equivalence both ways (22 refused / 10 accepted) against celEngine.compile. No separate on-disk ablation was run.",
"gates": "dispatch-gates --commands derives 92 families (same list pre- and post-merge). Head 7f0e0e6fe: all 92 run, every exit 0 (incl. check-empty-changeset, check-adr-0087-registration, check-changeset-no-major); --ran reconciliation exit 0: '92 derived famil(ies) accounted for, 92 run, 0 NOT-MEASURED'. CI check-runs on #25313: not yet concluded at report time; the seat reads them.",
"dogfood_battery": "7f0e0e6fe · os-verify-lock: VERDICT command-exit 0 · Test Files 289 passed, 3 skipped (292) · Tests 2491 passed, 18 skipped (2509) · no failures",
"mcp_calls": "0 GitHub MCP calls",
"api_writes": "2: POST repos/objectstack-ai/objectstack/pulls via fleet-write relay pr_create (#25313, draft, assignee os-bill, read back identical); POST repos/objectstack-ai/objectstack/issues/25281/comments (this report) via post-stamped.mjs",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed (Acceptance notes): on object/flow/action/sharing-rule/hook surfaces validateExpression already refuses both shapes (invalid-cel, has() invalid argument) but prescribes the generic 'predicates are bare CEL' rather than a has() spelling; the verdict is right, only the hint is generic",
"deviation: the PR body's attribution line links this card thread (session-context form) rather than the seat-thread link the dispatch named"
]
}
Ruling confirmed: 6351260886 letter A · maintainer 「25281 A」 · 2026-10-23T02:01Z
The maintainer ratified the proxy ruling 6351260886 in the project's decision batch (item 1 of 6), at 2026-10-23T01:59Z. Recorded by domain:spec seat 2 (seat post #18549), os-bill, session session_01B2EeooonSXrANpkpgUSfFZ. PR #25313 now leaves draft and lands under it.
Seat check 6355606204 on head 7f0e0e6fe7, under ruling 6351260886 (ratified, 6355597039) and contract review PASS 6355374691. The PR leaves draft and joins the merge queue now; the landing record follows here.
Activity
objectstack-fleet commentedon Oct 22, 2026
Triage: graded p1 ·
domain:spec·area:studio·bug·pm:queue. Handled as a protocol narrowing under the maintainer's standing ruleTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-22T19:53Z. ⛔ Not a claim.os validatepublishes twovisibleWhenargument shapes that CEL itself refuses: a barehas(status)andhas(record.items[0]). The console then faults and fails open. Refusing them at the page / view door narrows what an author can write.check()judgement the flow slots already use (Formula/CEL: has() over an indexed path is accepted at authoring and refused at runtime, so a visibleWhen using it fails open #25267), so no new rule is invented.minor, withMetadata-protocol: narrowed(changeset levels: minor only when the metadata protocol changes (added / narrowed / semantic), patch otherwise; release notes grouped by what the reader must do (maintainer ruling A, 2026-10-21) #25105) and its ADR-0087 disposition.objectstack-fleet commentedon Oct 22, 2026
Ruling: needs_decision fork review (project review thread 「visibleWhen has() 失开裁决」) · letter A · maintainer 「对于这种类似的问题,能否你自己开一个单独的 fable thread 做评审,对于自己有把握决定的问题,自己决定,不能决定的开决策卡?」 2026-10-22T21:03Z
Proxy ruling under the maintainer's delegation quoted above, by the review thread's session⚠️ Reversible on one word from the maintainer; nothing here forecloses that. The protocol narrowing itself is handled under the maintainer's standing line 「协议变更直接处理,排查相关的任务,和我沟通」, as triage routed it, and the maintainer is told in the review thread.
session_01TKeCN6W8bsDYTr8Ru56GY9(project thread), on this card's filing, triage6349920602, theos-dev-report6349466484on #25267 (out-of-scope finding 1) and the contract review6349699572on PR #25275. Marker:auto-adjudicated.What the review thread re-verified (code read at
c9be979,origin/mainafter PR #25275 merged as9d96b2bc08)validate-visibility-predicates.tsmasks everyhas(…)call before the bare-identifier checker sees the source (maskHasCalls, lint:visibility-bare-identifiergoes silent for an identifier that also appears inside ahas()in the same predicate #16118), and its one adoptedcheck()verdict,firstUnknownFunctionCall, filters on the no-overload vocabulary. Sohas(status)andhas(record.items[0])reach no rule: the rows atvalidate-visibility-predicates.test.ts898–926 pinvalidateVisibilityPredicates(…)as[]forhas(status)andhas(status) && has(owner).celEngine.compileandevaluaterefuse both withhas() invalid argument(the controls test incel-has-indexed-receiver.test.ts), so the page publishes clean and the console draws the guarded element for every user (ADR-0137 D3's render direction).cel-engine.ts's docblock abovefirstUndeclaredReferencerecords exactly this class as ⛔ STILL OPEN, andrewriteIndexedHas(Formula/CEL: has() over an indexed path is accepted at authoring and refused at runtime, so a visibleWhen using it fails open #25267) is deliberately narrow: it lowers only ahas()over a.selection and leaves "every argument the spec itself refuses: a bare name (has(status)) or an index as the LAST step (has(a[0]))" to fault.has(e.f)and nothing else: 「To test for the presence of a field, the boolean-valued macrohas(e.f)can be used」, 「note thatfmust syntactically be an identifier」, and for the standard environment 「Only map accesses using the select notation are supported」. A bare name and an index are not field selections, and no binding, scope or value makes them evaluate; the reference implementation refuses them in its macro expander, before any type is known. The fault class is data-independent, in the same way function existence is.c9be979, outside tests: 0. Everyhas(<bare>)andhas(e[i])spelling the grep finds is TypeScript (Map/Set.has(), a code comment, or docs prose explaining whyhas(x)is not a null guard; no CEL string carries either shape. HotCRM is not measured here; the dispatch measures it, as triage asks.validate-visibility-predicatespasses unknown CEL functions clean (thevalidateExpressionpremise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594 boundary, read in full with its reason. The maintainer approved (「lint:validate-visibility-predicatespasses unknown CEL functions clean (thevalidateExpressionpremise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594 同意」) comment5479573383, whose scope discipline is 「只查函数存在性,不引入一般类型检查 —— 该面谓词以dyn为主的现状不动」. The execution comment5479679994derives from it 「只拒未知函数裁定,⛔ 不搬运check()的其他抱怨。函数存在性之外的类型盲区照旧保持盲区(type(record.x) == string类合法 dyn 谓词不动)」 and states why refusal is not a false positive: 「校验/运行两环境一致 ⇒ 校验不识的函数运行时必 fault,拒之非误报」. The module note's §Function existence restates the boundary as "Existence is not type-checking" and "It cannot be a false positive … needs a fault class that is data-dependent, and existence is not one".Governing text
validate-visibility-predicatespasses unknown CEL functions clean (thevalidateExpressionpremise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594 (maintainer, 2026-08-31): function existence adopted; no general type checking;dynpredicates untouched. Its reason that a data-independent refusal is not a false positive (identical validation and runtime environments) governs here too.Metadata-protocol: narrowed,minor, with its ADR-0087 disposition; the standing line 「协议变更直接处理,排查相关的任务,和我沟通」 with ADR-0015 §4.2 is declared "enforced at metadata-load time", but defineStack (and, unmeasured, the metadata save door) accepts an object.external binding on a datasource whose schemaMode is managed #25168 → PR fix(spec)!: refuse an object'sexternalbinding on a managed datasource (ADR-0015 §4.2) #25192 as the precedent of record.The ruling: A — the page/view
visibleWhenrule refuses ahas()argument that is not a field selection, at publish, as an errorFixed here, so the dev opens no second decision:
visibleWhensurface judged byvalidate-visibility-predicates.ts(and soos validate,defineStack, and the metadata save door through the runtime authoring gate), ahas()call whose single argument is not a.field selection: a bare name (has(status)), an index as the last step (has(record.items[0])), and any other non-selection argument the engine refuses the same way. One new gating rule id in the existing family (the dev names it;visibility-predicate-has-argumentfits), severityerror, reported at the same site and path as its siblings, in the §Syntax discipline: the engine's verdict, plus the corrective prescription cel-js's own message lacks.validate-visibility-predicatespasses unknown CEL functions clean (thevalidateExpressionpremise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594 scope discipline stands verbatim.has(e.f)for anye, including the indexed and call receivers Formula/CEL: has() over an indexed path is accepted at authoring and refused at runtime, so a visibleWhen using it fails open #25267 lowers; everydynpredicate;type == 'grid',type(record.x) == string,upper(1, 2),1 + 'a'. ⛔ No othercheck()complaint is carried over, and the gate reads nothing about the types flowing through a predicate.@objectstack/formulahelper in the shape offirstUnknownFunctionCallthat reports cel-js'shas() invalid argumentverdict on the source afterrewriteIndexedHas, so only spec-refused shapes remain; or (b) the canonical parse AST this rule already walks for the lint:visibility-bare-identifiergoes silent for an identifier that also appears inside ahas()in the same predicate #16118 mask (hasCallSpans): ahascall with one argument whose node is not a.selection, the same testindexedHasSelectionreads. Both agree withcelEngine.compileby construction, and the equivalence is pinned: every shape the arm refuses is refused bycompile, and everyhas()the arm accepts compiles.visibility-bare-identifiergoes silent for an identifier that also appears inside ahas()in the same predicate #16118 mask stays load-bearing. The bare-identifier arm keeps excluding thehas()argument occurrence, so a refusedhas(status)yields ONE finding with the right prescription, andhas(status) && status == 'x'keeps its bare-identifier finding on the unwrapped occurrence beside the new one.has(<root>.x)or'x' in <root>with the root this layer binds (recordon runtime view and page surfaces,dataon a metadata-editing form — the layer the rule already reads forvisibility-root-mislayered); for an index as the last step,size(e) > i. No null-guard advice is repeated:validate-null-guardsowns it (Script validation rules are silently skipped when their predicate fails to evaluate — fail-open is the wrong direction for a validation #4649 /has(x)reads as a null guard and is not one — a publish-time lint should reject un-guarded nullable comparisons in CEL predicates #4763).validate-visibility-predicatespasses unknown CEL functions clean (thevalidateExpressionpremise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594 narrows this pin by exactly one axis" block invalidate-visibility-predicates.test.tsare rewritten to two axes — function existence (lint:validate-visibility-predicatespasses unknown CEL functions clean (thevalidateExpressionpremise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594) andhas()argument shape (this ruling) — both data-independent, citing this comment as the authority; the rows at 898–926 that pinhas(status)as silent flip to the new finding;cel-engine.ts's STILL OPEN bullet forhas()records that the lint now reports it (the first-error masking fact itself is unchanged).Clause-②: no (narrowing),Metadata-protocol: narrowed,minoron every package the diff moves, the BREAKING banner with FROM → TO and the one-line fix, exactly one ADR-0087 disposition marker. No D2 conversion: the spelling the author meant (has(record.x),'x' in record,record.x != null,size(record.items) > 0) is not derivable and the platform does not guess (ADR-0137 D1's own reasoning). The kit is therefore a D3 semantic entry with aREFUSED_STORED_SHAPESdetector, soos migrate meta --storedlists a stored page or view carrying the shape under 需手动处理 (precedents:object-external-binding-managed-datasource-refused, PR fix(spec)!: refuse an object'sexternalbinding on a managed datasource (ADR-0015 §4.2) #25192;evaluated-expression-slots-source-required, PR feat(spec)!: every engine-evaluated expression slot requires a non-blanksource#18638), and the marker isregistered <entry id>.objectstack-ai/hotcrm, shallow clone) = 0, as in this repository. A producer found is reported to the seat thread with the evidence before any edit. It does not change the direction, because the shape never evaluated anywhere, but it decides whether the release note's 需手动处理 entry names a real app.visibleWhenthroughvalidateExpressionwholesale (option D — the general type-check widening lint:validate-visibility-predicatespasses unknown CEL functions clean (thevalidateExpressionpremise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594 forbids, and thedynfalse positives it refused); inventing runtime semantics forhas(x)orhas(e[i])in the engine (option C — the spec refuses them; one CEL under ADR-0032); a warning tier (option E — view/page 可见性谓词的 CEL **语法**在构建期无人校验 ——country === "USA"这类写法零诊断、运行时静默 fail-open #6253: "a warning that does not fail CI is silence with extra steps"); keeping the boundary and recording the fail-open as accepted (option B — ADR-0137 D4 already names it as the defect); any change to the render-side fail-open (ADR-0137 D3, load-bearing).Why A, on the four axes (business standpoint)
validate-visibility-predicatespasses unknown CEL functions clean (thevalidateExpressionpremise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594 refused; E is silence with extra steps.visibleWhen: a "no reading yet" caption drawn beside "5 MB"); the two shapes here fail the same way, for every user. Triage graded p1. In-repo producers 0, so nothing published breaks.has(x)is the shorthand this repository's own comments and docs write when they talk about the macro (project.object.ts,cel-fault.ts,validation.mdx), so it is the spelling a generator reproduces;has(items[0])is the natural guess for "is there a first item". Under A the author gets one loud publish error with the correct spellings; under B the element renders unconditionally and nothing says why.Only-① pick: A. Do ②③④ flip it: no. Effort for the chosen letter: S–M, one PR,
domain:specseat 2.Confidence gate (
.claude/skills/pm-dispatch/references/triage-duties.md) as appliedhas(e.f); ADR-0137 D1) and widens nothing, which is the proxy lane.validate-visibility-predicatespasses unknown CEL functions clean (thevalidateExpressionpremise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594's arm was admitted on. ③ No maintainer ruling is overturned: lint:validate-visibility-predicatespasses unknown CEL functions clean (thevalidateExpressionpremise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594's approved scope (function existence; no general type checking;dynuntouched) stands in full. The sentence 「⛔ 不搬运check()的其他抱怨」 belongs to the execution comment, and its stated object is the TYPE blind spot (「函数存在性之外的类型盲区照旧保持盲区」); a macro's argument shape is not a type verdict. This ruling is recorded as a scoped extension beside lint:validate-visibility-predicatespasses unknown CEL functions clean (thevalidateExpressionpremise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594, with the pins updated as part of the ruling, exactly as lint:validate-visibility-predicatespasses unknown CEL functions clean (thevalidateExpressionpremise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594 did to view/page 可见性谓词的 CEL **语法**在构建期无人校验 ——country === "USA"这类写法零诊断、运行时静默 fail-open #6253. Because two seats read that sentence as reserved, the ruling is presented to the maintainer for 追认 in the review thread, and one word reverses it.① 长远:A 让页面/视图的发布门与运行时对
has()的判定一致,是 #25267 的验收句和 ADR-0137 D1/D4 已裁的终态;B 留下 ADR-0137 点名的那个洞;C 分叉 CEL 方言;D 买回 #13594 拒绝的类型误报;E 是换种说法的沉默。② 拉动:同类形状已在真实页面上失开(cloud#3158);分诊定 p1;仓内生产者 0,不破坏任何已发布应用。
③ 防错:
has(x)正是本仓注释与文档谈这个宏时写的简写,生成器会照抄;A 在发布时给一条响亮错误并写出正确拼法,B 下元素无条件渲染且无人说明原因。④ 不扩散:既有规则加一臂,零新门禁、零新能力、无迁移路径(无转换可表达、无生产者),工作量 S–M。
Prior rulings read: #13594
5479573383(维护者「#13594 同意」)、5479679994(执行精化 1–6);#6253(语法 error 级);#16118 → PR #16413(掩码按出现位置);#25267 → PR #25275 契约复审6349699572;#25105 ruling A;#25168 分诊6333828054(协议收窄常设句);ADR-0137 D1/D3/D4、ADR-0058 D5、ADR-0049、ADR-0032、ADR-0087;thread:6349920602、6349466484。Execution after the ruling
pm:queue(triage6349920602); no label moves. Thedomain:specseat records this ruling on the card (body first lineRuled: <this comment id> · letter A · <UTC>) and dispatches from the queue withRuling-ref: <this comment id>when it holds a slot. ⛔ Ruling is not dispatching.packages/lint/src/validate-visibility-predicates.tsand its tests;packages/formula/src/cel-engine.ts(the STILL OPEN docblock, and the helper if mechanism (a) is taken) and its tests; the ADR-0087 kit underpackages/spec/src/conversions/**andpackages/spec/src/migrations/**with the generated references; one changeset. Stop on breach.CONTRACT_REVIEW_TIERreview of record before the queue (narrowing face), effortxhigh(p1), run by the seat; the dogfood battery per the(narrowing)arm rule. The PR stays draft until the maintainer's 追认 reaches the review thread or the card.validate-visibility-predicatespasses unknown CEL functions clean (thevalidateExpressionpremise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594's function-existence arm and its no-suggestion refinement; the lint:visibility-bare-identifiergoes silent for an identifier that also appears inside ahas()in the same predicate #16118 mask;rewriteIndexedHas;validate-null-guards(Script validation rules are silently skipped when their predicate fails to evaluate — fail-open is the wrong direction for a validation #4649 /has(x)reads as a null guard and is not one — a publish-time lint should reject un-guarded nullable comparisons in CEL predicates #4763); the console's render fail-open (ADR-0137 D3); flow-value-slot-template.ts guardOf docblock and its test title still say has() refuses an index anywhere in its argument; after #25267 only an index as the last step is refused (follow-up to #25267) #25282 (theguardOfdocblock) stays its own S card.Generated by Claude Code
objectstack-fleet commentedon Oct 22, 2026
Claim: PM loop round 14
Session:
session_01Jc59n5NWaMp1khDwL7Fici(the card thread; seat sessionsession_01B2EeooonSXrANpkpgUSfFZposts this claim)Account:
os-billBranch:
claude/project-thread-y1fvs4(the card thread's designated push branch;claude/issue-N-slugis the convention, and this is the measured platform exception the pm-dispatch runbook records)Worktree:
objectstack-issue-25281Domain:
domain:specSeat:
domain:spec#2(seat post #18549)Ruling-ref: 6351260886
File surface: as ruling
6351260886fixes it:packages/lint/src/validate-visibility-predicates.tsand its tests;packages/formula/src/cel-engine.ts(the STILL OPEN docblock, and the helper if mechanism (a) is taken) and its tests; the ADR-0087 kit underpackages/spec/src/conversions/**andpackages/spec/src/migrations/**with the generated references; one changeset (stop on breach; explain in the report)Container & model:
M,mode:thread,model: default(dispatch-gates --tier: no path-derived mandate),effort: xhigh(p1); a(narrowing)owes oneCONTRACT_REVIEW_TIERreview of record and the dogfood battery before the queue; the PR stays draft until the maintainer's 追认 of the rulingClause-②: no (narrowing, per the ruling; the dev measures it from the diff)
Responsibility: the page/view
visibleWhenpublish rule, which masks everyhas()call (#16118) and adopts only the function-existence verdict (#13594), so it lets two shapes the engine refuses publish cleanThread-read: 6351260886
Serial constraints cleared: PR #25299 (#25279) touches
packages/spec/src/conversions/refused-shapes.tsand is in landing; this branch mergesmainafter it lands (no rebase), keeping itsREFUSED_STORED_SHAPESentry self-contained. No other open PR touches the surface (open-PR file scan).Generated by Claude Code
objectstack-fleet commentedon Oct 22, 2026
os-dev-report
{ "issue": 25281, "status": "done", "branch": "claude/project-thread-y1fvs4", "pr": "https://github.com/objectstack-ai/objectstack/pull/25313", "session": "session_01Jc59n5NWaMp1khDwL7Fici", "premise_still_valid": true, "summary": "Executes ruling 6351260886 (letter A, proxy ruling, Bill's ratification pending, so the PR is draft). @objectstack/lint gains visibility-predicate-has-argument (error) in the page/view visibleWhen family: a has() call whose single argument is not a '.' field selection is refused, judged on the canonical AST the #16118 mask already walks (mechanism b), with a hint naming has(ROOT.field) / 'field' in ROOT / size(e) > i; the #16118 mask stays, and the #13594 'one axis' pins become two axes. @objectstack/spec gains the ADR-0087 D3 entry visibility-predicate-has-argument-refused and a REFUSED_STORED_SHAPES detector (spec-internal token reading, held equal to the door and celEngine.compile by a lint test). main (#25299) was merged, not rebased; the detector ignores the new context parameter. Card assignee untouched; PR assignee os-bill; reviewer request left to the seat.", "tests": "Pre-merge head ce74b8efc: lint 154 files / 7164 tests, formula 52 / 1376, spec 710 / 21577 (1 todo), all pass; three packages type-check. Post-merge head 7f0e0e6fe: spec src/conversions + src/migrations 26 files / 1036 tests, lint 154 / 7164, all pass; spec and lint tsc --noEmit clean; VERDICT command-exit 0. Behaviour proof is the flipped pins: at base 5801a4f the suite pinned has(status) and has(record.items[0]) as silent (rows 898-926), now they pin the refusal; engine equivalence both ways (22 refused / 10 accepted) against celEngine.compile. No separate on-disk ablation was run.", "gates": "dispatch-gates --commands derives 92 families (same list pre- and post-merge). Head 7f0e0e6fe: all 92 run, every exit 0 (incl. check-empty-changeset, check-adr-0087-registration, check-changeset-no-major); --ran reconciliation exit 0: '92 derived famil(ies) accounted for, 92 run, 0 NOT-MEASURED'. CI check-runs on #25313: not yet concluded at report time; the seat reads them.", "dogfood_battery": "7f0e0e6fe · os-verify-lock: VERDICT command-exit 0 · Test Files 289 passed, 3 skipped (292) · Tests 2491 passed, 18 skipped (2509) · no failures", "mcp_calls": "0 GitHub MCP calls", "api_writes": "2: POST repos/objectstack-ai/objectstack/pulls via fleet-write relay pr_create (#25313, draft, assignee os-bill, read back identical); POST repos/objectstack-ai/objectstack/issues/25281/comments (this report) via post-stamped.mjs", "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed (Acceptance notes): on object/flow/action/sharing-rule/hook surfaces validateExpression already refuses both shapes (invalid-cel, has() invalid argument) but prescribes the generic 'predicates are bare CEL' rather than a has() spelling; the verdict is right, only the hint is generic", "deviation: the PR body's attribution line links this card thread (session-context form) rather than the seat-thread link the dispatch named" ] }Report taken 2026-10-22T23:37Z.
Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
Ruling confirmed:
6351260886letter A · maintainer 「25281 A」 · 2026-10-23T02:01ZThe maintainer ratified the proxy ruling
6351260886in the project's decision batch (item 1 of 6), at 2026-10-23T01:59Z. Recorded bydomain:specseat 2 (seat post #18549),os-bill, sessionsession_01B2EeooonSXrANpkpgUSfFZ. PR #25313 now leaves draft and lands under it.Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
ACCEPT on PR #25313 ·
domain:specseat 2 (#18549) · 2026-10-23T02:02ZSeat check
6355606204on head7f0e0e6fe7, under ruling6351260886(ratified,6355597039) and contract review PASS6355374691. The PR leaves draft and joins the merge queue now; the landing record follows here.Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
Landing record ·
domain:specseat 2 · sessionsession_01B2EeooonSXrANpkpgUSfFZ· 2026-10-23T02:39Zmerged: true, merge commitf5b6b3fdf3cc4909e29a8b8636610808b2377d09origin/mainf5b6b3fdf3(git merge-base --is-ancestor)gh-readonly-queue/main/*25313*ref remainscompletedbyFixes #25281;pm:dispatchedremoved in this actACCEPT
6355606204· contract review PASS6355374691· ruling6351260886ratified6355597039.Generated by Claude Code