kushi-agents 3.4.2 → 3.13.0

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 (73) hide show
  1. package/.github/copilot-instructions.kushi.md +38 -0
  2. package/README.md +33 -0
  3. package/bin/cli.mjs +2 -0
  4. package/package.json +17 -4
  5. package/plugin/agents/kushi.agent.md +155 -147
  6. package/plugin/instructions/ado-bootstrap-discovery.instructions.md +111 -0
  7. package/plugin/instructions/ado-engagement-tree.instructions.md +73 -0
  8. package/plugin/instructions/answer-from-evidence.instructions.md +1 -1
  9. package/plugin/instructions/auth-and-retry.instructions.md +51 -16
  10. package/plugin/instructions/azure-auth-patterns.instructions.md +13 -6
  11. package/plugin/instructions/bootstrap-status-format.instructions.md +113 -0
  12. package/plugin/instructions/capture-learnings.instructions.md +95 -0
  13. package/plugin/instructions/cleanup-on-resolution.instructions.md +69 -0
  14. package/plugin/instructions/crm-bootstrap-discovery.instructions.md +79 -0
  15. package/plugin/instructions/crm-internal-vs-confirmed.instructions.md +79 -0
  16. package/plugin/instructions/evidence-confidence-ladder.instructions.md +66 -0
  17. package/plugin/instructions/evidence-layout-canonical.instructions.md +115 -0
  18. package/plugin/instructions/evidence-thoroughness.instructions.md +82 -12
  19. package/plugin/instructions/full-view-gate.instructions.md +91 -0
  20. package/plugin/instructions/m365-id-registry.instructions.md +134 -0
  21. package/plugin/instructions/meetings-verbatim-required.instructions.md +176 -0
  22. package/plugin/instructions/run-reports.instructions.md +129 -0
  23. package/plugin/instructions/scope-boundaries.instructions.md +218 -0
  24. package/plugin/instructions/snapshot-vs-stream.instructions.md +2 -0
  25. package/plugin/instructions/update-ledger.instructions.md +132 -0
  26. package/plugin/instructions/verbatim-by-default.instructions.md +73 -0
  27. package/plugin/instructions/workiq-first.instructions.md +15 -31
  28. package/plugin/instructions/workiq-only.instructions.md +193 -0
  29. package/plugin/learnings/README.md +50 -0
  30. package/plugin/learnings/ado.md +45 -0
  31. package/plugin/learnings/crm.md +96 -0
  32. package/plugin/learnings/cross-cutting.md +36 -0
  33. package/plugin/learnings/email.md +33 -0
  34. package/plugin/learnings/meetings.md +30 -0
  35. package/plugin/learnings/misc.md +46 -0
  36. package/plugin/learnings/onenote.md +215 -0
  37. package/plugin/learnings/sharepoint.md +5 -0
  38. package/plugin/learnings/teams.md +5 -0
  39. package/plugin/plugin.json +22 -2
  40. package/plugin/prompts/apply-ado.prompt.md +14 -0
  41. package/plugin/prompts/propose-ado.prompt.md +12 -0
  42. package/plugin/reference-packs/fde/crm-field-manifest.md +165 -0
  43. package/plugin/skills/apply-ado-update/SKILL.md +125 -0
  44. package/plugin/skills/ask-project/SKILL.md +2 -0
  45. package/plugin/skills/bootstrap-project/SKILL.md +81 -3
  46. package/plugin/skills/propose-ado-update/SKILL.md +108 -0
  47. package/plugin/skills/pull-ado/SKILL.md +173 -23
  48. package/plugin/skills/pull-crm/SKILL.md +168 -15
  49. package/plugin/skills/pull-email/SKILL.md +139 -22
  50. package/plugin/skills/pull-meetings/SKILL.md +109 -25
  51. package/plugin/skills/pull-misc/README.md +84 -0
  52. package/plugin/skills/pull-misc/SKILL.md +257 -0
  53. package/plugin/skills/pull-misc/runner.mjs +280 -0
  54. package/plugin/skills/pull-onenote/README.md +90 -0
  55. package/plugin/skills/pull-onenote/SKILL.md +400 -51
  56. package/plugin/skills/pull-onenote/runner.mjs +356 -0
  57. package/plugin/skills/pull-onenote/scripts/recapture-section-url.mjs +295 -0
  58. package/plugin/skills/pull-onenote/write-snapshot.mjs +271 -0
  59. package/plugin/skills/pull-sharepoint/SKILL.md +44 -12
  60. package/plugin/skills/pull-teams/SKILL.md +40 -11
  61. package/plugin/skills/refresh-project/SKILL.md +33 -2
  62. package/plugin/skills/self-check/run.ps1 +186 -4
  63. package/plugin/templates/ado-update/discussion-comment.template.md +26 -0
  64. package/plugin/templates/ado-update/integrations-ado-writes.example.yml +49 -0
  65. package/plugin/templates/ado-update/proposed.template.md +78 -0
  66. package/plugin/templates/init/external-links.template.txt +30 -0
  67. package/plugin/templates/init/project-integrations.template.yml +57 -2
  68. package/plugin/templates/snapshot/meeting-verbatim.template.md +110 -0
  69. package/plugin/templates/snapshot/meetings-series-index.template.md +3 -1
  70. package/plugin/templates/snapshot/onenote-page.template.md +92 -23
  71. package/plugin/templates/weekly/meetings-stream.template.md +11 -6
  72. package/src/copilot-instructions.mjs +80 -0
  73. package/src/main.mjs +18 -1
@@ -1,11 +1,21 @@
1
1
  ---
2
2
  name: "bootstrap-project"
