kushi-agents 5.0.0 → 5.0.2

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 (54) hide show
  1. package/README.md +30 -7
  2. package/bin/cli.mjs +73 -45
  3. package/package.json +51 -51
  4. package/plugin/instructions/agentskills-compliance.instructions.md +144 -0
  5. package/plugin/instructions/multi-host-install.instructions.md +125 -0
  6. package/plugin/instructions/plan-validate-execute.instructions.md +75 -0
  7. package/plugin/instructions/release-genealogy.instructions.md +52 -0
  8. package/plugin/skills/aggregate-project/SKILL.md +11 -2
  9. package/plugin/skills/apply-ado-update/SKILL.md +11 -2
  10. package/plugin/skills/ask-project/SKILL.md +1 -1
  11. package/plugin/skills/bootstrap-project/SKILL.md +39 -127
  12. package/plugin/skills/bootstrap-project/references/discovery-sweep.md +40 -0
  13. package/plugin/skills/bootstrap-project/references/pull-dispatch.md +50 -0
  14. package/plugin/skills/bootstrap-project/references/registry-persistence.md +55 -0
  15. package/plugin/skills/build-state/SKILL.md +50 -2
  16. package/plugin/skills/consolidate-evidence/SKILL.md +11 -2
  17. package/plugin/skills/dashboard/SKILL.md +20 -1
  18. package/plugin/skills/emit-vertex/SKILL.md +10 -1
  19. package/plugin/skills/fde-intake/SKILL.md +10 -1
  20. package/plugin/skills/fde-report/SKILL.md +10 -1
  21. package/plugin/skills/fde-triage/SKILL.md +10 -1
  22. package/plugin/skills/intro/SKILL.md +1 -1
  23. package/plugin/skills/link-entities/SKILL.md +43 -1
  24. package/plugin/skills/project-status/SKILL.md +1 -1
  25. package/plugin/skills/propose-ado-update/SKILL.md +11 -2
  26. package/plugin/skills/pull-ado/SKILL.md +26 -9
  27. package/plugin/skills/pull-crm/SKILL.md +39 -125
  28. package/plugin/skills/pull-crm/references/dataverse-doctrine.md +108 -0
  29. package/plugin/skills/pull-crm/references/legacy-shape.md +28 -0
  30. package/plugin/skills/pull-email/SKILL.md +33 -79
  31. package/plugin/skills/pull-email/references/retrieval-order.md +43 -0
  32. package/plugin/skills/pull-email/references/two-pass-pull.md +41 -0
  33. package/plugin/skills/pull-loop/SKILL.md +194 -177
  34. package/plugin/skills/pull-meetings/SKILL.md +35 -72
  35. package/plugin/skills/pull-meetings/references/legacy-stream.md +15 -0
  36. package/plugin/skills/pull-meetings/references/verbatim-capture.md +61 -0
  37. package/plugin/skills/pull-misc/SKILL.md +24 -7
  38. package/plugin/skills/pull-onenote/SKILL.md +207 -555
  39. package/plugin/skills/pull-onenote/references/playwright-fallback.md +111 -0
  40. package/plugin/skills/pull-onenote/references/preflight.md +85 -0
  41. package/plugin/skills/pull-onenote/references/runtime-contract.md +118 -0
  42. package/plugin/skills/pull-sharepoint/SKILL.md +26 -9
  43. package/plugin/skills/pull-teams/SKILL.md +26 -9
  44. package/plugin/skills/refresh-project/SKILL.md +24 -2
  45. package/plugin/skills/self-check/SKILL.md +9 -1
  46. package/plugin/skills/self-check/run.ps1 +216 -4
  47. package/plugin/skills/setup/SKILL.md +14 -120
  48. package/plugin/skills/setup/references/onedrive-pin-sync.md +60 -0
  49. package/plugin/skills/setup/references/recovery-prompts.md +81 -0
  50. package/plugin/skills/tour/SKILL.md +18 -1
  51. package/plugin/skills/vertex-link/SKILL.md +1 -1
  52. package/src/constants.mjs +39 -1
  53. package/src/multi-host-install.test.mjs +170 -0
  54. package/src/multi-host.mjs +277 -0
