Skip to content

[finding] the two human-user predicates disagree on non-object rows, and the plugin-security spelling fails OPEN — the class #12512's pin deliberately excludes #12515

Description

@os-support-ai

Surfaced by the dev implementing #11959 (PR #12512) and filed by the domain:services PM seat on review. Filed unassigned; ⛔ no domain:* — routing and grading are triage's.

What was measured

The two owners of "is this sys_user row a HUMAN?" disagree on non-object truthy inputs:

inputplugin-authisHumanUserRowplugin-securityisHumanUser
'usr_alice' (a string)falsetrue
a numberfalsetrue
truefalsetrue

Cause, from the two spellings:

  • isHumanUserRow (plugin-auth/src/audience-posture.ts:317) requires typeof row === 'object'.
  • isHumanUser (plugin-security/src/bootstrap-platform-admin.ts:401) is u && u.id !== SystemUserId.SYSTEM && u.role !== 'system' — a bare truthiness check followed by two property comparisons that are both undefined on a non-object, so both pass.

Measured by executing the expression extracted verbatim from source, not by reading it.

⚠️Direction matters: the security copy fails OPEN — it calls a non-row "human", and it is the copy that performs the platform-admin promotion.

Why this is a finding and not a defect

It is unreachable today. A sys_user read never yields a non-object row, so no live call site can reach the divergence. ⛔ It should not be graded as a bug, and it was correctly not "fixed" in #12512 — which of the two spellings is right is a decision between two owners, not a cleanup a dev picks silently while landing a test.

Why it is still worth a card rather than a shrug

Two reasons, and the second is the load-bearing one:

  1. test(auth): pin the human-user predicate agreement across the plugin-security boundary #12512's pin deliberately excludes this input class. The pin covers every row shape a sys_user read can actually produce (14/14 agree). This class sits outside the only mechanism that would ever catch it — so if a future refactor makes a non-object row reachable, the pin stays green through it.
  2. ⚠️"The consolidation will delete it anyway" is not currently true. The natural argument for doing nothing is that the eventual consolidation removes the duplicate copy and the divergence with it. But consolidation is explicitly declined — triage ruled at [finding] a third hand-spelled copy of the human-user predicate lives in plugin-security, out of reach of the one #11767 consolidated #11959's grading that moving the predicate into @objectstack/spec / @objectstack/platform-objects expands a published surface and "stays declined absent new pull". So there is no scheduled event that retires this asymmetry.

⇒ Recording it makes it a decision rather than an oversight, which is the whole difference.

Options, so grading is cheap

shapenote
Aclose as won't-fix, on recordhonest if the reachability judgement is accepted and nobody wants the asymmetry tracked
Badd a typeof guard to the plugin-security copy so both spell the same rule⚠️ picks a winner between two owners — small, but it is a decision
Cextend #12512's pin to cover non-object inputs⛔ would fail today: it pins the disagreement rather than the agreement. Only viable after B

⛔ Deliberately not recommending one — this is a grading call, and the seat that filed it has no more evidence than the table above.

Related

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions