Skip to content

Report index gaps instead of writing them (#15) - #16

Merged
MartinForReal merged 4 commits into
mainfrom
feat/report-index-gaps
Aug 3, 2026
Merged

MartinForReal merged 4 commits into
mainfrom
feat/report-index-gaps

Conversation

@MartinForReal

Copy link
Copy Markdown
Owner

Closes #15. Follow-up to the closing comment on #8.

The ask

A vault that hand-writes its index.md files can't use index generation, because generation writes a shape the vault didn't choose. The reporter of #8 said so on the way out, and asked for the maintenance half instead:

  • flag folders without index.md files
  • flag missing entries (doesn't matter where in file)

What this adds

A Report index gaps setting, off by default. On, a vault scan warns:

  • where a folder has no index.md at all, and
  • where an existing one doesn't list a note anywhere in the file.

It writes nothing. Both are warnings, never errors — §8 makes an index optional and §11 forbids failing a bundle for a missing one, so this reports that a vault's own convention has slipped, not that the bundle is non-conformant. Findings are filed against the index's own path, since that is the file to edit, and it reads as "this should be here" in the case where it isn't.

"Doesn't matter where in file" is the whole requirement, so the check is the reading mergeIndex already does to decide what to append — an entry counts under whatever heading, in whatever order, in whatever grouping its author invented. Rather than write a second implementation that would drift from the first, mergeIndex now returns that reading alongside the merged text and unlistedEntries stops one step short of the append. Building a folder's listing and writing it are likewise now separate steps, so the report can ask what a folder's entries are without the generator's side effect of filling in a subfolder's missing index on the way past.

Also: the report pane's Open note → now appears only when the path resolves to a file. It was always shown and silently did nothing otherwise — which a finding that names the index.md a folder hasn't got would hit every time.

Verification

npx tsc --noEmit and npm run build are clean. Behaviour was checked with two throwaway harnesses (not committed — this repo has no test framework), 21 assertions, all passing.

Against a hand-authored index in the reporter's own shape — one H1 naming the directory, prose, H3 groupings the plugin would never generate, a numbered list, a wikilink, a bare subdirectory link, and nothing in the plugin's order:

  • a listing that names everything reports no gaps
  • removing one entry reports exactly that one; removing two reports both
  • a subdirectory linked bare (deep/) still counts as listed
  • the string handed in is not modified
  • what the report names is exactly what the merge would append, and after merging there is nothing left to report

And over a stubbed vault walked through the plugin's own folder logic: excluded folders are skipped and the root is included; a folder with no index reports one warning filed at the index it hasn't got; a root index that never names raft.md reports exactly that; every finding is a warning citing §8; and nothing is written — no index.md is created, and the existing one stays byte-identical.

Deliberately out of scope: the reporter also attached their own index.md/log.md contracts. Their log rules are already largely enforced (validateLog covers no-frontmatter, ISO date headings, newest-first). Their index structural rules — exactly one H1 naming the directory, H3 subgrouping, a description trailer on every link — are vault conventions rather than OKF, so they aren't checks to impose by default.

The changelog entry sits under ## [Unreleased]; this is a new capability, so it's a minor bump when you want to cut one.

🤖 Generated with Claude Code

MartinForReal and others added 4 commits August 3, 2026 12:07
A vault that writes its own `index.md` files — its own headings, its own
order, its own groupings — can't use index generation, because generation
writes a shape the vault didn't choose. The closing comment on #8 asked for
the maintenance half instead: flag a folder with no `index.md`, and flag the
entries a listing is missing, "doesn't matter where in file".

That last clause is the requirement. A note counts as listed if the index
links at it anywhere, under any heading, in any order — so the check had to
be the reading `mergeIndex` already does to decide what to append, not a
second one that would drift from it. `mergeIndex` now returns that reading
alongside the merged text, and `unlistedEntries` stops one step short of the
append.

Both findings are warnings. §8 makes an index optional and §11 forbids
failing a bundle for a missing one, so this says a vault's own convention has
slipped, not that the bundle is non-conformant. Off by default.

Building a folder's listing and writing it are now separate steps, so the
report can ask what a folder's entries are without the generator's side
effect of filling in a subfolder's missing index on the way past.

The report pane's "Open note →" now appears only when the path resolves to a
file. It was always shown and silently did nothing otherwise, which a finding
that names the `index.md` a folder hasn't got would hit every time.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The report pane labelled each finding with the file name alone. Two notes
named the same in different folders were one row repeated, and an index
finding — which names `index.md` — said nothing at all about which folder
it meant, so the gap report added in this branch was unreadable exactly
where it mattered.

The path is split so the folder part carries the shrink factor: at a narrow
pane width the folders ellipsize and the file name the row ends in stays
legible. The `aria-label` already carried the full path and is unchanged.
The report pane had two ways of wasting a reader's attention. Every row
carried its full vault-relative path, so a folder with a dozen findings
spent most of a sidebar's width writing its own name a dozen times. And
opening a note re-validated it -- that has always happened -- but sent
the verdict to the status bar alone, a count with the detail in a
tooltip, while the pane went on showing whatever the last vault scan had
found, every row collapsed. The note in front of you was the one thing
the pane wouldn't tell you about.

Rows are now grouped under a header naming the folder once, and the note
in the editor gets a section of its own at the top, listed open.

Grouping: results are bucketed by their folder, error-bearing folders
sort first and then alphabetically, and rows sort the same way inside
each group. Groups start expanded -- a header is there to say where a
finding lives, not to hide it -- and collapse per folder, with their
collapsed state pruned against the folders of the current results the
way per-file expansion already was. The full path stays on each row's
aria-label, so it never has to be reconstructed from what happens to be
rendered above it.

Active note: the plugin holds the active note's findings in a field of
its own and pushes them to the pane on file-open, on the live check
after a save, when a pane is opened later, and at the end of a scan --
that last one because a Fix all would otherwise leave the section
contradicting the list below it. They are kept out of the scan results
on purpose: those count the vault, and one note's verdict arriving
between scans would have the summary chips describing a vault nobody
scanned. Opening something that isn't a markdown note clears the
section rather than leaving a stale verdict standing.

renderBody() is split into summary / active / list, which also moves the
two empty-state early returns down into the list -- before, "no scan
yet" would have suppressed the active section, which is exactly the case
where it earns its place.
`Report index gaps` finds a gap by walking the folder tree, and only
`scanVault` walks it. `validateActive` runs `validateContent` alone, so
opening a folder's `index.md` dropped the very findings the scan files
against that path: the pane's Active note section and the status bar both
called the file clean while its own row in the list below carried the
warnings. Switching tabs away and back was enough to make the pane
contradict itself about one file.

Adds `indexGapsForActive`, which runs the same folder pass for the one
folder an open `index.md` describes. It is guarded on `folderIsIndexable`,
the predicate the scan's walk prunes by. Both halves of that predicate are
prefix-closed, so a folder passing implies every ancestor passes, and
checking the file's own folder is exactly equivalent to re-deriving the
walk — an index under an excluded folder, or under the config dir, picks up
nothing merely by being open.

The merge lands before `updateStatus`, so the status bar counts the gaps
too, and `openReport` reads the same list, so `Validate active note` does
as well.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@MartinForReal
MartinForReal merged commit e98126b into main Aug 3, 2026
1 check passed
@MartinForReal
MartinForReal deleted the feat/report-index-gaps branch August 5, 2026 09:20
@MartinForReal MartinForReal mentioned this pull request Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Flag index gaps instead of generating them, for hand-authored vaults

1 participant