@aitne-sh/aitne 0.1.5 → 0.1.7

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 (77) hide show
  1. package/README.md +295 -479
  2. package/agent-assets/agent-profiles/_safety.md +17 -2
  3. package/agent-assets/agent-profiles/routine-fetch-window.md +75 -40
  4. package/agent-assets/agent-profiles/wiki-agent.md +19 -0
  5. package/agent-assets/docs/features/messaging/bang-commands.md +161 -0
  6. package/agent-assets/docs/features/messaging/overview.md +3 -0
  7. package/agent-assets/docs/features/wiki/commands.md +222 -0
  8. package/agent-assets/docs/features/wiki/overview.md +145 -0
  9. package/agent-assets/docs/getting-started/03-what-can-this-do.md +18 -0
  10. package/agent-assets/docs/glossary.md +34 -0
  11. package/agent-assets/docs/guides/budget-and-cost-for-wiki.md +123 -0
  12. package/agent-assets/docs/guides/build-your-wiki.md +99 -0
  13. package/agent-assets/docs/guides/explore-with-trace-and-connect.md +169 -0
  14. package/agent-assets/docs/guides/maintain-wiki-health.md +168 -0
  15. package/agent-assets/docs/guides/multiple-wikis-for-multiple-domains.md +192 -0
  16. package/agent-assets/docs/guides/pause-the-agent.md +10 -3
  17. package/agent-assets/docs/guides/use-an-existing-obsidian-vault.md +156 -0
  18. package/agent-assets/docs/reference/cli-commands.md +24 -1
  19. package/agent-assets/docs/troubleshooting/wiki-ingest-full-blocked.md +96 -0
  20. package/agent-assets/docs/troubleshooting/wiki-write-failed.md +82 -0
  21. package/agent-assets/skills/context/SKILL.md +288 -17
  22. package/agent-assets/skills/external-services/SKILL.delegated.claude.md +2 -2
  23. package/agent-assets/skills/external-services/SKILL.delegated.codex.md +3 -3
  24. package/agent-assets/skills/external-services/SKILL.delegated.gemini.md +6 -6
  25. package/agent-assets/skills/external-services/SKILL.md +5 -3
  26. package/agent-assets/skills/external-services/SKILL.native.claude.md +49 -58
  27. package/agent-assets/skills/external-services/SKILL.native.codex.md +50 -58
  28. package/agent-assets/skills/external-services/SKILL.native.gemini.md +53 -56
  29. package/agent-assets/skills/mail/SKILL.md +5 -5
  30. package/agent-assets/skills/mail/SKILL.native.claude.md +57 -65
  31. package/agent-assets/skills/mail/SKILL.native.codex.md +73 -75
  32. package/agent-assets/skills/mail/SKILL.native.gemini.md +80 -75
  33. package/agent-assets/skills/management-task-register/SKILL.md +3 -3
  34. package/agent-assets/skills/notion/SKILL.native.claude.md +78 -82
  35. package/agent-assets/skills/notion/SKILL.native.codex.md +78 -80
  36. package/agent-assets/skills/notion/SKILL.native.gemini.md +91 -90
  37. package/agent-assets/skills/observations/SKILL.md +104 -14
  38. package/agent-assets/skills/roadmap/SKILL.md +19 -0
  39. package/agent-assets/skills/schedule/SKILL.md +44 -3
  40. package/agent-assets/skills/today/SKILL.md +25 -5
  41. package/agent-assets/skills/travel-time/SKILL.md +9 -0
  42. package/agent-assets/skills/wiki/wiki-ask/SKILL.md +32 -0
  43. package/agent-assets/skills/wiki/wiki-compile/SKILL.md +126 -0
  44. package/agent-assets/skills/wiki/wiki-connect/SKILL.md +75 -0
  45. package/agent-assets/skills/wiki/wiki-graduate/SKILL.md +45 -0
  46. package/agent-assets/skills/wiki/wiki-ingest/SKILL.md +182 -0
  47. package/agent-assets/skills/wiki/wiki-lint/SKILL.md +90 -0
  48. package/agent-assets/skills/wiki/wiki-trace/SKILL.md +72 -0
  49. package/agent-assets/skills/wiki/wiki-vault-rules/SKILL.md +145 -0
  50. package/agent-assets/task-flows/_partials/calendar-acquire.google_calendar.md +28 -9
  51. package/agent-assets/task-flows/_partials/calendar-acquire.outlook_calendar.md +26 -9
  52. package/agent-assets/task-flows/_partials/mail-acquire.gmail.md +51 -24
  53. package/agent-assets/task-flows/_partials/mail-acquire.outlook_mail.md +46 -16
  54. package/agent-assets/task-flows/_partials/notion-acquire.notion.md +29 -9
  55. package/agent-assets/task-flows/message.received.dm.md +35 -2
  56. package/agent-assets/task-flows/message.received.dm.native.claude.md +25 -26
  57. package/agent-assets/task-flows/message.received.dm.native.codex.md +30 -24
  58. package/agent-assets/task-flows/message.received.dm.native.gemini.md +36 -36
  59. package/agent-assets/task-flows/message.received.dm_first.md +43 -4
  60. package/agent-assets/task-flows/message.received.dm_first.native.claude.md +20 -20
  61. package/agent-assets/task-flows/message.received.dm_first.native.codex.md +22 -19
  62. package/agent-assets/task-flows/message.received.dm_first.native.gemini.md +28 -24
  63. package/agent-assets/task-flows/routine.fetch_window.md +51 -36
  64. package/agent-assets/task-flows/routine.morning_routine.md +12 -3
  65. package/agent-assets/task-flows/routine.morning_routine_initial.md +22 -1
  66. package/agent-assets/task-flows/scheduled.dm.md +477 -0
  67. package/agent-assets/task-flows/wiki.ask.md +11 -0
  68. package/agent-assets/task-flows/wiki.compile.md +28 -0
  69. package/agent-assets/task-flows/wiki.connect.md +12 -0
  70. package/agent-assets/task-flows/wiki.ingest_url.md +35 -0
  71. package/agent-assets/task-flows/wiki.lint.md +13 -0
  72. package/agent-assets/task-flows/wiki.trace.md +13 -0
  73. package/agent-assets/wiki-seeds/schemas/output.md +12 -0
  74. package/agent-assets/wiki-seeds/schemas/raw.md +13 -0
  75. package/agent-assets/wiki-seeds/schemas/wiki.md +12 -0
  76. package/agent-assets/wiki-seeds/taxonomy.md +13 -0
  77. package/package.json +21 -41
