kushi-agents 3.4.2 → 3.12.1

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 (69) hide show
  1. package/README.md +33 -0
  2. package/package.json +15 -3
  3. package/plugin/agents/kushi.agent.md +155 -147
  4. package/plugin/instructions/ado-bootstrap-discovery.instructions.md +111 -0
  5. package/plugin/instructions/ado-engagement-tree.instructions.md +73 -0
  6. package/plugin/instructions/answer-from-evidence.instructions.md +1 -1
  7. package/plugin/instructions/auth-and-retry.instructions.md +51 -16
  8. package/plugin/instructions/azure-auth-patterns.instructions.md +13 -6
  9. package/plugin/instructions/bootstrap-status-format.instructions.md +113 -0
  10. package/plugin/instructions/capture-learnings.instructions.md +95 -0
  11. package/plugin/instructions/cleanup-on-resolution.instructions.md +69 -0
  12. package/plugin/instructions/crm-bootstrap-discovery.instructions.md +79 -0
  13. package/plugin/instructions/crm-internal-vs-confirmed.instructions.md +79 -0
  14. package/plugin/instructions/evidence-confidence-ladder.instructions.md +66 -0
  15. package/plugin/instructions/evidence-layout-canonical.instructions.md +115 -0
  16. package/plugin/instructions/evidence-thoroughness.instructions.md +82 -12
  17. package/plugin/instructions/full-view-gate.instructions.md +91 -0
  18. package/plugin/instructions/m365-id-registry.instructions.md +134 -0
  19. package/plugin/instructions/meetings-verbatim-required.instructions.md +176 -0
  20. package/plugin/instructions/run-reports.instructions.md +129 -0
  21. package/plugin/instructions/scope-boundaries.instructions.md +218 -0
  22. package/plugin/instructions/snapshot-vs-stream.instructions.md +2 -0
  23. package/plugin/instructions/update-ledger.instructions.md +132 -0
  24. package/plugin/instructions/verbatim-by-default.instructions.md +73 -0
  25. package/plugin/instructions/workiq-first.instructions.md +15 -31
  26. package/plugin/instructions/workiq-only.instructions.md +193 -0
  27. package/plugin/learnings/README.md +50 -0
  28. package/plugin/learnings/ado.md +45 -0
  29. package/plugin/learnings/crm.md +96 -0
  30. package/plugin/learnings/cross-cutting.md +36 -0
  31. package/plugin/learnings/email.md +33 -0
  32. package/plugin/learnings/meetings.md +30 -0
  33. package/plugin/learnings/misc.md +46 -0
  34. package/plugin/learnings/onenote.md +215 -0
  35. package/plugin/learnings/sharepoint.md +5 -0
  36. package/plugin/learnings/teams.md +5 -0
  37. package/plugin/plugin.json +22 -2
  38. package/plugin/prompts/apply-ado.prompt.md +14 -0
  39. package/plugin/prompts/propose-ado.prompt.md +12 -0
  40. package/plugin/reference-packs/fde/crm-field-manifest.md +165 -0
  41. package/plugin/skills/apply-ado-update/SKILL.md +125 -0
  42. package/plugin/skills/ask-project/SKILL.md +2 -0
  43. package/plugin/skills/bootstrap-project/SKILL.md +81 -3
  44. package/plugin/skills/propose-ado-update/SKILL.md +108 -0
  45. package/plugin/skills/pull-ado/SKILL.md +173 -23
  46. package/plugin/skills/pull-crm/SKILL.md +168 -15
  47. package/plugin/skills/pull-email/SKILL.md +139 -22
  48. package/plugin/skills/pull-meetings/SKILL.md +109 -25
  49. package/plugin/skills/pull-misc/README.md +84 -0
  50. package/plugin/skills/pull-misc/SKILL.md +257 -0
  51. package/plugin/skills/pull-misc/runner.mjs +280 -0
  52. package/plugin/skills/pull-onenote/README.md +90 -0
  53. package/plugin/skills/pull-onenote/SKILL.md +400 -51
  54. package/plugin/skills/pull-onenote/runner.mjs +356 -0
  55. package/plugin/skills/pull-onenote/scripts/recapture-section-url.mjs +295 -0
  56. package/plugin/skills/pull-onenote/write-snapshot.mjs +271 -0
  57. package/plugin/skills/pull-sharepoint/SKILL.md +44 -12
  58. package/plugin/skills/pull-teams/SKILL.md +40 -11
  59. package/plugin/skills/refresh-project/SKILL.md +33 -2
  60. package/plugin/skills/self-check/run.ps1 +186 -4
  61. package/plugin/templates/ado-update/discussion-comment.template.md +26 -0
  62. package/plugin/templates/ado-update/integrations-ado-writes.example.yml +49 -0
  63. package/plugin/templates/ado-update/proposed.template.md +78 -0
  64. package/plugin/templates/init/external-links.template.txt +30 -0
  65. package/plugin/templates/init/project-integrations.template.yml +57 -2
  66. package/plugin/templates/snapshot/meeting-verbatim.template.md +110 -0
  67. package/plugin/templates/snapshot/meetings-series-index.template.md +3 -1
  68. package/plugin/templates/snapshot/onenote-page.template.md +92 -23
  69. package/plugin/templates/weekly/meetings-stream.template.md +11 -6
@@ -1,17 +1,25 @@
1
1
  ---
2
2
  name: "pull-email"
3
- version: "2.0.0"
4
- description: "Pull Email evidence (stream only — emails ARE events). WorkIQ-first. Scopes to configured project email folder per m365-mutable hints."
3
+ version: "2.3.1"
4
+ description: "Pull Email evidence (stream only — emails ARE events). WorkIQ-ONLY per workiq-only.instructions.md (m365_get_email / m365_search_emails / Graph REST FORBIDDEN — near-100% failure in this workspace). Folder-scoped fast path → root-scope WorkIQ fallback. Enumerate-then-fetch via two WorkIQ calls per project. Mutable folder-hint upsert during run. Throttle-aware. User-paste is a first-class fallback, NOT Graph."
5
5
  ---
