Skip to content

fix: linter blind spots — frontmatter comments, bad status/dates, published personal store - #7

Merged
aoc81 merged 3 commits into
mainfrom
fix/linter-blind-spots
Sep 30, 2026
Merged

aoc81 merged 3 commits into
mainfrom
fix/linter-blind-spots

Conversation

@aoc81

@aoc81 aoc81 commented Sep 30, 2026

Copy link
Copy Markdown
Owner

Summary

Three places where an entry or setting silently misbehaved while the linter said OK. Each is its own commit.

  • Inline comments leaked into values (e0870ff). The README documents the entry format with trailing # comments, but the parser kept them in the value. status: superseded # … was then read as an unknown, i.e. live, status; recall lost the age flag on commented dates; and files: collapsed into one bogus glob, so edit-time recall never matched. Trailing comments are now dropped as YAML does (outside quotes, after whitespace), with a fast path that keeps hook latency unchanged at 2,000 entries.
  • Values the hooks misread (638ef5e). A typo'd status (superseeded) was served as live guidance, and a non-ISO date:/verified: switched off the entry's freshness checks, yet --strict passed. The default mode now reports both for live entries, and --strict fails on them.
  • Published personal store (db6068d). An in-repo personalStoreDir that isn't gitignored (or already has committed files) is one git add -A away from a PR, and nothing said so. /lore:lint and /lore:stats now add a note. It's a note only, not a --strict failure.

Behavior changes to review:

  • --strict (pre-push and CI) now fails on a bad status or date. This reaches users' CI once they re-run /lore:init.
  • An unquoted value containing # (title: Fix #12) now ends before the #, as in YAML. The documented examples already quote titles.

Test plan

  • Each commit adds tests that fail without it. They include one binding the README's documented example to the parser, plus git-backed tests for the ignore and commit checks.
  • python -m unittest discover -s tests: 181 passed, 1 skipped.
  • Merges cleanly with the other two v0.5.2 branches in any order; the combined suite is green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DMXx6BJswH6xiwdqrwQnEf


Generated by Claude Code

The README documents the entry format with trailing comments:

    status: current         # or: superseded / obsolete
    verified: 2026-01-01     # optional; bump after a re-verify ...
    files: [.github/workflows/ci.yml]   # exact path, dir prefix ...

The frontmatter parser kept them as part of the value, so an entry written
that way parsed as

    status   -> 'current         # or: superseded / obsolete'
    verified -> '2026-01-01     # optional; bump after a re-verify ...'
    files    -> '[.github/workflows/ci.yml]   # exact path, ...'  (one string)

and each field then misbehaved quietly:

- status: anything not in staleStatuses is live, so
  `status: superseded  # replaced by X` served a dead entry as LIVE guidance.
- dates: recall's age flag parses the whole value and gave up, so the
  "verified N months ago" warning vanished (the linter happened to tolerate
  it, so the two disagreed about the same entry).
- files: the list collapsed into one bogus glob -- edit-time recall never
  matched the file and the linter reported it as missing.

Drop a trailing `# comment` as YAML does: a `#` that opens the value or
follows whitespace, outside quotes. `C#`, `"Why #1 wins"` and `"b # c"` in a
list keep theirs. One behavior change follows YAML too: an UNQUOTED value
with ` #` in it (`title: Fix issue #12`) now ends before the `#`; quote it to
keep it -- the documented examples already quote titles.

A `'#' not in line` fast path keeps the hooks at their previous latency
(measured at 2,000 entries).

Tests: comments on scalars, lists, block-list items and an empty key; `#`
inside quoted and unquoted values kept; and the README's documented example
must parse to the values it documents. All three fail without the change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DMXx6BJswH6xiwdqrwQnEf
`/lore:lint --strict` checked only that `files:` exist, so values the hooks
silently misinterpret went through clean:

    status: superseeded     recall: not a stale status -> LIVE guidance
    date: last week         no age flag, no drift triage -- silently
    verified: 2026-02-30    same
    --strict                exit 0, "all entries have valid frontmatter"

The status typo is the dangerous one: the entry was retired on purpose, and
recall resurrects it as current guidance with no [SUPERSEDED] marker.

The default mode now also reports, for live entries, a `status:` that is
neither `current` nor one of `staleStatuses` (the message names the valid
values, config-defined ones included) and a `date:`/`verified:` that is not
a real YYYY-MM-DD date. Both are actionable, so `--strict` -- the pre-push
check and the CI guard -- fails on them. Retired entries are not value-
checked: they drive no live guidance and no freshness flags. The report
header drops "file-ref" since it now covers both, and each kind of problem
gets its own advice line.

This depends on the previous commit: without it, every entry written in the
README's documented format (trailing `# comments`) would be flagged here.

Behavior change for CI: a store with such values will fail `--strict` once
the project refreshes its `.lore/` copies (re-running /lore:init).

Tests: a typo'd status and bad dates fail --strict (both fail without the
change); valid values, a config-defined stale status, and a retired entry
with a bad date stay clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DMXx6BJswH6xiwdqrwQnEf
`personalStoreDir` may resolve inside the repository (this repo uses
`.lore/personal`). It is then private only while .gitignore covers it, and
/lore:init adds that rule only when it creates the directory. Set the key by
hand, or drop the rule later, and the store is one `git add -A` away from a
PR -- with no warning anywhere:

    .lore.json      {"personalStoreDir": "notes"}
    /lore:lint      OK  learnings: all entries have valid frontmatter file refs.
    git status      ?? notes/

The personal store is exempt from the secret scan and the linter precisely
because it is never supposed to be pushed, so this is the one check that
keeps that assumption true. It holds preferences about how a person likes
to work -- the content the two-store split exists to keep out of teammates'
PRs.

`/lore:lint` and `/lore:stats` now add a note when an in-repo personal store
has files committed to git (published already, even if ignored since: the
note gives the `git rm -r --cached` fix) or is not ignored. A store outside
the repository, or no git, stays silent. It is a note, not a `--strict`
failure; the pre-push hook prints it on every push.

Tests (with git): unignored and committed stores are flagged, an ignored
one and one outside the repository are not, and both commands print it.
The lint/stats test fails without the change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DMXx6BJswH6xiwdqrwQnEf
@aoc81
aoc81 merged commit 0e5f826 into main Sep 30, 2026
6 checks passed
@aoc81 aoc81 mentioned this pull request Sep 30, 2026
2 of 3 tasks
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.

2 participants