@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.
@@ -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 result = spawnSync(bin, process.argv.slice(2), { stdio: "inherit" });
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 result = spawnSync(bin, process.argv.slice(2), { stdio: "inherit" });
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
- Fetch machine local time for `created` (`date '+%Y-%m-%d %H:%M'`) never infer it.
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.
@@ -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. Fetch machine local time: `date '+%Y-%m-%d %H:%M'` — never use inferred
175
- time. Use this for the `created` frontmatter field.
176
- 3. Build the file content:
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
- created: "{now}"
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
- 4. Write it with `synapse vault-write <path>` (content on stdin). Task mode: path
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: note that the note is intentionally near-empty.
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
- to populate the `## Notes` section and checklist before starting work,
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
- Fetch machine local time for `created` (`date '+%Y-%m-%d %H:%M'`) never infer it.
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" (before task-ID
36
- resolution prepends the resolved ID, per "Resolving a missing task ID" below)
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: run `synapse vault-search --fields
109
- frontmatter.task_id` with `{"var": "frontmatter.task_id"}` on stdin,
110
- filter the returned values client-side for ones matching
111
- `{prefix}-\d+`, take the highest number found, add 1. If none exist yet
112
- for that prefix, start at 1.
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. **Prepend the resolved task ID to the title itself** the final title
117
- becomes `{task-id} {original title}` (em dash). Use this same final
118
- title for both the `title` frontmatter field and the `# ` heading, and
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. Fetch machine local time: `date '+%Y-%m-%d %H:%M'` — never use inferred
173
- time. Use this for the `created` frontmatter field.
174
- 3. Build the file content:
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
- created: "{now}"
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
- 4. Write it with `synapse vault-write <path>` (content on stdin). Task mode: path
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: note that the note is intentionally near-empty.
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
- to populate the `## Notes` section and checklist before starting work,
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") — the synapse-note skill's task mode resolves the project prefix and `task_id`
72
- and prepends them itself.
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}/{prefix}-005 — Rollup direct storage implementation.md
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.1",
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.3.1",
26
- "@imunitic/synapse-linux-x64": "0.3.1",
27
- "@imunitic/synapse-linux-arm64": "0.3.1"
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
- `last_updated`. No notes needed at this point.
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
- `last_updated`, and append an implementation summary to the `## Notes`
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 `last_updated` in frontmatter.
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 `last_updated:` with two `synapse frontmatter set`
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> last_updated "{now}"`. Each call
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
- **Do not use `vault-patch --heading "{H1 title}" --replace` to edit checklist items.** A top-level
98
- heading's own section extends through *all* nested subheadings (including `## Notes`), not just
99
- the leading paragraph/checklist directly under it a replace there silently deletes everything
100
- past the checklist, including the Notes section. To check off checklist items,
101
- instead `synapse vault-read` the full file, edit the `- [ ]` `- [x]` lines in the
102
- returned content, and `synapse vault-write` the whole file back.
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}`). Implementation steps go as `- [ ]` checklist items
120
- **under that heading**, not as additional headings. Do not create `##
121
- Step` sub-headings for implementation steps.
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):