Overview
Taskless rules live in your repository and flag patterns your team has decided to catch. When a rule matches, it reports the issue and can suggest a fix. Your coding agent also reads your rules while it writes code, so problems get caught during generation instead of in review.
A rule is enforced by an engine
Section titled “A rule is enforced by an engine”Rules differ by what enforces them, and that follows from what the rule is decided by. There are four engines, covered in full on the Engines page:
| The rule is decided by… | Engine | Enforced by |
|---|---|---|
| a tool your repo already runs | linter | that tool, in your own lint step |
| one file’s syntax tree | sg |
taskless check |
| a document’s words | vale |
taskless check |
| more than one file, or evidence outside the files | runtime |
taskless check |
Two properties are worth carrying around.
sg and vale rules are inert data. Running one executes nothing and makes no network call, so they always run, offline and with no account.
Runtime rules execute a check.ts, so they run only when that code has been verified. See Runtime rules for the layout and Security for the verification model.
Linter rules are enforced by your toolchain, not by Taskless. taskless check does not invoke ESLint, Ruff, RuboCop, or Stylelint. It writes the rule in that tool’s dialect; the tool runs it the next time you lint.
What a rule looks like
Section titled “What a rule looks like”An sg rule matches against your code’s syntax tree, so it catches a pattern regardless of formatting, variable names, or whitespace:
id: no-console-loglanguage: TypeScriptrule: pattern: console.log($$$ARGS)severity: warningmessage: Avoid console.log in production code. Use a structured logger instead.fix: logger.info($$$ARGS)| Field | Purpose |
|---|---|
id |
Unique identifier for the rule |
language |
Target language (TypeScript, JavaScript, Python, etc.) |
rule |
The syntax-tree pattern to match against |
severity |
error, warning, info, or hint |
message |
Human-readable explanation of the issue |
fix |
Optional auto-fix replacement pattern |
A vale rule looks quite different, because it matches words rather than structure. See Vale.
How rules get authored
Section titled “How rules get authored”You don’t hand-write rule YAML. You describe the pattern in natural language and let your coding agent author it.
Taskless is local-first: it authors the rule on your machine whenever it reasonably can, and only reaches for the Taskless service when a rule genuinely needs it. Start with routing, which reads your repository and decides which engine fits:
npx @taskless/cli agent routeSee Authoring rules for how that decision gets made.
agent returns a recipe, the steps for an agent to follow. The commands that recipe runs are still there when you want them directly:
npx @taskless/cli rule create --from <file>--from points at a JSON file describing the rule: a prompt in natural language, plus optional successCases and failureCases. Add --anonymous to use the local-only path with no API call.
Where rules live
Section titled “Where rules live”Every rule occupies its own directory, under its engine:
your-repo/└── .taskless/ ├── taskless.json # Install manifest (targets, CLI version) └── rules/ ├── sg/ │ └── no-console-log/ │ ├── no-console-log.yml │ └── .tests/ │ └── no-console-log-test.yml ├── vale/ │ └── no-simply/ │ ├── no-simply.yml # the style │ ├── .vale.ini # the scope │ └── .tests/ │ ├── fail/bad.md │ └── pass/ok.md └── runtime/ └── env-read/ ├── captures/ ├── check.ts └── .tests/Writing a rule means creating one of those directories; deleting a rule means deleting it. Nothing outside it is touched either way.
The dot on .tests/ is required. See Engines for what breaks without it.
Validating a rule
Section titled “Validating a rule”Two commands close the authoring loop, whichever engine wrote the rule:
npx @taskless/cli verify .taskless/rules/sg/no-console-log/npx @taskless/cli test .taskless/rules/sg/no-console-log/verify checks that the rule has the components its engine requires. test runs its cases, after verifying it first. For Vale rules, verify is not optional. An incomplete Vale rule reports nothing rather than failing, so a passing check proves nothing until it has been verified.
Deleting a rule
Section titled “Deleting a rule”npx @taskless/cli rule delete <id>That takes a bare rule id, not a path. The CLI works out which engine holds it and removes the rule directory, then also tries to remove a legacy metadata sidecar at .taskless/rule-metadata/<id>.yml. That sidecar is one this CLI never writes, so expect it to be absent; the removal is there for a directory someone created by hand. What deleting by hand really skips is the ambiguity check below.
Ids are not unique across engines. If two engines hold the same id, the CLI refuses rather than guessing which you meant.