@aitne-sh/aitne 0.1.5 → 0.1.6

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 +16 -0
  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
@@ -78,6 +78,7 @@ content rules.
78
78
 
79
79
  - starts with `morning briefing` → see `## Morning briefing` below
80
80
  - starts with `profile_interview:` → see `## Profile interview` below
81
+ - starts with `confirm:` → see `## Confirmation follow-up` below
81
82
  - (future sub-flows added here)
82
83
 
83
84
  If no sub-flow matches:
@@ -148,6 +149,16 @@ the daily greeting.
148
149
  to your own backend's native Gmail MCP tools — that connector
149
150
  reads a different account than the user's delegated one.
150
151
  <!-- /mode:delegated-cross:gmail -->
152
+ <!-- mode:native:gmail -->
153
+ Non-Gmail accounts: use the `mail` skill as in direct mode.
154
+ Gmail accounts: the `/api/mail/*` per-account gate returns 410
155
+ `{"error":"integration_native"}` — use your session backend's
156
+ native Gmail MCP tool with a "10 most recent inbox" query
157
+ (`q=in:inbox`, limit/maxResults `10`). The materialized `mail`
158
+ skill body (`SKILL.native.<session-backend>.md`) lists the
159
+ per-backend tool names. The daemon does not proxy in native mode;
160
+ do NOT call `/api/integrations/gmail/exec` (also returns 410).
161
+ <!-- /mode:native:gmail -->
151
162
  <!-- mode:disabled:gmail -->
152
163
  Gmail is disabled — skip Gmail accounts entirely. Continue with
153
164
  the remaining accounts (iCloud / Outlook / Yahoo / IMAP) via the
@@ -391,3 +402,469 @@ preserve any other entries byte-for-byte, PATCH `mode=replace`. Without
391
402
  this transition, the DM-handler queue-flip (Operation 4) will see
392
403
  `state=scheduled` and treat the user's reply as unrelated, leaving the
393
404
  row open.