3
- version: "2.1.0"
4
- description: "First-time setup for a project: machine preflight (WorkIQ check, optional az login), side-by-side config (templates -> live filled), engagement-root + project resolution, initial 30d snapshot+stream pull across all enabled sources. Builds State/ only on the `full` profile (skipped on `standard`). Idempotent safe to re-run."
3
+ version: "2.3.1"
4
+ description: "First-time setup for a project: machine preflight, side-by-side config, engagement-root + project resolution, initial 30d snapshot+stream pull across all enabled sources. Verbatim-by-default per `verbatim-by-default.instructions.md` every enabled source dispatched, no silent skips. CRM bootstrap discovery REQUIRED to run live Dataverse 4-step resolution before declaring disabled per `crm-bootstrap-discovery.instructions.md` (v2.3.0). Writes per-user refresh report per `run-reports.instructions.md`. Cleans stale no-match notes on resolution per `cleanup-on-resolution.instructions.md`. Builds State/ only on `full` profile. Idempotent."
5
5
  ---
6
6
 
7
7
  # Skill: bootstrap-project
8
8
 
9
+ > **Verbatim-by-default**: every `pull-<source>` whose boundary is satisfied MUST be dispatched. Silent skips are defects. See `verbatim-by-default.instructions.md`.
10
+ >
11
+ > **Per-user bootstrap report REQUIRED**: at end of run, write `<project>/Evidence/<alias>/refresh-reports/<YYYY-MM-DD-HHmm>_bootstrap.md` per `run-reports.instructions.md`. Distinct from `<project>/bootstrap-status.md` (durable state) — this is per-user run narrative.
12
+ >
13
+ > **Cleanup on resolution**: when this run resolves an ID/folder/section, prune stale `no-match` / probe-trail notes per `cleanup-on-resolution.instructions.md`.
14
+ >
15
+ > **Capture learnings**: any quirk / fix discovered mid-run is appended to `<KUSHI_ROOT>/plugin/learnings/<source>.md` in the same turn per `capture-learnings.instructions.md`.
16
+ >
17
+ > **Canonical evidence layout** (HARD, kushi v3.12.1+): every per-source artifact lands under `<project>/Evidence/<alias>/<source>/{snapshot,stream,...}/` and **nowhere else** under `<project>/`. Sibling folders like `<project>/email-context/`, `<project>/notes/`, `<project>/_Weekly Summaries/` are FORBIDDEN. Bootstrap MUST (a) scaffold the canonical tree, and (b) before pulling, scan `<project>/` for non-canonical source-output folders and migrate each to `Evidence/<alias>/<source>/_legacy_<original-name>_pre-bootstrap/` (move, do not delete; log under `## Layout migrations` in the bootstrap report; add a `runs:` entry with `type: layout-migration`). See `evidence-layout-canonical.instructions.md`.
18
+
9
19
  Run this when the user says any of: "bootstrap a new project", "set up project evidence for `<X>`", "add me to project `<X>`", "I'm new to `<X>`", "do it all for `<X>`" (and the project has no Evidence/ folder yet).
10
20
 
11
21
  ## Profile-aware behavior
@@ -17,6 +27,10 @@ This skill is **profile-aware** as of Kushi v3.3.0:
17
27
 
18
28
  The active profile is read from `kushi-install.json#profile` next to this agent file. If absent, default to `standard`.
19
29
 
30
+ ## Bootstrap-status artifact
31
+
32
+ After every run (success or coverage-gaps), write `<project>/bootstrap-status.md` per the format contract in `instructions/bootstrap-status-format.instructions.md`. This is the project's fast-orientation artifact — required tables, normalized status vocabulary (`resolved`, `populated`, `unsynced`, `degraded-list-only`, `throttled-tooManyRequests`, `ado-not-complete`, `completed-with-coverage-gaps`), one-line final status. Do NOT inline run history here; that goes in `update-status.md`.
33
+
20
34
  ## Inputs
21
35
 
22
36
  - `<project>` — engagement name (fuzzy-matched per `engagement-root-resolution.instructions.md`).
@@ -62,6 +76,8 @@ Optional (only if user enables CRM/ADO):
62
76
  | `<engagement-root>/.project-evidence/crm/config.yml` | `templates/init/crm-config.template.yml` |
63
77
  | `<engagement-root>/.project-evidence/ado/config.yml` | `templates/init/ado-config.template.yml` |
64
78
 
79
+ **Boundaries upgrade for existing projects (v3.7.0+):** If `<engagement-root>/<project>/integrations.yml` already exists but has no top-level `boundaries:` key, append the scaffolded `boundaries:` block from `templates/init/project-integrations.template.yml` (preserving every existing key). Then proceed to Step 4 to populate it.
80
+
65
81
  ### Step 3 — Project folder scaffold
66
82
 
67
83
  Create the project folder structure (per `engagement-root-resolution.instructions.md`):
@@ -85,7 +101,66 @@ The `State/` subtree is created **only on `full` profile**. On `standard`, only
85
101
 
86
102
  ### Step 4 — Initial pull (last 30 days)
87
103
 
