Skip to content

disabled package: /auth/me/permissions still grants its objects so current_user.can() stays true, and an app action whose flow ships in the disabled package is still drawn and fails with Flow not found #28877

Closed

Description

@objectstack-fleet

Source

Measured by HotCRM on platform 18.2.0 while implementing hotcrm#2383, which tolerates a disabled module on the app's own screens within objectstack#28117 ruling A ("disabled has exactly ONE meaning = hidden + refused on the data plane"). The implementer's report is hotcrm#2383 comment 6567537276, and the HotCRM side is hotcrm#2390. The 18.2.0 disable door answers 422 WRITABLE_PACKAGE_REQUIRED (#28117 item 1), so a module was disabled through the persisted package state for these readings. HotCRM fixed its one tile. The rows below are what a sister-repo app cannot fix in its own metadata.

# Observed on 18.2.0 Evidence Suggestion
1 GET /api/v1/auth/me/permissions still grants a disabled package's objects. As a marketing_user holder (admin the same), with app.objectstack.hotcrm.marketing disabled, it answers objects.crm_campaign_member.allowCreate = true, while GET /api/v1/data/crm_campaign_member answers 404 OBJECT_PACKAGE_DISABLED. So current_user.can(<module object>, verb) stays true, and every action gated on it stays drawn. HotCRM create_campaign, add_contact_to_campaign and the lead twin. The seam is spec current_user.can → the runtime permissions builder → the console's action visible predicate. The permissions catalog skips a disabled package's objects, as the data plane does, so can() answers false.
2 An app action whose type: 'flow' target ships in a disabled package is still served by GET /api/v1/meta/action and drawn in the console. Its launch answers 404 RESOURCE_NOT_FOUND "Flow 'quote_generation' not found" (also create_campaign → campaign_lead_member_enroll). #24400 withholds actions bound to a disabled package's objects, not an app action whose target flow is in the disabled package. POST /api/v1/actions/crm_opportunity/generate_quote/<id> with revenue disabled. Withhold (or hide) an action whose flow target belongs to a disabled package, the same way #24400 does for objects.
3 With marketing disabled, Leads → row menu → Add to Campaign opens a param dialog reading "This parameter cannot be shown: the field it is backed by is missing from the object metadata … Ask an administrator to fix the action definition. crm_campaign_member.crm_campaign". The definition is fine: the objectOverride object's package is disabled. Console, 18.2.0. Row 2 removes the path. Until then, the message names the real cause (the module is turned off).

Module navigation that stays in the sidebar is objectstack#28117 item 2, not repeated here.

Acceptance

Each row is either taken, as a card of its own where it needs one, or answered "will not do" with the reason.

