@aitne-sh/aitne 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +464 -0
  3. package/agent-assets/agent-profiles/_safety.md +26 -0
  4. package/agent-assets/agent-profiles/conversational.md +33 -0
  5. package/agent-assets/agent-profiles/docs-qa.md +24 -0
  6. package/agent-assets/agent-profiles/observer.md +28 -0
  7. package/agent-assets/agent-profiles/profile-importer.md +63 -0
  8. package/agent-assets/agent-profiles/proxy.md +28 -0
  9. package/agent-assets/agent-profiles/routine.md +16 -0
  10. package/agent-assets/agent-profiles/task.md +18 -0
  11. package/agent-assets/docs/concepts/agent-day.md +88 -0
  12. package/agent-assets/docs/concepts/auth-health.md +75 -0
  13. package/agent-assets/docs/concepts/backends-and-tiers.md +126 -0
  14. package/agent-assets/docs/concepts/costs-and-quotas.md +103 -0
  15. package/agent-assets/docs/concepts/delegated-mode.md +223 -0
  16. package/agent-assets/docs/concepts/memory-model.md +118 -0
  17. package/agent-assets/docs/concepts/observations.md +80 -0
  18. package/agent-assets/docs/concepts/process-keys.md +89 -0
  19. package/agent-assets/docs/concepts/routines.md +108 -0
  20. package/agent-assets/docs/concepts/safety-and-execution.md +109 -0
  21. package/agent-assets/docs/concepts/safety-model.md +279 -0
  22. package/agent-assets/docs/concepts/skills.md +100 -0
  23. package/agent-assets/docs/features/integrations/calendar.md +92 -0
  24. package/agent-assets/docs/features/integrations/git.md +95 -0
  25. package/agent-assets/docs/features/integrations/github.md +170 -0
  26. package/agent-assets/docs/features/integrations/mail.md +106 -0
  27. package/agent-assets/docs/features/integrations/notion.md +69 -0
  28. package/agent-assets/docs/features/integrations/obsidian.md +71 -0
  29. package/agent-assets/docs/features/lifestyle/git.md +178 -0
  30. package/agent-assets/docs/features/lifestyle/reading.md +93 -0
  31. package/agent-assets/docs/features/lifestyle/receipts.md +71 -0
  32. package/agent-assets/docs/features/lifestyle/travel-bookings.md +44 -0
  33. package/agent-assets/docs/features/lifestyle/travel-time.md +52 -0
  34. package/agent-assets/docs/features/memory-files/agent-journal.md +105 -0
  35. package/agent-assets/docs/features/memory-files/projects.md +56 -0
  36. package/agent-assets/docs/features/memory-files/roadmap.md +61 -0
  37. package/agent-assets/docs/features/memory-files/schedule.md +112 -0
  38. package/agent-assets/docs/features/memory-files/today.md +73 -0
  39. package/agent-assets/docs/features/memory-files/user-profile.md +81 -0
  40. package/agent-assets/docs/features/messaging/dashboard-chat.md +93 -0
  41. package/agent-assets/docs/features/messaging/discord.md +50 -0
  42. package/agent-assets/docs/features/messaging/overview.md +111 -0
  43. package/agent-assets/docs/features/messaging/pairing-and-magic-phrase.md +69 -0
  44. package/agent-assets/docs/features/messaging/slack.md +51 -0
  45. package/agent-assets/docs/features/messaging/telegram.md +63 -0
  46. package/agent-assets/docs/features/messaging/whatsapp.md +48 -0
  47. package/agent-assets/docs/features/operations/activity-and-conversations.md +105 -0
  48. package/agent-assets/docs/features/operations/approvals.md +58 -0
  49. package/agent-assets/docs/features/operations/backend-routing.md +62 -0
  50. package/agent-assets/docs/features/operations/cost-tracking.md +59 -0
  51. package/agent-assets/docs/features/operations/notifications.md +69 -0
  52. package/agent-assets/docs/features/operations/quiet-hours.md +106 -0
  53. package/agent-assets/docs/features/operations/schedule-approaching.md +60 -0
  54. package/agent-assets/docs/features/routines/custom-routines.md +101 -0
  55. package/agent-assets/docs/features/routines/evening-review.md +81 -0
  56. package/agent-assets/docs/features/routines/hourly-check.md +85 -0
  57. package/agent-assets/docs/features/routines/monthly-review.md +65 -0
  58. package/agent-assets/docs/features/routines/morning-routine.md +123 -0
  59. package/agent-assets/docs/features/routines/weekly-review.md +70 -0
  60. package/agent-assets/docs/getting-started/01-what-is-this.md +192 -0
  61. package/agent-assets/docs/getting-started/02-first-steps.md +80 -0
  62. package/agent-assets/docs/getting-started/03-what-can-this-do.md +110 -0
  63. package/agent-assets/docs/getting-started/04-first-day.md +287 -0
  64. package/agent-assets/docs/glossary.md +116 -0
  65. package/agent-assets/docs/guides/add-a-custom-routine.md +71 -0
  66. package/agent-assets/docs/guides/backup-and-restore.md +54 -0
  67. package/agent-assets/docs/guides/change-which-model-handles-x.md +47 -0
  68. package/agent-assets/docs/guides/connect-a-new-mail-account.md +59 -0
  69. package/agent-assets/docs/guides/import-knowledge-file.md +275 -0
  70. package/agent-assets/docs/guides/install-and-run.md +72 -0
  71. package/agent-assets/docs/guides/migrate-machines.md +52 -0
  72. package/agent-assets/docs/guides/pause-the-agent.md +65 -0
  73. package/agent-assets/docs/guides/reinstall-cleanly.md +52 -0
  74. package/agent-assets/docs/guides/setup-wizard.md +107 -0
  75. package/agent-assets/docs/guides/switch-default-backend.md +60 -0
  76. package/agent-assets/docs/reference/api.md +51 -0
  77. package/agent-assets/docs/reference/cli-commands.md +121 -0
  78. package/agent-assets/docs/reference/config.md +74 -0
  79. package/agent-assets/docs/reference/disallowed-tools.md +76 -0
  80. package/agent-assets/docs/reference/keyboard-shortcuts.md +39 -0
  81. package/agent-assets/docs/reference/process-keys.md +59 -0
  82. package/agent-assets/docs/reference/skills.md +50 -0
  83. package/agent-assets/docs/troubleshooting/auth-failed.md +57 -0
  84. package/agent-assets/docs/troubleshooting/dashboard-shows-degraded.md +55 -0
  85. package/agent-assets/docs/troubleshooting/fallback-keeps-firing.md +54 -0
  86. package/agent-assets/docs/troubleshooting/messaging-not-pairing.md +53 -0
  87. package/agent-assets/docs/troubleshooting/morning-routine-didnt-run.md +75 -0
  88. package/agent-assets/docs/troubleshooting/observation-not-detected.md +57 -0
  89. package/agent-assets/docs/troubleshooting/quota-exhausted.md +57 -0
  90. package/agent-assets/optimizer-skills/drift-analysis/SKILL.md +75 -0
  91. package/agent-assets/optimizer-skills/knowledge-map/SKILL.md +71 -0
  92. package/agent-assets/optimizer-skills/skill-curation/SKILL.md +108 -0
  93. package/agent-assets/project-doc-templates/git-repo.md +21 -0
  94. package/agent-assets/project-doc-templates/project.md +38 -0
  95. package/agent-assets/skills/attach/SKILL.md +104 -0
  96. package/agent-assets/skills/context/SKILL.md +257 -0
  97. package/agent-assets/skills/context/curation.json +37 -0
  98. package/agent-assets/skills/context/seeds/file-responsibilities.seed.json +13 -0
  99. package/agent-assets/skills/context/seeds/frontmatter-requirements.seed.json +40 -0
  100. package/agent-assets/skills/docs-search/SKILL.md +176 -0
  101. package/agent-assets/skills/external-services/SKILL.delegated.claude.md +369 -0
  102. package/agent-assets/skills/external-services/SKILL.delegated.codex.md +349 -0
  103. package/agent-assets/skills/external-services/SKILL.delegated.gemini.md +347 -0
  104. package/agent-assets/skills/external-services/SKILL.md +371 -0
  105. package/agent-assets/skills/mail/SKILL.delegated.claude.md +284 -0
  106. package/agent-assets/skills/mail/SKILL.delegated.codex.md +261 -0
  107. package/agent-assets/skills/mail/SKILL.delegated.gemini.md +255 -0
  108. package/agent-assets/skills/mail/SKILL.md +313 -0
  109. package/agent-assets/skills/mail/references/errors.md +17 -0
  110. package/agent-assets/skills/mail/references/providers.md +40 -0
  111. package/agent-assets/skills/mail/references/query-grammar.md +24 -0
  112. package/agent-assets/skills/management-policy/SKILL.md +307 -0
  113. package/agent-assets/skills/management-policy/curation.json +13 -0
  114. package/agent-assets/skills/management-policy/seeds/policy-file-shape.seed.json +16 -0
  115. package/agent-assets/skills/management-task-modify/SKILL.md +202 -0
  116. package/agent-assets/skills/management-task-register/SKILL.md +330 -0
  117. package/agent-assets/skills/management-task-stop/SKILL.md +166 -0
  118. package/agent-assets/skills/notify/SKILL.md +196 -0
  119. package/agent-assets/skills/notion/SKILL.delegated.claude.md +254 -0
  120. package/agent-assets/skills/notion/SKILL.delegated.codex.md +195 -0
  121. package/agent-assets/skills/notion/SKILL.delegated.gemini.md +194 -0
  122. package/agent-assets/skills/notion/SKILL.md +86 -0
  123. package/agent-assets/skills/observations/SKILL.md +234 -0
  124. package/agent-assets/skills/observations/curation.json +13 -0
  125. package/agent-assets/skills/observations/seeds/source-namespacing.seed.json +20 -0
  126. package/agent-assets/skills/project-doc/SKILL.md +86 -0
  127. package/agent-assets/skills/project-doc/curation.json +21 -0
  128. package/agent-assets/skills/project-doc/seeds/project-shape.seed.json +25 -0
  129. package/agent-assets/skills/project-doc/seeds/slug-grammar.seed.json +20 -0
  130. package/agent-assets/skills/reading/SKILL.md +198 -0
  131. package/agent-assets/skills/reading/references/reading-taste.md +197 -0
  132. package/agent-assets/skills/receipts/SKILL.md +134 -0
  133. package/agent-assets/skills/roadmap/SKILL.md +276 -0
  134. package/agent-assets/skills/roadmap/curation.json +13 -0
  135. package/agent-assets/skills/roadmap/references/horizon-tags.md +40 -0
  136. package/agent-assets/skills/roadmap/references/preparation-timeline.md +47 -0
  137. package/agent-assets/skills/roadmap/seeds/entry-types.seed.json +16 -0
  138. package/agent-assets/skills/schedule/SKILL.md +228 -0
  139. package/agent-assets/skills/scheduled-managed-task/SKILL.md +392 -0
  140. package/agent-assets/skills/today/SKILL.md +198 -0
  141. package/agent-assets/skills/today/curation.json +21 -0
  142. package/agent-assets/skills/today/seeds/agent-notes-flavors.seed.json +17 -0
  143. package/agent-assets/skills/today/seeds/section-shape.seed.json +17 -0
  144. package/agent-assets/skills/travel/SKILL.md +132 -0
  145. package/agent-assets/skills/travel-time/SKILL.md +149 -0
  146. package/agent-assets/skills/user-interview/SKILL.md +323 -0
  147. package/agent-assets/skills/user-interview/references/sweep-and-fallback.md +94 -0
  148. package/agent-assets/skills/user-profile/SKILL.md +210 -0
  149. package/agent-assets/skills/user-profile/curation.json +29 -0
  150. package/agent-assets/skills/user-profile/seeds/learned-context-format.seed.json +14 -0
  151. package/agent-assets/skills/user-profile/seeds/routing-table.seed.json +53 -0
  152. package/agent-assets/skills/user-profile/seeds/topic-files.seed.json +27 -0
  153. package/agent-assets/task-flows/dashboard.docs_qa.md +43 -0
  154. package/agent-assets/task-flows/default.md +11 -0
  155. package/agent-assets/task-flows/git.branch.created.md +25 -0
  156. package/agent-assets/task-flows/git.lifecycle.poll.md +52 -0
  157. package/agent-assets/task-flows/git.local_ahead.stale.md +34 -0
  158. package/agent-assets/task-flows/git.merge_to_default.md +30 -0
  159. package/agent-assets/task-flows/git.project.refresh_architecture.md +100 -0
  160. package/agent-assets/task-flows/git.project.retemplate.md +73 -0
  161. package/agent-assets/task-flows/git.push.detected.md +32 -0
  162. package/agent-assets/task-flows/git.push.force_pushed.md +36 -0
  163. package/agent-assets/task-flows/git.tag.created.md +24 -0
  164. package/agent-assets/task-flows/github.assigned.md +43 -0
  165. package/agent-assets/task-flows/github.pull_request.review_requested.md +57 -0
  166. package/agent-assets/task-flows/github.security_alert.md +45 -0
  167. package/agent-assets/task-flows/github.workflow_run.failed.md +57 -0
  168. package/agent-assets/task-flows/knowledge.import.md +161 -0
  169. package/agent-assets/task-flows/message.received.dm.md +142 -0
  170. package/agent-assets/task-flows/message.received.dm_first.md +117 -0
  171. package/agent-assets/task-flows/message.received.md +14 -0
  172. package/agent-assets/task-flows/routine.custom.md +38 -0
  173. package/agent-assets/task-flows/routine.evening_review.md +323 -0
  174. package/agent-assets/task-flows/routine.hourly_check.delegated.claude.md +405 -0
  175. package/agent-assets/task-flows/routine.hourly_check.delegated.codex.md +400 -0
  176. package/agent-assets/task-flows/routine.hourly_check.delegated.gemini.md +404 -0
  177. package/agent-assets/task-flows/routine.hourly_check.md +184 -0
  178. package/agent-assets/task-flows/routine.hourly_check.triage.md +93 -0
  179. package/agent-assets/task-flows/routine.monthly_review.md +250 -0
  180. package/agent-assets/task-flows/routine.morning_routine.md +300 -0
  181. package/agent-assets/task-flows/routine.morning_routine_initial.md +184 -0
  182. package/agent-assets/task-flows/routine.roadmap_refresh.md +275 -0
  183. package/agent-assets/task-flows/routine.today_refresh.md +172 -0
  184. package/agent-assets/task-flows/routine.user_profile_sweep.md +242 -0
  185. package/agent-assets/task-flows/routine.weekly_review.md +247 -0
  186. package/agent-assets/task-flows/schedule.approaching.md +124 -0
  187. package/agent-assets/task-flows/scheduled.dm.md +391 -0
  188. package/agent-assets/task-flows/scheduled.task.md +141 -0
  189. package/agent-assets/task-flows/setup.initial.md +277 -0
  190. package/agent-assets/task-flows/setup.update.md +53 -0
  191. package/agent-assets/templates/README.md +85 -0
  192. package/agent-assets/templates/_index.md +39 -0
  193. package/agent-assets/templates/_manifest.json +103 -0
  194. package/agent-assets/templates/agent/journal.md +10 -0
  195. package/agent-assets/templates/agent/profile-questions.md +74 -0
  196. package/agent-assets/templates/context-index.md +42 -0
  197. package/agent-assets/templates/dossiers/_index.md +22 -0
  198. package/agent-assets/templates/dossiers/evening.md +23 -0
  199. package/agent-assets/templates/dossiers/hourly.md +23 -0
  200. package/agent-assets/templates/dossiers/monthly.md +23 -0
  201. package/agent-assets/templates/dossiers/morning.md +23 -0
  202. package/agent-assets/templates/dossiers/roadmap.md +23 -0
  203. package/agent-assets/templates/dossiers/weekly.md +23 -0
  204. package/agent-assets/templates/projects/_active.base +14 -0
  205. package/agent-assets/templates/projects/_index.md +29 -0
  206. package/agent-assets/templates/roadmap.md +15 -0
  207. package/agent-assets/templates/routines/_index.md +20 -0
  208. package/agent-assets/templates/routines/evening.md +22 -0
  209. package/agent-assets/templates/routines/hourly.md +30 -0
  210. package/agent-assets/templates/routines/monthly.md +25 -0
  211. package/agent-assets/templates/routines/morning.md +26 -0
  212. package/agent-assets/templates/routines/weekly.md +23 -0
  213. package/agent-assets/templates/rules/_index.md +19 -0
  214. package/agent-assets/templates/rules/journal-export.md +41 -0
  215. package/agent-assets/templates/rules/journal-format.md +61 -0
  216. package/agent-assets/templates/rules/management.md +48 -0
  217. package/agent-assets/templates/rules/mcp.md +40 -0
  218. package/agent-assets/templates/rules/policies/_index.md +22 -0
  219. package/agent-assets/templates/rules/redaction.md +30 -0
  220. package/agent-assets/templates/today.md +13 -0
  221. package/agent-assets/templates/user/_index.md +16 -0
  222. package/agent-assets/templates/user/expertise.md +7 -0
  223. package/agent-assets/templates/user/goals.md +7 -0
  224. package/agent-assets/templates/user/people.md +7 -0
  225. package/agent-assets/templates/user/personal.md +7 -0
  226. package/agent-assets/templates/user/profile.md +28 -0
  227. package/agent-assets/templates/user/work.md +7 -0
  228. package/bin/aitne.mjs +1096 -0
  229. package/package.json +78 -0
  230. package/personal-agent.mjs +39 -0
  231. package/scripts/browser.mjs +99 -0
  232. package/scripts/check-redaction-coverage.mjs +109 -0
  233. package/scripts/commands/audit.mjs +309 -0
  234. package/scripts/commands/doctor.mjs +437 -0
  235. package/scripts/commands/open.mjs +40 -0
  236. package/scripts/commands/setup.mjs +21 -0
  237. package/scripts/commands/uninstall.mjs +114 -0
  238. package/scripts/commands/update.mjs +96 -0
  239. package/scripts/commands/version.mjs +62 -0
  240. package/scripts/commands.md +0 -0
  241. package/scripts/lib/sqlite-loader.mjs +49 -0
  242. package/scripts/message-discipline-digest.mjs +535 -0
  243. package/scripts/poc/google-connector-inheritance/REPORT.md +197 -0
  244. package/scripts/poc/google-connector-inheritance/claude-sdk-probe.mjs +79 -0
  245. package/scripts/remint-roadmap-ids.mjs +257 -0
  246. package/scripts/rm-paths.mjs +22 -0
  247. package/scripts/run-node.mjs +223 -0
  248. package/scripts/smoke-obsidian-api.mjs +166 -0
  249. package/scripts/start.mjs +160 -0
@@ -0,0 +1,404 @@
1
+ {context}
2
+
3
+ ## Hourly Observation Review — Delegated Mode (Gemini DM, daemon proxy)
4
+
5
+ Variant of `routine.hourly_check` selected when any of Gmail /
6
+ Google Calendar / Notion is `delegated` while your DM runs on Gemini
7
+ CLI. The corresponding direct-mode signal paths (`MailPoller`
8
+ mail:lifecycle, `CalendarPoller` calendar:* + `schedule.approaching`
9
+ emits, `NotionPoller` notion:* observations) shut off under delegation;
10
+ Step 0 below restores coverage by fetching the same windows through
11
+ the daemon proxy. The rest of the decision workflow is identical to
12
+ the direct variant.
13
+
14
+ > **Connector mode (Gemini DM)**: `<integration_modes>` carries
15
+ > `<key>_delegated_to="<backend>"` for delegated keys. Same-backend
16
+ > (`=gemini`) → use `mcp_google-workspace_*` / `mcp_notion_*` natively;
17
+ > do NOT call `POST /api/integrations/<key>/exec` — the daemon returns
18
+ > `409 mode_mismatch` because session backend equals delegatedBackend.
19
+ > Cross-backend (`=claude` / `=codex`) → route through `POST
20
+ > /api/integrations/<key>/exec` with a natural-language `task` and a
21
+ > JSON `outputSchema`; do NOT fall back to your own `mcp_<server>_*`
22
+ > tools for that integration. The daemon enforces allowed-tools /
23
+ > destructive / deniedTools / cost / audit and validates the JSON.
24
+ > The `/api/notion/databases` label → UUID config dump is ungated in
25
+ > either mode — use it before any Notion read so your `task` prose
26
+ > carries a concrete data-source UUID.
27
+
28
+ The "Vault policy files" block appended to this prompt includes
29
+ `routines/hourly.md` — your canonical check list for this cadence.
30
+ The "Vault review context" block includes `context-index.md` and
31
+ `dossiers/hourly.md`; consult it before Step 1 and update the dossier's
32
+ Open items / Last run before finishing. Writes to `dossiers/<flow>.md`
33
+ MUST preserve the existing YAML frontmatter block (`---\ntype: dossier\nowner: agent\nupdated: <date>\n---`); prefer `PATCH` with a
34
+ section target to mutate a single block, and when doing a `PUT` full
35
+ rewrite keep the frontmatter and only refresh `updated:` — writes that
36
+ drop the frontmatter are rejected with 422.
37
+ Execute each `### <label>` entry in order, skipping any whose
38
+ precondition does not hold. The steps below are the built-in decision
39
+ framework for the observation-review step; additional checks the user
40
+ has added to the routine file run alongside them using the same
41
+ routing rules.
42
+
43
+ Fetch pending user-originated observations:
44
+ `GET /api/observations?pending=true&actor=user&limit=20`. Obsidian /
45
+ Git always feed that table; Notion only in direct mode.
46
+ Delegated signals arrive via Step 0 below.
47
+
48
+ ### Execution budget
49
+ Target: 5–10 turns. If you reach 15 turns, wrap up current work and log.
50
+ Do NOT read roadmap.md, projects/*.md, or user/*.md unless an
51
+ observation warrants project-state context (e.g. references a project,
52
+ milestone, or deliverable — not merely a file edit in a watched repo).
53
+
54
+ ### Default stance — silence + idempotence
55
+ Most hourly runs are silent bookkeeping: consume observations, update
56
+ today.md, log, done. The baseline assumption for every step below is
57
+ that the user does NOT want another notification and does NOT want a
58
+ new Agent Plan row unless this run has something genuinely new to add.
59
+ The morning routine is authoritative for the day's plan; your job is
60
+ to fold in new signals, not to re-plan. Two hard rules:
61
+
62
+ - **No duplicate Agent Plan rows / schedules.** Before appending to
63
+ `## Agent Plan` or calling `POST /api/schedule`, run the dedup
64
+ pre-check in Step 4. If a matching row/schedule already exists,
65
+ skip and log — never add a second one.
66
+ - **No duplicate notifications.** Before `POST /api/notify`, run the
67
+ dedup pre-check in Step 8. If the same item was already notified
68
+ earlier today, stay silent and log.
69
+
70
+ When in doubt, stay silent and log to `## Agent Log`.
71
+
72
+ ### External services are read-only this hour
73
+
74
+ This routine reads external state for context — it does not push back. While running this hourly check, do **not**:
75
+
76
+ - Create / update / archive Notion pages or change Notion schema.
77
+ - Send / draft / move / tag mail.
78
+ - Create / update / delete calendar events.
79
+ - Open / merge / comment on GitHub PRs or issues.
80
+
81
+ External-source signals (`mail:*`, `notion:*`, `calendar:*`, `git:*`) reach you through `<observations>` and the delegated-sync-worker rows pulled in Step 0 below. Consume them, route to `today.md` / `projects/*.md` / the `roadmap_candidate` queue per the Decision Framework, but do **not** act back on the source system through the connector or the daemon `/exec` proxy. Outbound writes against external services belong in the morning routine, evening review, or DM-reply paths — `routine.hourly_check` is a silent bookkeeping pass.
82
+
83
+ This rule applies regardless of integration mode (same-backend delegated, cross-backend delegated). It is owned by the routine, so a session whose `notion` / `mail` / `external-services` skill body was dropped under same-backend delegation (because the connector covers the surface) still inherits the constraint.
84
+
85
+ ### Decision Framework
86
+
87
+ #### Step 0 — Collect delegated-mode signals (replaces lost observations)
88
+
89
+ Run this step FIRST, before fetching `/api/observations`. The
90
+ delegated-sync-worker (0a / 0c) and the calendar reconcile route (0b)
91
+ write **real** `mail:lifecycle` / `notion:<db>` / `calendar:<id>`
92
+ observation rows server-side, so Step 1's `/api/observations` fetch
93
+ already includes them; Step 7's `/api/observations/consume` call still
94
+ applies — there are no "synthetic, row-less" Step 0 signals after
95
+ Phase 5. The only Step 0 output that does NOT flow through the
96
+ observations table is the imminent-meeting reminder (0b → EventBus
97
+ `schedule.approaching`), which the daemon DMs the user directly
98
+ without a DB row.
99
+
100
+ **Per-substep precondition.** Each `0[abc]` substep runs only when its
101
+ integration is `delegated`: 0a iff `gmail="delegated"`, 0b iff
102
+ `google_calendar="delegated"`, 0c iff `notion="delegated"`. In `direct`
103
+ / `disabled` mode the corresponding poller (MailPoller / CalendarPoller
104
+ / NotionPoller) is recording observations normally — Step 1's
105
+ `/api/observations` call sees them. Calendar additionally restores the
106
+ `schedule.approaching` emit path under direct mode.
107
+
108
+ **0a. Recent Gmail (delegated worker owns the drift snapshot).**
109
+ When `gmail="delegated"`, the daemon's `delegated-sync-worker` polls
110
+ Gmail on its own cadence (~30 min default, operator-tunable) and writes
111
+ coalesced `mail:lifecycle` observations server-side via the
112
+ `integration_snapshots` (`inbox:7d`) partition. Drafts the agent
113
+ created or labels the agent applied within the `integration_writes` TTL
114
+ window are pre-tagged `actor='agent'` so Step 1's
115
+ `GET /api/observations?actor=user` already excludes them. **Do NOT call
116
+ `/api/integrations/gmail/reconcile` from this step** — the route's
117
+ LLM-callable allowlist excludes the gmail partition for this reason: a
118
+ narrow `newer_than:1d` LLM fetch posted into the worker's 7d partition
119
+ would classify every prior 1-7d-old thread as `deleted` and trigger
120
+ ghost observations on the next worker tick.
121
+
122
+ If a downstream observation references a thread you want to read for
123
+ deeper context (e.g. an `mail:lifecycle` row in Step 1 looks
124
+ actionable), branch on `gmail_delegated_to`. Cross-backend reads are
125
+ dispatched through `/exec` with a natural-language intent:
126
+
127
+ ```bash
128
+ curl -sS -X POST http://localhost:8321/api/integrations/gmail/exec \
129
+ -H 'Content-Type: application/json' \
130
+ -d '{
131
+ "task": "Fetch every message in Gmail thread <THREAD_ID>. For each return from / subject / body / timestamp. Sort ascending by timestamp.",
132
+ "outputSchema": { "type": "object", "required": ["messages"], "properties": { "messages": { "type": "array", "items": { "type": "object", "required": ["from","subject","body","ts"] } } } },
133
+ "maxToolCalls": 7,
134
+ "cacheable": true
135
+ }'
136
+ ```
137
+
138
+ | `gmail_delegated_to` | Routing | How |
139
+ |---|---|---|
140
+ | `gemini` | same-backend | `mcp_google-workspace_gmail.get(messageId="<message-id>")` (message-level — the connector does not expose a thread-level read; group by `threadId` if reconstructing a conversation) |
141
+ | `codex` / `claude` / custom | cross-backend | `POST /api/integrations/gmail/exec` with the task prose above |
142
+
143
+ The worker's snapshot covers the actor-attribution case the legacy
144
+ prompt used to call out: you do NOT need to cross-check the agent
145
+ journal for prior agent writes; the daemon's `integration_writes` row
146
+ does it for you.
147
+
148
+ **0b. Calendar drift detection (replaces `calendar:*` observations AND
149
+ the lost `schedule.approaching` emit path).** When
150
+ `google_calendar="delegated"`, branch on `google_calendar_delegated_to`,
151
+ run TWO fetches against the connector, and POST each result to the
152
+ daemon's reconcile chokepoint. The chokepoint computes the structural
153
+ diff against the prior snapshot, fires per-change side effects
154
+ (observations, `routine.today_refresh` auto-schedule, roadmap-refresh
155
+ trigger), and returns `{diff, sideEffects}` for you to reason over.
156
+ Cross-backend fetches go through `/exec` with a natural-language
157
+ `task`:
158
+
159
+ ```bash
160
+ curl -sS -X POST http://localhost:8321/api/integrations/google_calendar/exec \
161
+ -H 'Content-Type: application/json' \
162
+ -d '{
163
+ "task": "List every event on the primary calendar between <timeMin> and <timeMax>. Return up to <maxResults> with id, title, start, end, attendees.",
164
+ "outputSchema": { "type": "object", "required": ["events"], "properties": { "events": { "type": "array", "items": { "type": "object", "required": ["id","title","start","end"] } } } },
165
+ "cacheable": true
166
+ }'
167
+ ```
168
+
169
+ | `google_calendar_delegated_to` | Routing | How |
170
+ |---|---|---|
171
+ | `gemini` | same-backend | `mcp_google-workspace_calendar.listEvents` |
172
+ | `codex` / `claude` / custom | cross-backend | `POST /api/integrations/google_calendar/exec` with the task prose above |
173
+
174
+ **Gemini-specific Calendar flow** (when `google_calendar_delegated_to=gemini`):
175
+
176
+ | Quirk | Effect | Workaround |
177
+ |---|---|---|
178
+ | `maxResults` is silently ignored | Result set bounded only by the time window + Google's per-list default | Same-backend example omits the arg; cross-backend curl still passes it (codex / claude honor it) |
179
+ | Default `attendeeResponseStatus = ["accepted","tentative","needsAction"]` drops declined events | Declined meetings invisible | Pass `attendeeResponseStatus=["declined"]` or the full set explicitly when you need them |
180
+
181
+ (The previous Gemini `updated`-field gap no longer applies under
182
+ reconcile: the daemon hashes the normalized payload on each call and
183
+ detects mid-horizon edits regardless of whether the connector surfaces
184
+ `updated`.)
185
+
186
+ Run TWO fetches with the same call shape, swapping only the time
187
+ window. Same-backend form (Gemini shown — `maxResults` omitted because
188
+ gemini ignores it; cross-backend cells in the table above pass it because
189
+ codex / claude honor it):
190
+
191
+ ```
192
+ mcp_google-workspace_calendar.listEvents(
193
+ calendarId="primary",
194
+ timeMin="<...>", timeMax="<...>",
195
+ )
196
+ ```
197
+
198
+ - **Fetch 1 — imminent meeting window**: `timeMin=now-15min`, `timeMax=now+60min`, `maxResults=50`.
199
+ - **Fetch 2 — 24-hour change-detection window**: `timeMin=now`, `timeMax=now+24h`, `maxResults=250`.
200
+
201
+ POST each result to the reconcile chokepoint. The daemon normalises
202
+ event payloads server-side and re-hashes them to detect drift, so pass
203
+ the raw connector items verbatim — no `contentHash`, no per-item
204
+ hashing on your side:
205
+
206
+ ```bash
207
+ # Fetch 1 — imminent (windowKey="primary:imminent")
208
+ curl -s -X POST http://localhost:8321/api/integrations/google_calendar/reconcile \
209
+ -H 'Content-Type: application/json' \
210
+ -d '{
211
+ "windowKey": "primary:imminent",
212
+ "windowMin": "<now-15min UTC ISO>",
213
+ "windowMax": "<now+60min UTC ISO>",
214
+ "fetchedAt": "<now UTC ISO>",
215
+ "items": [<raw event objects from Fetch 1>]
216
+ }'
217
+
218
+ # Fetch 2 — 24h (windowKey="primary:24h")
219
+ curl -s -X POST http://localhost:8321/api/integrations/google_calendar/reconcile \
220
+ -H 'Content-Type: application/json' \
221
+ -d '{
222
+ "windowKey": "primary:24h",
223
+ "windowMin": "<now UTC ISO>",
224
+ "windowMax": "<now+24h UTC ISO>",
225
+ "fetchedAt": "<now UTC ISO>",
226
+ "items": [<raw event objects from Fetch 2>]
227
+ }'
228
+ ```
229
+
230
+ Each response carries:
231
+
232
+ - `diff.created` — events new to this window-key partition since the previous reconcile.
233
+ - `diff.modified` — events whose normalised hash changed (start/end/summary/location/attendees moved).
234
+ - `diff.deleted` — events present in the prior snapshot but absent now AND still inside the queried window. Items that simply slid out of the window are pruned silently and counted under `diff.prunedOutOfWindow`; do not treat those as deletions.
235
+ - `diff.unchanged` — count only, no item bodies returned. Most hourly runs are mostly-unchanged.
236
+ - `diff.isInitialSnapshot` — `true` on the first reconcile of this window-key partition (e.g. fresh install, after a clean reinstall, or after a long downtime). Treat as "no signal": skip all per-change actions for that fetch this run; the next tick returns real diffs.
237
+ - `sideEffects.scheduleApproachingEmitted` — array of `eventId`s the daemon's imminent-event-scheduler already published to the EventBus (DM dispatch is in flight). Skip notifying these — re-notifying duplicates the user-facing reminder.
238
+ - `sideEffects.observationsWritten` — count of `observations` rows the daemon coalesced from this diff. The downstream `/api/observations` fetch in Step 1 picks them up.
239
+ - `sideEffects.todayRefreshScheduled` / `todayRefreshPending` — whether the daemon queued a `routine.today_refresh` (or deferred until the morning lock releases). When `true`, you should NOT also rewrite `## User Schedule` from Step 4 — the auto-refresh will own that section.
240
+ - `sideEffects.roadmapRefreshTriggered` — whether the daemon enqueued a roadmap-refresh because a created/modified event landed beyond the 14-day horizon.
241
+
242
+ Use the diff response for downstream decisions:
243
+
244
+ - **Imminent (Fetch 1, ≤15 min away).** The daemon's imminent-event-scheduler emits `schedule.approaching` every 60s for any snapshot row whose `start` falls in `[now, now+15min]`. **Do NOT send a duplicate `POST /api/notify` for any `eventId` listed in `sideEffects.scheduleApproachingEmitted`** — the dispatcher is already running the DM. For events NOT in that array AND NOT already covered by a `## Agent Log` `[cal] ... — reminder sent` entry within the last hour AND NOT covered by a matching Agent Plan row firing in the next 20 minutes, fold into Step 8 trigger (b) "calendar change needing same-hour attention" for at most one `POST /api/notify`. Then append a line to `## Agent Log`: `- HH:MM [cal] "<summary>" starts HH:MM — reminder sent`.
245
+ - **15–60 min out (Fetch 1).** Information-only; do not notify.
246
+ - **24-hour horizon (Fetch 2).** Treat each `diff.created` / `diff.modified` / `diff.deleted` entry as a calendar observation for Steps 1–5. The daemon already wrote the matching `observations` row (counted in `sideEffects.observationsWritten`) AND, when the change lands inside today's local-day window, scheduled `routine.today_refresh` to rewrite `## User Schedule` autonomously. **Do NOT cross-reference the Fetch 2 result with `## User Schedule` text-by-text** — `diff.deleted` carries the deletion signal that the legacy text-scan used to derive, and double-rewriting User Schedule races the auto-refresh.
247
+
248
+ If either fetch returns `diff.isInitialSnapshot === true`, log
249
+ `- HH:MM [delegated-sync] calendar partition <windowKey> bootstrapped (no diff this run)` to ## Agent Log and skip the per-change actions for that fetch.
250
+
251
+ **Dedup against a truncated `## Agent Log`:** if `<today>` contains
252
+ `[...N earlier entries omitted ...]`, a prior reminder this day may be
253
+ hidden. Before sending any notify from this step, issue
254
+ `curl -s http://localhost:8321/api/context/today` once and scan the
255
+ full log for the item. Do NOT skip this check — duplicate 15-min
256
+ reminders within the same meeting are a reported UX regression.
257
+
258
+ **0c. Recent Notion edits (delegated worker owns the drift snapshot).**
259
+ When `notion="delegated"`, the daemon's `delegated-sync-worker` polls
260
+ Notion on its own cadence (~60 min default) and writes coalesced
261
+ `notion:<parentDatabase>` (or `notion:lifecycle` for workspace-rooted
262
+ pages) observations server-side via the `integration_snapshots`
263
+ (`recently_updated`) partition. The daemon hashes each page on
264
+ `{title, lastEditedTime, parentDatabase, propertiesSummaryHash,
265
+ relationsHash}`, so a property-only edit surfaces as `modified` even
266
+ without a body fetch. Pages the agent wrote within the
267
+ `integration_writes` TTL window are pre-tagged `actor='agent'` so Step
268
+ 1's `GET /api/observations?actor=user` already excludes them. **Do NOT
269
+ call `/api/integrations/notion/reconcile` from this step** — the
270
+ route's LLM-callable allowlist excludes the notion partition for this
271
+ reason: a narrow `created_date_range:yesterday` LLM fetch posted into
272
+ the worker's 7d partition would classify every prior 1-7d-old page as
273
+ `deleted` and trigger ghost observations on the next worker tick.
274
+
275
+ If a downstream observation references a page you want to fetch the
276
+ full body for (e.g. a `notion:<db>` row in Step 1 needs context for
277
+ routing), branch on `notion_delegated_to`. The label→UUID map is
278
+ ungated under delegation (`/api/notion/databases` is a config dump, no
279
+ Notion API call) and remains useful when an observation identifies a
280
+ page by database label rather than UUID:
281
+
282
+ ```bash
283
+ curl -s http://localhost:8321/api/notion/databases
284
+ # → { "databases": { "tasks": "<uuid>", "projects": "<uuid>", ... } }
285
+ ```
286
+
287
+ | `notion_delegated_to` | Routing | Fetch tool |
288
+ |-----------------------|---------|------------|
289
+ | `gemini` | same-backend | `mcp_notion_notion-fetch(id="<page-url-or-uuid>")` |
290
+ | `codex` / `claude` / custom | cross-backend | `POST /api/integrations/notion/exec` with `{"task": "Fetch Notion page <UUID> with its block tree…", "outputSchema": …, "cacheable": true}` |
291
+
292
+ **Notion delegation coverage gap** (accepted): the worker's search uses
293
+ `created_date_range` (no `last_edited_time` filter exists upstream), so
294
+ pages created more than 7 days ago but edited within the last hour can
295
+ fall outside the worker's window; pages whose body / title don't match
296
+ the worker's `"updated"` query token may rank off the first 25 results
297
+ even when recently edited. Direct-mode `NotionPoller` covers both. If
298
+ the user notices missed Notion updates, flip the integration back to
299
+ `direct` mode.
300
+
301
+ **0d. Log the calendar collection summary** to ## Agent Log when Step
302
+ 0b ran: `- HH:MM [delegated-sync] calendar <M> events (<K> imminent)`.
303
+ The daemon's `delegated-sync-worker` records its own gmail / notion
304
+ audit rows in `agent_actions` (queryable via `audit --type=delegated_sync`),
305
+ so no prompt-side log line is needed for those. Skip this whole step
306
+ if `google_calendar` is `direct` / `disabled` this run.
307
+
308
+ #### Steps 1–9 — identical to the direct variant
309
+
310
+ 1. Group related observations before acting. One concise update beats many small patches.
311
+ 2. Classify each observation with a category tag: work/ folders, employer
312
+ repos, Notion "Work" → `[work]`; coursework, study notes → `[study]`;
313
+ journals, hobby repos, fitness, medical → `[personal]`; home logistics
314
+ → `[home]`. Default `[personal]` when ambiguous.
315
+ 3. Read the day-type filter on line 2 of <today>. Map categories to focus
316
+ dimensions per the today skill's "Category → focus-dimension mapping".
317
+ Drop observations whose focus is `off` and log:
318
+ `- HH:MM [observations] skipped <n> item(s): <category> focus off`.
319
+ 4. Route each surviving actionable observation to the right today.md section.
320
+ **Before writing a new row or scheduling anything, run the dedup
321
+ pre-check**:
322
+ - Scan `<today>` `## Agent Plan` for an existing row with HH:MM
323
+ within ±15 min AND overlapping subject/keywords. If found → skip
324
+ (log `- HH:MM [observations] skipped <item>: already planned`).
325
+ - Scan `<today>` `## User Tasks` for the same subject. If found →
326
+ skip (log `skipped: already in User Tasks`).
327
+ - For schedule registrations, also query:
328
+ `GET /api/schedule?status=pending,running` AND
329
+ `GET /api/recurring-schedules?enabled=true`.
330
+ If a pending/recurring item covers the same trigger → skip.
331
+
332
+ Only after dedup clears, route the observation:
333
+ - New TODO for the user → append to ## User Tasks with the row shape in the
334
+ context skill. Derive HH:MM from (a) deadline if known, (b) proximity
335
+ to a related calendar event, or (c) working-hours midpoint.
336
+ - New proactive reminder Claude should fire → append to ## Agent Plan AND
337
+ register the matching POST /api/schedule in the same turn. Agent Plan rows
338
+ and schedule entries are always in lock-step (see skill "User Tasks vs
339
+ Agent Plan → The Agent Plan contract").
340
+ - Day-time observation (git push, Step 0a/0b/0c sync, or `notion:*`
341
+ observation in direct mode) → append to ## Agent Notes.
342
+ 5. Update projects/*.md when the observation materially changes project
343
+ state. Do NOT write to roadmap.md from hourly — for long-horizon
344
+ signals that don't belong in today.md but aren't yet strong enough
345
+ for direct roadmap edits (e.g. a user edited a vault note mentioning
346
+ a trip "sometime this summer", a far-future calendar event with an
347
+ unclear prep window), **queue them as `roadmap_candidate`
348
+ observations** via POST /api/observations (observations skill). The
349
+ next roadmap_refresh run consumes them and decides routing; this
350
+ hourly flow intentionally does not load the long-term-plan taxonomy
351
+ at all.
352
+ ```
353
+ curl -s -X POST http://localhost:8321/api/observations \
354
+ -H 'Content-Type: application/json' \
355
+ -d '{"source":"roadmap_candidate:<subkind>","ref":"<stable-ref>","changeType":"created","actor":"agent","payload":{...}}'
356
+ ```
357
+ `<subkind>` examples: `travel`, `calendar`, `vault`, `dm`.
358
+ 6. Skip noise: journal-only edits, trivial formatting, auto-generated churn,
359
+ already-processed agent writes, deletion of auto-generated artifacts.
360
+ 7. Mark processed observations consumed via POST /api/observations/consume.
361
+ Note: Step 0b imminent-meeting reminders are emitted on the EventBus
362
+ (`schedule.approaching`), not the observations table — those are
363
+ delivered as direct DMs and have no row to consume here.
364
+ 8. Urgency gate for POST /api/notify — the default is SILENCE. At most
365
+ ONE call per run, and only after the dedup pre-check passes AND one
366
+ of (a)(b)(c) holds with its concrete threshold:
367
+
368
+ **Dedup pre-check (mandatory — skip `/api/notify` if any hit):**
369
+ - `<today>` `## Agent Log` contains a `notify sent` / `DM sent` /
370
+ `[cal] ... — reminder sent` entry for the same item within the
371
+ last 4 hours.
372
+ - The truncation marker `[...N earlier entries omitted ...]` appears
373
+ in `<today>` and you cannot rule out a same-day prior notification
374
+ from the truncated view. In that case `GET /api/context/today`
375
+ once for the full log before deciding.
376
+ - A matching pending `POST /api/schedule/dm` or Agent Plan row is
377
+ already going to fire for this item within the next 2 hours
378
+ (prefer the planned channel — don't pre-empt it).
379
+
380
+ **Positive triggers — at least one must hold with its threshold:**
381
+ (a) Hard deadline ≤ 2 hours away that the agent surfaced **this
382
+ hour** from new input (mail, DM, observation) AND the user has
383
+ not yet acted on it. **Self-set deadlines, course assignments,
384
+ class times, and items already in `today.md` ## User Tasks do
385
+ NOT qualify** — they fail the awareness gate (see notify skill
386
+ § Universal user-facing message discipline § Awareness gate).
387
+ A 6-hour deadline is NOT urgent regardless.
388
+ (b) Inbound DM, Gmail (from Step 0a), or calendar change (from Step
389
+ 0b) received in the last hour that needs a same-hour reply or
390
+ decision from the user. This is also where a Step 0b imminent
391
+ meeting (≤ 15 min) cleared for notification is delivered.
392
+ (c) Concrete failure / blocker / conflict the user would not
393
+ discover in time on their own (e.g. meeting moved onto an
394
+ existing slot; CI flagged a deploy the user triggered).
395
+
396
+ **Never urgency triggers** (log-only, no notify): "processed N
397
+ observations", Agent Plan / schedule reshuffles, context-file
398
+ updates, routine summaries of agent activity, roadmap candidates
399
+ queued, observations consumed.
400
+
401
+ When in doubt, stay silent and log.
402
+ 9. Append one line to ## Agent Log even on no-op runs (Agent Log only — do
403
+ not echo as final text, do not send via notify):
404
+ `- HH:MM [observations] reviewed N items, added X tasks / Y plan rows, skipped Z`.
@@ -0,0 +1,184 @@
1
+ {context}
2
+
3
+ ## Hourly Observation Review
4
+
5
+ The "Vault policy files" block appended to this prompt includes
6
+ `routines/hourly.md` — your canonical check list for this cadence.
7
+ The "Vault review context" block includes `context-index.md` and
8
+ `dossiers/hourly.md`; consult it before Step 1 and update the dossier's
9
+ Open items / Last run before finishing. Writes to `dossiers/<flow>.md`
10
+ MUST preserve the existing YAML frontmatter block (`---\ntype: dossier\nowner: agent\nupdated: <date>\n---`); prefer `PATCH` with a
11
+ section target to mutate a single block, and when doing a `PUT` full
12
+ rewrite keep the frontmatter and only refresh `updated:` — writes that
13
+ drop the frontmatter are rejected with 422.
14
+ Execute each `### <label>` entry in order, skipping any whose
15
+ precondition does not hold. The steps below are the built-in decision
16
+ framework for the observation-review step; additional checks the user
17
+ has added to the routine file run alongside them using the same
18
+ routing rules.
19
+
20
+ Use the observations skill to fetch pending user-originated items:
21
+ GET /api/observations?pending=true&actor=user&limit=20. Follow the
22
+ context skill for today.md section formats and the day-type filter
23
+ mapping — this prompt owns the decision workflow only. Most runs should
24
+ do bookkeeping silently; only fire POST /api/notify on the urgency gate
25
+ in Step 8 below.
26
+
27
+ ### Execution budget
28
+ Target: 5–10 turns. If you reach 15 turns, wrap up current work and log.
29
+ Do NOT read roadmap.md, projects/*.md, or user/*.md unless an
30
+ observation warrants project-state context (e.g. references a project,
31
+ milestone, or deliverable — not merely a file edit in a watched repo).
32
+
33
+ ### Stage gate decision (cost-reduction-structural §B)
34
+ When `<gate_decision>` is present, this run was escalated through the
35
+ three-stage funnel (Stage 1 deterministic gate or Stage 2 lite-tier
36
+ triage). The block tells you *why* you were spawned — pull the
37
+ `reason` and `signals_snapshot` into your prioritisation. Examples:
38
+
39
+ - `reason: vip_mail_unread` → start with the mail observation, do not
40
+ scan obsidian noise first.
41
+ - `reason: agent_plan_overdue` → re-check today.md `## Agent Plan` and
42
+ decide whether to log a slip, reschedule, or notify.
43
+ - `reason: schedule_approaching` → fold the upcoming
44
+ `agent_schedule` row into the run; don't pre-empt the schedule's
45
+ own dispatcher.
46
+ - `reason: heartbeat_due` → low-signal heartbeat. Stay maximally
47
+ silent unless something actually warrants action.
48
+
49
+ If `<gate_decision>` is absent, this is a legacy direct invocation
50
+ (Phase-9 path before the gate was wired) — proceed as before.
51
+
52
+ ### Pre-summarized observations (cost-reduction-structural §A)
53
+ Every observation arrives with `summary_text` (≤120 chars) and
54
+ `novelty_score` (0–3) populated by the daemon's per-observation
55
+ summarizer. **Use those signals first** — fetch raw content (Obsidian
56
+ notes, git diffs, mail bodies) only when `novelty_score >= 2`, OR when
57
+ a different observation in the same batch references the same
58
+ path/ref. For `novelty_score === 1` use the summary alone; for `0`
59
+ silently consume. When `summary_status !== 'done'` (pending / skipped
60
+ / failed) OR `summaryStale === true` (summary >6 h older than
61
+ observedAt) fall back to the legacy fetch-on-doubt rules in the
62
+ observations skill.
63
+
64
+ ### Default stance — silence + idempotence
65
+ Most hourly runs are silent bookkeeping: consume observations, update
66
+ today.md, log, done. The baseline assumption for every step below is
67
+ that the user does NOT want another notification and does NOT want a
68
+ new Agent Plan row unless this run has something genuinely new to add.
69
+ The morning routine is authoritative for the day's plan; your job is
70
+ to fold in new signals, not to re-plan. Two hard rules:
71
+
72
+ - **No duplicate Agent Plan rows / schedules.** Before appending to
73
+ `## Agent Plan` or calling `POST /api/schedule`, run the dedup
74
+ pre-check in Step 4. If a matching row/schedule already exists,
75
+ skip and log — never add a second one.
76
+ - **No duplicate notifications.** Before `POST /api/notify`, run the
77
+ dedup pre-check in Step 8. If the same item was already notified
78
+ earlier today, stay silent and log.
79
+
80
+ When in doubt, stay silent and log to `## Agent Log`.
81
+
82
+ ### External services are read-only this hour
83
+
84
+ This routine reads external state for context — it does not push back. While running this hourly check, do **not**:
85
+
86
+ - Create / update / archive Notion pages or change Notion schema.
87
+ - Send / draft / move / tag mail.
88
+ - Create / update / delete calendar events.
89
+ - Open / merge / comment on GitHub PRs or issues.
90
+
91
+ External-source signals (`mail:*`, `notion:*`, `calendar:*`, `git:*`) reach you through `<observations>`. Consume them, route to `today.md` / `projects/*.md` / the `roadmap_candidate` queue per the Decision Framework below, but do **not** act back on the source system. Outbound writes against external services belong in the morning routine, evening review, or DM-reply paths — `routine.hourly_check` is a silent bookkeeping pass.
92
+
93
+ This rule applies regardless of integration mode (direct, same-backend delegated, cross-backend delegated). It is owned by the routine, so a session whose `notion` / `mail` / `external-services` skill body was dropped under same-backend delegation (because the connector covers the surface) still inherits the constraint.
94
+
95
+ ### Decision Framework
96
+ 1. Group related observations before acting. One concise update beats many small patches.
97
+ 2. Classify each observation with a category tag: work/ folders, employer
98
+ repos, Notion "Work" → `[work]`; coursework, study notes → `[study]`;
99
+ journals, hobby repos, fitness, medical → `[personal]`; home logistics
100
+ → `[home]`. Default `[personal]` when ambiguous.
101
+ 3. Read the day-type filter on line 2 of <today>. Map categories to focus
102
+ dimensions per the today skill's "Category → focus-dimension mapping".
103
+ Drop observations whose focus is `off` and log:
104
+ `- HH:MM [observations] skipped <n> item(s): <category> focus off`.
105
+ 4. Route each surviving actionable observation to the right today.md section.
106
+ **Before writing a new row or scheduling anything, run the dedup
107
+ pre-check**:
108
+ - Scan `<today>` `## Agent Plan` for an existing row with HH:MM
109
+ within ±15 min AND overlapping subject/keywords. If found → skip
110
+ (log `- HH:MM [observations] skipped <item>: already planned`).
111
+ - Scan `<today>` `## User Tasks` for the same subject. If found →
112
+ skip (log `skipped: already in User Tasks`).
113
+ - For schedule registrations, also query:
114
+ `GET /api/schedule?status=pending,running` AND
115
+ `GET /api/recurring-schedules?enabled=true`.
116
+ If a pending/recurring item covers the same trigger → skip.
117
+
118
+ Only after dedup clears, route the observation:
119
+ - New TODO for the user → append to ## User Tasks with the row shape in the
120
+ context skill. Derive HH:MM from (a) deadline if known, (b) proximity
121
+ to a related calendar event, or (c) working-hours midpoint.
122
+ - New proactive reminder Claude should fire → append to ## Agent Plan AND
123
+ register the matching POST /api/schedule in the same turn. Agent Plan rows
124
+ and schedule entries are always in lock-step (see skill "User Tasks vs
125
+ Agent Plan → The Agent Plan contract").
126
+ - Day-time observation (git push, Notion status change) → append to ## Agent Notes.
127
+ 5. Update projects/*.md when the observation materially changes project
128
+ state. Do NOT write to roadmap.md from hourly — for long-horizon
129
+ signals that don't belong in today.md but aren't yet strong enough
130
+ for direct roadmap edits (e.g. a user edited a vault note mentioning
131
+ a trip "sometime this summer", a far-future calendar event with an
132
+ unclear prep window), **queue them as `roadmap_candidate`
133
+ observations** via POST /api/observations (observations skill). The
134
+ next roadmap_refresh run consumes them and decides routing; this
135
+ hourly flow intentionally does not load the long-term-plan taxonomy
136
+ at all.
137
+ ```
138
+ curl -s -X POST http://localhost:8321/api/observations \
139
+ -H 'Content-Type: application/json' \
140
+ -d '{"source":"roadmap_candidate:<subkind>","ref":"<stable-ref>","changeType":"created","actor":"agent","payload":{...}}'
141
+ ```
142
+ `<subkind>` examples: `travel`, `calendar`, `vault`, `dm`.
143
+ 6. Skip noise: journal-only edits, trivial formatting, auto-generated churn,
144
+ already-processed agent writes, deletion of auto-generated artifacts.
145
+ 7. Mark processed observations consumed via POST /api/observations/consume.
146
+ 8. Urgency gate for POST /api/notify — the default is SILENCE. At most
147
+ ONE call per run, and only after the dedup pre-check passes AND one
148
+ of (a)(b)(c) holds with its concrete threshold:
149
+
150
+ **Dedup pre-check (mandatory — skip `/api/notify` if any hit):**
151
+ - `<today>` `## Agent Log` contains a `notify sent` / `DM sent` /
152
+ `[cal] ... — reminder sent` entry for the same item within the
153
+ last 4 hours.
154
+ - The truncation marker `[...N earlier entries omitted ...]` appears
155
+ in `<today>` and you cannot rule out a same-day prior notification
156
+ from the truncated view. In that case `GET /api/context/today`
157
+ once for the full log before deciding.
158
+ - A matching pending `POST /api/schedule/dm` or Agent Plan row is
159
+ already going to fire for this item within the next 2 hours
160
+ (prefer the planned channel — don't pre-empt it).
161
+
162
+ **Positive triggers — at least one must hold with its threshold:**
163
+ (a) Hard deadline ≤ 2 hours away that the agent surfaced **this
164
+ hour** from new input (mail, DM, observation) AND the user has
165
+ not yet acted on it. **Self-set deadlines, course assignments,
166
+ class times, and items already in `today.md` ## User Tasks do
167
+ NOT qualify** — they fail the awareness gate (see notify skill
168
+ § Universal user-facing message discipline § Awareness gate).
169
+ A 6-hour deadline is NOT urgent regardless.
170
+ (b) Inbound DM or calendar change received in the last hour that
171
+ needs a same-hour reply or decision from the user.
172
+ (c) Concrete failure / blocker / conflict the user would not
173
+ discover in time on their own (e.g. meeting moved onto an
174
+ existing slot; CI flagged a deploy the user triggered).
175
+
176
+ **Never urgency triggers** (log-only, no notify): "processed N
177
+ observations", Agent Plan / schedule reshuffles, context-file
178
+ updates, routine summaries of agent activity, roadmap candidates
179
+ queued, observations consumed.
180
+
181
+ When in doubt, stay silent and log.
182
+ 9. Append one line to ## Agent Log even on no-op runs (Agent Log only — do
183
+ not echo as final text, do not send via notify):
184
+ `- HH:MM [observations] reviewed N items, added X tasks / Y plan rows, skipped Z`.