From 6677693fad59b4428a51b19bb69cd09c94e9f899 Mon Sep 17 00:00:00 2001 From: Han Ngo Date: Mon, 20 Jul 2026 13:51:00 +0700 Subject: [PATCH 1/3] feat(skills): add memory-tidy, the judgment half of the memory plane --- skills/memory-tidy/SKILL.md | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 skills/memory-tidy/SKILL.md diff --git a/skills/memory-tidy/SKILL.md b/skills/memory-tidy/SKILL.md new file mode 100644 index 00000000..7072bc8e --- /dev/null +++ b/skills/memory-tidy/SKILL.md @@ -0,0 +1,45 @@ +--- +name: memory-tidy +description: Use when auditing, consolidating, or cleaning a repo's .claude/memory store, "dọn memory", "memory tidy", the biweekly memory audit, duplicate or stale memory notes, MEMORY.md index drift (notes missing from the index, index entries with no file), or when a scheduled run asks for the memory-tidy audit of a repository. NOT for the built-in machine-local auto-memory under ~/.claude/projects (not git-tracked, no PR gate) and NOT for editing a single note (just edit it). +disable-model-invocation: false +--- + +# Memory tidy + +## Overview + +Audit a repo's git-tracked `.claude/memory/` store and ship the cleanup as a PR. Core principle: **every verdict cites checkable evidence, and deletions reach main only through a PR merge**, the PR is the operator's approval gate, so the audit itself never needs to ask permission. + +This is the judgment half of the kit's memory plane. The mechanical half is `stats memory-sweep` (lib/stats, SPEC-136): a read-only scanner that never writes. This skill consumes its output and is the only path that edits a store, always behind a PR. + +## Process + +1. **Branch in a worktree first** (native worktree tool). All edits and deletions ride this branch. Touching the store on the current branch is the failure this skill exists to prevent. + +2. **Mechanical pre-pass.** Run `uv run stats memory-sweep` from the kit's `lib/stats/` and filter its JSON for this repo's store: dead path refs per note, stale index entries. Always also diff both directions: the note files on disk vs the entries in `MEMORY.md` (files not indexed are invisible to sessions; entries with no file are ghosts). + +3. **Fan-out judgment.** Split the notes into 2-4 subsystem clusters (roughly one cluster per 40 notes; a tiny store is audited directly) and dispatch parallel read-only agents. Each agent reads every note in its cluster fully and returns, per note: a 1-line gist plus one verdict: + + | Verdict | Required evidence | + |---|---| + | KEEP | distinct fact, referents spot-checked alive | + | MERGE into `` | quote the overlapping claim from both notes | + | STALE | concrete reason: referenced path tested and gone, superseded by a NAMED doc/ADR/newer note, the event concluded (PR merged, tool graduated), or the rule now codified verbatim in an always-loaded file (CLAUDE.md) | + | UNSURE | what only the operator can answer | + + A verdict with no checkable evidence is not actionable: re-check it or downgrade to UNSURE. UNSURE notes are never deleted; list them in the report. + +4. **Danger check.** A note whose recommended fix contradicts current policy is worse than stale, someone may follow it. Fold its still-true diagnostics into the policy-carrying note, then delete it, and say so in the PR body. + +5. **Apply.** Merges preserve incident detail and dates. Fix path-drift and dangling `[[wikilinks]]` in surviving notes. Before deleting any note, grep the store for references to its name and rewrite them. + +6. **Rebuild the index.** Every remaining note gets one short line in `MEMORY.md` derived from its frontmatter `description`. Verify: entry count equals note count, and a grep for every deleted note's name returns nothing. + +7. **Ship.** Follow the repo's own session-close conventions (lab log, backlog rows, proof/gate ledger, whatever that repo's CLAUDE.md requires), commit, push, open a PR whose body lists each removal with its reason. If nothing needed changing, create no branch and report CLEAN. + +## Red flags + +- Editing or deleting notes without the worktree branch: stop, branch, start over. +- "Obviously stale" with no tested referent. +- Keeping a doubtful note because deleting feels safer: the honest form is UNSURE + operator list. +- Hand-editing index lines instead of deriving them from frontmatter. From 0c72cbfa2a283f2a64f4303ae55f51d029c1bed2 Mon Sep 17 00:00:00 2001 From: Han Ngo Date: Mon, 20 Jul 2026 22:59:06 +0700 Subject: [PATCH 2/3] docs(kit): component-intro convention + register memory-tidy --- CONTRIBUTING.md | 21 +++++++++++++++++++++ README.md | 3 ++- docs/CHANGELOG.md | 11 +++++++++++ kit.toml | 6 ++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 11fc4ec0..4ffc7bc4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,6 +56,27 @@ Beyond the table, see [`docs/PHILOSOPHY.md`](docs/PHILOSOPHY.md) "What we explic 4. Use conventional commits: `feat(scope): ...`, `fix(scope): ...`, `docs: ...`. One logical change per commit. Keep spec/task IDs OUT of the subject line (no `TASK-3`, no trailing `(SPEC-002 ...)` tags); that context belongs in the body or PR description. 5. Update `docs/CHANGELOG.md` under an `[Unreleased]` section if your PR is non-trivial (the root `CHANGELOG.md` is a thin pointer stub, SPEC-185). The maintainer moves it to a versioned section at release time. +## Introducing a component (skill, command, agent) + +A new component never lands as a bare file drop; it registers everywhere the kit +already tracks its kind, in the same PR: + +1. **Name**: kebab-case, and the plain-words rule below applies to the name + itself (`memory-tidy`, `skill-review`, `get-api-docs`, a non-engineer PM + should parse it cold). Config keys derived from it use snake_case + (`memory_tidy`). +2. **Description discipline (skills)**: frontmatter `description` starts with + "Use when ..." and lists ONLY triggering conditions, never a summary of the + workflow (an agent will follow a summarized description instead of reading + the body). +3. **Register it**: add the README roster row for its kind (Skill / Command / + Agent table). If it has a consumer-side toggle or an unattended cadence, add + a `[consumer]`-tagged key to `kit.toml` `[features]`, the harness config is + the single control surface; skills and commands themselves always install + (no `[modules]` gate). +4. **Evidence (skills)**: the PR body carries writing-skills RED/GREEN + evidence (baseline failure without the skill, compliance with it). +5. **Changelog**: step 5 above applies, a new component is always non-trivial. ## Plain words rule (2026-07-16) diff --git a/README.md b/README.md index 9e588c7e..70a008a0 100644 --- a/README.md +++ b/README.md @@ -323,6 +323,7 @@ Which hooks BLOCK vs warn vs neither is a declared contract: `docs/architecture. | Skill | What it does | |-------|-------------| | get-api-docs | Fetches curated API docs via Context Hub before coding | +| memory-tidy | Audits a repo's `.claude/memory` store: evidence-gated verdicts, PR-gated merges/deletions, index rebuild (judgment half of `stats memory-sweep`) | | skill-review | Reviews + promotes skill drafts staged by skill-curator | | stats | Queries/renders the ledger read plane (relocated from `lib/stats/skill/` per ADR-0034 so it actually installs) | @@ -372,7 +373,7 @@ dwarves-kit/ lib/board/parse-board.sh The one structured BACKLOG.md parser other tools reuse (SPEC-146): `rows ` (id/status/full-line) and `queue-rows ` (allow-listed `#queue{repo=...,pointer=...}` token extraction -- charset gate, repo self-consistency, `../` traversal hardening, existence check; every failure is a skip with a stderr reason, never a hard error) lib/board/board-mirror.sh The git<->Hermes kanban bridge engine (SPEC-147), backing `board.sh mirror`/`status`: extracts opted-in BACKLOG.md rows (reusing `lib/board/parse-board.sh`) + active mega-goal roadmaps into normalized rows, diffs them (bash + `jq`/awk keyed comparison, no DuckDB) against an incremental NDJSON snapshot, and loads via `hermes kanban` CLI verbs only. Reachable native states are `{triage, ready, blocked, done}` only -- `todo`/`running` were probed live and found to have no durable CLI-only path (auto-promote back to `ready` within seconds); `_target_native`/`_create_flags_for`/`_followup_for` document the full state-mapping + Hermes-CLI-reality findings. `apply-plan` decodes each op's argv over NUL-delimited jq output (never a templated shell string), so a multi-line card body is preserved as one opaque argv element end to end -- the fix for a real bug this build's own live dev-home E2E caught (a newline-delimited decode silently split a multi-line body into extra positional args, rejected by the real CLI, masked by a naive stub). `HERMES_BIN` overrides the binary for tests (a stub logs argv; no real Hermes calls in the automated suite). lib/board/board-writeback.sh The git<->Hermes kanban bridge WRITEBACK leg (SPEC-149), backing `board.sh writeback`: sources `lib/board/board-mirror.sh` (not re-forked) for extract/hash/native-state machinery. `diff` reads each opted-in repo's live board (`hermes kanban --board list --json`, one batched call per board) + the SPEC-147 mirror snapshot, and builds a validated changeset of rows whose Hermes status moved -- validating opted-in repo (defense in depth on top of the mirror's own filter), a legal `backlog.sh` reverse-mapped target status, and the row_hash CONFLICT RULE (a Hermes-side edit applies only if the row's current git-side hash still equals the snapshot's recorded value; git wins, always). A missing or corrupt snapshot REFUSES ALL edits (explicit error, nonzero exit) rather than degrading to "no conflicts, apply everything". `apply` builds the `chore/board-sync` branch in an ISOLATED `git worktree` off the CURRENT HEAD (never the caller's own checkout, never a stale ref -- this is what keeps a concurrent append-only writer's row safe), edits ONLY the Status column of matched rows (reusing `lib/board/backlog.sh`'s own `set`), commits with `actor=hermes` in the body, pushes, and opens a HELD PR via `gh pr create` (argv-only, never a templated shell string; never auto-merged). Snapshot refresh updates ONLY `hermes_status` (to stop re-diffing the same not-yet-merged move); `row_hash` passes through UNCHANGED (the git SoT hasn't actually changed until the PR merges) -- `mirror`'s own idempotence self-heals the row once it does. `GH_BIN` (mirrors `HERMES_BIN`) overrides the `gh` binary for tests; no real Hermes write call or `gh` API call happens in the automated suite. - skills/ Claude-triggered skills (get-api-docs, skill-review, stats) + skills/ Claude-triggered skills (one folder per skill; roster in the Skill table above) rules/ Path-scoped coding-standard templates examples/hello-spec/ Demo: small CLAUDE.md + SPEC.md walkthrough tests/test-hooks.sh Hook behavior assertions diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 598243cb..cbd9efa6 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to dwarves-kit are documented here. ## [Unreleased] ### Added +- **`skills/memory-tidy`, the judgment half of the memory plane.** Pairs with the + read-only `stats memory-sweep` scanner (SPEC-136): evidence-required per-note + verdicts (KEEP / MERGE / STALE / UNSURE) via agent fan-out, danger check for + notes whose advice contradicts current policy, index rebuild with parity + checks, all confined to a worktree branch and shipped as a PR (the operator's + approval gate). Consumer cadence toggle `[features].memory_tidy` in `kit.toml` + (`[consumer]`, default off); the scheduled runner lives consumer-side + (ops-toolkit `tools/memory-tidy/`). CONTRIBUTING gains an "Introducing a + component" convention (naming, description discipline, registration, + evidence) so new skills/commands/agents never land as unregistered file + drops. First production run: tieubao/ops-toolkit#972. - **`sync` one-way insert-only push to a foreign team board** (`notion-taskboard` app, SPEC-003, implements ops-toolkit ID-138). Pushes a repo's board rows out to a team-OWNED Notion board (create page, never update, never read for merge, diff --git a/kit.toml b/kit.toml index 2c20e01e..eb47d76e 100644 --- a/kit.toml +++ b/kit.toml @@ -83,6 +83,12 @@ learning_ledger = true # [consumer] the orchestration is an external sk # (ops-toolkit/dotfiles, SPEC-126); the kit only # collects (weekend-batch.sh) + routes. Toggle kept # here so the harness config is the single surface. +memory_tidy = false # [consumer] the unattended biweekly memory-audit cadence. + # The judgment skill itself ships with the kit + # (skills/memory-tidy, pairing with `stats memory-sweep`) + # and is always available; this flag gates only the + # scheduled consumer-side runner (e.g. ops-toolkit + # tools/memory-tidy LaunchAgent). Default off (opt-in). # =============== TEAM COLLABORATION (designed, not built , all off) =============== # Source: ops-toolkit/_meta/megagoals/team-mode/ (ROADMAP + DESIGN-01..07), From c56f0011ab7e29a5e4a2b86cc2f1f230f8e793af Mon Sep 17 00:00:00 2001 From: Han Ngo Date: Mon, 20 Jul 2026 23:10:08 +0700 Subject: [PATCH 3/3] docs(readme): bump Skills header count for memory-tidy --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 70a008a0..afa3fa38 100644 --- a/README.md +++ b/README.md @@ -290,7 +290,7 @@ Which hooks BLOCK vs warn vs neither is a declared contract: `docs/architecture.
-Agents (25, dispatched by commands) and Skills (3, Claude-triggered) +Agents (25, dispatched by commands) and Skills (4, Claude-triggered) | Agent | Dispatched by | What it does | |-------|--------------|-------------|