@@ -0,0 +1,96 @@
1
+ ---
2
+ schema_version: 1
3
+ slug: troubleshooting/wiki-ingest-full-blocked
4
+ title: "`!compile full` Is Blocked"
5
+ id: wiki-ingest-full-blocked
6
+ aliases:
7
+ - ingest full refused
8
+ - wiki approval pending
9
+ - wiki dirty tree refused
10
+ category: troubleshooting
11
+ summary: |
12
+ `!compile full` either refused with "uncommitted changes" (git
13
+ pre-compile gate) or returned "Sent for approval" (cost gate).
14
+ This entry tells you how to clear each branch.
15
+ section: wiki-ingest-full-blocked
16
+ tags:
17
+ - troubleshooting
18
+ - wiki
19
+ - cost
20
+ - git
21
+ status: stable
22
+ ask_examples:
23
+ - Why did !compile full refuse to run?
24
+ - Where do I approve a pending wiki compile?
25
+ - Why does !compile full want a clean git tree?
26
+ locale: en-US
27
+ created: 2026-05-12
28
+ updated: 2026-05-12
29
+ related:
30
+ - features/wiki/commands
31
+ - guides/budget-and-cost-for-wiki
32
+ - features/wiki/overview
33
+ ui_anchors:
34
+ - /settings/wiki
35
+ - /approvals
36
+ ---
37
+
38
+ # `!compile full` Is Blocked
39
+
40
+ ## What You See
41
+
42
+ You ran `!compile full` and the bang reply says either:
43
+
44
+ - "Cannot run `!compile full` — the external vault has uncommitted
45
+ changes."
46
+ - "Sent for approval. Open `/settings/wiki` → Approvals to confirm
47
+ and the compile will start."
48
+
49
+ ## "Uncommitted Changes"
50
+
51
+ This is the **git pre-compile gate** firing. Aitne refuses to start
52
+ `!compile full` on an external git-tracked vault with a dirty working
53
+ tree because the pre-compile snapshot it would create can no longer
54
+ be a clean baseline.
55
+
56
+ To proceed:
57
+
58
+ 1. `git -C <vault> status` — review the dirty paths Aitne listed.
59
+ 2. Commit or stash them: `git add -A && git commit -m "wip"` or
60
+ `git stash -u`.
61
+ 3. Re-run `!compile full`. On a clean tree Aitne runs
62
+ `git add -A && git commit -m "aitne wiki: pre-compile snapshot <ts>"`
63
+ automatically.
64
+
65
+ If you don't want the auto-commit, disable **Auto-commit before
66
+ `!compile full`** in **Settings → Wiki** (only visible for
67
+ git-tracked external vaults). Aitne will then run without taking a
68
+ snapshot — and the approval-gate DM will explicitly say "no git
69
+ backup taken".
70
+
71
+ ## "Sent for Approval"
72
+
73
+ The cost estimator's pessimistic bound (`2× expected`) exceeded the
74
+ per-workspace approval threshold (default $2.00). To approve:
75
+
76
+ 1. Open the dashboard.
77
+ 2. Go to **Settings → Wiki → Approvals** (or hit the **Approvals**
78
+ notification card directly).
79
+ 3. Review the estimate. Click **Approve** to run, **Deny** to skip.
80
+
81
+ If the estimate looks wrong, you have three levers:
82
+
83
+ - **Lower the avg input tokens** — the default 1500 is conservative
84
+ for short raw notes; check whether your typical raw note is
85
+ smaller.
86
+ - **Switch the `wiki.compile` model** to a lite tier in the
87
+ per-command selector. Sonnet's per-token cost is the dominant
88
+ variable.
89
+ - **Raise the threshold** in **Settings → Wiki** so routine
90
+ recompiles don't queue an approval.
91
+
92
+ ## "Not Enabled"
93
+
94
+ If `!compile full` replies "Wiki is not enabled", you have no active
95
+ workspace row. Run **Enable Internal Workspace** (or **Probe &
96
+ Create External**) on `/settings/wiki` first.
@@ -0,0 +1,82 @@
1
+ ---
2
+ schema_version: 1
3
+ slug: troubleshooting/wiki-write-failed
4
+ title: Wiki Write Failed
5
+ id: wiki-write-failed
6
+ aliases:
7
+ - wiki EPERM
8
+ - obsidian cli not detected
9
+ - wiki write strategy stuck on cli
10
+ category: troubleshooting
11
+ summary: |
12
+ A wiki bang command failed to write, or the dashboard health card
13
+ shows a non-`fs` strategy with `cliAvailable: false`. Triage the
14
+ filesystem permission vs. the Obsidian CLI fallback path.
15
+ section: wiki-write-failed
16
+ tags:
17
+ - troubleshooting
18
+ - wiki
19
+ - obsidian
20
+ status: stable
21
+ ask_examples:
22
+ - Why can't the wiki write to my Obsidian vault?
23
+ - Why does the wiki health card say cliAvailable false?
24
+ - How do I retry the write-strategy probe?
25
+ locale: en-US
26
+ created: 2026-05-12
27
+ updated: 2026-05-12
28
+ related:
29
+ - features/wiki/overview
30
+ - guides/use-an-existing-obsidian-vault
31
+ ---
32
+
33
+ # Wiki Write Failed
34
+
35
+ ## What You See
36
+
37
+ A wiki bang command (`!ingest`, `!compile`) reports a write failure in
38
+ the daemon log, or the dashboard `/api/wiki/:ws/health` endpoint
39
+ surfaces a non-`fs` strategy with `cliAvailable: false`.
40
+
41
+ ## Quick Checklist
42
+
43
+ 1. **Internal mode?** Internal workspaces always write via the local
44
+ filesystem. If you see EPERM there, the underlying issue is
45
+ `dataDir` permissions — fix those rather than thinking the wiki
46
+ is the problem.
47
+ 2. **External mode + `write_strategy=fs`?** The filesystem rejected
48
+ the write. Common causes:
49
+ - iCloud sandbox (most frequent on macOS).
50
+ - Read-only volume / snapshot mount.
51
+ - Filesystem ACL on the parent directory.
52
+ 3. **External mode + `write_strategy=cli`?** The probe already fell
53
+ back to the Obsidian CLI but the CLI is unavailable. See the CLI
54
+ checklist below.
55
+
56
+ ## The CLI Fallback Path
57
+
58
+ When direct fs writes fail with `EPERM` / `EACCES` / `EROFS` /
59
+ `EBUSY`, Aitne falls back to the official Obsidian CLI (1.12+).
60
+ Requirements:
61
+
62
+ - Obsidian installed (1.12 or later).
63
+ - **Settings → General → Command line interface** enabled inside
64
+ Obsidian.
65
+ - The Obsidian app is running (the CLI is a thin client to the live
66
+ process).
67
+ - The `obsidian` binary is on `PATH` (`~/.zprofile` is auto-updated
68
+ when CLI is enabled).
69
+
70
+ If any of these is missing, the daemon surfaces a structured error:
71
+
72
+ | Error code | Meaning |
73
+ |---|---|
74
+ | `EWIKI_CLI_UNAVAILABLE` | Aitne's `ObsidianService` is not configured. Open `Settings → Integrations → Obsidian` and complete the pairing. |
75
+ | `EWIKI_CLI_NOT_RUNNING` | Obsidian is not running. Launch the app and retry. |
76
+
77
+ ## Force a Re-Probe
78
+
79
+ If you've fixed the underlying issue (granted iCloud permission,
80
+ mounted the disk read-write) but the cached strategy is still `cli`,
81
+ flip the dropdown in **Settings → Wiki → Write strategy** back to
82
+ `auto`. The next write probes again and persists the fresh outcome.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: context
3
- description: Load when reading or writing project notes, weekly/monthly summaries, or agent-journal.md. Owns GET/PATCH for context files except today.md and roadmap.md, which use their dedicated skills.
3
+ description: Load when reading or writing project notes, weekly/monthly summaries, or agent/journal.md. Owns GET/PATCH for context files except today.md and roadmap.md, which use their dedicated skills.
4
4
  allowed-tools:
5
5
  - Bash(curl *)
6
6
  - Read
@@ -95,12 +95,58 @@ can copy the same pattern into their own skills.
95
95
 
96
96
  **Decision tree:**
97
97
 
98
- 1. **Existing or new?** `GET /api/context/list/projects` and match
99
- the user's wording against returned filenames (slug stem) and, if
100
- needed, against the H1 / title of each candidate via
101
- `GET /api/context/projects/<slug>`. If multiple candidates match,
102
- prefer the one with the closest slug stem; if still ambiguous,
103
- ask *"is this for `<slug-A>` or `<slug-B>`?"* before writing.
98
+ 1. **Existing or new? (Plus decline-marker pre-check.)**
99
+ - `GET /api/context/list/projects` and match the user's wording
100
+ against returned filenames (slug stem) and, if needed, against
101
+ the H1 / title of each candidate via
102
+ `GET /api/context/projects/<slug>`. If multiple candidates
103
+ match, prefer the one with the closest slug stem; if still
104
+ ambiguous, ask *"is this for `<slug-A>` or `<slug-B>`?"* before
105
+ writing.
106
+ - **Decline-marker pre-check (Goal 3 — never ask twice).** Before
107
+ classifying a no-match as new, compute the candidate slug and
108
+ read `agent/journal.md ## Declined Intents`:
109
+ ```bash
110
+ curl -s "http://localhost:8321/api/context/agent/journal" \
111
+ | jq -r '.content // ""' \
112
+ | awk '/^## Declined Intents/{f=1;next} f && /^## /{exit} f'
113
+ ```
114
+ (404 from the GET means the journal file does not yet exist →
115
+ no marker can exist → treat as "no marker present" and continue
116
+ to the no-match path.) If any line under that section contains
117
+ `create_project:<slug>` (the dedup_key shape — see §"Reply
118
+ branches" below), the user previously declined this exact
119
+ intent. The default behaviour is **skip silently** — do NOT
120
+ re-ask, do NOT schedule a confirm, do NOT write.
121
+
122
+ **Reversal-signal carve-out (rare).** If the user's current DM
123
+ contains an unambiguous reversal phrase for this exact intent,
124
+ treat the DM as a fresh affirmative. Run the
125
+ §"Decline-marker reversal" recipe below (remove the matching
126
+ marker line, then PUT the project file per Step 4). Pattern
127
+ shape: explicit verb of resumption + the topic + action
128
+ consent. Match on intent, not surface vocabulary — any
129
+ language qualifies.
130
+
131
+ Counts as reversal (do the recipe):
132
+ - *"actually let's start that LA project after all"*
133
+ - *"go ahead and track LA PM"*
134
+ - *"changed my mind, let's do it"*
135
+
136
+ Does NOT count as reversal (skip silently, marker stays):
137
+ - bare re-mention without an action verb: *"still thinking
138
+ about LA PM"*, *"made a bit of progress on LA"*
139
+ - status update on the topic without consent: *"LA classes
140
+ started"*, *"midterm was hard"*
141
+ - ambiguous "maybe" / non-commitment: *"maybe I'll track it
142
+ eventually"*, *"might revisit this later"*
143
+
144
+ The examples are English for prompt clarity only; recognise the
145
+ same shapes in any language the user writes in. Bias
146
+ conservative — when in doubt, skip silently and wait for a
147
+ clearer signal. A missed reversal costs nothing (user can
148
+ re-state explicitly); a false reversal silently overwrites a
149
+ deliberate "no".
104
150
 
105
151
  2. **Existing match → append a dated bullet.** Default section is
106
152
  `## Log` (or whatever section the file already uses for time-ordered
@@ -128,13 +174,84 @@ can copy the same pattern into their own skills.
128
174
  frontmatter parser is line-scalar so per-key PATCH is not available.
129
175
  Bump `updated` to today on the same write.
130
176
 
131
- 3. **No match DM-confirm before any write.** Reply with one short
132
- sentence: *"Create project `<slug>`? (one-line summary:
133
- <paraphrase>)"*. Wait for an explicit yes. This mirrors the
134
- morning-routine inbox-triage gate (`routine.morning_routine.md`,
135
- "High-risk triggers DM for confirmation before writing") so the
136
- two paths can't fork — silently inferring a slug and writing is
137
- forbidden regardless of how the project entered the system.
177
+ **Cross-path cancellation.** If this existing-match write resolves
178
+ what was previously a "no match" confirm path (i.e. a confirm row
179
+ was scheduled and the user has since clarified the project is the
180
+ same as an existing one), delete the pending confirm rows before
181
+ completing the write. See §"Reply branches" below.
182
+
183
+ 3. **No match schedule a confirm DM (do NOT ask inline).**
184
+ The universal "no topic-pivoting trailing question" rule in
185
+ `message.received.dm{,_first}.md` Step 3 forbids appending a
186
+ project-creation ask to a content-rich reply. Instead of inline
187
+ "Create project `<slug>`? (yes/no)" and waiting on the same DM
188
+ turn, schedule a `confirm:` sub-flow row for a natural follow-up
189
+ moment (default: next morning briefing slot, or `<current_time>
190
+ + 4h` if the DM landed well before quiet hours):
191
+
192
+ **a. Pre-flight idempotency check.** Compute the dedup_key
193
+ `create_project:<slug>` and ensure no row is already pending
194
+ (Goal 3):
195
+
196
+ ```bash
197
+ curl -s "http://localhost:8321/api/schedule?status=pending,running" \
198
+ | jq --arg k "create_project:<slug>" \
199
+ '[.items[] | select(.taskContext.confirm_dedup_key == $k)] | length'
200
+ ```
201
+
202
+ If the count is `≥ 1`, a confirm is already queued — do NOT
203
+ schedule a duplicate. Log to today.md `## Agent Log` and proceed:
204
+ ```
205
+ - HH:MM [confirm] skipped create_project:<slug>: row already pending
206
+ ```
207
+
208
+ **b. Schedule the confirm.** Use the shape documented in
209
+ `scheduled.dm.md` §"Confirmation follow-up":
210
+
211
+ ```bash
212
+ curl -s -X POST http://localhost:8321/api/schedule \
213
+ -H 'Content-Type: application/json' \
214
+ -d @- <<JSON
215
+ {
216
+ "time": "<next morning-briefing slot, or current_time + 4h — ISO 8601 with offset>",
217
+ "taskType": "dm_session",
218
+ "description": "confirm:create_project:<slug> — track <paraphrase> as a project?",
219
+ "model": "sonnet",
220
+ "taskContext": {
221
+ "scheduledBy": "dm_handler.project_creation_gate",
222
+ "sub_flow": "confirm",
223
+ "confirm_id": "<short uuid v4 first 8 chars>",
224
+ "confirm_dedup_key": "create_project:<slug>",
225
+ "confirm_hint": "create project \"<slug>\"? (origin: <one-line paraphrase of user's DM>)",
226
+ "confirm_recent_window_hours": 24,
227
+ "confirm_attempt": 1,
228
+ "confirm_max_attempts": 2,
229
+ "confirm_defer_count": 0,
230
+ "confirm_max_defers": 3,
231
+ "confirm_decline_marker": {
232
+ "path": "agent/journal.md",
233
+ "section": "declined_intents",
234
+ "match": "create_project:<slug>"
235
+ },
236
+ "confirm_slot": {
237
+ "path": "projects/<slug>.md"
238
+ },
239
+ "importance": "low"
240
+ }
241
+ }
242
+ JSON
243
+ ```
244
+
245
+ The `confirm_slot.path` (just the file path, no section / anchor)
246
+ makes the fire-time slot-filled probe abort if the project file
247
+ already exists by the time the confirm fires — covering the case
248
+ where the user volunteers an affirmative shape between scheduling
249
+ and fire.
250
+
251
+ **c. Do NOT inline-ask.** The DM reply must remain in the user's
252
+ thread per the universal rule. The confirm sub-flow will surface
253
+ the question at the next natural moment. Silently inferring a
254
+ slug and writing without confirmation remains forbidden.
138
255
 
139
256
  4. **On confirmed creation → PUT the file.** Required + conventional
140
257
  frontmatter and an H1:
@@ -150,6 +267,152 @@ can copy the same pattern into their own skills.
150
267
  Add `due`, `stakeholders`, `next_milestone`, `tags` only when the
151
268
  user supplied them. Do not invent values.
152
269
 
270
+ 5. **Reply branches — how the user's response to a scheduled confirm
271
+ is handled.** When the confirm sub-flow fires (`scheduled.dm.md`
272
+ ## Confirmation follow-up) and the user replies, the reply routes
273
+ through `message.received.dm.md` as usual; the dispatcher injects
274
+ `<conversation_history>` so the agent sees both the confirm DM and
275
+ the user's reply in the same turn. This gate is the writer for all
276
+ three branches; every branch is REQUIRED.
277
+
278
+ - **Affirmative** — user gave a clear positive answer to the
279
+ confirm question. Examples: *"yes"*, *"go ahead"*,
280
+ *"track it as X"*, *"sounds good"*, *"please do"*. Match on
281
+ intent, not surface vocabulary — recognise the same shape in
282
+ any language the user writes in. Execute the "On confirmed
283
+ creation → PUT the file" path in Step 4. Then run
284
+ §"Cross-path cancellation" below (delete any sibling confirm
285
+ rows with matching `confirm_dedup_key` so a parallel queued
286
+ row does not re-fire).
287
+
288
+ - **Counter-proposal** — user supplied new info ("call it
289
+ `la-pm` instead", "actually make it the syllabus dossier",
290
+ "rename it to `la-pm`").
291
+ Use the user's wording, not your original paraphrase:
292
+ re-compute the slug from the corrected wording, PUT the file,
293
+ then cancel pending confirm rows whose `confirm_dedup_key`
294
+ matches **either** the original or the new slug:
295
+ - `create_project:<original-slug>` — the chained-fire successor
296
+ inherits the original key, so this is the primary sweep.
297
+ - `create_project:<new-slug>` — a separate gate fire from an
298
+ earlier DM may have queued a confirm with the new slug (e.g.
299
+ the user previously paraphrased the same project differently
300
+ and that fire's confirm has not yet aborted). The fire-time
301
+ `slot-filled` probe would catch this (the project file now
302
+ exists), but a symmetric sweep here saves a wasted session.
303
+
304
+ Run the §"Cross-path cancellation" loop twice — once with each
305
+ key — or merge the two `select`s in jq.
306
+
307
+ - **Decline** — user wrote a clear negative answer to the
308
+ confirm question.
309
+
310
+ Counts as decline: *"no"*, *"don't bother"*, *"not now"*,
311
+ *"later"*, *"skip it"*, *"forget it"*, *"drop it"*.
312
+
313
+ Does NOT count as decline (treat as ambiguous → no DM action
314
+ this turn, marker NOT written, sweep NOT run; the chain's
315
+ softened re-check will handle it):
316
+ - non-answer continuations: *"hmm"*, *"not sure"*, *"I don't
317
+ know"*
318
+ - questions back to the agent: *"why are you asking?"*,
319
+ *"what would that involve?"* — these are clarification
320
+ requests, not declines
321
+
322
+ The examples are English for prompt clarity only; recognise
323
+ the same shapes in any language the user writes in. On a true
324
+ decline, do NOT write the project file. Two mandatory writes:
325
+
326
+ a. **Write the decline marker** to
327
+ `agent/journal.md ## Declined Intents`. Three cases — file
328
+ missing entirely, file present but section missing, file +
329
+ section both present — are handled in one read-then-branch
330
+ sequence:
331
+
332
+ ```bash
333
+ # 1. GET. HTTP 404 means the journal file does not yet exist.
334
+ body=$(curl -sS -w '\n%{http_code}' "http://localhost:8321/api/context/agent/journal")
335
+ status=$(printf '%s\n' "$body" | tail -n1)
336
+ content=$(printf '%s\n' "$body" | sed '$d' | jq -r '.content // ""' 2>/dev/null)
337
+
338
+ marker_line='- 2026-05-12 [create_project:<slug>] user declined inline (DM)'
339
+
340
+ if [ "$status" = "404" ]; then
341
+ # Case A — file missing. CREATE_ONLY_PUT is enabled for
342
+ # agent/journal, so PUT creates the file in a single call.
343
+ # Include both the H1 and the Declined Intents section.
344
+ curl -s -X PUT "http://localhost:8321/api/context/agent/journal" \
345
+ -H 'Content-Type: application/json' \
346
+ -d "$(jq -n --arg m "$marker_line" '{content: "# Agent Journal\n\n## Declined Intents\n\($m)\n"}')"
347
+ elif printf '%s' "$content" | grep -q '^## Declined Intents'; then
348
+ # Case B — file + section present. Append a bullet to the
349
+ # existing section.
350
+ curl -s -X PATCH "http://localhost:8321/api/context/agent/journal" \
351
+ -H 'Content-Type: application/json' \
352
+ -d "$(jq -n --arg m "$marker_line" '{section:"declined_intents",mode:"append",content:$m}')"
353
+ else
354
+ # Case C — file present but section missing. append_to_file
355
+ # adds the section header + bullet to the end of the file.
356
+ curl -s -X PATCH "http://localhost:8321/api/context/agent/journal" \
357
+ -H 'Content-Type: application/json' \
358
+ -d "$(jq -n --arg m "$marker_line" '{mode:"append_to_file",content:"\n## Declined Intents\n\($m)\n"}')"
359
+ fi
360
+ ```
361
+
362
+ Use today's date (resolve via `<current_time>`) for the
363
+ marker line.
364
+
365
+ b. **Cancellation** — see §"Cross-path cancellation" below.
366
+ The decline must also delete any pending confirm rows with
367
+ the matching `confirm_dedup_key`, otherwise the chained-fire
368
+ successor will re-ask 24h later despite the explicit "no".
369
+
370
+ The decline marker is what the next DM-intent detection (Step 1
371
+ above) consults — without it, the next time the user mentions LA
372
+ PM master's, this gate would compute the same slug, see no
373
+ existing project, and schedule another confirm. The marker is
374
+ how Goal 3 ("never ask the same question twice") survives across
375
+ sessions.
376
+
377
+ **Decline-marker reversal.** When the user later volunteers an
378
+ unambiguously affirmative shape ("OK now let's start that LA
379
+ project after all", "actually go ahead and track LA PM") — either
380
+ inline in a fresh DM (the carve-out in Step 1) or as a reply to a
381
+ confirm DM — run this recipe instead of skipping:
382
+
383
+ 1. GET `agent/journal.md`, parse the `## Declined Intents`
384
+ section, drop the line whose bracketed dedup_key matches
385
+ `create_project:<slug>`, and PATCH the section with
386
+ `mode: "replace"` carrying the rebuilt body (the other lines
387
+ preserved byte-for-byte). If the rebuilt section is empty,
388
+ replace with the empty string — `mode: "replace"` accepts an
389
+ empty `content` and leaves the heading in place.
390
+ 2. Proceed with Step 4 (PUT the project file).
391
+
392
+ Without the reversal, a previously-declined project would stay
393
+ dormant forever.
394
+
395
+ **Cross-path cancellation (required for affirmative,
396
+ counter-proposal, and decline branches).** When this gate
397
+ commits durable state via ANY of the three branches above, sweep
398
+ pending confirm rows with the same dedup_key so a queued
399
+ successor does not re-fire:
400
+
401
+ ```bash
402
+ curl -s "http://localhost:8321/api/schedule?status=pending,running" \
403
+ | jq -r --arg k "create_project:<slug>" \
404
+ '.items[] | select(.taskContext.confirm_dedup_key == $k) | .id' \
405
+ | while read -r id; do
406
+ curl -s -X DELETE "http://localhost:8321/api/schedule/$id" >/dev/null
407
+ done
408
+ ```
409
+
410
+ Apply this after the affirmative write, after the counter-proposal
411
+ write (with the ORIGINAL slug, not the corrected one), and after
412
+ the decline marker write. The cost is one GET plus zero-to-one
413
+ DELETE per write — bounded by "gate wrote something", which is
414
+ rare relative to inbound DM volume.
415
+
153
416
  **Slug grammar (convention only — no API-level validation today):**
154
417
  - match `^[a-z0-9][a-z0-9-]*[a-z0-9]$` (or a single `[a-z0-9]`)
155
418
  - ≤ 64 chars
@@ -235,11 +498,19 @@ curl -s -X PATCH http://localhost:8321/api/context/today \
235
498
 
236
499
  | Field | Type | Description |
237
500
  |---|---|---|
238
- | `section` | string | snake_case of heading. **Omit for `append_to_file`**. |
239
- | `mode` | `append` \| `replace` \| `clear` \| `append_to_file` | Default `append` |
240
- | `content` | string | Ignored for `clear` |
501
+ | `section` | string | snake_case of heading. **Omit for `append_to_file`**; required for every other mode. |
502
+ | `mode` | `append` \| `replace` \| `clear` \| `clear_before` \| `append_to_file` | Default `append` |
503
+ | `content` | string | Ignored for `clear` / `clear_before` |
504
+ | `cutoff` | string | **Required when `mode: "clear_before"`.** Format `YYYY-MM-DD HH:MM:SS` (zero-padded). Removes bullet rows whose `- [YYYY-MM-DD HH:MM:SS]` timestamp is ≤ cutoff. |
505
+ | `maxEntries` | number | Optional for `mode: "append"`. After appending, trim oldest bullet entries from the top of the section body so at most `maxEntries` bullets remain. Non-bullet lines are preserved. |
241
506
 
242
507
  `append_to_file`: appends to end of file (no `section`). Use for agent/journal.md.
508
+ `clear_before`: rolling-log trim — pass a SQLite-format `cutoff` to drop bullets older than it. Non-bullet lines are preserved.
509
+
510
+ **Error responses worth knowing:**
511
+ - `400 {error:"section_not_found", section, availableSections:[...]}` — the section name didn't match. The `availableSections` array lists every section heading the file actually has (snake_cased); pick the closest match from that list before retrying. Do NOT retry the same `section` value.
512
+ - `400 {error:"validation_error", message, path}` — content failed the file-specific validator (e.g. today.md day-type line, roadmap.md preparation-timeline row). The `message` field names the offending line and (where applicable) the expected shape.
513
+ - `400 {error:"cutoff_required", message}` — `clear_before` was called without a valid `cutoff`.
243
514
 
244
515
  ### GET /api/context/list/:dir — List files
245
516
  `curl -s http://localhost:8321/api/context/list/projects` → `{ "files": [{ "name", "lastModified" }] }`
@@ -16,8 +16,8 @@ Your DM session runs on Claude Code. **Google Calendar** access has been
16
16
  delegated to a different backend whose Calendar connector is signed in.
17
17
  You describe Calendar intent in natural language; the daemon spawns the
18
18
  delegated backend, lets it pick the right MCP tool, and returns a
19
- schema-validated JSON result. The `mcp__claude_ai_Google_Calendar__*`
20
- tools are not on the inventory here (Calendar is not delegated to
19
+ schema-validated JSON result. The hosted Google Calendar connector
20
+ tools are not on this session's tool menu (Calendar is not delegated to
21
21
  Claude in this session).
22
22
 
23
23
  The rest of this skill — Obsidian, GitHub, recurring schedules, one-shot
@@ -16,9 +16,9 @@ Your DM session runs on Codex CLI. **Google Calendar** access has been
16
16
  delegated to a different backend whose Calendar connector is signed
17
17
  in. You describe Calendar intent in natural language; the daemon
18
18
  spawns the delegated backend, lets it pick the right MCP tool, and
19
- returns a schema-validated JSON result. The
20
- `mcp__codex_apps__google_calendar._*` tools are not on the inventory
21
- for these calls (Calendar is not delegated to Codex here).
19
+ returns a schema-validated JSON result. The in-session Google Calendar
20
+ connector your Codex harness might expose is not on the inventory for
21
+ these calls (Calendar is not delegated to Codex here).
22
22
 
23
23
  The rest of this skill — Obsidian, GitHub, recurring schedules, one-shot
24
24
  scheduling, skills CRUD — works identically to the direct-mode body.
@@ -15,12 +15,12 @@ Base URL: `http://localhost:8321`. All calls via `curl -s` with
15
15
  Your DM session runs on Gemini CLI. **Google Calendar** access has been
16
16
  delegated to a *different* backend (Claude or Codex) whose Calendar
17
17
  connector is signed in. Same-backend setups (DM on Gemini, Calendar
18
- also delegated to Gemini via the `google-workspace` extension) never
19
- load this skill — the resolver returns no body and the agent uses
20
- Gemini's native `mcp_google-workspace_calendar.*` tools directly. This
21
- skill body is the cross-backend variant only: every Calendar operation
22
- goes through the daemon proxy because the connector lives on a
23
- different backend than the one running the DM.
18
+ also delegated to Gemini via the in-session Calendar connector your
19
+ Gemini harness exposes) never load this skill — the resolver returns
20
+ no body and the agent uses its in-session Calendar connector directly.
21
+ This skill body is the cross-backend variant only: every Calendar
22
+ operation goes through the daemon proxy because the connector lives on
23
+ a different backend than the one running the DM.
24
24
 
25
25
  The rest of this skill — Obsidian, GitHub, recurring schedules, one-shot
26
26
  scheduling, skills CRUD — works identically to the direct-mode body.
@@ -61,9 +61,11 @@ Calendar's mode.
61
61
  Read the `<integration_modes>` block injected above. If
62
62
  `google_calendar="delegated"` (same-backend), the entire
63
63
  `/api/calendar/*` prefix returns `410 {"error": "integration_delegated"}`
64
- (route-prefix gate). Use the connector tools your session already holds
65
- natively`mcp__claude_ai_Google_Calendar__*` (Claude session) or
66
- `mcp__codex_apps__google_calendar._*` (Codex session).
64
+ (route-prefix gate). Use the in-session Google Calendar connector your
65
+ harness exposes your tool menu lists every available tool at session
66
+ start. The exact tool namespace depends on which connector your
67
+ harness has loaded (Claude / Codex sessions each surface Calendar under
68
+ their own connector's namespace).
67
69
 
68
70
  The Calendar section below documents the direct-mode route shapes;
69
71
  consult it for tool argument shapes (native MCP tools mirror the route