0.4.0 - 2026-08-11
Release Notes
A task's ID is derived from its title rather than stored, so a change to the
derivation rules moves every unpinned ID while every content hash stays
identical — and incremental indexing, which keys off those hashes, never
notices. This release makes the index aware of the rules it was built under,
and adds lash migrate-ids to repair the @depends-on references a past move
left dangling.
Added
lash migrate-idsrewrites@depends-onreferences left dangling by a
task-ID derivation change. It reports by default and writes only with
--write;--forgetdiscards the pending renames for a project that would
rather repair by hand. Only whole references on@depends-on:lines are
rewritten — prose mentioning an old ID is left alone, and so is the
unqualifiedold-idform, since a bare token can name a file as readily as
a task.
Fixed
- Stale task IDs no longer survive
lash index. A task's ID is derived from
its title and is not written to the Markdown unless pinned with@id:, so a
release that changes the derivation rules moves every unpinned ID while every
content hash stays identical — and incremental indexing, which keys off those
hashes, never re-derives. A file nobody had edited kept serving IDs from
rules no longer in force:lash showprinted the stored ID,lash lint
derived a different one and refused to resolve it, andlash check-index
called the index in sync throughout. The index now records the derivation
version it was built under and re-derives every file when that does not
match, so an upgrade repairs itself on the nextlash index. The IDs that
moved are reported, and recorded forlash migrate-ids— the re-derive is
the only moment both spellings exist.lash index --force, which wipes the
database, salvages the mapping before doing so — it was the workaround people
reached for, and it used to destroy the one record that could repair the
references it broke. lash check-indexcompares stored task IDs against freshly derived ones
instead of only comparing content hashes, which by construction cannot see a
change in how IDs are derived from unchanged content.lash lintnow says when an unresolved reference points at a task ID that a
derivation change moved, rather than at a task that is missing. Without it
the error reads as a false positive: the ID it names is exactly the one
lash showprints back.lash add --before/--afternow accept the file-qualified task ID thatlash showandlash listprint (index#beta-task), not just the bare slug. The
target file is already fixed by--file, so the qualifier was redundant, but
passing it back failed with "task not found" — which read as the task being
missing rather than the argument being spelled the way lash spells it. A
qualifier naming a different file is still rejected, since that means the
task was expected somewhere it is not. The not-found error now lists the IDs
that do exist at that level.lash add --dry-runnow resolves the request instead of echoing it back. It
never opened the target file, so it reported success for a--beforenaming
a task that did not exist and the real add then failed on the same argument.
Dry run and the real add now share one code path, and dry run reports the
insert line it resolved.
Install lash 0.4.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/fixture-dev/lash/releases/download/v0.4.0/lash-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/fixture-dev/lash/releases/download/v0.4.0/lash-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install fixture-dev/tap/lashDownload lash 0.4.0
| File | Platform | Checksum |
|---|---|---|
| lash-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| lash-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| lash-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| lash-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| lash-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |