Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ Which hooks BLOCK vs warn vs neither is a declared contract: `docs/architecture.
</details>

<details>
<summary><b>Agents</b> (25, dispatched by commands) and <b>Skills</b> (3, Claude-triggered)</summary>
<summary><b>Agents</b> (25, dispatched by commands) and <b>Skills</b> (4, Claude-triggered)</summary>

| Agent | Dispatched by | What it does |
|-------|--------------|-------------|
Expand Down Expand Up @@ -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) |

Expand Down Expand Up @@ -372,7 +373,7 @@ dwarves-kit/
lib/board/parse-board.sh The one structured BACKLOG.md parser other tools reuse (SPEC-146): `rows <file>` (id/status/full-line) and `queue-rows <file> <repo-name> <repo-root>` (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 <b> 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
Expand Down
11 changes: 11 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions kit.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
45 changes: 45 additions & 0 deletions skills/memory-tidy/SKILL.md
Original file line number Diff line number Diff line change
@@ -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 `<sibling>` | 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.
Loading