From 482957ce2f9ffd1cc2a9ccdec047513b753caec1 Mon Sep 17 00:00:00 2001 From: Patrick Buckley Date: Mon, 20 Jul 2026 11:15:32 -0700 Subject: [PATCH] docs(auth): correct require_project predicate docstring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The docstring claimed the non-string project_id coercion matched both _coord_create_build_kwargs and the interactive create path, but _interactive_create_build_kwargs passes body.get("project_id") through rather than coercing. Restate it as the gate's own rule — only a non-empty stripped string counts as an attached project — and reference only the coordinator persistence that actually matches. Behavior unchanged. --- turnstone/core/auth.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/turnstone/core/auth.py b/turnstone/core/auth.py index 3ffed226..4a10615a 100644 --- a/turnstone/core/auth.py +++ b/turnstone/core/auth.py @@ -509,12 +509,14 @@ def require_project_denies_create(config_store: Any, auth: Any, project_id: Any) Serves both gated mounts: interactive creates on nodes and coordinator creates on the console. Refuses only when the gate is on, the caller is - not exempt automation, and no project is attached. "No project" is read - the way the create path actually persists it: a non-string body value - (int / bool / list / dict) is coerced to absent — exactly as - ``_coord_create_build_kwargs`` and the interactive create do — so a - truthy non-string like ``project_id: 123`` cannot stringify past the - gate and mint a projectless session; whitespace-only counts as none too. + not exempt automation, and no project is attached. The gate counts a + project as attached only when ``project_id`` is a non-empty string after + stripping; absent, empty, whitespace-only, or a non-string body value + (int / bool / list / dict) all read as "no project." Treating a + non-string as absent matches how ``_coord_create_build_kwargs`` persists + it (``None``) and stops a truthy non-string like ``project_id: 123`` from + stringifying past the gate and minting a projectless session while the + create stores no project. Exempt identities: the ``service`` scope (channel gateway, scheduler) and the sessions a coordinator spawns (``token_source == "coordinator"`` — minted per coordinator session for