405
+
406
+ ---
407
+
408
+ ## Confirmation follow-up
409
+
410
+ Triggered by `{event_data[task]}` starting with `confirm:`. The DM
411
+ handler scheduled this row because a gate (e.g. project-creation, a
412
+ managed-task duplicate, a long-horizon ambiguity) was triggered during
413
+ an earlier DM but could not be asked then — either the conversation
414
+ topic was incompatible, or the user's reply was already long, or the
415
+ universal "no topic-pivoting trailing question" rule in
416
+ `message.received.dm{,_first}.md` suppressed the inline ask.
417
+
418
+ The full `taskContext` schema is documented in
419
+ `docs/design/appendices/dm-conversational-flow.md` §B1. The fields
420
+ this sub-flow reads at fire time are:
421
+
422
+ - `confirm_id` — opaque per-row identifier; logged on abort / fire
423
+ - `confirm_dedup_key` (required) — stable topic key (`<gate>:<topic>`)
424
+ - `confirm_hint` — agent-internal English brief of what to ask
425
+ - `confirm_recent_window_hours` (default 24) — DM-history scan horizon
426
+ - `confirm_attempt` (default 1) / `confirm_max_attempts` (default 2)
427
+ — retry accounting (see Step 3)
428
+ - `confirm_defer_count` (default 0) / `confirm_max_defers` (default 3)
429
+ — active-conversation-interlock deferrals (see Step 1 check 1)
430
+ - `confirm_decline_marker` (optional, `{path, section, match}`) —
431
+ file + section + match string that, when present in the named
432
+ context file, signals the user already declined
433
+ - `confirm_slot` (optional, `{path, section?, anchor?}`) — file slot
434
+ that, when filled, means the answer already landed via a different
435
+ path
436
+
437
+ ### Step 1 — Fire-time abort or defer
438
+
439
+ Four checks, run in order. **The first positive result terminates
440
+ Step 1** — either abort silent or self-defer; checks after a positive
441
+ are skipped.
442
+
443
+ #### Check 1 — Active-conversation interlock
444
+
445
+ Inspect `<recent_dm_messages window="60min">`:
446
+
447
+ - **state=very-recent** (inbound user DM in the last 5 min):
448
+ **self-defer.** The user is mid-thread. Firing now violates Goal 1
449
+ ("don't break the conversation thread") even with a Variant-B
450
+ bridge. POST a replacement schedule row at
451
+ `<current_time> + 15 min` with the SAME `confirm_dedup_key`,
452
+ `confirm_attempt` unchanged, `confirm_defer_count += 1`, and all
453
+ other `taskContext` fields inherited:
454
+
455
+ ```bash
456
+ curl -s -X POST http://localhost:8321/api/schedule \
457
+ -H 'Content-Type: application/json' \
458
+ -d @- <<JSON
459
+ {
460
+ "time": "<current_time + 15min, ISO 8601 with offset>",
461
+ "taskType": "dm_session",
462
+ "description": "confirm:<topic> — <hint>",
463
+ "model": "sonnet",
464
+ "taskContext": {
465
+ "scheduledBy": "scheduled_dm.confirm_followup.self_defer",
466
+ "sub_flow": "confirm",
467
+ "confirm_id": "<new short id>",
468
+ "confirm_dedup_key": "<inherited>",
469
+ "confirm_hint": "<inherited>",
470
+ "confirm_recent_window_hours": <inherited>,
471
+ "confirm_attempt": <inherited unchanged>,
472
+ "confirm_max_attempts": <inherited>,
473
+ "confirm_defer_count": <prior + 1>,
474
+ "confirm_max_defers": <inherited>,
475
+ "confirm_decline_marker": <inherited>,
476
+ "confirm_slot": <inherited>,
477
+ "importance": "low"
478
+ }
479
+ }
480
+ JSON
481
+ ```
482
+
483
+ If `confirm_defer_count` after increment would **exceed**
484
+ `confirm_max_defers` (default 3, max total ~45 min delay), do NOT
485
+ re-schedule. Instead fall through to checks 2-4 below and let one
486
+ of them decide whether to fire or abort. The conversation has been
487
+ continuously hot for ~45 min; further deferral is unlikely to find
488
+ a better moment, and at this point the universal Goal-1-outranks-
489
+ Goal-3 rule means we accept Variant-B framing rather than letting
490
+ the confirm starve forever.
491
+
492
+ Log:
493
+ ```
494
+ - HH:MM [dm_session] confirm:<topic> deferred (defer=N): user mid-thread
495
+ ```
496
+ Then end the turn with NO assistant text.
497
+
498
+ - **state=active** (last 30 min, not last 5 min): **proceed** to
499
+ check 2. The conversational weave (Variant B) framing in this
500
+ task flow handles the on-the-fly bridge.
501
+
502
+ - **state=asleep**: **proceed** to check 2. Variant A applies.
503
+
504
+ #### Check 2 — Decline-marker probe (when `confirm_decline_marker` is set)
505
+
506
+ If `taskContext.confirm_decline_marker` is set, the user may have
507
+ previously said "no" to this exact intent. Read the file and grep the
508
+ named section for the `match` string:
509
+
510
+ ```bash
511
+ curl -sS -w '\n%{http_code}' "http://localhost:8321/api/context/<confirm_decline_marker.path>"
512
+ ```
513
+
514
+ A `404` status means the file has not yet been created → no marker
515
+ can exist → continue to Check 3. On `200`, parse the body and look
516
+ under the `<confirm_decline_marker.section>` heading: if any line in
517
+ that section contains `<confirm_decline_marker.match>` as a
518
+ substring, the marker is present → **abort silent** with reason
519
+ `decline-marker`.
520
+
521
+ #### Check 3 — Slot-filled probe (when `confirm_slot` is set)
522
+
523
+ If `taskContext.confirm_slot` is set, the gate's durable state may
524
+ have been written via a different path since this confirm was
525
+ scheduled. Probe:
526
+
527
+ ```bash
528
+ curl -s "http://localhost:8321/api/profile-questions/slot-filled?path=<confirm_slot.path>&section=<confirm_slot.section?>&anchor=<confirm_slot.anchor?>"
529
+ ```
530
+
531
+ (The endpoint is profile-questions-named but generic — see
532
+ `packages/daemon/src/core/profile-questions/slot-filled.ts`.)
533
+
534
+ **Abort silent** with reason `slot-filled` when EITHER:
535
+
536
+ - `filled: true` — the named section / anchor has a substantive
537
+ bullet. This is the standard case (e.g. user-profile slot already
538
+ landed via a separate write), OR
539
+ - `fileExists: true` AND `confirm_slot.section` is null/omitted
540
+ AND `confirm_slot.anchor` is null/omitted — i.e. the gate
541
+ encoded "the file's mere existence is the answer". This covers
542
+ project-creation, where the durable state is "a
543
+ `projects/<slug>.md` file with frontmatter + H1 exists" rather
544
+ than "a specific section is populated". The endpoint reports
545
+ `filled: false` for a fresh frontmatter+H1 file (zero substantive
546
+ bullets), so a `filled`-only check would let the confirm fire
547
+ even though the project was already created — `fileExists` is
548
+ the correct signal in the no-section/no-anchor case.
549
+
550
+ Otherwise (file does not exist, or filled=false with a section
551
+ or anchor specified) continue to Check 4.
552
+
553
+ #### Check 4 — DM-history scan
554
+
555
+ Read `<recent_dm_conversation>` (last 20 turns) AND
556
+ `<recent_dm_messages window="60min">`. Judge whether the user
557
+ already answered the question described in
558
+ `taskContext.confirm_hint`. Three answer shapes count as "answered":
559
+
560
+ - **Affirmative resolution** — user wrote a sentence that lets the
561
+ gate complete its write. Examples: *"yeah, track it as
562
+ la-pm-masters"*, *"sounds good"*, *"go ahead"*, *"please do"*.
563
+ Abort silent with reason `already answered (yes) in DM`.
564
+ - **Negative resolution** — user wrote a clear decline. Examples:
565
+ *"no"*, *"don't bother"*, *"later"*, *"skip it"*, *"drop it"*.
566
+ Abort silent with reason `already answered (no) in DM`. AND if
567
+ `taskContext.confirm_decline_marker` is set, write the marker now
568
+ (see §"Decline-marker write" below) so subsequent re-fires of the
569
+ same gate see the declined state.
570
+ - **Volunteered answer** — user wrote the underlying fact without
571
+ prompting (e.g. *"I changed my mind about LA — heading back to
572
+ Tokyo"*, or supplied the value the gate would have asked for).
573
+ The gate's write-side picks this up. Abort silent with reason
574
+ `already answered (volunteered) in DM`.
575
+
576
+ Does NOT count as "answered" (fire the DM):
577
+ - bare re-mention without action: *"still thinking about LA PM"*
578
+ - status updates on the topic without consent/decline: *"LA
579
+ classes started"*, *"midterm was hard"*
580
+ - non-answer continuations: *"hmm"*, *"not sure"*
581
+
582
+ The examples above are English for prompt clarity only; recognise
583
+ the same shapes in any language the user writes in.
584
+
585
+ **Bias conservative on ambiguous shapes, strict on explicit "yes" /
586
+ "no".** A redundant ask is recoverable; a missed confirmation can
587
+ leave a project / roadmap entry unwritten. When the DM-history
588
+ evidence is genuinely ambiguous, fire the DM (fall through to
589
+ Step 2).
590
+
591
+ #### On abort
592
+
593
+ 1. Append one line to today.md `## Agent Log` via the `today` skill
594
+ (PATCH `mode: "append"`, `section: "agent_log"`):
595
+ ```
596
+ - HH:MM [dm_session] confirm:<topic> aborted: <reason>
597
+ ```
598
+ Where `<reason>` is one of `interlock-skip`, `decline-marker`,
599
+ `slot-filled`, `already answered (yes|no|volunteered) in DM`.
600
+ 2. End the turn with **NO assistant text**. `shouldNotify` is
601
+ unconditional for `scheduled.dm` — an empty turn means no DM is
602
+ sent. Do NOT emit a placeholder string.
603
+ 3. Step 2 and Step 3 are skipped.
604
+
605
+ #### On self-defer (Check 1 only)
606
+
607
+ Same as abort (no DM, no Step 2 / Step 3 execution) — the replacement
608
+ schedule row is the recovery path.
609
+
610
+ #### Decline-marker write (helper for Check 4 "no" branch and evaluator)
611
+
612
+ When Check 4 detects a "no" (or the evaluator branch in Step 2 fires
613
+ on silence) and `taskContext.confirm_decline_marker` is set, write
614
+ the marker before ending the turn. Three cases — file missing
615
+ entirely, file present but section missing, both present — are
616
+ handled in one read-then-branch sequence:
617
+
618
+ 1. GET the file, capturing the status code:
619
+ ```bash
620
+ resp=$(curl -sS -w '\n%{http_code}' "http://localhost:8321/api/context/<confirm_decline_marker.path>")
621
+ status=$(printf '%s\n' "$resp" | tail -n1)
622
+ body=$(printf '%s\n' "$resp" | sed '$d')
623
+ ```
624
+
625
+ `<confirm_decline_marker.path>` is the value from `taskContext`
626
+ (e.g. `agent/journal.md`). The endpoint accepts the path with or
627
+ without the `.md` suffix.
628
+
629
+ 2. Branch on status + section presence:
630
+ - **status=404 (file missing).** Some marker paths
631
+ (`agent/journal`) support `PUT` for first-write creation via the
632
+ daemon's CREATE_ONLY_PUT allowlist. PUT a minimal file
633
+ containing an H1, the section header, and the new marker line:
634
+ ```bash
635
+ curl -s -X PUT "http://localhost:8321/api/context/<confirm_decline_marker.path>" \
636
+ -H 'Content-Type: application/json' \
637
+ -d "$(jq -n --arg m '- <YYYY-MM-DD> [<confirm_dedup_key>] user declined (DM fire-time scan)' \
638
+ --arg s '<confirm_decline_marker.section as Title Case heading>' \
639
+ '{content: "# Agent Journal\n\n## \($s)\n\($m)\n"}')"
640
+ ```
641
+ If the PUT returns 403 (`forbidden`), the path is not on the
642
+ create-only allowlist — fall through to an `append_to_file`
643
+ PATCH (which will fail with 404, surfacing the design gap so
644
+ the operator can fix it).
645
+ - **status=200 AND `body` contains `## <section title>`.**
646
+ Append a bullet to the existing section:
647
+ ```bash
648
+ curl -s -X PATCH "http://localhost:8321/api/context/<confirm_decline_marker.path>" \
649
+ -H 'Content-Type: application/json' \
650
+ -d '{"section":"<section snake_case>","mode":"append","content":"- <YYYY-MM-DD> [<confirm_dedup_key>] user declined (DM fire-time scan)"}'
651
+ ```
652
+ - **status=200 AND section heading missing.** Use
653
+ `mode: "append_to_file"` and include the header in the content:
654
+ ```bash
655
+ curl -s -X PATCH "http://localhost:8321/api/context/<confirm_decline_marker.path>" \
656
+ -H 'Content-Type: application/json' \
657
+ -d '{"mode":"append_to_file","content":"\n## <Section Title>\n- <YYYY-MM-DD> [<confirm_dedup_key>] user declined (DM fire-time scan)\n"}'
658
+ ```
659
+
660
+ The marker is one short line; do not paraphrase the user's words.
661
+ Lifetime: append-only, no rotation — the gate that opted in can
662
+ remove the line later if the user explicitly revisits ("OK now let's
663
+ start that LA project after all"), but the confirm sub-flow itself
664
+ never deletes markers.
665
+
666
+ **Duplicate-line tolerance.** Both this helper and the gate's
667
+ reply-branch Decline path (e.g. `context` skill §"Reply branches")
668
+ can in principle write the same `[<confirm_dedup_key>]` line if a
669
+ narrow race slips past `runWithSessionGates`. Do NOT add a defensive
670
+ "check before write" guard. The pre-check readers (e.g. Step 1
671
+ Check 2 here; the gate's Step 1 decline-marker pre-check) all match
672
+ on the `[<confirm_dedup_key>]` substring, so duplicate lines do not
673
+ break the dedup contract — they are cosmetic only.
674
+
675
+ ### Step 2 — Compose the confirmation (or close the chain silently)
676
+
677
+ #### Evaluator branch — chain-close on silence
678
+
679
+ If `taskContext.confirm_attempt > taskContext.confirm_max_attempts`,
680
+ this row is a **decline-on-silence evaluator** (see Step 3). Step 1's
681
+ four checks already established that the user has not replied. Do
682
+ NOT compose a DM. Instead:
683
+
684
+ 1. Write the decline marker described in §"Decline-marker write"
685
+ above, with marker text:
686
+ ```
687
+ - <YYYY-MM-DD> [<confirm_dedup_key>] silence-after-<confirm_max_attempts>-asks
688
+ ```
689
+ 2. Append one line to today.md `## Agent Log`:
690
+ ```
691
+ - HH:MM [dm_session] confirm:<topic> chain closed: silent decline
692
+ ```
693
+ 3. End the turn with **NO assistant text**. The chain is now closed;
694
+ Step 3 below is skipped for the evaluator branch.
695
+
696
+ #### Compose branch — emit a single short DM
697
+
698
+ `taskContext.confirm_attempt <= taskContext.confirm_max_attempts`.
699
+ Compose a single short DM in persona voice that asks the question
700
+ from `taskContext.confirm_hint`. The hint is agent-internal English
701
+ (Policy A); the rendered DM follows `<output_language_policy>` and
702
+ the persona / Character voice rules at the top of this file.
703
+
704
+ Hard rules:
705
+
706
+ - **One question, one sentence** unless the topic genuinely needs
707
+ more context. End with the question; do NOT layer a second topic.
708
+ - **Do NOT mention** the schedule, the queue, the gate that fired
709
+ it, the word "confirm" / "confirmation", `taskContext`, IDs, or
710
+ any internal mechanism. The user sees a natural DM, not a queue
711
+ entry.
712
+ - **Conversation-state framing applies.** If state=active or
713
+ very-recent (Check 1 returned "proceed"), use Variant B — no
714
+ greeting, acknowledge the interruption briefly, hand the floor
715
+ back at the end. If state=asleep, use Variant A — persona
716
+ greeting opener is fine.
717
+ - **Softened re-check on retries.** If `confirm_attempt > 1`, this
718
+ is a re-check after silence, NOT a re-issue of the same question.
719
+ Phrase it as a check-in with a soft exit ("...or skip?", "...or
720
+ did this change?", "...or want me to leave it open?"). The user
721
+ must feel acknowledged, not pestered.
722
+
723
+ Examples (English source; render per `<output_language_policy>` and
724
+ persona — these phrasings are STRUCTURAL):
725
+
726
+ - Initial (`confirm_attempt=1`):
727
+ - hint: `create project "la-pm-masters"? (origin: DM said they moved to LA and started a PM master's)` →
728
+ *"Should I track 'LA PM master's' as a project so I can keep the syllabus / deadlines in one place?"*
729
+ - hint: `ambiguous: trip to Tokyo "next month" — date 5/15?` →
730
+ *"Was the Tokyo trip 5/15, or are you still picking the date?"*
731
+ - Softened re-check (`confirm_attempt=2`):
732
+ - same first hint →
733
+ *"Still on for tracking the LA PM master's as its own project, or skip for now?"*
734
+ - same second hint →
735
+ *"Did the Tokyo date settle, or want me to leave it open?"*
736
+
737
+ ### Step 3 — Schedule the chain successor
738
+
739
+ This step runs only on the **compose branch** of Step 2 (a DM was
740
+ emitted). The evaluator branch already closed the chain in Step 2;
741
+ Step 3 is skipped in that case.
742
+
743
+ After Step 2 emits the DM text (which the daemon will dispatch as
744
+ this turn's final assistant text), schedule a successor row based on
745
+ the current attempt counter. Inherit all unchanged `taskContext`
746
+ fields from this row.
747
+
748
+ **Quiet-hours discipline.** The literal `<current_time> + 24h` may
749
+ land inside the user's quiet hours (default 22:00-08:00, configurable
750
+ via `runtime_settings.quietHoursStart/End`). The schedule skill's
751
+ "Time discipline" section forbids non-`critical` rows in quiet hours,
752
+ and the scheduler rejects them. Pick a target time that:
753
+
754
+ 1. Is at least 24h after `<current_time>` (the §B6 minimum-interval
755
+ contract), and
756
+ 2. Falls outside the user's quiet hours.
757
+
758
+ The simplest pattern: take `<current_time> + 24h` and, if that
759
+ clock-time is inside quiet hours, roll forward to quiet-hours-end
760
+ on that day. For a 23:30 initial fire with quiet hours starting at
761
+ 22:00, this yields a 08:00 successor 24h+8.5h later — slightly more
762
+ than 24h, still within the spirit of "next agent-day". Do not roll
763
+ *backward* to fit before quiet hours; that violates the 24h
764
+ minimum.
765
+
766
+ #### Case A — `confirm_attempt < confirm_max_attempts`
767
+
768
+ Schedule a **softened retry** at `<current_time> + 24h`:
769
+
770
+ ```bash
771
+ curl -s -X POST http://localhost:8321/api/schedule \
772
+ -H 'Content-Type: application/json' \
773
+ -d @- <<JSON
774
+ {
775
+ "time": "<current_time + 24h, ISO 8601 with offset>",
776
+ "taskType": "dm_session",
777
+ "description": "confirm:<topic> — <hint, softened paraphrase>",
778
+ "model": "sonnet",
779
+ "taskContext": {
780
+ "scheduledBy": "scheduled_dm.confirm_followup.retry",
781
+ "sub_flow": "confirm",
782
+ "confirm_id": "<new short id>",
783
+ "confirm_dedup_key": "<inherited unchanged>",
784
+ "confirm_hint": "<softened English brief — e.g. 'still on for tracking LA PM master's, or skip?'>",
785
+ "confirm_recent_window_hours": <inherited>,
786
+ "confirm_attempt": <prior + 1>,
787
+ "confirm_max_attempts": <inherited>,
788
+ "confirm_defer_count": 0,
789
+ "confirm_max_defers": <inherited>,
790
+ "confirm_decline_marker": <inherited>,
791
+ "confirm_slot": <inherited>,
792
+ "importance": "low"
793
+ }
794
+ }
795
+ JSON
796
+ ```
797
+
798
+ The successor's `confirm_hint` SHOULD be softened by the composing
799
+ session — the next fire's Step 2 reads the hint as written, so a
800
+ gentler hint produces a gentler DM. Reset `confirm_defer_count` to 0
801
+ on a retry (defers are per-fire, not per-chain).
802
+
803
+ #### Case B — `confirm_attempt == confirm_max_attempts`
804
+
805
+ Schedule a single **decline-on-silence evaluator** at
806
+ `<current_time> + 24h` with `confirm_attempt = confirm_max_attempts + 1`
807
+ (sentinel). At the evaluator's fire time, Step 1 runs as usual; if
808
+ all four checks pass (user still has not replied), Step 2's
809
+ evaluator branch silently writes the decline marker and ends —
810
+ no DM, no further successor.
811
+
812
+ ```bash
813
+ curl -s -X POST http://localhost:8321/api/schedule \
814
+ -H 'Content-Type: application/json' \
815
+ -d @- <<JSON
816
+ {
817
+ "time": "<current_time + 24h, ISO 8601 with offset>",
818
+ "taskType": "dm_session",
819
+ "description": "confirm:<topic> — silence-evaluator",
820
+ "model": "sonnet",
821
+ "taskContext": {
822
+ "scheduledBy": "scheduled_dm.confirm_followup.evaluator",
823
+ "sub_flow": "confirm",
824
+ "confirm_id": "<new short id>",
825
+ "confirm_dedup_key": "<inherited unchanged>",
826
+ "confirm_hint": "<inherited>",
827
+ "confirm_recent_window_hours": <inherited>,
828
+ "confirm_attempt": <confirm_max_attempts + 1>,
829
+ "confirm_max_attempts": <inherited>,
830
+ "confirm_defer_count": 0,
831
+ "confirm_max_defers": <inherited>,
832
+ "confirm_decline_marker": <inherited>,
833
+ "confirm_slot": <inherited>,
834
+ "importance": "low"
835
+ }
836
+ }
837
+ JSON
838
+ ```
839
+
840
+ #### Bookkeeping (silent — never visible to the user)
841
+
842
+ After scheduling the successor (Case A or B), append one line to
843
+ today.md `## Agent Log`:
844
+
845
+ ```
846
+ - HH:MM [dm_session] confirm:<topic> sent (attempt=N/max=M); successor scheduled <case>
847
+ ```
848
+
849
+ (Use `case=retry` for Case A and `case=evaluator` for Case B.)
850
+
851
+ ### Notes — invariants this sub-flow upholds
852
+
853
+ - **Goal 1 (thread preservation).** Check 1's self-defer keeps a
854
+ hot thread intact even though the confirm has been waiting.
855
+ When defers exhaust, Goal 1 still wins: Variant-B framing softens
856
+ the entry rather than firing a cold opener.
857
+ - **Goal 2 (confirmations happen naturally).** Step 2's persona
858
+ voice + state-aware framing means the user experiences the DM as
859
+ a check-in, not a queue ping.
860
+ - **Goal 3 (never ask twice).** Checks 2-4 dedup against all
861
+ observable answer surfaces (declined, slot-filled, replied in
862
+ DM). Step 3's chained-fire model caps the chain at
863
+ `confirm_max_attempts + 1` fires (default 3), of which only
864
+ `confirm_max_attempts` (default 2) send DMs. The minimum
865
+ inter-fire interval is 24h — no same-day re-asks.
866
+ - **Schedule row IS the queue.** No new table, no
867
+ `pending-confirmations.md`. All chain state lives in
868
+ `taskContext`; cross-path cancellation is the gate's
869
+ responsibility (see the gate's reply-branch contract, e.g. the
870
+ `context` skill's Project DM-intent detection §"Reply branches").
@@ -0,0 +1,11 @@
1
+ {context}
2
+
3
+ ## Task: Answer from the wiki
4
+
5
+ Read `<wiki_command>` for the question. Follow the `wiki-ask` skill:
6
+
7
+ 1. Search `20_wiki/` for relevant notes.
8
+ 2. Verify against source links or `10_raw/` only when needed.
9
+ 3. Write one `30_outputs/<YYYY-MM-DD>-<slug>.md` answer through the Wiki API with `x-process-key: wiki.ask`.
10
+ 4. Keep the final assistant response concise; the durable answer belongs in `30_outputs/`.
11
+
@@ -0,0 +1,28 @@
1
+ {context}
2
+
3
+ ## Task: Compile wiki notes
4
+
5
+ A `!compile` (or approved `!compile full`) landed here. Read `<wiki_command>` for `data.mode` — `"incremental"` (compile only `10_raw/` notes touched since the last compile) or `"full"` (compile every raw). Read `<wiki_workspace>` for the destination workspace.
6
+
7
+ You succeed if and only if every wiki note you claim to have created came back from the daemon's Wiki API as `{"ok":true,"path":"20_wiki/<slug>.md"}`. The vault is on disk and there is no other path to write it.
8
+
9
+ ### Critical: the only write surface is the Wiki API via `curl`
10
+
11
+ - `Write` and `Edit` tools are **stripped from this session's allow-list**. The SDK denies them silently under `dontAsk` ("Permission to use Write has been denied …"). They cannot be made to work via path rewriting; do not try.
12
+ - `Bash(find ...)`, `Bash(ls ...)`, `Bash(cat ...)`, `Bash(grep ...)`, `Bash(wc ...)` and every other shell utility are also silently denied. Only `Bash(curl *)` and `Bash(jq *)` are on the allow-list. Enumerate raw notes via `GET /api/wiki/<workspace>/index`, NOT by walking `{{vault_path}}` from disk.
13
+ - The `Bash(curl *)` allow-rule is prefix-matched against the full command. Wrappers — `echo '{...}' | curl …`, `cat <<JSON | curl …`, `bash -c "curl …"`, `( curl … )`, `var=… curl …`, chained `curl … ; curl …` — are silently denied (no output, no `PA_API_ERROR`). The reverse, `curl … -d @- <<'JSON' … JSON` on the same line, IS allowed because the command still starts with `curl`; the shim reads stdin via `-d @-`.
14
+
15
+ ### Procedure
16
+
17
+ 1. **Baseline (incremental only).** Read `20_wiki/_index.md` (or the most recent `wiki.compile` entry in `log.md`) to recover the prior `compiled_at` ISO timestamp. If the workspace has never been compiled, compile every raw.
18
+ 2. **Enumerate raw notes** via `GET /api/wiki/<workspace>/index`, then filter with `jq` for `path` under `10_raw/` and (incremental) `mtime > <baseline>`.
19
+ 3. **Read existing wiki + taxonomy** so synthesis can de-duplicate and use canonical slugs.
20
+ 4. **For each topic**, synthesize a root-level `20_wiki/<slug>.md` note (one note per coherent topic — merge multiple raws when they cover the same thing). `POST` for a new slug, `PATCH mode: "replace"` for an existing one (read-before-write).
21
+ 5. **Append `20_wiki/_index.md`** (`PATCH mode: "append"`) with one bullet per added or updated note.
22
+ 6. **Append `log.md`** (`PATCH mode: "append"`) with one summary line: `[<ISO>] wiki.compile (<mode>): compiled <N> notes from <M> raws — added <A>, updated <B>, unchanged <C>`.
23
+
24
+ The `wiki-compile` skill carries the canonical curl shapes (including the multi-KB heredoc body shape), the per-error recovery table, and the slug rules. The `wiki-vault-rules` skill carries the body-quoting cheat-sheet and the full layer/endpoint reference.
25
+
26
+ Do NOT modify `00_inbox/` or existing `10_raw/` notes. Do NOT call `/api/send-message`, `/api/whatsapp/send`, `/api/notify-user`, or any other "send" endpoint — those routes do not exist. Your final assistant text IS the delivery channel; the daemon forwards it to the bang's reply target automatically.
27
+
28
+ End with the single-line completion DM from the skill (Success / Partial / Failure).
@@ -0,0 +1,12 @@
1
+ {context}
2
+
3
+ ## Task: Bridge two domains in the wiki
4
+
5
+ Read `<wiki_command>` for `topic_a` and `topic_b`. Follow the `wiki-connect` skill:
6
+
7
+ 1. Search each topic independently and read enough matches to characterise each domain. Disambiguate string matches against `90_meta/taxonomy.md`.
8
+ 2. Surface bridges in four buckets — shared terminology, common references, structural analogies, and bridging concept candidates. Cite every bridge with at least one wiki path from each side. A bridge that can only cite one side belongs in the candidates list, not in the others.
9
+ 3. Write one connection report to `30_outputs/<YYYY-MM-DD>-connect-<slug-a>--<slug-b>.md` through the Wiki API with `x-process-key: wiki.connect`. Use the canonical slugs from `90_meta/taxonomy.md` when available.
10
+ 4. Append a one-line `log.md` entry referencing the output filename and both topics.
11
+
12
+ If no real bridges exist, write the report anyway with empty sections marked `_(none)_` and a `## Summary` that names that directly — a "no connection" finding is still useful. Do not create new wiki notes from this flow; the bridging concept candidates section is a proposal for `wiki.compile` to pick up later. End with a short internal summary only.
@@ -0,0 +1,35 @@
1
+ {context}
2
+
3
+ ## Task: Ingest URL into the wiki
4
+
5
+ A single `!ingest` call landed here. Read `<wiki_command>` for the target `url` and `<wiki_workspace>` for the destination workspace.
6
+
7
+ You succeed if and only if you POST a new file to `/api/wiki/<workspace>/files/10_raw/<slug>.md` and the daemon responds with `{"ok":true,"path":"10_raw/<slug>.md"}`. There is no other definition of success. The vault file is created by that POST and nothing else — `Write` tool / shell redirection / other endpoints do not put a file in the vault.
8
+
9
+ ### Critical: the Bash command MUST start with literal `curl`
10
+
11
+ The `Bash(curl *)` allow-list is prefix-matched. Wrappers (`echo '{...}' | curl …`, `cat <<JSON | curl … -d @-`, `bash -c "curl …"`, parentheses, chained `curl … ; curl …`) are silently denied under `dontAsk` — you get no error, no `PA_API_ERROR`, just an empty response. If a Bash call returns nothing, rewrite it to start with `curl`.
12
+
13
+ Article bodies are typically multi-KB — use a heredoc redirected directly into curl (the command still starts with `curl`):
14
+
15
+ ```
16
+ curl http://localhost:8321/api/wiki/<workspace>/files/10_raw/<slug>.md \
17
+ -X POST \
18
+ -H 'content-type: application/json' \
19
+ -H 'x-process-key: wiki.ingest_url' \
20
+ -d @- <<'JSON'
21
+ {"content":"---\n…frontmatter…\n---\n\n# Title\n\n## Source extracts\n\n…"}
22
+ JSON
23
+ ```
24
+
25
+ The shim reads stdin when `-d @-` is passed, so the heredoc body lands as the JSON payload. Inside `<<'JSON'` (single-quoted marker) the body is verbatim — no shell escaping, only JSON escapes (`\"`, `\\`, `\n`). For short bodies (a few hundred bytes) inline `-d '<json>'` is fine.
26
+
27
+ ### Procedure
28
+
29
+ 1. Fetch the URL with the per-backend primitive (WebFetch on Claude, curl on Codex, web_fetch on Gemini).
30
+ 2. POST one new note at `10_raw/<slug>.md` via the daemon Wiki API with `-H 'x-process-key: wiki.ingest_url'`. Path is EXACTLY `10_raw/<slug>.md` — no nested folders like `10_raw/articles/...`.
31
+ 3. **Inspect the curl response.** Only `{"ok":true,"path":"10_raw/<slug>.md"}` counts as success. Anything else (4xx, 5xx, missing fields, hallucinated endpoint like `/api/send-message`, no response) means the file was NOT created — PATCH `log.md` with the failure reason and emit the failure DM.
32
+
33
+ The `wiki-ingest` skill carries the full curl shapes, slug rules, error-code recovery table, and completion-DM format.
34
+
35
+ End the turn with the single-line completion DM defined in the skill. The path you cite in the success DM must be byte-identical to the `path` field the daemon returned. Do not fabricate a path. Do not claim success without seeing `{"ok":true,...}`. The daemon forwards your final assistant text to the user — you must NOT also call `/api/send-message`, `/api/whatsapp/send`, `/api/notify-user`, `/api/dm`, or any other "send" endpoint; those do not exist.
@@ -0,0 +1,13 @@
1
+ {context}
2
+
3
+ ## Task: Audit wiki health
4
+
5
+ Read `<wiki_command>` for the target workspace. Follow the `wiki-lint` skill:
6
+
7
+ 1. Inventory the workspace via `GET /api/wiki/{{workspace_name}}/index` and read `90_meta/schemas/*` for the current schema.
8
+ 2. Run every check the skill enumerates (orphans, broken wikilinks, missing frontmatter, stale content, term inconsistencies, taxonomy candidates, index drift). Empty findings are still reported.
9
+ 3. Write exactly one health report to `90_meta/health/<YYYY-MM-DD>.md` through the Wiki API with `x-process-key: wiki.lint`. The report must include the `## Action items` and `## Summary` sections in the exact order documented by the skill.
10
+ 4. If — and only if — there are taxonomy candidates, append a `# Candidates` section to `90_meta/taxonomy.md` with `mode: "append"`. The owner reviews this before any promotion happens; do not edit the existing `## Topics` section.
11
+ 5. Append a concise summary line to `log.md`.
12
+
13
+ Do not modify content layers (`10_raw/`, `20_wiki/`, `30_outputs/`). End with a short internal summary only.
@@ -0,0 +1,13 @@
1
+ {context}
2
+
3
+ ## Task: Trace an idea's evolution across the wiki
4
+
5
+ Read `<wiki_command>` for the `topic`. Follow the `wiki-trace` skill:
6
+
7
+ 1. Search across all layers (`10_raw/`, `20_wiki/`, `30_outputs/`) and read the matches that look load-bearing. Use `90_meta/taxonomy.md` to canonicalise the topic slug.
8
+ 2. Order findings chronologically using the most authoritative date available (asserted dates → output filename dates → file `mtime`). Mark any date that came from `mtime` as "discovered on" rather than "happened on".
9
+ 3. Group into two to five phases of stable framing. For each phase, name the dominant question, the new evidence, and what changed compared to the previous phase. Cite every claim with a wiki path.
10
+ 4. Write one timeline report to `30_outputs/<YYYY-MM-DD>-trace-<slug>.md` through the Wiki API with `x-process-key: wiki.trace`. Follow the section order documented by the skill.
11
+ 5. Append a one-line `log.md` entry referencing the output filename and the topic.
12
+
13
+ If the wiki has fewer than two distinct sources on the topic, keep the report short and say so directly in `## Summary` — do not pad it with speculation. End with a short internal summary only.
@@ -0,0 +1,12 @@
1
+ # Output Schema
2
+
3
+ Outputs live under `30_outputs/` and answer a specific user question from the wiki.
4
+
5
+ ## Required Shape
6
+
7
+ - Question
8
+ - Short answer
9
+ - Evidence
10
+ - Source links
11
+ - Follow-up gaps
12
+
@@ -0,0 +1,13 @@
1
+ # Raw Note Schema
2
+
3
+ Raw notes live under `10_raw/` and preserve source facts before synthesis.
4
+
5
+ ## Required Shape
6
+
7
+ - Source URL
8
+ - Retrieved timestamp
9
+ - Source title when available
10
+ - Extracted facts
11
+ - Quotes or snippets only when they are short and necessary
12
+ - Open questions
13
+
@@ -0,0 +1,12 @@
1
+ # Wiki Note Schema
2
+
3
+ Wiki notes live under `20_wiki/` and contain synthesized, cited knowledge.
4
+
5
+ ## Required Shape
6
+
7
+ - Summary
8
+ - Key facts
9
+ - Source links
10
+ - Related notes
11
+ - Unknowns or conflicts
12
+