@imunitic/synapse 0.0.1-test.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/Index.md.template +23 -0
  2. package/bin/synapse-hook.cjs +19 -0
  3. package/bin/synapse-setup.cjs +420 -0
  4. package/bin/synapse.cjs +20 -0
  5. package/commands/synapse-design-note.md +229 -0
  6. package/commands/synapse-init.md +354 -0
  7. package/commands/synapse-note.md +196 -0
  8. package/commands/synapse-rebuild-diff.md +314 -0
  9. package/commands/synapse-rebuild-full.md +152 -0
  10. package/commands/synapse-status.md +144 -0
  11. package/commands/synapse-task-note.md +133 -0
  12. package/commands/synapse-vault-tidy.md +187 -0
  13. package/harness/claude/hooks.json +54 -0
  14. package/harness/codex/hooks.json +54 -0
  15. package/harness/codex/skills/synapse-design-note/SKILL.md +236 -0
  16. package/harness/codex/skills/synapse-init/SKILL.md +354 -0
  17. package/harness/codex/skills/synapse-note/SKILL.md +212 -0
  18. package/harness/codex/skills/synapse-rebuild-diff/SKILL.md +315 -0
  19. package/harness/codex/skills/synapse-rebuild-full/SKILL.md +149 -0
  20. package/harness/codex/skills/synapse-status/SKILL.md +146 -0
  21. package/harness/codex/skills/synapse-task-note/SKILL.md +133 -0
  22. package/harness/codex/skills/synapse-vault-tidy/SKILL.md +187 -0
  23. package/harness/opencode/plugin/synapse.js +164 -0
  24. package/lib/obsidian-mcp-refresh.cjs +303 -0
  25. package/lib/resolve-binaries.cjs +54 -0
  26. package/package.json +26 -0
  27. package/skills/synapse-node/SKILL.md +211 -0
  28. package/skills/synapse-node-authoring/SKILL.md +188 -0
  29. package/skills/synapse-node-format/SKILL.md +205 -0
  30. package/skills/synapse-orientation/SKILL.md +468 -0
  31. package/skills/synapse-query/SKILL.md +99 -0
  32. package/skills/synapse-task/SKILL.md +261 -0
  33. package/skills/synapse-vault/SKILL.md +107 -0
  34. package/synapse-claude.md +220 -0
  35. package/synapse-fence-languages.conf.template +24 -0
  36. package/synapse-ignore-files.conf.template +45 -0
  37. package/synapse-module-boilerplate.conf.template +24 -0
  38. package/synapse-projects.conf.template +14 -0
  39. package/synapse-prompt-stopwords.conf.template +594 -0
  40. package/synapse.conf.template +23 -0
@@ -0,0 +1,152 @@
1
+ ---
2
+ name: synapse-rebuild-full
3
+ description: Wipe a repo's Synapse namespace and rebuild it from scratch via /synapse-init, for the case where diff-driven triage isn't the right tool — the graph has drifted too far, or a clean rebuild is just wanted directly. Preserves any hand-written `## Notes` content first and auto-merges what it can back into the new nodes. For ordinary same-branch drift, use /synapse-rebuild-diff instead — it's cheaper and never deletes a node outright.
4
+ ---
5
+
6
+ # Synapse Rebuild Full: Wipe and Rebuild a Namespace From Scratch
7
+
8
+ `/synapse-rebuild-diff` triages drift node by node — reseat, patch, or re-orient — and never deletes
9
+ anything. This command is the other tool: it deletes the current namespace outright and rebuilds it
10
+ from nothing via `/synapse-init`'s own First-time-build procedure. Reach for it when the graph has
11
+ drifted past the point where triage is worth it (most nodes would land in *re-orient* anyway), when
12
+ the namespace is corrupted or was built badly, or when a clean rebuild is simply what's wanted —
13
+ never as a reflex for ordinary drift, which `/synapse-rebuild-diff` handles more cheaply and without
14
+ touching anything irreplaceable.
15
+
16
+ **Unlike `/synapse-rebuild-diff`, this command does not care which branch is checked out beyond the
17
+ ordinary sense.** It isn't diffing against anything — it resolves `{repo}@{branch}` for whatever's
18
+ currently checked out and rebuilds *that* namespace, exactly as `/synapse-init` does. There is no
19
+ branch-identity guardrail here because there is nothing to compare against; the branch you're on is
20
+ simply the branch being rebuilt.
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ /synapse-rebuild-full
26
+ ```
27
+
28
+ No arguments — always operates on the repo and branch containing the current working directory.
29
+
30
+ ## Prerequisites
31
+
32
+ - Requires the `obsidian` MCP server (`mcp__obsidian__*` tools). If unreachable, say so and stop —
33
+ same requirement `/synapse-init` has.
34
+ - Must be run from inside a git repository, on a named branch (not detached `HEAD`) — same
35
+ requirement `/synapse-init` has, since `synapse_namespace` needs a branch to key on.
36
+
37
+ ## Procedure
38
+
39
+ ### 1. Resolve the namespace
40
+
41
+ Same resolution `/synapse-init` uses: repo root (`git rev-parse --show-toplevel`), namespace key
42
+ (`{repo}@{branch}`, printed by `synapse namespace`), remote (for the `Index.md`
43
+ verification field).
44
+
45
+ Check whether `synapse/{repo}@{branch}/Index.md` exists.
46
+
47
+ - **Doesn't exist** → there is nothing to wipe. This is just a first build, not a rebuild — hand off
48
+ directly to `/synapse-init` and stop here. Do not run the wipe step at all in this case; it would
49
+ only fail on a directory that isn't there.
50
+ - **Exists, `remote` mismatches** → belongs to a different repo sharing this key. Same refusal
51
+ `/synapse-init` gives in this case: stop, name both remotes, do not touch it.
52
+ - **Exists, `remote` matches** → continue to step 2.
53
+
54
+ ### 2. Preview the wipe and get explicit confirmation
55
+
56
+ ```sh
57
+ ~/.synapse graph-wipe --dry-run
58
+ ```
59
+
60
+ Report its output plainly: node count, and — the one thing this step exists to surface — how many
61
+ nodes carry hand-written `## Notes` content that's about to be deleted, and which ones. `## Notes` is
62
+ human-authored, lives outside every generated fence, and no rebuild regenerates it; a wipe is the one
63
+ operation in this pair of commands that actually deletes files rather than overwriting them with
64
+ preservation, so it earns an explicit stop here that `/synapse-rebuild-diff` deliberately does not
65
+ have.
66
+
67
+ **Get an explicit yes before continuing.** This is a hard-to-reverse filesystem operation on content
68
+ that includes irreplaceable human prose — do not proceed past this point on an assumption, even if
69
+ the human is the one who typed `/synapse-rebuild-full` in the first place. Typing the command signals
70
+ intent to rebuild; it is not itself confirmation of a delete that touches N nodes with hand-written
71
+ notes attached, which the human hasn't seen a number for yet.
72
+
73
+ ### 3. Wipe
74
+
75
+ Once confirmed:
76
+
77
+ ```sh
78
+ ~/.synapse graph-wipe
79
+ ```
80
+
81
+ This deletes `synapse/{repo}@{branch}/` and, if any node had non-empty `## Notes`, first dumps that
82
+ content verbatim to `scratchpad/{repo}@{branch} — preserved notes before full rebuild.md`. See
83
+ `synapse graph-wipe`'s own header for the exact mechanics (belt-and-braces path check, same
84
+ discipline `synapse graph-clean` uses for its own deletion).
85
+
86
+ ### 4. Rebuild from scratch
87
+
88
+ Run `/synapse-init`'s **First-time build** procedure (its steps 1–8) against the now-empty namespace,
89
+ by reference rather than repeating it here — enumerate, read hint files, orientation pass, cluster
90
+ into `manifest.tsv`, gate, write each node, build `_index.bin`, build `Index.md`. Same command, same
91
+ judgment calls, nothing rebuild-specific about this phase: from the namespace's perspective this is
92
+ identical to a first build, because as of step 3 it is one.
93
+
94
+ ### 5. Merge preserved notes back
95
+
96
+ Skip this step entirely if step 2 found nothing preserved (no staging note was created).
97
+
98
+ **Only once the new `Index.md` exists** — not during clustering, not node-by-node as nodes are
99
+ written. Read the staging note (`scratchpad/{repo}@{branch} — preserved notes before full rebuild.md`)
100
+ and classify each preserved note's old title + content against the finished new node list, the same
101
+ technique `/synapse-init`'s `_unassigned` sweep already uses for classifying files against an existing
102
+ node list: read the note against the new summaries, judge which node it best fits.
103
+
104
+ - **Confident match** → append the note's content into that node's `## Notes` section (every node
105
+ written by `synapse write-node` already carries one, empty if nothing else was there — never a
106
+ "create the section" case) with a one-line provenance breadcrumb: `(carried over from "{old node
107
+ title}" during full rebuild on {date})`. The note is losing its original context by moving to a new
108
+ home, and that breadcrumb is the only way a future reader recovers why it's there.
109
+
110
+ **Report the placement even though it succeeded.** Say which old node's notes went to which new
111
+ node, for every single one, not only the ones that failed to place. A wrong auto-placement is most
112
+ dangerous exactly when it's silent — this is the one class of content in the whole system marked
113
+ irreplaceable, and "it succeeded" is not the same claim as "it succeeded correctly."
114
+ - **No confident match** → leave it in the staging note, and say why: no equivalent concept survived
115
+ the re-cluster, or more than one new node looked equally plausible. Do not guess past a stated
116
+ uncertainty here — a note in the wrong node is worse than a note sitting in scratchpad waiting for a
117
+ human to place it.
118
+
119
+ Once every preserved note has been classified: if every one found a confident home, delete the
120
+ staging note — nothing is left needing manual attention. If any remain unplaced, leave the staging
121
+ note live containing only the leftovers, trimmed of everything that did get merged.
122
+
123
+ ### 6. Report
124
+
125
+ - Old node count vs. new node count.
126
+ - Whether any notes were preserved, and the outcome of every single one from step 5 (merged where, or
127
+ left for manual placement and why) — not just a summary count.
128
+ - If the staging note still exists, say so explicitly and give its path — it needs a human look.
129
+
130
+ ## Guardrails
131
+
132
+ - **Never wipe without running `--dry-run` first and getting explicit confirmation on its output.**
133
+ The preview step exists specifically so "how many notes are about to be deleted" is answered before
134
+ it happens, not after.
135
+ - **Never invent a placement for a preserved note that isn't a confident match.** Leaving it in
136
+ scratchpad, flagged, is the correct outcome when nothing else is — don't fill the gap with a guess
137
+ to make the report look cleaner.
138
+ - **Never merge into a node's generated region.** The merge target is always `## Notes`, appended, never
139
+ touching anything inside the `<!-- synapse:generated:start -->`…`<!-- synapse:generated:end -->`
140
+ fence — that region belongs to `synapse write-node` alone.
141
+ - **Never treat this as the default repair path.** `/synapse-rebuild-diff` is cheaper, preserves every
142
+ node rather than deleting them, and is the right tool for ordinary drift. Use this command when
143
+ triage genuinely isn't worth it, not as a heavier habit that replaces the lighter one.
144
+
145
+ ## Integration
146
+
147
+ - Delegates the actual rebuild to `/synapse-init`'s First-time-build procedure — this command owns
148
+ only the wipe-with-preservation step before it and the note-merge step after it.
149
+ - The wipe itself is `synapse graph-wipe` (via `synapse graph-wipe`), mirroring
150
+ `synapse graph-clean` as the only other destructive tool in the system.
151
+ - Resolves the namespace the same way `/synapse-init` and `/synapse-rebuild-diff` do -- one chain, in `core/identity.zig`
152
+ — never re-derives repo/branch/remote independently.
@@ -0,0 +1,144 @@
1
+ ---
2
+ description: Print a read-only report of what in Synapse Vault currently needs a human decision -- design notes still Discussing, design notes marked Ready with no compiled task yet, design notes with open questions, task notes with unchecked items, and task notes stuck in REVIEW. Use whenever the user wants a status check on the vault ("what's outstanding", "what needs my attention", "vault status", "what did we leave open"). Never modifies anything -- a report only, not a task-management action. Not for creating, continuing, or listing a specific note kind (that's synapse-note/synapse-design-note/synapse-task-note's own --list modes) -- this is the one cross-cutting view over all of them at once.
3
+ ---
4
+
5
+ # Synapse Status: Vault-Wide Attention Report
6
+
7
+ A read-only sweep over Synapse Vault answering one question: what currently needs a human decision?
8
+ Five categories, one pass, printed as plain chat text -- an org-agenda-style check-in, not a document
9
+ to hand to someone else and not a live dashboard (a published Artifact has no route to the local
10
+ Obsidian REST API, so nothing here is ever presented that way). Run it on demand, or from a
11
+ scheduled `/loop`/cron invocation of this same command -- never wired into `SessionStart`: two of
12
+ the five categories need a per-note body read, not just a frontmatter check, and unlike `Index.md`
13
+ this report is a periodic human check-in, not something the agent needs injected every session to
14
+ behave correctly.
15
+
16
+ Scoped to `designs/`/`tasks/` only -- the two folders `/synapse-design-note`/`/synapse-note --task`
17
+ structurally require, so every Synapse install has them in the same shape. The free-form taxonomy
18
+ (`inbox/`/`research/`/`scratchpad/`) is per-install customizable (see `Index.md`), not guaranteed to
19
+ exist or mean the same thing across installs, and `inbox/` specifically is for the vault owner's own
20
+ periodic look on their own schedule -- this report doesn't cover it.
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ /synapse-status # Print the current vault status report
26
+ ```
27
+
28
+ ## Prerequisites
29
+
30
+ Requires the `obsidian` MCP server (`mcp__obsidian__*` tools). If unreachable, say so and stop --
31
+ there is no local-file fallback.
32
+
33
+ ## Producing the report
34
+
35
+ Run all five queries in parallel where the tool call shape allows it; none depends on another's
36
+ result.
37
+
38
+ **1. Design notes still `Discussing`.** Design notes carry status in-body under `## Status`, not in
39
+ frontmatter (unlike task notes) -- a content match, scoped to `designs/`:
40
+
41
+ ```
42
+ {"and": [
43
+ {"glob": ["designs/*", {"var": "path"}]},
44
+ {"regexp": ["## Status\\nDiscussing", {"var": "content"}]}
45
+ ]}
46
+ ```
47
+
48
+ **2. `Ready` design notes with no compiled task yet.** `/synapse-task-note`'s own "Linking back"
49
+ step patches a compiled design note with a `> Compiled task: [[...]]` line right after its title --
50
+ "`Ready` and missing that line" is a direct signal, not fuzzy title-matching against `tasks/`:
51
+
52
+ ```
53
+ {"and": [
54
+ {"glob": ["designs/*", {"var": "path"}]},
55
+ {"regexp": ["## Status\\nReady", {"var": "content"}]},
56
+ {"!": [{"regexp": ["Compiled task:", {"var": "content"}]}]}
57
+ ]}
58
+ ```
59
+
60
+ **3. Design notes (any status) with a non-empty `## Open Questions`.** Match the heading followed by
61
+ at least one bullet -- a heading with nothing under it (fully pruned, per the Ready-gate convention
62
+ `/synapse-design-note` now follows) doesn't count as open. Since this section spans every status,
63
+ each line in the composed report also shows *which* status the note is currently in:
64
+
65
+ ```
66
+ {"and": [
67
+ {"glob": ["designs/*", {"var": "path"}]},
68
+ {"regexp": ["## Open Questions\\n- ", {"var": "content"}]}
69
+ ]}
70
+ ```
71
+
72
+ `regexp` is boolean-only (no captured groups), so getting each match's actual status needs a second
73
+ pass. Run the same query three more times, `and`-ed with `{"regexp": ["## Status\\nDiscussing", ...]}`
74
+ / `Ready` / `Reference` respectively, to sort the matches from the first query into the three known
75
+ statuses without a body read. **A design note written before `## Status` was standardized on those
76
+ exact three words can carry free text there instead** (e.g. `Superseded by [[...]]`) -- it matches
77
+ the first query but none of the three status-scoped ones. Whatever's left over after removing the
78
+ Discussing/Ready/Reference matches from the first query's full result set is exactly this case:
79
+ report those under a fourth bucket, "Other", rather than silently dropping them -- surfacing an odd
80
+ note beats losing it, the same reasoning behind reporting a 0-unchecked task instead of hiding it
81
+ (see Query 4 below).
82
+
83
+ **4. Open task notes with at least one unchecked item.** Task notes carry `status:` in frontmatter,
84
+ unlike design notes -- filter there first:
85
+
86
+ ```
87
+ {"in": [{"var": "frontmatter.status"}, ["TODO", "IN-PROGRESS"]]}
88
+ ```
89
+
90
+ Then `mcp__obsidian__vault_read` each match and count `- [ ]` lines in the body. A match with zero
91
+ unchecked lines (a checklist that's fully checked but hasn't been promoted to `REVIEW` yet) is still
92
+ worth surfacing -- report it under this section with its count shown as 0, rather than silently
93
+ dropping it, since that state itself is worth a human noticing.
94
+
95
+ **5. Task notes stuck in `REVIEW`.** Frontmatter-only, no body read needed -- a fully-checked
96
+ checklist waiting specifically on human sign-off, since `synapse-task` deliberately never promotes a
97
+ note past `REVIEW` on its own:
98
+
99
+ ```
100
+ {"==": [{"var": "frontmatter.status"}, "REVIEW"]}
101
+ ```
102
+
103
+ ## Composing the report
104
+
105
+ One section per category, in the order above. Each line names the note (title, or filename if no
106
+ `title` frontmatter) plus the one identifying detail that category needs. The Open Questions section
107
+ is the one place a note's status also belongs on the line -- every other section's heading already
108
+ implies it (the "Discussing" section only ever holds `Discussing` notes), but Open Questions spans
109
+ every status (`Discussing`/`Ready`/`Reference`/the "Other" catch-all from Query 3 above), so put the
110
+ status first, before the title, so it's the first thing scanned:
111
+
112
+ ```
113
+ ## Discussing
114
+ - {title}
115
+
116
+ ## Ready, not yet compiled
117
+ - {title}
118
+
119
+ ## Open questions
120
+ - **{status}** — {title}
121
+
122
+ ## In progress (unchecked items)
123
+ - {title} ({N} unchecked)
124
+
125
+ ## Awaiting review
126
+ - {title}
127
+ ```
128
+
129
+ Omit a section entirely when it has zero matches -- matching `/synapse-note --list`'s own convention
130
+ of leaving out empty headers -- rather than printing five headers with nothing under most of them. If
131
+ every category is empty, say so in one line ("Vault is clear -- nothing outstanding.") instead of five
132
+ empty headers.
133
+
134
+ Print the report directly in the response, not left only in tool-call output the user would have to
135
+ go dig for.
136
+
137
+ ## Constraints
138
+
139
+ - Read-only end to end. Never calls `vault_write`/`vault_patch`/`vault_move`/`vault_delete`/
140
+ `vault_copy` -- if a step here ever seems to need one, that step is out of scope, not a case to
141
+ special-case around.
142
+ - No Artifact/web-UI output. No `SessionStart` wiring. Not a new binary CLI subcommand -- every
143
+ category above is a plain mechanical query already reachable through `mcp__obsidian__*` tools.
144
+ - Scoped to `designs/`/`tasks/` only -- never `inbox/`/`research/`/`scratchpad/`.
@@ -0,0 +1,133 @@
1
+ ---
2
+ description: Compile a Ready design note into a single tracked, checklist-based task note in Synapse Vault — delegates the actual note creation to synapse-note --task under the hood. Use whenever the user wants to turn a settled design discussion into actionable, tracked work ("let's compile a task note", "let's turn this into a task", "make this a task now") — not for starting or continuing the design discussion itself (that's synapse-design-note).
3
+ ---
4
+
5
+ # Synapse Task Note: Compile a Design into a Tracked Checklist
6
+
7
+ Compiles a `Ready` design note (from `/synapse-design-note`) into a single tracked task, using the
8
+ vault's existing task-tracking machinery instead of a bespoke format: creation goes through
9
+ `/synapse-note --task`, and status transitions from then on belong entirely to the `synapse-task`
10
+ skill. This command's only job is the compile step — turning a design into an ordered checklist —
11
+ not tracking progress itself.
12
+
13
+ One design compiles into **one** task note, one `task_id`, and the checklist items *are* the
14
+ steps — matching how every other task in the vault already works (see `synapse-task`'s "Task file
15
+ structure").
16
+
17
+ ## Usage
18
+
19
+ ```
20
+ /synapse-task-note "topic" # Compile the task note for a Ready design note
21
+ ```
22
+
23
+ No `--continue`/`--list` here — once created, the task note's own progress (checked items,
24
+ `status:` frontmatter) is what `/synapse-note --list` and the `synapse-task` skill already track.
25
+ Use those instead of reinventing a parallel view.
26
+
27
+ ## Prerequisites
28
+
29
+ - Requires a matching Obsidian design note (`designs/`) with `Status: Ready`.
30
+ - No matching note → "No Ready design note found for '{topic}'. Run
31
+ `/synapse-design-note \"{topic}\"` first." Never generate a checklist from scratch.
32
+ - Matching note but `Status: Discussing` → "Design note for '{topic}' is still in Discussing. Finish
33
+ it first."
34
+ - Matching note but `Status: Reference` → "Design note for '{topic}' concluded as Reference —
35
+ nothing to compile. Reopen it with `/synapse-design-note \"{topic}\"` and mark it Ready if that's
36
+ changed."
37
+ - A task note already exists for this design (check the design note's `> Compiled task:` annotation,
38
+ or `search_query` for a `tasks/` note linking to it) → show its current state (title, `status:`,
39
+ checked/total) and ask: view it, or recompile (only on explicit confirmation — recompiling rewrites
40
+ the checklist, so any progress on items that no longer exist is lost).
41
+
42
+ **A recompile updates the existing note in place. It never creates a second one and never bumps
43
+ the `task_id`.** One design has exactly one task note for its whole life; a design that gets
44
+ revised mid-implementation is the normal case, not a new task. Preserve `task_id`, `created`,
45
+ `status`, the filename, and any `## Notes` content the human added; replace the checklist and the
46
+ pre-implementation notes. Carry forward `- [x]` marks for items that survive the recompile
47
+ unchanged — work already done doesn't become undone because the plan around it grew.
48
+
49
+ Record *why* in the note itself, in one line at the top of the body: what the old plan got wrong
50
+ and what changed. A recompiled task with no explanation reads like a plan that was always this
51
+ shape, which hides the fact that implementation found a gap.
52
+
53
+ ## Compiling the checklist
54
+
55
+ 1. Read the design note in full.
56
+ 2. Break the approach into an ordered list of small, sequential, independently-completable steps.
57
+ 3. For each step, write it the way `synapse-task`'s own checklist convention expects: a short
58
+ `- [ ] {Do}` line; for substantive steps (type definitions, API surfaces, interface signatures)
59
+ add a one-line nested description plus a fenced code block showing the exact interface, per that
60
+ skill's "Inline code examples in checklist items". Don't invent separate Files/Tests/AC fields —
61
+ fold what matters into the item's own description instead (e.g. "...; test: X returns Y").
62
+ 4. Note explicit exclusions — things a reasonable implementer might also attempt that are out of
63
+ scope — for the `## Notes` section below, not a separate heading.
64
+
65
+ ## Creating the note
66
+
67
+ Follow `/synapse-note`'s task-mode procedure exactly (its "Creating the note" section) — don't
68
+ duplicate that scaffolding here, just supply its inputs:
69
+
70
+ - **Title:** a short, plain description of the compiled plan (e.g. "Rollup direct storage
71
+ implementation") — `/synapse-note --task` resolves the project prefix and `task_id` and prepends
72
+ them itself.
73
+ - **Project:** derive from the source design note's `project:` frontmatter — that prefix is already
74
+ resolved (the design note went through `/synapse-design-note`'s resolution when it was created,
75
+ which reads/appends the resolved `synapse-projects.conf`, per `/synapse-note`'s tiered lookup),
76
+ so supply it directly instead of
77
+ re-deriving or re-asking. This is the *prefix* (`ecs`, `sb`, ...), not the `tasks/{project}/`
78
+ folder name — `/synapse-note --task`'s own "Resolving the project folder" step turns it into
79
+ the folder name. Never hardcode a specific project/prefix pair in this command's own
80
+ instructions — the conf file is machine-local and deliberately outside the portable
81
+ Synapse package, so projects from unrelated contexts (e.g. personal vs. work) must
82
+ never end up in the same place.
83
+ - **Body:** the checklist from "Compiling the checklist" above, under the single top-level heading —
84
+ exactly the structure `synapse-task`'s "Task file structure" requires (no `## Step` sub-headings).
85
+ - **`## Notes` (pre-implementation):** populate per `synapse-task`'s own convention —
86
+ - Design reference: `[[{design note title}]]`
87
+ - Key constraints the implementor must not miss
88
+ - Deliberate exclusions (from step 4 above) and why
89
+
90
+ ## Linking back
91
+
92
+ After creation, patch the design note: add `> Compiled task: [[{task note title}]]` near the top
93
+ (via `mcp__obsidian__vault_patch`) — a small annotation line, same idea as any other cross-reference
94
+ you'd drop near a note's title. This is a one-time link; the design note's `Status` itself does not
95
+ change and does not need to track the task's progress — Obsidian's own backlinks panel already
96
+ surfaces the live task note from the design note, and the task's `status:` frontmatter is the
97
+ authoritative progress signal from here on.
98
+
99
+ ## Confirm
100
+
101
+ Report the created path and title, and remind the user that `status:` transitions (TODO →
102
+ IN-PROGRESS → REVIEW) happen automatically via the `synapse-task` skill once work starts — this
103
+ command doesn't set or watch that field itself.
104
+
105
+ ## Integration
106
+
107
+ - Reads `/synapse-design-note`'s `Ready` output only — never invents a design.
108
+ - Delegates note creation to `/synapse-note --task` and status tracking to the `synapse-task` skill
109
+ — this command owns only the design → checklist compile step.
110
+
111
+ ---
112
+
113
+ ## Example
114
+
115
+ ```
116
+ User: /synapse-task-note "rollup direct storage"
117
+
118
+ Claude: [reads designs/{PROJECT} — Rollup direct storage.md, Status: Ready]
119
+ [no existing linked task note]
120
+
121
+ Compiled checklist:
122
+ - [ ] Add ReplacingMergeTree target table for the daily rollup
123
+ - [ ] Point the rollup MV at the new table instead of AggregatingMergeTree
124
+ - [ ] Migrate existing daily/monthly rollups (drop+recreate, sort-key change)
125
+ - [ ] Update the 5 retrofitted dashboards' rollup queries to drop argMaxMerge
126
+
127
+ Project already known: {PROJECT} → {prefix}-005.
128
+
129
+ Created: tasks/{PROJECT}/{prefix}-005 — Rollup direct storage implementation.md
130
+ Linked back from designs/{PROJECT} — Rollup direct storage.md.
131
+
132
+ Status transitions happen automatically via the synapse-task skill once you start work.
133
+ ```
@@ -0,0 +1,187 @@
1
+ ---
2
+ description: Run one on-demand vault-tidy pass over Synapse Vault -- surface recategorization candidates (notes that no longer fit their folder, clusters that deserve a new top-level category, categories that have gone stale) into a single inbox/ proposal, silently fill in the one class of missing-frontmatter default that has an unambiguous fix, and fold the rest of note health (broken links, orphaned notes, duplicate titles) into that same proposal since none of them has a safe mechanical repair either. Use whenever the user wants the vault's own organization checked or tidied ("tidy the vault", "check vault health", "any notes drifted out of their folder", "find orphaned/duplicate notes"). Never invoked automatically -- no SessionStart wiring, no autonomous scheduling; run it yourself, or under your own /loop if you want a cadence. Scoped to everything except designs/, tasks/, and synapse/ (the code graph's own generated namespace), which stay entirely /synapse-status's (the first two) or /synapse-init's (the third) territory.
3
+ ---
4
+
5
+ # Synapse Vault Tidy: Recategorization Proposals and Note-Health Fixes
6
+
7
+ A single on-demand pass over Synapse Vault's free-form taxonomy (`inbox/`/`research/`/`scratchpad/`/
8
+ whatever a given install's `Index.md` currently lists) that a person would otherwise have to notice
9
+ and fix by hand: a note's folder fit drifting since it was filed, several notes sharing enough in
10
+ common to deserve a category of their own, a category that stopped earning its keep, a link that
11
+ broke when its target moved, a note nothing points to or from, two notes that are really the same
12
+ thing under slightly different titles. Nothing here runs unprompted — it exists because nothing else
13
+ in the vault periodically re-checks any of this, not because it's meant to run behind the user's
14
+ back.
15
+
16
+ Never touches `designs/`, `tasks/`, or `synapse/` — the first two are `/synapse-design-note`'s and
17
+ `/synapse-note --task`'s own interface for directing project work (any gap found there is
18
+ `/synapse-status`'s territory, not this command's); `synapse/` is the code graph's own generated
19
+ namespace, a different note kind entirely, owned and kept fresh by `/synapse-init` and the
20
+ `synapse-node`/`synapse-node-format` skills. `Index.md` and any vault-root `README.md` are also out
21
+ of scope — foundational files, not taxonomy notes.
22
+
23
+ ## Usage
24
+
25
+ ```
26
+ /synapse-vault-tidy # Run one vault-tidy pass
27
+ ```
28
+
29
+ ## Prerequisites
30
+
31
+ Requires the `obsidian` MCP server (`mcp__obsidian__*` tools). If unreachable, say so and stop —
32
+ there is no local-file fallback.
33
+
34
+ No compiled code anywhere in this command. `adapters/obsidian/store.zig`'s `ObsidianStore` does now
35
+ implement `read`/`list`/`search`, but for `synapse frontmatter get`/`set`'s narrow one-field-at-a-time
36
+ use — a full-vault tidy sweep, reading every note's whole body to judge category, tags and broken
37
+ links, is a different shape of work entirely, and stays on these same MCP tools rather than the
38
+ compiled binary, the same way `/synapse-status` and `/synapse-rebuild-diff`'s vault-side checks
39
+ already work. The one exception is Step 3's broken-link history check, a plain `git log` call (via
40
+ Bash, not a compiled tool) against the vault's own local repo when one exists — best-effort, never
41
+ a hard requirement.
42
+
43
+ ## Step 1: Inventory sweep
44
+
45
+ Enumerate every note in scope and read each one exactly once — every later step reuses this same
46
+ sweep rather than re-reading anything.
47
+
48
+ ```
49
+ {"and": [
50
+ {"!": [{"glob": ["designs/*", {"var": "path"}]}]},
51
+ {"!": [{"glob": ["tasks/*", {"var": "path"}]}]},
52
+ {"!": [{"glob": ["synapse/*", {"var": "path"}]}]},
53
+ {"!=": [{"var": "path"}, "Index.md"]},
54
+ {"!=": [{"var": "path"}, "README.md"]}
55
+ ]}
56
+ ```
57
+
58
+ `mcp__obsidian__search_query` with that filter, then `mcp__obsidian__vault_read` each match. Keep
59
+ the full result (`path`, `frontmatter`, `tags`, `links`, `backlinks`, `unresolvedLinks`, `stat`,
60
+ `content`) per note — Steps 2–4 below only ever reason over the structured fields, never `content`
61
+ itself; the judgment layer in Step 5 is the first point anything actually reads note *bodies*, and
62
+ only for the subset flagged by then. `content` is fetched for free in this same call regardless, so
63
+ there's no separate "light" read to bother with.
64
+
65
+ ## Step 2: Frontmatter defaults (fixed directly)
66
+
67
+ The only note-health fix applied silently, because it's the only one with exactly one correct
68
+ answer per note. Design and task notes are out of scope entirely; the plain/free-form schema is
69
+ just two fields (`title`, `created` — see `synapse-note`'s bare-mode format), both derivable without
70
+ guessing:
71
+
72
+ - Missing `title` → the filename with its `.md` extension stripped.
73
+ - Missing `created` → `stat.ctime`, formatted `YYYY-MM-DD HH:MM` to match every other note's
74
+ convention.
75
+
76
+ Apply via read-modify-write on the whole file (`vault_read` → edit the one frontmatter line in the
77
+ returned content → `vault_write` the whole file back) — never `vault_patch` with
78
+ `targetType: frontmatter`, which re-serializes the entire YAML block and silently reformats
79
+ unrelated fields, the same hazard `synapse-vault`/`synapse-task` already document.
80
+
81
+ ## Step 3: Note-health findings (reported, not fixed)
82
+
83
+ From the same inventory, no additional vault reads (the broken-link history check below reads the
84
+ vault's local git log, not more notes). Each of these three has no safe mechanical repair —
85
+ fixing any of them means guessing at intent — so they become findings for the Step 6 proposal
86
+ instead of a silent edit:
87
+
88
+ - **Broken links** — `unresolvedLinks` non-empty. Before writing the finding, check the vault's own
89
+ local git history to say *why* it's broken instead of leaving that to guesswork — the vault is
90
+ usually a git repo (`db-sync` auto-commits every agent-driven edit into it, opt-in per vault, same
91
+ precondition as that hook). Resolve the vault's filesystem path the same way `synapse.conf`
92
+ already does (`OBSIDIAN_VAULT_DIR`), skip this sub-step entirely if `{vault}/.git` doesn't exist,
93
+ and never let a missing/unreachable git repo block the rest of the finding — worst case it's
94
+ reported with no history context, same as today.
95
+ - `git -C {vault} log --all --diff-filter=A --name-only --pretty=format: -- "**/{target}.md"` — a
96
+ hit means a note by that exact title was created at some point (even if later renamed or
97
+ deleted): report it as *"used to be a note — find what it's called now, or was deleted"*.
98
+ - No hit → `git -C {vault} log --all -p -S"[[{target}]]" -- .` and look at the earliest match's
99
+ diff: if the line first appears inside a `new file mode` addition (the note was created with the
100
+ link already broken), report it as *"born broken — never pointed at a real note; probably meant
101
+ as plain text or an external reference, not a vault link"* rather than implying anything was
102
+ lost.
103
+ - Neither check resolves anything more specific → report the target plainly, same as before.
104
+ - **Orphaned notes** — `links` empty *and* `backlinks` empty.
105
+ - **Duplicate/near-duplicate titles** — group notes by title normalized (lowercased, trimmed,
106
+ internal whitespace collapsed); any group with 2+ members is a finding. This is a mechanical
107
+ string-normalization match, not fuzzy similarity — genuinely fuzzy "these might be the same
108
+ note" calls belong to the judgment layer in Step 5, not here.
109
+
110
+ ## Step 4: Recategorization signal layer
111
+
112
+ Mechanical, from the same inventory, still no note-body reasoning:
113
+
114
+ - **Weak folder fit** — a note (not already an orphan from Step 3) that shares no tag with any
115
+ other note in its own folder, and where fewer than half of its combined `links`+`backlinks` point
116
+ to notes within that same folder.
117
+ - **Uncovered clusters** — a tag or keyword held by 3+ notes spanning 2+ different folders, where no
118
+ existing top-level folder name (from `Index.md`'s current list) already matches it.
119
+ - **Stale categories** — a scope-eligible top-level folder whose newest note's `stat.mtime` is more
120
+ than 90 days old (no new note gained in that window) is flagged as a merge-back candidate, the
121
+ same way a newly proposed split is flagged — one signal, checked at a different point in a
122
+ category's life.
123
+
124
+ ## Step 5: Recategorization judgment layer
125
+
126
+ For each Step 4 candidate — a weak-fit note, a cluster, or a stale folder — read its full content
127
+ (and, for a cluster, every member's content) and decide with real judgment, not the mechanical
128
+ signal alone:
129
+
130
+ - Weak-fit note: does it genuinely belong in a different existing category, or was the signal a
131
+ false positive (a note that's fine where it is, just thin on tags/links)?
132
+ - Cluster: given what these notes actually say, is this a real emerging category, or just a
133
+ coincidental shared keyword?
134
+ - Stale folder: has it truly stopped being useful, or is low volume expected and fine (a narrow but
135
+ still-active category)?
136
+
137
+ Never moves a note or creates a folder here — every conclusion becomes one line in the Step 6
138
+ proposal, for the vault owner to act on.
139
+
140
+ ## Step 6: Compose the proposal
141
+
142
+ One note, `inbox/Vault tidy — {YYYY-MM-DD}.md` (fetch machine local time, never infer it), written
143
+ via `vault_write` (creates `inbox/` automatically if it doesn't exist yet; if `inbox/` isn't already
144
+ in `Index.md`'s folder list, add it there in the same pass, matching the folder-layout rule every
145
+ other command that can create a top-level folder already follows). Two sections:
146
+
147
+ ```
148
+ ## Recategorization
149
+ - {note or cluster}: {proposed move/new category/merge}, because {reasoning}
150
+
151
+ ## Note health
152
+ - Broken link in {note}: → {target text that doesn't resolve} ({history classification, if the
153
+ vault's local git repo yielded one — "used to be a note", "born broken", or omitted entirely if
154
+ neither check resolved anything})
155
+ - Orphaned: {note}
156
+ - Possible duplicate: {note A} / {note B}
157
+ ```
158
+
159
+ Omit either section if Steps 3–5 found nothing for it. If both are empty, don't write a proposal at
160
+ all — say so directly instead of creating an empty note.
161
+
162
+ This command never edits `Index.md` itself beyond the `inbox/` bootstrap case above — creating or
163
+ renaming a category is the vault owner's call, made by hand using
164
+ [[sb — Make vault folder taxonomy user-customizable via Index.md.template]]'s already-shipped
165
+ mechanism, not something this command does on its own.
166
+
167
+ ## Step 7: Report
168
+
169
+ Print a short summary directly in the response, not left only in tool-call output:
170
+
171
+ - Count of frontmatter defaults silently filled, with filenames.
172
+ - Count of recategorization candidates and note-health findings written to the proposal, with a
173
+ link to `inbox/Vault tidy — {date}.md`.
174
+ - If nothing was found anywhere: "Vault's in good shape — nothing to fix or propose."
175
+
176
+ ## Constraints
177
+
178
+ - Never touches — moves, edits, or reorganizes — anything inside `designs/`, `tasks/`, or
179
+ `synapse/`. `Index.md` and a vault-root `README.md` are also out of scope.
180
+ - Recategorization never auto-moves a note or auto-creates a folder — proposal only, to `inbox/`.
181
+ - Only the frontmatter-default class of note-health fix (Step 2) is applied directly and silently.
182
+ Broken links, orphaned notes, and duplicate titles are proposal findings, never auto-repaired.
183
+ - Invoked on demand only — no `SessionStart` wiring, no autonomous scheduling. Run it directly, or
184
+ under a `/loop` the user sets up themselves.
185
+ - No compiled code — every step above is a plain `mcp__obsidian__*` call, or (Step 3's broken-link
186
+ history check only) a plain `git log` via Bash against the vault's own local repo; this command
187
+ itself never calls into `ObsidianStore`.