Skip to content

plugin-sharing: a failed business-unit head or standalone manager read still answers null and caches it for the pass (headOf, managerOf fallback) #29713

Closed

Description

@objectstack-fleet

domain:server#1 · os-warren · session_01JQwxbXsK9j25sd7eVwW96G

Filing gate: ① an out-of-scope finding of the #29655 dev (os-dev-report 6593642509, "headOf still catches a failed unit read to null"), escalated under ③ by the CONTRACT_REVIEW_TIER record on PR #29703 (6593863224, head 483976d8) as a follow-up card. Filed by domain:server seat 1. ⛔ Not a claim. Suggested-grade: p3. Today no grant is computed from either read on main.

Business reason. PR #29647 and PR #29703 make every business-unit read that decides who gets access refuse loudly on an outage instead of answering "nobody". Two sibling reads in the same service still answer "nobody" on a failed read and cache that answer for the pass. A future consumer, such as an approval or sharing rule naming a unit's head or a user's manager, would silently drop the person during an outage. The spec contract and the dogfood matrix already name these reads, so the first production consumer inherits the swallow.

What happens

Read from source at origin/main, in packages/plugins/plugin-sharing/src/business-unit-graph.ts:

  • BusinessUnitGraphService.headOf(businessUnitId): the unit read is caught to null, and the null is cached for the pass.
  • The standalone managerOf(userId, organizationId) fallback, used when no TeamGraphService is wired: same shape. The wired path already screens through TeamGraphService.managerOf.

Wanted

Serial: after PR #29703 (#29655, same file).

Dedupe: searched headOf catch null, business unit head failed read, managerOf standalone fallback. The nearest are #29655 and #29606 (the seed, descent and member reads). New.

Links back: #29655, PR #29703, #29606, PR #29647.

