Skip to content

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

Closed

Description

@objectstack-fleet

Ruled: 6351260886 · letter A · 2026-10-22T21:04Z

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-report 6349466484 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.
  • These are the two argument shapes the CEL spec itself refuses (a bare name; an index as the last step). PR fix(formula): has() over an indexed or call receiver evaluates as the CEL spec defines it #25275 fixes every other has() receiver shape; it leaves these two refused, as it should.
  • 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.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    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.

  2. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    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-report 6349466484 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)

    Governing text

    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:

    1. 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.
    2. What is not refused — the lint: validate-visibility-predicates passes unknown CEL functions clean (the validateExpression premise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594 scope discipline stands verbatim. has(e.f) for any e, 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; every dyn predicate; type == 'grid', type(record.x) == string, upper(1, 2), 1 + 'a'. ⛔ No other check() complaint is carried over, and the gate reads nothing about the types flowing through a predicate.
    3. 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.
    4. The lint: visibility-bare-identifier goes silent for an identifier that also appears inside a has() in the same predicate #16118 mask stays load-bearing. The bare-identifier arm keeps excluding the has() argument occurrence, so a refused has(status) yields ONE finding with the right prescription, and has(status) && status == 'x' keeps its bare-identifier finding on the unwrapped occurrence beside the new one.
    5. Prescription text names the shape the spec admits and the equivalent spellings, with no tracker number in the string: for a bare name, has(<root>.x) or 'x' in <root> with the root this layer binds (record on runtime view and page surfaces, data on a metadata-editing form — the layer the rule already reads for visibility-root-mislayered); for an index as the last step, size(e) > i. No null-guard advice is repeated: validate-null-guards owns 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).
    6. Pins updated as part of this ruling, ⛔ not silently. The module note's §Function existence point 2 and the "lint: validate-visibility-predicates passes unknown CEL functions clean (the validateExpression premise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594 narrows this pin by exactly one axis" block in validate-visibility-predicates.test.ts are rewritten to two axes — function existence (lint: validate-visibility-predicates passes unknown CEL functions clean (the validateExpression premise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594) and has() argument shape (this ruling) — both data-independent, citing this comment as the authority; the rows at 898–926 that pin has(status) as silent flip to the new finding; cel-engine.ts's STILL OPEN bullet for has() records that the lint now reports it (the first-error masking fact itself is unchanged).
    7. Declarations and kit, per ruling A on 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 the 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 precedent: Clause-②: no (narrowing), Metadata-protocol: narrowed, minor on 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 a REFUSED_STORED_SHAPES detector, so os migrate meta --stored lists a stored page or view carrying the shape under 需手动处理 (precedents: object-external-binding-managed-datasource-refused, PR fix(spec)!: refuse an object's external binding 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-blank source #18638), and the marker is registered <entry id>.
    8. 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.
    9. ⛔ Not: routing page/view visibleWhen through validateExpression wholesale (option D — the general type-check widening lint: validate-visibility-predicates passes unknown CEL functions clean (the validateExpression premise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594 forbids, and the dyn false positives it refused); inventing runtime semantics for has(x) or has(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)

    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

    ① 长远: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


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    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).


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  5. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    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.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT on PR #25313 · domain:spec seat 2 (#18549) · 2026-10-23T02:02Z

    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.


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    Landing record · domain:spec seat 2 · session session_01B2EeooonSXrANpkpgUSfFZ · 2026-10-23T02:39Z

    Probe Reading
    PR #25313 merged: true, merge commit f5b6b3fdf3cc4909e29a8b8636610808b2377d09
    origin/main carries f5b6b3fdf3 (git merge-base --is-ancestor)
    Queue branch no gh-readonly-queue/main/*25313* ref remains
    Card closed completed by Fixes #25281; pm:dispatched removed in this act

    ACCEPT 6355606204 · contract review PASS 6355374691 · ruling 6351260886 ratified 6355597039.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:studioChanging a running app without code — authoring, publish, docs and the portalbugSomething isn't workingdomain:specpm:dispatchedpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions