Skip to content

record form + modal form: an object field visibleWhen naming current_user.hasCapability faults (AppContent / RecordFormPage evaluators omit systemPermissions), while page predicates answer (objectui#13006 follow-up) #13083

Closed

Description

@objectstack-fleet

Filed by domain:ui seat 2 (seat post #9771, session session_01XVp19E1MYUTFM3dnkwz8gB) from objectui#13006's dev report and its contract review (PR #13081). ⛔ Not a claim.

Measured (code read at PR #13081's head 2c0b003d70)

objectui#13006 makes current_user.hasCapability('NAME') answer on page predicates. The ExpressionProvider scope binds the viewer's systemPermissions through objectui#4421's symbol carrier, and @object-ui/core's evalFieldPredicate hands it to the engine.

Two surfaces build their object-field evaluator by hand and do not pass the list:

  • packages/app-shell/src/console/AppContent.tsx;
  • packages/app-shell/src/views/RecordFormPage.tsx.

Both call createExpressionEvaluator({ user, features, permissions: expressionPermissions }). So an object field's visibleWhen naming hasCapability faults on the record form and the modal form, while page nodes, nav and actions answer. The fault is loud (reported), never a silent false. It is exactly the per-surface hand-off the objectui#4421 comment in ExpressionProvider warns about.

Target

  • Each site reads useExpressionSystemPermissions() beside useExpressionPermissions() and passes it through.
  • Pin: a field visibleWhen: "current_user.hasCapability('X')" shows for a holder and hides for a non-holder, on the record form and on the modal form.
  • Control: not loaded still faults loudly.

Dedupe

Searched objectui for "hasCapability createExpressionEvaluator RecordFormPage systemPermissions": 0 open hits.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: p2 · bug · lane domain:ui · area:access · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-19T21:56Z

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.

    Filing-gate answers completed under ruling 「B」 (objectstack-ai/objectstack#24589).

    • Category ①: a named defect with the producer read in code.
    • Reader who acts: the objectui domain:ui execution seat, in one PR on the two files below.
    • Dedupe: MCP search_issues "hasCapability record form modal form visibleWhen systemPermissions createExpressionEvaluator RecordFormPage AppContent", objectui, closed included: 11 hits.
      • The nearest are objectui#13006 (closed, this card's parent, page predicates) and objectui#6515 (closed, RecordFormPage's hand-rolled current_user).
      • None covers the missing systemPermissions hand-off.

    Fresh read (objectui origin/main b0176214a9). Both evaluators pass only { user, features, permissions }:

    • packages/app-shell/src/console/AppContent.tsx:955;
    • packages/app-shell/src/views/RecordFormPage.tsx:220.

    Neither reads useExpressionSystemPermissions(). The premise holds.

    Why p2. It is a consumer follow-up of objectui#13006 (p2). The fault is loud (reported), never a silent false, so it is a tax, not a wall (filing-gate.md:40).


    Generated by Claude Code

  2. added
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Oct 19, 2026
  3. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 14 (priority:p2)
    Session: session_01XVp19E1MYUTFM3dnkwz8gB
    Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-13083-form-evaluator-capabilities
    Worktree: objectui-issue-13083
    Domain: domain:ui
    Seat: domain:ui#2
    File surface:

    • packages/app-shell/src/console/AppContent.tsx (the modal form's object-field evaluator, :955 on main b0176214a9);
    • packages/app-shell/src/views/RecordFormPage.tsx (the record form's evaluator, :220);
    • each reads useExpressionSystemPermissions() beside useExpressionPermissions() and passes it to createExpressionEvaluator, the way objectui#13006's page scope does;
    • tests beside them;
    • one .changeset/13083-form-evaluator-capabilities.md (@object-ui/app-shell patch).

    ⛔ No new export, and no change to @object-ui/core's evaluator. Stop on breach; explain in the report.
    Container & model: S, mode:subagent, model: opus (default tier; no path-derived mandate over this surface)
    Clause-②: no (two internal call sites hand an existing value to an existing parameter; no export, key or prop moves)
    Responsibility: two app-shell surfaces build their object-field evaluator by hand without the viewer's systemPermissions | objectui#13006's ExpressionProvider scope already binds it, and useExpressionSystemPermissions() exposes it | every record form and modal form whose object field's visibleWhen names current_user.hasCapability (loud fault today)
    Thread-read: 6278267726
    Serial constraints cleared: objectui#13006 (2493e85) landed. No open objectui PR touches these files (read at 22:24Z: #13092, #13028, #11845, #11844, #11600).
    Claimed at: 2026-10-19T22:24Z


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 13083,
      "status": "done",
      "branch": "claude/issue-13083-form-evaluator-capabilities",
      "pr": "https://github.com/objectstack-ai/objectui/pull/13094",
      "session": "session_01XVp19E1MYUTFM3dnkwz8gB",
      "premise_still_valid": true,
      "summary": "Premise held on origin/main 6058bf8b71: RecordFormPage and AppContent each built their object-field evaluator with createExpressionEvaluator({ user, features, permissions }) and no systemPermissions. Each site now reads useExpressionSystemPermissions() beside useExpressionPermissions() and passes it as systemPermissions, the same name and shape ExpressionProvider uses; not-loaded stays undefined, so the predicate still faults loudly. Hand-built createExpressionEvaluator sites under packages/app-shell/src (non-test): ExpressionProvider's own scope (already passes systemPermissions), useExpressionContext's no-provider default (user+features only, deliberately unbound outside a provider), RecordFormPage, AppContent. No third site carries the omission. File surface as claimed: the two .tsx, two new tests beside them, .changeset/13083-form-evaluator-capabilities.md (app-shell patch). Zero label writes: the dispatch named none and objectui has no gate reading dev-applied labels.",
      "tests": "Head 73bc2d5e84. New: RecordFormPage.hasCapability-13083.test.tsx + console/__tests__/AppContent.hasCapability-13083.test.tsx, real components under a real MePermissionsProvider (AppContent mounted, not an evaluator stand-in): HOLDER shown+silent, NON-HOLDER hidden+silent, CONTROL not-loaded shown by the fail-open field gate with the fault reported naming the predicate  to  2 files, 6 passed. Ablation (fix committed first at 0ae227d904; scripts/ablation-replace.mjs wrap mode, anchor 'systemPermissions: expressionSystemPermissions,' x1 to x0): RecordFormPage blob 7358a6fba6d4 to d8e9897a7276, its HOLDER and NON-HOLDER red, other 4 green, restored blob == HEAD 7358a6fba6d4 + git diff HEAD empty; AppContent blob 787a250edc4d to 3587782483d2, its HOLDER and NON-HOLDER red, other 4 green, restored blob == HEAD 787a250edc4d + git diff HEAD empty. Direction as predicted (HOLDER red via silence assertion). No dist build needed: tests import the components from src. Family (tests importing/reading the two files + hasCapability-13006 + currentUserCan-4421): 50 files in 3 batches, 413 passed, --maxWorkers=1 under os-verify-lock. Closure build pnpm --filter '@object-ui/app-shell^...' build exit 0; pnpm --filter @object-ui/app-shell type-check (tsc --noEmit && tsc -p tsconfig.test.json) exit 0 on head, --listFilesOnly shows both new tests in tsconfig.test.json. Full packages/app-shell dir: declared to CI (dispatch bars whole-package runs).",
      "gates": "all exit 0, run on the final tree: check:phantom-deps, check:self-import, check:unreferenced-sources, check:doc-example-readers, check:handler-key-reads, check:side-effects-array, check:element-data-source-declaration, check:spec-symbols, check:action-forward-parity, check:designer-field-key-parity, check:icon-record-names, check:i18n-keys, check:i18n-drift, check:i18n-designer-parity, check:control-bytes, scripts/check-hand-rolled-comment-mask.mjs, scripts/check-changeset-fixed.mjs, scripts/check-changeset-presence.mjs, check:new-line-citations (0 new). Lint narrowed: eslint --no-inline-config --format json on the 4 changed files = 4 files, 0 errors; new tests 0 warnings; the 2 production files carry only pre-existing warnings, none on a changed line. Population is the 4 changed files named by git diff 6058bf8b71..HEAD; eslint.config.js declares no projectService/parserOptions.project, so linting is not type-aware and the diff cannot move any untouched file's verdict. CI: in_progress.",
      "files_changed": [
        "packages/app-shell/src/console/AppContent.tsx",
        "packages/app-shell/src/views/RecordFormPage.tsx",
        "packages/app-shell/src/views/RecordFormPage.hasCapability-13083.test.tsx",
        "packages/app-shell/src/console/__tests__/AppContent.hasCapability-13083.test.tsx",
        ".changeset/13083-form-evaluator-capabilities.md"
      ],
      "deviations": "none. The order asked for each touched package's full dir under the lock and also barred whole-package runs; the bar was followed and the app-shell dir declared to CI.",
      "mcp_calls": "0",
      "api_writes": "2 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches): (1) pr_create  to  POST /repos/objectstack-ai/objectui/pulls (draft #13094) + POST /repos/objectstack-ai/objectui/issues/13094/assignees [baozhoutao], read back 5497/5497 bytes identical; (2) comment  to  POST /repos/objectstack-ai/objectui/issues/13083/comments (this report). Plus git pushes of the branch (not REST).",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #13094 @ 73bc2d5e84e19c14fbe5e5a87fc4b5fb2fba4b5a · domain:ui seat 2 (session_01XVp19E1MYUTFM3dnkwz8gB)

    • Clause-②: no. Two internal call sites hand an existing value to an existing parameter, with no export moved. No contract review is owed, and the seat read the diff.
    • Seat review:
      • AppContent.tsx and RecordFormPage.tsx each read useExpressionSystemPermissions() and pass it as systemPermissions to createExpressionEvaluator, with the dependency added to each useMemo. That is the same name and shape objectui#13006's ExpressionProvider uses. Not loaded stays undefined, so the predicate still faults loudly.
      • The report enumerates all four hand-built evaluator sites. The provider's own site already passes the list, and the no-provider default deliberately binds no permissions, so no third site has the omission.
    • Pins:
      • Both surfaces are mounted for real under a real MePermissionsProvider, with holder shown, non-holder hidden, and a not-loaded control that still faults.
      • The ablation per site turns that site's two legs red, with each restore proven by blob.
    • Changeset: @object-ui/app-shell patch.
    • CI: all 36 check-runs completed on the head, 33 passed and 3 skipped. Mergeable state: clean.
    • Path fork: no governed path, 392 changed lines. Seat landing.
    • Findings: none.

    Next: ready + auto-merge; landing is verified by content on main.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 2 · session_01XVp19E1MYUTFM3dnkwz8gB

    pm:dispatched is removed.


    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:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions