Skip to content

fix(cli): make flux ready respect the default project - #87

Merged
sirsjg merged 2 commits into
sirsjg:mainfrom
avilches:fix/ready-default-project
Aug 27, 2026
Merged

fix(cli): make flux ready respect the default project#87
sirsjg merged 2 commits into
sirsjg:mainfrom
avilches:fix/ready-default-project

Conversation

@avilches

Copy link
Copy Markdown
Contributor

The problem

flux ready is the only command that ignores the default project stored in .flux/config.json. In a repository whose default project is set with flux project use, flux task list and flux prime scope their answer to that project, but flux ready returns unblocked tasks from every project on the board.

The cause is a missing argument rather than a design decision. In packages/cli/src/index.ts the dispatcher computes defaultProject from initStorage() and hands it to projectCommand and taskCommand, but the readyCommand call a few lines below leaves it out, even though the variable is in scope. readyCommand then resolves the project from args[0], -p and --project only, and getReadyTasks(undefined) skips the project filter entirely.

This is easy to miss with a single project on the board, because the unfiltered answer happens to match. It starts hurting as soon as there is a second one, and it hurts in a particular way: ready sorts by priority, so a P0 that belongs to an unrelated project lands at the top of the list. --help does not mention that ready accepts a project at all, which is what keeps the whole thing out of sight.

Reproducing it takes two projects and a repository pointed at one of them:

flux project use <project-a>
flux ready              # before: tasks from A *and* B, B's P0 listed first
flux ready <project-a>  # correct output, but you have to know to ask

The change

readyCommand now takes defaultProject as its last parameter and uses it when nothing more specific was given, which is the same shape taskCommand already uses for flux task list. Precedence is unchanged where it was already defined: a positional project or -p/--project still wins over the default.

Since bare flux ready used to mean "the whole board", and that is a reasonable thing to want, this adds --all to ask for it explicitly. Worth calling out plainly: for anyone relying on bare flux ready as a cross-project view, the default output changes and --all is the replacement. It seemed better to make the common case correct and give the global view a name than to leave the default wrong, but that trade-off is yours to make, and I am happy to reshape this if you would rather keep the old default and require the project explicitly.

The --help line for ready now shows the positional argument and --all, and docs/cli.md gets the three variants.

Verification

bun run build passes and the full suite is green: 274 tests across 18 files, no failures. Three cases were added to packages/cli/tests/ready.test.ts, covering the fallback to the default project, explicit selection winning over it, and --all ignoring it. The seven existing ready tests were left untouched and still pass, since they call readyCommand without the new parameter.

Checked against a real three-project board as well, from a repository whose default project is the first one: before the change bare flux ready returned 36 tasks spread over the three projects; after it, 17 tasks from that project alone, --all returns the same 36, and passing another project's id returns its 15.

`flux ready` was the only command that ignored the default project from
`.flux/config.json`. The dispatcher already computes `defaultProject` and
passes it to `projectCommand` and `taskCommand`, but the `readyCommand`
call was missing the argument, so with no project on the command line
`getReadyTasks` received `undefined` and returned unblocked tasks from
every project on the board.

In a multi-project board that silently mixes other projects into the
answer, and since `ready` sorts by priority, a P0 belonging to another
project ends up at the top of the list.

Explicit selection still wins: a positional project or `-p`/`--project`
takes precedence over the default. The new `--all` flag asks for the
whole board on purpose, which is what bare `flux ready` used to do.

Also documents the positional project argument in `--help`, which was
missing even though the argument already worked.

@thebulklord thebulklord left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR fixes a real inconsistency where flux ready was the only command ignoring the default project from .flux/config.json. The fix is minimal and surgical: thread defaultProject into readyCommand (same pattern taskCommand and projectCommand already use), add a --all escape hatch for the old cross-project behavior, and update help/docs. The precedence chain (--all > explicit project > default > undefined) is clean and the three new tests nail the key paths. The intentional behavior change for bare flux ready is clearly called out in the PR description, which is the right call — making the common case correct beats preserving a silent footgun.