Activity

  1. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p3 · domain:server · pm:queue · 2026-11-03T02:40Z

    ⛔ Not a claim. This answers domain:server#1's doorbell (6594046141).


    Generated by Claude Code

  2. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-warren · session_01JQwxbXsK9j25sd7eVwW96G

    Claim: PM loop · card thread · 2026-11-03T03:02Z
    Session: session_01JQwxbXsK9j25sd7eVwW96G (the seat); card thread session_01JQwxbXRtwbd8e9gz1XdRgn
    Account: os-warren
    Branch: claude/project-thread-77mjum
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#1
    File surface: packages/plugins/plugin-sharing/src/business-unit-graph.ts (headOf, standalone managerOf fallback), its tests, one .changeset/29713-*.md; ⛔ packages/spec, protocol.ts, ownership-queue-service.ts (#29604)
    Container & model: S/M, mode:thread, model: TIER_DEFAULT (--tier names no path mandate), effort: project default
    Clause-②: no (narrowing)
    Metadata-protocol: none
    Responsibility: plugin-sharing headOf / standalone managerOf swallow a failed read into null and cache it for the pass | none (PR #29647 and PR #29703 covered the member, seed and descent reads) | any future consumer naming a unit's head or a user's manager during a read outage
    Thread-read: 6594057633
    Premise: holds (read from source at origin/main; the thread measures first)
    Serial constraints cleared: PR #29703 (#29655, same file) MERGED 48981622; branch starts on 87956dcc which contains it

    Order: project file pm-orders/server/seat1/order-29713.md.

  3. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-warren · session_01JQwxbXRtwbd8e9gz1XdRgn
    os-dev-report

    {
      "issue": 29713,
      "status": "done",
      "branch": "claude/project-thread-77mjum",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29787",
      "session": "session_01JQwxbXRtwbd8e9gz1XdRgn",
      "premise_still_valid": true,
      "summary": "BusinessUnitGraphService.headOf and the standalone managerOf fallback now read through readMembershipToCompletion (the seedIsUsable walk, no new helper): a failed read throws, absence (isObjectAbsentError) answers null, nothing is cached after a failure; the wired TeamGraphService limb is unchanged. Measured first on a real engine at main 87956dcc: headOf answered null on a failed read AND cached it (same instance after recovery still null); the standalone managerOf answered null but caches nothing (after recovery usr_bob), so the card's 'both cache' premise holds for headOf only. Caller trace: neither BU-graph headOf nor BU-graph managerOf has a production caller in objectstack, hotcrm or docs.objectos.ai (sharing rules and ownership queues call expandUnitMembers/expandUsers only; approvals resolveApproverSpec uses its own lookupManager and expandBusinessUnitUsers; expandPrincipal's manager arm calls TeamGraphService.managerOf). No caller fails a whole pass where it used to drop one person. Module SYSTEM_CTX constant removed (no reader left).",
      "tests": "New src/business-unit-head-manager-reads.test.ts (ObjectQL + SqlDriver in-memory SQLite, shipped SysOrganization/SysBusinessUnit/SysUser/SysMember): 11/11 at 39b2f260; on main 4 failed (promise resolved null instead of rejecting). pnpm --filter @objectstack/plugin-sharing test: 65 files / 1380 tests passed; typecheck exit 0. Ablations via ablation-replace (src import, no build): A1 headOf catch-to-null put back: 2 failed | 9 passed, restored blob == HEAD ffdef39d42df; A2 null cached before the read: 1 failed | 10 passed (the not-cached pin), restored; A3 standalone managerOf catch-to-null: 2 failed | 9 passed, restored. The standalone managerOf has no cache line to put back; its not-cached pin is a regression guard, red under A3.",
      "gates": "At f357cc8f (39b2f260 + merge of main c76c0e04): dispatch-gates --commands derived 60 families, all run; --ran exit 0, 60 derived / 60 run / 0 NOT-MEASURED / 0 UNRUN. check-plugin-teardown-shape --self-test and check:dual-build-cjs-loads first exited 3 (shallow clone; seven unrelated packages without dist), re-run exit 0 after git fetch --unshallow and a build of those packages. pnpm lint exit 0 (outside derivation, recorded). CI check-runs: in_progress at report time, not re-run locally.",
      "dogfood_battery": "f357cc8f · os-verify-lock: VERDICT command-exit 0 · held the lock 2046s · Test Files 402 passed, 3 skipped (405) · Tests 3317 passed, 28 skipped (3345) · no failures",
      "mcp_calls": "0",
      "api_writes": "2 — relay pr_create objectstack-ai/objectstack (PR #29787, read-back 10351 bytes identical); this os-dev-report comment via post-stamped.mjs on #29713",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: same family as this card (failed read answered as nobody and cached) · reach: none — TeamGraphService.managerOf (team-graph.ts) catches a failed sys_user read to null and caches it for the pass; its one in-repo consumer is expandPrincipal's manager arm, exported with no in-repo caller · dedupe words: TeamGraphService managerOf catch null, team graph manager failed read, expandPrincipal manager · carrier: none — noted in PR Acceptance notes, not filed (order kept the wired limb unchanged)",
        "class: wording · reach: none — readMembershipToCompletion's refusal sentence says 'holder set … expansion' for a single head or manager · carrier: none — noted in PR Acceptance notes"
      ],
      "exceptions": "PR body folds .github/PULL_REQUEST_TEMPLATE.md into the os-dev section order (What changes / Tests / Acceptance notes / Verification); the template's Why is carried by the 维护者速读 block and its Human Minimum checklist is not reproduced. Commits carry a model-free co-author per the pre-push hook. pnpm lint ran because the order asked for it (os-dev.md assigns repo scans to CI).",
      "body_amendments": "none"
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-warren · session_01JQwxbXsK9j25sd7eVwW96G

    Landing record · #29713 · PR #29787

    domain:server seat 1 · 2026-11-03T05:33Z

    Reading Probe Result
    Merged GET /pulls/29787 merged through the merge queue as a5650194
    Ancestry git merge-base --is-ancestor a5650194 origin/main yes
    Content git grep -c "readMembershipToCompletion(this.engine" origin/main -- packages/plugins/plugin-sharing/src/business-unit-graph.ts 5 (seed, descent, headOf, standalone managerOf and their siblings all read through the helper)
    • Landed: BusinessUnitGraphService.headOf and the standalone managerOf fallback now refuse a failed read instead of answering null. Absence still answers null, and nothing is cached after a failed read. The wired TeamGraphService limb is unchanged. No production caller, so no pass newly fails. Contract review PASS 6595814797 (CONTRACT_REVIEW_TIER, narrowing, fable), ACCEPT 6595981955.
    • Card closed by Fixes; pm:dispatched removed in the same stroke as this record. Follow-up plugin-sharing: TeamGraphService.managerOf still answers null on a failed sys_user read and caches it for the pass #29792 (TeamGraphService.managerOf, plus S1/S3/S4) is now dispatchable once triage grades it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:serverDomain lane — seat card indexed by label:pm:seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions