@aitne-sh/aitne 0.1.4 → 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 (87) hide show
  1. package/README.md +16 -0
  2. package/agent-assets/agent-profiles/_safety.md +29 -0
  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 +123 -15
  38. package/agent-assets/skills/roadmap/SKILL.md +31 -4
  39. package/agent-assets/skills/schedule/SKILL.md +44 -3
  40. package/agent-assets/skills/today/SKILL.md +50 -11
  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/routine.roadmap_refresh.md +7 -3
  67. package/agent-assets/task-flows/scheduled.dm.md +477 -0
  68. package/agent-assets/task-flows/setup.initial.md +50 -23
  69. package/agent-assets/task-flows/wiki.ask.md +11 -0
  70. package/agent-assets/task-flows/wiki.compile.md +28 -0
  71. package/agent-assets/task-flows/wiki.connect.md +12 -0
  72. package/agent-assets/task-flows/wiki.ingest_url.md +35 -0
  73. package/agent-assets/task-flows/wiki.lint.md +13 -0
  74. package/agent-assets/task-flows/wiki.trace.md +13 -0
  75. package/agent-assets/wiki-seeds/schemas/output.md +12 -0
  76. package/agent-assets/wiki-seeds/schemas/raw.md +13 -0
  77. package/agent-assets/wiki-seeds/schemas/wiki.md +12 -0
  78. package/agent-assets/wiki-seeds/taxonomy.md +13 -0
  79. package/package.json +10 -6
  80. package/scripts/check-redaction-coverage.mjs +0 -109
  81. package/scripts/commands.md +0 -0
  82. package/scripts/message-discipline-digest.mjs +0 -535
  83. package/scripts/poc/google-connector-inheritance/REPORT.md +0 -197
  84. package/scripts/poc/google-connector-inheritance/claude-sdk-probe.mjs +0 -79
  85. package/scripts/regen-skill-fixtures.mjs +0 -39
  86. package/scripts/remint-roadmap-ids.mjs +0 -257
  87. package/scripts/smoke-obsidian-api.mjs +0 -166
@@ -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").
@@ -7,8 +7,9 @@ Vault step already captured `settings.primary_language` (BCP-47 tag, e.g.
7
7
  `en` or `es`) and `settings.vault_mode` (`plain` or `obsidian`) — the
8
8
  latter controls how the **primary management vault** is laid out, not the
9
9
  separate external Obsidian integration. Do NOT re-ask those questions.
10
- Output language for user-editable prose (profile, today, daily journal):
11
- follow `<output_language_policy>`.
10
+ Output language for everything written to disk in this flow
11
+ (`user/profile.md`, `user/*.md`, `rules/management.md` body): follow
12
+ `<output_language_policy>`.
12
13
 
13
14
  SETUP-FLOW-REDESIGN-PLAN §5.8 — the legacy "tool selections" form was
14
15
  removed. Steps 4–6 of the wizard (Mail, Calendar, Note) already
@@ -68,6 +69,15 @@ about all four rows rather than fabricating defaults. Do not retry
68
69
  with a curl.
69
70
 
70
71
  ### Instructions
72
+
73
+ Steps 3–8 all happen in the same agent turn. They are ordered to match
74
+ the actual emission sequence: the two staged code blocks (rules then
75
+ character) land in the stream first; the silent curl writes follow. The
76
+ dashboard reveals the preview as soon as the rules block lands but
77
+ keeps "Save & Finish" disabled until the whole turn ends, so emitting
78
+ blocks before curls minimizes how long the user waits on a disabled
79
+ button.
80
+
71
81
  1. Run Step 0 silently. Greet the user in one line, introducing
72
82
  yourself by the name in `<agent_identity>` display_name. Then
73
83
  present the derived Source-of-Truth table and ask any remaining
@@ -78,28 +88,30 @@ with a curl.
78
88
  - Project-management method preferences (anything you want the agent
79
89
  to follow when handling your projects?)
80
90
  Either answer may be "no preference" / "skip" — accept that cleanly.
81
- 3. Once the user has answered, generate rules/management.md inside a
82
- ```management-rules code block. This triggers the dashboard preview.
83
- 4. If the user requests changes, revise. Complete within **at most 2
84
- revision rounds**.
91
+ 3. Generate rules/management.md inside a ```management-rules code
92
+ block. This is the FIRST thing emitted in the turn — the dashboard
93
+ reveals the preview as soon as it lands.
94
+ 4. If the user requests changes, revise (return to step 3). Complete
95
+ within **at most 2 revision rounds**.
85
96
  5. **Do NOT put communication style inside rules/management.md, and do NOT
86
97
  put it inside `user/profile.md` either.** Tone / style / voice / emoji
87
98
  / language preferences live in the `character` runtime-config field