88
- Dispatch to each enabled per-source skill with `--window last 30 days`:
104
+ **Boundaries gate** (kushi v3.7.0+, per `scope-boundaries.instructions.md`): before dispatching any `pull-*`, read `<engagement-root>/<project>/integrations.yml#boundaries` and verify each enabled source has its required boundary key populated. For sources where bootstrap can auto-populate from existing `m365-mutable.json` discovery hints (e.g. a previously-discovered `section_file_id` lands in `boundaries.onenote.section_file_ids`), do so and continue. For sources where the boundary cannot be auto-populated, write the source as **disabled** in `integrations.yml` and add a one-liner to `<project>/OPEN-QUESTIONS-DRAFT.md` (or `State/09_open-questions.md` on `full` profile) asking the user to fill the boundary and re-enable.
105
+
106
+ For CRM and ADO additionally verify the global config files exist (`<engagement-root>/.project-evidence/{crm,ado}/config.yml`) with non-placeholder values; if missing, scaffold from `templates/init/{crm,ado}-config.template.yml` and park in Open Questions with the path and template reference. **Do NOT auto-improvise** by inferring a tenant/org or by narrating CRM evidence from email — both are explicit anti-patterns in v3.7.0.
107
+
108
+ **CRM discovery is REQUIRED before declaring `disabled` (kushi v3.11.0+, per `crm-bootstrap-discovery.instructions.md`).** If `<engagement-root>/.project-evidence/crm/config.yml` exists and `az` auth succeeds, bootstrap MUST run the full 4-step Dataverse REST resolution sequence from `pull-crm/SKILL.md#resolution-order-when-crmrecordid-is-unset` (title-first → all matching accounts → wide-text → recent-slice → ask user) against the live endpoint before writing `boundaries.crm.disabled: true`. Any other path that sets `disabled: true` is a defect. If steps 1–4 all return 0, present the top 5 candidates from step 4 to the user before final disposition. Log the full attempt trail (queries + counts + outcome) to the bootstrap refresh-report under `## CRM resolution attempts`. If auth fails or Dataverse is unreachable, leave the boundary empty with `reason: 'crm-auth-unavailable-<date>'` — NOT `disabled: true` — so the next refresh retries.
109
+
110
+ #### Step 4a — Discovery & registry persistence (REQUIRED, kushi v3.7.8+, per `m365-id-registry.instructions.md`)
111
+
112
+ **Doctrine: bootstrap discovers, refresh consumes.** Bootstrap is the ONLY phase that probes WorkIQ to resolve canonical M365 identifiers. Refresh runs MUST read these from `m365-mutable.json#knownSections.<project>` and pass them into the index extractor verbatim. Refresh must NEVER re-discover — that is the source of "OneNote works for me sometimes" non-determinism.
113
+
114
+ For each enabled source, resolve and persist the canonical lookup keys into `<engagement-root>/.project-evidence/m365/m365-mutable.json#knownSections.<projectKey>`. The schema is fixed — populate every key the source supports:
115
+
116
+ ```jsonc
117
+ "knownSections": {
118
+ "<projectKey>": {
119
+ // OneNote (pull-onenote uses these as Step A inputs)
120
+ "one_sectionName": "<displayName>.one",
121
+ "one_sectionFileId": "<wdsectionfileid GUID>", // PRIMARY — verbatim into wdsectionfileid = <id>
122
+ "one_sectionGroupId": "<wdsectiongroupid GUID>", // when boundary is a section group
123
+ "one_sectionOneNoteGuid": "<wdsectiononenoteguid GUID>", // alternate identifier (older notebooks)
124
+ "one_sectionPath": "/<group>/<section>.one", // human-readable path for run-reports
125
+ "one_notebookSourceDoc": "<notebook sourceDoc GUID>", // parentReferenceId fallback
126
+ // Email
127
+ "emailContext": "Inbox/<folder-path>",
128
+ // Teams
129
+ "teamsChatContext": {
130
+ "chatHints": ["..."], // exact chat topics
131
+ "channelHints": ["..."], // exact channel display names
132
+ "participantHints": ["..."] // exact display names
133
+ },
134
+ // SharePoint (when boundary is a SP site/library/folder)
135
+ "sp_siteId": "<siteId>",
136
+ "sp_webId": "<webId>",
137
+ "sp_listId": "<listId>",
138
+ "sp_path": "/<site>/<library>/<folder>"
139
+ }
140
+ }
141
+ ```
142
+
143
+ **OneNote discovery procedure (deterministic, follow exactly):**
144
+
145
+ 1. From `boundaries.onenote.section_names[]` or user-provided section name (e.g. `HCA.one`), run:
146
+ ```
147
+ workiq ask -q "Search Microsoft 365 OneNote for sections matching the name <name>. For each match return: section display name, wdsectionfileid, wdsectiongroupid (parent group), wdsectiononenoteguid, parentReferenceId (notebook), sourceDoc URL. Flat table, no commentary, no truncation."
148
+ ```
149
+ 2. Extract the GUIDs from the table (the `wdsectionfileid={GUID}` and `wdsectionfileid={GUID}` markers appear inline in the cell text — parse them out; do NOT use prose summaries).
150
+ 3. Validate by re-issuing the Step A.1 enumerate query from `pull-onenote/SKILL.md` against the discovered `wdsectionfileid`. If the table has ≥ 1 page row, persist the IDs. If empty, try `wdsectiongroupid`, then `wdsectiononenoteguid`. If all three fail, write the section as `disabled` with `next_step: ask user for sourceDoc URL` and continue.
151
+ 4. Persist to `m365-mutable.json#knownSections.<projectKey>` and mirror the resolved IDs into `boundaries.onenote.section_file_ids[]` / `section_group_ids[]` in `integrations.yml`.
152
+ 5. Record one line in `bootstrap-status.md`: `OneNote: resolved wdsectionfileid=<id> via name "<name>" (N pages enumerated)`.
153
+ 6. **Browser-URL completeness gate (kushi v3.10.0+).** After persisting `one_sectionFileId`, the browser-required fields (`one_notebookSourceDoc`, `one_notebookSpoBaseUrl`, `one_sectionWebUrl`, `one_sectionName`) MUST also be present and valid. Run:
154
+ ```pwsh
155
+ node plugin/skills/pull-onenote/scripts/recapture-section-url.mjs --project <name> --engagement-root <engagement-root> --check
156
+ ```
157
+ If exit code = 1, dispatch the same script WITHOUT `--check` so it prompts the user to paste the section's address-bar URL from OneNote-for-Web. The script parses + persists all five fields. Re-run `--check` to confirm exit 0 before continuing to Step 4b. If the user declines to paste, mark `boundaries.onenote.disabled = true, reason = "section-url-not-captured"` in `integrations.yml`, park in Open Questions, and skip pull-onenote dispatch.
158
+
159
+ **SharePoint, Teams, Email, CRM, ADO** follow the same shape: bootstrap discovers and persists; refresh consumes. Per-source discovery procedures live in each `pull-*/SKILL.md`'s "Bootstrap discovery" section. Bootstrap MUST invoke each pull-*'s discovery probe with the user-supplied seed (folder name, channel name, request id, work item id), persist the resolved IDs, and only then dispatch the pull.
160
+
161
+ #### Step 4b — Dispatch
162
+
163
+ Then dispatch to each enabled per-source skill with `--window last 30 days` (each skill self-refuses if its boundary is still empty):
89
164
 
90
165
  1. `pull-email`
91
166
  2. `pull-teams`
@@ -94,9 +169,12 @@ Dispatch to each enabled per-source skill with `--window last 30 days`:
94
169
  5. `pull-sharepoint`
95
170
  6. `pull-crm` (if enabled)
96
171
  7. `pull-ado` (if enabled)
172
+ 8. `pull-misc` (if `<project>/external-links.txt` exists with ≥ 1 non-placeholder, non-delegated link)
97
173
 
98
174
  Each produces snapshot/ + stream/ output per `snapshot-vs-stream.instructions.md`.
99
175
 
176
+ **pull-misc bootstrap note:** if `<project>/external-links.txt` does NOT exist, scaffold it from `templates/init/external-links.template.txt` so the user has a place to paste links. Mark the source as `enabled: true, links: 0` in `integrations.yml#boundaries.misc` and skip the dispatch (nothing to fetch yet).
177
+
100
178
  ### Step 5 — Consolidate (single contributor = no-op)
101
179
 
102
180
  If multiple contributors already exist, dispatch `consolidate-evidence`. For first-time bootstrap (one user), skip.
@@ -0,0 +1,108 @@
1
+ ---
2
+ name: "propose-ado-update"
3
+ version: "0.1.0-preview"
4
+ status: "preview"
5
+ description: "Read-only generator: reads the latest <project>/Evidence/_Consolidated/ and produces <project>/ado-updates/<YYYY-MM-DD>/proposed.md — a Markdown preview of the proposed ADO Initiative updates (FDE Status Summary field + Discussion comment). Performs NO writes to ADO. Safe to run unattended on the same Monday-9am schedule as refresh."
6
+ ---
7
+
8
+ # Skill: propose-ado-update
9
+
10
+ Run this when the user says any of: "propose ado update for `<X>`", "what would kushi write to ADO for `<X>`", "ado preview `<X>`", "draft ado update `<X>`".
11
+
12
+ This skill is **read-only**. It reads the most recent consolidated evidence and produces a Markdown file the user can review. It does **not** call ADO. Apply happens in the separate `apply-ado-update` skill, which is gated.
13
+
14
+ ## Profile
15
+
16
+ Belongs to the **`preview`** profile. Not present in `core` / `standard` / `full`. Opt in via `npx kushi-agents --clawpilot --profile preview`.
17
+
18
+ ## Deterministic config — do not invent paths
19
+
20
+ This skill does **NOT** create a new top-level config file. It reads from the configs Kushi already maintains:
21
+
22
+ | What | Where | Maintained by |
23
+ |---|---|---|
24
+ | ADO tenant + org + apiVersion + auth strategy | `<engagement-root>/.project-evidence/ado/config.yml` | `bootstrap-project` (global, one-time) |
25
+ | Per-project Initiative ID (`engagement_id`), area, title filter, discovery hints | `<engagement-root>/<project>/integrations.yml` under `ado:` | `bootstrap-project` + `pull-ado` (auto-discovers and persists) |
26
+ | Per-project **writes block** (allowlist, autoApply per field, strategy, fieldRefName) | `<engagement-root>/<project>/integrations.yml` under `ado.writes:` | First run of this skill scaffolds the block from the template, then user edits |
27
+
28
+ **Resolution is fully deterministic** — never ask the user for paths the bootstrap layer already resolved:
29
+
30
+ 1. `<engagement-root>` ← `~/.copilot/project-evidence.yml engagement_root` (per `engagement-root-resolution.instructions.md`).
31
+ 2. `<project>` folder ← fuzzy-match per `engagement-root-resolution.instructions.md` (knownSections → active_projects → subfolders).
32
+ 3. `<project>/integrations.yml ado.engagement_id` — REQUIRED. If `0` or missing → see "Prerequisites" below; never invent an ID.
33
+ 4. `<project>/integrations.yml ado.writes` — if missing, append the block from `<KUSHI_ROOT>/plugin/templates/ado-update/integrations-ado-writes.example.yml` and stop for user confirmation of `fieldRefName`.
34
+ 5. `<project>/Evidence/_Consolidated/<latest>_consolidated.md` — REQUIRED input. Never re-aggregate.
35
+
36
+ ## Prerequisites (HARD — do not bypass)
37
+
38
+ Refuse to produce `proposed.md` and tell the user exactly what's missing if any of these fail:
39
+
40
+ | Prereq | Failure message |
41
+ |---|---|
42
+ | `<project>/integrations.yml` exists | "Project not bootstrapped. Run `@Kushi bootstrap <project>` first." |
43
+ | `ado.engagement_id` > 0 | "ADO Initiative not yet linked for `<project>` (engagement_id is 0). pull-ado will auto-discover on next refresh; once `engagement_id` is set in `integrations.yml`, retry." |
44
+ | `<engagement-root>/.project-evidence/ado/config.yml` exists with non-placeholder `organization` | "Global ADO config missing or has placeholder org. Run `@Kushi bootstrap <project>` to scaffold, then fill `.project-evidence/ado/config.yml`." |
45
+ | At least one `Evidence/_Consolidated/<date>_consolidated.md` exists | "No consolidated evidence yet for `<project>`. Run `@Kushi refresh <project>` then `consolidate` first." |
46
+ | Latest consolidated file ≤ 8 days old | Continue but flag `stale-evidence` at top of `proposed.md`. |
47
+
48
+ ## Steps
49
+
50
+ 1. **Resolve config (deterministic)** — load global ADO config (tenant/org/apiVersion) + per-project `integrations.yml` (engagement_id + writes block). All from known paths above. No prompts unless `writes:` is missing.
51
+ 2. **First-run scaffold of `ado.writes:`** — if missing, append the template block to `integrations.yml ado:` (preserving every existing key). Stop. Tell user to confirm `fieldRefName` matches their tenant's process template. (Honors `side-by-side-config.instructions.md`: template stays generic, live filled file holds the real value.)
52
+ 3. **Pick the consolidated file** — most recent `<project>/Evidence/_Consolidated/<YYYY-MM-DD>_consolidated.md`. Optionally chase referenced per-source files for citations.
53
+ 4. **Read current ADO field value** (read-only GET, per `azure-auth-patterns.instructions.md` — pre-flight + tenant validation + token reuse + 401 retry):
54
+ ```
55
+ GET https://dev.azure.com/{org}/{project}/_apis/wit/workitems/{engagement_id}?api-version=7.1&fields={ado.writes.statusSummary.fieldRefName},System.Rev
56
+ ```
57
+ If field reference name not on the work item → record `field-not-found` in `proposed.md` and skip the field portion. Don't fail the run.
58
+ 5. **Distill the field update**:
59
+ - Format: `<MMM YYYY>: <one-liner ≤ 140 chars>`. Source: TL;DR / first-paragraph / explicit "summary" line in the consolidated file.
60
+ - Apply `strategy` (`append-month` default, `replace`, or `rolling-6`) to the value fetched in step 4.
61
+ - Compute the proposed new value locally; record `currentValue`, `proposedValue`, `revFetched` for the apply step's optimistic-lock guard.
62
+ 6. **Distill the Discussion comment**:
63
+ - Render `<KUSHI_ROOT>/plugin/templates/ado-update/discussion-comment.template.md` with sections: TL;DR, Decisions this week, Action items (owner + due), Risks/blockers, Asks, Evidence pack.
64
+ - Every bullet **must** carry a citation from the consolidated file (`[source: <path> · <date>]`). No bullet without a citation.
65
+ - End with the Kushi fingerprint line (used by `apply-ado-update` to detect duplicates):
66
+ ```
67
+ — Generated by Kushi v<version> · proposed <YYYY-MM-DD> · ledger:ado-updates/<YYYY-MM-DD>/ledger.jsonl
68
+ ```
69
+ 7. **Score confidence** per item (`high` / `medium` / `low`):
70
+ - `high` — explicit verbatim source quote, single source, recent (≤ this consolidated window).
71
+ - `medium` — paraphrased or aggregated from 2–3 sources.
72
+ - `low` — inferred, conflicting sources, or older than this window.
73
+ 8. **Write `proposed.md`** to `<engagement-root>/<project>/ado-updates/<YYYY-MM-DD>/proposed.md` from `<KUSHI_ROOT>/plugin/templates/ado-update/proposed.template.md`.
74
+ 9. **Echo summary** to user with path to `proposed.md` and one-line stats footer (`1 field, 1 comment, 7 cited bullets, 5 high / 2 medium / 0 low confidence`).
75
+
76
+ ## What this skill does NOT do
77
+
78
+ - Does NOT call any ADO `PATCH` or `POST` endpoint.
79
+ - Does NOT create or modify the global `.project-evidence/ado/config.yml`.
80
+ - Does NOT touch `<project>/integrations.yml` other than appending the `ado.writes:` sub-block on first run (preserving every existing key).
81
+ - Does NOT re-pull evidence (uses the most recent `_Consolidated/` file as-is).
82
+ - Does NOT write to the ledger (the ledger is written only by `apply-ado-update`).
83
+
84
+ ## Outputs
85
+
86
+ - `<engagement-root>/<project>/ado-updates/<YYYY-MM-DD>/proposed.md` — the human-reviewable proposal.
87
+ - (No state file changes, no ledger writes, no ADO writes.)
88
+
89
+ ## Failure modes
90
+
91
+ | Symptom | Recovery |
92
+ |---|---|
93
+ | `integrations.yml` missing | Refuse. Tell user to run `@Kushi bootstrap <project>` first. |
94
+ | `ado.engagement_id == 0` (Initiative not linked yet) | Refuse with the message in Prerequisites table. Do NOT prompt for an ID. |
95
+ | `ado.writes:` block missing | Scaffold from template, stop, ask user to confirm `fieldRefName`. |
96
+ | ADO 401 on fetch | Per `azure-auth-patterns.instructions.md` §4 — re-acquire token once, retry. If still 401, abort and report `auth-failed`. |
97
+ | ADO 404 on Initiative ID | Abort, report `initiative-not-found`. The `engagement_id` in integrations.yml is wrong; tell user to re-run pull-ado discovery or fix manually. |
98
+ | Field reference name not present on work item type | Skip the field portion of `proposed.md`; still produce the comment portion. |
99
+ | Latest consolidated > 8 days old | Continue but flag `stale-evidence` at top of `proposed.md`. |
100
+ | No `_Consolidated/` files at all | Refuse. Tell user to run `@Kushi refresh <project>` then `consolidate` first. |
101
+
102
+ ## References
103
+
104
+ - `update-ledger.instructions.md` — write-path doctrine and ledger schema (used by `apply-ado-update`; this skill produces the `proposed.md` shape `apply-ado-update` consumes).
105
+ - `azure-auth-patterns.instructions.md` — token acquisition, tenant validation, retry-on-401.
106
+ - `side-by-side-config.instructions.md` — config template vs live filled file (here: `integrations.yml ado.writes:` block).
107
+ - `engagement-root-resolution.instructions.md` — resolving `<engagement-root>` and `<project>`.
108
+ - `evidence-thoroughness.instructions.md` — what counts as a citable bullet.
@@ -1,23 +1,34 @@
1
1
  ---
2
2
  name: "pull-ado"
3
- version: "2.0.0"
4
- description: "Pull ADO evidence (snapshot: work-item fields; stream: comments + state changes + history). Conditional az login per az-auth-conditional rule."
3
+ version: "2.3.1"
4
+ description: "Pull ADO evidence as an engagement TREE (parent Engagement + every child WI). Per-item discussion comments + revision history (verbatim). Deterministic resolution; no parent-only summaries. Per ado-engagement-tree.instructions.md."
5
5
  ---
6
6
 
7
7
  # Skill: pull-ado
8
8
 
9
+
10
+ > **v3.7.6 contracts** — This skill operates under four HARD-rule doctrines:
11
+ > - `verbatim-by-default.instructions.md` — full bodies/notetext/fields by default; no preview-grade pulls accepted.
12
+ > - `capture-learnings.instructions.md` — every fix/discovery is logged to `plugin/learnings/<source>.md` immediately.
13
+ > - `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.
14
+ > - `run-reports.instructions.md` — every refresh writes a per-user report under `Evidence/<alias>/refresh-reports/YYYY-MM-DD-HHMM_refresh.md`.
15
+
16
+ > **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`.
17
+
9
18
  Pulls **ado** evidence in two shapes per `snapshot-vs-stream.instructions.md`:
