Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,40 @@ While the major version is 0, minor version bumps may contain breaking changes.

## [Unreleased]

### Added

- `lash migrate-ids` rewrites `@depends-on` references left dangling by a
task-ID derivation change. It reports by default and writes only with
`--write`; `--forget` discards 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
unqualified `old-id` form, 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 show` printed the stored ID, `lash lint`
derived a different one and refused to resolve it, and `lash 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 next `lash index`. The IDs that
moved are reported, and recorded for `lash 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-index` compares 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 lint` now 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 show` prints back.
- `lash add --before/--after` now accept the file-qualified task ID that `lash
show` and `lash list` print (`index#beta-task`), not just the bare slug. The
target file is already fixed by `--file`, so the qualifier was redundant, but
Expand Down
19 changes: 19 additions & 0 deletions crates/lash-agent/src/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ lash format [PATH...] # Normalize formatting
# Indexing
lash index # Update SQLite index after changes
lash check-index # Verify database consistency
lash migrate-ids # Show references left dangling by an ID rule change
lash migrate-ids --write # Rewrite those references

# Dependencies & Links
lash graph # Show dependency graph (ascii)
Expand Down Expand Up @@ -196,6 +198,22 @@ lash index --force # Force full reindex
lash check-index # Verify consistency
```

### Task IDs That Moved

A task without an explicit `@id:` gets its ID derived from its title, so a
release that changes the derivation rules moves every such ID. `lash index`
detects this, re-derives the stored IDs, and records what each one used to be.
References written against the old IDs stop resolving at that moment:

```bash
lash migrate-ids # Show what changed and which references it affects
lash migrate-ids --write # Rewrite the references, then re-index
```

`lash check-index` reports stored IDs that no longer match the current rules,
and `lash lint` says when an unresolved reference is one of these rather than a
typo. Pin an ID with `@id:` to keep it stable across future changes.

### Broken References

If `@depends-on` or `@doc` references are broken:
Expand Down Expand Up @@ -340,6 +358,7 @@ pub const TOP_LEVEL_SUBCOMMANDS: &[&str] = &[
"init",
"lint",
"list",
"migrate-ids",
"playground",
"search",
"show",
Expand Down
12 changes: 12 additions & 0 deletions crates/lash-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,18 @@ pub enum Commands {
diff: bool,
},

/// Rewrite references to task IDs that a derivation change moved
#[command()]
MigrateIds {
/// Rewrite the references (without this, only reports what would change)
#[arg(long)]
write: bool,

/// Discard the pending renames without rewriting anything
#[arg(long, conflicts_with = "write")]
forget: bool,
},

/// List tasks matching specified criteria
List {
/// Filter by task ID (supports fuzzy matching)
Expand Down
11 changes: 11 additions & 0 deletions crates/lash-cli/src/commands/check_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ pub fn execute(args: CheckIndexArgs) -> Result<i32> {
verifier_config = verifier_config.with_paths(absolute_paths);
}

// Re-deriving task IDs has to use the project's own parser settings, or
// the IDs compared against are not the ones this project would get.
let parser_config = lash_types::LashConfig::from_root(&project_root).unwrap_or_default();
verifier_config = verifier_config.with_parser_config(parser_config);

let verifier = IndexVerifier::new(&conn, verifier_config);

// Run verification
Expand Down Expand Up @@ -159,6 +164,7 @@ fn output_json_report(report: &lash_db::VerificationReport) -> Result<()> {
"hash_mismatches": report.count_by_kind(lash_db::IssueKind::HashMismatch),
"orphaned_tasks": report.count_by_kind(lash_db::IssueKind::OrphanedTasks),
"orphaned_dependencies": report.count_by_kind(lash_db::IssueKind::OrphanedDependencies),
"stale_task_ids": report.count_by_kind(lash_db::IssueKind::StaleTaskIds),
}),
});

Expand Down Expand Up @@ -224,6 +230,11 @@ fn output_text_report(
report.count_by_kind(lash_db::IssueKind::OrphanedDependencies),
theme,
);
print_issue_count_if_any(
"Stale task IDs (derived under older ID rules)",
report.count_by_kind(lash_db::IssueKind::StaleTaskIds),
theme,
);

// Detailed issue list if requested
if show_diff {
Expand Down
Loading
Loading