@@ -0,0 +1,111 @@
1
+ # references/playwright-fallback.md (pull-onenote)
2
+
3
+ > **Load this file when `m365Auth.oneNote.playwrightFallback: true` AND `_index/entities.yml` shows ≥ N pages with `status: body-not-exposed` for ≥ 2 consecutive refreshes** (N default 5; configurable via `m365-mutable.json#pullOnenote.playwrightThreshold`).
4
+ >
5
+ > Otherwise: WorkIQ is the primary path. Do NOT bootstrap a Playwright profile on a fresh install.
6
+
7
+ ## When to enable
8
+
9
+ ```jsonc
10
+ // In <kushi-config-root>/user/m365-auth.json:
11
+ "oneNote": {
12
+ "enabled": true,
13
+ "defaultNotebookName": "...",
14
+ "playwrightFallback": true // OPT-IN — default false
15
+ }
16
+ ```
17
+
18
+ Once opted in, the driver auto-escalates to Playwright when the threshold is met. Until then the driver MUST continue using WorkIQ. Never auto-bootstrap a profile — that is always a user-initiated action.
19
+
20
+ ## Bootstrap the Playwright profile
21
+
22
+ ```pwsh
23
+ cd <kushi-repo-root>
24
+ node plugin/skills/pull-onenote/runner.mjs --bootstrap
25
+ ```
26
+
27
+ Expected console flow (any deviation = defect):
28
+
29
+ ```
30
+ [bootstrap] Step 1/2: Sign in to OneNote-for-Web. ...
31
+ [bootstrap] Sign-in detected (OneNote chrome rendered). ← MUST appear before Step 2/2
32
+ [bootstrap] Step 2/2: Seeding SharePoint cookies at https://microsoft-my.sharepoint.com/
33
+ [bootstrap] Step 2/2: Seeding SharePoint cookies at https://microsoft-my.sharepoint-df.com/
34
+ [bootstrap] Both surfaces visited. Close the browser window when ready.
35
+ [bootstrap] Profile saved at: ...
36
+ ```
37
+
38
+ If `Sign-in detected` is missing → the v3.11.1-or-earlier `waitForURL` bug is back; bootstrap is invalid; cookies are not minted; every subsequent scrape will return `auth-required`. Re-bootstrap with v3.11.2+.
39
+
40
+ The visible Edge window stays open until you close it. Sign in fully, wait for OneNote to render, then close.
41
+
42
+ ## Scrape a section — use the wrapper, not the bare runner
43
+
44
+ The bare `runner.mjs` emits JSON to stdout only. Hand-wiring snapshot writes from runner JSON is brittle (PowerShell's default `Out-File` mangles UTF-8 — NBSP becomes `┬á`) and almost always violates the canonical layout.
45
+
46
+ **HARD rule (kushi v3.11.5+):** drivers MUST call `write-snapshot.mjs`. Never hand-write snapshot files from runner JSON.
47
+
48
+ ```pwsh
49
+ cd <kushi-repo-root>
50
+ $url = '<exact-section-url-from-m365-mutable.json#one_sectionWebUrl>'
51
+ node plugin/skills/pull-onenote/write-snapshot.mjs `
52
+ --section-url $url `
53
+ --project "<project>" `
54
+ --engagement-root "<engagement-root>" `
55
+ --alias <your-alias>
56
+ ```
57
+
58
+ The wrapper writes back to `m365-mutable.json`:
59
+
60
+ - `one_pages[]` — per-page retry registry (title, wdpartid, webPageId, lastModified, last_status, attempts, snapshot_path, captured_at)
61
+ - `one_lastPullAt`, `one_lastPullRunStatus`, `one_lastPullKushiVersion`
62
+
63
+ Wrapper-set flags (all load-bearing):
64
+
65
+ | Flag | Why required |
66
+ |---|---|
67
+ | `--skip-preflight` | Standalone preflight at `https://onenote.cloud.microsoft/` is unreliable post-bootstrap (OneNote SPA forces `prompt=select_account` even when SPO cookies are valid). The Doc.aspx URL uses different cookies and works fine. |
68
+ | `--headless` | Without it, a visible browser opens — if the user accidentally closes it during scrape, run fails with `Target page, context or browser has been closed`. |
69
+ | `--timeout 120000` | Default 60s is too short under tenant load. 120s is the empirical safety margin for sections up to ~25 pages. |
70
+
71
+ ## Diagnostics (when scrape produces `pages: []`)
72
+
73
+ If the scrape returns `pages: []` with `runStatus: "partial"` and `error: "frame.waitForFunction: Timeout"`, the cause is almost always one of:
74
+
75
+ 1. **Single-page section regex mismatch** — fixed in v3.11.3. Verify runner is v3.11.3+.
76
+ 2. **Page-rail genuinely slow to render** — bump `--timeout` to 180000 and retry once.
77
+ 3. **Section URL stale** — re-run `scripts/recapture-section-url.mjs`.
78
+
79
+ Quick aria-label diagnostic (drop into `plugin/skills/pull-onenote/diag.mjs`):
80
+
81
+ ```js
82
+ import { chromium } from 'playwright';
83
+ import os from 'os';
84
+ import path from 'path';
85
+ const ctx = await chromium.launchPersistentContext(
86
+ path.join(os.homedir(), '.copilot', 'playwright-profile', 'onenote'),
87
+ { headless: true, channel: 'msedge', viewport: { width: 1400, height: 900 } }
88
+ );
89
+ const page = ctx.pages()[0] || await ctx.newPage();
90
+ await page.goto(process.argv[2], { timeout: 60000 });
91
+ await page.waitForTimeout(60000);
92
+ for (const f of page.frames()) {
93
+ try {
94
+ const labels = await f.evaluate(() =>
95
+ Array.from(document.querySelectorAll('[aria-label]'))
96
+ .map(n => n.getAttribute('aria-label'))
97
+ .filter(x => x && (x.includes('page') || x.includes('Page')))
98
+ );
99
+ if (labels.length) console.log(f.url().slice(0, 80), labels.slice(0, 40));
100
+ } catch (e) {}
101
+ }
102
+ await ctx.close();
103
+ ```
104
+
105
+ If you see `, Page. Selected.` but not `, page N of M, Page.` → single-page section, runner must be v3.11.3+.
106
+
107
+ ## Browser-channel and two-surface bootstrap (HARD rules)
108
+
109
+ - **Channel:** `channel: 'msedge'` only. Vanilla Chromium is denied by Conditional Access with "You can't get there from here". If the user has no Edge installed, fail fast — do NOT fall back to Chromium.
110
+ - **Two-surface seed:** bootstrap MUST visit `https://onenote.cloud.microsoft/` AND BOTH `https://microsoft-my.sharepoint.com/` AND `https://microsoft-my.sharepoint-df.com/`. Cookie domains don't transfer between `*.cloud.microsoft` and `*.sharepoint(-df).com`. Profile reset required when switching channels.
111
+ - **Post-auth signal:** bootstrap MUST wait for one of `[aria-label*="Account manager" i]`, `[data-automationid="NotebookList"]`, `button[aria-label*="notebook" i]`, `iframe[src*="onenoteframe.aspx"]` — NOT for a URL match (no-op because we just navigated there).
@@ -0,0 +1,85 @@
1
+ # references/preflight.md (pull-onenote)
2
+
3
+ > **Load this file when constructing or debugging the OneNote pre-flight gate** (browser-URL completeness, OneNote-for-Web reachability, three-way runStatus classification).
4
+
5
+ ## A. Playwright profile (only if fallback opted in)
6
+
7
+ ```pwsh
8
+ $prof = "$env:USERPROFILE\.kushi\playwright-profile\onenote"
9
+ if (-not (Test-Path $prof)) {
10
+ Write-Host "[pull-onenote] Playwright profile not yet seeded."
11
+ Write-Host "[pull-onenote] Run plugin/skills/pull-onenote/runner.mjs --bootstrap once interactively to sign in."
12
+ # Skill MUST NOT fall through silently. Surface as run-report 'auth-required' on every pending page.
13
+ }
14
+ ```
15
+
16
+ See `playwright-fallback.md` for the bootstrap procedure, channel/cookie rules, and recovery patterns.
17
+
18
+ ## B. WorkIQ EULA (PRIMARY path needs this once per machine)
19
+
20
+ ```pwsh
21
+ workiq accept-eula # idempotent
22
+ ```
23
+
24
+ ## C. OneNote-for-Web reachability — three-way classification (HARD, v3.11.0+)
25
+
26
+ Before navigating to any section URL the runner MUST probe `https://onenote.cloud.microsoft/` and classify the end-state:
27
+
28
+ | End-state | Detection | runStatus | Driver action |
29
+ |---|---|---|---|
30
+ | `ok` | Account chrome / notebook list / `onenoteframe.aspx` iframe rendered within `--preflight-timeout` (default 25s) | n/a (proceed) | Navigate to section URL as normal. |
31
+ | `auth-required` | URL bounced to `login.microsoftonline.com` or `login.live.com` | `auth-required` | Surface in run report; next refresh re-attempts. |
32
+ | `onenote-web-unavailable` | "Sorry, we ran into a problem" / "Something went wrong" / "We couldn't open" / "This notebook can't be opened" / "There was a problem" detected in any frame's body text, OR pre-flight timeout with no chrome | `notebook-unavailable` | **Do NOT retry blindly.** Surface recovery checklist (below). Mark pages `status: notebook-unavailable`. |
33
+
34
+ The same three-way classification ALSO applies after navigating to the section URL. The two failure modes are distinct; conflating them sends the user down the wrong recovery path.
35
+
36
+ Standalone pre-flight CLI:
37
+
38
+ ```pwsh
39
+ node plugin/skills/pull-onenote/runner.mjs --preflight
40
+ # Exit 0 = ok, 4 = auth-required, 3 = onenote-web-unavailable, 1 = unexpected
41
+ # stdout: { "preflight": { "ok": bool, "reason"?, "detail"? } }
42
+ ```
43
+
44
+ ### Recovery checklist for `notebook-unavailable` (surface verbatim)
45
+
46
+ 1. Hard-refresh `https://onenote.cloud.microsoft/` — if the root page shows the same error, the issue is service- or notebook-side.
47
+ 2. Open the notebook in **OneNote desktop** and let it fully sync.
48
+ 3. If the notebook opens in desktop but not web, wait 10–15 minutes (web index lag) and retry.
49
+ 4. If the section specifically fails (root loads, section errors), re-capture `one_sectionWebUrl` from the address bar via `scripts/recapture-section-url.mjs` — the persisted URL may be stale or for a moved section.
50
+ 5. Only after the user can manually open the notebook in OneNote-for-Web, re-run the kushi pull.
51
+
52
+ ## D. Browser-URL completeness gate (HARD, v3.10.0+)
53
+
54
+ The Playwright runner navigates to `one_sectionWebUrl`. That URL is composed from FIVE registry fields, and ALL must be present and accurate:
55
+
56
+ | Key | Source | Notes |
57
+ |---|---|---|
58
+ | `one_sectionName` | OneNote section file name | e.g. `AGCO.one` |
59
+ | `one_sectionFileId` | `wd=target(<name>\|<GUID>/)` fragment | Same GUID as WorkIQ's wd-section-file id |
60
+ | `one_notebookSourceDoc` | `sourcedoc={<GUID>}` query param | Identifies the parent notebook |
61
+ | `one_notebookSpoBaseUrl` | `<scheme>://<host>/personal/<upn>` segment | Tenant-specific |
62
+ | `one_sectionWebUrl` | the canonical Doc.aspx URL | **MUST be user-pasted from address bar** |
63
+
64
+ **No-synthesis rule (HARD, v3.10.2+):** `one_sectionWebUrl` MUST be the URL the user actually copied. Two formulas were tried and BOTH failed in production:
65
+
66
+ - `wd=target(<name>|<fileId>/)` → silent "Sorry, we ran into a problem".
67
+ - `wd=target(/<name>/)` → silent "Sorry, we ran into a problem".
68
+
69
+ OneNote's routing depends on internal session/tenant tokens we cannot reverse-engineer. Any template-synthesized URL is invalid.
70
+
71
+ **Gate before dispatching the runner:**
72
+
73
+ ```pwsh
74
+ node plugin/skills/pull-onenote/scripts/recapture-section-url.mjs `
75
+ --project <name> `
76
+ --engagement-root <engagement-root> `
77
+ --check
78
+ ```
79
+
80
+ - Exit 0 + `{"status":"ok"}` → proceed.
81
+ - Exit 1 + `{"status":"incomplete","missing":[...]}` → invoke recapture (interactive paste prompt) without `--check`.
82
+
83
+ This gate runs in BOTH bootstrap and refresh — if a refresh discovers the gate is open, it MUST self-heal via the script before invoking the runner. This is a one-time backfill of pre-doctrine or stale registry fields, NOT re-discovery.
84
+
85
+ If the user declines to paste a URL, mark the OneNote source as `disabled: true, reason: section-url-not-captured` in `<project>/integrations.yml#boundaries.onenote`, log a refresh-report entry, and skip the runner. Do NOT fall back to WorkIQ-only — WorkIQ returns `body-not-exposed` for most pages without browser-URL grounding.
@@ -0,0 +1,118 @@
1
+ # references/runtime-contract.md (pull-onenote)
2
+
3
+ > **Load this file when implementing the runner / wrapper / per-page retry logic, or when debugging snapshot/+stream/ legacy artifacts.** Reading-only consumers (build-state, ask-project, link-entities) should not need this file — the canonical contract for them is the v4.9.0 weekly CSC + `_index/entities.yml` layout described in the main SKILL.md.
4
+
5
+ ## Empirical contract (HARD-rule, supersedes earlier doctrine)
6
+
7
+ 1. **WorkIQ natural-language by display name IS the primary, working path for OneNote.** Reframed v4.7.3 (2026-05-26) — see `workiq-onenote-query-shape.instructions.md`. The only working WorkIQ phrasings: (a) narrow, single-section-by-display-name discovery; (b) single-page-by-title body pulls; (c) section-scoped edit-event stream. Forbidden: enumeration verbs, structured-field filter syntax, ID-lookup questions, broad notebook-enumeration probes — all empirically fail.
8
+ 2. **WorkIQ body retrieval is non-deterministic, BUT per-page retry registries close the gap over multiple refreshes.** The v3.8.0 pivot to Playwright was driven by HCA's 1-of-18 body-retrieval rate on a single refresh. The `_index/entities.yml#status` field accumulates captures across refreshes; over 3–5 refreshes the cumulative rate converges. Playwright remains as the opt-in recovery valve for contributors who need faster convergence.
9
+ 3. **The OneNote-for-Web `pageid` and the WorkIQ wd-part id are different identifiers.** Persist both per page when both are observed (`webPageId` for browser navigation; `wdpartid` for WorkIQ correlation). WorkIQ-only operation needs `wdpartid` alone.
10
+ 4. **Browser-scrape via OneNote-for-Web returns higher-fidelity verbatim bodies when it works** — HCA on 2026-05-14: 16/16 pages captured (~120KB) in one run. NOT the default because: (a) Edge + Conditional Access compliance required; (b) per-machine bootstrap expires every 3–5 days; (c) multi-refresh WorkIQ converges adequately for time-windowed engagement evidence.
11
+
12
+ ## Bootstrap discovery (one-time per project)
13
+
14
+ For a project we have not pulled OneNote for before, capture into `m365-mutable.json#knownSections.<projectKey>`:
15
+
16
+ | Key | Source | Example (HCA) |
17
+ |---|---|---|
18
+ | `one_sectionName` | OneNote section file name | `HCA.one` |
19
+ | `one_sectionFileId` | OneNote-for-Web wd= URL fragment AND WorkIQ wd-section-file id (they match) | `3d0ad388-fd6b-45a8-8619-60dd709b7ade` |
20
+ | `one_notebookSourceDoc` | SharePoint sourcedoc GUID for the parent notebook | `2036c7b1-db1b-47fd-a14f-d8ee94ddd9bc` |
21
+ | `one_notebookName` | OneNote notebook display name | `ISE Work` |
22
+ | `one_notebookSpoBaseUrl` | host segment before `/_layouts/15/Doc.aspx` | `https://microsoft-my.sharepoint-df.com/personal/<upn>` |
23
+
24
+ Discover by:
25
+
26
+ 1. Open OneNote-for-Web at `https://onenote.cloud.microsoft/`. Sign in.
27
+ 2. Open notebook → click section.
28
+ 3. Read URL — contains `sourcedoc={<notebookSourceDoc>}` and `wd=target(<sectionName>|<sectionFileId>/...)`.
29
+ 4. Persist all five values per `m365-id-registry.instructions.md`.
30
+
31
+ If the section already exists in `knownSections.<projectKey>` from a prior WorkIQ run, only the browser-specific fields need to be added.
32
+
33
+ ## Step A — enumerate pages (browser fallback only)
34
+
35
+ The runner navigates to the section's deep-link URL and waits for the OneNote canvas inside the nested `ffc-onenote.officeapps.live.com/onenoteframe.aspx` frame. Page enumeration uses the accessibility tree and MUST handle BOTH aria-label formats (kushi v3.11.3+):
36
+
37
+ | Section state | aria-label format | Parsed as |
38
+ |---|---|---|
39
+ | Multi-page | `<title>, page N of M, Page.` | `{ title, pos: N, total: M }` |
40
+ | Single-page | `<title>, Page. Selected.` | `{ title, pos: 1, total: 1 }` |
41
+
42
+ **HARD rule (v3.11.3+):** any aria-label-driven enumerator MUST handle the N=1 special-case format. Single-page sections do NOT render the multi-page format; assuming they do is the defect signature `frame.waitForFunction: Timeout` with `pages: []`.
43
+
44
+ After clicking each page, capture `webPageId` from the `wd=target(...|<title>|<webPageId>/)` segment.
45
+
46
+ ## Step B — fetch verbatim body (browser fallback only)
47
+
48
+ For each page, click the rail entry, wait 2.5s, then:
49
+
50
+ ```js
51
+ const body = await wac.evaluate(() => {
52
+ const node = document.querySelector('#PageContentWrapper')
53
+ || document.querySelector('.Page')
54
+ || document.querySelector('[role="main"]');
55
+ return node ? node.innerText : '';
56
+ });
57
+ ```
58
+
59
+ Acceptance check (HARD):
60
+
61
+ - Captured body must contain title line + ≥ 1 non-toolbar paragraph.
62
+ - Body < 50 chars: mark `status: short-suspect` and retry unless verified sparse in OneNote desktop.
63
+ - Redirect to `login.microsoftonline.com`: mark `status: auth-required` and EXIT the run loop early.
64
+
65
+ ## Step B' — fallback: WorkIQ verbatim probe
66
+
67
+ Used ONLY when (a) Playwright profile absent, (b) previous run hit auth-required and retry-cooldown not elapsed (24h), or (c) user requests WorkIQ-only diagnostic.
68
+
69
+ The canonical WorkIQ Step B verbatim prompt lives in `workiq-only.instructions.md` § OneNote verbatim. Acceptance: real body OR explicit unavailable marker. Anything else → `status: workiq-degraded`.
70
+
71
+ ## Per-page retry registry (legacy m365-mutable schema)
72
+
73
+ > v4.9.0 moves the per-page retry registry into `_index/entities.yml#status` and `low_signal`. The legacy `m365-mutable.json#knownSections.<projectKey>.one_pages[]` shape is retained for back-compat reads. New writes go to `_index/entities.yml`.
74
+
75
+ Status values (canonical across both old and new schemas):
76
+
77
+ | Status | Meaning | Next-run action |
78
+ |---|---|---|
79
+ | `captured` | Browser scrape returned full body | None unless `lastModified` advanced |
80
+ | `user-pasted` | Human pasted body | Treat as captured; do not overwrite |
81
+ | `auth-required` | Browser hit MFA / sign-in page | Retry browser; surface in run report |
82
+ | `notebook-unavailable` | OneNote-for-Web error dialog (service- or notebook-side) | Do NOT auto-retry. Surface recovery checklist (preflight.md §C). |
83
+ | `workiq-degraded` | Browser unavailable AND WorkIQ returned unavailable marker | Retry browser when profile valid again |
84
+ | `body-not-exposed` | WorkIQ-fallback explicitly returned the literal marker | Retry browser next run |
85
+ | `short-suspect` | Body < 50 chars, may not be genuinely sparse | Verify in OneNote desktop, then retry |
86
+ | `enumeration-only` | Page enumerated but Step B not yet attempted | Attempt Step B on next run |
87
+
88
+ Retry doctrine: every refresh re-runs Step A and re-runs Step B for any page where status NOT IN (`captured`, `user-pasted`). Pages with `captured` are re-fetched only if `lastModified` differs.
89
+
90
+ ## Legacy snapshot file shape (pre-v4.9.0, reader fallback only)
91
+
92
+ Front-matter (yaml) at top of every legacy `snapshot/pages/<safe-title>.md`:
93
+
94
+ ```yaml
95
+ ---
96
+ page_title: "4/3 - HCA with Jay and Martin"
97
+ section: "HCA.one"
98
+ notebook: "ISE Work"
99
+ section_id: "3d0ad388-fd6b-45a8-8619-60dd709b7ade"
100
+ wdpartid: "2233ac5a-007a-4b70-9d93-d6113f318ba3"
101
+ webPageId: "78aac9b5-0629-4daa-ada3-f2436cb2381c"
102
+ last_modified: "April 3rd"
103
+ last_status: "captured"
104
+ captured_via: "browser"
105
+ attempts: 2
106
+ last_attempt: "2026-05-14T03:55:00Z"
107
+ captured_at: "2026-05-14T03:55:00Z"
108
+ ---
109
+ ```
110
+
111
+ Body shape:
112
+
113
+ - `captured` / `user-pasted`: `## AI Narrative Summary` (3+ paragraphs) + `## Body (verbatim)` with exact extracted text. NEVER paraphrase a captured body.
114
+ - `auth-required` / `workiq-degraded` / `body-not-exposed`: unavailable-marker block + `### next_step` block + metadata table. Do NOT fabricate body content from emails or chats.
115
+ - `enumeration-only`: transient marker only.
116
+ - `short-suspect`: captured text + `### unconfirmed` note.
117
+
118
+ This file is NOT written by v4.9.0+ runs; it is read by build-state's legacy fallback chain.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "pull-sharepoint"
3
3
  version: "3.0.0"
4
- description: "v3.0.0 (kushi v4.9.0): Pull SharePoint evidence as Comprehensive Structured Capture (CSC) blocks written to weekly/YYYY-MM-DD_sharepoint-csc.md. One block per file touched that week, upserted in _index/entities.yml. Local synced-folder walk for metadata still allowed (filesystem only). WorkIQ-ONLY for content via CSC canonical prompts. No snapshot/+stream/ split. Per-contributor _index/. See weekly-csc + comprehensive-structured-capture doctrines."
4
+ description: "USE WHEN refresh-project / bootstrap-project dispatches SharePoint source OR the user says \"pull SharePoint for <X>\" AND project boundaries.sharepoint.site_url is set. DO NOT USE for tenant-wide site discovery. Capability: pulls SharePoint evidence (document library activity, page edits, list items) as CSC blocks written to weekly/YYYY-MM-DD_sharepoint-csc.md, upserted in _index/entities.yml. WorkIQ-only."
5
5
  ---
6
6
 
7
7
  # Skill: pull-sharepoint
@@ -21,6 +21,14 @@ description: "v3.0.0 (kushi v4.9.0): Pull SharePoint evidence as Comprehensive S
21
21
  > - ~~`verbatim-by-default.instructions.md`~~ (LEGACY — superseded by CSC).
22
22
  > - ~~`snapshot-vs-stream.instructions.md`~~ (LEGACY — superseded by weekly-csc).
23
23
 
24
+ ## Gotchas
25
+
26
+ - **Forbidden tenant-wide phrasings** — tenant-wide "list … sites" enumerate, site-ID lookups, and structured-field site searches all punt to Graph admin endpoints. Use display-name content queries per `sharepoint-bootstrap-discovery.instructions.md` (which holds the canonical forbidden list).
27
+ - **`sharepoint://siteurl=...` is the canonical id, not `siteId`** — site GUIDs change when a site is renamed; the URL is stable. Always cite by URL.
28
+ - **Drive item activity needs `Sites.Read.All` AND `Files.Read.All`** — when WorkIQ returns "no activity" for a site that clearly has activity, the consent scope is the culprit. Surface as `degraded-list-only` in `_index/entities.yml#status`.
29
+ - **List items vs library files vs pages all live under one site** — the CSC writer separates them under different anchors (`{#list-<name>}`, `{#file-<safe-title>}`, `{#page-<safe-title>}`). Do NOT collapse into one block.
30
+ - **SharePoint syncing to OneDrive is on a separate pin policy** — see `sharepoint-to-onedrive-sync.instructions.md`. Pulled content is read FROM SharePoint, not from the local OneDrive mirror.
31
+
24
32
  ## v4.9.0 — Comprehensive Structured Capture (CSC) + weekly/ layout (HARD RULE; supersedes all snapshot/+stream/ guidance below)
25
33
 
26
34
  Per `comprehensive-structured-capture.instructions.md` and `weekly-csc.instructions.md`:
@@ -61,8 +69,8 @@ Per `comprehensive-structured-capture.instructions.md` and `weekly-csc.instructi
61
69
 
62
70
  - Local synced folder walk stays (filesystem metadata enumeration is always allowed; not an M365 API call).
63
71
  - WorkIQ CSC prompt per selected file.
64
- - Drop `snapshot/tree.md` + `snapshot/files/<path>.md`. Tree information becomes the `Artifacts/Links` section of CSC blocks (each block lists the file's path + parent folder + any sibling files cited in its content).
65
-
72
+ - Drop `snapshot/tree.md` + `snapshot/files/<path>.md`. Tree information becomes the `Artifacts/Links` section of CSC blocks (each block lists the file's path + parent folder + any sibling files cited in its content).
73
+
66
74
  Pulls **sharepoint** evidence in two shapes per `snapshot-vs-stream.instructions.md`:
67
75
 
68
76
  - **snapshot/** — tree.md (full folder tree) + files/<path>.md (key files with full extracted content)
@@ -171,11 +179,11 @@ An entity that cannot meet the threshold is flagged `low_signal: true` in `_inde
171
179
 
172
180
  - Hint missing AND fuzzy resolution returns 0 candidates → ask user once, persist answer to mutable, continue.
173
181
  - Multiple plausible candidates → ask user to pick, persist answer.
174
- - WorkIQ fails (after doubled-strict retry) AND user declines to paste → write evidence file with `❌ workiq-empty-after-retry` marker, log to run-log errors with `next_step: re-run after WorkIQ recovery or paste verbatim`, continue with rest of run. Do NOT fall through to Graph / `m365_*` — FORBIDDEN per workiq-only.
175
- ## References (v4.4.7)
176
-
177
- - Name → ID resolution follows ..\..\instructions\fuzzy-disambiguation.instructions.md (universal fuzzy contract).
178
- - After this pull completes, the per-source verification gate runs: ..\..\instructions\per-source-verification-gate.instructions.md (retry once, then write FOLLOW-UPS.md).
182
+ - WorkIQ fails (after doubled-strict retry) AND user declines to paste → write evidence file with `❌ workiq-empty-after-retry` marker, log to run-log errors with `next_step: re-run after WorkIQ recovery or paste verbatim`, continue with rest of run. Do NOT fall through to Graph / `m365_*` — FORBIDDEN per workiq-only.
183
+ ## References (v4.4.7)
184
+
185
+ - Name → ID resolution follows ..\..\instructions\fuzzy-disambiguation.instructions.md (universal fuzzy contract).
186
+ - After this pull completes, the per-source verification gate runs: ..\..\instructions\per-source-verification-gate.instructions.md (retry once, then write FOLLOW-UPS.md).
179
187
 
180
188
 
181
189
  ## Issue Recovery
@@ -190,4 +198,13 @@ When this skill exposes a reusable defect (auth pattern, doctrine gap, layout mi
190
198
  info now lands in CSC `Artifacts/Links`). WorkIQ prompts switched to CSC canonical prompts. AI
191
199
  Narrative Summary requirement removed (CSC bulleted sections carry the load). Local synced-folder
192
200
  walk unchanged. Legacy snapshot/+stream/ folders left readable; no migration.
193
- - **v2.x.x**: prior snapshot/+stream/ + verbatim-by-default shape. See git history.
201
+ - **v2.x.x**: prior snapshot/+stream/ + verbatim-by-default shape. See git history.
202
+
203
+ ## Validation loop
204
+
205
+ After writing outputs:
206
+
207
+ 1. Run self-check targeted at this skill: `pwsh plugin/skills/self-check/run.ps1 -Targeted pull-sharepoint`
208
+ 2. If failures: fix and re-run the affected step (not the whole skill).
209
+ 3. Repeat until self-check exits 0.
210
+ 4. Only then update `run-log.yml` with success status.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "pull-teams"
3
3
  version: "3.0.0"
4
- description: "v3.0.0 (kushi v4.9.0): Pull Teams chat + channel evidence as Comprehensive Structured Capture (CSC) blocks written to weekly/YYYY-MM-DD_teams-csc.md. One block per chat/channel thread touched that week, upserted in _index/entities.yml. WorkIQ-ONLY via CSC canonical prompts; `m365_list_chat_messages` allowed as parallel structured-data dump to `_index/chat-messages_<chatId>.json`. No snapshot/+stream/ split. Per-contributor _index/. See weekly-csc + comprehensive-structured-capture doctrines."
4
+ description: "USE WHEN refresh-project / bootstrap-project dispatches Teams source OR the user says \"pull Teams for <X>\" AND project boundaries.teams (chats/channels) list is non-empty. DO NOT USE for general Teams search. Capability: pulls Teams chat + channel evidence as CSC blocks written to weekly/YYYY-MM-DD_teams-csc.md, one block per thread touched, upserted in _index/entities.yml. WorkIQ-only."
5
5
  ---
6
6
 
7
7
  # Skill: pull-teams
@@ -21,6 +21,14 @@ description: "v3.0.0 (kushi v4.9.0): Pull Teams chat + channel evidence as Compr
21
21
  > - ~~`verbatim-by-default.instructions.md`~~ (LEGACY — superseded by CSC).
22
22
  > - ~~`snapshot-vs-stream.instructions.md`~~ (LEGACY — superseded by weekly-csc).
23
23
 
24
+ ## Gotchas
25
+
26
+ - **Chat ID and channel ID are different namespaces** — `teams://chat_id=<id>` (1:1 / group chats) and `teams://channel=<teamId>/<channelId>` (team channels) must NOT be merged in `_index/entities.yml`. Different `id:` prefixes are intentional.
27
+ - **Forbidden bulk-enumerate phrasings** — bulk "list … chats", chat-ID lookups, and structured-field chat searches all punt to Graph. Use display-name content queries per `teams-bootstrap-discovery.instructions.md` (which holds the canonical forbidden list).
28
+ - **Edited messages do NOT change `createdDateTime`** — incrementality keyed on `createdDateTime` misses edits. Use `lastModifiedDateTime` for the watermark; CSC bullets cite the edit time.
29
+ - **Channel threads include the root + replies as one entity** — `_index/entities.yml` rolls the whole thread under one anchor. Reply-only weeks still touch the thread''s `last_touched`.
30
+ - **Bots and webhook posts have synthetic `from.user` blocks** — when `from.user.userIdentityType == "applicationInstance"`, render as `[bot] <displayName>` in CSC participants; never resolve as a person.
31
+
24
32
  ## v4.9.0 — Comprehensive Structured Capture (CSC) + weekly/ layout (HARD RULE; supersedes all snapshot/+stream/ guidance below)
25
33
 
26
34
  Per `comprehensive-structured-capture.instructions.md` and `weekly-csc.instructions.md`:
@@ -63,8 +71,8 @@ Per `comprehensive-structured-capture.instructions.md` and `weekly-csc.instructi
63
71
 
64
72
  - Run `m365_list_chat_messages(chatId)` in **PARALLEL** with WorkIQ CSC pull (allowed structured-data dump exception). Save raw JSON to `_index/chat-messages_<chatId>.json` (no longer the legacy `snapshot/`).
65
73
  - WorkIQ CSC prompts per chat/channel — one CSC block per thread.
66
- - For channels, the root post defines the thread; the entity anchor is the rootMessageId.
67
-
74
+ - For channels, the root post defines the thread; the entity anchor is the rootMessageId.
75
+
68
76
  Pulls **teams** evidence in two shapes per `snapshot-vs-stream.instructions.md`:
69
77
 
70
78
  - **snapshot/** — chat roster + member list per chat the user is part of for this project
@@ -158,11 +166,11 @@ After successful pass:
158
166
 
159
167
  - Hint missing AND fuzzy resolution returns 0 candidates → ask user once, persist answer to mutable, continue.
160
168
  - Multiple plausible candidates → ask user to pick, persist answer.
161
- - WorkIQ fails (after doubled-strict retry) AND user declines to paste → write evidence file with `❌ workiq-empty-after-retry` marker, log to run-log errors with `next_step: re-run after WorkIQ recovery or paste verbatim`, continue with rest of run. Do NOT fall through to Graph / `m365_*` — FORBIDDEN per workiq-only.
162
- ## References (v4.4.7)
163
-
164
- - Name → ID resolution follows ..\..\instructions\fuzzy-disambiguation.instructions.md (universal fuzzy contract).
165
- - After this pull completes, the per-source verification gate runs: ..\..\instructions\per-source-verification-gate.instructions.md (retry once, then write FOLLOW-UPS.md).
169
+ - WorkIQ fails (after doubled-strict retry) AND user declines to paste → write evidence file with `❌ workiq-empty-after-retry` marker, log to run-log errors with `next_step: re-run after WorkIQ recovery or paste verbatim`, continue with rest of run. Do NOT fall through to Graph / `m365_*` — FORBIDDEN per workiq-only.
170
+ ## References (v4.4.7)
171
+
172
+ - Name → ID resolution follows ..\..\instructions\fuzzy-disambiguation.instructions.md (universal fuzzy contract).
173
+ - After this pull completes, the per-source verification gate runs: ..\..\instructions\per-source-verification-gate.instructions.md (retry once, then write FOLLOW-UPS.md).
166
174
 
167
175
 
168
176
  ## Issue Recovery
@@ -183,4 +191,13 @@ An entity that cannot meet the threshold is flagged `low_signal: true` in `_inde
183
191
  removed (CSC bulleted sections carry the load). Per-message verbatim reproduction removed.
184
192
  `m365_list_chat_messages` parallel dump now lands in `_index/chat-messages_<chatId>.json`.
185
193
  Legacy snapshot/+stream/ folders left readable; no migration.
186
- - **v2.x.x**: prior snapshot/+stream/ + verbatim-by-default shape. See git history.
194
+ - **v2.x.x**: prior snapshot/+stream/ + verbatim-by-default shape. See git history.
195
+
196
+ ## Validation loop
197
+
198
+ After writing outputs:
199
+
200
+ 1. Run self-check targeted at this skill: `pwsh plugin/skills/self-check/run.ps1 -Targeted pull-teams`
201
+ 2. If failures: fix and re-run the affected step (not the whole skill).
202
+ 3. Repeat until self-check exits 0.
203
+ 4. Only then update `run-log.yml` with success status.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "refresh-project"
3
3
  version: "4.0.0"
4
- description: "Incremental refresh for an already-bootstrapped project (kushi v4.9.0+). Uses _index/entities.yml for incrementality; writes to the current ISO week's weekly/<YYYY-MM-DD>_<source>-csc.md; intra-week reruns merge by entity-anchor per weekly-csc.instructions.md. Verbatim-by-default — every enabled source MUST be dispatched, no silent skips. 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."
4
+ description: "USE WHEN the user says \"refresh <X>\", \"weekly extract for <X>\", \"update <X>\", \"pull <X> since <date>\", or \"do it all for <X>\" AND the project is ALREADY bootstrapped (Evidence/ exists). DO NOT USE for first-time setup (use bootstrap-project). Capability: incremental refresh — uses _index/entities.yml for incrementality, writes to current ISO week's weekly/<YYYY-MM-DD>_<source>-csc.md, intra-week reruns merge by entity-anchor. Verbatim-by-default. Builds State/ only on full profile."
5
5
  ---
6
6
 
7
7
  # Skill: refresh-project
@@ -46,6 +46,19 @@ Profile is read from `kushi-install.json#profile` next to the agent file. Defaul
46
46
 
47
47
  ## Steps
48
48
 
49
+
50
+ ## Step checklist
51
+
52
+ Progress-trackable view of the steps below. Each `### Step` block expands the corresponding checkbox.
53
+
54
+ - [ ] Step 1 — Resolve project + read run-log
55
+ - [ ] Step 2a — Drain deferred-retry queue (REQUIRED, kushi v4.4.1+, per `deferred-retry-on-workiq-fail.instructions.md`)
56
+ - [ ] Step 2 — Per-source dispatch
57
+ - [ ] Step 3 — Consolidate (if multi-user)
58
+ - [ ] Step 4 — Build state (FULL PROFILE ONLY)
59
+ - [ ] Step 4b — Cross-source graph + dashboard + tour (kushi v5.0.0)
60
+ - [ ] Step 5 — Run summary
61
+
49
62
  ### Step 1 — Resolve project + read run-log
50
63
 
51
64
  - **Sync-pending gate (v4.5.0):** before resolving, check `<workspace>/.kushi/config/user/project-evidence.yml#projects_root_status`. If `syncing` or `manual-sync-pending`, refuse: *"Engagement root sync still pending. Run `@Kushi setup --resume-sync` once OneDrive finishes."* Exit cleanly.
@@ -200,4 +213,13 @@ When this skill exposes a reusable defect (auth pattern, doctrine gap, layout mi
200
213
  ## Changelog
201
214
 
202
215
  - **v4.0.0 (kushi v5.0.0, 2026-05-26)**: After build-state, dispatch the v5 enrichment chain — `link-entities` → `dashboard` → `tour`. Each step's success/failure is recorded in the run summary; a failure of one step does not block the next.
203
- - **v3.0.0 (kushi v4.9.0, 2026-05-26)**: Incremental refresh now driven by `_index/entities.yml` (last_touched comparison). Output goes to current week's `weekly/<YYYY-MM-DD>_<source>-csc.md`; intra-week reruns merge by entity-anchor. Deferred-retry drain targets weekly/ output. No new writes to snapshot/ or stream/.
216
+ - **v3.0.0 (kushi v4.9.0, 2026-05-26)**: Incremental refresh now driven by `_index/entities.yml` (last_touched comparison). Output goes to current week's `weekly/<YYYY-MM-DD>_<source>-csc.md`; intra-week reruns merge by entity-anchor. Deferred-retry drain targets weekly/ output. No new writes to snapshot/ or stream/.
217
+
218
+ ## Validation loop
219
+
220
+ After writing outputs:
221
+
222
+ 1. Run self-check targeted at this skill: `pwsh plugin/skills/self-check/run.ps1 -Targeted refresh`
223
+ 2. If failures: fix and re-run the affected step (not the whole skill).
224
+ 3. Repeat until self-check exits 0.
225
+ 4. Only then update `run-log.yml` with success status.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "self-check"
3
3
  version: "2.0.0"
4
- description: "Pre-commit / pre-push validator. Checks that Kushi's skills, instructions, prompts, agent definition, templates, and key docs are all internally consistent. Source of truth is the file system. Findings are warnings with concrete fixes — never blocking."
4
+ description: "USE WHEN the user says \"self-check\", \"validate kushi\", \"pre-commit check\", \"are all skills documented?\", or before committing/pushing changes to the kushi repo. DO NOT USE for evidence-validation of a specific project (use ask-project / project-status). Capability: validates kushi's skills, instructions, prompts, agent definition, templates, and key docs are internally consistent against the file system. Findings are warnings with concrete fixes — never blocking."
5
5
  ---
6
6
 
7
7
  # Skill: self-check
@@ -61,6 +61,14 @@ Checks split into **core** (always run) and **deep** (opt-in).
61
61
  | D27 | Personal-path leakage | no contributor-specific path / email / repo-local-path patterns (`OneDrive - Microsoft\ISE\Engagement Assets`, `C:\Users\<contributor>`, `<contributor>@microsoft.com`, etc.) appear in `plugin/`, `docs/getting-started/`, or `README.md`. Examples must use placeholders. Exemptions: `CHANGELOG.md`, `plugin/learnings/`, `plugin/reference-packs/`. |
62
62
  | D28 | Predecessor-agent leakage | no current-doc file in `plugin/` or `docs/` references the historical predecessor agent by name. Use neutral language ('predecessor agent' / 'another internal agent'). Exemptions: `CHANGELOG.md` (historical commit notes), `plugin/learnings/`, `plugin/reference-packs/`, `docs/reference/instructions-map.md`. |
63
63
  | D29 | Vertex integration integrity | validates `plugin/config/studios.json` + schema, parses `detect-vertex-repo.mjs` / `vertex-validate.mjs`, asserts no vendored `templates/vertex-schemas/`, and confirms `emit-vertex` + `vertex-link` skills ship. |
64
+ | D30.skill-size | agentskills.io size cap | every `plugin/skills/<name>/SKILL.md` ≤ 500 lines AND ≤ 5000 tokens (chars/4). See `agentskills-compliance.instructions.md`. |
65
+ | D30.references-layout | Load-on-trigger pointers | any skill with a sibling `references/` folder must cite at least one `references/<file>.md` from SKILL.md (the load-on-trigger pattern). |
66
+ | D30.gotchas-section | Top-5 gotchas surfaced | every `pull-*` SKILL has a `## Gotchas` section near the top. |
67
+ | D30.checklist-orchestrators | Orchestrator checklists | `bootstrap-project`, `refresh-project`, `build-state`, `link-entities`, `dashboard`, `tour` use `- [ ]` syntax for their step lists. |
68
+ | D30.validation-loop | Writer validation loop | every writer skill (writes to `Evidence/`, `State/`, `_graph/`, `dashboards/`, `tours/`) ends with a `## Validation loop` section. |
69
+ | D30.description-optimized | Trigger-based description | every SKILL.md `description:` front-matter leads with `USE WHEN` or `WHEN ` per <https://agentskills.io/skill-creation/optimizing-descriptions>. |
70
+ | D31.genealogy | Release genealogy entry exists | every `git tag` matching `v<x.y.z>` MUST appear in `docs/genealogy.md` as a `## v<x.y.z>` heading or be named under a parent's "Patch lineage" line. See `release-genealogy.instructions.md`. |
71
+ | D32.multi-host | Multi-host install integrity | validates `src/multi-host.mjs` exports + `bin/cli.mjs` flag handling, then performs a temp-dir dry-run install for BOTH supported hosts (Clawpilot + VS Code Chat) under a fake `$HOME` in `$env:TEMP`. Asserts SKILL.md + agent file + skills/ + prompts/ + skills-metadata.json with a kushi entry are present, then asserts a clean uninstall. NEVER touches the real `~/.copilot/` or `~/.vscode/`. See `multi-host-install.instructions.md`. |
64
72
  | **CSC weekly-layout checks (kushi v4.9.0)** | | gated on `Resolve-EngagementRoots` — no-ops on the kushi repo itself. |
65
73
  | D11.csc | CSC entity coverage + depth | every `Evidence/<alias>/<source>/weekly/*-csc.md` has ≥ 1 entity heading; per-source minimum bullet count + populated-section count (meetings 25/6, email 8/4, teams 6/3, onenote 10/4, sharepoint 8/3, crm 12/5, ado 8/4). Coverage-Notes-only blocks (low-signal escape) are exempt. |
66
74
  | D12.csc | CSC section order | every entity block's `###` section headings appear in the canonical order: Participants → Topics → Q&A → Who Said What → Decisions → Dates & Numbers → Action Items → Next Steps → Open Questions → Risks → Customer Asks → Artifacts → Coverage Notes. |