vault-triage
Triages inbox fleeting notes with promote, merge, discard or defer decisions. Use when the user wants the inbox cleared fast ("triage inbox", "fleeting review", "clear inbox", "batch decide"). Not for
SKILL.mdplugins/mz-knowledge/skills/vault-triage/SKILL.md
GitHubVault Triage
Overview
Discipline skill that runs a 30-second triage pump over a small batch of fleeting inbox notes. The triage-scorer agent scores each note with a deterministic heuristic ladder and proposes a default decision from a closed vocabulary (promote | merge | discard | defer). The user accepts the defaults wholesale or overrides individual decisions. Merge targets are never chosen by the agent — the user names them explicitly.
When to Use
Clearing a backlog of fleeting inbox notes in small fast batches, such as after a bursty capture session.
When NOT to use
- Resurfacing permanent notes already in the vault for review — use
vault-review. - Editing a single known note — use
Editdirectly. - Atomizing a long fleeting note into multiple permanent notes — use
process-notes. - Proposing
[[wikilinks]]between existing notes — usevault-connect.
Constants
- PROJECT_ROOT: resolved once in setup with
echo "${MZ_MEMORY_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; expand<PROJECT_ROOT>to it in every command and agent prompt. - TASK_DIR:
<PROJECT_ROOT>/.mz/task/ - BATCH_SIZE: 7
- INBOX_FOLDER:
inbox/ - FLEETING_AGE_DAYS_DEFER_THRESHOLD: 14
- STUB_WORD_THRESHOLD: 20
Core Process
Phase Overview
| Phase | Goal | Details |
|---|---|---|
| 0 | Setup | Inline below |
| 1 | Score batch | phases/score_batch.md |
| 1.5 | User approval — decisions | Inline below |
| 2 | Execute decisions | phases/execute_decisions.md |
Phase 0: Setup
Resolve the project root. Resolve
PROJECT_ROOTwith the command in Constants, before anycdor worktree switch, and use it for every.mz/path below.Resolve the vault path. Resolve the vault path with precedence:
$ARGUMENTS→OBSIDIAN_VAULT_PATHenv →MZ_VAULT_PATHenv → walk up from cwd to the nearest.obsidian/directory.Resolve the permanent-notes folder. Read
<vault>/CLAUDE.md. Extract the permanent-notes folder name from it (e.g.,04 - Permanent/). IfCLAUDE.mdis absent or does not declare the permanent-notes folder, ask the user via AskUserQuestion:What is your vault's permanent-notes folder path (relative to vault root)?Store the answer instate.mdunder the keyPERMANENT_FOLDER. Never hardcode a fallback path.Resolve the batch. List
<vault>/<INBOX_FOLDER>/*.mdsorted by mtime ascending; take the firstBATCH_SIZEnotes. If zero notes are found, create the task directory andstate.mdas in the next step, but withStatus: complete,Phase: empty_inbox,phase_complete: trueandwhat_remains: []; tell the user the inbox is empty, then stop.Create the task directory. Derive
task_name = <YYYY_MM_DD>_vault-triage_<slug>where<YYYY_MM_DD>is today's date (underscores) and<slug>is a short mode tag (e.g.,weekly); on same-day collision append_v2,_v3. CreateTASK_DIR<task_name>/on disk. Writestate.mdwithschema_version: 2,Status: running,Phase: 0,Started: <ISO timestamp>,phase_complete: false,what_remains: [],Vault: <absolute path>,PERMANENT_FOLDER: <resolved path>,BatchPaths: <list of absolute paths>.
Phase 1.5: User approval — Triage Decisions
This orchestrator (not a subagent) presents this gate. This step is interactive and must not be delegated.
Pre-read: Read <PROJECT_ROOT>/.mz/task/<task_name>/triage_batch.md in full and capture its contents into context.
State: write state.md with the artifact path and what_remains: [awaiting approval: triage gate]; Status stays running.
Surface 1 — emit the plan message. Output the artifact verbatim as a normal markdown chat message — each note's title, preview, proposed decision, and rationale:
## Triage batch ready for review — vault-triage
<verbatim contents of <PROJECT_ROOT>/.mz/task/<task_name>/triage_batch.md>
---
**Approve** → proceed to Phase 2 and execute all proposed decisions · **Reject** → abort the triage session, no vault changes · reply with an override list (e.g. 1=discard, 3=merge::Target Note Name) to revise
Emit the full verbatim contents of <PROJECT_ROOT>/.mz/task/<task_name>/triage_batch.md — do not substitute a path, summary, or placeholder.
Surface 2 — call AskUserQuestion. A short selector — do not re-embed the artifact in the question body:
- question:
The triage batch above is ready for review. - header:
Triage - options: Approve — proceed to Phase 2 and execute all proposed decisions · Reject — abort the triage session, no vault changes
The user may also reply with an override list such as 1=discard, 3=merge::Target Note Name. Each override binds to the note's index in the batch.
Response handling:
- Approve → add
- decisions approved: .mz/task/<task_name>/triage_batch.mdunder## Decisionsinstate.md, remove theawaiting approvalentry, setPhase: decisions_approved, and proceed to Phase 2 with the default decision map fromtriage_batch.md. - Reject → update state to
aborted_by_userand stop. - Any other reply (feedback or override list) → apply overrides to the decision map and return to Surface 1, re-read
triage_batch.md, and re-emit the entire plan message from scratch; OR proceed with the user-supplied override map if the user's reply is an explicit and unambiguous override list. For anymergedecision in the override list, requiremerge::<target note name>syntax; if the user writes justmergewithout a target, re-ask for the missing target via AskUserQuestion. This is a loop — repeat until explicit approval. Never proceed to Phase 2 without the user's explicit approval or an unambiguous override map. - No answer → set
Status: blocked, keepawaiting approval: triage gate, stop. A missing answer is never approval.
Decision Vocabulary
- promote — move the note from the inbox folder to the permanent-notes folder and patch frontmatter.
- merge — append the note's body to a user-named target note, then delete the inbox source.
- discard — delete the inbox file.
- defer — stamp a
review_afterfrontmatter field and keep the note in the inbox.
Common Rationalizations
| Rationalization | Rebuttal |
|---|---|
| "Auto-discard all stubs without showing them to the user." | "Even stubs might represent intentional micro-notes; every discard must be approved." |
| "Pick the merge target automatically from the most-similar note." | "Merge target requires user intent; the agent proposes, the user names the target." |
| "Process the whole inbox in one pass, don't cap at 7." | "BATCH_SIZE = 7 keeps each session under 30 seconds; larger batches cause decision fatigue and erode the quality of every decision." |
Red Flags
- Applying any decision before Phase 1.5 approval — all vault writes are gated.
- Auto-selecting a merge target instead of leaving
proposed_merge_target: nulland requiring the user to name it. - Processing more than
BATCH_SIZE(7) notes in a session — batching prevents decision fatigue. - Deleting an inbox source file before verifying the destination write on promote or merge.
- Proceeding to Phase 2 without explicit "approve" from the user at Phase 1.5.
State Management
State persists to .mz/task/<task_name>/state.md under the project root, ${MZ_MEMORY_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}, resolved once at the start of the run. Schema is v2: line 1 is schema_version: 2; line 2 is Status: with one of pending, running, blocked, complete, aborted_by_user or failed, lowercase and unquoted; then Phase, Started, phase_complete (boolean) and what_remains (YAML list of strings), with skill-specific keys below them. Milestones go in Phase; the reason for blocked or failed goes in what_remains. Set phase_complete: false on phase entry and true once the phase's artifacts are written and its gates pass. Refresh what_remains on every phase transition; it is [] when Status is complete. Write the file after every phase and before every gate, and re-read it after compaction. Record decisions worth recalling under a heading that reads exactly ## Decisions, placed before ## Notes; mz-memory harvests them only once Status is complete. Under a heading that reads exactly ## Lessons, add one line whenever a review finding is accepted and fixed, a step fails, gate feedback corrects a mistake, or an approved approach is reversed, in the form - (review|failure|correction|change) WHEN <situation> → <what to do> — WHY <cause>, concrete and under about 40 words; mz-memory harvests lessons whatever the Status. Treat a legacy completed as complete. On reading a schema_version: 1 or unversioned file, put schema_version: 2 on line 1 and Status on line 2, add the missing keys, and log the upgrade. Leave a file with a higher schema_version untouched: ask whether to Restart (archive it), start New (_v2) or Abort, and stop if nobody can answer.