6
6
 
7
7
  # Skill: pull-email
8
8
 
9
+
10
+ > **v3.7.6 + v3.11.0 contracts** — This skill operates under five HARD-rule doctrines:
11
+ > - `verbatim-by-default.instructions.md` — full bodies/notetext/fields by default; no preview-grade pulls accepted.
12
+ > - **`workiq-only.instructions.md` (v3.11.0)** — email list + body fetch go through WorkIQ ONLY. `m365_get_email`, `m365_search_emails`, `m365_list_emails`, and Graph REST URLs are FORBIDDEN as fallbacks (they fail nearly every call in this workspace). The canonical WorkIQ prompts (folder-scoped enumerate, root-scope fallback, per-message body fetch) are codified in that instruction — do not re-discover them.
13
+ > - `capture-learnings.instructions.md` — every fix/discovery is logged to `plugin/learnings/<source>.md` immediately.
14
+ > - `cleanup-on-resolution.instructions.md` — when a value resolves, all stale `no-match` / `not yet` notes referencing the prior unresolved state must be rewritten in the same turn.
15
+ > - `run-reports.instructions.md` — every refresh writes a per-user report under `Evidence/<alias>/refresh-reports/YYYY-MM-DD-HHMM_refresh.md`.
16
+ > - `evidence-layout-canonical.instructions.md` (kushi v3.12.1+) — ALL email artifacts MUST be written under `<project>/Evidence/<alias>/email/{snapshot,stream,_index,_legacy_*}/`. Writing to `<project>/email-context/`, `<project>/email/`, `<project>/_Weekly Summaries/`, or any other sibling path under `<project>/` is a DEFECT.
9
17
  Pulls **email** evidence in two shapes per `snapshot-vs-stream.instructions.md`:
10
18
 
11
19
  - **snapshot/** — (none — emails ARE events, no snapshot)
12
20
  - **stream/** — messages with full body, sender, recipients, attachments, reply chain — grouped by thread
13
21
 
14
- WorkIQ-first per `workiq-first.instructions.md`. Thoroughness per `evidence-thoroughness.instructions.md`; runtime detector + auto-retry + paste-prompt per `thoroughness-detector.instructions.md`. Citations per `citation-ledger.instructions.md`. Side-by-side mutable hints written immediately on discovery per `side-by-side-config.instructions.md`.
22
+ Thoroughness per `evidence-thoroughness.instructions.md`; runtime detector + auto-retry + paste-prompt per `thoroughness-detector.instructions.md`. Citations per `citation-ledger.instructions.md`. Side-by-side mutable hints written immediately on discovery per `side-by-side-config.instructions.md`.
15
23
 
16
24
  ## Inputs
17
25
 
@@ -26,50 +34,159 @@ WorkIQ-first per `workiq-first.instructions.md`. Thoroughness per `evidence-thor
26
34
  - `emailContext.folder` — full path of the project's email folder
27
35
  - `emailContext.folderId` — folder ID for direct lookup
28
36
 
29
- ## Snapshot pass
37
+ ## Boundaries (REQUIRED — see `scope-boundaries.instructions.md`)
30
38
 
31
- _(no snapshot for email emails are inherently event-stream)_
39
+ This skill REFUSES to query unless `<engagement-root>/<project>/integrations.yml#boundaries.email` is satisfied:
40
+
41
+ - `boundaries.email.mailboxes` — REQUIRED, non-empty. Empty = source disabled.
42
+ - `boundaries.email.sender_domains` — optional narrowing.
43
+ - `boundaries.email.subject_keywords` — optional narrowing.
44
+ - `boundaries.date_window_days` — defaults to 30 if absent.
45
+
46
+ Every WorkIQ ask, every `m365_search_emails` / `m365_list_emails` call, every Graph fallback MUST be scoped to those mailboxes + (if set) sender_domains + subject_keywords. Empty hits inside the boundary → write Coverage Notes citing the limiting key; do NOT widen the scope.
47
+
48
+ Refusal message when boundary is missing:
49
+
50
+ ```
51
+ email-boundary-missing — add boundaries.email.mailboxes to
52
+ <engagement-root>/<project>/integrations.yml. See doctrine in
53
+ plugin/instructions/scope-boundaries.instructions.md.
54
+ ```
55
+
56
+ ## Retrieval order (deterministic — folder fast path → root fallback)
57
+
58
+ Borrowed from production email-context hardening. WorkIQ is more reliable when the query is folder-scoped to one known project folder than when it scans the whole mailbox. Order:
59
+
60
+ ### Order 1 — Exact project-folder fast path (when hint exists)
61
+
62
+ If `m365Mutable.knownSections.<project>.emailContext.folder` (or `.folderId`) is set with `confidence >= medium`:
63
+
64
+ ```
65
+ workiq ask -q "Search my Outlook mailbox for emails from <window-start> onward in folder(s) '<projectFolder>' (including all subfolders) related to <project aliases>. Return: sent datetime, subject, sender, recipients, folder path, message link, and a short relevance reason. Do not return NO_RESULTS unless the folder truly has no messages in that range."
66
+ ```
67
+
68
+ If this path returns 0 results AND the same query has succeeded recently in this project's run-log, retry the exact-folder query ONCE before escalating. WorkIQ has been observed to return spurious empty hits on first call.
69
+
70
+ ### Order 2 — Root-scope fallback
71
+
72
+ Only if Order 1 returns insufficient/empty results after the retry, OR the project has no folder hint, OR multiple plausible hints exist:
73
+
74
+ ```
75
+ workiq ask -q "Search my Outlook mailbox for emails from <window-start> onward in folder(s) <boundaries.email.mailboxes joined> (including all subfolders) related to <project aliases>. Return: sent datetime, subject, sender, recipients, folder path, message link, and a short relevance reason."
76
+ ```
77
+
78
+ ### Throttle handling (HARD stop in same run)
79
+
80
+ If WorkIQ returns `tooManyRequests`, `More than 3 retries performed`, or `We're experiencing high demand`:
81
+
82
+ - Mark `sources.email.coverage_state = throttled-tooManyRequests` in run-log.
83
+ - Write what enumeration HAS been collected so far to the message-index file with a `⚠️ throttled — partial enumeration` banner.
84
+ - Stop email pulls for this run. Do NOT issue broader mailbox queries.
85
+ - Continue with non-email sources.
86
+
87
+ ### Degraded list-only state
88
+
89
+ If Step A enumeration succeeds but Step B body fetch fails repeatedly (host + WorkIQ both returning empty / `body-unavailable` for >50% of messages):
90
+
91
+ - Set `sources.email.coverage_state = degraded-list-only`.
92
+ - Keep the message index with sent date, subject, sender, recipients, folder path, message link as evidence — these ARE usable signals at list level.
93
+ - Mark each affected message with `❌ body-unavailable` in the weekly stream file.
94
+ - Do NOT discard list-level evidence. Do NOT loop on broad fallback queries trying to rescue bodies.
95
+
96
+ ## Two-pass pull (REQUIRED — no single-call summarization)
32
97
 
33
- Write to: `<engagement-root>/<project>/Evidence/<alias>/email/snapshot/<entity>.md`
98
+ Same anti-summarization pattern as `pull-onenote` v2.2.0. WorkIQ summarizes by default — relying on a single "give me all emails this week" call returns a curated subset, not the full set. Doctrine: **enumerate the message list first, then fetch each message body individually.**
34
99
 
35
- Use template: `templates/snapshot/email-<kind>.template.md`
100
+ ### Step A — Enumerate the message list
36
101
 
37
- ## Stream pass
102
+ For each `boundaries.email.mailboxes[i]` × the date window (and optional sender_domains / subject_keywords):
38
103
 
39
- Per `evidence-thoroughness.instructions.md`: every substantive thread gets sender + recipients (to+cc) + subject + **full body or close paraphrase** + attachments + reply chain in order. Group by thread.
104
+ ```
105
+ workiq ask -q "List EVERY email in mailbox <mb> received between <start> and <end> [filtered to senders @<domain>] [containing subject keyword <kw>]. Return one row per message: messageId, internetMessageId, conversationId, sentDate, fromAddress, toAddresses, ccAddresses, subject, hasAttachments, sizeKB. Do NOT summarize, do NOT omit, do NOT group by thread yet, do NOT add commentary. Flat table only."
106
+ ```
107
+
108
+ If the response is summarized (heuristics: `"and N more"`, `"sample"`, row count noticeably less than expected, message bodies included instead of just the index): **retry once** with `Return as a flat table with no commentary, no grouping, no truncation. Message count must equal the actual count in the date window.`
109
+
110
+ Persist the enumerated list to `Evidence/<alias>/email/_index/<YYYY-MM-DD>_message-index.md` (date = Monday of the ISO week). This makes the run idempotent + resumable.
111
+
112
+ ### Step B — Per-message body fetch (one WorkIQ call per message)
113
+
114
+ For each message in the index, ONE WorkIQ call (per `workiq-only.instructions.md`):
115
+
116
+ ```
117
+ workiq ask -q "Get the FULL body of email with messageId <id> (mailbox <mb>). Return: full text body verbatim (no summarization, no truncation), all headers (from, to, cc, bcc, sentDate, subject, conversationId, internetMessageId, references, in-reply-to), attachment list (filename + size + mimetype, do NOT download binary), and any inline images as alt text or skip with marker."
118
+ ```
119
+
120
+ **FORBIDDEN** (per workiq-only): `m365_get_email`, `m365_search_emails`, `m365_list_emails`, Graph REST. Do NOT add them as fallbacks. If WorkIQ body fetch fails, use the doubled-strict retry then user-paste — NEVER fall through to a host m365_* call.
121
+
122
+ If body comes back < 200 chars or includes phrases like `"unable to retrieve"`, `"body unavailable"`, `"truncated"`: doubled-strict retry once with `Return the raw email body, no summary, no truncation. If you cannot return the full body, say "body-unavailable" exactly and nothing else.` On second failure, record that message as `❌ body-unavailable` in the weekly stream file + continue (or prompt user to paste — first-class evidence path per workiq-only).
123
+
124
+ ### Step C — Group by conversationId into threads
125
+
126
+ After all bodies fetched, group messages by `conversationId`. For each thread:
127
+
128
+ - Sort by `sentDate` ascending.
129
+ - Write a `## Thread: <subject>` block in the weekly stream file with:
130
+ - **AI Narrative Summary (REQUIRED FIRST, 3+ paragraphs)** — conversation arc, who's pushing what, decisions, asks, tone (per `evidence-thoroughness.instructions.md`).
131
+ - One `### Message <N> — <fromAddress> (<sentDate>)` block per message with: full headers (to/cc/subject), full body verbatim as a blockquote, attachment list.
132
+ - Append the thread block to the weekly file (`<YYYY-MM-DD>_email-stream.md`, date = Monday of the ISO week).
133
+
134
+ ## Snapshot pass
135
+
136
+ _(no snapshot for email — emails are inherently event-stream)_
137
+
138
+ ## Stream pass output
40
139
 
41
140
  Write to: `<engagement-root>/<project>/Evidence/<alias>/email/stream/<YYYY-MM-DD>_email-stream.md` (date = Monday of the ISO week the events fall in).
42
141
 
142
+ Write the message index to: `<engagement-root>/<project>/Evidence/<alias>/email/_index/<YYYY-MM-DD>_message-index.md`.
143
+
43
144
  Use template: `templates/weekly/email-summary.template.md`
44
145
 
45
- If a week file already exists, MERGE (dedupe by event ID, append new events, keep existing).
146
+ If a week file already exists, MERGE (dedupe by `internetMessageId`, append new threads, keep existing).
147
+
148
+ ## Tools (WorkIQ ONLY — per `workiq-only.instructions.md`)
46
149
 
47
- ## Tools (in order)
150
+ 1. **WorkIQ (`workiq ask`)** — the ONLY allowed path for both enumeration (Step A) and per-message body fetch (Step B). Always include the boundary keys + the verbatim/no-summary phrasing. Use the canonical prompts codified in `plugin/instructions/workiq-only.instructions.md` (Email Bodies section) — do NOT re-derive them.
151
+ 2. **Ask user (paste verbatim)** — first-class fallback when WorkIQ doubled-strict retry fails or returns `body-unavailable`. NOT a degradation; coverage.md labels it `Source: User paste on <ISO>`.
48
152
 
49
- 1. **WorkIQ** ````$WorkIQQuery`````
50
- 2. **Host fallback** — `m365_search_emails` / `m365_list_emails` scoped to `m365Auth.emailContext.folders`
51
- 3. **Graph REST** — last resort, soft-fail per `az-auth-conditional.instructions.md`.
52
- 4. **Ask user** — paste verbatim source content if all above fail.
153
+ **FORBIDDEN** for this skill (per workiq-only.instructions.md): `m365_get_email`, `m365_search_emails`, `m365_list_emails`, `m365_list_mail_folders`, `m365_search_mail`, Graph REST `https://graph.microsoft.com/v1.0/me/messages*`, Graph REST `https://graph.microsoft.com/v1.0/me/mailFolders*`. These tools have a near-100% failure rate in this workspace. Calling them is a DEFECT; coverage.md must NOT show them in the attempt trail.
53
154
 
54
- Document which path succeeded under `## Source Basis` in each output file.
155
+ Document the WorkIQ request-id + the exact prompt + which boundary was applied under `## Source Basis` in each output file (per workiq-only coverage.md requirements).
55
156
 
56
157
  ## Mutable hints to upsert (during the run, not at the end)
57
158
 
58
- If discovered, immediately write to `m365Mutable.knownSections.<project>.<source>` with `discoveredOn` + `confidence`:
159
+ If discovered, immediately write to `m365Mutable.knownSections.<project>.<source>` with `discoveredOn` + `confidence`. Mandatory in EVERY run mode (bootstrap, refresh, retry):
59
160
 
60
- - `emailContext.folder` — full path of the project's email folder
161
+ - `emailContext.folder` — full path of the project's email folder (upsert only when `confidence >= medium`)
61
162
  - `emailContext.folderId` — folder ID for direct lookup
163
+ - `emailContext.lastFastPathUsed` — boolean; whether Order 1 succeeded this run
164
+
165
+ Upsert only confident folder mappings. Do not write low-confidence guesses — that pollutes the hint cache and degrades future fast-path success.
62
166
 
63
167
  ## Update run-log
64
168
 
65
169
  After successful pass:
66
170
  - `sources.email.last_pulled = now`
67
- - `sources.email.watermark = now` (stream only; snapshot has no watermark)
68
- - `sources.email.item_count = <N>`
171
+ - `sources.email.watermark = now`
172
+ - `sources.email.coverage_state = ok | degraded-list-only | throttled-tooManyRequests | unavailable`
173
+ - `sources.email.retrieval_path = fast-folder | fast-folder-retry | root-fallback`
174
+ - `sources.email.messages_enumerated = <N>` — total messages discovered in scope
175
+ - `sources.email.messages_fetched = <N>` — total messages with full body captured
176
+ - `sources.email.messages_failed = <N>` — `body-unavailable` count
177
+ - `sources.email.threads = <N>` — number of conversations grouped
69
178
  - For each week touched, add to `weekly_files` index.
70
179
 
180
+ ## Depth bar (per `evidence-thoroughness.instructions.md`)
181
+
182
+ The pre-Step-C anti-summary doctrine + the Step-C AI Narrative Summary per thread together produce the depth users expect. Empty thread bodies are a defect — if Step B couldn't fetch a body, the message gets a `❌ body-unavailable` marker, NOT a paraphrase from the index row.
183
+
184
+ **Completeness bar:** `messages_enumerated` should equal `messages_fetched + messages_failed`. Drift surfaces in run-log; consolidate skill renders it as a Coverage Note.
185
+
71
186
  ## Stop conditions
72
187
 
188
+ - Boundary missing → refuse (see Boundaries above).
73
189
  - Hint missing AND fuzzy resolution returns 0 candidates → ask user once, persist answer to mutable, continue.
74
- - Multiple plausible candidatesask user to pick, persist answer.
75
- - WorkIQ fails AND host fallback fails AND Graph fails write evidence file with `❌ all paths failed` marker, log to run-log errors, continue with rest of run.
190
+ - Enumeration step (Step A) failed write `_index/<date>_message-index.md` with failure marker, log to run-log errors, do NOT proceed to per-message loop.
191
+ - Per-message fetch failedrecord marker, continue.
192
+ - All paths failed → write evidence file with `❌ all paths failed` marker, log to run-log errors, continue with rest of run.
@@ -1,17 +1,29 @@
1
1
  ---
2
2
  name: "pull-meetings"
3
- version: "2.1.0"
4
- description: "Pull Meetings evidence (snapshot: series-index; stream: per-meeting transcripts + decisions + actions). WorkIQ-first; falls back to chat reconstruction when transcripts don't exist."
3
+ version: "2.2.1"
4
+ description: "Pull Meetings evidence in THREE shapes: snapshot/ series-index, stream/ per-meeting curated blocks, AND verbatim/ raw immutable folder per meeting (meetings expire — verbatim/ MUST contain the full transcript text, not just chat). v2.4.0: WorkIQ-ONLY for transcript capture per workiq-only.instructions.md (m365_get_transcript / Graph REST FORBIDDEN — they have near-100% failure rate in this workspace). Chat is parallel supporting evidence (via m365_list_chat_messages structured dump), NEVER a transcript substitute."
5
5
  ---
6
6
 
7
7
  # Skill: pull-meetings
8
8
 
9
- Pulls **meetings** evidence in two shapes per `snapshot-vs-stream.instructions.md`:
10
9
 
11
- - **snapshot/** series-index.md listing all recurring meeting series for this project (subject, recurrence, organizer, current attendees)
12
- - **stream/**per-meeting blocks: attendees (req/opt/actual), agenda, **chronological transcript walk-through with verbatim quotes + timestamps**, decisions, actions, open questions, artifact links
10
+ > **v3.7.6 + v3.10.0 + v3.11.0 contracts** This skill operates under six HARD-rule doctrines:
11
+ > - `verbatim-by-default.instructions.md`full bodies/notetext/fields by default; no preview-grade pulls accepted.
12
+ > - `meetings-verbatim-required.instructions.md` (v3.10.0) — meetings are an EXPIRING evidence class; every captured meeting MUST also produce a sibling `verbatim/<YYYY-MM-DD-HHMM>_<slug>/` folder with raw chat + transcript + recording URL. Curated snapshot alone is a defect.
13
+ > - **`workiq-only.instructions.md` (v3.11.0)** — transcripts, facilitator notes, calendar discovery, and chat-thread human rendering go through WorkIQ ONLY. `m365_get_transcript`, `m365_get_facilitator_notes`, `m365_list_meetings`, `m365_list_events`, and Graph REST URLs are FORBIDDEN as fallbacks (they fail nearly every call). The canonical WorkIQ prompts are codified in that instruction — do not re-discover them.
14
+ > - `capture-learnings.instructions.md` — every fix/discovery is logged to `plugin/learnings/<source>.md` immediately.
15
+ > - `cleanup-on-resolution.instructions.md` — when a value resolves, all stale `no-match` / `not yet` notes referencing the prior unresolved state must be rewritten in the same turn.
16
+ > - `run-reports.instructions.md` — every refresh writes a per-user report under `Evidence/<alias>/refresh-reports/YYYY-MM-DD-HHMM_refresh.md`.
13
17
 
14
- Auth + retry + error logging per `auth-and-retry.instructions.md`. WorkIQ-first per `workiq-first.instructions.md`. Thoroughness per `evidence-thoroughness.instructions.md`; runtime detector + auto-retry + paste-prompt per `thoroughness-detector.instructions.md`. Citations per `citation-ledger.instructions.md`.
18
+ > **Canonical evidence layout** (HARD, kushi v3.12.1+): all artifacts produced by this skill MUST be written under `<project>/Evidence/<alias>/<source>/{snapshot,stream,...}/` sibling folders under `<project>/` (e.g. `<project>/<source>-context/`, `<project>/<source>/`, `<project>/_Weekly Summaries/`) are FORBIDDEN. See `evidence-layout-canonical.instructions.md`.
19
+
20
+ Pulls **meetings** evidence in three shapes per `snapshot-vs-stream.instructions.md` + `meetings-verbatim-required.instructions.md`:
21
+
22
+ - **snapshot/** — `series-index.md` listing all recurring meeting series for this project (subject, recurrence, organizer, current attendees)
23
+ - **stream/** — per-meeting curated blocks: attendees (req/opt/actual), agenda, **chronological transcript walk-through with verbatim quotes + timestamps**, decisions, actions, open questions, artifact links. Each block MUST cite the matching verbatim/ folder.
24
+ - **verbatim/** (REQUIRED, NEW v2.2.0) — per-meeting subfolder `verbatim/<YYYY-MM-DD-HHMM>_<slug>/` containing the raw immutable capture: `captured-at.txt`, `chat-messages.json`, `chat-messages.md`, `transcript.vtt` or `transcript-source.md`, `recording-url.txt`, `recap-card.md`, `attachments/`, `coverage.md`. See `templates/snapshot/meeting-verbatim.template.md` for the contract.
25
+
26
+ Auth + retry + error logging per `auth-and-retry.instructions.md`. WorkIQ-only per `workiq-only.instructions.md`. Thoroughness per `evidence-thoroughness.instructions.md`; runtime detector + auto-retry + paste-prompt per `thoroughness-detector.instructions.md`. Citations per `citation-ledger.instructions.md`.
15
27
 
16
28
  ## Inputs
17
29
 
@@ -20,39 +32,110 @@ Auth + retry + error logging per `auth-and-retry.instructions.md`. WorkIQ-first
20
32
  - `<window>` — date range. For snapshot: ignored (always full re-fetch). For stream: `(from, to)`.
21
33
  - (read) `<engagement-root>/.project-evidence/m365/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints (`calendarContext.subjectKeywords`, `calendarContext.knownSeries`).
22
34
 
23
- ## Discovery (snapshot pass)
35
+ ## Discovery (snapshot pass — WorkIQ ONLY per `workiq-only.instructions.md`)
24
36
 
25
- 1. `m365_list_meetings` over the window with `subjectKeywords` filter (post-filter — Graph search is relevance-ranked, not exact).
26
- 2. For each match: capture `id`, `subject`, `start`, `end`, `organizer`, `joinUrl`. Persist to mutable as `calendarContext.knownSeries[]`.
27
- 3. Cross-reference with Teams chat IDs from `pull-teams` (meeting chat IDs of form `19:meeting_…@thread.v2`). Each match enriches with attendee roster + chat ID.
37
+ 1. **WorkIQ meeting discovery** (canonical prompt from `workiq-only.instructions.md` Calendar/online meetings section):
38
+ > `List my Teams meetings between <start> and <end> where the subject contains "<token>". Return subject, date, start time, organizer, joinUrl, and Teams chat id.`
39
+ - Issued ONCE per token in `calendarContext.subjectKeywords`.
40
+ - For each match: capture `subject`, `start`, `end`, `organizer`, `joinUrl`, `chatId`. Persist to mutable as `calendarContext.knownSeries[]`.
41
+ 2. Cross-reference returned `chatId` values with `boundaries.teams.chat_ids[]` — every meeting whose chat-id falls inside the teams boundary is treated as in-scope. This enriches each meeting with the attendee roster the `pull-teams` snapshot already captured.
42
+
43
+ **FORBIDDEN** for discovery: `m365_list_meetings`, `m365_list_events`, Graph REST `/me/calendar/events`, Graph REST `/me/onlineMeetings`. Use WorkIQ.
28
44
 
29
45
  Write `snapshot/series-index.md` listing all matched series. Updated every refresh (organizers/attendees may change).
30
46
 
31
- ## Per-meeting stream pass path cascade
47
+ ## Boundaries (REQUIREDsee `scope-boundaries.instructions.md`)
48
+
49
+ This skill REFUSES to query unless `<engagement-root>/<project>/integrations.yml#boundaries.meetings` is satisfied:
50
+
51
+ - `boundaries.meetings.series_join_urls` — REQUIRED, non-empty (pinned join URLs; no subject-keyword fuzz).
52
+ - `boundaries.meetings.organizer_emails` — optional additional filter.
53
+ - `boundaries.date_window_days` — defaults to 30 if absent.
54
+
55
+ The pre-existing `subjectKeywords` / `knownSeries` discovery loop is now a **bootstrap-time aid only** — it helps populate `boundaries.meetings.series_join_urls` once. At pull time, only meetings whose `joinUrl` is in the boundary list are processed.
56
+
57
+ Refusal message when boundary is missing:
58
+
59
+ ```
60
+ meetings-boundary-missing — add boundaries.meetings.series_join_urls to
61
+ <engagement-root>/<project>/integrations.yml. See doctrine in
62
+ plugin/instructions/scope-boundaries.instructions.md.
63
+ ```
64
+
65
+ ## Per-meeting capture cascade — verbatim/ FIRST, then curated stream
66
+
67
+ For EACH meeting in the window, the cascade has **two halves**: (A) write raw artifacts to `verbatim/<YYYY-MM-DD-HHMM>_<slug>/`, then (B) produce the curated stream block citing those verbatim files. Half A is mandatory before Half B per `meetings-verbatim-required.instructions.md`.
68
+
69
+ ### Half A — verbatim/ capture (REQUIRED, transcript-first, WorkIQ-only per workiq-only.instructions.md)
70
+
71
+ For every meeting, BEFORE writing any curated text:
72
+
73
+ 0. Compute slug + timestamp, create `Evidence/<alias>/meetings/verbatim/<YYYY-MM-DD-HHMM>_<slug>/`, write `captured-at.txt` (started_at, kushi_version).
32
74
 
33
- For EACH meeting in the window, attempt to capture transcript text via this cascade. Apply retry pattern per `auth-and-retry.instructions.md`. Record errors[] entry for each path attempted.
75
+ 1. **Transcript cascade WorkIQ-only, EXHAUSTIVE, in order. Do NOT stop at first weak result; record each path's result in `coverage.md`.** Per `meetings-verbatim-required.instructions.md` "Transcript capture cascade":
76
+ - **a. WorkIQ full-transcript pull (REQUIRED first attempt)** with the canonical prompt from `workiq-only.instructions.md`:
77
+ > `Find the Teams meeting titled "<subject>" that occurred on <YYYY-MM-DD>. Return the full transcript verbatim with speaker labels and timestamps. Do not summarize.`
78
+ - If WorkIQ returns full speaker-turn text (≥ ~10 distinct `Name:` lines) → strip `request-id:` prefix, save body as `transcript.txt` with header (source, query, request-id, fidelity).
79
+ - If WorkIQ returns a summary (paragraphs only, no speaker turns) → run the **doubled-strict retry** from `workiq-only.instructions.md` once. If still summary-only, save as `transcript-source.md` with the `WARNING: NOT a verbatim transcript` header.
80
+ - **b. WorkIQ Copilot recap (SUPPLEMENTARY, always attempt)**:
81
+ > `Get the Copilot meeting recap (decisions, action items, key points) for the Teams meeting "<subject>" on <YYYY-MM-DD>. Return the FULL recap card verbatim. Do not summarize.`
82
+ - Save as `facilitator-notes.md`. Supplementary; does NOT replace the transcript.
83
+ - **c. WorkIQ recording-URL discovery**:
84
+ > `For the meeting "<subject>" on <YYYY-MM-DD>, return the SharePoint Stream recording URL if it exists, and the calendar event body.`
85
+ - Save URL(s) to `recording-url.txt`. If a `.mp4` URL is present, downloading the recording binary is governed by the `pull-meetings` recording-download carve-out (size < 200MB) — this is the ONLY allowed `m365_download_file` call in any kushi pull-* skill, because the recording is binary media and WorkIQ does not download binaries.
86
+ - **d. User-paste fallback (first-class, NOT a degradation)** — when (a) returns empty/`body-unavailable` after doubled-strict retry AND (b) returns nothing usable: prompt the user to paste verbatim transcript or notes. Save to `transcript.txt` with header `User-pasted; WorkIQ returned no transcript on <ISO timestamp>`.
34
87
 
35
- 1. **WorkIQ**: `Get full transcript and Copilot recap for meeting "<subject>" on <date>`.
36
- 2. **`m365_get_transcript`** with the meeting's `joinUrl`.
37
- 3. **Reconstruction from chat** (NOT a failure — first-class fallback). When transcripts simply don't exist (transcription was off, or VTT not yet attached), reconstruct evidence from:
38
- - Meeting chat thread: `m365_list_chat_messages(chatId)` — captured already by `pull-teams`.
39
- - Pre/post-meeting context messages.
40
- - Any Copilot recap card posted to the chat.
41
- Mark the per-meeting block `Source basis: reconstructed from meeting chat (no transcript)`. This is **evidence**, not a gap.
42
- 4. **Ask user**: paste verbatim transcript or notes if all above produced nothing usable.
88
+ **FORBIDDEN** (do NOT attempt; do NOT log as cascade steps in coverage.md): `m365_get_transcript`, `m365_get_facilitator_notes`, `m365_list_meetings`, `m365_list_events`, Graph REST `/me/onlineMeetings/.../transcripts/.../content`. These have a near-100% failure rate in this workspace and pollute the coverage trail. Use WorkIQ steps (a)/(b)/(c).
43
89
 
44
- A meeting block built from chat reconstruction with attendees + key decisions + actions is **valid evidence**. Do NOT mark such a meeting as `failed`. Mark it `partial` only if NO source produced any content (no transcript AND no chat messages).
90
+ 2. **Chat capture (ALWAYS, parallel with step 1 — allowed exception to workiq-only)**: `m365_list_chat_messages(chatId)` → `chat-messages.json` (raw structured-data dump) + `chat-messages.md` (rendered, one heading-block per message). Chat is **supporting evidence**; it is **NEVER** a transcript substitute. If `m365_list_chat_messages` fails, run the WorkIQ chat-thread prompt as fallback (per `workiq-only.instructions.md` Teams chat thread section).
91
+
92
+ 3. Walk chat for attachments → `m365_download_file` each → `attachments/<original-name>` (binary download carve-out, same as recording.mp4).
93
+
94
+ 4. Walk chat for Copilot recap card → `recap-card.md` verbatim.
95
+
96
+ 5. **Classify** the verbatim folder per the doctrine:
97
+ - `transcript-complete` — at least one of `transcript.vtt` / `transcript.txt` / `transcript-source.md` is non-empty.
98
+ - `transcript-missing` — only `chat-messages.*` present; all WorkIQ cascade paths (1a–1d) returned empty.
99
+
100
+ 6. Write `coverage.md` with classification + per-path attempt log (every WorkIQ path attempted, with request-id, even successful ones) + source-basis classification for stream/.
101
+
102
+ 7. Update `captured-at.txt` with `completed_at` + `final_status` (one of `transcript-complete-text` / `transcript-complete-summary-only` / `transcript-missing-chat-only` / `unrecoverable`).
103
+
104
+ If 1a–1d ALL return empty AND step 2 chat is also empty AND user paste is declined → `unrecoverable`. Apply retry pattern per `auth-and-retry.instructions.md`.
105
+
106
+ ### Half B — curated stream/ block (cites verbatim/)
107
+
108
+ Build the per-meeting block in `stream/<week>_meetings-stream.md` using ONLY content already persisted in verbatim/. Every assertion cites a verbatim file. Preferred citation chain:
109
+ - `[source: Evidence/<alias>/meetings/verbatim/<dir>/transcript.vtt · <date>]` when transcript-complete-vtt.
110
+ - `[source: Evidence/<alias>/meetings/verbatim/<dir>/transcript.txt · <date>]` when transcript-complete-text.
111
+ - `[source: Evidence/<alias>/meetings/verbatim/<dir>/transcript-source.md · <date>]` when only Copilot summary (always note the warning).
112
+ - `[source: Evidence/<alias>/meetings/verbatim/<dir>/chat-messages.md · <date>]` only for assertions backed by chat (not transcript content).
113
+
114
+ `Source basis` line in the curated block reflects verbatim classification:
115
+ - `transcript (raw VTT)` — transcript.vtt present
116
+ - `transcript (plain text)` — transcript.txt present, no VTT
117
+ - `transcript (WorkIQ Copilot summary — NOT verbatim)` — only transcript-source.md
118
+ - `❌ no-transcript-recovered-chat-only` — only chat-messages.*
119
+ - `❌ unrecoverable` — only captured-at.txt + coverage.md
120
+
121
+ A meeting with `transcript-missing-chat-only` is valid evidence in a degraded sense — actions/decisions citeable from chat are still useful — but the curated block MUST flag the gap prominently and the run-log MUST record `transcript-unrecoverable` for that meeting.
122
+
123
+ ### Ask-user fallback
124
+
125
+ When the entire cascade (1a–1d + chat) produces nothing usable, ask the user to paste verbatim transcript or notes. Persist any paste to `verbatim/<dir>/transcript.txt` with header `User-pasted; automated paths returned no transcript on <ISO timestamp>`.
45
126
 
46
127
  ## Stream pass
47
128
 
48
- Per `evidence-thoroughness.instructions.md`: every meeting in the window gets a full per-meeting block. **A 30-line meetings file for a week with 2 meetings is a defect — expect 200+ lines.**
129
+ Per `evidence-thoroughness.instructions.md`: every meeting in the window gets a full per-meeting block whose **FIRST sub-section is an AI Narrative Summary (REQUIRED, 5+ paragraphs)** covering the whole meeting end-to-end — context, what was discussed in what order, who took which position, the reasoning, the back-and-forth, soft signals, sentiment, what landed and what stayed open (cited to transcript timestamps). Then attendees → agenda → Detailed Discussion Summary (topic-organized) → chronological transcript walk-through with verbatim quotes → Decisions → Open Questions → Next Steps → Action Items → Risks → Customer Asks → Coverage Notes. **A 30-line meetings file for a week with 2 meetings is a defect — expect 300+ lines (the AI Narrative Summary alone is typically 30-50 lines per meeting).** A per-meeting block missing the AI Narrative Summary as the first sub-section is a defect even if every other section is present.
49
130
 
50
131
  Write to: `<engagement-root>/<project>/Evidence/<alias>/Meetings/stream/<YYYY-MM-DD>_meetings-stream.md` (date = Monday of the ISO week).
51
132
 
52
- Use template: `templates/weekly/meetings-summary.template.md`
133
+ Use template: `templates/weekly/meetings-stream.template.md`
53
134
 
54
135
  If a week file already exists, MERGE (dedupe by event ID, append new events, keep existing).
55
136
 
137
+ **Verbatim sibling required.** Every per-meeting block written here MUST have a matching `Evidence/<alias>/meetings/verbatim/<YYYY-MM-DD-HHMM>_<slug>/` folder produced by Half A. The block's `Source basis` line and `Artifacts` section must cite the verbatim folder's relative path. Self-check D13 enforces this.
138
+
56
139
  ## Mutable hints to upsert (during the run, not at the end)
57
140
 
58
141
  If discovered, immediately write to `m365Mutable.knownSections.<project>.calendarContext` with `discoveredOn` + `confidence`:
@@ -73,5 +156,6 @@ After the pass:
73
156
  ## Stop conditions
74
157
 
75
158
  - Subject keyword resolution returns 0 candidates → ask user once for keywords, persist to mutable, continue.
76
- - A meeting has neither transcript NOR chat messages → write `❌ no source available` block, log error, continue.
77
- - All paths failed for ALL meetings in the window → mark source `failed`, write a single `❌ all paths failed` evidence file with actionable next step.
159
+ - A meeting has neither transcript NOR chat messages → write `verbatim/<dir>/coverage.md` documenting every failed path + write `❌ source-expired-or-unrecoverable` block in stream/, log error, continue.
160
+ - All paths failed for ALL meetings in the window → mark source `failed`, write a single `❌ all paths failed` evidence file with actionable next step. Verbatim/ folders for each meeting still get created with captured-at.txt + coverage.md (the empty-folder audit trail is itself evidence).
161
+ - A per-meeting block lacks a sibling `verbatim/<YYYY-MM-DD-HHMM>_<slug>/` directory → **defect** per `meetings-verbatim-required.instructions.md`. Re-run Half A immediately; do NOT ship the curated block alone.
@@ -0,0 +1,84 @@
1
+ # pull-misc runner
2
+
3
+ Pulls evidence from a user-curated link list (`<project>/external-links.txt`). Handles types that don't fit a dedicated `pull-*` skill: Loop pages, public web pages, learn.microsoft.com / docs sites, GitHub repos, local files. Delegated types (onenote, sharepoint, ado) are recorded as `delegated` and pulled by their dedicated skills.
4
+
5
+ See `SKILL.md` for the full doctrine.
6
+
7
+ ## One-time setup
8
+
9
+ ```pwsh
10
+ # Install deps in the kushi repo
11
+ cd C:\Usha\ISERepos\kushi
12
+ npm install playwright jsdom @mozilla/readability
13
+ npx playwright install chromium
14
+
15
+ # (Loop links only) Seed the Playwright profile by reusing the OneNote profile
16
+ # If you've already done OneNote bootstrap, you're done — Loop reuses it.
17
+ node plugin/skills/pull-onenote/runner.mjs --bootstrap
18
+ ```
19
+
20
+ ## Per-project run
21
+
22
+ ```pwsh
23
+ node plugin/skills/pull-misc/runner.mjs `
24
+ --project "ABN AMRO" `
25
+ --links-file "C:\Users\ushak\OneDrive - Microsoft\ISE\Engagement Assets\ABN AMRO\external-links.txt" `
26
+ --engagement-root "C:\Users\ushak\OneDrive - Microsoft\ISE\Engagement Assets" `
27
+ --headless
28
+ ```
29
+
30
+ ## Output
31
+
32
+ Single JSON object on stdout:
33
+
34
+ ```json
35
+ {
36
+ "project": "ABN AMRO",
37
+ "linksFile": "...",
38
+ "runStatus": "ok | auth-required | partial",
39
+ "counts": {
40
+ "total": 12,
41
+ "captured": 8,
42
+ "placeholder": 2,
43
+ "delegated": 1,
44
+ "auth_required": 0,
45
+ "fetch_failed": 1,
46
+ "skipped_binary": 0
47
+ },
48
+ "links": [
49
+ { "type": "web", "title": "...", "url": "...", "last_status": "captured", "captured_via": "http",
50
+ "http_status": 200, "content_type": "text/html", "etag": "...", "last_modified_http": "...",
51
+ "body": "...", "char_count": 18420, "captured_at": "..." }
52
+ /* ... */
53
+ ]
54
+ }
55
+ ```
56
+
57
+ ## Filtering
58
+
59
+ ```pwsh
60
+ # Only loop links
61
+ node runner.mjs --project ABN ... --types loop
62
+
63
+ # Only specific titles (substring match)
64
+ node runner.mjs --project ABN ... --titles "Workshop,Architecture"
65
+
66
+ # Combine
67
+ node runner.mjs --project ABN ... --types web,loop --titles "Architecture"
68
+ ```
69
+
70
+ ## Scheduled / unattended runs
71
+
72
+ - Browser branch (loop) reuses `~/.copilot/playwright-profile/onenote/`. When MFA fires, runner marks loop links `auth-required` and exits cleanly. User does one interactive bootstrap; next scheduled run silent.
73
+ - HTTP branch needs no auth for public links. For sites behind SSO (auth'd Confluence, internal dashboards) it returns `auth-required` and is currently NOT supported — paste the content into a `file` link as a workaround, OR add a per-site auth handler in a future version.
74
+ - `placeholder` links surface in the run report every refresh until the user fills them in.
75
+
76
+ ## Troubleshooting
77
+
78
+ | Symptom | Cause | Fix |
79
+ |---|---|---|
80
+ | `Cannot find module 'jsdom'` | deps not installed | `npm install jsdom @mozilla/readability` in repo root |
81
+ | All loop links `auth-required` | profile expired | `node plugin/skills/pull-onenote/runner.mjs --bootstrap` |
82
+ | `web` link returns near-empty body | site is SPA / requires JS render | Add it as a `loop` type to use browser path; or paste content as `file` |
83
+ | `confluence` link returns auth-required | private Confluence, no anon access | not supported in v0.1; paste content as `file` |
84
+ | `pdf` link returns "text extraction not yet implemented" | pdfjs not bundled | text extract is a v0.2 item; manually extract for now |