88
- only — emitted as a ```character``` code block (see step 6.5). See
99
+ only — emitted as a ```character``` code block (see step 6). See
89
100
  `docs/design/15-character.md` for the split rule.
90
- 6. In the same turn as the management-rules block, silently call
91
- PUT /api/context/user/profile using the template in "user/profile.md
92
- Format" below. Working hours and quiet hours are pre-populated with
93
- defaults (Weekdays 09:00–18:00, Quiet hours 22:00–08:00) — do not
94
- ask about them. Fill Platforms from the **derived Source-of-Truth
95
- table** (Step 0) — the row values you confirmed with the user.
96
- Leave Identity blank setup does not collect name or timezone.
97
- 6.5. If the user stated a communication-style preference in step 2,
98
- emit a ```character``` code block in the same turn (see "Character
99
- code block format" below). If the user skipped, omit the block.
100
- 7. If the conversation surfaced detail-heavy facts that belong in the
101
- detailed profile layer, also PATCH the matching user/*.md file in the
102
- same turn. Typical Q&A file mappings:
101
+ 6. If the user stated a communication-style preference in step 2, emit
102
+ a ```character``` code block immediately after the rules block (see
103
+ "Character code block format" below). If the user skipped, omit the
104
+ block.
105
+ 7. After the staged blocks, silently call PUT /api/context/user/profile
106
+ using the template in "user/profile.md Format" below. Working hours
107
+ and quiet hours are pre-populated with defaults (Weekdays 09:00–18:00,
108
+ Quiet hours 22:00–08:00) do not ask about them. Fill Platforms
109
+ from the **derived Source-of-Truth table** (Step 0) the row values
110
+ you confirmed with the user. Leave Identity blank setup does not
111
+ collect name or timezone.
112
+ 8. If the conversation surfaced detail-heavy facts that belong in the
113
+ detailed profile layer, also PATCH the matching user/*.md file (still
114
+ the same turn, after step 7). Typical Q&A → file mappings:
103
115
  - Named colleagues, family, friends → user/people.md
104
116
  - Current company, role specifics, ongoing projects → user/work.md
105
117
  - Specific frameworks, years of experience → user/expertise.md
@@ -109,7 +121,7 @@ with a curl.
109
121
  Only seed what the user actually stated — do not invent or infer. If
110
122
  nothing came up for a given file, do not touch it. See the user-profile
111
123
  skill for the read-before-write PATCH recipe.
112
- 8. Keep the updates in steps 67 silent — the dashboard handles saving
124
+ 9. Keep steps 78 silent — the dashboard handles saving
113
125
  rules/management.md separately when the user approves the preview. Do
114
126
  not create or modify any other context files; the daemon seeds the
115
127
  rest of the primary-vault scaffold automatically.
@@ -218,6 +230,18 @@ write tone/style preferences into user/profile.md.
218
230
 
219
231
  ### rules/management.md Format
220
232
 
233
+ Output language: follow `<output_language_policy>`. rules/management.md
234
+ is Policy B — the section headers (`## Agent Identity`, `## Source of
235
+ Truth`, `## Autonomy Levels`, `## Notification Rules`, `## Schedule`,
236
+ `## Project Management`) are skeleton and stay English; the descriptive
237
+ bullets under `## Autonomy Levels`, `## Notification Rules`, `## Schedule`,
238
+ and `## Project Management` are written in `<settings primary_language>`.
239
+ File-specific carve-outs that also stay English: the `## Agent Identity`
240
+ field labels (`- AI name:`, `- WhatsApp label:`) — the daemon parses and
241
+ rewrites this section; the Source of Truth table column headers and
242
+ Domain-column row labels; and product/brand cells (`Google Calendar`,
243
+ `Obsidian`, `Notion`, `today.md`, `projects/*.md`).
244
+
221
245
  Fill the Source of Truth table from the **derived rows you confirmed
222
246
  with the user in Step 0** (Schedule / Tasks / Notes / Projects).
223
247
  Generate using this format:
@@ -274,5 +298,8 @@ heading onto the end of the previous list item (e.g. `- Label:
274
298
  <value>## Next Section` on one line). Copy user-provided values as-is,
275
299
  then newline, blank line, then the next heading.
276
300
 
277
- **Important**: Outputting a ```management-rules code block triggers the dashboard to display a preview.
278
- The dashboard handles saving when the user approves, so you do NOT need to save via curl.
301
+ **Important**: Do NOT curl-write rules/management.md to disk yourself.
302
+ The dashboard persists it via `POST /setup/save-rules` when the user
303
+ clicks Save & Finish. (The silent curl PUT to /api/context/user/profile
304
+ in step 7 and the user/*.md PATCHes in step 8 ARE required — only the
305
+ rules file is saved by the dashboard.)
@@ -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).