@aitne-sh/aitne 0.1.7 → 0.1.8

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 (222) hide show
  1. package/README.md +195 -829
  2. package/agent-assets/agent-profiles/_safety.md +49 -17
  3. package/agent-assets/agent-profiles/profile-importer.md +1 -1
  4. package/agent-assets/agent-profiles/routine.md +4 -3
  5. package/agent-assets/docs/concepts/agent-day.md +6 -1
  6. package/agent-assets/docs/concepts/auth-health.md +10 -1
  7. package/agent-assets/docs/concepts/backends-and-tiers.md +74 -40
  8. package/agent-assets/docs/concepts/costs-and-quotas.md +25 -5
  9. package/agent-assets/docs/concepts/delegated-mode.md +147 -68
  10. package/agent-assets/docs/concepts/memory-model.md +9 -4
  11. package/agent-assets/docs/concepts/observations.md +13 -1
  12. package/agent-assets/docs/concepts/process-keys.md +20 -5
  13. package/agent-assets/docs/concepts/routines.md +38 -20
  14. package/agent-assets/docs/concepts/safety-model.md +30 -13
  15. package/agent-assets/docs/concepts/skills.md +12 -7
  16. package/agent-assets/docs/features/integrations/calendar.md +1 -1
  17. package/agent-assets/docs/features/integrations/git.md +2 -2
  18. package/agent-assets/docs/features/integrations/github.md +9 -2
  19. package/agent-assets/docs/features/integrations/mail.md +1 -1
  20. package/agent-assets/docs/features/integrations/notion.md +34 -6
  21. package/agent-assets/docs/features/integrations/obsidian.md +7 -2
  22. package/agent-assets/docs/features/lifestyle/git.md +4 -7
  23. package/agent-assets/docs/features/lifestyle/receipts.md +17 -2
  24. package/agent-assets/docs/features/lifestyle/travel-bookings.md +15 -0
  25. package/agent-assets/docs/features/lifestyle/travel-time.md +7 -1
  26. package/agent-assets/docs/features/memory-files/agent-journal.md +2 -2
  27. package/agent-assets/docs/features/memory-files/projects.md +6 -0
  28. package/agent-assets/docs/features/memory-files/roadmap.md +5 -0
  29. package/agent-assets/docs/features/memory-files/today.md +1 -0
  30. package/agent-assets/docs/features/memory-files/user-profile.md +6 -0
  31. package/agent-assets/docs/features/messaging/bang-commands.md +20 -10
  32. package/agent-assets/docs/features/messaging/discord.md +12 -1
  33. package/agent-assets/docs/features/messaging/overview.md +10 -7
  34. package/agent-assets/docs/features/messaging/slack.md +13 -1
  35. package/agent-assets/docs/features/messaging/telegram.md +7 -1
  36. package/agent-assets/docs/features/messaging/whatsapp.md +12 -1
  37. package/agent-assets/docs/features/operations/activity-and-conversations.md +2 -2
  38. package/agent-assets/docs/features/operations/approvals.md +6 -0
  39. package/agent-assets/docs/features/operations/backend-routing.md +7 -0
  40. package/agent-assets/docs/features/operations/cost-tracking.md +6 -0
  41. package/agent-assets/docs/features/operations/notifications.md +6 -0
  42. package/agent-assets/docs/features/operations/schedule-approaching.md +22 -9
  43. package/agent-assets/docs/features/routines/custom-routines.md +10 -4
  44. package/agent-assets/docs/features/routines/evening-review.md +1 -1
  45. package/agent-assets/docs/features/routines/hourly-check.md +1 -1
  46. package/agent-assets/docs/features/routines/morning-routine.md +24 -15
  47. package/agent-assets/docs/features/routines/weekly-review.md +38 -12
  48. package/agent-assets/docs/features/wiki/commands.md +11 -0
  49. package/agent-assets/docs/features/wiki/overview.md +13 -3
  50. package/agent-assets/docs/getting-started/01-what-is-this.md +32 -11
  51. package/agent-assets/docs/getting-started/02-first-steps.md +17 -4
  52. package/agent-assets/docs/getting-started/03-what-can-this-do.md +21 -11
  53. package/agent-assets/docs/getting-started/04-first-day.md +14 -0
  54. package/agent-assets/docs/glossary.md +65 -12
  55. package/agent-assets/docs/guides/add-a-custom-routine.md +12 -0
  56. package/agent-assets/docs/guides/backup-and-restore.md +16 -2
  57. package/agent-assets/docs/guides/budget-and-cost-for-wiki.md +6 -0
  58. package/agent-assets/docs/guides/build-your-wiki.md +14 -0
  59. package/agent-assets/docs/guides/change-which-model-handles-x.md +7 -0
  60. package/agent-assets/docs/guides/connect-a-new-mail-account.md +16 -0
  61. package/agent-assets/docs/guides/explore-with-trace-and-connect.md +6 -0
  62. package/agent-assets/docs/guides/import-knowledge-file.md +11 -0
  63. package/agent-assets/docs/guides/install-and-run.md +20 -4
  64. package/agent-assets/docs/guides/maintain-wiki-health.md +6 -0
  65. package/agent-assets/docs/guides/migrate-machines.md +13 -1
  66. package/agent-assets/docs/guides/multiple-wikis-for-multiple-domains.md +9 -0
  67. package/agent-assets/docs/guides/pause-the-agent.md +12 -4
  68. package/agent-assets/docs/guides/reinstall-cleanly.md +19 -4
  69. package/agent-assets/docs/guides/setup-wizard.md +20 -9
  70. package/agent-assets/docs/guides/switch-default-backend.md +10 -1
  71. package/agent-assets/docs/guides/use-an-existing-obsidian-vault.md +5 -0
  72. package/agent-assets/docs/reference/api.md +29 -1
  73. package/agent-assets/docs/reference/cli-commands.md +22 -3
  74. package/agent-assets/docs/reference/config.md +37 -5
  75. package/agent-assets/docs/reference/disallowed-tools.md +13 -0
  76. package/agent-assets/docs/reference/keyboard-shortcuts.md +13 -0
  77. package/agent-assets/docs/reference/process-keys.md +70 -20
  78. package/agent-assets/docs/reference/skills.md +27 -9
  79. package/agent-assets/docs/troubleshooting/auth-failed.md +7 -2
  80. package/agent-assets/docs/troubleshooting/dashboard-shows-degraded.md +13 -1
  81. package/agent-assets/docs/troubleshooting/fallback-keeps-firing.md +10 -0
  82. package/agent-assets/docs/troubleshooting/messaging-not-pairing.md +11 -0
  83. package/agent-assets/docs/troubleshooting/morning-routine-didnt-run.md +9 -4
  84. package/agent-assets/docs/troubleshooting/observation-not-detected.md +12 -0
  85. package/agent-assets/docs/troubleshooting/quota-exhausted.md +7 -1
  86. package/agent-assets/docs/troubleshooting/wiki-ingest-full-blocked.md +5 -0
  87. package/agent-assets/docs/troubleshooting/wiki-write-failed.md +5 -0
  88. package/agent-assets/optimizer-skills/drift-analysis/SKILL.md +1 -1
  89. package/agent-assets/optimizer-skills/skill-curation/SKILL.md +2 -2
  90. package/agent-assets/skills/agent-actions/SKILL.md +122 -0
  91. package/agent-assets/skills/attach/SKILL.md +1 -2
  92. package/agent-assets/skills/context/SKILL.md +36 -454
  93. package/agent-assets/skills/context/references/api.md +220 -0
  94. package/agent-assets/skills/context/references/required-frontmatter.md +73 -0
  95. package/agent-assets/skills/context/references/snapshot-files.md +103 -0
  96. package/agent-assets/skills/context/seeds/file-responsibilities.seed.json +1 -1
  97. package/agent-assets/skills/docs-search/SKILL.md +13 -13
  98. package/agent-assets/skills/external-services/SKILL.delegated.claude.md +5 -7
  99. package/agent-assets/skills/external-services/SKILL.delegated.codex.md +5 -7
  100. package/agent-assets/skills/external-services/SKILL.delegated.gemini.md +5 -7
  101. package/agent-assets/skills/external-services/SKILL.md +6 -259
  102. package/agent-assets/skills/external-services/SKILL.native.claude.md +1 -2
  103. package/agent-assets/skills/external-services/SKILL.native.codex.md +1 -2
  104. package/agent-assets/skills/external-services/SKILL.native.gemini.md +1 -2
  105. package/agent-assets/skills/external-services/references/calendar-apple.md +97 -0
  106. package/agent-assets/skills/external-services/references/calendar-google.md +72 -0
  107. package/agent-assets/skills/external-services/references/calendar-outlook.md +36 -0
  108. package/agent-assets/skills/external-services/references/github.md +17 -0
  109. package/agent-assets/skills/external-services/references/obsidian.md +49 -0
  110. package/agent-assets/skills/external-services/references/skills-crud.md +27 -0
  111. package/agent-assets/skills/gmail-lifestyle/SKILL.md +224 -0
  112. package/agent-assets/skills/gmail-lifestyle/references/receipts-api.md +93 -0
  113. package/agent-assets/skills/gmail-lifestyle/references/travel-bookings-api.md +75 -0
  114. package/agent-assets/skills/gmail-lifestyle/references/travel-time-api.md +59 -0
  115. package/agent-assets/skills/mail/SKILL.delegated.claude.md +1 -1
  116. package/agent-assets/skills/mail/SKILL.delegated.codex.md +1 -1
  117. package/agent-assets/skills/mail/SKILL.delegated.gemini.md +1 -1
  118. package/agent-assets/skills/mail/SKILL.md +9 -114
  119. package/agent-assets/skills/mail/SKILL.native.claude.md +1 -1
  120. package/agent-assets/skills/mail/SKILL.native.codex.md +1 -1
  121. package/agent-assets/skills/mail/SKILL.native.gemini.md +1 -1
  122. package/agent-assets/skills/mail/references/api.md +108 -0
  123. package/agent-assets/skills/mail/references/examples.md +70 -0
  124. package/agent-assets/skills/mail/references/providers.md +8 -8
  125. package/agent-assets/skills/managed-tasks/SKILL.md +472 -0
  126. package/agent-assets/skills/managed-tasks/references/errors.md +70 -0
  127. package/agent-assets/skills/managed-tasks/references/output-path.md +75 -0
  128. package/agent-assets/skills/managed-tasks/references/recurrence-rule.md +86 -0
  129. package/agent-assets/skills/management-policy/SKILL.md +33 -105
  130. package/agent-assets/skills/management-policy/references/policy-workflow.md +101 -0
  131. package/agent-assets/skills/notify/SKILL.md +6 -78
  132. package/agent-assets/skills/notify/references/priority.md +60 -0
  133. package/agent-assets/skills/notion/SKILL.delegated.claude.md +1 -1
  134. package/agent-assets/skills/notion/SKILL.delegated.codex.md +1 -1
  135. package/agent-assets/skills/notion/SKILL.delegated.gemini.md +1 -1
  136. package/agent-assets/skills/notion/SKILL.md +6 -10
  137. package/agent-assets/skills/notion/SKILL.native.claude.md +1 -2
  138. package/agent-assets/skills/notion/SKILL.native.codex.md +1 -2
  139. package/agent-assets/skills/notion/SKILL.native.gemini.md +1 -2
  140. package/agent-assets/skills/observations/SKILL.md +1 -6
  141. package/agent-assets/skills/project-doc/SKILL.md +1 -5
  142. package/agent-assets/skills/reading/SKILL.md +2 -2
  143. package/agent-assets/skills/roadmap/SKILL.md +37 -135
  144. package/agent-assets/skills/roadmap/references/api.md +100 -0
  145. package/agent-assets/skills/roadmap/references/cross-check.md +73 -0
  146. package/agent-assets/skills/roadmap/references/migration.md +56 -0
  147. package/agent-assets/skills/roadmap/references/preparation-timeline.md +2 -2
  148. package/agent-assets/skills/schedule/SKILL.md +52 -88
  149. package/agent-assets/skills/schedule/references/batch.md +93 -0
  150. package/agent-assets/skills/schedule/references/errors.md +214 -0
  151. package/agent-assets/skills/schedule/references/model-selection.md +96 -0
  152. package/agent-assets/skills/schedule/references/recurrence-rule.md +86 -0
  153. package/agent-assets/skills/schedule/references/recurring.md +185 -0
  154. package/agent-assets/skills/scheduled-managed-task/SKILL.md +13 -15
  155. package/agent-assets/skills/today/SKILL.md +27 -57
  156. package/agent-assets/skills/today/references/agent-plan-lifecycle.md +113 -0
  157. package/agent-assets/skills/user-interview/SKILL.md +12 -59
  158. package/agent-assets/skills/user-interview/references/op-briefing.md +51 -0
  159. package/agent-assets/skills/user-interview/references/op-morning.md +59 -0
  160. package/agent-assets/skills/user-interview/references/sweep-and-fallback.md +1 -1
  161. package/agent-assets/skills/user-profile/SKILL.md +43 -63
  162. package/agent-assets/skills/user-profile/references/character-preferences.md +83 -0
  163. package/agent-assets/skills/user-profile/seeds/topic-files.seed.json +28 -0
  164. package/agent-assets/skills/wiki/wiki-ask/SKILL.md +0 -1
  165. package/agent-assets/skills/wiki/wiki-compile/SKILL.md +0 -1
  166. package/agent-assets/skills/wiki/wiki-connect/SKILL.md +0 -1
  167. package/agent-assets/skills/wiki/wiki-graduate/SKILL.md +0 -1
  168. package/agent-assets/skills/wiki/wiki-ingest/SKILL.md +0 -1
  169. package/agent-assets/skills/wiki/wiki-lint/SKILL.md +0 -1
  170. package/agent-assets/skills/wiki/wiki-trace/SKILL.md +0 -1
  171. package/agent-assets/skills/wiki/wiki-vault-rules/SKILL.md +0 -1
  172. package/agent-assets/system-prompts/routine-fetch-window.md +68 -0
  173. package/agent-assets/system-prompts/skill-index-instruction.md +26 -0
  174. package/agent-assets/task-flows/_partials/calendar-acquire.google_calendar.md +18 -11
  175. package/agent-assets/task-flows/_partials/calendar-acquire.outlook_calendar.md +16 -9
  176. package/agent-assets/task-flows/_partials/capture-user-info.md +24 -0
  177. package/agent-assets/task-flows/_partials/confirm-subflow.md +68 -0
  178. package/agent-assets/task-flows/_partials/dm-intent.long-horizon.md +35 -0
  179. package/agent-assets/task-flows/_partials/dm-intent.project.md +391 -0
  180. package/agent-assets/task-flows/_partials/mail-acquire.gmail.md +20 -11
  181. package/agent-assets/task-flows/_partials/mail-acquire.outlook_mail.md +17 -9
  182. package/agent-assets/task-flows/_partials/notion-acquire.notion.md +18 -12
  183. package/agent-assets/task-flows/knowledge.import.md +1 -1
  184. package/agent-assets/task-flows/message.received.dm.md +13 -15
  185. package/agent-assets/task-flows/message.received.dm_first.md +10 -14
  186. package/agent-assets/task-flows/routine.custom.md +3 -1
  187. package/agent-assets/task-flows/routine.evening_review.md +39 -163
  188. package/agent-assets/task-flows/routine.fetch_window.md +17 -12
  189. package/agent-assets/task-flows/routine.hourly_check.md +16 -8
  190. package/agent-assets/task-flows/routine.hourly_check.triage.md +1 -1
  191. package/agent-assets/task-flows/routine.monthly_review.md +46 -4
  192. package/agent-assets/task-flows/routine.morning_routine_journal.md +113 -0
  193. package/agent-assets/task-flows/routine.morning_routine_today.md +673 -0
  194. package/agent-assets/task-flows/routine.roadmap_refresh.md +60 -15
  195. package/agent-assets/task-flows/routine.user_profile_sweep.md +9 -10
  196. package/agent-assets/task-flows/routine.weekly_review.md +285 -70
  197. package/agent-assets/task-flows/scheduled.dm.md +8 -8
  198. package/agent-assets/task-flows/scheduled.task.md +5 -5
  199. package/agent-assets/task-flows/setup.initial.md +165 -245
  200. package/agent-assets/task-flows/wiki.ingest_url.md +1 -1
  201. package/agent-assets/templates/_manifest.json +7 -7
  202. package/agent-assets/templates/dossiers/_index.md +1 -1
  203. package/agent-assets/templates/rules/journal-format.md +145 -38
  204. package/agent-assets/templates/user/expertise.md +4 -2
  205. package/agent-assets/templates/user/goals.md +4 -2
  206. package/agent-assets/templates/user/people.md +8 -2
  207. package/agent-assets/templates/user/personal.md +4 -2
  208. package/agent-assets/templates/user/work.md +4 -2
  209. package/bin/aitne.mjs +8 -1
  210. package/package.json +4 -4
  211. package/scripts/commands/doctor.mjs +52 -0
  212. package/scripts/commands/run-now.mjs +202 -0
  213. package/scripts/commands/verify.mjs +264 -0
  214. package/agent-assets/docs/features/routines/monthly-review.md +0 -65
  215. package/agent-assets/skills/management-task-modify/SKILL.md +0 -203
  216. package/agent-assets/skills/management-task-register/SKILL.md +0 -330
  217. package/agent-assets/skills/management-task-stop/SKILL.md +0 -166
  218. package/agent-assets/skills/receipts/SKILL.md +0 -134
  219. package/agent-assets/skills/travel/SKILL.md +0 -132
  220. package/agent-assets/skills/travel-time/SKILL.md +0 -158
  221. package/agent-assets/task-flows/routine.morning_routine.md +0 -322
  222. package/agent-assets/task-flows/routine.morning_routine_initial.md +0 -204
@@ -0,0 +1,391 @@
1
+ ---
2
+ # dm-intent.project — Project DM-intent detection decision tree.
3
+ # Included from: message.received.dm.md (Step 4), message.received.dm_first.md
4
+ # (Step 4). The `context` skill is the WRITER (PUT / PATCH / archive); this
5
+ # partial carries the decision tree that the DM dispatcher applies before
6
+ # the writer runs.
7
+ ---
8
+
9
+ Referenced from `message.received.dm`, `message.received.dm_first`, and
10
+ any other handler that wants to record project-state changes from a DM.
11
+ Identifies user messages about a named, ongoing workstream so the
12
+ handler can route them into `projects/<slug>.md` instead of letting the
13
+ fact bleed into the model's own scratch memory.
14
+
15
+ This block mirrors the shape of the "Long-horizon DM-intent detection"
16
+ partial so future durable-state domains (e.g. git, personal plans)
17
+ can copy the same pattern into their own skills.
18
+
19
+ **Signals (positive):**
20
+ - Named project + state verb: *"Project Alpha is now in review"*,
21
+ *"the migration project hit its first milestone"*, *"blocked on Y
22
+ for the reporting overhaul"*.
23
+ - New-project shape: *"let's start a project for X"*, *"I'm kicking
24
+ off …"*, *"track this as a project"*, *"add a new project"*. The
25
+ same shape in any other language counts — match on intent, not
26
+ surface vocabulary.
27
+ - Status / progress / blocker / decision phrasing tied to a
28
+ recognizable workstream the user has named before.
29
+
30
+ **Not signals — route elsewhere:**
31
+ - One-off task with a single deadline (*"remind me at 3pm to call …"*)
32
+ → `schedule` skill.
33
+ - Long-horizon plan with a date or horizon but no named workstream
34
+ (*"going to Tokyo next month"*) → `roadmap` skill ("Long-horizon
35
+ DM-intent detection"). When BOTH apply (a project with a dated
36
+ milestone), run both flows — see "Tie-breakers" below.
37
+ - Pure user fact / preference (*"I work on the platform team"*) →
38
+ `user-profile` skill.
39
+ - Durable management rule with a recurring cadence (*"every morning,
40
+ X"*) → `management-policy` skill.
41
+
42
+ **Decision tree:**
43
+
44
+ 1. **Existing or new? (Plus decline-marker pre-check.)**
45
+ - `GET /api/context/list/projects` and match the user's wording
46
+ against returned filenames (slug stem) and, if needed, against
47
+ the H1 / title of each candidate via
48
+ `GET /api/context/projects/<slug>`. If multiple candidates
49
+ match, prefer the one with the closest slug stem; if still
50
+ ambiguous, ask *"is this for `<slug-A>` or `<slug-B>`?"* before
51
+ writing.
52
+ - **Decline-marker pre-check (Goal 3 — never ask twice).** Before
53
+ classifying a no-match as new, compute the candidate slug and
54
+ read `agent/journal.md ## Declined Intents`:
55
+ ```bash
56
+ curl -s "http://localhost:8321/api/context/agent/journal" \
57
+ | jq -r '.content // ""' \
58
+ | awk '/^## Declined Intents/{f=1;next} f && /^## /{exit} f'
59
+ ```
60
+ (404 from the GET means the journal file does not yet exist →
61
+ no marker can exist → treat as "no marker present" and continue
62
+ to the no-match path.) If any line under that section contains
63
+ `create_project:<slug>` (the dedup_key shape — see §"Reply
64
+ branches" below), the user previously declined this exact
65
+ intent. The default behaviour is **skip silently** — do NOT
66
+ re-ask, do NOT schedule a confirm, do NOT write.
67
+
68
+ **Reversal-signal carve-out (rare).** If the user's current DM
69
+ contains an unambiguous reversal phrase for this exact intent,
70
+ treat the DM as a fresh affirmative. Run the
71
+ §"Decline-marker reversal" recipe below (remove the matching
72
+ marker line, then PUT the project file per Step 4). Pattern
73
+ shape: explicit verb of resumption + the topic + action
74
+ consent. Match on intent, not surface vocabulary — any
75
+ language qualifies.
76
+
77
+ Counts as reversal (do the recipe):
78
+ - *"actually let's start that LA project after all"*
79
+ - *"go ahead and track LA PM"*
80
+ - *"changed my mind, let's do it"*
81
+
82
+ Does NOT count as reversal (skip silently, marker stays):
83
+ - bare re-mention without an action verb: *"still thinking
84
+ about LA PM"*, *"made a bit of progress on LA"*
85
+ - status update on the topic without consent: *"LA classes
86
+ started"*, *"midterm was hard"*
87
+ - ambiguous "maybe" / non-commitment: *"maybe I'll track it
88
+ eventually"*, *"might revisit this later"*
89
+
90
+ The examples are English for prompt clarity only; recognise the
91
+ same shapes in any language the user writes in. Bias
92
+ conservative — when in doubt, skip silently and wait for a
93
+ clearer signal. A missed reversal costs nothing (user can
94
+ re-state explicitly); a false reversal silently overwrites a
95
+ deliberate "no".
96
+
97
+ 2. **Existing match → append a dated bullet.** Default section is
98
+ `## Log` (or whatever section the file already uses for time-ordered
99
+ entries — match existing files' conventions; do not invent a parallel
100
+ section if one already exists).
101
+
102
+ ```bash
103
+ curl -s -X PATCH http://localhost:8321/api/context/projects/<slug> \
104
+ -H 'Content-Type: application/json' \
105
+ -d '{"section": "log", "mode": "append", "content": "- 2026-04-30: <one-line summary>"}'
106
+ ```
107
+
108
+ If the PATCH responds with `{"error": "section_not_found"}` (the
109
+ file pre-dates the convention), retry once with `mode:
110
+ "append_to_file"` and include the section header in the content:
111
+
112
+ ```bash
113
+ curl -s -X PATCH http://localhost:8321/api/context/projects/<slug> \
114
+ -H 'Content-Type: application/json' \
115
+ -d '{"mode": "append_to_file", "content": "\n## Log\n- 2026-04-30: <one-line summary>"}'
116
+ ```
117
+
118
+ For a **state change** (e.g. `active → on-hold`, milestone reached),
119
+ also rebuild the frontmatter `state` field via GET-merge-PUT — the
120
+ frontmatter parser is line-scalar so per-key PATCH is not available.
121
+ Bump `updated` to today on the same write.
122
+
123
+ **Cross-path cancellation.** If this existing-match write resolves
124
+ what was previously a "no match" → confirm path (i.e. a confirm row
125
+ was scheduled and the user has since clarified the project is the
126
+ same as an existing one), delete the pending confirm rows before
127
+ completing the write. See §"Reply branches" below.
128
+
129
+ 3. **No match — schedule a confirm DM (do NOT ask inline).**
130
+ The universal "no topic-pivoting trailing question" rule in
131
+ `message.received.dm{,_first}.md` Step 3 forbids appending a
132
+ project-creation ask to a content-rich reply. Instead of inline
133
+ "Create project `<slug>`? (yes/no)" and waiting on the same DM
134
+ turn, schedule a `confirm:` sub-flow row for a natural follow-up
135
+ moment (default: next morning briefing slot, or `<current_time>
136
+ + 4h` if the DM landed well before quiet hours):
137
+
138
+ **a. Pre-flight idempotency check.** Compute the dedup_key
139
+ `create_project:<slug>` and ensure no row is already pending
140
+ (Goal 3):
141
+
142
+ ```bash
143
+ curl -s "http://localhost:8321/api/schedule?status=pending,running" \
144
+ | jq --arg k "create_project:<slug>" \
145
+ '[.items[] | select(.taskContext.confirm_dedup_key == $k)] | length'
146
+ ```
147
+
148
+ If the count is `≥ 1`, a confirm is already queued — do NOT
149
+ schedule a duplicate. Log to today.md `## Agent Log` and proceed:
150
+ ```
151
+ - HH:MM [confirm] skipped create_project:<slug>: row already pending
152
+ ```
153
+
154
+ **b. Schedule the confirm.** Use the shape documented in
155
+ `scheduled.dm.md` §"Confirmation follow-up":
156
+
157
+ ```bash
158
+ curl -s -X POST http://localhost:8321/api/schedule \
159
+ -H 'Content-Type: application/json' \
160
+ -d @- <<JSON
161
+ {
162
+ "time": "<next morning-briefing slot, or current_time + 4h — ISO 8601 with offset>",
163
+ "taskType": "dm_session",
164
+ "description": "confirm:create_project:<slug> — track <paraphrase> as a project?",
165
+ "tier": "medium",
166
+ "taskContext": {
167
+ "scheduledBy": "dm_handler.project_creation_gate",
168
+ "sub_flow": "confirm",
169
+ "confirm_id": "<short uuid v4 first 8 chars>",
170
+ "confirm_dedup_key": "create_project:<slug>",
171
+ "confirm_hint": "create project \"<slug>\"? (origin: <one-line paraphrase of user's DM>)",
172
+ "confirm_recent_window_hours": 24,
173
+ "confirm_attempt": 1,
174
+ "confirm_max_attempts": 2,
175
+ "confirm_defer_count": 0,
176
+ "confirm_max_defers": 3,
177
+ "confirm_decline_marker": {
178
+ "path": "agent/journal.md",
179
+ "section": "declined_intents",
180
+ "match": "create_project:<slug>"
181
+ },
182
+ "confirm_slot": {
183
+ "path": "projects/<slug>.md"
184
+ },
185
+ "importance": "low"
186
+ }
187
+ }
188
+ JSON
189
+ ```
190
+
191
+ The `confirm_slot.path` (just the file path, no section / anchor)
192
+ makes the fire-time slot-filled probe abort if the project file
193
+ already exists by the time the confirm fires — covering the case
194
+ where the user volunteers an affirmative shape between scheduling
195
+ and fire.
196
+
197
+ **c. Do NOT inline-ask.** The DM reply must remain in the user's
198
+ thread per the universal rule. The confirm sub-flow will surface
199
+ the question at the next natural moment. Silently inferring a
200
+ slug and writing without confirmation remains forbidden.
201
+
202
+ 4. **On confirmed creation → PUT the file.** Required + conventional
203
+ frontmatter and an H1:
204
+
205
+ ```bash
206
+ curl -s -X PUT http://localhost:8321/api/context/projects/<slug> \
207
+ -H 'Content-Type: application/json' \
208
+ -d @- <<'JSON'
209
+ {"content":"---\ntype: project\nslug: <slug>\nstate: active\nowner: shared\nstart: 2026-04-30\nupdated: 2026-04-30\n---\n# <Title>\n\n## Log\n- 2026-04-30: created via DM — <one-line origin>\n"}
210
+ JSON
211
+ ```
212
+
213
+ Add `due`, `stakeholders`, `next_milestone`, `tags` only when the
214
+ user supplied them. Do not invent values.
215
+
216
+ 5. **Reply branches — how the user's response to a scheduled confirm
217
+ is handled.** When the confirm sub-flow fires (`scheduled.dm.md`
218
+ ## Confirmation follow-up) and the user replies, the reply routes
219
+ through `message.received.dm.md` as usual; the dispatcher injects
220
+ `<conversation_history>` so the agent sees both the confirm DM and
221
+ the user's reply in the same turn. This gate is the writer for all
222
+ three branches; every branch is REQUIRED.
223
+
224
+ - **Affirmative** — user gave a clear positive answer to the
225
+ confirm question. Examples: *"yes"*, *"go ahead"*,
226
+ *"track it as X"*, *"sounds good"*, *"please do"*. Match on
227
+ intent, not surface vocabulary — recognise the same shape in
228
+ any language the user writes in. Execute the "On confirmed
229
+ creation → PUT the file" path in Step 4. Then run
230
+ §"Cross-path cancellation" below (delete any sibling confirm
231
+ rows with matching `confirm_dedup_key` so a parallel queued
232
+ row does not re-fire).
233
+
234
+ - **Counter-proposal** — user supplied new info ("call it
235
+ `la-pm` instead", "actually make it the syllabus dossier",
236
+ "rename it to `la-pm`").
237
+ Use the user's wording, not your original paraphrase:
238
+ re-compute the slug from the corrected wording, PUT the file,
239
+ then cancel pending confirm rows whose `confirm_dedup_key`
240
+ matches **either** the original or the new slug:
241
+ - `create_project:<original-slug>` — the chained-fire successor
242
+ inherits the original key, so this is the primary sweep.
243
+ - `create_project:<new-slug>` — a separate gate fire from an
244
+ earlier DM may have queued a confirm with the new slug (e.g.
245
+ the user previously paraphrased the same project differently
246
+ and that fire's confirm has not yet aborted). The fire-time
247
+ `slot-filled` probe would catch this (the project file now
248
+ exists), but a symmetric sweep here saves a wasted session.
249
+
250
+ Run the §"Cross-path cancellation" loop twice — once with each
251
+ key — or merge the two `select`s in jq.
252
+
253
+ - **Decline** — user wrote a clear negative answer to the
254
+ confirm question.
255
+
256
+ Counts as decline: *"no"*, *"don't bother"*, *"not now"*,
257
+ *"later"*, *"skip it"*, *"forget it"*, *"drop it"*.
258
+
259
+ Does NOT count as decline (treat as ambiguous → no DM action
260
+ this turn, marker NOT written, sweep NOT run; the chain's
261
+ softened re-check will handle it):
262
+ - non-answer continuations: *"hmm"*, *"not sure"*, *"I don't
263
+ know"*
264
+ - questions back to the agent: *"why are you asking?"*,
265
+ *"what would that involve?"* — these are clarification
266
+ requests, not declines
267
+
268
+ The examples are English for prompt clarity only; recognise
269
+ the same shapes in any language the user writes in. On a true
270
+ decline, do NOT write the project file. Two mandatory writes:
271
+
272
+ a. **Write the decline marker** to
273
+ `agent/journal.md ## Declined Intents`. Three cases — file
274
+ missing entirely, file present but section missing, file +
275
+ section both present — are handled in one read-then-branch
276
+ sequence:
277
+
278
+ ```bash
279
+ # 1. GET. HTTP 404 means the journal file does not yet exist.
280
+ body=$(curl -sS -w '\n%{http_code}' "http://localhost:8321/api/context/agent/journal")
281
+ status=$(printf '%s\n' "$body" | tail -n1)
282
+ content=$(printf '%s\n' "$body" | sed '$d' | jq -r '.content // ""' 2>/dev/null)
283
+
284
+ marker_line='- 2026-05-12 [create_project:<slug>] user declined inline (DM)'
285
+
286
+ if [ "$status" = "404" ]; then
287
+ # Case A — file missing. CREATE_ONLY_PUT is enabled for
288
+ # agent/journal, so PUT creates the file in a single call.
289
+ # Include both the H1 and the Declined Intents section.
290
+ curl -s -X PUT "http://localhost:8321/api/context/agent/journal" \
291
+ -H 'Content-Type: application/json' \
292
+ -d "$(jq -n --arg m "$marker_line" '{content: "# Agent Journal\n\n## Declined Intents\n\($m)\n"}')"
293
+ elif printf '%s' "$content" | grep -q '^## Declined Intents'; then
294
+ # Case B — file + section present. Append a bullet to the
295
+ # existing section.
296
+ curl -s -X PATCH "http://localhost:8321/api/context/agent/journal" \
297
+ -H 'Content-Type: application/json' \
298
+ -d "$(jq -n --arg m "$marker_line" '{section:"declined_intents",mode:"append",content:$m}')"
299
+ else
300
+ # Case C — file present but section missing. append_to_file
301
+ # adds the section header + bullet to the end of the file.
302
+ curl -s -X PATCH "http://localhost:8321/api/context/agent/journal" \
303
+ -H 'Content-Type: application/json' \
304
+ -d "$(jq -n --arg m "$marker_line" '{mode:"append_to_file",content:"\n## Declined Intents\n\($m)\n"}')"
305
+ fi
306
+ ```
307
+
308
+ Use today's date (resolve via `<current_time>`) for the
309
+ marker line.
310
+
311
+ b. **Cancellation** — see §"Cross-path cancellation" below.
312
+ The decline must also delete any pending confirm rows with
313
+ the matching `confirm_dedup_key`, otherwise the chained-fire
314
+ successor will re-ask 24h later despite the explicit "no".
315
+
316
+ The decline marker is what the next DM-intent detection (Step 1
317
+ above) consults — without it, the next time the user mentions LA
318
+ PM master's, this gate would compute the same slug, see no
319
+ existing project, and schedule another confirm. The marker is
320
+ how Goal 3 ("never ask the same question twice") survives across
321
+ sessions.
322
+
323
+ **Decline-marker reversal.** When the user later volunteers an
324
+ unambiguously affirmative shape ("OK now let's start that LA
325
+ project after all", "actually go ahead and track LA PM") — either
326
+ inline in a fresh DM (the carve-out in Step 1) or as a reply to a
327
+ confirm DM — run this recipe instead of skipping:
328
+
329
+ 1. GET `agent/journal.md`, parse the `## Declined Intents`
330
+ section, drop the line whose bracketed dedup_key matches
331
+ `create_project:<slug>`, and PATCH the section with
332
+ `mode: "replace"` carrying the rebuilt body (the other lines
333
+ preserved byte-for-byte). If the rebuilt section is empty,
334
+ replace with the empty string — `mode: "replace"` accepts an
335
+ empty `content` and leaves the heading in place.
336
+ 2. Proceed with Step 4 (PUT the project file).
337
+
338
+ Without the reversal, a previously-declined project would stay
339
+ dormant forever.
340
+
341
+ **Cross-path cancellation (required for affirmative,
342
+ counter-proposal, and decline branches).** When this gate
343
+ commits durable state via ANY of the three branches above, sweep
344
+ pending confirm rows with the same dedup_key so a queued
345
+ successor does not re-fire:
346
+
347
+ ```bash
348
+ curl -s "http://localhost:8321/api/schedule?status=pending,running" \
349
+ | jq -r --arg k "create_project:<slug>" \
350
+ '.items[] | select(.taskContext.confirm_dedup_key == $k) | .id' \
351
+ | while read -r id; do
352
+ curl -s -X DELETE "http://localhost:8321/api/schedule/$id" >/dev/null
353
+ done
354
+ ```
355
+
356
+ Apply this after the affirmative write, after the counter-proposal
357
+ write (with the ORIGINAL slug, not the corrected one), and after
358
+ the decline marker write. The cost is one GET plus zero-to-one
359
+ DELETE per write — bounded by "gate wrote something", which is
360
+ rare relative to inbound DM volume.
361
+
362
+ **Slug grammar (convention only — no API-level validation today):**
363
+ - match `^[a-z0-9][a-z0-9-]*[a-z0-9]$` (or a single `[a-z0-9]`)
364
+ - ≤ 64 chars
365
+ - equal to the filename stem
366
+ - avoid the reserved stems `_index`, `_active`
367
+
368
+ The context API does not currently reject malformed project slugs, so
369
+ the agent is the gate. A non-conforming slug will be written as-is and
370
+ later cause friction with the Obsidian Bases view (`_active.base`).
371
+
372
+ **Tie-breakers:**
373
+ - *Project AND long-horizon* — both can apply. A new project with a
374
+ dated milestone gets a `projects/<slug>.md` AND a roadmap entry.
375
+ Run both flows in the same turn; reuse the slug across them where
376
+ natural so the user can correlate the two.
377
+ - *Project AND user fact* — write the project state; do NOT also
378
+ write to `user/*.md` unless the message conveys a separate
379
+ identity / preference fact.
380
+ - *Project AND management policy* — if the user's wording is "from
381
+ now on, when X happens to project Y, do Z", that's a durable rule
382
+ → `management-policy` skill, not this section. The policy file's
383
+ `linked.dossier` may still point at a `projects/<slug>.md`.
384
+
385
+ **What this section does NOT cover:**
386
+ - Inbox-derived project creation — that path runs in
387
+ `routine.morning_routine.md` Step 4 against `inbox/*` source files
388
+ with a different file-move semantic; do not duplicate it here.
389
+ - Roll-off / archive — when a project ends, flip `state: archived`
390
+ via GET-merge-PUT; do not delete the file. The `_active.base`
391
+ Obsidian view filters by `state`.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: mail-acquire.gmail
3
3
  description: Acquire a Gmail message window per <acquisition-plan> row.
4
- spec: ROUTINE_DATA_ACQUISITION_DESIGN.md §6.8 / §8.1
4
+ spec: docs/design/appendices/routine-data-acquisition.md §6.8 / §8.1
5
5
  ---
6
6
 
7
7
  # Gmail acquisition
@@ -19,9 +19,16 @@ string `default` wherever the observation contract below references
19
19
  `<accountId>`. Never invent an accountId from the message body — `default`
20
20
  is the canonical placeholder.
21
21
 
22
- POST every returned message — for a whole window in **one** call — to
23
- `http://localhost:8321/api/observations/batch` (the response from the
24
- upstream call IS the payload; do not summarise or rank). Build the body as:
22
+ Submit every returned message — for a whole window in **one** call — via the
23
+ `mcp__aitne-observations__submit_observations` MCP tool when it is in your
24
+ allowed tools (preferred the structured MCP transport carries
25
+ Unicode-bearing subjects/snippets that would deterministically trip
26
+ `curl … -d '{…}'` on the SDK's bash preflight). Build the tool input as
27
+ `{"observations":[…]}` with one entry per message; the response from the
28
+ upstream call IS the payload (do not summarise or rank).
29
+
30
+ If the MCP tool is unavailable (non-Claude session backend), fall back to
31
+ `POST http://localhost:8321/api/observations/batch` with the same envelope:
25
32
 
26
33
  ```json
27
34
  {"observations":[
@@ -46,8 +53,8 @@ Field rules per element:
46
53
 
47
54
  Do NOT compute the dedup hash — the server derives it from `(source, payload)`.
48
55
 
49
- The batch endpoint always returns `200` with a JSON envelope `{ "results": [...],
50
- "fetched": N, "posted": N, "duplicates": N, "errors": N }`. Add each
56
+ The MCP tool and the batch endpoint return the same envelope: `{ "results":
57
+ [...], "fetched": N, "posted": N, "duplicates": N, "errors": N }`. Add each
51
58
  field-count into your top-level totals. Per-item `results[*].status` values:
52
59
 
53
60
  - `"created"` / `"modified"` — fresh or updated row; rolled into `posted`.
@@ -60,8 +67,8 @@ field-count into your top-level totals. Per-item `results[*].status` values:
60
67
  `{type:"validation-error","integration":"gmail","account":"<accountId>","ref":"<ref>","detail":"<results[*].error>"}`
61
68
  to `errors` and continue.
62
69
 
63
- Cap each batch at 200 entries — split the window into multiple POSTs if the
64
- upstream call returns more than that.
70
+ Cap each batch at 200 entries — split the window into multiple
71
+ `submit_observations` (or POST) calls if the upstream returns more than that.
65
72
 
66
73
  <!-- mode:direct:gmail -->
67
74
  GET `http://localhost:8321/api/mail/<accountId>/messages<query>` where
@@ -72,7 +79,8 @@ GET `http://localhost:8321/api/mail/<accountId>/messages<query>` where
72
79
  accepts `since` (ISO 8601 datetime), `limit`, `folder`, `q`,
73
80
  `unreadOnly` — `days=…` is NOT recognised. The daemon returns
74
81
  `{ "messages": [...] }`; map every message into the `observations[]`
75
- array of a single `POST /api/observations/batch` call.
82
+ array of a single `submit_observations` MCP tool call (or `POST
83
+ /api/observations/batch` fallback).
76
84
  <!-- /mode:direct:gmail -->
77
85
 
78
86
  <!-- mode:delegated-same:gmail -->
@@ -120,8 +128,9 @@ single user, so the task is account-implicit:
120
128
  }
121
129
  ```
122
130
 
123
- Map all items in `result.messages[]` into a single
124
- `POST /api/observations/batch` call.
131
+ Map all items in `result.messages[]` into a single `submit_observations`
132
+ MCP tool call (or `POST /api/observations/batch` fallback when the MCP
133
+ tool is not available).
125
134
  <!-- /mode:delegated-cross:gmail -->
126
135
 
127
136
  <!-- mode:native:gmail -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: mail-acquire.outlook_mail
3
3
  description: Acquire an Outlook Mail message window per <acquisition-plan> row.
4
- spec: ROUTINE_DATA_ACQUISITION_DESIGN.md §6.8 / §8.2
4
+ spec: docs/design/appendices/routine-data-acquisition.md §6.8 / §8.2
5
5
  ---
6
6
 
7
7
  # Outlook Mail acquisition
@@ -30,8 +30,14 @@ The four non-disabled branches therefore split into two real flows:
30
30
  this partial states the intent, not specific tool names. If no surface
31
31
  is bound, record an error and continue.
32
32
 
33
- POST every returned message — for a whole window in **one** call — to
34
- `http://localhost:8321/api/observations/batch`. Build the body as:
33
+ Submit every returned message — for a whole window in **one** call — via the
34
+ `mcp__aitne-observations__submit_observations` MCP tool when it is in your
35
+ allowed tools (preferred — bypasses the SDK bash preflight which denies
36
+ Unicode-whitespace-bearing bodies). Build the tool input as
37
+ `{"observations":[…]}` with one entry per message.
38
+
39
+ If the MCP tool is unavailable (non-Claude session backend), fall back to
40
+ `POST http://localhost:8321/api/observations/batch` with the same envelope:
35
41
 
36
42
  ```json
37
43
  {"observations":[
@@ -55,9 +61,10 @@ Field rules per element:
55
61
  "date": ... } }` (providerId is `"default"` when no
56
62
  `account` attribute)
57
63
 
58
- The server computes the dedup hash from `(source, payload)`. The batch
59
- endpoint always returns `200` with `{ "results": [...], "fetched": N,
60
- "posted": N, "duplicates": N, "errors": N }`. Per-item `results[*].status`:
64
+ The server computes the dedup hash from `(source, payload)`. The MCP tool
65
+ and the batch endpoint return the same envelope: `{ "results": [...],
66
+ "fetched": N, "posted": N, "duplicates": N, "errors": N }`. Per-item
67
+ `results[*].status`:
61
68
 
62
69
  - `"created"` / `"modified"` — rolled into `posted`.
63
70
  - `"duplicate"` — rolled into `duplicates`.
@@ -69,8 +76,8 @@ endpoint always returns `200` with `{ "results": [...], "fetched": N,
69
76
  `{type:"validation-error","integration":"outlook_mail","account":"<accountId>","ref":"<ref>","detail":"<results[*].error>"}`
70
77
  to `errors` and continue.
71
78
 
72
- Cap each batch at 200 entries — split the window into multiple POSTs if the
73
- upstream call returns more than that.
79
+ Cap each batch at 200 entries — split the window into multiple
80
+ `submit_observations` (or POST) calls if the upstream returns more than that.
74
81
 
75
82
  <!-- mode:direct:outlook_mail -->
76
83
  GET `http://localhost:8321/api/mail/<accountId>/messages<query>` where
@@ -80,7 +87,8 @@ GET `http://localhost:8321/api/mail/<accountId>/messages<query>` where
80
87
  accepts `since` (ISO 8601), `limit`, `folder`, `q`, `unreadOnly` — it
81
88
  does NOT accept `days=…`. The daemon returns `{ "messages": [...] }`
82
89
  regardless of the underlying provider; map every message into the
83
- `observations[]` array of a single `POST /api/observations/batch` call.
90
+ `observations[]` array of a single `submit_observations` MCP tool call
91
+ (or `POST /api/observations/batch` fallback).
84
92
  <!-- /mode:direct:outlook_mail -->
85
93
 
86
94
  <!-- mode:delegated-same:outlook_mail -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: notion-acquire.notion
3
3
  description: Acquire recently-updated Notion pages per <acquisition-plan> row.
4
- spec: ROUTINE_DATA_ACQUISITION_DESIGN.md §6.8 / §8.5
4
+ spec: docs/design/appendices/routine-data-acquisition.md §6.8 / §8.5
5
5
  ---
6
6
 
7
7
  # Notion acquisition
@@ -10,8 +10,13 @@ For every `<fetch integration="notion" ...>` row in `<acquisition-plan>`,
10
10
  take the branch below that matches the row's `mode` attribute. Notion rows
11
11
  do not fan out per account — the dispatcher emits one row per workspace.
12
12
 
13
- POST every returned page — for a whole window in **one** call — to
14
- `http://localhost:8321/api/observations/batch`. Build the body as:
13
+ Submit every returned page — for a whole window in **one** call — via the
14
+ `mcp__aitne-observations__submit_observations` MCP tool when it is in your
15
+ allowed tools (preferred — bypasses the SDK bash preflight). Build the
16
+ tool input as `{"observations":[…]}` with one entry per page.
17
+
18
+ If the MCP tool is unavailable (non-Claude session backend), fall back to
19
+ `POST http://localhost:8321/api/observations/batch` with the same envelope:
15
20
 
16
21
  ```json
17
22
  {"observations":[
@@ -33,9 +38,10 @@ Field rules per element:
33
38
  "raw": { "title": ..., "last_edited": ...,
34
39
  "parent": ..., "url": ... } }`
35
40
 
36
- The server computes the dedup hash from `(source, payload)`. The batch
37
- endpoint always returns `200` with `{ "results": [...], "fetched": N,
38
- "posted": N, "duplicates": N, "errors": N }`. Per-item `results[*].status`:
41
+ The server computes the dedup hash from `(source, payload)`. The MCP tool
42
+ and the batch endpoint return the same envelope: `{ "results": [...],
43
+ "fetched": N, "posted": N, "duplicates": N, "errors": N }`. Per-item
44
+ `results[*].status`:
39
45
 
40
46
  - `"created"` / `"modified"` — rolled into `posted`.
41
47
  - `"duplicate"` — rolled into `duplicates`.
@@ -44,8 +50,8 @@ endpoint always returns `200` with `{ "results": [...], "fetched": N,
44
50
  - `"validation_error"` — append `{type:"validation-error","integration":"notion","ref":"<ref>","detail":"<results[*].error>"}`
45
51
  to `errors` and continue.
46
52
 
47
- Cap each batch at 200 entries — split the window into multiple POSTs if the
48
- upstream call returns more than that.
53
+ Cap each batch at 200 entries — split the window into multiple
54
+ `submit_observations` (or POST) calls if the upstream returns more than that.
49
55
 
50
56
  <!-- mode:direct:notion -->
51
57
  GET `http://localhost:8321/api/notion/search<query>` where `<query>` is
@@ -58,8 +64,8 @@ sorted by `last_edited_time` descending; filter to entries whose
58
64
  `last_edited_time` is at or after the window the `<fetch>` row's
59
65
  window symbol implies (`updated_24h` → today's agent-day start,
60
66
  `updated_1h` → the current hour boundary), then map every surviving
61
- page into the `observations[]` array of a single
62
- `POST /api/observations/batch` call.
67
+ page into the `observations[]` array of a single `submit_observations`
68
+ MCP tool call (or `POST /api/observations/batch` fallback).
63
69
  <!-- /mode:direct:notion -->
64
70
 
65
71
  <!-- mode:delegated-same:notion -->
@@ -104,8 +110,8 @@ body (substitute the row's `query` into `task`):
104
110
  }
105
111
  ```
106
112
 
107
- Map all items in `result.pages[]` into a single
108
- `POST /api/observations/batch` call.
113
+ Map all items in `result.pages[]` into a single `submit_observations`
114
+ MCP tool call (or `POST /api/observations/batch` fallback).
109
115
  <!-- /mode:delegated-cross:notion -->
110
116
 
111
117
  <!-- mode:native:notion -->
@@ -112,7 +112,7 @@ curl -s -X PATCH http://localhost:8321/api/context/user/<topic> \
112
112
  -H 'Content-Type: application/json' \
113
113
  -d "$(jq -nc --arg s 'family' \
114
114
  --arg c '- Sister (Sarah): two kids as of 2026-04
115
- - Yoko (1955–)' \
115
+ - Mother Yoko (1955–)' \
116
116
  '{section:$s, mode:"append", content:$c}')"
117
117
  ```
118
118