Strengths

  • The fix is one line of logic in ready.ts plus one argument at the call site. No refactoring, no new abstractions. Exactly the right scope for a bug fix.
  • Precedence is unambiguous: --all short-circuits to undefined, then explicit project (positional or -p/--project) wins over the stored default. This mirrors taskCommand's existing pattern, so the mental model is consistent across the CLI.
  • The three new tests cover the fallback, the explicit-over-default override, and the --all opt-out. Existing tests that call readyCommand without the fourth argument still pass because defaultProject defaults to undefined, preserving the old behavior for repos without a configured project.
  • Help text and docs/cli.md are updated in the same commit, so the new --all flag and positional argument aren't a hidden feature.
  • The PR description is honest about the breaking change and offers to reshape the default if maintainers prefer. That's good citizenship.

Suggestions

  • Consider adding a test for flux ready proj-1 --all to lock in the current behavior where --all wins over an explicit project. Right now the code makes --all a hard override, which is defensible, but a test documents the intent so a future refactor doesn't accidentally flip it.
  • The --all flag isn't listed in the flags reference table at the bottom of docs/cli.md. It's covered in the quick-commands block, but the table is where people look for flag semantics. A one-line addition would close the gap.
  • Minor: the --help line now shows [project] in yellow (positional) and [--all] in green (flag), which is consistent with the rest of the help output. Nice.

Risk and coverage

  • Verified the logic in ready.ts by tracing all four input combinations (no project, explicit project, default project, --all) against the getReadyTasks call. All paths produce the expected projectId value.
  • Verified the dispatcher in index.ts passes defaultProject (sourced from initStorage().project) to readyCommand, matching the pattern used for taskCommand and projectCommand two lines above.
  • Verified the three new tests exercise the correct argument positions and assert the right getReadyTasks call. The seven pre-existing tests are untouched and their calls omit the fourth argument, so they exercise the defaultProject === undefined path.
  • Did not execute the test suite or build; the PR description reports 274/274 green. No static analysis commands were configured in the repository analysis.
  • No security concerns: defaultProject originates from a local config file read by initStorage(), the same trust boundary as every other command. No new external input, no injection surface.

Findings:

  • [nit] packages/cli/tests/ready.test.ts:78 - Add a test for --all combined with an explicit project: The current logic makes --all a hard override: flags.all === true ? undefined : explicitProject || defaultProject. So flux ready proj-1 --all returns all projects, ignoring proj-1. That's a reasonable design choice, but there's no test pinning it down. A future refactor that reorders the ternary would silently change behavior with no test failure. Suggest adding:
it('--all wins over an explicit project', async () => {
  mockGetReadyTasks.mockResolvedValue([]);
  await readyCommand(['proj-1'], { all: true }, false, 'proj-default');
  expect(mockGetReadyTasks).toHaveBeenCalledWith(undefined);
});

Not blocking — the current behavior is correct — but it's a one-liner that prevents a future regression.

  • [nit] docs/cli.md:148 - Add --all to the flags reference table: The flags table at the bottom of docs/cli.md lists --json, -P/--priority, -e/--epic, --note, and --status, but not --all. The quick-commands block above covers it, but the table is the canonical reference for flag semantics. A row like | --all | Show ready tasks across all projects (overrides default project) | would close the gap. Non-blocking since the quick-commands section already documents it.

Coverage notes:

  • Traced all four projectId resolution paths in ready.ts (no project, explicit, default, --all) against the getReadyTasks call — all correct.

  • Verified the dispatcher in index.ts passes defaultProject from initStorage().project, consistent with taskCommand and projectCommand.

  • Verified the three new tests assert the correct getReadyTasks arguments and that the seven pre-existing tests omit the fourth parameter (defaultProject = undefined), preserving old behavior.

  • Did not execute the test suite or build; PR description reports 274/274 green. No static analysis or SAST commands were configured.

  • No secrets detected in changed files. No new external input or trust-boundary changes.

  • Caller/reference search was skipped (rg not available), but the only caller of readyCommand is the dispatcher in index.ts, which is in the diff.

Review checklist:

Area Status Area Status
Intent match ✅ reviewed against PR text/commits Correctness ⚠️ 2 non-blocking finding(s)
Security ✅ built-in secret scan Regression risk ✅ checkout context, 4 file(s), 1 reference set(s)
Tests ➖ reviewed from code/test context Static checks ➖ not configured
Maintainability ⚠️ 2 non-blocking finding(s) Performance ⚠️ no obvious issue flagged

✅ Clean fix, bro. You spotted the one command that was skipping leg day while all the others were hitting the default project, and you fixed it with the minimum effective dose. The --all escape hatch is the right call — nobody should have to guess that bare flux ready used to mean the whole board. Ship it, then go hit a double-bicep set to celebrate.


await readyCommand([], { all: true }, false, 'proj-default');

expect(mockGetReadyTasks).toHaveBeenCalledWith(undefined);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Add a test for --all combined with an explicit project

The current logic makes --all a hard override: flags.all === true ? undefined : explicitProject || defaultProject. So flux ready proj-1 --all returns all projects, ignoring proj-1. That's a reasonable design choice, but there's no test pinning it down. A future refactor that reorders the ternary would silently change behavior with no test failure. Suggest adding:

it('--all wins over an explicit project', async () => {
  mockGetReadyTasks.mockResolvedValue([]);
  await readyCommand(['proj-1'], { all: true }, false, 'proj-default');
  expect(mockGetReadyTasks).toHaveBeenCalledWith(undefined);
});

Not blocking — the current behavior is correct — but it's a one-liner that prevents a future regression.

Review follow-up. `--all` was documented in the quick commands block and in
the `ready` usage line, but missing from both flag lists: the `Flags:`
section of `flux --help` and the flags table in docs/cli.md.

Adds the test the review suggested, `--all` together with an explicit
project, which pins down that `--all` is a hard override rather than a
fallback.
@avilches

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Both nits are now addressed, plus a third instance of the same omission that the review did not catch.

--all was documented in the quick commands block of docs/cli.md and in the ready usage line, but it was missing from both flag lists: the table in docs/cli.md, which you flagged, and the Flags: section of flux --help itself, which lists --json, --force, -P/--priority, -e/--epic, --blocked, --ac, --guardrail, --data and --no-logo. That second one is the more visible of the two, since it is what people actually read. Both now list it.

Your suggested test is in as well: --all together with an explicit project, which pins down that --all is a hard override rather than a fallback. That was worth having, since the implementation makes that choice deliberately and nothing was holding it in place. Suite is at 275 passing, 0 failing.

On the colouring of [project] and [--all] in the usage line, I followed what the file already does elsewhere: yellow for required or positional arguments and green for flags, which is the convention in the flux task create and flux epic list lines. Left as is for consistency, but happy to change it if you would rather it read differently.

The workflows are still sitting at action_required, which is the standard hold on a first contribution from a fork, so the CI has not run rather than failed. bun run test and bun run build both pass locally if that helps in the meantime.

The open question from the PR body still stands and is for @sirsjg rather than a follow-up commit: whether making bare flux ready scope to the repository's project is the behaviour you want, given it changes the default for anyone using it as a cross-project view, with --all as the replacement. Happy to reshape it the other way round if you prefer.

@sirsjg

sirsjg commented Aug 27, 2026

Copy link
Copy Markdown
Owner

Good catch — this was an oversight on my part, not a design choice. defaultProject was already wired into task list and project, and ready just missed it. Happy with the new default too: once you've run flux project use, getting other projects' tasks back from ready is surprising, and --all is a fair replacement for the old behaviour. Tests and docs updates appreciated.

@sirsjg
sirsjg merged commit efe66ed into sirsjg:main Aug 27, 2026
3 checks passed
@avilches
avilches deleted the fix/ready-default-project branch September 1, 2026 20:45
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.

3 participants