Skip to content

feat(cli): add qawolf flows lint to check flow files against QA Wolf's rules - #1537

Closed
Jason Allen (JAllen2022) wants to merge 5 commits into
mainfrom
jallen/nova-1586-qawolf-lint
Closed

feat(cli): add qawolf flows lint to check flow files against QA Wolf's rules#1537
Jason Allen (JAllen2022) wants to merge 5 commits into
mainfrom
jallen/nova-1586-qawolf-lint

Conversation

@JAllen2022

@JAllen2022 Jason Allen (JAllen2022) commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Overview of Changes

qawolf flows lint [pattern] lints source files with QA Wolf's own rules - the same linter the platform editor and Tester run, imported from @qawolf/workflow-linter. It sits in the flows group beside list, pull, and run, and expands patterns through the same expandPatterns helper (node_modules excluded, .qawolf/<env>/ caches included). No pattern means every .ts/.js file in the project - flows, helpers, and page objects alike, since the POM rules a team's .eslintrc.json enables target exactly the non-flow files. The lint domain owns lintability: patterns select candidates, and selectLintableFiles keeps .ts/.js (same extension test as the platform's lintFilePath), so a pattern matching a .json never gets parsed as TypeScript. It reads the project's .eslintrc.json (severity-only overrides, bundled plugins only, same contract as the editor) by walking up from cwd, bounded by the package root via resolveProjectDirSafe. Output is eslint-stylish per file plus a problem count. Type-aware rules work - the command builds a real TS program over each file's import graph, so things like no-unnecessary-type-assertion fire across files.

Exit codes: 0 for clean or warnings-only, testFailure (1) when any error-level finding exists, invalidArgs (2) when a pattern matches no lintable file - including a pattern that matches only non-lintable files, which would otherwise pass a gate it never checked. Zero matches follows flows run, not flows list - the two siblings disagree, and lint is a CI gate like run: a typo'd pattern silently passing is the failure worth catching. --allow-no-match downgrades it to 0, same flag as run. Mixed matches lint the lintable subset silently, like eslint.

Registered as a local command (no auth, no API), so the generated skill table advertises it to agents as safe to run and retry. Minor changeset included.

Testing

bun run typecheck
bun run lint
bun run format:check
bun run knip
bun test
bun run generate

1874 tests pass, 18 of them lint's. Also verified end to end with the built bundle (node dist/cli.js) in a temp project: a bare run reports the flows, a helper, and a src/pages/ page object while .json/.md files never appear; a pattern narrows; a .json-only pattern is a no-match (exit 2 with the --allow-no-match hint, 0 with the flag); .eslintrc.json downgrading a rule to warn turns all findings into warnings + exit 0; cross-file type-aware findings fire; and a parent directory's .eslintrc.json outside the project is ignored.

Checklist

  • Changes follow the code style of this project
  • Self-review completed
  • Tests added/updated (or not applicable)
  • No breaking changes (or described below)

@coderabbitai

coderabbitai Bot commented Aug 29, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Comment @coderabbitai help to get the list of available commands.

@JAllen2022 Jason Allen (JAllen2022) changed the title feat(cli): add qawolf lint to check flow files against QA Wolf's rules feat(cli): add qawolf flows lint to check flow files against QA Wolf's rules Aug 31, 2026
JasonAllenQAWolf and others added 4 commits September 2, 2026 15:02
Five problems, all in the path a bare `qawolf flows lint` takes:

Generated output was linted. Without a pattern the command selects every
source file in the project, and only node_modules was excluded, so a repo
that builds to dist/ failed the gate on its own compiled output.

The team config search could escape the project. It stopped at the package
root, but that is unknown whenever the selected files span more than one
package, which is every monorepo. The walk then ran to the filesystem root
and could read a stray .eslintrc.json from the user's home directory.

Every file was linted at once, each building its own TypeScript program
and its own linter. The linter is the same for all of them and the repo
already has a bounded-concurrency helper.

An unreadable file took the whole run down with a stack trace and no
report. It is now named in the output and fails the gate, because a gate
that passes files it never checked is worse than one that stops.

The lint domain reached into the flows domain, which the architecture
forbids and nothing caught: the boundary rules are listed per domain by
hand and lint was never added.
The fixes pushed both the module and its test past the line limits oxlint
enforces, so file selection and the team config lookup each move out on
their own. Also reformats the lint config the fixes edited, which was not
written the way oxfmt writes it.
@JAllen2022

Copy link
Copy Markdown
Contributor Author

Closed in favour of qawolf/platform#31630, which solves NOVA-1586 without publishing anything.

The lint gate Tester actually needs already shipped: createRunEntryPointLint refuses a qawolf runner run whose entry point has errors, using the linter already loaded in the pod. What was missing was a way to lint on demand, and #31630 adds that as qawolf-lint <file...> — a bin on @qawolf/tester-session that reaches the shell through PATH.

That covers Tester and the session console without publishing @qawolf/workflow-linter, and avoids what this route cost: the CLI bundle 15MB → 43MB, the standalone binary 90MB → 146MB, and TypeScript shipping to every CLI install.

The gap it leaves is customers linting on their own laptops or in CI, which nothing covers today. Worth its own ticket if that turns out to be wanted.

@JAllen2022

Copy link
Copy Markdown
Contributor Author

Reopened. The earlier close was reversed — this is the route we're taking after all.

The reasoning is on qawolf/platform#31630, which is now closed in favour of this: Tester's capabilities are moving onto qawolf ... commands through the generic shell tool (#32522 dropped the getScreenshot tool for qawolf runner screenshot), and the CLI already reaches the pod console, the gym, and customer laptops where a platform-internal binary reached only the console.

Still blocked on qawolf/platform#32443 for the published @qawolf/workflow-linter@1.0.0, and bun.lock still needs regenerating once that lands.

| `qawolf environment update` | write | Update an environment owned by the caller's team and return it in the environment.get shape. Omitted fields remain unchanged. |
| `qawolf flow addTag` | write | Assign an existing tag to the selected flows. Create tags with tag.create. |
| `qawolf flow update` | write | Move a flow between draft and active readiness. The other statuses shown in the app are derived and cannot be set. |
| `qawolf flows lint` | local | Lint source files matching [pattern], or every .ts/.js file when omitted, with QA Wolf's rules, honoring the project's .eslintrc.json |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does this belong on the flows namespace?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I am not even sure if we need this, if we publish our own rules and add them to the .eslintrc.json as a package, users can use eslint command directly instead of going throught our cli

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants