Skip to content

fix(work): the ExecPlan projection is workspace-scoped, not tenant-scoped - #743

Merged
CueCrux-Myles merged 1 commit into
mainfrom
fix/execplan-projection-tenant-scope
Aug 22, 2026
Merged

CueCrux-Myles merged 1 commit into
mainfrom
fix/execplan-projection-tenant-scope

Conversation

@CueCrux-Myles

Copy link
Copy Markdown
Contributor

The work board currently returns zero ExecPlans to any caller whose tenant claim is not default — silently, as 200 {"count":0}.

What happened

a413ce6f (#686, the red-steel replay) bound /v1/work to the caller's authenticated tenant so one tenant's kanban rows could not be counted into another's roll-up. That is right for kanban. It was applied to the ExecPlan projection too, which has no tenant dimension at all: list_execplans walks plan files under one root and stamps every item tenant_id: None, so work_tenant_id(w) == tenant_id compares "default" against the caller's tenant and drops every plan.

Why it went unnoticed

200 with count: 0 is indistinguishable from an empty backlog or an unconfigured root — and the console's own copy names the unset-CRUX_EXECPLANS_ROOT case with the same signature, which sends you looking at host env instead of at tenant scoping.

Observed on host crux v0.5.62 with an agent token scoped to tenant work:

/v1/work?source=execplans              -> 200 {"count":0}   (3 consecutive probes)
/v1/work?source=execplans&tenant_id=default -> 403 TENANT_FORBIDDEN
/v1/work/execplan:<slug>               -> 404
/v1/attention/summary                  -> needs_you: 0

The same host listed 42 plans at v0.5.59, before this shipped. attention.rs:73 calls the same helper, which is the needs_you: 0.

The change

Filter only the items that actually declare a tenant. Workspace-scoped plans (tenant_id: None) are visible to any authorized reader — the same argument the code already makes one line above for project_id — while a future tenant-stamped plan stays narrowed. Kanban binding is untouched.

Split the narrowing into filter_execplan_items so the rule is assertable without a plan root on disk or process env.

Verification

Two tests, both positive-controlled — they fail against the previous predicate, the first dropping exactly the workspace-scoped plan:

  • workspace_scoped_plans_are_visible_to_a_non_default_tenant
  • tenant_stamped_plans_are_still_narrowed_to_the_caller (so the predicate is not vacuously true)

Gates: cargo test --workspace --no-fail-fast green · clippy --workspace -D warnings clean · fmt clean · unwrap-ratchet 389/389.

🤖 Generated with Claude Code

…oped

`a413ce6f` (#686, the red-steel replay) bound /v1/work to the caller's
authenticated tenant so one tenant's kanban rows could not be counted into
another's roll-up. That is right for kanban. It was applied to the ExecPlan
projection too, which has no tenant dimension at all: `list_execplans` walks
plan *files* under one root and stamps every item `tenant_id: None`, so
`work_tenant_id(w) == tenant_id` compared "default" against the caller's tenant
and dropped every plan.

The failure is silent. The board answers `200 {"count":0}`, which is
indistinguishable from an empty backlog or an unconfigured root, so any token
whose tenant claim is not `default` sees no work at all — on /v1/work and on
/v1/attention/summary, which calls the same helper. Observed on host crux
v0.5.62 with an agent token scoped to tenant `work`: source=execplans returned
count 0 while ?tenant_id=default returned 403 TENANT_FORBIDDEN. The same host
listed 42 plans at v0.5.59, before this commit shipped.

Filter only the items that actually declare a tenant. Workspace-scoped plans
(`tenant_id: None`) are visible to any authorized reader — the same argument the
code already makes one line above for `project_id` — while a future
tenant-stamped plan stays narrowed. Kanban binding is untouched.

Split the narrowing into `filter_execplan_items` so the rule is assertable
without a plan root on disk or process env. Positive-controlled: both new tests
fail against the previous predicate, the first dropping exactly the
workspace-scoped plan.

Gates: cargo test --workspace --no-fail-fast 8325 passed / 0 failed; clippy
--workspace -D warnings clean; fmt clean; unwrap-ratchet OK (389/389).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@CueCrux-Myles
CueCrux-Myles added this pull request to the merge queue Aug 22, 2026
Merged via the queue into main with commit 96bd8d0 Aug 22, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant