Skip to content

reindex cannot ingest an artifact whose filename differs from slugify(title): it resolves by a recomputed path, not by the file it scanned #495

Description

@fedorovvvv

Summary

When an artifact is ingested into the index for the first time — a fresh clone, a new worktree, any checkout whose .forgeplan/lance/ does not have it yet — forgeplan reindex derives the expected path from the artifact's current title and fails with create failed: file not found if the file on disk is named differently. It does this even though it just read that very file to learn the title.

The artifact is then invisible to every query (list, health, get, link), and nothing reports the gap: list prints 1 artifact(s) total for a project that has three.

The defect is checkout-dependent. In the author's checkout the rows already exist, reindex reports those files as unchanged, and everything looks healthy. Every other checkout of the same commit loses them.

Reproducer

forgeplan 0.37.0, empty directory:

$ git init -q && forgeplan init -y
$ forgeplan new note "Замер 87 мс против 6060 мс"    # -> NOTE-001-87-6060.md
$ forgeplan new note "Original english title"         # -> NOTE-002-original-english-title.md
$ forgeplan new note "Control note left untouched"    # -> NOTE-003-control-note-left-untouched.md

# 1) give NOTE-001 a readable filename
$ mv .forgeplan/notes/NOTE-001-87-6060.md .forgeplan/notes/NOTE-001-index-note.md
# 2) correct NOTE-002's title by hand (both frontmatter blocks); the file keeps its name
$ sed -i 's/Original english title/Corrected english title/g' .forgeplan/notes/NOTE-002-*.md

$ forgeplan reindex
Reindex complete: 0 synced, 3 unchanged, 0 removed, 0 orphan relations, 0 errors, 0 id-collisions.
$ forgeplan list          # all three listed, looks fine

$ git add .forgeplan && git commit -qm repro
$ git clone -q . ../clone && cd ../clone      # no .forgeplan/lance, like any fresh checkout
$ forgeplan reindex
  WARN NOTE-002 — create failed: file not found for NOTE-002 at notes/NOTE-002-corrected-english-title.md
  WARN NOTE-001 — create failed: file not found for NOTE-001 at notes/NOTE-001-87-6060.md
Reindex complete: 1 synced, 0 unchanged, 0 removed, 0 orphan relations, 2 errors, 0 id-collisions.
$ forgeplan list
ID        Kind  Status  Title
NOTE-003  note  draft   Control note left untouched

  1 artifact(s) total

The path in each warning is not a file that exists — it is slugify(current title). Reindex read NOTE-001-index-note.md to obtain the title, then went looking for a different file.

Running reindex again does not help: the same artifacts fail the same way on every run.

Non-ASCII titles make this the default path, not an edge case

The slug keeps only ASCII letters and digits:

  • "Замер 87 мс против 6060 мс" becomes 87-6060 — the meaning is gone, only the numbers survive.
  • "Заметка про индекс" is now refused by forgeplan new: invalid slug: title produces empty slug (all non-ASCII or empty). Earlier versions accepted the same input and wrote …-untitled.md; those files still exist in repositories.

For a team that writes titles in a non-Latin script, the canonical filename carries no information, so people and agents give files readable names — and each such file becomes unresolvable in the next fresh checkout.

Impact, measured on a real repository

  • 27 of 129 artifacts were invisible in the main checkout. Two consecutive reindex runs: 101 unchanged, 29 errors.
  • forgeplan health printed a coherent report over 79% of the graph without marking it as partial. Orphans were reported as 5; with the complete index there are 17.
  • Mutations on the partial index are destructive: activate re-saved an artifact and silently dropped a links: entry whose target the index could not see.
  • forgeplan new allocates the next number from the index, so on a partial index it hands out ids that already exist on disk.

Workaround we applied: renamed every affected file to its computed slug with git mv — accepting names such as PROB-024-0.md and …-untitled.md — and added a disk-vs-index check to catch recurrence. After that, reindex in a fresh worktree reports 0 orphan relations, 0 id-collisions.

Related, but distinct

Suggested fix

  1. During ingestion, use the path of the file actually being scanned. A recomputed slug should never be used to locate a file that was just read.
  2. If a canonical filename is desirable, report a mismatch as a warning, or offer an explicit rename command — rather than failing to index the artifact.
  3. Let list and health state when files were skipped (3 files on disk, 1 indexed, 2 skipped), so a partial index cannot present itself as complete.
  4. Consider a Unicode-aware slug (transliteration, or keeping letters) so non-Latin titles do not collapse to digits or to nothing.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions