@imunitic/synapse 0.3.1 → 0.4.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.
- package/bin/synapse-hook.cjs +4 -1
- package/bin/synapse.cjs +4 -1
- package/commands/synapse-design-note.md +11 -2
- package/commands/synapse-note.md +37 -10
- package/harness/codex/skills/synapse-design-note/SKILL.md +7 -1
- package/harness/codex/skills/synapse-note/SKILL.md +63 -21
- package/harness/codex/skills/synapse-task-note/SKILL.md +4 -3
- package/package.json +5 -4
- package/schema/vault-design-note/v1.yaml +95 -0
- package/schema/vault-note/v1.yaml +67 -0
- package/schema/vault-task-note/v1.yaml +93 -0
- package/skills/synapse-task/SKILL.md +25 -15
package/bin/synapse-hook.cjs
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
"use strict";
|
|
8
8
|
|
|
9
9
|
const { spawnSync } = require("child_process");
|
|
10
|
+
const path = require("path");
|
|
10
11
|
const { hookPath, platformPackageName } = require("../lib/resolve-binaries.cjs");
|
|
11
12
|
|
|
12
13
|
const bin = hookPath();
|
|
@@ -15,5 +16,7 @@ if (!bin) {
|
|
|
15
16
|
process.exit(1);
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
const
|
|
19
|
+
const env = { ...process.env };
|
|
20
|
+
if (!env.SYNAPSE_CONTENT_ROOT) env.SYNAPSE_CONTENT_ROOT = path.join(__dirname, "..");
|
|
21
|
+
const result = spawnSync(bin, process.argv.slice(2), { stdio: "inherit", env });
|
|
19
22
|
process.exit(result.status ?? 1);
|
package/bin/synapse.cjs
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"use strict";
|
|
9
9
|
|
|
10
10
|
const { spawnSync } = require("child_process");
|
|
11
|
+
const path = require("path");
|
|
11
12
|
const { cliPath, platformPackageName } = require("../lib/resolve-binaries.cjs");
|
|
12
13
|
|
|
13
14
|
const bin = cliPath();
|
|
@@ -16,5 +17,7 @@ if (!bin) {
|
|
|
16
17
|
process.exit(1);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
const
|
|
20
|
+
const env = { ...process.env };
|
|
21
|
+
if (!env.SYNAPSE_CONTENT_ROOT) env.SYNAPSE_CONTENT_ROOT = path.join(__dirname, "..");
|
|
22
|
+
const result = spawnSync(bin, process.argv.slice(2), { stdio: "inherit", env });
|
|
20
23
|
process.exit(result.status ?? 1);
|
|
@@ -177,10 +177,13 @@ so the `## Status` line is the only lifecycle marker that matters. It simply sta
|
|
|
177
177
|
|
|
178
178
|
```
|
|
179
179
|
---
|
|
180
|
+
schema: vault-design-note/v1
|
|
180
181
|
title: "{PROJECT} — {Topic}"
|
|
181
182
|
project: {prefix}
|
|
182
|
-
created: "{now}"
|
|
183
183
|
note_id: {id}
|
|
184
|
+
created: "{now}"
|
|
185
|
+
updated: "{now}"
|
|
186
|
+
tags: [{comma-separated configured tags, or empty}]
|
|
184
187
|
---
|
|
185
188
|
|
|
186
189
|
# {PROJECT} — {Topic}
|
|
@@ -204,7 +207,13 @@ Discussing | Ready | Reference
|
|
|
204
207
|
- {Anything deferred or unresolved}
|
|
205
208
|
```
|
|
206
209
|
|
|
207
|
-
|
|
210
|
+
Any string scalar that is all digits (e.g. a numeric-looking title) must be quoted: an
|
|
211
|
+
unquoted all-digit value is parsed as an integer, which fails a `type: string` schema
|
|
212
|
+
field. `title`, `project`, `note_id`, and both timestamps are quoted above.
|
|
213
|
+
|
|
214
|
+
Resolve tags through the `synapse-vault` skill's configured vocabulary procedure. Fetch machine
|
|
215
|
+
local time once (`date '+%Y-%m-%d %H:%M:%S %Z'`) — never infer it — and use the exact same value
|
|
216
|
+
for `created` and `updated`.
|
|
208
217
|
|
|
209
218
|
No `Notes`/changelog section — it stays a small, single-conclusion note; there's no long-running
|
|
210
219
|
edit history here worth tracking separately.
|
package/commands/synapse-note.md
CHANGED
|
@@ -171,36 +171,64 @@ or supplied directly by a caller like `/synapse-task-note`):
|
|
|
171
171
|
characters (`/ : * ? " < > |`) with `-`, collapse repeated whitespace.
|
|
172
172
|
No timestamp prefix, no project-slug prefix — the filename is just the
|
|
173
173
|
(sanitized) title.
|
|
174
|
-
2.
|
|
175
|
-
|
|
176
|
-
|
|
174
|
+
2. Resolve tags through the `synapse-vault` skill's configured
|
|
175
|
+
`synapse-tag-vocabulary.conf` procedure. Choose only entries already in that vocabulary; an
|
|
176
|
+
empty list is valid when no tag applies, but the `tags` field itself is mandatory.
|
|
177
|
+
3. Fetch machine local time once: `date '+%Y-%m-%d %H:%M:%S %Z'` — never use inferred
|
|
178
|
+
time. Use the exact same value for `created` and `updated`.
|
|
179
|
+
4. Build the file content. A bare note needs a concise Summary. A task note needs lead prose and
|
|
180
|
+
at least one real flat checklist item before it can be written; never create an invalid empty
|
|
181
|
+
skeleton and promise to populate it later.
|
|
177
182
|
|
|
178
183
|
**Bare mode:**
|
|
179
184
|
```
|
|
180
185
|
---
|
|
186
|
+
schema: vault-note/v1
|
|
181
187
|
title: "{title}"
|
|
182
|
-
created: "{now}"
|
|
183
188
|
note_id: {id}
|
|
189
|
+
created: "{now}"
|
|
190
|
+
updated: "{now}"
|
|
191
|
+
tags: [{comma-separated tags, or empty}]
|
|
184
192
|
---
|
|
185
193
|
|
|
194
|
+
# {title}
|
|
195
|
+
|
|
196
|
+
## Summary
|
|
197
|
+
|
|
198
|
+
{essential content}
|
|
186
199
|
```
|
|
187
200
|
|
|
188
201
|
**Task mode:**
|
|
189
202
|
```
|
|
190
203
|
---
|
|
204
|
+
schema: vault-task-note/v1
|
|
191
205
|
title: "{title}"
|
|
192
|
-
|
|
206
|
+
project: {prefix}
|
|
193
207
|
task_id: {task-id}
|
|
208
|
+
created: "{now}"
|
|
209
|
+
updated: "{now}"
|
|
210
|
+
tags: [{comma-separated tags, or empty}]
|
|
194
211
|
status: TODO
|
|
195
|
-
last_updated: "{now}"
|
|
196
212
|
---
|
|
197
213
|
|
|
198
214
|
# {title}
|
|
199
215
|
|
|
216
|
+
{task description}
|
|
217
|
+
|
|
218
|
+
## Checklist
|
|
219
|
+
|
|
220
|
+
- [ ] {first implementation step}
|
|
221
|
+
|
|
200
222
|
## Notes
|
|
201
223
|
|
|
224
|
+
{constraints or context}
|
|
202
225
|
```
|
|
203
|
-
|
|
226
|
+
|
|
227
|
+
Quote every scalar string value. In particular, a value that is all digits
|
|
228
|
+
(a numeric-looking `title`, or any future numeric id) must be quoted — an
|
|
229
|
+
unquoted all-digit value is parsed as an integer and fails a `type: string`
|
|
230
|
+
schema field.
|
|
231
|
+
5. Write it with `synapse vault-write <path>` (content on stdin). Task mode: path
|
|
204
232
|
`tasks/{project}/{filename}.md` (project resolved in "Resolving the
|
|
205
233
|
project folder" above). Bare mode: path `{category}/{filename}.md`
|
|
206
234
|
(category resolved above).
|
|
@@ -209,7 +237,6 @@ or supplied directly by a caller like `/synapse-task-note`):
|
|
|
209
237
|
|
|
210
238
|
Report the file path back to the user.
|
|
211
239
|
|
|
212
|
-
- Bare mode:
|
|
240
|
+
- Bare mode: report the Summary that was captured.
|
|
213
241
|
- Task mode: note the task ID extracted or resolved, and remind the user
|
|
214
|
-
|
|
215
|
-
per the `synapse-task` skill.
|
|
242
|
+
that future status transitions are managed by the `synapse-task` skill.
|
|
@@ -178,9 +178,13 @@ so the `## Status` line is the only lifecycle marker that matters. It simply sta
|
|
|
178
178
|
|
|
179
179
|
```
|
|
180
180
|
---
|
|
181
|
+
schema: vault-design-note/v1
|
|
181
182
|
title: "{PROJECT} — {Topic}"
|
|
182
183
|
project: {prefix}
|
|
184
|
+
note_id: {id}
|
|
183
185
|
created: "{now}"
|
|
186
|
+
updated: "{now}"
|
|
187
|
+
tags: [{comma-separated configured tags, or empty}]
|
|
184
188
|
---
|
|
185
189
|
|
|
186
190
|
# {PROJECT} — {Topic}
|
|
@@ -204,7 +208,9 @@ Discussing | Ready | Reference
|
|
|
204
208
|
- {Anything deferred or unresolved}
|
|
205
209
|
```
|
|
206
210
|
|
|
207
|
-
|
|
211
|
+
Resolve tags through the `synapse-vault` skill's configured vocabulary procedure. Fetch machine
|
|
212
|
+
local time once (`date '+%Y-%m-%d %H:%M:%S %Z'`) — never infer it — and use the exact same value
|
|
213
|
+
for `created` and `updated`.
|
|
208
214
|
|
|
209
215
|
No `Notes`/changelog section — it stays a small, single-conclusion note; there's no long-running
|
|
210
216
|
edit history here worth tracking separately.
|
|
@@ -32,8 +32,9 @@ below:
|
|
|
32
32
|
The title is whatever the user is naming or describing, minus any mode-signaling phrasing. Example:
|
|
33
33
|
|
|
34
34
|
- "make a note about my idea" → bare note titled "My idea"
|
|
35
|
-
- "track this as a task: implement Foo" → task note titled "Implement Foo"
|
|
36
|
-
|
|
35
|
+
- "track this as a task: implement Foo" → task note titled "Implement Foo"; if no task ID is
|
|
36
|
+
already present in that phrasing, "Resolving a missing task ID" below assigns one to `task_id`
|
|
37
|
+
in frontmatter only, without changing the title
|
|
37
38
|
|
|
38
39
|
In task mode, also attempt to extract a task ID from the title by matching a `{prefix}-\d+` pattern
|
|
39
40
|
(letters, a hyphen, then digits) — the user sometimes already names one directly (e.g. "track
|
|
@@ -105,19 +106,19 @@ unchanged for anyone who has never touched an XDG config directory.
|
|
|
105
106
|
5. Whenever step 3 or step 4 resolves a pair not already in the conf file (including a fresh
|
|
106
107
|
`project-name=prefix` line matching what was just deduced or asked), append it — so the next task
|
|
107
108
|
for this project resolves from step 2 without a search or a question.
|
|
108
|
-
6. Once the prefix is known, find the next number
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
`
|
|
112
|
-
|
|
109
|
+
6. Once the prefix is known, find the next number. `task_id` and `note_id` (the id every note gets
|
|
110
|
+
— see "Assigning a note_id" below) share one counter per prefix, so this has to check both
|
|
111
|
+
fields, not just `task_id` alone: run `synapse vault-search --fields
|
|
112
|
+
frontmatter.task_id,frontmatter.note_id` with `{"or": [{"!=": [{"var": "frontmatter.task_id"},
|
|
113
|
+
null]}, {"!=": [{"var": "frontmatter.note_id"}, null]}]}` on stdin, filter both returned columns
|
|
114
|
+
client-side for values matching `{prefix}-\d+`, take the highest number found across both, add
|
|
115
|
+
1. If none exist yet for that prefix, start at 1.
|
|
113
116
|
7. Format the new task ID **zero-padded to 3 digits** (`{prefix}-001`, `{prefix}-030`,
|
|
114
117
|
`{prefix}-037`, ...), matching the org-roam-era convention — widening
|
|
115
118
|
naturally past 3 digits if a prefix ever needs it.
|
|
116
|
-
8. **
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
use the resolved task ID for `task_id`. Don't let the frontmatter task
|
|
120
|
-
ID and the visible title disagree.
|
|
119
|
+
8. **The title itself is never prefixed with the task ID.** `task_id` lives in frontmatter only —
|
|
120
|
+
the same convention `note_id` already uses for bare and design notes, now consistent across all
|
|
121
|
+
three kinds. `title`, the `# ` heading, and the filename all stay exactly the given topic.
|
|
121
122
|
|
|
122
123
|
The synapse-design-note and synapse-task-note skills read the same conf file directly for the same
|
|
123
124
|
reason — they don't duplicate this resolution logic, just this file.
|
|
@@ -146,6 +147,25 @@ triage/priority one a vault owner might maintain by hand (e.g.
|
|
|
146
147
|
sorting a note into one of those, if a category has one, is never an
|
|
147
148
|
agent's call to make.
|
|
148
149
|
|
|
150
|
+
## Assigning a note_id (bare mode only)
|
|
151
|
+
|
|
152
|
+
Task mode already gets its id via "Resolving a missing task ID" above. Bare mode needs one too —
|
|
153
|
+
every note gets a `note_id`, not just tasks — but unlike task mode, this step never blocks with a
|
|
154
|
+
question: a quick bare note shouldn't require an interrupt just to get an id.
|
|
155
|
+
|
|
156
|
+
1. Try steps 1-3 of "Resolving a missing task ID" above (repo context, the resolved
|
|
157
|
+
`synapse-projects.conf`, then deducing from the vault itself) to resolve a project prefix. Skip
|
|
158
|
+
step 4 entirely — if none of the first three resolve one, fall through to step 2 below instead of
|
|
159
|
+
asking.
|
|
160
|
+
2. If a prefix resolved: mint the next number the same shared-counter way as that section's step 6
|
|
161
|
+
(checking both `task_id` and `note_id` across the vault for that prefix). If no prefix resolved:
|
|
162
|
+
mint `note-NNN` instead — its own flat, project-less counter, found the same way (`synapse
|
|
163
|
+
vault-search --fields frontmatter.note_id`, filter for `note-\d+`, take the highest, add 1; start
|
|
164
|
+
at 1 if none exist yet).
|
|
165
|
+
3. Carry the resolved `{id}` into "Creating the note" below as `note_id`. Unlike task mode, the title
|
|
166
|
+
itself is untouched — no id prefix on the visible title or the `title` frontmatter field for a bare
|
|
167
|
+
note.
|
|
168
|
+
|
|
149
169
|
## Resolving the project folder (task mode only)
|
|
150
170
|
|
|
151
171
|
Task notes are grouped one level deeper by project, `tasks/{project}/{filename}.md` — see the
|
|
@@ -169,35 +189,58 @@ or supplied directly by a caller like the synapse-task-note skill):
|
|
|
169
189
|
characters (`/ : * ? " < > |`) with `-`, collapse repeated whitespace.
|
|
170
190
|
No timestamp prefix, no project-slug prefix — the filename is just the
|
|
171
191
|
(sanitized) title.
|
|
172
|
-
2.
|
|
173
|
-
|
|
174
|
-
|
|
192
|
+
2. Resolve tags through the `synapse-vault` skill's configured
|
|
193
|
+
`synapse-tag-vocabulary.conf` procedure. Choose only configured entries; an empty list is valid
|
|
194
|
+
when no tag applies, but the field itself is mandatory.
|
|
195
|
+
3. Fetch machine local time once: `date '+%Y-%m-%d %H:%M:%S %Z'` — never use inferred
|
|
196
|
+
time. Use the exact same value for `created` and `updated`.
|
|
197
|
+
4. Build the file content. Bare notes require a concise Summary. Task notes require lead prose and
|
|
198
|
+
at least one real flat checklist item before the first write.
|
|
175
199
|
|
|
176
200
|
**Bare mode:**
|
|
177
201
|
```
|
|
178
202
|
---
|
|
203
|
+
schema: vault-note/v1
|
|
179
204
|
title: "{title}"
|
|
205
|
+
note_id: {id}
|
|
180
206
|
created: "{now}"
|
|
207
|
+
updated: "{now}"
|
|
208
|
+
tags: [{comma-separated tags, or empty}]
|
|
181
209
|
---
|
|
182
210
|
|
|
211
|
+
# {title}
|
|
212
|
+
|
|
213
|
+
## Summary
|
|
214
|
+
|
|
215
|
+
{essential content}
|
|
183
216
|
```
|
|
184
217
|
|
|
185
218
|
**Task mode:**
|
|
186
219
|
```
|
|
187
220
|
---
|
|
221
|
+
schema: vault-task-note/v1
|
|
188
222
|
title: "{title}"
|
|
189
|
-
|
|
223
|
+
project: {prefix}
|
|
190
224
|
task_id: {task-id}
|
|
225
|
+
created: "{now}"
|
|
226
|
+
updated: "{now}"
|
|
227
|
+
tags: [{comma-separated tags, or empty}]
|
|
191
228
|
status: TODO
|
|
192
|
-
last_updated: "{now}"
|
|
193
229
|
---
|
|
194
230
|
|
|
195
231
|
# {title}
|
|
196
232
|
|
|
233
|
+
{task description}
|
|
234
|
+
|
|
235
|
+
## Checklist
|
|
236
|
+
|
|
237
|
+
- [ ] {first implementation step}
|
|
238
|
+
|
|
197
239
|
## Notes
|
|
198
240
|
|
|
241
|
+
{constraints or context}
|
|
199
242
|
```
|
|
200
|
-
|
|
243
|
+
5. Write it with `synapse vault-write <path>` (content on stdin). Task mode: path
|
|
201
244
|
`tasks/{project}/{filename}.md` (project resolved in "Resolving the
|
|
202
245
|
project folder" above). Bare mode: path `{category}/{filename}.md`
|
|
203
246
|
(category resolved above).
|
|
@@ -206,7 +249,6 @@ or supplied directly by a caller like the synapse-task-note skill):
|
|
|
206
249
|
|
|
207
250
|
Report the file path back to the user.
|
|
208
251
|
|
|
209
|
-
- Bare mode:
|
|
252
|
+
- Bare mode: report the Summary that was captured.
|
|
210
253
|
- Task mode: note the task ID extracted or resolved, and remind the user
|
|
211
|
-
|
|
212
|
-
per the `synapse-task` skill.
|
|
254
|
+
that future status transitions are managed by the `synapse-task` skill.
|
|
@@ -68,8 +68,9 @@ Follow the synapse-note skill's task-mode procedure exactly (its "Creating the n
|
|
|
68
68
|
don't duplicate that scaffolding here, just supply its inputs:
|
|
69
69
|
|
|
70
70
|
- **Title:** a short, plain description of the compiled plan (e.g. "Rollup direct storage
|
|
71
|
-
implementation") —
|
|
72
|
-
and
|
|
71
|
+
implementation") — stays exactly this, never prefixed with the resolved `task_id`; the
|
|
72
|
+
synapse-note skill's task mode resolves the project prefix and `task_id` itself and puts them in
|
|
73
|
+
frontmatter only.
|
|
73
74
|
- **Project:** derive from the source design note's `project:` frontmatter — that prefix is already
|
|
74
75
|
resolved (the design note went through the synapse-design-note skill's resolution when it was
|
|
75
76
|
created, which reads/appends the resolved `synapse-projects.conf`, per the synapse-note skill's
|
|
@@ -133,7 +134,7 @@ Codex: [reads designs/{PROJECT} — Rollup direct storage.md, Status: Ready]
|
|
|
133
134
|
|
|
134
135
|
Project already known: {PROJECT} → {prefix}-005.
|
|
135
136
|
|
|
136
|
-
Created: tasks/{PROJECT}/
|
|
137
|
+
Created: tasks/{PROJECT}/Rollup direct storage implementation.md (task_id: {prefix}-005)
|
|
137
138
|
Linked back from designs/{PROJECT} — Rollup direct storage.md.
|
|
138
139
|
|
|
139
140
|
Status transitions happen automatically via the synapse-task skill once you start work.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imunitic/synapse",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Memory for Claude Code, Codex CLI, and OpenCode: a durable Obsidian vault plus a per-repo code graph.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,14 +17,15 @@
|
|
|
17
17
|
"skills",
|
|
18
18
|
"commands",
|
|
19
19
|
"harness",
|
|
20
|
+
"schema",
|
|
20
21
|
"synapse-claude.md",
|
|
21
22
|
"Index.md.template",
|
|
22
23
|
"*.conf.template"
|
|
23
24
|
],
|
|
24
25
|
"optionalDependencies": {
|
|
25
|
-
"@imunitic/synapse-darwin-arm64": "0.
|
|
26
|
-
"@imunitic/synapse-linux-x64": "0.
|
|
27
|
-
"@imunitic/synapse-linux-arm64": "0.
|
|
26
|
+
"@imunitic/synapse-darwin-arm64": "0.4.0",
|
|
27
|
+
"@imunitic/synapse-linux-x64": "0.4.0",
|
|
28
|
+
"@imunitic/synapse-linux-arm64": "0.4.0"
|
|
28
29
|
},
|
|
29
30
|
"license": "SEE LICENSE IN LICENSE"
|
|
30
31
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Declared keys are the full v1 spec; enforcement is a deliberate subset — unknown note fields are ignored, never an error.
|
|
2
|
+
schema: synapse-note-schema/v1
|
|
3
|
+
id: vault-design-note/v1
|
|
4
|
+
|
|
5
|
+
frontmatter:
|
|
6
|
+
fields:
|
|
7
|
+
schema:
|
|
8
|
+
type: string
|
|
9
|
+
required: true
|
|
10
|
+
const: vault-design-note/v1
|
|
11
|
+
title:
|
|
12
|
+
type: string
|
|
13
|
+
required: true
|
|
14
|
+
min_length: 1
|
|
15
|
+
project:
|
|
16
|
+
type: string
|
|
17
|
+
required: true
|
|
18
|
+
min_length: 1
|
|
19
|
+
note_id:
|
|
20
|
+
type: string
|
|
21
|
+
required: true
|
|
22
|
+
pattern: '^[a-z][a-z0-9-]*-[0-9]{3,}$'
|
|
23
|
+
mutable: false
|
|
24
|
+
created:
|
|
25
|
+
type: timestamp
|
|
26
|
+
required: true
|
|
27
|
+
format: 'YYYY-MM-dd HH:mm:ss TZ'
|
|
28
|
+
timezone: local
|
|
29
|
+
mutable: false
|
|
30
|
+
updated:
|
|
31
|
+
type: timestamp
|
|
32
|
+
required: true
|
|
33
|
+
format: 'YYYY-MM-dd HH:mm:ss TZ'
|
|
34
|
+
timezone: local
|
|
35
|
+
update_on: [vault-write, vault-patch]
|
|
36
|
+
tags:
|
|
37
|
+
type: list
|
|
38
|
+
required: true
|
|
39
|
+
items: string
|
|
40
|
+
|
|
41
|
+
body:
|
|
42
|
+
h1:
|
|
43
|
+
required: true
|
|
44
|
+
count: 1
|
|
45
|
+
equals: frontmatter.title
|
|
46
|
+
preamble:
|
|
47
|
+
- type: blockquote
|
|
48
|
+
required: false
|
|
49
|
+
position: immediately_after_h1
|
|
50
|
+
pattern: '^> Compiled task: \[\[[^\]]+\]\]$'
|
|
51
|
+
sections:
|
|
52
|
+
- title: Status
|
|
53
|
+
level: 2
|
|
54
|
+
required: true
|
|
55
|
+
content:
|
|
56
|
+
type: string
|
|
57
|
+
enum: [Discussing, Ready, Reference]
|
|
58
|
+
- title: Problem
|
|
59
|
+
level: 2
|
|
60
|
+
required: true
|
|
61
|
+
non_empty: true
|
|
62
|
+
- title: Approach
|
|
63
|
+
level: 2
|
|
64
|
+
required: true
|
|
65
|
+
non_empty: true
|
|
66
|
+
children:
|
|
67
|
+
- title: Alternatives considered
|
|
68
|
+
level: 3
|
|
69
|
+
required: false
|
|
70
|
+
non_empty: true
|
|
71
|
+
- title: Constraints
|
|
72
|
+
level: 2
|
|
73
|
+
required: true
|
|
74
|
+
non_empty: true
|
|
75
|
+
- title: Open Questions
|
|
76
|
+
level: 2
|
|
77
|
+
required: false
|
|
78
|
+
non_empty: true
|
|
79
|
+
section_order: relative
|
|
80
|
+
|
|
81
|
+
checks:
|
|
82
|
+
- equals: [filename.stem, frontmatter.title]
|
|
83
|
+
- unique: frontmatter.note_id
|
|
84
|
+
when: create
|
|
85
|
+
- vocabulary:
|
|
86
|
+
field: frontmatter.project
|
|
87
|
+
source: synapse-projects.conf
|
|
88
|
+
projection: values
|
|
89
|
+
- vocabulary:
|
|
90
|
+
field: frontmatter.tags
|
|
91
|
+
source: synapse-tag-vocabulary.conf
|
|
92
|
+
- not_before: [frontmatter.updated, frontmatter.created]
|
|
93
|
+
- equals:
|
|
94
|
+
values: [frontmatter.created, frontmatter.updated]
|
|
95
|
+
when: create
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Declared keys are the full v1 spec; enforcement is a deliberate subset — unknown note fields are ignored, never an error.
|
|
2
|
+
schema: synapse-note-schema/v1
|
|
3
|
+
id: vault-note/v1
|
|
4
|
+
|
|
5
|
+
frontmatter:
|
|
6
|
+
fields:
|
|
7
|
+
schema:
|
|
8
|
+
type: string
|
|
9
|
+
required: true
|
|
10
|
+
const: vault-note/v1
|
|
11
|
+
title:
|
|
12
|
+
type: string
|
|
13
|
+
required: true
|
|
14
|
+
min_length: 1
|
|
15
|
+
note_id:
|
|
16
|
+
type: string
|
|
17
|
+
required: true
|
|
18
|
+
pattern: '^[a-z][a-z0-9-]*-[0-9]{3,}$'
|
|
19
|
+
mutable: false
|
|
20
|
+
created:
|
|
21
|
+
type: timestamp
|
|
22
|
+
required: true
|
|
23
|
+
format: 'YYYY-MM-dd HH:mm:ss TZ'
|
|
24
|
+
timezone: local
|
|
25
|
+
mutable: false
|
|
26
|
+
updated:
|
|
27
|
+
type: timestamp
|
|
28
|
+
required: true
|
|
29
|
+
format: 'YYYY-MM-dd HH:mm:ss TZ'
|
|
30
|
+
timezone: local
|
|
31
|
+
update_on: [vault-write, vault-patch]
|
|
32
|
+
tags:
|
|
33
|
+
type: list
|
|
34
|
+
required: true
|
|
35
|
+
items: string
|
|
36
|
+
|
|
37
|
+
body:
|
|
38
|
+
h1:
|
|
39
|
+
required: true
|
|
40
|
+
count: 1
|
|
41
|
+
equals: frontmatter.title
|
|
42
|
+
sections:
|
|
43
|
+
- title: Summary
|
|
44
|
+
level: 2
|
|
45
|
+
required: true
|
|
46
|
+
- title: Details
|
|
47
|
+
level: 2
|
|
48
|
+
required: false
|
|
49
|
+
- title: Sources
|
|
50
|
+
level: 2
|
|
51
|
+
required: false
|
|
52
|
+
- title: Open Questions
|
|
53
|
+
level: 2
|
|
54
|
+
required: false
|
|
55
|
+
section_order: relative
|
|
56
|
+
|
|
57
|
+
checks:
|
|
58
|
+
- equals: [filename.stem, frontmatter.title]
|
|
59
|
+
- unique: frontmatter.note_id
|
|
60
|
+
when: create
|
|
61
|
+
- vocabulary:
|
|
62
|
+
field: frontmatter.tags
|
|
63
|
+
source: synapse-tag-vocabulary.conf
|
|
64
|
+
- not_before: [frontmatter.updated, frontmatter.created]
|
|
65
|
+
- equals:
|
|
66
|
+
values: [frontmatter.created, frontmatter.updated]
|
|
67
|
+
when: create
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Declared keys are the full v1 spec; enforcement is a deliberate subset — unknown note fields are ignored, never an error.
|
|
2
|
+
schema: synapse-note-schema/v1
|
|
3
|
+
id: vault-task-note/v1
|
|
4
|
+
|
|
5
|
+
frontmatter:
|
|
6
|
+
fields:
|
|
7
|
+
schema:
|
|
8
|
+
type: string
|
|
9
|
+
required: true
|
|
10
|
+
const: vault-task-note/v1
|
|
11
|
+
title:
|
|
12
|
+
type: string
|
|
13
|
+
required: true
|
|
14
|
+
min_length: 1
|
|
15
|
+
project:
|
|
16
|
+
type: string
|
|
17
|
+
required: true
|
|
18
|
+
min_length: 1
|
|
19
|
+
task_id:
|
|
20
|
+
type: string
|
|
21
|
+
required: true
|
|
22
|
+
pattern: '^[a-z][a-z0-9-]*-[0-9]{3,}$'
|
|
23
|
+
mutable: false
|
|
24
|
+
created:
|
|
25
|
+
type: timestamp
|
|
26
|
+
required: true
|
|
27
|
+
format: 'YYYY-MM-dd HH:mm:ss TZ'
|
|
28
|
+
timezone: local
|
|
29
|
+
mutable: false
|
|
30
|
+
updated:
|
|
31
|
+
type: timestamp
|
|
32
|
+
required: true
|
|
33
|
+
format: 'YYYY-MM-dd HH:mm:ss TZ'
|
|
34
|
+
timezone: local
|
|
35
|
+
update_on: [vault-write, vault-patch]
|
|
36
|
+
tags:
|
|
37
|
+
type: list
|
|
38
|
+
required: true
|
|
39
|
+
items: string
|
|
40
|
+
status:
|
|
41
|
+
type: string
|
|
42
|
+
required: true
|
|
43
|
+
enum: [TODO, IN-PROGRESS, REVIEW, DONE, CANCELED]
|
|
44
|
+
|
|
45
|
+
body:
|
|
46
|
+
h1:
|
|
47
|
+
required: true
|
|
48
|
+
count: 1
|
|
49
|
+
equals: frontmatter.title
|
|
50
|
+
lead:
|
|
51
|
+
type: prose
|
|
52
|
+
required: true
|
|
53
|
+
position: before_sections
|
|
54
|
+
checklist:
|
|
55
|
+
required: true
|
|
56
|
+
min_items: 1
|
|
57
|
+
position: before_sections
|
|
58
|
+
nested_items: false
|
|
59
|
+
allowed_children: [paragraph, fenced_code]
|
|
60
|
+
sections:
|
|
61
|
+
- title: Checklist
|
|
62
|
+
level: 2
|
|
63
|
+
required: true
|
|
64
|
+
- title: Notes
|
|
65
|
+
level: 2
|
|
66
|
+
required: false
|
|
67
|
+
max_occurs: 1
|
|
68
|
+
children:
|
|
69
|
+
- level: 3
|
|
70
|
+
required: false
|
|
71
|
+
repeatable: true
|
|
72
|
+
title_pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2} — .+$'
|
|
73
|
+
section_order: relative
|
|
74
|
+
|
|
75
|
+
checks:
|
|
76
|
+
- equals: [filename.stem, frontmatter.title]
|
|
77
|
+
- unique: frontmatter.task_id
|
|
78
|
+
when: create
|
|
79
|
+
- vocabulary:
|
|
80
|
+
field: frontmatter.project
|
|
81
|
+
source: synapse-projects.conf
|
|
82
|
+
projection: values
|
|
83
|
+
- vocabulary:
|
|
84
|
+
field: frontmatter.tags
|
|
85
|
+
source: synapse-tag-vocabulary.conf
|
|
86
|
+
- not_before: [frontmatter.updated, frontmatter.created]
|
|
87
|
+
- equals:
|
|
88
|
+
values: [frontmatter.created, frontmatter.updated]
|
|
89
|
+
when: create
|
|
90
|
+
- const:
|
|
91
|
+
field: frontmatter.status
|
|
92
|
+
value: TODO
|
|
93
|
+
when: create
|
|
@@ -21,12 +21,12 @@ Invoke this skill **automatically** in two situations:
|
|
|
21
21
|
|
|
22
22
|
1. **Starting work on a task** — as soon as the user confirms work is
|
|
23
23
|
beginning, before writing any code. Set `status: IN-PROGRESS` and update
|
|
24
|
-
`
|
|
24
|
+
`updated`. No notes needed at this point.
|
|
25
25
|
|
|
26
26
|
2. **Finishing work on a task** — after all phases are committed and
|
|
27
27
|
the task note's checklist has been updated. Set `status: REVIEW` (if all
|
|
28
28
|
items checked) or `status: IN-PROGRESS` (if any remain), update
|
|
29
|
-
`
|
|
29
|
+
`updated`, and append an implementation summary to the `## Notes`
|
|
30
30
|
section.
|
|
31
31
|
|
|
32
32
|
Never wait to be asked — apply this skill proactively at both transitions.
|
|
@@ -35,7 +35,7 @@ Never wait to be asked — apply this skill proactively at both transitions.
|
|
|
35
35
|
|
|
36
36
|
- Updates the `status:` frontmatter field (`TODO` → `IN-PROGRESS` or
|
|
37
37
|
`REVIEW`).
|
|
38
|
-
- Updates `
|
|
38
|
+
- Updates `updated` in frontmatter.
|
|
39
39
|
- At task completion: appends concise implementation bullets to the
|
|
40
40
|
existing `## Notes` section (or creates one if none exists).
|
|
41
41
|
|
|
@@ -60,11 +60,11 @@ into `status:` either — always go through this skill, which caps at
|
|
|
60
60
|
2. Inspect its checklist items (`- [ ]` / `- [x]`).
|
|
61
61
|
3. Determine the new `status:` value: `IN-PROGRESS` if any unchecked,
|
|
62
62
|
`REVIEW` if all checked.
|
|
63
|
-
4. Fetch machine local time: `date '+%Y-%m-%d %H:%M'` — never use inferred
|
|
63
|
+
4. Fetch machine local time: `date '+%Y-%m-%d %H:%M:%S %Z'` — never use inferred
|
|
64
64
|
time.
|
|
65
|
-
5. Update `status:` and `
|
|
65
|
+
5. Update `status:` and `updated:` with two `synapse frontmatter set`
|
|
66
66
|
calls, one per field: `synapse frontmatter set <path> status <value>`
|
|
67
|
-
then `synapse frontmatter set <path>
|
|
67
|
+
then `synapse frontmatter set <path> updated "{now}"`. Each call
|
|
68
68
|
changes exactly that one line and nothing else, entirely inside the
|
|
69
69
|
compiled binary — the note's body never enters your context at all.
|
|
70
70
|
When the command isn't available, fall back to **read-modify-write**:
|
|
@@ -94,12 +94,14 @@ The vault-patch hazards below are the task-note-specific instance of a general r
|
|
|
94
94
|
`synapse-vault` skill carries the full list (H1 replace, nested heading paths, frontmatter) for
|
|
95
95
|
every note, not just task notes.
|
|
96
96
|
|
|
97
|
-
**
|
|
98
|
-
|
|
99
|
-
the
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
**Edit checklist items through the `Checklist` heading, never the H1.** With `## Checklist` as its
|
|
98
|
+
own H2 — a sibling of `## Notes` — `vault-patch --heading "{H1 title}::Checklist" --replace`
|
|
99
|
+
(supplying the full new checklist) is the correct tool for a whole-checklist edit and leaves
|
|
100
|
+
`## Notes` untouched. Never target the top-level H1 (`--heading "{H1 title}"`): its section extends
|
|
101
|
+
through *all* nested subheadings (including `## Notes`), so a replace there silently deletes
|
|
102
|
+
everything past the checklist. To flip a single `- [ ]` → `- [x]` in place, `synapse vault-read`
|
|
103
|
+
the full file, edit that one line, and `synapse vault-write` the whole file back — there is no
|
|
104
|
+
per-item toggle yet.
|
|
103
105
|
|
|
104
106
|
## Notes format
|
|
105
107
|
|
|
@@ -116,18 +118,26 @@ level. Append to the last existing notes section.
|
|
|
116
118
|
## Task file structure
|
|
117
119
|
|
|
118
120
|
Each task note, in any project, has **exactly one top-level heading** (the
|
|
119
|
-
task itself, `# {title}`).
|
|
120
|
-
|
|
121
|
-
Step` sub-headings for
|
|
121
|
+
task itself, `# {title}`). Under it: lead prose describing the task, then a
|
|
122
|
+
required `## Checklist` heading holding the `- [ ]` implementation items,
|
|
123
|
+
then a `## Notes` section. Do not create `## Step` sub-headings for
|
|
124
|
+
implementation steps, and do not put checklist items directly under the H1.
|
|
122
125
|
|
|
123
126
|
Correct structure:
|
|
124
127
|
```md
|
|
125
128
|
# Implement something
|
|
126
129
|
|
|
127
130
|
Description of the task.
|
|
131
|
+
|
|
132
|
+
## Checklist
|
|
133
|
+
|
|
128
134
|
- [ ] Step one
|
|
129
135
|
- [ ] Step two
|
|
130
136
|
- [ ] Step three
|
|
137
|
+
|
|
138
|
+
## Notes
|
|
139
|
+
|
|
140
|
+
{constraints or context}
|
|
131
141
|
```
|
|
132
142
|
|
|
133
143
|
Wrong structure (do not do this):
|