A flat-YAML task tracker that is just files. No daemon, no database, no git hooks. diarie.dev
issue-tracker cli nodejs agent-friendly

feat!: the store is a diarium — `diarium/` or `.diarium/`, posture on disk #12

Open voxpelli.com
Open opened by and assigned to voxpelli.com
targeting main

Files decisions diarie-pos, diarie-loc and diarie-ids, and implements the first.

The store is named diarium, in exactly one of two forms: visible diarium/ or dotted .diarium/. Which one exists on disk IS the choice — no config key, no environment variable, nothing to resolve before the store can be found, and git mv flips it whenever, because the reader accepts both. Repos legitimately differ in posture the way myndigheter do; the statute fixes the register, the myndighet chooses whether it is a webbdiarium.

TRACKER_DIR is removed rather than renamed. A singular constant cannot answer "which of two forms is on disk", and leaving it as 'diarium' would have kept join(root, TRACKER_DIR, 'tasks') working on visible stores while silently missing dotted ones — this package's founding defect in a new coat. Resolution is now trackerDirIn(root), and the compiler found every call site.

Three failure modes the pair creates, each pinned by a test:

  • both forms present -> ETWOSTORES naming both paths. A precedence rule is how the losing store becomes a file nobody reads and everybody keeps editing.
  • a legacy .diarie/ -> named, with the git mv, and it HALTS the upward walk. Stepping over it to an ancestor diarium/ would read a real store belonging to someone else: exit 0, no warning, entirely the wrong backlog.
  • init beside a legacy store -> ELEGACY. The read side answers a legacy store with ENOSTORE, and a helpful caller answers ENOSTORE by running init; without this the project ends up with two backlogs and nothing pointing at the full one.

listTaskFiles now throws on an unproven root instead of returning an empty list — it stopped being total when the store dir stopped being a constant, and it is a public export.

migrate's overwrite guard checks every store name, not just the one the run would write, and gains --dotted to match init.

This repo adopts the dotted posture while the shipped default stays visible; that divergence is deliberate and recorded in diarie-pos's Revisions.

BREAKING CHANGE: TRACKER_DIR is removed from diarie/schema — use TRACKER_DIRS, TRACKER_LABEL, and trackerDirIn(root). The store is diarium/ or .diarium/; run git mv .diarie diarium (or .diarium) to migrate an existing store. TASKS_ROOT is now DIARIUM_ROOT, and a stale TASKS_ROOT is a hard EUSAGE rather than a silent ignore. New exit-1 codes: ETWOSTORES and ELEGACY.

Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com

Commits 37

All changes

refactor: handling of unknown fields in bd mapping
fix: make sure we don't try to write to non-dirs
refactor: tweak the compositiom of npm scripts
chore: clarify `.gitignore` role in `CLAUDE.md`
chore(tasks): record what a five-lens review found, and change nothing else
fix(brand): generate the page's stated size instead of asserting it
fix(brand): give hektograf a paper ink so the marker wash reaches AA
polish(brand): obey the Measure Rule and settle the micro-label size
build: scope check:ast-grep away from agent-tooling state
build: track the tooling decisions, ignore the tooling output
feat(design-sync): rebuild the Claude Design bundle instead of excavating it
fix(brand): make the specimen sheet reflow-safe and screen-readable
style(brand): converge the specimen sheet onto the documented type steps
refactor(brand): share one base between the specimen sheet and the bundle
fix(brand): correct the stampel-dov contrast figure on papper
chore(tasks): record three rows this pass deliberately left open
docs(brand): document the refusal cards and the second display ramp
refactor(build): parse the deploy pages instead of pattern-matching them
feat(brand): publish the brand book, unlisted
style(brand): fold the chapter numbers into their headings
refactor(brand): adopt the hallinje token for every hairline
fix(brand): load tokens.css rather than inlining a copy of it
feat(brand): name the herbarium green and the stämpel paper tints
chore(tasks): record five deferred rows from the brand pass
docs: record what a tool answering a narrower question than asked costs
fix(brand): the mono subset was missing six codepoints the pages render
style(brand): collapse the type ramp to one prose size and one heading cut
test(brand): assert diarie.dev's CLI mocks byte-for-byte against the binary
fix(docs): the ast-grep bullet went stale in the commit that changed the rules
style(md): reformat every tracked markdown to the shared preset
build: adopt @voxpelli/ast-grep-rules and @voxpelli/remark-preset
docs: record the diarium-pair rules in CLAUDE.md, correct two SYNERGY claims
docs(brand): diarie.dev states the store pair and the whole exit-code set
fix(store): resolve the store from disk, never from a flag or an assumption
refactor(errors): make the exit-code vocabulary a checked contract
fix(migrate)!: refuse rather than silently discard data the store cannot hold
feat!: the store is a diarium — `diarium/` or `.diarium/`, posture on disk