Found while measuring #13906 (PR #14272, the tenancy-posture seam legs). Recorded for triage; no severity asserted — the refusals themselves FIRE and fail closed, so this is dead declared observability, not a bypass.
The shape
resolveAuthzContext (packages/core/src/security/resolve-authz-context.ts) sets ctx.authRefusal = { reason, message } for the two posture-conditional refusals (organization_required at admission, organization_membership_ended after grants), and its doc block says a caller that can report WHY uses this surface. #8287's design comments say the point of refusing is to be "loud at call time instead of silently empty", and the organization_membership_ended message even explains "The key was not revoked — the membership that backed it ended."
Measured on today's tree (b93d4fd): resolveAuthzContext has eight non-test consumers across packages (rest-server, runtime resolve-execution-context, mcp plugin, plugin-sharing, service-datasource admin-routes, service-settings, service-storage, cloud-connection marketplace-install) and grep -c authRefusal is ZERO in every one of them. The only readers of the field anywhere are the resolver itself and its own unit test.
The consequence
A refused principal carries no userId, so every transport takes its anonymous path: on REST the refused ex-member key answers the same 401 UNAUTHENTICATED body as a request with no credential at all (driven in PR #14272's section 2 — the positive-control leg observes the reason only by calling the resolver directly). The half of #8287's loudness that distinguishes "your key's backing membership ended" from "you are not signed in" is authored, stored, and dropped before every wire.
Why filed rather than folded into #13906
#13906 is about failures being ABSORBED upstream of the refusals; this is about the refusal's answer being under-reported downstream even when everything works. Fixing it is a wire-contract judgement (which doors may disclose WHY admission was refused, and whether that leaks key-existence information to a caller probing with a stolen key) — a decision, not a mechanical repair.
Related, and distinct: #13906 (the absorb seams that can SKIP these refusals), #13255-family CONTEXT-LOST (a lost context also answers 401 — one more member of the crowd this reason disappears into).
Generated by Claude Code
Found while measuring #13906 (PR #14272, the tenancy-posture seam legs). Recorded for triage; no severity asserted — the refusals themselves FIRE and fail closed, so this is dead declared observability, not a bypass.
The shape
resolveAuthzContext(packages/core/src/security/resolve-authz-context.ts) setsctx.authRefusal = { reason, message }for the two posture-conditional refusals (organization_requiredat admission,organization_membership_endedafter grants), and its doc block says a caller that can report WHY uses this surface. #8287's design comments say the point of refusing is to be "loud at call time instead of silently empty", and theorganization_membership_endedmessage even explains "The key was not revoked — the membership that backed it ended."Measured on today's tree (b93d4fd):
resolveAuthzContexthas eight non-test consumers across packages (rest-server, runtime resolve-execution-context, mcp plugin, plugin-sharing, service-datasource admin-routes, service-settings, service-storage, cloud-connection marketplace-install) andgrep -c authRefusalis ZERO in every one of them. The only readers of the field anywhere are the resolver itself and its own unit test.The consequence
A refused principal carries no
userId, so every transport takes its anonymous path: on REST the refused ex-member key answers the same401 UNAUTHENTICATEDbody as a request with no credential at all (driven in PR #14272's section 2 — the positive-control leg observes the reason only by calling the resolver directly). The half of #8287's loudness that distinguishes "your key's backing membership ended" from "you are not signed in" is authored, stored, and dropped before every wire.Why filed rather than folded into #13906
#13906 is about failures being ABSORBED upstream of the refusals; this is about the refusal's answer being under-reported downstream even when everything works. Fixing it is a wire-contract judgement (which doors may disclose WHY admission was refused, and whether that leaks key-existence information to a caller probing with a stolen key) — a decision, not a mechanical repair.
Related, and distinct: #13906 (the absorb seams that can SKIP these refusals), #13255-family CONTEXT-LOST (a lost context also answers 401 — one more member of the crowd this reason disappears into).
Generated by Claude Code