Filed from the cloud side, where we consume @taskless/cli/reference.json to grade rule generation against your reference. Context for why this is coming up now: we hit a defect that the current shape made hard to see, and the fix changes how we model fixtures everywhere.
What we ran into
Our request format carried a rule's examples as anonymous code strings — one blob per case, no filename, no siblings. Generating from your runtime/env-keys-declared prompt therefore produced a rule that failed the fixtures we shipped beside it: your case is a two-file tree
.tests/fail/undeclared/src/config.ts
.tests/fail/undeclared/.env
and ours could only be a single file, so nothing was declared and the passing case flagged too. Reading one file to judge another is the whole reason a rule is runtime rather than sg, and our request shape could not express it. We are fixing that end to end.
What we would like from reference.json
Fixtures expressed as cases, rather than a flat file list whose grouping is implied by path.
Today tests is Array<{ path, content }>, and recovering "which files belong to which case" requires per-engine knowledge:
runtime — a directory under pass/ or fail/ is a case (many files)
vale — a file under pass/ or fail/ is a case (one file)
sg — not a layout at all; a single ast-grep YAML whose valid: / invalid: are snippet lists
We can and currently do derive that. The reason to ask anyway: every consumer has to re-derive it, each of them encoding your layout rules on their side, and that is exactly the kind of transcribed fact that goes stale silently when a layout changes. Publishing the grouping makes the reference authoritative about its own shape.
A shape along these lines would work for us — the field names matter less than the grouping being explicit:
Each inner array is one case's file layout. vale cases are single-element arrays; sg cases are single-element arrays whose one file is the snippet. If sg is better left in its existing YAML form, that is fine too — the grouping is what we are asking for, not uniformity for its own sake.
We are adopting pass / fail as the vocabulary on our side, matching your directory names and the reasoning you recorded for choosing them over ast-grep's valid / invalid.
Compatibility
We are not blocked. We pin your nightly and derive grouping from paths today, and we will keep doing that for as long as the current shape ships. If you take this, version bumping is enough of a signal — we assert it on load and stop rather than interpret a shape we do not understand.
One smaller, separate ask
taskless verify --json reports failures as prose with no constraint identifier:
{"ok":false,"rules":[{"engine":"sg","ruleId":"no-eval-call","ok":false,
"errors":["id: \"mismatched\" does not match the rule's directory …"]}]}
reference.json lists constraints[] with ids, summaries and rationales, and the natural thing for a consumer to do is map a rejection back to the constraint it violated so a failure says what generation must learn. With prose only, that mapping is a text match on your wording, which rots the first time you rephrase an error — so we quote your message verbatim instead and list the engine's constraints as context, explicitly not claiming which one fired.
An id (or constraintId) on each verify error would let us report the rationale you already wrote. Happy to split this into its own issue if you would rather keep them separate.
Filed from the cloud side, where we consume
@taskless/cli/reference.jsonto grade rule generation against your reference. Context for why this is coming up now: we hit a defect that the current shape made hard to see, and the fix changes how we model fixtures everywhere.What we ran into
Our request format carried a rule's examples as anonymous code strings — one blob per case, no filename, no siblings. Generating from your
runtime/env-keys-declaredprompt therefore produced a rule that failed the fixtures we shipped beside it: your case is a two-file treeand ours could only be a single file, so nothing was declared and the passing case flagged too. Reading one file to judge another is the whole reason a rule is
runtimerather thansg, and our request shape could not express it. We are fixing that end to end.What we would like from
reference.jsonFixtures expressed as cases, rather than a flat file list whose grouping is implied by path.
Today
testsisArray<{ path, content }>, and recovering "which files belong to which case" requires per-engine knowledge:runtime— a directory underpass/orfail/is a case (many files)vale— a file underpass/orfail/is a case (one file)sg— not a layout at all; a single ast-grep YAML whosevalid:/invalid:are snippet listsWe can and currently do derive that. The reason to ask anyway: every consumer has to re-derive it, each of them encoding your layout rules on their side, and that is exactly the kind of transcribed fact that goes stale silently when a layout changes. Publishing the grouping makes the reference authoritative about its own shape.
A shape along these lines would work for us — the field names matter less than the grouping being explicit:
Each inner array is one case's file layout.
valecases are single-element arrays;sgcases are single-element arrays whose one file is the snippet. Ifsgis better left in its existing YAML form, that is fine too — the grouping is what we are asking for, not uniformity for its own sake.We are adopting
pass/failas the vocabulary on our side, matching your directory names and the reasoning you recorded for choosing them over ast-grep'svalid/invalid.Compatibility
We are not blocked. We pin your nightly and derive grouping from paths today, and we will keep doing that for as long as the current shape ships. If you take this,
versionbumping is enough of a signal — we assert it on load and stop rather than interpret a shape we do not understand.One smaller, separate ask
taskless verify --jsonreports failures as prose with no constraint identifier:{"ok":false,"rules":[{"engine":"sg","ruleId":"no-eval-call","ok":false, "errors":["id: \"mismatched\" does not match the rule's directory …"]}]}reference.jsonlistsconstraints[]with ids, summaries and rationales, and the natural thing for a consumer to do is map a rejection back to the constraint it violated so a failure says what generation must learn. With prose only, that mapping is a text match on your wording, which rots the first time you rephrase an error — so we quote your message verbatim instead and list the engine's constraints as context, explicitly not claiming which one fired.An
id(orconstraintId) on each verify error would let us report the rationale you already wrote. Happy to split this into its own issue if you would rather keep them separate.