Running check, verify, or test against a .taskless/ tree from an older schema migrates it: files move, taskless.json and .gitignore are rewritten. Measured on a pristine copy of this repo's own tree (schema version 2 → 5), that is six tracked files changed.
This is not a request to stop migrating. check and verify require a specific layout to function at all, so for anyone pinned to * or @latest the alternatives are an automatic migration or a hard failure on every run, and the failure is plainly worse. Migration is a precondition, not a side effect.
The problem is that nothing the caller can read says it happened.
What the caller actually gets
| Surface |
Migration signal |
| Human stderr |
one line: Migrating to latest .taskless/ schema... |
| Human stdout |
nothing |
--json stdout |
nothing — the envelope has no migration field |
| Exit code |
unchanged |
So the machine interface is silent. A CI script reading {"success":true,"results":[]} has no way to learn that the working tree was rewritten underneath it, and a person looking at six changed tracked files afterwards has nothing connecting them to the command they ran — the notice names no file, no source version, and no target version.
Two things that are already fine — please don't re-investigate
Both were suspected and neither reproduced on 0.10.2:
CI=1 does not suppress the notice. It prints in both environments.
--json stdout is valid JSON. The notice goes to stderr, correctly, so JSON.parse(stdout) is safe. (Banner noise seen through pnpm cli comes from pnpm, not from the CLI.)
The gap is not that the notice is misrouted. It is that stderr is the only place it exists.
Suggested shape
- Add a migration record to the JSON envelope — at minimum
from and to schema versions, ideally the paths that moved. A consumer can then log it, fail on it, or ignore it deliberately.
- Have the human notice name what changed, or how many files, rather than announcing only that something did.
Open question: does test need this?
test migrates, but it may be inheriting rather than requiring it. The call chain is ensureTasklessDirectory → runMigrations, reached from commands/check.ts, commands/verify.ts, commands/init.ts, commands/onboard.ts, and rules/verify.ts:497 — the last of which is how test picks it up, since test runs verify first.
Unlike a whole-project check, test is handed an explicit rule directory. Whether it needs the entire tree at the current schema, or only the path it was given, is worth establishing before this is designed — it may be that test can operate on an old-layout tree for a path that is already well-formed.
Measured command-by-command, for reference:
| command |
migrates |
info |
no |
detect |
no |
verify |
yes |
test |
yes |
check |
yes |
Running
check,verify, ortestagainst a.taskless/tree from an older schema migrates it: files move,taskless.jsonand.gitignoreare rewritten. Measured on a pristine copy of this repo's own tree (schema version 2 → 5), that is six tracked files changed.This is not a request to stop migrating.
checkandverifyrequire a specific layout to function at all, so for anyone pinned to*or@latestthe alternatives are an automatic migration or a hard failure on every run, and the failure is plainly worse. Migration is a precondition, not a side effect.The problem is that nothing the caller can read says it happened.
What the caller actually gets
Migrating to latest .taskless/ schema...--jsonstdoutSo the machine interface is silent. A CI script reading
{"success":true,"results":[]}has no way to learn that the working tree was rewritten underneath it, and a person looking at six changed tracked files afterwards has nothing connecting them to the command they ran — the notice names no file, no source version, and no target version.Two things that are already fine — please don't re-investigate
Both were suspected and neither reproduced on
0.10.2:CI=1does not suppress the notice. It prints in both environments.--jsonstdout is valid JSON. The notice goes to stderr, correctly, soJSON.parse(stdout)is safe. (Banner noise seen throughpnpm clicomes from pnpm, not from the CLI.)The gap is not that the notice is misrouted. It is that stderr is the only place it exists.
Suggested shape
fromandtoschema versions, ideally the paths that moved. A consumer can then log it, fail on it, or ignore it deliberately.Open question: does
testneed this?testmigrates, but it may be inheriting rather than requiring it. The call chain isensureTasklessDirectory→runMigrations, reached fromcommands/check.ts,commands/verify.ts,commands/init.ts,commands/onboard.ts, andrules/verify.ts:497— the last of which is howtestpicks it up, sincetestrunsverifyfirst.Unlike a whole-project
check,testis handed an explicit rule directory. Whether it needs the entire tree at the current schema, or only the path it was given, is worth establishing before this is designed — it may be thattestcan operate on an old-layout tree for a path that is already well-formed.Measured command-by-command, for reference:
infodetectverifytestcheck