feat(tasks): auto-scope linear tasks to the cwd's Linear project - #41
Conversation
Agents launched in a project subfolder listed every project's tasks and
picked up the wrong project's work. `linear tasks` (and `--board`) now
auto-scope to the Linear project bound to the current directory, resolved
from `agents projects for-cwd` (longest-match over bound roots + monorepo
subpaths). cwd behaves like an implicit `--project`.
Precedence: explicit `--project` wins; `--all` forces the whole team;
otherwise the cwd binding scopes the queue. Opt out with `autoScope: false`
in config. Fail-open in two bounded subprocess calls: no `agents` on PATH,
no def for the cwd, or a slow/broken call leaves the queue unscoped exactly
as before — never raises on the hot path.
--json gains `project: {id, name, auto}` (null when unscoped) so consumers
can distinguish an auto-scoped queue from the full workspace.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Code ReviewerVerdict: Ready to merge with one note Build: clean (Python syntax OK) Changes that work well
Issue to considerCycle widening differs between In scope = resolve_task_scope(args.cycle, project, ...)In scope = args.cycle or "active" # line 2296 — project not yet known
fragment, scope_label, cycle_meta = build_cycle_scope(...) # "active" stays
...
pid, auto_project_name = resolve_cwd_project() # line 2322 — too late
if pid:
filters.append(...)The comment at line 2309-2311 says
This may be intentional — a board across all cycles could be noisy. But the comment is misleading, and the inconsistency could surprise someone who expects auto-scope to behave the same way across both commands (especially since
Either is fine — the important thing is that the documentation matches the implementation. Things to verify manuallyNothing beyond what was already tested. The fail-open path ( Reviewed by Code Reviewer — actually ran the build and tests on this branch. |
Review of #41 found the BLOCKER: the auto-detected cwd project id was fed to resolve_project_id(strict=True) — the same abort-on-typo path used for an explicit --project. A stale/malformed `linear.projectId` from `agents projects` (drift outside this CLI's control) then hard-exited the single most-common command instead of falling back, breaking the documented fail-open guarantee. New resolve_auto_project_id() validates the auto-detected id/name against the team's LIVE projects and returns None (never raises/exits) on no match — which also catches a stale-but-UUID-shaped id that resolve_project_id's shape-only UUID passthrough would have waved into a filter that silently matches nothing. Both list_tasks and show_board now share this one policy (the SHOULD): explicit --project resolves strictly and aborts on a typo; the cwd auto path validates fail-open and, on drift, shows the whole team with a one-line stderr note. Tests: +8 (resolve_auto_project_id unit coverage; drifted-UUID and malformed non-UUID bindings fall back to unscoped for both tasks and board). 131 pass. Live: prix still scopes to Prix (now validated), /tmp unscoped, no spurious note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The automated reviewer noted show_board resolved the cycle scope BEFORE the project, so an auto-scoped (or explicit --project) board stayed on the active cycle while `linear tasks` widened to all cycles — contradicting the "same as list_tasks" comment. Resolve the project first and widen `scope` to "all" when a project is set, so both surfaces treat a project scope as the whole deliverable across cycles. Unscoped boards still default to the active cycle. Tests: board auto-scope now asserts scope=="all"; added an unscoped-board stays-active case. 132 pass. Live: `tasks --board` from prix/ → Prix, scope all. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Code ReviewerVerdict: Ready to merge Build: N/A — single-file Python (stdlib only), no build step Changes that work well
Issues that need attentionNone. The diff is clean, all edge cases in the fail-open contract are covered by tests, the documentation is updated (CHANGELOG, README, Things to verify manually
Reviewed by Code Reviewer — actually ran the build and tests on this branch. |
Code ReviewerVerdict: Ready to merge Build: No build step — Python script with zero external dependencies. Syntax check passes. Read: no Changes that work well
Things to verify manually (can't be automated here)
Reviewed by Code Reviewer — actually ran the build and tests on this branch. |
Review of #41 found the BLOCKER: the auto-detected cwd project id was fed to resolve_project_id(strict=True) — the same abort-on-typo path used for an explicit --project. A stale/malformed `linear.projectId` from `agents projects` (drift outside this CLI's control) then hard-exited the single most-common command instead of falling back, breaking the documented fail-open guarantee. New resolve_auto_project_id() validates the auto-detected id/name against the team's LIVE projects and returns None (never raises/exits) on no match — which also catches a stale-but-UUID-shaped id that resolve_project_id's shape-only UUID passthrough would have waved into a filter that silently matches nothing. Both list_tasks and show_board now share this one policy (the SHOULD): explicit --project resolves strictly and aborts on a typo; the cwd auto path validates fail-open and, on drift, shows the whole team with a one-line stderr note. Tests: +8 (resolve_auto_project_id unit coverage; drifted-UUID and malformed non-UUID bindings fall back to unscoped for both tasks and board). 131 pass. Live: prix still scopes to Prix (now validated), /tmp unscoped, no spurious note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Updated Release ordering: this calls the installed |
Problem
Muqsit opens Claude Code sessions in various project subfolders (e.g.
~/src/…/agents/prix). The SessionStart injection and a barelinear tasksboth list every project's tasks regardless of which folder the agent is in, so agents pick up work from the wrong project.Fix
linear tasks(and--board) now auto-scope to the Linear project bound to the current working directory. The directory→project mapping is owned by theagents projectsCLI —agents projects for-cwd --jsondoes a longest-match over every bound root and monorepo subpath, which a basename comparison can't. cwd behaves like an implicit--project.Precedence: explicit
--projectwins →--allforces the whole team → otherwise the cwd binding scopes the queue → else unscoped.Overrides:
--all(whole team),--project X, orautoScope: falsein~/.linear-cli/config.json.Fail-open (two bounded subprocess calls, ~0.3s each): no
agentson PATH, no def for the cwd, a def without a Linear binding, or a slow/broken/timed-out call all leave the queue unscoped exactly as before.resolve_cwd_project()never raises — it runs on the hot path of the most-used command.--jsongainsproject: {id, name, auto}(null when unscoped) so consumers can distinguish an auto-scoped queue from the full workspace.Live verification (real Linear, from this fleet)
Human-readable banner:
Tests
python3 -m unittest test_linear→ 123 passed, hermetic (0.018s). AddedResolveCwdProjectTest(6: resolves, unbound, missing binary, nonzero exit, no-binding, timeout — all fail-open) andCwdAutoScopeTest/ board tests (no-binding unscoped, binding auto-scopes + widens to all cycles,--alldisables, explicit--projectoverrides cwd,autoScope:falseopts out). The shared harness stubsresolve_cwd_projectso pre-existing tests stay deterministic and never shell out.Docs
CHANGELOG
0.20.0, README (For humans and agents+ examples),skill.md. Version bumped0.19.1 → 0.20.0.