10
19
 
11
- - **snapshot/** — work item with all fields (title, type, area, iteration, parent, state, assignee, custom fields)
12
- - **stream/** — comments verbatim + state changes (old → new) + field changes + attachments per work item
20
+ - **snapshot/** — engagement TREE: parent Engagement + every child WI; each item gets full fields + every discussion comment verbatim + full revision history
21
+ - **stream/** — new comments + state changes + field changes + attachments across the entire tree, weekly bucket
13
22
 
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`.
23
+ WorkIQ-first per `workiq-first.instructions.md` — but **for ADO, REST is preferred for snapshot** because WorkIQ summarizes discussion threads. 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`. Tree expansion + per-item discussion + revision pulls per `ado-engagement-tree.instructions.md`.
15
24
 
16
25
  ## Inputs
17
26
 
18
27
  - `<project>` — already-resolved project name.
19
28
  - `<alias>` — current contributor.
20
29
  - `<window>` — date range. For snapshot: ignored (always full re-fetch). For stream: `(from, to)`.
30
+ - (read) `<engagement-root>/.project-evidence/ado/config.yml` — connection.
31
+ - (read) `<engagement-root>/<project>/integrations.yml#ado` — per-project pinned IDs.
21
32
  - (read) `<engagement-root>/.project-evidence/m365/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints.
22
33
  - (read) `<engagement-root>/<project>/Evidence/<alias>/.settings.yml` — per-(project x user) overrides.
23
34
 
@@ -27,30 +38,162 @@ WorkIQ-first per `workiq-first.instructions.md`. Thoroughness per `evidence-thor
27
38
  - `ado.queryId` — saved query ID for the project
28
39
  - `ado.areaPath` / `ado.iterationPath` — for filtering
29
40
 
30
- ## Snapshot pass
41
+ ## Hard prerequisites (REQUIRED — see `scope-boundaries.instructions.md` Rule 3)
42
+
43
+ This skill is **HARD-fail** without both:
44
+
45
+ 1. Global config: `<engagement-root>/.project-evidence/ado/config.yml` exists with non-placeholder `organization` AND `defaultProject`.
46
+ 2. Per-project boundary: `<engagement-root>/<project>/integrations.yml#boundaries.ado.area_paths` is non-empty (OR `boundaries.ado.work_item_ids` is pinned).
47
+
48
+ If either is missing, refuse with the exact message:
49
+
50
+ ```
51
+ ado-config-missing — drop a filled config.yml at
52
+ <engagement-root>/.project-evidence/ado/config.yml. See template at
53
+ plugin/templates/init/ado-config.template.yml. Then add boundaries.ado.area_paths
54
+ to <project>/integrations.yml.
55
+ ```
56
+
57
+ Discovery loops (`title_contains`, `tags_contains`, `custom_iscrm_id`) are still
58
+ permitted — but ONLY inside `boundaries.ado.area_paths`. There is no
59
+ tenant-wide title-fuzzy scan in v3.7.0+.
60
+
61
+ ## Auth (deterministic, no improvisation)
62
+
63
+ ```powershell
64
+ $cfg = Get-Content "<engagement-root>/.project-evidence/ado/config.yml" | ConvertFrom-Yaml
65
+ $tok = az account get-access-token `
66
+ --resource $cfg.azDevOpsResourceId `
67
+ --tenant $cfg.tenantId `
68
+ --query accessToken -o tsv
69
+ $h = @{ Authorization = "Bearer $tok"; 'Content-Type' = 'application/json' }
70
+ $org = $cfg.organization # e.g. https://dev.azure.com/IndustrySolutions
71
+ $proj = $cfg.defaultProject # e.g. "IS Engagements"
72
+ $projUrl = "$org/$([uri]::EscapeDataString($proj))"
73
+ ```
74
+
75
+ If `az account get-access-token` returns nothing → ask user to `az login --tenant <tenantId>` and stop. Do NOT silently proceed.
76
+
77
+ ## Step 1 — Resolve the Engagement WI ID (deterministic order)
78
+
79
+ Per `ado-engagement-tree.instructions.md` § Resolution order:
80
+
81
+ ### 1a — Pinned ID
82
+
83
+ If `<project>/integrations.yml#ado.engagement_id` is non-null, use it. Skip to Step 2.
84
+
85
+ ### 1b — Pinned query
86
+
87
+ If `ado.queryId` is set:
88
+
89
+ ```powershell
90
+ $q = Invoke-RestMethod -Headers $h -Uri "$projUrl/_apis/wit/wiql/$($cfg.ado.queryId)?api-version=7.1"
91
+ ```
92
+
93
+ Take the single `Engagement`-typed row. If multiple → ask user, persist choice.
94
+
95
+ ### 1c — Bounded WIQL
96
+
97
+ Build the WIQL ONLY inside `boundaries.ado.area_paths`:
98
+
99
+ ```sql
100
+ SELECT [System.Id],[System.Title],[System.WorkItemType],[System.State],
101
+ [System.AreaPath],[System.IterationPath],[System.Tags],[System.AssignedTo],
102
+ [System.Parent],[System.CreatedDate],[System.ChangedDate]
103
+ FROM workitems
104
+ WHERE [System.TeamProject] = '<defaultProject>'
105
+ AND [System.WorkItemType] = 'Engagement'
106
+ AND [System.AreaPath] UNDER '<area_path>'
107
+ AND ([System.Title] CONTAINS '<project>' OR
108
+ [Custom.ISCRMRequestId] = '<crm.request_id>' OR
109
+ [System.Tags] CONTAINS '<project>')
110
+ AND [System.CreatedDate] > '<2-years-ago>'
111
+ AND [System.State] <> 'Removed'
112
+ ORDER BY [System.ChangedDate] DESC
113
+ ```
114
+
115
+ POST to `$projUrl/_apis/wit/wiql?api-version=7.1` with body `{ "query": "<wiql>" }`.
116
+
117
+ - 0 rows → write `last_discovery_result: no-match` + the WIQL used + `next_step` and STOP.
118
+ - 1 row → use it. Persist `engagement_id` to `integrations.yml#ado` AND to `m365Mutable.knownSections.<project>.ado.engagementId`.
119
+ - N rows → render the candidates table to the user (id, title, state, areaPath, createdDate). Ask which one. **Never auto-pick.** Persist on confirmation.
120
+
121
+ ## Step 2 — Expand the engagement tree
122
+
123
+ ```powershell
124
+ $wi = Invoke-RestMethod -Headers $h -Uri "$projUrl/_apis/wit/workitems/$engagementId?`$expand=relations&api-version=7.1"
125
+ $childIds = $wi.relations |
126
+ Where-Object { $_.rel -eq 'System.LinkTypes.Hierarchy-Forward' } |
127
+ ForEach-Object { ($_.url -split '/')[-1] }
128
+ ```
129
+
130
+ Recurse — expand each child's `relations` the same way until the tree closes. Persist the resulting tree (parent → children IDs) to `Evidence/<alias>/ado/snapshot/tree.md`.
131
+
132
+ ## Step 3 — Per-item fetch (parent + every child, no batching)
133
+
134
+ For each WI id in the tree:
135
+
136
+ ### 3a — Core fields
137
+
138
+ ```powershell
139
+ $item = Invoke-RestMethod -Headers $h `
140
+ -Uri "$projUrl/_apis/wit/workitems/$id?`$expand=all&api-version=7.1"
141
+ ```
142
+
143
+ `$expand=all` returns every populated field including custom fields. Don't pre-filter.
144
+
145
+ ### 3b — Discussion comments (verbatim, paginated until exhausted)
146
+
147
+ ```powershell
148
+ $cmts = @()
149
+ $ct = $null
150
+ do {
151
+ $url = "$projUrl/_apis/wit/workItems/$id/comments?api-version=7.1-preview.4&`$top=200"
152
+ if ($ct) { $url += "&continuationToken=$ct" }
153
+ $page = Invoke-RestMethod -Headers $h -Uri $url
154
+ $cmts += $page.comments
155
+ $ct = $page.continuationToken
156
+ } while ($ct)
157
+ ```
158
+
159
+ Capture EVERY comment: `id`, `text` (verbatim, no truncation), `createdBy.displayName`, `createdDate`, `modifiedDate`. Render newest-first under `## Discussion (verbatim)`.
160
+
161
+ ### 3c — Revision history (every state + assignee + field change)
162
+
163
+ ```powershell
164
+ $updates = Invoke-RestMethod -Headers $h `
165
+ -Uri "$projUrl/_apis/wit/workItems/$id/updates?api-version=7.1&`$top=500"
166
+ ```
167
+
168
+ For each update, render `revisedBy.displayName`, `revisedDate`, and the `fields.<name>.{oldValue → newValue}` deltas. Group by date.
31
169
 
32
- Write `snapshot/items/<id>.md` per work item with every field. Top header: id, title, type, state, parent, area, iteration, assignee, last fetched.
170
+ ### 3d Attachments (metadata only)
33
171
 
34
- Write to: `<engagement-root>/<project>/Evidence/<alias>/ado/snapshot/<entity>.md`
172
+ From `$item.relations` where `rel = 'AttachedFile'` — record `name`, `resourceSize`, `attributes`. Do not download binary unless user asks.
35
173
 
36
- Use template: `templates/snapshot/ado-<kind>.template.md`
174
+ ### 3e — Write per-item snapshot file
37
175
 
38
- ## Stream pass
176
+ `<engagement-root>/<project>/Evidence/<alias>/ado/snapshot/items/<id>.md` (parent gets `engagement-<id>.md` at the snapshot root):
39
177
 
40
- Per `evidence-thoroughness.instructions.md`: every comment verbatim with author + timestamp; every state change; every field change. Bucketed by ISO week into `stream/items/<id>/<YYYY-MM-DD>_ado-stream.md`.
178
+ - Header: id, type, title, state, areaPath, parent, assignee, last fetched.
179
+ - `## Source Basis` — tool used (rest), boundary applied, comments fetched, revisions fetched.
180
+ - `## AI Narrative Summary` (3+ paragraphs — REQUIRED FIRST).
181
+ - `## All fields` — 2-column table; empty as `_(empty)_`.
182
+ - `## Discussion (verbatim)` — every comment as `### <author> · <date>` then verbatim blockquote.
183
+ - `## Revision history` — every update row.
184
+ - `## Attachments` — metadata table.
41
185
 
42
- Write to: `<engagement-root>/<project>/Evidence/<alias>/ado/stream/<YYYY-MM-DD>_ado-stream.md` (date = Monday of the ISO week the events fall in).
186
+ ## Step 4 Stream pass
43
187
 
44
- Use template: `templates/weekly/ado-summary.template.md`
188
+ After all snapshots written, build the weekly stream from comments + updates whose `createdDate` / `revisedDate` fall in `<window>`. Bucket by Monday-of-ISO-week into `stream/<YYYY-MM-DD>_ado-stream.md` with AI Narrative Summary first then the verbatim entries grouped by WI.
45
189
 
46
- If a week file already exists, MERGE (dedupe by event ID, append new events, keep existing).
190
+ If a week file exists, MERGE (dedupe by `comment.id` and `update.rev`).
47
191
 
48
192
  ## Tools (in order)
49
193
 
50
- 1. **WorkIQ** — ````$WorkIQQuery`````
51
- 2. **Host fallback** — ADO REST API via `az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798` (read connection/queries from `<engagement-root>/.project-evidence/ado/config.yml`)
52
- 3. **Graph REST** — last resort, soft-fail per `az-auth-conditional.instructions.md`.
53
- 4. **Ask user** — paste verbatim source content if all above fail.
194
+ 1. **WorkIQ** — discovery only (asking "is there an Engagement WI for HCA?" before doing the WIQL). Never used for discussion-comment fetch — it summarizes.
195
+ 2. **ADO REST** — REQUIRED for fields + comments + updates + tree expansion (preferred path). Auth block above.
196
+ 3. **Ask user** — paste verbatim source content if REST returns 401 / 404 / persistent 5xx after 2 retries (3s/6s).
54
197
 
55
198
  Document which path succeeded under `## Source Basis` in each output file.
56
199
 
@@ -61,17 +204,24 @@ If discovered, immediately write to `m365Mutable.knownSections.<project>.<source
61
204
  - `ado.engagementId` — pinned engagement work-item ID
62
205
  - `ado.queryId` — saved query ID for the project
63
206
  - `ado.areaPath` / `ado.iterationPath` — for filtering
207
+ - `ado.tree.lastBuiltOn` + `ado.tree.itemCount` — so consolidate can detect drift between runs.
64
208
 
65
209
  ## Update run-log
66
210
 
67
211
  After successful pass:
68
212
  - `sources.ado.last_pulled = now`
69
- - `sources.ado.watermark = now` (stream only; snapshot has no watermark)
70
- - `sources.ado.item_count = <N>`
213
+ - `sources.ado.engagement_id = <id>`
214
+ - `sources.ado.tree_size = <N>` — total WIs in the tree
215
+ - `sources.ado.comments_fetched = <N>` — across the tree
216
+ - `sources.ado.updates_fetched = <N>` — across the tree
217
+ - `sources.ado.items_failed = <N>`
218
+ - `sources.ado.watermark = now` (stream only)
71
219
  - For each week touched, add to `weekly_files` index.
72
220
 
73
221
  ## Stop conditions
74
222
 
75
- - Hint missing AND fuzzy resolution returns 0 candidates ask user once, persist answer to mutable, continue.
76
- - Multiple plausible candidates → ask user to pick, persist answer.
77
- - 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.
223
+ - Hard-prereq missing → refuse (see Hard prerequisites above).
224
+ - Step 1 returns multiple Engagement candidates → ask user, never auto-pick.
225
+ - Step 1 returns zero candidatesrecord `no-match` + WIQL + `next_step`, stop.
226
+ - A child WI fetch fails → mark `❌ item-fetch-failed` in `tree.md`, continue tree.
227
+ - Discussion paging fails mid-stream → record `❌ comments-partial — N/M fetched` and continue.