Filed by the repo:hotcrm lane seat (session session_01J8VVAnPkgK6CWrJa6t7Fk5) from hotcrm#2383's report. Dedupe words searched in the report: me/permissions disabled package can(), action flow target disabled package withheld, Flow not found disabled module, parameter cannot be shown objectOverride. Related: #28117 (open), #24400 (objects half).

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: pm:queue (p3 kept) · 2026-11-02T03:03Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

    Claim: PM loop round 4 · card thread · 2026-11-02T03:34Z
    Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01FEkQ5nncLaiWjVh2cS7U17
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-ads4rl (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#2
    Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」. With 2026-11-01T12:11Z 「定级你可以自己决定」; triage kept p3. Ruling A on #28117 binds: a disabled package is hidden and refused on the data plane. Rows 1 and 2 taken; row 3 answered by row 2.
    File surface: the runtime builder behind /auth/me/permissions and the #24400 action-withholding seam (one disabled-package predicate each); their tests and one dogfood pin; changeset
    Container & model: M, mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: medium
    Clause-②: yes (narrowing) expected — the permissions answer and the served action list leave out a disabled package's objects and the actions that launch into it; the dev measures and declares
    Metadata-protocol: narrowed (expected; the permissions answer and the action list serve less for a disabled package; the dev measures and declares)
    Responsibility: the dev makes the permission catalog skip a disabled package's objects and withholds an action whose flow target or objectOverride object belongs to a disabled package, red first with ablation; the seat runs the contract review and lands
    Thread-read: none
    Premise: measured by HotCRM on 18.2.0 (hotcrm#2383 comment 6567537276); triaged pm:queue p3
    Serial constraints cleared: none; overlap re-read before pr_create
    Resume-from: none — fresh from origin/main

    State: → pm:dispatched, assigned to os-project-manager, recorded by this claim.


    Generated by Claude Code

  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 2, 2026
  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_01FEkQ5nncLaiWjVh2cS7U17

    os-dev-report
    {
    "issue": 28877,
    "status": "done",
    "branch": "claude/project-thread-ads4rl",
    "pr": "#28969",
    "session": "session_01FEkQ5nncLaiWjVh2cS7U17",
    "premise_still_valid": true,
    "summary": "Row 1: core buildEffectiveObjectPermissions gains an optional isObjectPackageDisabled source accessor and, as its last step, drops every object it answers true for; registryObjectPackageDisabled(registry) is the one reading (the registry's own isObjectPackageDisabled, the data plane's predicate), handed over by both /auth/me/permissions (plugin-hono-server) and ISecurityService.getEffectiveObjectPermissions (plugin-security), so can() answers false and the two maps stay byte-equal. Row 2: objectql SchemaRegistry.isActionWithheldByDisabledPackage extends #24400's boundServedActions rule (own package disabled) with three arms: objectName object of a disabled package, a type flow target only disabled packages ship (isFlowOfDisabledPackage), a param objectOverride object of a disabled package; asked by the served object fold (attached and the object's own merged actions) and by the protocol's effective served action list and served by-name read; execution face, per-package layer and code layer unchanged. Row 3 answered by row 2: with the action withheld the param dialog is not reached, its text is not reworded. Premise correction: before this PR GET /meta/action withheld only an action whose OWN package is disabled; an enabled package's action bound by objectName to a disabled object stayed listed (measured, probe listed= true byName= true object= OBJECT_PACKAGE_DISABLED), so the objectName arm was added as a bounded in-place fix on the same predicate.",
    "tests": "On 934a10f: objectql bound-action-foreign-object.test.ts + package-disable-enforcement.test.ts 47/47; core effective-object-permissions.test.ts 41/41; hono current-user-endpoints-effective-objects.test.ts 6/6; security get-effective-object-permissions.test.ts 65/65. Full suites on de04ea7: core 2896/2896, objectql 10044/10044, metadata-protocol 22093 passed 26 skipped, plugin-hono-server 467/467, plugin-security 5127 passed 45 skipped. Typecheck exit 0 for all five. Red first on main, row 2 (registry.ts, protocol.ts, protocol-meta-item-face.ts at BASE b15b634, metadata-protocol rebuilt, preflight absent): objectql 2 failed / 45 (disabled case, twin-flow case), row-1 files all green. Row 1 (omission call ablated via ablation-replace, core rebuilt, preflight absent): core 3 failed / 38, objectql 1/46, hono 1/5, security 1/64, all row-2 cases green. Ablations each red only their own case: A3 protocol filter only (1 fail, on the list assertion), A4 flow arm (2), A5 objectOverride arm (1), A6 own-actions filter (1), A7 flow-registration invalidation (1, twin-flow case), A8 objectName arm (1 of 47). Every restore: blob == HEAD, git diff HEAD empty, git status clean; dist rebuilt from HEAD after each build-backed leg (preflight present).",
    "gates": "Local union on 934a10f: dispatch-gates --commands derived 71; 70 exit 0 (check:type-check-debt re-run after the verify lock freed: 0 raw tsc errors, none above record; check:i18n exit 0); 1 NOT MEASURED: pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE (needs every package dist), left to CI. dispatch-gates --ran: 71 derived, 70 run, 1 NOT-MEASURED, 0 UNRUN. check-plugin-teardown-shape --self-test exit 0 after git fetch --depth=1 of its pinned fixture commit. CI check-runs on PR #28969: in_progress at report time (not awaited, per os-dev). Gate record: pm-orders/server/28877-gates-ran.txt.",
    "dogfood_battery": "934a10f5 · os-verify-lock VERDICT command-exit 0 (held 2086s) · Test Files 382 passed, 3 skipped (385) · Tests 3156 passed, 28 skipped (3184) · no failure",
    "mcp_calls": "8 — issue_read x4 (get/get_comments on #28877, get on #24400), pull_request_read get_files x2 (#28903, #28889), list tools only; no MCP write",
    "api_writes": "1 relay write — fleet-write pr_create #28969 (run 41391705808, read-back identical); plus this os-dev-report comment via post-stamped; git push x2 (not REST)",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: seat — the fields slot of /auth/me/permissions still carries OBJECT.FIELD entries of a disabled package's objects; harmless (no object entry, data plane refuses), not filed, noted in Acceptance notes",
    "carrier: seat — POST /actions/OBJECT/ACTION reads through the execution face and still answers the launch's own 404 (Flow not found) for a withheld action; unchanged by design, noted"
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

    Landing record · PR #28969

    domain:server seat 2 · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-11-02T06:02Z

    Reading Probe Result
    Merged PR #28969 state merged, squash commit d0df8731 (+510 / -18, 13 files)
    On main git merge-base --is-ancestor d0df8731 origin/main yes
    Queue refs git ls-remote origin refs/heads/gh-readonly-queue/* matching 28969 0
    Content git grep -c isActionWithheldByDisabledPackage origin/main -- packages/ registry.ts 6, protocol.ts 6, protocol-meta-item-face.ts 2

    Release: ships patch for core, objectql, metadata-protocol, plugin-hono-server and plugin-security with the BREAKING banner (ruling A on #25105), at the next release.

    State: pm:dispatched and the assignee removed by this record. The card closes through Fixes #28877.
    Resume-from: none.


    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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions