Skip to content

feat(lint): check the references a run would resolve - #512

Merged
pbertsch merged 8 commits into
mainfrom
feat/lint-references
Oct 27, 2026
Merged

pbertsch merged 8 commits into
mainfrom
feat/lint-references

Conversation

@pbertsch

Copy link
Copy Markdown
Member

Summary

  • probe lint checks the references a run would resolve, without a device: each step and recipe call that matches no built-in step and no recipe, each selector that can never match, and each <name> placeholder that no value defines.
  • An unresolved step or recipe call, and a selector that can never match, fail the command with status 1. An undefined placeholder and a missing use file are warnings and keep the status at 0.
  • The MCP lint tool description and the CLI reference describe the new checks.
  • probe lint and probe test --dry-run share recipe loading.
  • The changelog entry has an example of an error and of a warning.
  • Also fixes the example tests under tests/ and the case of Robot recipe names in probe-convert.

Test plan

  • go vet ./... and go test ./... pass locally
  • probe-convert module tests pass locally
  • CI green: Build, Go unit tests, probe-convert tests, Lint, Disclosure guard

🤖 Generated with Claude Code

…own steps fail probe lint

probe lint now checks the references a run would resolve, without a device or a run. Recipe calls and use statements resolve the way --dry-run resolves them, with the recipes folder from the config. Every step is walked, nested steps included, for selectors that can never match and for <name> placeholders that no example column, recipe parameter, stored value or runtime value defines.

A step or recipe call that resolves to nothing, and a selector that can never match, fail the command. A placeholder no value defines, and a use file that does not exist, are warnings. Environment variables are not checked.

The recipe loading that --dry-run used moves into loadFileRecipes, so lint and dry-run share it.
…resolve from each file

The step `go back to main menu` is read as the built-in `go back` plus a recipe call named `main menu`, so the test failed at run time. probe test --dry-run and probe lint both reported it. The step now inlines the three steps of the recipe it meant: go back, wait 1 seconds, see "Home".

Five test files named tests/recipes/common.probe from the project root. A use path resolves relative to the file that contains it, so those paths now name the file from its own location.

A follow-up will decide whether a phrase like this should be able to name a recipe.
The lexer writes every call word in lower case, and the runner matches a recipe name exactly. A recipe header with capital letters can therefore never be called, and the Robot converter wrote its keyword recipes in the Robot spelling (`Login As User`), so no converted call matched.

The converter now writes keyword recipes in lower case, and a call to a keyword the file defines uses the same spelling, whatever case the body wrote. The checkout golden is regenerated. A new test checks that the converted example defines and calls its recipes in lower case.

A follow-up will normalise recipe headers in the runner, so capitalised headers become callable.
go back takes no arguments, so the words after it on the same line are read as a separate step. Lint reported those words as an unknown step, which does not say why. A call on the line of a go back step now gets the reason in its message.
… diagram names the parser

The grammar and the syntax page say that go back takes no arguments, and that words after it on the same line are a separate step. The grammar's production is unchanged; only its comment is new.

The Studio page now says what the editor's lint marks (parse errors and empty tests), and that probe lint also checks references, selectors and placeholders. Its architecture diagram listed an internal/lint package that does not exist; the Go backend imports internal/parser.

The generated ProbeScript dictionary is regenerated, because its source hash covers the grammar page.
Add an example of an error and of a warning from probe lint to the changelog entry, with the exit status for each.
@pbertsch
pbertsch requested a review from a team as a code owner October 26, 2026 23:07
…omposite bodies, and the report

Adds tests for the checks that had none: an ordinal selector without a position or text, an id selector without a name, an empty in container, a placeholder defined by read with ai, unknown steps in hook bodies and in composite device blocks, the count and zero-state checks, the order of errors and warnings, and the failure mark with the error count. A temporary mutation check removed the composite walk, the read with ai definition and the zero-state rule; each removal fails the new tests.
…the report example

Lint checks hook bodies and composite device blocks, which the dry run skips. The changelog example is the command's output for two sample files, with the colour codes left out. The CLI reference lists the four known limits of the references check, and the MCP lint description says what lint checks beyond the dry run.
@pbertsch
pbertsch merged commit 8193e18 into main Oct 27, 2026
21 of 22 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