kushi-agents 4.4.3 → 4.7.4

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 (95) hide show
  1. package/README.md +4 -0
  2. package/package.json +4 -4
  3. package/plugin/agents/kushi.agent.md +30 -14
  4. package/plugin/config/studios.json +37 -0
  5. package/plugin/config/studios.schema.json +45 -0
  6. package/plugin/instructions/auth-and-retry.instructions.md +268 -1
  7. package/plugin/instructions/engagement-root-resolution.instructions.md +5 -1
  8. package/plugin/instructions/evidence-thoroughness.instructions.md +103 -1
  9. package/plugin/instructions/fuzzy-disambiguation.instructions.md +97 -0
  10. package/plugin/instructions/identity-resolution.instructions.md +76 -0
  11. package/plugin/instructions/issue-recovery.instructions.md +58 -0
  12. package/plugin/instructions/kushi-config-root.instructions.md +66 -0
  13. package/plugin/instructions/loop-bootstrap-discovery.instructions.md +105 -0
  14. package/plugin/instructions/m365-id-registry.instructions.md +1 -1
  15. package/plugin/instructions/onedrive-pin-policy.instructions.md +132 -0
  16. package/plugin/instructions/per-source-verification-gate.instructions.md +193 -0
  17. package/plugin/instructions/sharepoint-to-onedrive-sync.instructions.md +116 -0
  18. package/plugin/instructions/status-color-rule.instructions.md +62 -0
  19. package/plugin/instructions/studio-registry.instructions.md +48 -0
  20. package/plugin/instructions/update-ledger.instructions.md +1 -1
  21. package/plugin/instructions/verbatim-by-default.instructions.md +1 -1
  22. package/plugin/instructions/vertex-emit.instructions.md +120 -0
  23. package/plugin/instructions/workiq-input-sanitization.instructions.md +43 -0
  24. package/plugin/instructions/workiq-onenote-query-shape.instructions.md +79 -0
  25. package/plugin/instructions/workiq-only.instructions.md +15 -9
  26. package/plugin/learnings/loop.md +11 -0
  27. package/plugin/learnings/onenote.md +27 -1
  28. package/plugin/lib/Get-KushiConfig.ps1 +22 -9
  29. package/plugin/lib/detect-vertex-repo.mjs +96 -0
  30. package/plugin/lib/render-vertex.mjs +249 -0
  31. package/plugin/lib/sanitize-workiq-input.mjs +72 -0
  32. package/plugin/lib/studio-registry.mjs +39 -0
  33. package/plugin/lib/vertex-validate.mjs +121 -0
  34. package/plugin/plugin.json +16 -6
  35. package/plugin/prompts/bootstrap.prompt.md +9 -7
  36. package/plugin/prompts/emit-vertex.prompt.md +33 -0
  37. package/plugin/prompts/setup.prompt.md +29 -0
  38. package/plugin/prompts/vertex-link.prompt.md +27 -0
  39. package/plugin/skills/aggregate-project/SKILL.md +24 -2
  40. package/plugin/skills/apply-ado-update/SKILL.md +9 -4
  41. package/plugin/skills/ask-project/SKILL.md +4 -0
  42. package/plugin/skills/bootstrap-project/SKILL.md +67 -41
  43. package/plugin/skills/consolidate-evidence/SKILL.md +5 -1
  44. package/plugin/skills/emit-vertex/README.md +37 -0
  45. package/plugin/skills/emit-vertex/SKILL.md +173 -0
  46. package/plugin/skills/intro/SKILL.md +2 -0
  47. package/plugin/skills/propose-ado-update/SKILL.md +8 -3
  48. package/plugin/skills/pull-ado/SKILL.md +11 -1
  49. package/plugin/skills/pull-crm/SKILL.md +12 -2
  50. package/plugin/skills/pull-email/SKILL.md +11 -1
  51. package/plugin/skills/pull-loop/README.md +64 -0
  52. package/plugin/skills/pull-loop/SKILL.md +180 -0
  53. package/plugin/skills/pull-loop/runner.mjs +261 -0
  54. package/plugin/skills/pull-loop/write-snapshot.mjs +181 -0
  55. package/plugin/skills/pull-meetings/SKILL.md +11 -1
  56. package/plugin/skills/pull-misc/README.md +4 -4
  57. package/plugin/skills/pull-misc/SKILL.md +18 -12
  58. package/plugin/skills/pull-onenote/SKILL.md +71 -19
  59. package/plugin/skills/pull-sharepoint/SKILL.md +11 -2
  60. package/plugin/skills/pull-teams/SKILL.md +11 -2
  61. package/plugin/skills/refresh-project/SKILL.md +38 -7
  62. package/plugin/skills/self-check/SKILL.md +14 -1
  63. package/plugin/skills/self-check/run.ps1 +442 -20
  64. package/plugin/skills/setup/SKILL.md +377 -0
  65. package/plugin/skills/vertex-link/SKILL.md +143 -0
  66. package/plugin/templates/init/m365-auth.template.json +10 -4
  67. package/plugin/templates/init/project-evidence.template.yml +10 -3
  68. package/plugin/templates/init/project-integrations.template.yml +5 -0
  69. package/plugin/templates/snapshot/ado-item.template.md +1 -1
  70. package/plugin/templates/snapshot/crm-record.template.md +1 -1
  71. package/plugin/templates/snapshot/meetings-series-index.template.md +1 -1
  72. package/plugin/templates/snapshot/onenote-page.template.md +1 -1
  73. package/plugin/templates/snapshot/sharepoint-file.template.md +1 -1
  74. package/plugin/templates/snapshot/sharepoint-tree.template.md +1 -1
  75. package/plugin/templates/snapshot/teams-roster.template.md +1 -1
  76. package/plugin/templates/weekly/ado-stream.template.md +1 -1
  77. package/plugin/templates/weekly/crm-stream.template.md +1 -1
  78. package/plugin/templates/weekly/email-stream.template.md +1 -1
  79. package/plugin/templates/weekly/meetings-stream.template.md +1 -1
  80. package/plugin/templates/weekly/onenote-stream.template.md +1 -1
  81. package/plugin/templates/weekly/sharepoint-stream.template.md +1 -1
  82. package/plugin/templates/weekly/teams-stream.template.md +1 -1
  83. package/src/check-workiq.mjs +109 -15
  84. package/src/config-loader.mjs +71 -13
  85. package/src/config-root-resolve.test.mjs +137 -0
  86. package/src/detect-vertex-repo.test.mjs +128 -0
  87. package/src/emit-vertex.e2e.test.mjs +308 -0
  88. package/src/forbidden-workiq-phrasings.test.mjs +111 -0
  89. package/src/main.mjs +11 -2
  90. package/src/sanitize-workiq-input.test.mjs +45 -0
  91. package/src/vertex-validate.test.mjs +142 -0
  92. package/plugin/instructions/az-auth-conditional.instructions.md +0 -39
  93. package/plugin/instructions/azure-auth-patterns.instructions.md +0 -233
  94. package/plugin/instructions/thoroughness-detector.instructions.md +0 -105
  95. package/plugin/instructions/workiq-first.instructions.md +0 -31
@@ -0,0 +1,132 @@
1
+ ---
2
+ applyTo: "**"
3
+ description: "After a SharePoint library is synced to a local OneDrive mirror, kushi must pin (always-keep-on-device) the folders this contributor reads+writes constantly while leaving other contributors' evidence cloud-only. This avoids 8× disk duplication on shared engagements without losing visibility of shared state. Cross-platform: Windows uses `attrib +P` / `attrib -P +U`; macOS uses `xattr` + `brctl`."
4
+ ---
5
+
6
+ # OneDrive pin policy (v4.5.0)
7
+
8
+ ## Why
9
+
10
+ A kushi engagement root, once synced, contains:
11
+
12
+ ```
13
+ <engagement-root>/
14
+ <project>/
15
+ integrations.yml ← shared, tiny, read every run
16
+ bootstrap-status.md ← shared, read every run
17
+ Evidence/
18
+ run-log.yml ← shared
19
+ contributors.yml ← shared
20
+ ushak/ ← YOUR slice — read+write heavy
21
+ bob/ ← other contributor — read only during consolidate
22
+ maria/ ← other contributor — read only during consolidate
23
+ _Consolidated/ ← shared output of consolidate
24
+ State/ ← shared, rewritten by state/consolidate
25
+ ```
26
+
27
+ If every contributor's OneDrive holds every byte of every other contributor's evidence, an 8-person engagement multiplies disk usage by 8× for no benefit.
28
+
29
+ But scoping the sync per-contributor breaks `consolidate` and `state` (they need to read everyone's evidence). The right answer is: **one sync root, Files-On-Demand pinning per folder**.
30
+
31
+ Files-On-Demand makes non-pinned folders **cloud-only stubs** on disk:
32
+
33
+ - Folder + file metadata visible (so kushi can walk the tree).
34
+ - Bytes downloaded only when a file is opened.
35
+ - Pinned folders are kept locally always.
36
+
37
+ ## What to pin (per contributor)
38
+
39
+ The pin policy on each contributor's machine:
40
+
41
+ | Folder/file | Action | Reason |
42
+ |---|---|---|
43
+ | `<project>/integrations.yml` | **Pin** | Read every kushi run; tiny |
44
+ | `<project>/bootstrap-status.md` | **Pin** | Read every kushi run; tiny |
45
+ | `<project>/FOLLOW-UPS.md` | **Pin** | Written by gate; read by next run |
46
+ | `<project>/Evidence/run-log.yml` | **Pin** | Watermark, every run |
47
+ | `<project>/Evidence/contributors.yml` | **Pin** | Every run |
48
+ | `<project>/Evidence/<my-alias>/` | **Pin (recursive)** | Read+write heavy; can't be cloud-only |
49
+ | `<project>/Evidence/_Consolidated/` | **Pin (recursive)** | Read+write by consolidate/state |
50
+ | `<project>/State/` | **Pin (recursive)** | Read+write by build-state |
51
+ | `<project>/Evidence/<other-alias>/` | **Leave cloud-only** (default) | Only touched during consolidate; OneDrive auto-downloads on demand |
52
+
53
+ ## Cross-platform pin commands
54
+
55
+ ### Windows
56
+
57
+ ```powershell
58
+ # Pin (always keep on device) — recursive on folders
59
+ attrib +P "<absolute-path>" /S /D
60
+
61
+ # Cloud-only (free up space) — recursive on folders
62
+ attrib -P +U "<absolute-path>" /S /D
63
+ ```
64
+
65
+ `attrib` ships with Windows; no admin required for files the user owns. Note: `+P` and `+U` are mutually exclusive — when pinning, OneDrive auto-clears `+U` and vice versa.
66
+
67
+ ### macOS
68
+
69
+ ```bash
70
+ # Pin a folder
71
+ xattr -w com.microsoft.OneDrive.PinnedToDevice 1 "<absolute-path>"
72
+
73
+ # Force download (materialize) a cloud-only path
74
+ brctl download "<absolute-path>"
75
+
76
+ # Free up local space (set cloud-only)
77
+ brctl evict "<absolute-path>"
78
+ ```
79
+
80
+ For recursive operations, walk the tree and apply per-folder. macOS does NOT support a single recursive pin command equivalent to Windows' `attrib /S /D` — kushi MUST iterate sub-folders.
81
+
82
+ ### Linux
83
+
84
+ Not supported. No OneDrive client → no Files-On-Demand → no pin semantics. Skip pin policy on Linux; surface a one-line info note.
85
+
86
+ ## When kushi applies the policy
87
+
88
+ ### Setup Step 4h — initial pin policy
89
+
90
+ After `projects_root` is resolved AND points at a real directory AND OneDrive is detected (Windows: OneDrive.exe process; macOS: `pgrep OneDrive`), apply pin policy to **every project listed in `active_projects`** that has an `Evidence/<my-alias>/` folder. Skip projects with no own-alias folder yet (they'll be picked up by bootstrap).
91
+
92
+ Output:
93
+
94
+ ```
95
+ 🔧 Optimizing OneDrive cache for your engagement…
96
+ ✅ Pinned (kept on device): HCA/Evidence/ushak/, HCA/State/, HCA/_Consolidated/,
97
+ + project-level run-log.yml + integrations.yml
98
+ ➖ Left cloud-only (downloaded on demand): HCA/Evidence/bob/, HCA/Evidence/maria/
99
+ Disk impact: ~180 MB local · ~6.2 GB available cloud-only.
100
+ Change later: right-click any folder → 'Always keep on device' / 'Free up space',
101
+ or run @Kushi setup --reconfigure.
102
+ ```
103
+
104
+ ### Bootstrap pin hook
105
+
106
+ When `bootstrap-project` creates a new project's `Evidence/<my-alias>/` + `State/` + `_Consolidated/`, it MUST extend the pin set to those new folders. Idempotent — repeated calls are no-ops.
107
+
108
+ ### Refresh — no policy action
109
+
110
+ `refresh-project` does NOT re-apply pin policy. Trust the user's manual overrides between runs. (Self-check D24 surfaces if the policy got demoted, e.g. user clicked "Free up space" on their own evidence folder.)
111
+
112
+ ## Detection of unsupported environments
113
+
114
+ Pin commands MUST be a no-op (with info log) when:
115
+
116
+ - OS = Linux.
117
+ - OneDrive process not running.
118
+ - Path is NOT inside a OneDrive-synced root (Windows: not under `%USERPROFILE%`; macOS: not under `~/Library/CloudStorage/` or `~/<Tenant>/`).
119
+ - Path is on a network share or mapped drive that bypasses OneDrive entirely.
120
+
121
+ In all those cases: log `Pin policy skipped (OneDrive Files-On-Demand not applicable on this path).` and continue.
122
+
123
+ ## Override mechanics
124
+
125
+ Users can manually pin/unpin via right-click Explorer/Finder at any time. Kushi MUST NOT overwrite user choices on subsequent runs. The pin operations above are **additive**: kushi adds folders to the pin set, never removes them. (Demotion is solely the user's action.)
126
+
127
+ ## References
128
+
129
+ - `sharepoint-to-onedrive-sync.instructions.md` — the auto-sync doctrine that precedes this.
130
+ - `engagement-root-resolution.instructions.md` — where `projects_root` is canonically resolved.
131
+ - `docs/concepts/sync-and-pinning.md` — user-facing doc explaining the disk-footprint model.
132
+ - `docs/concepts/storage-backends.md` — supported backend matrix.
@@ -0,0 +1,193 @@
1
+ ---
2
+ applyTo: "**/SKILL.md,**/bootstrap-project/**,**/refresh-project/**,**/aggregate-project/**"
3
+ description: "HARD rule v4.4.7+ — after each pull-<source> completes inside bootstrap/refresh/aggregate, run a verification gate that confirms snapshot/stream/verbatim shapes are healthy. On gate failure, retry the pull ONCE. If still failing, write a follow-on entry to <project>/FOLLOW-UPS.md + tracking + run-log and continue to the next source. Never silently move on."
4
+ ---
5
+
6
+ # Per-Source Verification Gate (HARD RULE, v4.4.7+)
7
+
8
+ Every `pull-<source>` invocation inside `bootstrap-project` / `refresh-project` / `aggregate-project` MUST be followed by a verification gate before the next source starts. Sources are independent — one source's gate failure does NOT cascade to others, but it MUST be captured in three places (run-log, tracking, FOLLOW-UPS.md) so the user can review later.
9
+
10
+ ## Why this exists
11
+
12
+ Before v4.4.7, a `pull-*` could "complete" with `status: ok` but leave an empty `snapshot/` folder, no `stream/<week>.md` file, or a meetings `verbatim/` folder with only `captured-at.txt`. The orchestrator moved on. The user only discovered the gap days later when querying.
13
+
14
+ The gate detects the gap **in the same run** and retries once. If retry still fails, the gap is queued for review instead of buried.
15
+
16
+ ## Gate contract (per source)
17
+
18
+ Run this check **after** `pull-<source>` returns, **before** the next source starts:
19
+
20
+ ### 1. Shape checks (universal)
21
+
22
+ | Check | Pass criteria |
23
+ |---|---|
24
+ | `Evidence/<alias>/<source>/` exists | directory exists, not empty |
25
+ | Snapshot shape (if source has snapshots) | at least one `snapshot/<artifact>.md` file ≥ 200 bytes, OR `snapshot/.empty` marker with documented reason |
26
+ | Stream shape (if source has streams) | at least one `stream/<YYYY-MM-DD>_<source>-stream.md` per ISO week in the run window, OR `stream/.empty` marker with documented reason |
27
+ | Run-log entry written | `Evidence/run-log.yml#sources.<source>.last_run` matches THIS run's timestamp + has `last_status` from `status-taxonomy.instructions.md` |
28
+ | Status taxonomy compliance | `last_status` ∈ closed set from `status-taxonomy.instructions.md` (no ad-hoc strings) |
29
+ | Citation compliance | spot-check 3 random assertions in newly written files — each MUST have inline `[source: ...]` citation per `citation-ledger.instructions.md` |
30
+
31
+ ### 2. Per-source extra checks
32
+
33
+ | Source | Additional gate |
34
+ |---|---|
35
+ | `meetings` | every per-meeting block in `stream/<week>.md` HAS a sibling `verbatim/<YYYY-MM-DD-HHMM>_<slug>/` containing at least `captured-at.txt` + `coverage.md` + ONE transcript-class file (`transcript.vtt` / `transcript.txt` / `transcript-source.md`) OR `coverage.md` documents `transcript-unrecoverable` |
36
+ | `onenote` | every section listed in `m365-mutable.json#knownSections.<project>.onenote` resolved to a `sectionId` (no `<auto>` left); per-page-body or per-section-summary written |
37
+ | `loop` | every workspace listed in `m365-mutable.json#knownSections.<project>.loop.workspaces[]` resolved to a `workspaceId`; per `loop-bootstrap-discovery.instructions.md` Pre-flight A–D; per-page-body file written for each registered `loop_pages[]` OR a documented `page-body-unavailable` / `tree-only` annotation |
38
+ | `sharepoint` | resolved `siteId` persisted to `<project>/integrations.yml`; tree file in `snapshot/sharepoint-tree.md` exists |
39
+ | `teams` | resolved `chatId[]` persisted to `<alias>/.settings.yml`; chat-stream files cover the window |
40
+ | `email` | mailbox-folder filter persisted in `m365-auth.json#emailContext.folders`; email-stream files cover the window |
41
+ | `crm` | resolved `engagementRecordId` persisted to `<project>/integrations.yml`; 4-step Dataverse resolution per `crm-bootstrap-discovery.instructions.md` is complete (not stuck at step 1 or 2) |
42
+
43
+ ### 2a. Evidence-source-kind annotation (v4.5.1+ — HARD)
44
+
45
+ When a `pull-<source>` cannot read the canonical surface and falls back to a secondary surface, the per-source coverage notes (`coverage.md` for meetings; `<week>-stream.md` header for streamed sources; `snapshot/<artifact>.md` header for snapshotted sources) MUST emit an explicit annotation:
46
+
47
+ ```yaml
48
+ # At top of the written artifact, immediately after the source/window header:
49
+ evidence_source_kind: comments # canonical — preferred
50
+ # OR
51
+ evidence_source_kind: history # secondary — record/board history when comments are empty
52
+ # OR
53
+ evidence_source_kind: attachment-fallback # tertiary — discussion lives in a .docx/.pdf attachment
54
+ # OR
55
+ evidence_source_kind: page-body-unavailable # OneNote stub section (pages moved/pulled out)
56
+ # OR
57
+ evidence_source_kind: transcript-unrecoverable # meetings only — recording exists but transcript inaccessible
58
+ ```
59
+
60
+ The gate MUST verify this annotation:
61
+ - ✅ **Pass** when `evidence_source_kind: comments` (or a source's canonical equivalent) is present.
62
+ - ✅ **Pass** when a non-canonical `evidence_source_kind` is present AND the coverage notes explain why the canonical surface was not used.
63
+ - ❌ **Fail** when the artifact carries discussion content but NO `evidence_source_kind` annotation (caller can't tell whether comments were empty or the canonical surface was simply not checked).
64
+
65
+ Canonical surface by source (the value that means "the preferred surface was used"):
66
+
67
+ | Source | Canonical kind | Fallback kinds |
68
+ |---|---|---|
69
+ | `ado` | `comments` | `history`, `attachment-fallback`, `description-only` |
70
+ | `crm` | `notes` | `timeline`, `attachment-fallback`, `summary-only` |
71
+ | `onenote` | `page-body` | `page-body-unavailable`, `section-summary-only` |
72
+ | `loop` | `page-body` | `page-body-unavailable`, `tree-only` |
73
+ | `sharepoint` | `tree+modified` | `tree-only`, `permission-denied` |
74
+ | `meetings` | `transcript` | `transcript-unrecoverable`, `attachment-fallback`, `chat-only` |
75
+ | `teams` | `messages` | `messages-truncated`, `attachment-fallback` |
76
+ | `email` | `messages` | `messages-truncated`, `attachment-fallback` |
77
+
78
+ Downstream consumers (`aggregate-project`, `ask-project`, `fde-report`) MUST read this annotation and surface it in any answer/report that cites the artifact, so the user knows whether they are reading canonical discussion or a fallback (which is rarely as complete).
79
+ | `ado` | resolved `areaPath` persisted to `<project>/integrations.yml`; ADO-update-snapshot rendered |
80
+ | `misc` | OPEN-QUESTIONS-DRAFT.md, external-links.txt processed (`read` markers updated) |
81
+
82
+ ### 3. Process-compliance checks (the "did we actually follow the process" audit)
83
+
84
+ These verify the skill **followed its own doctrine**, not just produced output:
85
+
86
+ | Process check | Doctrine | What we verify |
87
+ |---|---|---|
88
+ | WorkIQ-first | `workiq-only.instructions.md` | run-log entry's `tools_used:` lists `workiq` as tool #1 (when applicable for the source) |
89
+ | Verbatim-by-default | `verbatim-by-default.instructions.md` | source was dispatched (not silently skipped) IF boundary was satisfied |
90
+ | Capture learnings | `capture-learnings.instructions.md` | if `run-log#sources.<source>.errors[]` has entries this run, `<KUSHI_ROOT>/plugin/learnings/<source>.md` was updated in the same turn |
91
+ | Run-report mention | `run-reports.instructions.md` | this source appears in today's `refresh-reports/<timestamp>_<mode>.md` per-source table |
92
+ | Fuzzy disambiguation | `fuzzy-disambiguation.instructions.md` | if any name → id resolution happened, the resolution was persisted to the right registry (id-registry / integrations.yml / .settings.yml) |
93
+ | Cleanup on resolution | `cleanup-on-resolution.instructions.md` | if a previous `unresolved-<x>` was resolved this run, the stale marker was pruned |
94
+ | Citation ledger | `citation-ledger.instructions.md` | newly written files carry inline `[source: …]` citations |
95
+
96
+ ## Retry-once contract
97
+
98
+ If ANY gate check fails on first pass:
99
+
100
+ 1. **Diagnose** — record which checks failed and why.
101
+ 2. **Re-dispatch** the same `pull-<source>` skill with the same boundary, same window, plus a `--gate-retry` flag (skill MAY use this to widen its cascade, e.g. relax a strict-match in step 2).
102
+ 3. **Re-run** the gate.
103
+ 4. **If second pass also fails** → record + continue (see "Failure handling").
104
+
105
+ NEVER retry more than once per source per run. NEVER block other sources.
106
+
107
+ ## Failure handling (write to all three surfaces)
108
+
109
+ When the gate fails after retry, write to ALL of:
110
+
111
+ ### A. `Evidence/run-log.yml#sources.<source>`
112
+
113
+ ```yaml
114
+ sources:
115
+ <source>:
116
+ last_status: completed-with-coverage-gaps # or partial / failed per status-taxonomy
117
+ retry_signal: user-action # or retry / watch per status-taxonomy
118
+ last_run: <ISO-8601>
119
+ gate_failures:
120
+ - check: <gate-check-id>
121
+ reason: <short>
122
+ retried: true
123
+ retry_outcome: <status>
124
+ process_audit:
125
+ workiq_first: pass | fail | n/a
126
+ verbatim_by_default: pass | fail | n/a
127
+ capture_learnings: pass | fail | n/a
128
+ run_report_mention: pass | fail | n/a
129
+ fuzzy_disambiguation: pass | fail | n/a
130
+ cleanup_on_resolution: pass | fail | n/a
131
+ citation_ledger: pass | fail | n/a
132
+ ```
133
+
134
+ ### B. `<project>/FOLLOW-UPS.md` (new file — shared across contributors per `multi-user-shared-files.instructions.md`)
135
+
136
+ Append an entry under `## Open follow-ups`:
137
+
138
+ ```markdown
139
+ ### <source> · <YYYY-MM-DD> · <alias>
140
+
141
+ **Gate failures (after 1 retry):**
142
+ - ❌ <check-name>: <reason>
143
+ - ❌ <check-name>: <reason>
144
+
145
+ **Process audit failures:**
146
+ - ❌ <doctrine>: <what was skipped>
147
+
148
+ **Next-time-do:**
149
+ 1. <specific actionable step>
150
+ 2. <specific actionable step>
151
+
152
+ **Tracking:** `.kushi/tracking/runs/<YYYY-MM-DD>-<project>-<verb>.md`
153
+ **Run-log entry:** `Evidence/run-log.yml#sources.<source>.last_run = <ISO-8601>`
154
+ **Status:** open
155
+ ```
156
+
157
+ The "Next-time-do" steps are the same actionable hints the agent would have surfaced in chat — but written somewhere the user (or a future kushi run) can pick them up.
158
+
159
+ ### C. `<workspace>/.kushi/tracking/runs/<YYYY-MM-DD>-<project>-<verb>.md`
160
+
161
+ In the tracking file's `## Decisions & open questions` section:
162
+
163
+ ```markdown
164
+ - ⚠ <source> gate failed after 1 retry. See `<project>/FOLLOW-UPS.md` entry dated <YYYY-MM-DD>.
165
+ - Failed checks: <check-name>, <check-name>
166
+ - Process audit: <doctrine>=<pass|fail>, ...
167
+ - Continued to next source per per-source-verification-gate.instructions.md.
168
+ ```
169
+
170
+ ## Resolution / closeout
171
+
172
+ When a follow-up is resolved (next refresh fixes the gap, or user fixes manually):
173
+
174
+ 1. The next gate run detects the source is now healthy → marks the FOLLOW-UPS.md entry `**Status:** resolved-<YYYY-MM-DD>` and moves it under `## Resolved follow-ups`.
175
+ 2. The `cleanup-on-resolution.instructions.md` rules apply: stale `gate_failures` rows older than 60 days with status `resolved` get pruned from run-log.
176
+
177
+ ## Self-check enforcement
178
+
179
+ **D18 — Per-source verification gate cite.** Every `pull-*` SKILL.md MUST reference `per-source-verification-gate.instructions.md` in its body. `bootstrap-project` + `refresh-project` + `aggregate-project` SKILL.md MUST describe gate invocation between sources.
180
+
181
+ **D19 — FOLLOW-UPS.md format compliance.** When a `<project>/FOLLOW-UPS.md` exists, every "Open follow-ups" entry MUST have the 5 required fields (Gate failures / Process audit / Next-time-do / Tracking / Run-log entry / Status). Same shape per multi-user merge rules.
182
+
183
+ ## References
184
+
185
+ - `status-taxonomy.instructions.md` — closed-set vocabulary for `last_status` and `retry_signal`.
186
+ - `verbatim-by-default.instructions.md` — sources may be silently skipped only when boundary is missing.
187
+ - `meetings-verbatim-required.instructions.md` — meetings-source extra gate (transcript-class file required).
188
+ - `citation-ledger.instructions.md` — citation spot-check rule.
189
+ - `multi-user-shared-files.instructions.md` — `FOLLOW-UPS.md` is shared across contributors via OneDrive; absorb conflict copies on every write.
190
+ - `run-reports.instructions.md` — the per-user refresh report references gate outcomes.
191
+ - `tracking.instructions.md` — tracking file is the per-run audit surface.
192
+ - `fuzzy-disambiguation.instructions.md` — universal name → id resolution flow.
193
+ - `cleanup-on-resolution.instructions.md` — closeout flow for resolved gate failures.
@@ -0,0 +1,116 @@
1
+ ---
2
+ applyTo: "**"
3
+ description: "When a user provides a SharePoint URL where a local path is required (e.g. projects_root), trigger the OneDrive client to sync the library via the sanctioned odopen:// URI scheme with user consent, then poll for the local mirror and persist that path. Cross-platform: Windows + macOS supported; Linux falls through to manual. Never silent — user always sees + confirms OneDrive's native sync dialog."
4
+ ---
5
+
6
+ # SharePoint → OneDrive auto-sync (v4.5.0)
7
+
8
+ ## Why
9
+
10
+ `projects_root` and any other "local engagement folder" config field MUST resolve to a real filesystem path. Kushi reads + writes with normal filesystem APIs and cannot dereference `https://*.sharepoint.com/*` URLs.
11
+
12
+ But users typically know their engagement library as a SharePoint URL (the link they got in a Teams message), not as a local path. This doctrine bridges the two with **one user consent**.
13
+
14
+ ## Detection
15
+
16
+ A value supplied for `projects_root`, `engagement_root`, or any `*_root` config field is a SharePoint URL when ALL of:
17
+
18
+ - Scheme is `https://`
19
+ - Hostname matches `*.sharepoint.com`
20
+ - Path contains `/sites/` or `/teams/` followed by additional segments
21
+
22
+ Examples:
23
+
24
+ - ✅ `https://contoso.sharepoint.com/sites/ISE-FDE/Shared%20Documents/Engagement%20Assets`
25
+ - ✅ `https://contoso.sharepoint.com/teams/CTO-Eng/Documents/Engagements`
26
+ - ❌ `https://contoso.sharepoint.com` (root only — ambiguous, ask)
27
+ - ❌ `C:\Users\…` / `/Users/…` (already local — proceed as today)
28
+
29
+ ## The `odopen://` handshake
30
+
31
+ OneDrive Personal + OneDrive for Business both register the `odopen://` URI scheme. This is the same handler triggered by the SharePoint "Sync" button. Using it is **not** intrusive — OneDrive itself presents a native confirmation dialog ("Start syncing this library?") and the user must consent.
32
+
33
+ ### URI shape
34
+
35
+ ```
36
+ odopen://sync/?url={encoded-sp-folder-url}&userEmail={encoded-upn}
37
+ ```
38
+
39
+ Where:
40
+
41
+ - `url` — full SharePoint URL the user provided, percent-encoded.
42
+ - `userEmail` — current contributor UPN (resolved by `setup` Step 3). Helps OneDrive pick the right tenant identity when the user has multiple.
43
+
44
+ This simple form works in all recent OneDrive client builds. If it ever fails on a particular machine, the recovery path is the documented manual flow (click Sync in browser).
45
+
46
+ ### Launching the URI
47
+
48
+ Cross-platform launch (always non-blocking; OneDrive runs in its own process):
49
+
50
+ - **Windows (PowerShell)** — `Start-Process "odopen://sync/?url=...&userEmail=..."`
51
+ - **macOS (zsh / bash)** — `open "odopen://sync/?url=...&userEmail=..."`
52
+ - **Linux** — not supported (no OneDrive client). Fall through to manual recipe in `docs/concepts/sync-and-pinning.md`.
53
+
54
+ ## Expected local mirror path
55
+
56
+ After sync starts, OneDrive creates a folder under the user's home directory. Path conventions:
57
+
58
+ ### Windows
59
+
60
+ - `%USERPROFILE%\<Tenant Display Name>\<Site Display Name> - <Library Display Name>\<sub-folder>`
61
+ - Common form: `C:\Users\<you>\Contoso\ISE-FDE - Documents\Engagement Assets`
62
+ - Personal OneDrive (rare for engagements): `C:\Users\<you>\OneDrive - Contoso\<sub-folder>`
63
+
64
+ ### macOS (12.3+ uses CloudStorage)
65
+
66
+ - `~/Library/CloudStorage/OneDrive-SharedLibraries-<Tenant>/<Site> - <Library>/<sub-folder>`
67
+ - Example: `~/Library/CloudStorage/OneDrive-SharedLibraries-Contoso/ISE-FDE - Documents/Engagement Assets`
68
+ - Legacy (pre-CloudStorage): `~/<Tenant>/<Site> - <Library>/<sub-folder>` (still detected as fallback).
69
+
70
+ ### Linux
71
+
72
+ - Not supported. Skill MUST not poll on Linux — surface the manual recipe immediately.
73
+
74
+ ## Polling contract
75
+
76
+ After launching the `odopen://` URI:
77
+
78
+ 1. Compute the **candidate local paths** (Windows: 2 candidates including personal OneDrive; macOS: 2 candidates including legacy). Best-guess the tenant/site/library segments from the URL path.
79
+ 2. Poll every 5 seconds for up to **5 minutes** (cold libraries can take a while to materialize the top-level folder structure). Each poll: `Test-Path` / `[ -d ]` on every candidate.
80
+ 3. On first hit, **also wait for at least one expected sub-folder to appear** (any folder, not just empty mirror) — confirms the directory tree has started populating, not just the empty stub.
81
+ 4. Print a progress line every 15 seconds: `Waiting for OneDrive to materialize <path-being-watched>… (elapsed: 45s)`.
82
+ 5. On hit → persist + green ✅.
83
+ 6. On 5-min timeout → write `projects_root_status: "syncing"` + `projects_root_candidate: "<best-guess-path>"` to `project-evidence.yml` and surface: `Sync started but the local mirror hasn't appeared yet. Run @Kushi setup --resume-sync once OneDrive finishes (status bar icon shows ✅).`
84
+
85
+ ## ask_user contract (Setup Step 4c)
86
+
87
+ When user pastes a SharePoint URL where a path was expected:
88
+
89
+ > ⚠ That's a SharePoint URL, not a local path. Kushi needs a local folder (the OneDrive client mirrors SharePoint to disk).
90
+ >
91
+ > • **Sync this library now** (recommended) — opens OneDrive's native sync dialog; you click "Sync" once.
92
+ > • **I'll sync it myself** — show me the manual steps + docs link.
93
+ > • **Cancel** — let me paste a local path instead.
94
+
95
+ | Choice | Action |
96
+ |---|---|
97
+ | Sync this library now | Launch `odopen://`, then poll. |
98
+ | I'll sync it myself | Print 3-step manual recipe (open URL in browser → click Sync → wait → run `setup --resume-sync`). Persist `projects_root_status: "manual-sync-pending"`. |
99
+ | Cancel | Go back to "type a local path" prompt. |
100
+
101
+ ## Bootstrap blocking behavior
102
+
103
+ If `projects_root_status` is `syncing` or `manual-sync-pending`, `bootstrap`/`refresh` MUST refuse to start with: `Engagement root sync still pending. Run @Kushi setup --resume-sync to complete.`
104
+
105
+ `ask`, `status`, and Q&A verbs remain available (they read existing files only).
106
+
107
+ ## Linux + fallback
108
+
109
+ On Linux OR if OneDrive client process is not detected (`Get-Process onedrive` on Windows; `pgrep -f OneDrive` on macOS returns nothing), skip the `odopen://` path entirely and show the manual recipe with a link to `docs/concepts/sync-and-pinning.md`.
110
+
111
+ ## References
112
+
113
+ - `engagement-root-resolution.instructions.md` — where `projects_root` is canonically read/written.
114
+ - `onedrive-pin-policy.instructions.md` — what to pin AFTER the local mirror exists (this doctrine's natural follow-on).
115
+ - `docs/concepts/sync-and-pinning.md` — user-facing companion doc.
116
+ - `docs/concepts/storage-backends.md` — supported backend matrix.
@@ -0,0 +1,62 @@
1
+ ---
2
+ applyTo: "plugin/skills/project-status/**, plugin/skills/emit-vertex/**, plugin/skills/build-state/**"
3
+ description: "Deterministic status-color rule (🟢/🟡/🔴) with auditable Reason: line. Borrowed from vertex weekly-status agent Phase 3."
4
+ ---
5
+
6
+ # Status-color rule (first-match-wins)
7
+
8
+ When emitting a project status (kushi `project-status`, `emit-vertex` weekly status, `build-state` health summary), the color MUST be computed from inputs by the rule below — never from LLM inference. The triggering rule MUST be recorded verbatim in the `Reason:` line of any emitted status email or in the `status_reason` field of any structured artifact.
9
+
10
+ ## Rule order — first match wins
11
+
12
+ ### 🔴 Red — any of:
13
+
14
+ | # | Trigger | Evidence source |
15
+ |---|---|---|
16
+ | R1 | ≥1 active risk with `Severity: High` AND `Mitigation:` empty | `State/06_risks-and-issues.md` (or `Evidence/_Consolidated/risks-ledger.md`) |
17
+ | R2 | Previous status was 🔴 AND no closing note exists in this period | last `status-updates/*.md` + `Evidence/*/<period>` |
18
+ | R3 | Explicit escalation in this period — `"blocked"`, `"stop work"`, `"executive escalation"`, `"go/no-go"` in emails or meetings | `Evidence/*/email/snapshot/`, `meetings/snapshot/` |
19
+ | R4 | ADO work item with `Severity: 1` opened in period | `Evidence/*/ado/snapshot/` |
20
+
21
+ ### 🟡 Yellow — any of:
22
+
23
+ | # | Trigger | Evidence source |
24
+ |---|---|---|
25
+ | Y1 | ≥1 active risk with `Severity: High` (mitigation present) | risks ledger |
26
+ | Y2 | ≥3 active risks with `Severity: Medium` | risks ledger |
27
+ | Y3 | Milestone slipped past due date in this period | `State/07_timeline-and-milestones.md` (or game-plan equivalents) |
28
+ | Y4 | Open action items overdue by > 7 days | `State/05_action-items.md` |
29
+
30
+ ### 🟢 Green
31
+
32
+ None of the above triggered.
33
+
34
+ ## Reason line shape
35
+
36
+ Format: `<RULE-ID>: <brief human reason>`
37
+
38
+ Examples:
39
+
40
+ ```text
41
+ Reason: R1: 1 High-severity risk has no mitigation (FK schema mismatch, Bob Jones, opened 2026-05-12)
42
+ Reason: Y2: 3 Medium risks active (data migration, AKS skill gap, CI/CD timeline)
43
+ Reason: Y3: Milestone "Production deployment readiness" slipped from 2026-05-15 to 2026-05-22
44
+ Reason: GREEN: 0 reds; 0 yellows; 1 milestone on track
45
+ ```
46
+
47
+ The rule-id MUST be present so the choice is reproducible from inputs alone.
48
+
49
+ ## Implementation contract
50
+
51
+ A skill computing status color SHOULD:
52
+
53
+ 1. Load risks ledger, action items ledger, timeline ledger, and the current reporting-period evidence index.
54
+ 2. Evaluate R1→R4, then Y1→Y4 in order.
55
+ 3. Stop at first match. Record `rule_id`, `triggers` (list of matching items with citations), and `color`.
56
+ 4. Emit `status_color`, `status_reason`, and `status_evidence` (list of `[source: ...]` citations) into the output artifact.
57
+
58
+ If the inputs are insufficient to evaluate any rule (missing ledgers), the color MUST be `🟡` with `Reason: INSUFFICIENT-EVIDENCE: <list of missing inputs>` — never default-green.
59
+
60
+ ## Why this exists
61
+
62
+ Status color is the single most consequential field in a weekly status email — it drives the ADO field via the Studio automation. Letting an LLM infer it from prose is non-reproducible and creates audit gaps. The rule borrowed from vertex's weekly-status agent (where it was a single block of doctrine) is promoted to a kushi instruction file so it applies to `project-status`, `emit-vertex`, and `build-state` uniformly.
@@ -0,0 +1,48 @@
1
+ ---
2
+ applyTo: "plugin/skills/setup/**, plugin/skills/bootstrap-project/**, plugin/skills/refresh-project/**, plugin/skills/emit-vertex/**, plugin/skills/fde-report/**"
3
+ description: "Studio registry — single source of truth for studio-scoped distribution lists, report recipients, and org-default behavior. Loaded via plugin/lib/studio-registry.mjs."
4
+ ---
5
+
6
+ # Studio registry
7
+
8
+ A **studio** in kushi is an organizational unit that owns:
9
+
10
+ - The Studio engagement automation address for status emails (`status_email.automation_to`)
11
+ - The Studio leadership BCC list (`status_email.bcc_recipients`)
12
+ - FDE report distribution defaults (`report_recipients.fde_report_to`, `_cc`)
13
+ - Brand metadata for emitted reports (`brand.name`, `brand.color_hex`)
14
+
15
+ Borrowed from vertex's `.vertex/scripts/studios.json` (status_email namespace only there); extended in kushi with `report_recipients` and `brand`.
16
+
17
+ ## Files
18
+
19
+ - **Packaged registry**: `plugin/config/studios.json` (ships with kushi)
20
+ - **User override**: `~/.copilot/m-skills/kushi/config/studios.json` (optional)
21
+ - **Per-project override**: `<project>/.kushi/studios.json` (optional, highest precedence)
22
+
23
+ Schema: `plugin/config/studios.schema.json`. Self-check D29 validates packaged registry against schema.
24
+
25
+ ## Lookup
26
+
27
+ ```js
28
+ import { loadStudioRegistry, getStudio } from '../../lib/studio-registry.mjs';
29
+ const reg = loadStudioRegistry({ projectRoot, userOverrideDir });
30
+ const studio = getStudio(reg, studioId); // null if unknown
31
+ ```
32
+
33
+ ## How skills SHOULD use this
34
+
35
+ 1. Read `project.studio` from `<project>/kushi.yaml` (added in v4.7.0).
36
+ 2. `getStudio(reg, project.studio)`.
37
+ 3. If `null` (unknown studio): treat distribution lists as user-managed; warn once per run with a link to GOVERNANCE.md (when published).
38
+ 4. If `studio._default` was returned (no project.studio set): ask user once, save into `kushi.yaml`.
39
+
40
+ ## Adding a new studio
41
+
42
+ Editing the registry is data-only. Until a Decision Process is published, contributors:
43
+
44
+ 1. Open a PR against `plugin/config/studios.json` adding their studio block.
45
+ 2. CI validates against `studios.schema.json`.
46
+ 3. Once merged + published, the new studio is selectable across every kushi install.
47
+
48
+ For local-only use, drop the block into `~/.copilot/m-skills/kushi/config/studios.json` (user override).
@@ -128,5 +128,5 @@ C13 is not yet implemented in `self-check/run.ps1` — added when `apply-ado-upd
128
128
 
129
129
  - `propose-ado-update` SKILL.md — produces the `proposed.md` this doctrine governs.
130
130
  - `apply-ado-update` SKILL.md — the only code path authorized to write to ADO; consumes `proposed.md`, appends to `ledger.jsonl`.
131
- - `azure-auth-patterns.instructions.md` — pre-flight + token + tenant guards that protect every write.
131
+ - `auth-and-retry.instructions.md` — pre-flight + token + tenant guards that protect every write.
132
132
  - `side-by-side-config.instructions.md` — `integrations.yml ado.writes:` follows the same template-vs-live pattern.
@@ -23,7 +23,7 @@ This restates and hardens the existing thoroughness contract in `evidence-thorou
23
23
 
24
24
  ## Anti-patterns (defects to refuse, not negotiate)
25
25
 
26
- The following are defects in any `pull-*` output. If the runtime detects one, it MUST auto-retry with stricter prompting per `thoroughness-detector.instructions.md` rather than ship the thin output.
26
+ The following are defects in any `pull-*` output. If the runtime detects one, it MUST auto-retry with stricter prompting per `evidence-thoroughness.instructions.md` rather than ship the thin output.
27
27
 
28
28
  1. **Email preview-only** — capturing only `bodyPreview` (200 chars) instead of `body`. Defect.
29
29
  2. **"And N more"** — any phrase like "...and 12 more emails", "10 of 47 shown", "see source for rest". Defect.