Summary
Add a lightweight, no-LLM guardrail that keeps work/active/ honest by surfacing two drift modes automatically: (1) notes marked done but never archived, and (2) related notes piling up loose in the active/ root that should be grouped into a folder. Wired into the SessionStart and Stop hooks (nudge-only) and the weekly review (where you act on it).
Motivation
active/ is meant to hold only current work, but it rots predictably:
- Completed notes stay because "archive later" never comes. A
status: completed note in active/ pollutes the SessionStart task aggregation and any status-filtered Base view.
- A workstream spawns meetings/reports that scatter as loose files in the root instead of grouping, until the folder feels disorganized.
Both are invisible until someone notices. This makes them visible on a cadence, with zero enforcement.
Design
New shared lib .claude/scripts/lib/active-hygiene.ts, consumed by both hooks. Two detectors, both conservative (false negatives preferred over nagging — same philosophy as the existing org-change detector in stop-checklist.ts):
-
completed-not-archived — recursively scan work/active/**.md; flag any whose frontmatter status ∈ {completed, archived, done}. Reuses extractFrontmatterField from lib/session-start.ts.
-
ungrouped multi-file topic — among notes directly in the active/ root (subfoldered = already grouped), tokenise titles (strip a leading YYYY-MM-DD prefix, lowercase, drop tokens <4 chars, numerics, and a small generic-process stopword list: report/sync/plan/notes/draft/meeting/review/…). Flag a token shared by ≥2 root notes. Guards against noise:
- Document-frequency cap: a token in >50% of root notes is treated as a generic vault-wide term (e.g. a team/element name), not a topic → dropped. This is what makes it safe without a vault-specific stopword list.
- Min-root threshold: stay silent until the root has ≥4 loose notes — folders are for taming size, not ceremony.
formatActiveHygiene(report) returns [] when clean, so callers emit nothing.
Wiring
- SessionStart hook: conditional
### Vault Hygiene (active/ drift) section, appended only when there are findings (silent when clean).
- Stop hook: append findings under the existing wrap-up checklist.
/om-weekly: a new "active/ Hygiene Sweep" step — the hooks flag, the weekly is where you act (archive / group / mark stale).
- CLAUDE.md: codify the two conventions this enforces —
- Date naming:
YYYY-MM-DD prefix for point-in-time notes (meetings, syncs, retros, prep); bare title for living project/concept notes.
- Folder grouping: a workstream with >1 note gets
active/<Topic>/ holding the project note + its sub-notes; mirror in archive/YYYY/.
Files
lib/active-hygiene.ts (new), tests/active-hygiene.test.ts (new)
session-start.ts, stop-checklist.ts (wire-in, ~6 lines each)
commands/om-weekly.md, CLAUDE.md (docs/conventions)
Testing
10 unit tests over fixtures (mkdtemp): status detection incl. recursion, cluster detection, stopword + DF guard + min-root behaviour, date-prefix stripping, and the renderer. Full suite stays green.
Out of scope / config
- Never moves files — nudge only.
- Thresholds (DF cap 50%, min 4 root files, stopword list) are constants; could be
vault-manifest.json-configurable in a follow-up if desired.
Summary
Add a lightweight, no-LLM guardrail that keeps
work/active/honest by surfacing two drift modes automatically: (1) notes marked done but never archived, and (2) related notes piling up loose in theactive/root that should be grouped into a folder. Wired into the SessionStart and Stop hooks (nudge-only) and the weekly review (where you act on it).Motivation
active/is meant to hold only current work, but it rots predictably:status: completednote inactive/pollutes the SessionStart task aggregation and any status-filtered Base view.Both are invisible until someone notices. This makes them visible on a cadence, with zero enforcement.
Design
New shared lib
.claude/scripts/lib/active-hygiene.ts, consumed by both hooks. Two detectors, both conservative (false negatives preferred over nagging — same philosophy as the existing org-change detector instop-checklist.ts):completed-not-archived — recursively scan
work/active/**.md; flag any whose frontmatterstatus∈ {completed, archived, done}. ReusesextractFrontmatterFieldfromlib/session-start.ts.ungrouped multi-file topic — among notes directly in the
active/root (subfoldered = already grouped), tokenise titles (strip a leadingYYYY-MM-DDprefix, lowercase, drop tokens <4 chars, numerics, and a small generic-process stopword list: report/sync/plan/notes/draft/meeting/review/…). Flag a token shared by ≥2 root notes. Guards against noise:formatActiveHygiene(report)returns[]when clean, so callers emit nothing.Wiring
### Vault Hygiene (active/ drift)section, appended only when there are findings (silent when clean)./om-weekly: a new "active/ Hygiene Sweep" step — the hooks flag, the weekly is where you act (archive / group / mark stale).YYYY-MM-DDprefix for point-in-time notes (meetings, syncs, retros, prep); bare title for living project/concept notes.active/<Topic>/holding the project note + its sub-notes; mirror inarchive/YYYY/.Files
lib/active-hygiene.ts(new),tests/active-hygiene.test.ts(new)session-start.ts,stop-checklist.ts(wire-in, ~6 lines each)commands/om-weekly.md,CLAUDE.md(docs/conventions)Testing
10 unit tests over fixtures (
mkdtemp): status detection incl. recursion, cluster detection, stopword + DF guard + min-root behaviour, date-prefix stripping, and the renderer. Full suite stays green.Out of scope / config
vault-manifest.json-configurable in a follow-up if desired.