Skip to content

claim_case's visible cannot be made TOTAL without moving the sharing grant and its parity pin together — the one site #1890's sweep left unguarded #1896

Description

@os-project-manager

Blocked-by: #1874

Filed by the repo:hotcrm seat out of #1890's review (PR #1894), rather than ridden on that PR. 1 of 12 sites, deliberately excluded there with the mechanism written at both code sites.

The site

src/actions/case.actions.ts, ClaimCaseAction:

visible: P`record.owner_id == null && record.status != "resolved" && record.status != "closed"`,

Every other action visible in src/actions/ is now TOTAL per AGENTS.md → Validation predicates must be TOTAL (#630). This one is not, and ⛔ cannot be made so in isolation.

Why the obvious fix is wrong

Three facts, each measured on #1890's branch:

  1. The predicate is the sharing grant's text, verbatim. case_unassigned_triage_sharing's criteria condition and this visible are the same string, and test/claim-case-one-owner-writer.test.ts asserts string equality of the two. Changing one without the other reds that pin.
  2. The grant cannot carry has(). plugin-sharing compiles a criteria condition through compileCelToFilter, which rejects the whole function-call class. So has(record.owner_id) && … is untranslatable as a sharing rule.
  3. ⛔ An untranslatable rule is DROPPED by the seeder (Both territory sharing rules are silently dropped at seed — in [...] conditions are untranslatable, so na_sales_team / eu_sales_team get no access at all #621) — silently. So a naive guard here does not fail loudly; it deletes the sharing grant, which is a security-visible outcome, not a lint nit.

⇒ making this predicate total means moving the grant, the action and the parity pin together. That is a deliberate change to a sharing boundary, ⛔ not a sweep item.

⭐ The reasoning already written in the tree does not cover this surface

src/sharing/case.sharing.ts explains why status needs no guard there: it is required with storage: { notNull: true } on the stored row.

⚠️ That is true for a pushdown filter and false for this surface. An action visible is interpreted by CEL in the browser, against whatever the page holds — and #1890 measured that to be {} (zero keys) for the first two renders of every record page. So the same string is total in one place and aborts in the other, for reasons neither site's comment states. Both files now carry a note saying so; this card is the work.

Measured consequence today

Small, and stated rather than inflated: the console prints one predicate-failure warning for claim_case per JS context (the warning is de-duplicated per [label, expression], so it means at least one failing render, never exactly one). Steady-state gating is unaffected — object-ui passes fallback: false, so the button is hidden on the aborting renders, which is where it should be during hydration anyway.

⇒ p3. The defect is the declared-contract violation and the trap it leaves for the next author, ⛔ not a user-facing misbehaviour.

Why pm:blocked rather than pm:queue

The parity pin lives in test/, which is epic #1579's fence, and this lane's rule is that the fence opens only on a maintainer or director sentence. ⭐ This is the same question as #1874. ⚠️ Correction to this line as first written: the live reverse index reads 11 carriers including this card and #1897 — ⛔ not the "eleven" this card claimed at filing time, which was one ahead of the derivation.

⚠️ Whoever takes it should note the change is not purely mechanical: it touches a sharing grant, and "security / permission boundaries" sit on the manual floor. The likely shape is that the grant keeps its untranslatable-free text and the action's predicate is allowed to differ from it, with the parity pin rewritten to assert the intended relationship rather than string equality — but ⛔ that is a proposal, not a ruling, and it is not made here.

Refs #1890 (the sweep that excluded it) · PR #1894 (the exclusion and its comments) · #1874 (the fence) · #621 (untranslatable rules are dropped) · #630 (the totality rule).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    repo:hotcrm seat, session_01X8U3asekbiC7yWoEPWR4Dg · stock re-triage group 6 (maintainer-confirmed ten-card group; maintainer reply verbatim: 「同意」) · 2026-09-25T04:32Z

    Blocked-by: #1874 is a dead pointer (404; consistent with a banned author), and the test/** fence it carried does not hold today (the epic PM's territory is its sub-issue tree only).

    Closed not_planned

    The measured consequence is one console warning; the button is correctly hidden while the record hydrates. Making visible total needs a platform change, not app metadata.


    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

No one assigned

    Labels

    bugSomething isn't workingmetadataDeclarative metadata — schema, security posture, UI surfacespriority:p3

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions