@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,86 @@
1
+ ---
2
+ kind: reference
3
+ name: recurrence-rule
4
+ description: recurrenceRule grammar — hourly / daily / weekly / monthly. Engine accepts all four; the managed-tasks consumer specifically refuses sub-daily for app-fetch correctness (template below).
5
+ ---
6
+
7
+ # recurrenceRule grammar
8
+
9
+ The daemon's recurrence engine accepts four frequencies: `hourly`,
10
+ `daily`, `weekly`, `monthly`. Each frequency requires its own set of
11
+ fields and rejects fields that don't apply — the daemon's Zod
12
+ refinements return a `schedule.frequency_field_mismatch` issue (with
13
+ the offending field path) when the shape disagrees with the chosen
14
+ frequency. Pre-validate to save a round-trip.
15
+
16
+ Times are `HH:MM` 24-hour local; `timezone` is IANA (auto-fills from
17
+ daemon config when omitted, but explicit is safer so a roaming laptop
18
+ does not surprise the user).
19
+
20
+ ## Engine — per-frequency field rules
21
+
22
+ | `frequency` | Required | Allowed | Forbidden |
23
+ |---|---|---|---|
24
+ | `hourly` | — | `intervalHours` (1..23, default 1), `minuteOfHour` (0..59, default 0), `timezone` | `time`, `daysOfWeek`, `daysOfMonth`, `onMissingDay` |
25
+ | `daily` | `time` | `timezone` | `intervalHours`, `minuteOfHour`, `daysOfWeek`, `daysOfMonth`, `onMissingDay` |
26
+ | `weekly` | `time`, `daysOfWeek` | `timezone` | `intervalHours`, `minuteOfHour`, `daysOfMonth`, `onMissingDay` |
27
+ | `monthly` | `time`, `daysOfMonth` | `timezone`, `onMissingDay` (default `lastDayOfMonth`) | `intervalHours`, `minuteOfHour`, `daysOfWeek` |
28
+
29
+ - `daysOfWeek` is `0=Sun..6=Sat`; 1..7 distinct entries, dupes rejected.
30
+ - `daysOfMonth` is `1..31`; 1..31 distinct entries, dupes rejected.
31
+ Days 29-31 may not exist in a given month — see `onMissingDay`.
32
+ - `onMissingDay`: `"skip"` (don't fire that month) or
33
+ `"lastDayOfMonth"` (fire on the actual last day, preserving the
34
+ pre-redesign clamp behavior). Default `"lastDayOfMonth"` for
35
+ back-compat. The engine also de-duplicates calendar dates that
36
+ collapse to the same fire (e.g. `[28,31]` in non-leap Feb with
37
+ `"lastDayOfMonth"` fires Feb 28 once, not twice).
38
+ - `intervalHours=N` fires when `(localHour % N) == 0` at
39
+ `minuteOfHour` local, anchored at midnight in the rule's
40
+ `timezone`.
41
+
42
+ ## Mapping table
43
+
44
+ | User said | `cadence` | `recurrenceRule` |
45
+ |---|---|---|
46
+ | every hour | `hourly :00 (UTC)` | `{frequency:"hourly"}` |
47
+ | every 2 hours at :30 | `hourly /2 :30 (UTC)` | `{frequency:"hourly", intervalHours:2, minuteOfHour:30}` |
48
+ | every day at 10am (Asia/Tokyo) | `daily 10:00 (Asia/Tokyo)` | `{frequency:"daily", time:"10:00", timezone:"Asia/Tokyo"}` |
49
+ | every Monday 9am | `weekly Mon 09:00` | `{frequency:"weekly", time:"09:00", timezone:<user tz>, daysOfWeek:[1]}` |
50
+ | every weekday at 8am | `weekdays 08:00` | `{frequency:"weekly", time:"08:00", timezone:<user tz>, daysOfWeek:[1,2,3,4,5]}` |
51
+ | 1st of every month at noon | `monthly day 1 12:00` | `{frequency:"monthly", time:"12:00", timezone:<user tz>, daysOfMonth:[1]}` |
52
+ | 25th of every month at 21:00 | `monthly day 25 21:00` | `{frequency:"monthly", time:"21:00", timezone:<user tz>, daysOfMonth:[25]}` |
53
+ | last day of every month at 21:00 | `monthly last 21:00` | `{frequency:"monthly", time:"21:00", timezone:<user tz>, daysOfMonth:[31], onMissingDay:"lastDayOfMonth"}` |
54
+ | every 5 minutes | _not representable_ | _refuse — sub-hour cadences are not supported_ |
55
+
56
+ ## Consumer-specific refusal — managed-tasks only
57
+
58
+ The managed-tasks skill (`mt_<n>` rows) refuses sub-daily cadences
59
+ because app-fetch correctness requires a daily-or-coarser window to
60
+ amortise rate limits and to map cleanly onto the entity-mirror's
61
+ daily granularity. Schedule callers (`/api/schedule`,
62
+ `/api/recurring-schedules`) have no such constraint and may use any
63
+ of the four frequencies the engine accepts.
64
+
65
+ ### managed-tasks sub-daily refusal — DM template
66
+
67
+ > Managed tasks only support daily, weekly, or monthly cadences.
68
+ > "every hour" / "every 5 minutes" is too tight for a recurring app
69
+ > fetch — pick `daily` or coarser. (If you want a daemon-internal
70
+ > hourly check, use `/api/recurring-schedules` via the `schedule`
71
+ > skill.)
72
+
73
+ Same template applies to "every 5 minutes", "every 30 minutes",
74
+ "every 2 hours", etc. when the registering surface is managed-tasks.
75
+
76
+ ## Cadence string vs structured rule
77
+
78
+ Always send both `cadence` (human-readable, rendered in
79
+ `rules/management.md` §B) and `recurrenceRule` (structured, what the
80
+ scheduler executes). They must agree — if they drift, the rendered
81
+ file misleads the user about what the scheduler will actually do.
82
+
83
+ When the user modifies just the time (`"9am instead of 10am"`),
84
+ send the new `cadence` and new `recurrenceRule` together in the same
85
+ PATCH so the §B label matches the executable schedule in one
86
+ transition.
@@ -0,0 +1,185 @@
1
+ ---
2
+ kind: reference
3
+ name: recurring
4
+ description: /api/recurring-schedules — hourly / daily / weekly / monthly cadences auto-regenerating one-shot rows. Includes hourly anchor semantics, monthly missing-day recipes, and the tier ↔ model swap on PATCH.
5
+ ---
6
+
7
+ # Recurring schedules
8
+
9
+ For tasks that repeat on a fixed pattern. The daemon auto-regenerates
10
+ the next one-shot occurrence after each execution; you never have to
11
+ re-POST a daily reminder.
12
+
13
+ Use a recurring schedule when the cadence is all that matters and
14
+ there is no need to record *why* the rule exists. If the user wants
15
+ the WHY captured alongside the cadence (so the rule survives a
16
+ context reset), use the `management-policy` skill — it produces a
17
+ `rules/policies/<slug>.md` linked to a `routines/custom/<slug>.md`
18
+ custom routine.
19
+
20
+ ## POST /api/recurring-schedules — Create
21
+
22
+ ```bash
23
+ # Hourly at :00 (every hour)
24
+ curl -s -X POST http://localhost:8321/api/recurring-schedules \
25
+ -H 'Content-Type: application/json' \
26
+ -d '{"taskType":"wake","description":"Hourly docker container health check.","recurrenceRule":{"frequency":"hourly"},"tier":"lite"}'
27
+
28
+ # Every 2 hours at :30 (00:30, 02:30, …, 22:30 local)
29
+ curl -s -X POST http://localhost:8321/api/recurring-schedules \
30
+ -H 'Content-Type: application/json' \
31
+ -d '{"taskType":"wake","description":"Sync inbox triage signals.","recurrenceRule":{"frequency":"hourly","intervalHours":2,"minuteOfHour":30},"tier":"lite"}'
32
+
33
+ # Daily at 09:00
34
+ curl -s -X POST http://localhost:8321/api/recurring-schedules \
35
+ -H 'Content-Type: application/json' \
36
+ -d '{"taskType":"wake","description":"Morning inbox triage — check pending observations and update today.md.","recurrenceRule":{"frequency":"daily","time":"09:00"}}'
37
+
38
+ # Weekly Mon/Wed/Fri at 10:00
39
+ curl -s -X POST http://localhost:8321/api/recurring-schedules \
40
+ -H 'Content-Type: application/json' \
41
+ -d '{"taskType":"wake","description":"Standup prep — review PRs, calendar, and blockers.","recurrenceRule":{"frequency":"weekly","time":"10:00","daysOfWeek":[1,3,5]}}'
42
+
43
+ # Monthly on the 25th at 21:00 (billing reconciliation)
44
+ curl -s -X POST http://localhost:8321/api/recurring-schedules \
45
+ -H 'Content-Type: application/json' \
46
+ -d '{"taskType":"wake","description":"Monthly card reconciliation — pull statement, log balance to finance dossier.","recurrenceRule":{"frequency":"monthly","time":"21:00","daysOfMonth":[25]},"tier":"medium"}'
47
+ ```
48
+
49
+ | Field | Required | Description |
50
+ |---|---|---|
51
+ | `taskType` | Yes | Task type for dispatch (e.g. `wake`) |
52
+ | `description` | Yes | Self-contained (min 20 chars). Same rules as one-shot. Doubles as the agent body unless `prompt` overrides it. |
53
+ | `prompt` | No | Optional override for the agent body (min 20 chars when set). Each materialized one-shot row inherits this from the recurring parent. |
54
+ | `recurrenceRule` | Yes | `{ frequency, time?, timezone?, intervalHours?, minuteOfHour?, daysOfWeek?, daysOfMonth?, onMissingDay? }` — fields gated by `frequency`; see grammar below. |
55
+ | `tier` | No | `lite` / `medium` / `high`. Mutually exclusive with `model`. |
56
+ | `model` | No | Registered model id (`claude-opus-4-7`, `gpt-5.4`, `gemini-3.1-pro-preview`, …), legacy alias (`sonnet` / `opus` — auto-rewritten to `tier`), or composite `<backendId>/<modelId>` for future disambiguation. Mutually exclusive with `tier`. The row stores `(model, backend_id)` together so the dispatcher honors the pin at fire time. |
57
+ | `taskContext` | No | Structured metadata object |
58
+
59
+ ### Recurrence rule grammar (engine)
60
+
61
+ The recurrence engine accepts four frequencies. Each frequency
62
+ requires its own set of fields and rejects fields that don't apply.
63
+
64
+ | `frequency` | Required | Allowed | Forbidden |
65
+ |---|---|---|---|
66
+ | `"hourly"` | — | `intervalHours` (1..23, default 1), `minuteOfHour` (0..59, default 0), `timezone` | `time`, `daysOfWeek`, `daysOfMonth`, `onMissingDay` |
67
+ | `"daily"` | `time` | `timezone` | `intervalHours`, `minuteOfHour`, `daysOfWeek`, `daysOfMonth`, `onMissingDay` |
68
+ | `"weekly"` | `time`, `daysOfWeek` (1..7 distinct entries, 0=Sun..6=Sat) | `timezone` | `intervalHours`, `minuteOfHour`, `daysOfMonth`, `onMissingDay` |
69
+ | `"monthly"` | `time`, `daysOfMonth` (1..31 distinct entries) | `timezone`, `onMissingDay` (default `"lastDayOfMonth"`) | `intervalHours`, `minuteOfHour`, `daysOfWeek` |
70
+
71
+ `time` is `HH:MM` 24-hour local. `timezone` is an IANA zone
72
+ (`Asia/Tokyo`, `America/New_York`, `UTC`); auto-filled from daemon
73
+ config when omitted, but explicit is safer so a roaming laptop does
74
+ not surprise the user. The error envelope cites `validValues` on
75
+ every range / format failure — read it and resubmit instead of
76
+ guessing.
77
+
78
+ ### Hourly anchor semantics
79
+
80
+ `intervalHours=N` fires when `(localHour % N) == 0` at
81
+ `minuteOfHour` local. The anchor is **local midnight** in the rule's
82
+ `timezone`, so `intervalHours:2, minuteOfHour:30` fires at 00:30,
83
+ 02:30, …, 22:30 local — predictable for the user's mental model.
84
+
85
+ | Intent | `recurrenceRule` |
86
+ |---|---|
87
+ | Every hour at :00 | `{frequency:"hourly"}` |
88
+ | Every hour at :15 | `{frequency:"hourly", minuteOfHour:15}` |
89
+ | Every 2 hours at :30 | `{frequency:"hourly", intervalHours:2, minuteOfHour:30}` |
90
+ | Every 6 hours at :00 (Asia/Tokyo) | `{frequency:"hourly", intervalHours:6, timezone:"Asia/Tokyo"}` |
91
+
92
+ DST: in zones that observe it, a skipped local hour drops one fire;
93
+ a doubled local hour fires once. Accepted edge case — do not try to
94
+ compensate from the caller.
95
+
96
+ Use hourly sparingly. Sub-hour cadences are not representable (the
97
+ minimum is 1 hour); pick a coarser cadence or move the work into a
98
+ daemon-internal poller instead.
99
+
100
+ ### Monthly missing-day semantics
101
+
102
+ Some months don't contain the day the user asked for (Feb 30, Apr
103
+ 31). `onMissingDay` controls what happens that month:
104
+
105
+ - `"skip"` — don't fire that month for the missing day.
106
+ - `"lastDayOfMonth"` (default) — fire on the actual last day of the
107
+ month. Preserves the pre-redesign clamp behavior, so existing
108
+ recurring rules created before this redesign keep firing
109
+ bit-identically.
110
+
111
+ The engine de-duplicates calendar dates that collapse to the same
112
+ fire (e.g. `daysOfMonth:[28,31]` in non-leap Feb with
113
+ `"lastDayOfMonth"` lands on Feb 28 once, not twice).
114
+
115
+ | Recipe | `recurrenceRule` |
116
+ |---|---|
117
+ | 25th of every month at 21:00 | `{frequency:"monthly", time:"21:00", daysOfMonth:[25]}` |
118
+ | 31st at 21:00, skip Feb/Apr/Jun/Sep/Nov | `{frequency:"monthly", time:"21:00", daysOfMonth:[31], onMissingDay:"skip"}` |
119
+ | 31st at 21:00, fall back to last day | `{frequency:"monthly", time:"21:00", daysOfMonth:[31], onMissingDay:"lastDayOfMonth"}` |
120
+ | **Last day of every month at 21:00** | same as above — `daysOfMonth:[31] + onMissingDay:"lastDayOfMonth"` clamps Feb/Apr/Jun/Sep/Nov to their last day; the 31st of every other month is already that month's last day |
121
+ | 29th at 21:00, skip non-leap Feb | `{frequency:"monthly", time:"21:00", daysOfMonth:[29], onMissingDay:"skip"}` |
122
+ | 1st AND 15th at 10:00 | `{frequency:"monthly", time:"10:00", daysOfMonth:[1,15]}` |
123
+
124
+ When `daysOfMonth` contains 29/30/31 and `onMissingDay` is omitted,
125
+ the daemon returns a `warnings[]` entry nudging you to be explicit.
126
+ Persistence still happens — the warning is advisory.
127
+
128
+ Response: `{ "status":"created", "item":{ "id","recurrenceRule","recurrenceLabel","nextRunAt",...}, "warnings":[] }`. `nextRunAt` is the UTC timestamp the engine has materialized as the first one-shot row's `scheduled_for`. `recurrenceLabel` is the human-readable form rendered into `rules/management.md` §B (e.g. `"Every 2 hours at :30 (UTC)"`, `"Monthly on the 31st at 21:00 (Asia/Tokyo); falls back to last day of month"`).
129
+
130
+ ## GET /api/recurring-schedules — List
131
+
132
+ ```bash
133
+ curl -s "http://localhost:8321/api/recurring-schedules?enabled=true"
134
+ ```
135
+
136
+ Response: `{ "items":[{ "id","taskType","description","recurrenceRule","enabled","nextRunAt","recurrenceLabel","model","backendId","tier","taskContext" }] }`. `model` / `backendId` are populated together when the row pins a registered id; otherwise `model:null, backendId:null` and `tier` carries the pin (or all three are null and the row inherits the dispatcher's process-key default).
137
+
138
+ ## PATCH /api/recurring-schedules/:id — Update
139
+
140
+ ```bash
141
+ # Swap a daily rule to hourly + change model to Opus in one PATCH
142
+ curl -s -X PATCH http://localhost:8321/api/recurring-schedules/1 \
143
+ -H 'Content-Type: application/json' \
144
+ -d '{"recurrenceRule":{"frequency":"hourly","intervalHours":2,"minuteOfHour":0},"tier":null,"model":"claude-opus-4-7"}'
145
+ ```
146
+
147
+ Updatable: `recurrenceRule`, `description`, `prompt` (string sets an
148
+ override, `null` clears), `tier` (set / `null` to clear), `model`
149
+ (set / `null` to clear), `taskContext`, `enabled`. Changing
150
+ `recurrenceRule` / `enabled` auto-reschedules — the pending one-shot
151
+ row tied to the old rule is cancelled and a fresh row is materialized
152
+ from the new `recurrenceRule` value, preserving the parent's
153
+ `(model, backend_id)` or `tier` pin.
154
+
155
+ **Tier ↔ model swap.** Pass `null` to clear one and a concrete value
156
+ to set the other in the same request — the row carries at most one
157
+ pin at rest. Setting a registered `model` token also clears any
158
+ prior `tier_override`. Setting a legacy alias (`sonnet` / `opus`) on
159
+ PATCH is rewritten to `tier:"medium"` / `tier:"high"`; the alias is
160
+ never stored verbatim.
161
+
162
+ **Re-materialization scope.** Changing `model` / `tier` alone does
163
+ **not** re-point the already-materialized pending one-shot row — only
164
+ `recurrenceRule` / `enabled` re-materialize. Delete the pending row
165
+ (`DELETE /api/schedule/:id`) and let the next reconcile pass pick up
166
+ the new pin, or PATCH `/api/schedule/:id` directly if you need the
167
+ pending row repointed now.
168
+
169
+ Set `{"enabled":false}` to pause without deleting. Surface
170
+ `warnings[]` (e.g. `schedule.model_deprecated`,
171
+ `schedule.on_missing_day_unused`) to the next turn — the PATCH still
172
+ succeeds but the warning carries replacement guidance.
173
+
174
+ ## DELETE /api/recurring-schedules/:id — Delete
175
+
176
+ ```bash
177
+ curl -s -X DELETE http://localhost:8321/api/recurring-schedules/1
178
+ ```
179
+
180
+ Deletes the rule and cancels every materialized pending instance.
181
+ Response: `{ "status":"deleted", "id":1 }`.
182
+
183
+ Use pause (`PATCH ... {"enabled":false}`) instead when the user might
184
+ want to resume the same cadence later — DELETE loses the
185
+ `recurrenceRule` shape and the `taskContext` payload.
@@ -1,20 +1,18 @@
1
1
  ---
2
2
  name: scheduled-managed-task
3
- description: Run an `mt_<n>` managed task at its scheduled slot — pick a tool, fetch since last run, resolve to entity via §7.6, merge into `<domain>/<type-plural>/<slug>.md`. Trigger when `task_context.mt_id` matches `mt_<n>`; `task_context.adhoc === true` means run-now.
4
- when_to_use: A `scheduled.task` session with `task_context.mt_id` matching `mt_<n>`; `task_context.adhoc === true` marks on-demand pulls. SKIP for regular scheduled tasks, DM-tone scheduled sessions (`scheduled.dm`), or one-off reminder delivery.
3
+ description: A `scheduled.task` session with `task_context.mt_id` matching `mt_<n>`; `task_context.adhoc === true` marks on-demand pulls. SKIP for regular scheduled tasks, DM-tone scheduled sessions (`scheduled.dm`), or one-off reminder delivery.
5
4
  allowed-tools:
6
5
  - Bash(curl *)
7
- - Bash(jq *)
8
6
  - Read
9
7
  ---
10
8
 
11
9
  # Scheduled Managed-Task Run
12
10
 
13
11
  This skill is the **scheduled execution** half of the management
14
- registry. The DM-side counterparts
15
- `management-task-register` / `-modify` / `-stop` — own the row's
16
- lifecycle. This skill is what fires every cron slot to actually do
17
- the work.
12
+ registry. The DM-side counterpart the `managed-tasks` skill
13
+ (`## Register` / `## Modify` / `## Stop` / `## Run once`)owns the
14
+ row's lifecycle. This skill is what fires every cron slot to actually
15
+ do the work.
18
16
 
19
17
  ## When this skill activates
20
18
 
@@ -72,8 +70,8 @@ records the orphan firing.
72
70
 
73
71
  ### Step 2 — Select tool (LLM judgment, fresh each run)
74
72
 
75
- Same rule as `management-task-register` Step 3: enumerate the
76
- tools available to this session and pick by capability for
73
+ Same rule as the `managed-tasks` skill's `## Register` Step 3:
74
+ enumerate the tools available to this session and pick by capability for
77
75
  `item.app`. The user's prior choice (when surfaced as a hint in
78
76
  `task_context.lastToolChoice` by an earlier run) is a **hint**, not
79
77
  a binding — if it no longer exists in this session (the user
@@ -146,9 +144,9 @@ output_path = "finance/receipts/" → domain=finance, type=receipt
146
144
  ```
147
145
 
148
146
  If `output_path` is null (first run), pick the best `(domain, type)`
149
- from the data shape using the same prior table from
150
- `management-task-register` Step 4a. After this run, write the
151
- chosen path back to the row (Step 5b) so subsequent runs converge.
147
+ from the data shape using the same prior table from the `managed-tasks`
148
+ skill `## Register` Step 4a. After this run, write the chosen path back
149
+ to the row (Step 5b) so subsequent runs converge.
152
150
 
153
151
  Slug: `<YYYY-MM-DD>-<sanitized-title>`. Sanitization rules:
154
152
  lowercase, ASCII-fold, replace `[^a-z0-9-]+` with `-`, collapse
@@ -256,8 +254,8 @@ audit shape.
256
254
  If the run produced a mix of domains/types (e.g. Drive PDFs that were
257
255
  half receipts and half random docs), leave `output_path` null — let
258
256
  the next run try again. The renderer marks null-path rows in §B with
259
- an em-dash; the user can also set the path explicitly via
260
- `management-task-modify`.
257
+ an em-dash; the user can also set the path explicitly via the
258
+ `managed-tasks` skill `## Modify` flow.
261
259
 
262
260
  ### Step 6 — Three-strikes notify
263
261
 
@@ -359,7 +357,7 @@ appended `## <App> Notes` body, not as a separate DM.
359
357
  daemon owns the threshold notify, the agent emits one DM at the
360
358
  3rd consecutive failure, then stays silent until success or stop.
361
359
  - Does NOT touch the §B row's `app` or `cadence` — those are
362
- user-mutable only via `management-task-modify`.
360
+ user-mutable only via the `managed-tasks` skill `## Modify` flow.
363
361
  - Does NOT INSERT `agent_schedule` rows. The cron scheduler does.
364
362
  - Does NOT delete entity files when a tool returns "this item was
365
363
  removed upstream". Removal-from-source is recorded as a
@@ -8,10 +8,7 @@ allowed-tools:
8
8
 
9
9
  # today.md Guide
10
10
 
11
- Output language: follow `<output_language_policy>`. today.md is
12
- Policy B — the **skeleton lines listed below stay English verbatim**;
13
- bullets, narrative, and free-text fields under each H2 are written in
14
- `<settings primary_language>`. Preserve user-customized headers verbatim.
11
+ Output language: today.md is Policy B — see `<output_language_policy>`. The skeleton lines listed below stay English verbatim; bullets and narrative under each H2 are in `<settings primary_language>`.
15
12
 
16
13
  **Skeleton (do NOT translate — exact-regex-validated on PUT):**
17
14
 
@@ -143,28 +140,16 @@ Violations: row without schedule → silently never fires. Schedule without row
143
140
 
144
141
  ## Agent Plan lifecycle — close the loop
145
142
 
146
- When spawned by a `scheduled.task` for an Agent Plan row:
147
-
148
- 1. Execute the task (send DM, fire notification, run check-in).
149
- 2. Append to Agent Log: `- HH:MM [agent_plan] <action> — <outcome>`
150
- Outcomes: `DM sent`, `notify sent`, `skipped (user in meeting)`, `failed: <reason>`
151
- 3. Read today.md, locate the matching Agent Plan row (match HH:MM + action text), flip `[ ]` → `[x]`:
152
- ```bash
153
- curl -s http://localhost:8321/api/context/today
154
- # Edit the agent_plan section body, then:
155
- curl -s -X PATCH http://localhost:8321/api/context/today \
156
- -H 'Content-Type: application/json' \
157
- -d '{"section": "agent_plan", "mode": "replace", "content": "- [x] 08:55 ..."}'
158
- ```
159
- **Read-before-write is mandatory** — PATCH replace replaces the entire section.
160
- 4. If the Agent Plan row is missing (user hand-edited), log and skip.
161
- 5. Always flip to `[x]`, even for skips/failures — annotate non-success:
162
- - Success: no annotation
163
- - Skip: `⚠ skipped: <reason>`
164
- - Failure: `⚠ failed: <reason>`
165
- 6. If PATCH returns 409 (Morning Routine lock), retry after 30s up to 3 times. If still locked, log `loop-closeout deferred`.
166
-
167
- **Why flip inside the scheduled task, not later?** The user may DM at 09:00 asking "did you send the reminder?" — if the row is still `[ ]`, the DM handler can't tell whether it was sent.
143
+ `scheduled.task` and `scheduled.dm` (and any other event that flips an
144
+ Agent Plan row) follow the close-the-loop lifecycle in the reference
145
+ below: execute, append Agent Log entry, read-then-flip the row to
146
+ `[x]` with annotation, retry on `today.md` lock, surface missing-row
147
+ state.
148
+
149
+ DM handlers and hourly checks do not flip Agent Plan rows — read the
150
+ reference only if your event type is in its applicability list.
151
+
152
+ {{> ref:agent-plan-lifecycle }}
168
153
 
169
154
  ## Agent Log format
170
155
 
@@ -193,7 +178,7 @@ so the LLM never sees this format-using event with a wider lookahead.
193
178
  No additional gate is restated here.
194
179
 
195
180
  **Agent Notes**:
196
- `- 📅 event_title starts at HH:MM [— blocks/relates to: <task>]`
181
+ `- event_title starts at HH:MM [— blocks/relates to: <task>]`
197
182
 
198
183
  **Agent Log** (always):
199
184
  `- HH:MM [cal] event_title — action`
@@ -204,36 +189,21 @@ Morning Routine acquires exclusive lock. Other sessions get 409 on PUT/PATCH (GE
204
189
 
205
190
  PUT today.md must contain the H1 date line, day-type header quote, and all six sections in order.
206
191
 
207
- ## today.md API (subset of context API)
208
-
209
- Body submission follows the rule in `_safety.md` "Daemon-API body
210
- submission": small section PATCHes use inline `-d '{...}'`; full-file
211
- PUT uses the stdin heredoc `-d @- <<'JSON'` shape because the body
212
- runs multi-KB. Add `X-Lock-Id: <today_write_lock_id>` on every
213
- PUT / PATCH when that tag is in your context.
214
-
215
- ```bash
216
- # Read
217
- curl -s http://localhost:8321/api/context/today
218
-
219
- # Full replace Morning Routine only. Multi-KB body heredoc.
220
- curl -s -X PUT http://localhost:8321/api/context/today \
221
- -H 'Content-Type: application/json' \
222
- -H 'X-Lock-Id: <today_write_lock_id>' \
223
- -d @- <<'JSON'
224
- {"content":"# 2026-04-02 (Thursday)\n> Day type: Weekday | Work focus: on | Study focus: on | Personal focus: on\n\n## User Schedule\n- 14:00–15:00 Design review [work]\n\n## User Tasks\n- [ ] 11:00 Finalize Q2 draft [work]\n\n## Agent Plan\n- [ ] 08:55 DM reminder: standup [work] →DM\n\n## Agent Notes\n\n## Agent Log\n- 04:00 Morning Routine completed (day-type: Weekday)\n\n## Handoff\n- (none)\n"}
225
- JSON
226
-
227
- # Section operation — small body, inline `-d` is fine.
228
- curl -s -X PATCH http://localhost:8321/api/context/today \
229
- -H 'Content-Type: application/json' \
230
- -H 'X-Lock-Id: <today_write_lock_id>' \
231
- -d '{"section":"agent_log","mode":"append","content":"- 09:35 ..."}'
232
- ```
233
-
234
- The H1 date and the `> Day type:` quote line are validated by exact
235
- regex on PUT — keep both English-shaped exactly as the template. See
236
- context skill for the full endpoint reference.
192
+ ## today.md API
193
+
194
+ The generic GET / PUT / PATCH / DELETE surface — modes, fields, error
195
+ envelopes, body-submission shape is documented in the **context**
196
+ skill `references/api.md`. today.md-specific rules layered on top:
197
+
198
+ - **Lock.** `today.md` is locked by the Morning Routine. Include
199
+ `X-Lock-Id: <today_write_lock_id>` on every PUT / PATCH when the
200
+ tag is in your context; other sessions get `409
201
+ today_write_lock_held` while the lock is held.
202
+ - **Skeleton validators.** PUT is rejected (400) if line 1 fails the
203
+ H1 date regex or line 2 fails the day-type quote regex (see §"Line 1
204
+ which date?" and §"Header line day-type filter" above). PUT is
205
+ rejected (422) if line 1's date disagrees with the daemon's current
206
+ agent-day the error echoes both values.
237
207
 
238
208
  ## Knowledge map — section shape (auto-curated)
239
209
 
@@ -0,0 +1,113 @@
1
+ ---
2
+ kind: reference
3
+ name: agent-plan-lifecycle
4
+ description: Agent Plan close-the-loop lifecycle for scheduled.task / scheduled.dm sessions — execute, log, flip [x], handle missing rows / failures / lock retries.
5
+ ---
6
+
7
+ # Agent Plan lifecycle — close the loop
8
+
9
+ This reference is consumed by `scheduled.task`, `scheduled.dm`,
10
+ `routine.morning_routine`, and `routine.evening_review` — every event
11
+ that may flip an Agent Plan row. DM handlers and hourly checks never
12
+ flip Agent Plan rows; if you are not one of the listed events, the
13
+ lifecycle below does not apply to your turn.
14
+
15
+ ## Why flip inside the scheduled task, not later?
16
+
17
+ The user may DM at 09:00 asking "did you send the reminder?" — if the
18
+ matching Agent Plan row is still `[ ]`, the DM handler cannot tell
19
+ whether the reminder was sent. The flip must land in the same turn as
20
+ the action it represents, before the session exits.
21
+
22
+ ## Steps (when spawned by `scheduled.task` for an Agent Plan row)
23
+
24
+ 1. **Execute the task.** Send the DM, fire the notification, run the
25
+ check-in — whatever the row's action text describes. The row's
26
+ trigger tag (`→DM`, `→notify`, `→check-in`, `→wake`) selects the
27
+ API surface (see `today` skill §"Entry formats").
28
+
29
+ 2. **Append an Agent Log entry** describing the outcome:
30
+
31
+ ```
32
+ - HH:MM [agent_plan] <action> — <outcome>
33
+ ```
34
+
35
+ Outcomes (use exactly one, lowercase):
36
+ - `DM sent`
37
+ - `notify sent`
38
+ - `check-in done`
39
+ - `wake fired`
40
+ - `skipped (<reason>)` — e.g. `skipped (user in meeting)`,
41
+ `skipped (focus off)`, `skipped (deduped)`
42
+ - `failed: <reason>` — short, single-line, no stack trace
43
+
44
+ Agent Log entries are mandatory before the flip, not after. If the
45
+ PATCH that flips the row to `[x]` fails, the log entry is the only
46
+ record that the action ran.
47
+
48
+ 3. **Read today.md, locate the matching Agent Plan row** (match HH:MM
49
+ + action text), and flip `[ ]` → `[x]`:
50
+
51
+ ```bash
52
+ curl -s http://localhost:8321/api/context/today
53
+ # Find the agent_plan section. Edit the matching row's checkbox.
54
+ # Then PATCH the full updated section body:
55
+ curl -s -X PATCH http://localhost:8321/api/context/today \
56
+ -H 'Content-Type: application/json' \
57
+ -H 'X-Lock-Id: <today_write_lock_id>' \
58
+ -d '{"section": "agent_plan", "mode": "replace", "content": "<full merged section>"}'
59
+ ```
60
+
61
+ **Read-before-write is mandatory** — `PATCH mode: "replace"` replaces
62
+ the entire section body. Send only the flipped row and you erase
63
+ every other Agent Plan row.
64
+
65
+ 4. **If the Agent Plan row is missing** (user hand-edited, row was
66
+ pruned by Evening Review, race with a concurrent rewrite), log
67
+ `skipped (row_missing)` to Agent Log and exit. Do not append a
68
+ new row to back-fill — the row's absence is informative state.
69
+
70
+ ## Flip-to-[x] cardinality rule
71
+
72
+ **Always flip to `[x]`, even for skips and failures.** The cardinality
73
+ rule is: every Agent Plan row reaches exactly one terminal state per
74
+ agent-day, and that state is `[x]`. Annotate the non-success cases in
75
+ parentheses appended to the action text:
76
+
77
+ | Outcome | Row content after flip |
78
+ |---|---|
79
+ | Success | `- [x] HH:MM <action> [category] →<trigger>` (no annotation) |
80
+ | Skip | `- [x] HH:MM <action> [category] →<trigger> (skipped: <reason>)` |
81
+ | Failure | `- [x] HH:MM <action> [category] →<trigger> (failed: <reason>)` |
82
+
83
+ Leaving rows as `[ ]` is a bug: Morning Routine's reconciliation
84
+ treats unflipped past rows as "scheduler dropped the wake", which
85
+ triggers self-recovery and inflates the agent-actions audit.
86
+
87
+ ## Lock retry rules
88
+
89
+ The Morning Routine holds the `today.md` write lock. Other sessions
90
+ get `409 today_write_lock_held` on PUT / PATCH while the lock is held.
91
+
92
+ - Detect by the response body `{"error":"lock_held"}` or by the
93
+ status code 409 alone.
94
+ - Retry policy: 30 s back-off, max 3 attempts. If the third attempt
95
+ also returns 409, log `loop-closeout deferred (lock_held)` to Agent
96
+ Log and exit. The next Evening Review reconciliation will catch the
97
+ un-flipped row.
98
+ - Do NOT retry without the `X-Lock-Id` header when the tag is in
99
+ context. Sending a PATCH without the header during a held-lock
100
+ window returns 409 even though you would have been allowed in
101
+ during a no-lock window.
102
+
103
+ ## What this lifecycle does NOT cover
104
+
105
+ - Agent Notes flavors and their flips (`Profile question (latent)` ↔
106
+ `Profile question (asked HH:MM)`) — those live in the
107
+ `user-interview` skill, not in this lifecycle.
108
+ - The schedule.approaching → Agent Notes / Agent Log format — that
109
+ is in the `today` skill body (§"schedule.approaching → Agent Notes
110
+ + Agent Log"), not here. The 15-minute firing gate is the daemon's,
111
+ not the skill's.
112
+ - The Morning Routine's initial population of Agent Plan rows — see
113
+ the morning routine task-flow.