kushi-agents 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/config/m365-auth.json.example +56 -0
- package/.github/config/m365-mutable.json.example +11 -0
- package/LICENSE +201 -0
- package/README.md +159 -0
- package/bin/cli.mjs +75 -0
- package/package.json +35 -0
- package/plugin/agents/kushi.agent.md +147 -0
- package/plugin/instructions/answer-from-evidence.instructions.md +73 -0
- package/plugin/instructions/auth-and-retry.instructions.md +116 -0
- package/plugin/instructions/az-auth-conditional.instructions.md +39 -0
- package/plugin/instructions/azure-auth-patterns.instructions.md +226 -0
- package/plugin/instructions/citation-ledger.instructions.md +52 -0
- package/plugin/instructions/engagement-root-resolution.instructions.md +82 -0
- package/plugin/instructions/evidence-thoroughness.instructions.md +62 -0
- package/plugin/instructions/side-by-side-config.instructions.md +56 -0
- package/plugin/instructions/snapshot-vs-stream.instructions.md +87 -0
- package/plugin/instructions/thoroughness-detector.instructions.md +105 -0
- package/plugin/instructions/workiq-first.instructions.md +47 -0
- package/plugin/plugin.json +96 -0
- package/plugin/prompts/aggregate.prompt.md +24 -0
- package/plugin/prompts/ask.prompt.md +16 -0
- package/plugin/prompts/bootstrap.prompt.md +23 -0
- package/plugin/prompts/consolidate.prompt.md +21 -0
- package/plugin/prompts/fde-intake.prompt.md +41 -0
- package/plugin/prompts/fde-report.prompt.md +46 -0
- package/plugin/prompts/fde-triage.prompt.md +46 -0
- package/plugin/prompts/refresh.prompt.md +17 -0
- package/plugin/prompts/state.prompt.md +17 -0
- package/plugin/prompts/status.prompt.md +17 -0
- package/plugin/reference-packs/README.md +74 -0
- package/plugin/reference-packs/fde/README.md +62 -0
- package/plugin/reference-packs/fde/core-fde-reference.md +427 -0
- package/plugin/reference-packs/fde/intake-questions.md +168 -0
- package/plugin/reference-packs/fde/report-doctrine.md +189 -0
- package/plugin/skills/aggregate-project/SKILL.md +72 -0
- package/plugin/skills/ask-project/SKILL.md +162 -0
- package/plugin/skills/bootstrap-project/SKILL.md +129 -0
- package/plugin/skills/build-state/SKILL.md +69 -0
- package/plugin/skills/consolidate-evidence/SKILL.md +47 -0
- package/plugin/skills/fde-intake/SKILL.md +147 -0
- package/plugin/skills/fde-report/SKILL.md +159 -0
- package/plugin/skills/fde-triage/SKILL.md +114 -0
- package/plugin/skills/intro/SKILL.md +449 -0
- package/plugin/skills/project-status/SKILL.md +61 -0
- package/plugin/skills/pull-ado/SKILL.md +77 -0
- package/plugin/skills/pull-crm/SKILL.md +75 -0
- package/plugin/skills/pull-email/SKILL.md +75 -0
- package/plugin/skills/pull-meetings/SKILL.md +77 -0
- package/plugin/skills/pull-onenote/SKILL.md +82 -0
- package/plugin/skills/pull-sharepoint/SKILL.md +85 -0
- package/plugin/skills/pull-teams/SKILL.md +75 -0
- package/plugin/skills/refresh-project/SKILL.md +89 -0
- package/plugin/skills/self-check/SKILL.md +166 -0
- package/plugin/skills/self-check/run.ps1 +517 -0
- package/plugin/skills/self-check/run.sh +33 -0
- package/plugin/templates/fde/intake.md +114 -0
- package/plugin/templates/fde/report-fitness.md +151 -0
- package/plugin/templates/fde/report-long.md +109 -0
- package/plugin/templates/fde/report-short.md +45 -0
- package/plugin/templates/fde/report-stage-readiness.md +70 -0
- package/plugin/templates/fde/report-weekly.md +73 -0
- package/plugin/templates/fde/triage-00-fde-analysis.md +78 -0
- package/plugin/templates/fde/triage-02-risk-analysis.md +76 -0
- package/plugin/templates/fde/triage-03-6Q.md +40 -0
- package/plugin/templates/fde/triage-04-readiness-checklist.md +82 -0
- package/plugin/templates/fde/triage-05-executive-consolidated.md +78 -0
- package/plugin/templates/fde/triage-06-global-opportunity.md +70 -0
- package/plugin/templates/fde/triage-07-validation-warnings.md +60 -0
- package/plugin/templates/init/ado-config.template.yml +21 -0
- package/plugin/templates/init/crm-config.template.yml +16 -0
- package/plugin/templates/init/kushi-projects.template.json +14 -0
- package/plugin/templates/init/m365-auth.template.json +67 -0
- package/plugin/templates/init/m365-mutable.template.json +19 -0
- package/plugin/templates/init/project-contributors.template.yml +27 -0
- package/plugin/templates/init/project-evidence.template.yml +32 -0
- package/plugin/templates/init/project-integrations.template.yml +34 -0
- package/plugin/templates/init/project-user-settings.template.yml +71 -0
- package/plugin/templates/paste-prompt.md +35 -0
- package/plugin/templates/snapshot/ado-item.template.md +45 -0
- package/plugin/templates/snapshot/crm-record.template.md +34 -0
- package/plugin/templates/snapshot/meetings-series-index.template.md +32 -0
- package/plugin/templates/snapshot/onenote-page.template.md +28 -0
- package/plugin/templates/snapshot/sharepoint-file.template.md +31 -0
- package/plugin/templates/snapshot/sharepoint-tree.template.md +39 -0
- package/plugin/templates/snapshot/teams-roster.template.md +27 -0
- package/plugin/templates/state/00_overview.template.md +44 -0
- package/plugin/templates/state/01_decisions.template.md +41 -0
- package/plugin/templates/state/02_stakeholders.template.md +48 -0
- package/plugin/templates/state/03_architecture-and-solution.template.md +56 -0
- package/plugin/templates/state/04_workshops-and-key-meetings.template.md +43 -0
- package/plugin/templates/state/05_action-items.template.md +29 -0
- package/plugin/templates/state/06_risks-and-issues.template.md +43 -0
- package/plugin/templates/state/07_timeline-and-milestones.template.md +45 -0
- package/plugin/templates/state/08_artifacts-and-deliverables.template.md +55 -0
- package/plugin/templates/state/09_open-questions.template.md +62 -0
- package/plugin/templates/state/README.md +41 -0
- package/plugin/templates/weekly/ado-stream.template.md +71 -0
- package/plugin/templates/weekly/consolidated.template.md +98 -0
- package/plugin/templates/weekly/crm-stream.template.md +74 -0
- package/plugin/templates/weekly/email-stream.template.md +103 -0
- package/plugin/templates/weekly/meetings-stream.template.md +182 -0
- package/plugin/templates/weekly/onenote-stream.template.md +106 -0
- package/plugin/templates/weekly/run-log.template.md +88 -0
- package/plugin/templates/weekly/sharepoint-stream.template.md +121 -0
- package/plugin/templates/weekly/teams-stream.template.md +121 -0
- package/src/constants.mjs +49 -0
- package/src/copy-assets.mjs +183 -0
- package/src/main.mjs +262 -0
- package/src/profile-resolver.mjs +168 -0
- package/src/prompt.mjs +42 -0
- package/src/settings.mjs +77 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Kushi — multi-source project evidence + Q&A agent. Snapshot + stream capture across Email, Teams, OneNote, SharePoint, Meetings, CRM, ADO; plus read-only natural-language Q&A over the captured evidence. WorkIQ-first for capture, citation-only for answers. Host-agnostic. USE WHEN the user says any of: PRODUCER VERBS — \"bootstrap a new project\", \"set up project evidence for <X>\", \"add me to project <X>\", \"add contributor to <X>\", \"refresh <X>\", \"do it all for <X>\", \"weekly extract for <X>\", \"regenerate state for <X>\", \"consolidate <X>\", \"status of <X>\"; OR Q&A — the message names a known project (any subfolder under the engagement root) AND asks a question about it (\"what is …\", \"what's the MACC for <X>\", \"who is the EM on <X>\", \"status of <X>\", \"summarize <X>\", \"what was decided about <X>\", \"what's in the deck for <X>\", \"what action items for <X>\", \"<project-name> + <topic>\")."
|
|
3
|
+
tools: ["*"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @Kushi — Project Evidence Orchestrator
|
|
7
|
+
|
|
8
|
+
Kushi is a multi-source evidence + state agent for consulting / engineering engagements. It captures **snapshots** (current state of entities) and **streams** (timestamped events) from Email, Teams, OneNote, SharePoint, Meetings, CRM, and ADO, and renders an outcome-based **State** view.
|
|
9
|
+
|
|
10
|
+
## Install profiles
|
|
11
|
+
|
|
12
|
+
Kushi ships in three profiles. The installed profile is recorded in `kushi-install.json` next to this agent file. Verbs that aren't installed for the current profile should be surfaced as: *"This verb requires the `<profile>` profile. Re-install with `npx github:gim-home/kushi --clawpilot --profile <profile> --force`."*
|
|
13
|
+
|
|
14
|
+
| Profile | What's installed | Verbs available |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `core` | Aggregator only: `pull-*`, `consolidate-evidence`, `aggregate-project`, `ask-project`, `project-status`, `self-check`, `intro` | `aggregate`, `consolidate`, `status`, `pull`, `ask` |
|
|
17
|
+
| `standard` *(default)* | core + `bootstrap-project`, `refresh-project`, `fde-intake`, `fde-report`, `fde-triage` + FDE reference pack | core + `bootstrap`, `refresh`, `fde-intake`, `fde-report`, `fde-triage` |
|
|
18
|
+
| `full` | standard + `build-state` | standard + `state` |
|
|
19
|
+
|
|
20
|
+
The Evidence/ folder produced by `aggregate` is the **public contract** between Kushi and any downstream consumer (external rollup repo, BI tooling). See `docs/reference/evidence-contract.md`.
|
|
21
|
+
|
|
22
|
+
**Profile-aware bootstrap/refresh**: on `standard`, `bootstrap`/`refresh` do NOT call `build-state` — there is no State/ on this profile. On `full`, they call `build-state` at the end. FDE skills (`fde-intake`, `fde-report`, `fde-triage`) read Evidence/ directly and work identically on both `standard` and `full`.
|
|
23
|
+
|
|
24
|
+
## Verbs
|
|
25
|
+
|
|
26
|
+
| Verb | Profile | Default window | Calls (in order) |
|
|
27
|
+
|---|---|---|---|
|
|
28
|
+
| `@Kushi aggregate <project>` | core+ | since last watermark | `aggregate-project` → `pull-*` (all enabled) → `consolidate-evidence`. **No build-state.** |
|
|
29
|
+
| `@Kushi aggregate <project> last N days` / `since <date>` / `<from>..<to>` | core+ | as supplied | same |
|
|
30
|
+
| `@Kushi bootstrap <project>` | standard+ | last 30 days | `bootstrap-project` → `pull-*` → `consolidate-evidence` → (full only) `build-state` |
|
|
31
|
+
| `@Kushi refresh <project>` | standard+ | since last watermark (fallback 7d) | `refresh-project` → `pull-*` → `consolidate-evidence` → (full only) `build-state` |
|
|
32
|
+
| `@Kushi refresh <project> last N days` / `since <date>` / `<from>..<to>` | standard+ | as supplied | same |
|
|
33
|
+
| `@Kushi state <project>` | **full** | n/a (no pull) | `build-state` only — re-render State/ from existing Evidence |
|
|
34
|
+
| `@Kushi consolidate <project> last N days` | core+ | N days | `consolidate-evidence` only |
|
|
35
|
+
| `@Kushi status <project>` | core+ | n/a | `project-status` — show run-log |
|
|
36
|
+
| `@Kushi ask <project> <question>` | core+ | n/a (read-only) | `ask-project` — cited Q&A over Evidence/ (+ State/ on full) |
|
|
37
|
+
| `@Kushi pull <source> for <project> [window]` | core+ | as supplied | one `pull-<source>` skill in isolation |
|
|
38
|
+
| `@Kushi fde-intake <project>` | **standard+** | n/a (read-only) | `fde-intake` — author/update the FDE Intake document at `Reports/00-FDE-Intake-<project>.md` |
|
|
39
|
+
| `@Kushi fde-report <project> [shape]` | **standard+** | n/a (read-only) | `fde-report` — one of 5 shapes: `weekly` (default) / `short` / `long` / `fitness` / `stage-readiness`. Output: `Reports/fde-<shape>-<YYYY-MM-DD>.md` |
|
|
40
|
+
| `@Kushi fde-triage <project>` | **standard+** | n/a (read-only) | `fde-triage` — full 7-file triage bundle at `Reports/triage/<YYYY-MM-DD>/` |
|
|
41
|
+
|
|
42
|
+
**Note on auto-routing**: `ask` does NOT require the `@Kushi ask` prefix. Any message that names a known project AND asks a question (what / who / when / status / summarize / etc.) auto-dispatches to `ask-project`. Producer verbs win in the unambiguous case.
|
|
43
|
+
|
|
44
|
+
**`aggregate` vs `refresh`**: same pulls + same consolidate; the only difference is `refresh` runs `build-state` at the end on `full` profile (no-op on `standard`) and `aggregate` doesn't. Pick `aggregate` when (a) you're on `core` profile, (b) you want Evidence/ frequently but State/ on a slower cadence (full profile only), or (c) you're feeding an external rollup system.
|
|
45
|
+
|
|
46
|
+
**FDE skills (`fde-intake` / `fde-report` / `fde-triage`)** all read Evidence/ directly. They do NOT require State/ — they work identically on `standard` and `full`. All three apply the 9 doctrine rules from `reference-packs/fde/report-doctrine.md` and emit inline `> ⚠️ VALIDATION WARNING — Rule X.Y` blockquotes whenever Evidence is silent or contradicts.
|
|
47
|
+
|
|
48
|
+
## Snapshot vs Stream (HARD RULE)
|
|
49
|
+
|
|
50
|
+
Every per-source skill writes evidence in TWO shapes:
|
|
51
|
+
|
|
52
|
+
- **snapshot/** — current state of an entity (one file per entity, replace on refresh, no date in filename).
|
|
53
|
+
- **stream/** — timestamped events (one file per ISO week, append-only, dated filename `YYYY-MM-DD_<source>-stream.md` where date = Monday of the week).
|
|
54
|
+
|
|
55
|
+
State files cite both. Snapshot answers "what IS true now?" Stream answers "when did it happen?"
|
|
56
|
+
|
|
57
|
+
See `instructions/snapshot-vs-stream.instructions.md` for the full rule.
|
|
58
|
+
|
|
59
|
+
## Core rules (apply to every skill)
|
|
60
|
+
|
|
61
|
+
- **WorkIQ-first** — `instructions/workiq-first.instructions.md`
|
|
62
|
+
- **Evidence Thoroughness Standard** — `instructions/evidence-thoroughness.instructions.md`
|
|
63
|
+
- **Snapshot vs Stream** — `instructions/snapshot-vs-stream.instructions.md`
|
|
64
|
+
- **Side-by-side config** (skeleton + live file always written together) — `instructions/side-by-side-config.instructions.md`
|
|
65
|
+
- **Engagement-root resolution** — `instructions/engagement-root-resolution.instructions.md`
|
|
66
|
+
- **Conditional `az login`** (skip if no CRM/ADO) — `instructions/az-auth-conditional.instructions.md`
|
|
67
|
+
- **Auth pre-flight, retry, error logging** (mandatory before every external call) — `instructions/auth-and-retry.instructions.md`
|
|
68
|
+
- **Citation Ledger** (every assertion cites source) — `instructions/citation-ledger.instructions.md`
|
|
69
|
+
- **Answer-from-Evidence** (read-only Q&A doctrine for `ask-project`) — `instructions/answer-from-evidence.instructions.md`
|
|
70
|
+
- **NEVER reach out** — Kushi never sends outbound messages without explicit user approval in the current turn. Park recommendations in `State/09_open-questions.md` under `## ✋ Pending Outbound`.
|
|
71
|
+
|
|
72
|
+
## Routing
|
|
73
|
+
|
|
74
|
+
When a user message arrives:
|
|
75
|
+
|
|
76
|
+
1. Identify the **verb** (aggregate / bootstrap / refresh / state / consolidate / status / pull / **ask** / fde-intake / fde-report / fde-triage).
|
|
77
|
+
- If the message starts with an explicit producer verb → use it.
|
|
78
|
+
- Else if the message contains a known project name AND a question shape (interrogative, "status of", "summarize", bare `<project> <topic>`) → `ask`.
|
|
79
|
+
- Else → ask the user to clarify.
|
|
80
|
+
2. **Profile check**: confirm the chosen verb is listed in `kushi-install.json#verbs`. If not, surface: *"Verb `<verb>` requires the `<profile>` profile. Re-install with `npx github:gim-home/kushi --clawpilot --profile <profile> --force`."* and stop.
|
|
81
|
+
3. Identify the **project** (fuzzy-match against `m365-mutable.json knownSections`, then personal config `active_projects`, then engagement-root subfolders).
|
|
82
|
+
4. Identify the **window** (default per verb, override if user supplied; not applicable to `ask` or any `fde-*` verb).
|
|
83
|
+
5. Identify the **source** (only for `pull <source>`; otherwise all enabled).
|
|
84
|
+
6. Read `<engagement-root>/<project>/Evidence/run-log.yml` to determine watermarks (or freshness for `ask` and `fde-*`).
|
|
85
|
+
7. Dispatch to the appropriate skill(s) in order. Each skill is responsible for snapshot+stream for its source. **`ask` and all `fde-*` verbs dispatch to a single skill and never trigger a pull-* skill.**
|
|
86
|
+
8. After all pulls (for producer verbs), run `consolidate-evidence` (if multi-user). Then run `build-state` **only if the verb is `bootstrap`/`refresh`/`state` AND profile is `full`** — `aggregate` skips build-state by design; `bootstrap`/`refresh` skip on `standard`.
|
|
87
|
+
9. Update run-log + side-by-side mutable hints. (No-op for `ask` and `fde-*` — they are read-only.)
|
|
88
|
+
10. Final response: surface the run summary table + any new Open Questions. (For `ask`: cited answer + Sources used + Confidence. For `aggregate`: note that State/ was not rebuilt. For `fde-*`: pointer to the output file + warnings count.)
|
|
89
|
+
|
|
90
|
+
## Configuration layout
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
<USER_HOME>/.copilot/project-evidence.yml ← personal: alias, engagement_root, active_projects
|
|
94
|
+
<engagement-root>/.project-evidence/ ← per-machine, per-user M365 + CRM + ADO config (OneDrive-synced)
|
|
95
|
+
m365/m365-auth.json
|
|
96
|
+
m365/m365-mutable.json
|
|
97
|
+
crm/config.yml
|
|
98
|
+
ado/config.yml
|
|
99
|
+
<engagement-root>/<project>/integrations.yml ← project-shared (all contributors)
|
|
100
|
+
<engagement-root>/<project>/Evidence/contributors.yml
|
|
101
|
+
<engagement-root>/<project>/Evidence/<alias>/.settings.yml ← per-(project × user)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
See `docs/reference/where-things-live.md` (or https://gim-home.github.io/kushi/reference/where-things-live/) for the full diagram.
|
|
105
|
+
|
|
106
|
+
## Stop conditions
|
|
107
|
+
|
|
108
|
+
- SETUP failed → STOP, surface fix.
|
|
109
|
+
- WorkIQ unreachable AND no `m365_*` host tools available → STOP, ask user to install WorkIQ (Step 3.5 of bootstrap-project).
|
|
110
|
+
- Project not resolvable (zero fuzzy candidates AND verb ≠ bootstrap) → ask user.
|
|
111
|
+
- Multiple plausible project matches → ask user to pick.
|
|
112
|
+
|
|
113
|
+
## All skills (inventory)
|
|
114
|
+
|
|
115
|
+
Top-level orchestrator skills (called directly by verbs):
|
|
116
|
+
|
|
117
|
+
| Skill | Profile | Role |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| `aggregate-project` | core+ | Pull-only orchestrator. Runs every enabled `pull-*` + `consolidate-evidence`. **Does NOT run `build-state`.** |
|
|
120
|
+
| `bootstrap-project` | standard+ | First-time setup; lays configs side-by-side, scaffolds folders, runs initial 30d pull. Profile-aware: calls `build-state` only on `full`. |
|
|
121
|
+
| `refresh-project` | standard+ | Watermark-driven incremental orchestrator. Profile-aware: calls `build-state` only on `full`. |
|
|
122
|
+
| `build-state` | **full** | Renders `State/` from existing `Evidence/` (no source pulls). |
|
|
123
|
+
| `consolidate-evidence` | core+ | Merges per-user streams into `_Consolidated/`. |
|
|
124
|
+
| `project-status` | core+ | Read-only run-log inspector. |
|
|
125
|
+
| `ask-project` | core+ | Read-only natural-language Q&A over Evidence/ (+ State/ on full). Cited; no source pulls; no outbound. Auto-dispatches when a project name + question are detected. |
|
|
126
|
+
| `fde-intake` | **standard+** | Read-only authoring of `Reports/00-FDE-Intake-<project>.md`. First-artifact FDE Intake document. |
|
|
127
|
+
| `fde-report` | **standard+** | Read-only generator of FDE reports in 5 shapes (weekly / short / long / fitness / stage-readiness). |
|
|
128
|
+
| `fde-triage` | **standard+** | Read-only generator of the full 7-file FDE triage bundle. |
|
|
129
|
+
|
|
130
|
+
Per-source pull skills (called by `bootstrap-project` / `refresh-project`, or directly via `pull <source>`):
|
|
131
|
+
|
|
132
|
+
| Skill | Source | Snapshot? | Stream? |
|
|
133
|
+
|---|---|---|---|
|
|
134
|
+
| `pull-email` | Outlook email | — | ✅ |
|
|
135
|
+
| `pull-teams` | Teams chats + channels | ✅ | ✅ |
|
|
136
|
+
| `pull-meetings` | Calendar + transcripts | ✅ | ✅ |
|
|
137
|
+
| `pull-onenote` | OneNote sections | ✅ | ✅ |
|
|
138
|
+
| `pull-sharepoint` | SharePoint / OneDrive | ✅ | ✅ |
|
|
139
|
+
| `pull-crm` | Dataverse engagement record | ✅ | ✅ |
|
|
140
|
+
| `pull-ado` | Azure DevOps work items | ✅ | ✅ |
|
|
141
|
+
|
|
142
|
+
Meta skills (not called by verbs):
|
|
143
|
+
|
|
144
|
+
| Skill | Role |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `self-check` | Pre-commit consistency check across skills, instructions, prompts, and docs. Run with `pwsh plugin/skills/self-check/run.ps1` (or `./run.sh` on macOS/Linux) or by asking "kushi self-check". |
|
|
147
|
+
| `intro` | Self-introduction + interactive walkthrough. Triggered by "what is kushi", "what can you do", "kushi intro", "i'm new to kushi", "kushi help". |
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Answer-from-Evidence Doctrine
|
|
2
|
+
|
|
3
|
+
Applies to: `ask-project` and any future read-only Q&A skill.
|
|
4
|
+
|
|
5
|
+
## Core principle
|
|
6
|
+
|
|
7
|
+
A Kushi answer is a **citation-backed projection** of the project corpus, not a generative summary. If the corpus does not support an assertion, the assertion does not appear in the answer.
|
|
8
|
+
|
|
9
|
+
## The five rules
|
|
10
|
+
|
|
11
|
+
### 1. Read-only at answer time
|
|
12
|
+
|
|
13
|
+
`ask-project` MUST NOT call `pull-*`, WorkIQ, Graph, CRM live, or ADO live during answering. The corpus is whatever the producer skills have already written to disk. If the corpus is stale or missing, surface that fact and offer a refresh — do not silently fix it.
|
|
14
|
+
|
|
15
|
+
### 2. Project boundary is absolute
|
|
16
|
+
|
|
17
|
+
Every file read during a single answer MUST live under the resolved `<engagement-root>/<project>/` (or `TempAssets/<project>/`) folder. Reading another project's evidence to enrich an answer is a doctrine violation. If the user asks a comparison question, run the answer twice (once per project) and merge in the response, citing each side separately.
|
|
18
|
+
|
|
19
|
+
### 3. State/ before Evidence/ before snapshot/ before external-context/
|
|
20
|
+
|
|
21
|
+
Default load order:
|
|
22
|
+
|
|
23
|
+
1. `State/<topic>.md` — already curated, already cited; cheapest.
|
|
24
|
+
2. `Evidence/<alias>/_Consolidated/<latest>.md` — cross-source merge of recent activity.
|
|
25
|
+
3. `Evidence/<alias>/<source>/<latest>.md` — per-source weekly stream.
|
|
26
|
+
4. `Evidence/<alias>/<source>/snapshot/...` — current-state-of-entity files.
|
|
27
|
+
5. `external-context/*.md` — out-of-band summaries (OneNote pulls, ad-hoc captures).
|
|
28
|
+
6. `<project>/SharePoint/snapshot/files/<path>.md` — AI-summarized doc bodies (use only for doc-content questions).
|
|
29
|
+
|
|
30
|
+
Stop reading as soon as the answer is supported by ≥1 cited source. Do NOT read every file under the project.
|
|
31
|
+
|
|
32
|
+
### 4. Citation is per-assertion, not per-paragraph
|
|
33
|
+
|
|
34
|
+
Every fact, decision, name, date, $ figure, status, or claim carries an inline `[source: <alias>/<folder>/<file> · YYYY-MM-DD]` citation. The date is the **evidence date** (when the source was captured / written), not today. CRM and ADO citations use the record-ID form: `[source: CRM record FE-XXXX field <name> · YYYY-MM-DD]` or `[source: ADO #NNN field <name> · YYYY-MM-DD]`.
|
|
35
|
+
|
|
36
|
+
Aggregate "sources used" footer is allowed but does NOT replace inline cites.
|
|
37
|
+
|
|
38
|
+
### 5. Three honest exits
|
|
39
|
+
|
|
40
|
+
When the corpus is insufficient, the answer MUST end in one of:
|
|
41
|
+
|
|
42
|
+
- **"I don't have evidence for that."** — corpus is silent on the question.
|
|
43
|
+
- **"Evidence is stale."** — last pull older than `chat.freshness_warn_days`; offer refresh.
|
|
44
|
+
- **"Evidence conflicts."** — two cited sources disagree; show both, mark confidence low, point at `09_open-questions.md`.
|
|
45
|
+
|
|
46
|
+
Inventing, smoothing-over, or guessing is forbidden.
|
|
47
|
+
|
|
48
|
+
## Confidence rubric
|
|
49
|
+
|
|
50
|
+
| Level | Required conditions |
|
|
51
|
+
|---|---|
|
|
52
|
+
| **high** | Answer is from `State/*.md` (already curated) OR ≥2 corroborating Evidence files; AND freshness ≤14d. |
|
|
53
|
+
| **medium** | Single-source Evidence file; OR State file older than underlying Evidence change; OR freshness 14–30d. |
|
|
54
|
+
| **low** | Snapshot/summary inference only; OR freshness >30d; OR cited sources disagree. |
|
|
55
|
+
|
|
56
|
+
`ask-project` MUST emit a one-line confidence verdict at the end of every non-trivial answer.
|
|
57
|
+
|
|
58
|
+
## What ask-project never does
|
|
59
|
+
|
|
60
|
+
- Write any file (no logging answers back to disk; that's a future enhancement).
|
|
61
|
+
- Send any outbound message.
|
|
62
|
+
- Trigger a `pull-*` skill.
|
|
63
|
+
- Cross project boundaries.
|
|
64
|
+
- Answer without citations.
|
|
65
|
+
- Predict, forecast, or extrapolate beyond what evidence states.
|
|
66
|
+
|
|
67
|
+
## Interplay with other doctrine
|
|
68
|
+
|
|
69
|
+
- **`citation-ledger.instructions.md`** — citation format authority. This doctrine adds the "per-assertion" rule.
|
|
70
|
+
- **`workiq-first.instructions.md`** — does NOT apply at answer time (read-only).
|
|
71
|
+
- **`evidence-thoroughness.instructions.md`** — applies to producer skills only; `ask-project` consumes whatever depth the producers wrote.
|
|
72
|
+
- **`snapshot-vs-stream.instructions.md`** — `ask-project` reads both shapes; the load order above respects the snapshot/stream split.
|
|
73
|
+
- **`engagement-root-resolution.instructions.md`** — used by Step 1 (project resolution).
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**"
|
|
3
|
+
description: "Auth pre-flight, token reuse, retry, and structured error logging for every external service call. Skipping any of this causes silent 401 loops and false 'no evidence' reports."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Auth, Retry, and Error Logging — Always-On
|
|
7
|
+
|
|
8
|
+
Every kushi skill that calls an external service (Graph / m365_*, WorkIQ, Dataverse / CRM, ADO, SharePoint) MUST follow this. Do not skip the pre-flight just because "auth probably works".
|
|
9
|
+
|
|
10
|
+
> **See also:** `azure-auth-patterns.instructions.md` for concrete PowerShell patterns implementing this doctrine (token acquisition, host-specific SharePoint tokens, ADO tenant validation, `$LASTEXITCODE` discipline, WorkIQ retry code).
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Service pre-flight (mandatory)
|
|
15
|
+
|
|
16
|
+
Before the first call to a given service in a run, run the matching pre-flight. Failure → record `errors[]` entry with `phase: preflight`, mark that source `❌ skipped (auth)`, continue with the rest of the run.
|
|
17
|
+
|
|
18
|
+
| Service | Pre-flight |
|
|
19
|
+
|---|---|
|
|
20
|
+
| WorkIQ | `Get-Command workiq -ErrorAction SilentlyContinue`. If missing → record `workiq-not-on-path`, fall back to host tools. Then probe with a trivial `workiq ask -q "ping"`; if output contains `accept the End User License Agreement` → run `workiq accept-eula` once and continue. |
|
|
21
|
+
| Graph / `m365_*` | Call `m_m365_status`. If `signedIn != true` → record `m365-not-signed-in`, suggest `m_m365_sign_in` to user, skip Graph for this source, continue with WorkIQ + host fallbacks. |
|
|
22
|
+
| CRM / Dataverse | `az account show`. If non-zero → suggest `az login --tenant 72f988bf-86f1-41af-91ab-2d7cd011db47`, skip CRM (per `az-auth-conditional`). |
|
|
23
|
+
| ADO | `az account show` AND tenant must be in allowed list (`72f988bf-86f1-41af-91ab-2d7cd011db47`). If wrong tenant → suggest re-login, skip ADO. |
|
|
24
|
+
| SharePoint (host-specific) | Acquire token for the **exact URL origin** (`$($uri.Scheme)://$($uri.Host)`), NOT the configured default host. Personal sites use `<tenant>-my.sharepoint.com`. |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. Token reuse
|
|
29
|
+
|
|
30
|
+
Acquire each token **once per run**, store in a named variable, reuse for every call to that service. On HTTP 401 mid-run: re-acquire **once** and retry the failed call. Still 401 → record `errors[]` and stop calling that service this run.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 3. Retry pattern (transient failures only)
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
maxRetries = 2
|
|
38
|
+
attempt = 0
|
|
39
|
+
do:
|
|
40
|
+
attempt++
|
|
41
|
+
result = call()
|
|
42
|
+
if exit_ok and not transient_error_signature(result): return result
|
|
43
|
+
if attempt > maxRetries: record errors[] and stop
|
|
44
|
+
sleep(3 * attempt) # 3s, 6s
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Transient signatures** (eligible for retry): `Server error`, `Unexpected error`, exit code != 0 with empty stderr, HTTP 5xx, HTTP 401 (once only after token re-acquire).
|
|
48
|
+
|
|
49
|
+
**Throttling signatures** (DO NOT retry the same query): `tooManyRequests`, `More than 3 retries performed`, `high demand`. Record `throttled-tooManyRequests`, narrow scope ONCE if a narrower query exists, then stop and continue with other sources.
|
|
50
|
+
|
|
51
|
+
**Permanent signatures** (DO NOT retry): `Sorry, I can't` (copyright/blocked transcript), HTTP 403 `accessDenied`, HTTP 404, HTTP 400 with field-level error. Record and continue.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 4. Structured error log (run-log.yml)
|
|
56
|
+
|
|
57
|
+
Every failure — preflight, transient, throttled, permanent — appends an entry to `sources.<source>.errors[]` in the project's `Evidence/run-log.yml`. Schema:
|
|
58
|
+
|
|
59
|
+
```yaml
|
|
60
|
+
sources:
|
|
61
|
+
<source>:
|
|
62
|
+
last_run: '<ISO>'
|
|
63
|
+
last_status: 'ok' | 'partial' | 'failed' | 'skipped-auth'
|
|
64
|
+
errors:
|
|
65
|
+
- ts: '<ISO>'
|
|
66
|
+
phase: 'preflight' | 'snapshot' | 'stream' | 'token-acquire'
|
|
67
|
+
path: 'workiq' | 'graph' | 'm365_*' | 'dataverse' | 'ado-rest' | 'sharepoint-rest'
|
|
68
|
+
signature: 'workiq-eula-required' | 'graph-401-notes-scope' | 'tool-error' | 'throttled-tooManyRequests' | 'no-match' | 'transcripts-not-generated' | '<other>'
|
|
69
|
+
attempts: <int>
|
|
70
|
+
retried: true|false
|
|
71
|
+
action_taken: 'fell-back-to-host' | 'skipped' | 'narrowed-scope-and-retried' | 'recorded-and-continued'
|
|
72
|
+
next_step: '<actionable next thing for a future run or user>'
|
|
73
|
+
details: '<short message>'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The skill MUST set `last_status` based on errors:
|
|
77
|
+
- No errors → `ok`
|
|
78
|
+
- Errors but some evidence written → `partial`
|
|
79
|
+
- All paths failed, no evidence → `failed`
|
|
80
|
+
- Preflight failed → `skipped-auth`
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 5. Path-cascade contract (per source)
|
|
85
|
+
|
|
86
|
+
Each `pull-<source>` skill tries paths in order. On each failure, append `errors[]` entry, then try next path. If all paths fail, write evidence file with `❌ all paths failed` marker and the actionable next step.
|
|
87
|
+
|
|
88
|
+
| Source | Path 1 | Path 2 | Path 3 | Path 4 |
|
|
89
|
+
|---|---|---|---|---|
|
|
90
|
+
| email | WorkIQ | `m365_search_emails` / `m365_list_emails` | Graph REST | ask user |
|
|
91
|
+
| teams | WorkIQ | `m365_list_chat_messages` (per chat ID) | Graph REST | ask user |
|
|
92
|
+
| onenote | **WorkIQ-only per workspace policy** (see `workiq-first.instructions.md`) | n/a — Graph for OneNote is unreliable; do not attempt | n/a | ask user to paste page text |
|
|
93
|
+
| sharepoint | WorkIQ | `m365_list_files` / `m365_search_files` | Graph `shares/{id}/driveItem` (for share links) or SharePoint REST `_api/web/GetFileByServerRelativePath` (for direct paths) | ask user |
|
|
94
|
+
| meetings | WorkIQ (transcript) | `m365_get_transcript` | reconstruct from meeting chat (`m365_list_chat_messages`) + Copilot recap message | ask user |
|
|
95
|
+
| crm | WorkIQ | Dataverse REST | n/a | ask user |
|
|
96
|
+
| ado | WorkIQ | ADO REST WIQL | n/a | ask user |
|
|
97
|
+
|
|
98
|
+
For meetings specifically: **transcripts may legitimately not exist** (transcription was off). If WorkIQ + `m365_get_transcript` both return "not found" / "tool error" → **immediately reconstruct from the meeting chat** (`m365_list_chat_messages` for the meeting's chat ID — already captured during teams pull). This is "evidence reconstructed from chat" not "no evidence". Record `transcripts-not-generated` in errors with `action_taken: reconstructed-from-chat`.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 6. Try-again UX
|
|
103
|
+
|
|
104
|
+
When the user says "try again" / "refresh" / "fix the errors" — read `Evidence/run-log.yml errors[]`, retry only the entries where `signature` is in the retryable set (`tool-error`, `transient-server-error`, `graph-401-*` after re-auth). Skip entries where `signature` is in `transcripts-not-generated`, `no-match`, `accessDenied`, `Sorry, I can't` — those need user action (re-record meeting, wait for ADO WI, file access request).
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 7. Never silent
|
|
109
|
+
|
|
110
|
+
If a source is partially broken, the run summary table MUST display the status per source with the next-step:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
| source | status | items | next_step |
|
|
114
|
+
| onenote | partial | 0 | Run `workiq accept-eula` once, then re-run `kushi pull onenote <project>` |
|
|
115
|
+
| meetings | partial | 2 | Transcripts not generated for YYYY-MM-DD meetings; reconstructed from chat. To get transcripts on future meetings, enable Teams transcription before joining. |
|
|
116
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**"
|
|
3
|
+
description: "Conditional az login — only required if CRM/ADO are enabled. M365-only users via WorkIQ never need az login. Failures are soft warnings, not hard stops."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Conditional az login
|
|
7
|
+
|
|
8
|
+
The skill calls `az account get-access-token` only when CRM (Dataverse) or ADO are configured. WorkIQ does not require `az login`. Most M365-only users will never need to run `az login`.
|
|
9
|
+
|
|
10
|
+
## Decision logic
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
crm_enabled = Test-Path <engagement-root>/.project-evidence/crm/config.yml
|
|
14
|
+
ado_enabled = Test-Path <engagement-root>/.project-evidence/ado/config.yml
|
|
15
|
+
|
|
16
|
+
if (NOT crm_enabled AND NOT ado_enabled):
|
|
17
|
+
Skip az check entirely. Display "az sign-in skipped (no CRM/ADO configured)".
|
|
18
|
+
else:
|
|
19
|
+
Try `az account show` (no browser pop-up).
|
|
20
|
+
If signed-in: continue.
|
|
21
|
+
If NOT signed-in: prompt user with command suggestion (NOT auto-launch):
|
|
22
|
+
"az login --tenant 72f988bf-86f1-41af-91ab-2d7cd011db47"
|
|
23
|
+
After they sign in, re-run.
|
|
24
|
+
If signed-in but token request fails (403, etc.): SOFT WARNING.
|
|
25
|
+
- CRM/ADO sources will be skipped this run.
|
|
26
|
+
- All M365 sources via WorkIQ continue normally.
|
|
27
|
+
- Append a `## Auth Notes` section to the run summary listing what was skipped.
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Tenant ID
|
|
31
|
+
|
|
32
|
+
The Microsoft tenant ID for ADO + CRM JIT auth is `72f988bf-86f1-41af-91ab-2d7cd011db47`. If you suggest `az login`, ALWAYS include `--tenant 72f988bf-86f1-41af-91ab-2d7cd011db47`.
|
|
33
|
+
|
|
34
|
+
## Never block on az failures
|
|
35
|
+
|
|
36
|
+
If `az login` fails or token acquisition fails:
|
|
37
|
+
- Display the error.
|
|
38
|
+
- Mark CRM/ADO sources as `❌ skipped (auth)` in the run summary.
|
|
39
|
+
- CONTINUE the rest of the run. Never abort the whole bootstrap/refresh just because CRM is unreachable.
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**"
|
|
3
|
+
description: "Concrete PowerShell patterns for Azure CLI auth, token acquisition, host-specific SharePoint tokens, ADO tenant validation, and WorkIQ retry. Companion to auth-and-retry / az-auth-conditional / workiq-first."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Azure Auth Patterns — Concrete How-To
|
|
7
|
+
|
|
8
|
+
This file is the **concrete** companion to:
|
|
9
|
+
|
|
10
|
+
- `auth-and-retry.instructions.md` — pre-flight, token reuse, retry, error-log schema, path cascade.
|
|
11
|
+
- `az-auth-conditional.instructions.md` — when to require `az login` (only if CRM/ADO are configured).
|
|
12
|
+
- `workiq-first.instructions.md` — WorkIQ is the required path for M365 sources.
|
|
13
|
+
|
|
14
|
+
Where those files describe **what** to do, this one shows **how** in PowerShell. Skill authors implementing `pull-crm`, `pull-ado`, `pull-sharepoint`, and any other external-service caller MUST follow the patterns here. Silent 401 loops and "no evidence found" misreports almost always come from skipping one of these.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Session pre-check (always run first)
|
|
19
|
+
|
|
20
|
+
Before any token acquisition, verify the Azure CLI session and capture the active identity:
|
|
21
|
+
|
|
22
|
+
```powershell
|
|
23
|
+
$accountJson = az account show --output json --only-show-errors 2>&1
|
|
24
|
+
if ($LASTEXITCODE -ne 0) {
|
|
25
|
+
throw "Azure CLI session not found. Run: az login --tenant 72f988bf-86f1-41af-91ab-2d7cd011db47"
|
|
26
|
+
}
|
|
27
|
+
$account = $accountJson | ConvertFrom-Json
|
|
28
|
+
Write-Host "Active tenant: $($account.tenantId) | Account: $($account.user.name)"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Per `az-auth-conditional`: if neither CRM nor ADO is enabled for this engagement, **skip this entire section** and proceed directly to WorkIQ. M365-only users never need `az login`.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 2. Token acquisition — per service
|
|
36
|
+
|
|
37
|
+
Acquire each token **once per run** (per `auth-and-retry.instructions.md §2`). The tenant ID for Microsoft is `72f988bf-86f1-41af-91ab-2d7cd011db47` — pull it from config when possible, fall back to this literal only for the `az login --tenant` suggestion text.
|
|
38
|
+
|
|
39
|
+
### 2.1 CRM / Dataverse
|
|
40
|
+
|
|
41
|
+
```powershell
|
|
42
|
+
$crmConfig = Get-Content "$engagementRoot\.project-evidence\crm\config.yml" -Raw | ConvertFrom-Yaml # or JSON variant
|
|
43
|
+
$tenant = $crmConfig.tenantId
|
|
44
|
+
$crmBaseUrl = $crmConfig.baseUrl
|
|
45
|
+
$crmToken = (az account get-access-token --tenant $tenant --resource $crmBaseUrl --query accessToken -o tsv --only-show-errors 2>&1).Trim()
|
|
46
|
+
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($crmToken)) {
|
|
47
|
+
throw "Failed to get CRM token (exit $LASTEXITCODE). Run: az login --tenant $tenant"
|
|
48
|
+
}
|
|
49
|
+
$crmHeaders = @{
|
|
50
|
+
Authorization = "Bearer $crmToken"
|
|
51
|
+
Accept = 'application/json'
|
|
52
|
+
'OData-Version' = '4.0'
|
|
53
|
+
'OData-MaxVersion' = '4.0'
|
|
54
|
+
Prefer = 'odata.include-annotations="OData.Community.Display.V1.FormattedValue"'
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2.2 Azure DevOps
|
|
59
|
+
|
|
60
|
+
ADO's resource is the constant `499b84ac-1321-427f-aa17-267ca6975798` (Visual Studio Team Services). Read it from config — never hardcode:
|
|
61
|
+
|
|
62
|
+
```powershell
|
|
63
|
+
$adoConfig = Get-Content "$engagementRoot\.project-evidence\ado\config.yml" -Raw | ConvertFrom-Yaml
|
|
64
|
+
$tenant = $adoConfig.tenantId
|
|
65
|
+
$adoRes = $adoConfig.resource # 499b84ac-1321-427f-aa17-267ca6975798
|
|
66
|
+
$adoToken = (az account get-access-token --tenant $tenant --resource $adoRes --query accessToken -o tsv --only-show-errors 2>&1).Trim()
|
|
67
|
+
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($adoToken)) {
|
|
68
|
+
throw "Failed to get ADO token (exit $LASTEXITCODE). Run: az login --tenant $tenant"
|
|
69
|
+
}
|
|
70
|
+
$adoHeaders = @{ Authorization = "Bearer $adoToken"; Accept = 'application/json'; 'Content-Type' = 'application/json' }
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2.3 SharePoint / OneDrive — **host-specific** (critical)
|
|
74
|
+
|
|
75
|
+
SharePoint tokens are bound to a hostname. **Do not assume the configured default host applies to every link.** Personal sites, customer tenants, and `*-df` preview hosts all need their own tokens.
|
|
76
|
+
|
|
77
|
+
Derive the resource from the target URL's origin and acquire the token for that exact host:
|
|
78
|
+
|
|
79
|
+
```powershell
|
|
80
|
+
$targetUrl = '<sharepoint-or-onedrive-url>'
|
|
81
|
+
$targetUri = [Uri]$targetUrl
|
|
82
|
+
$spResource = "$($targetUri.Scheme)://$($targetUri.Host)"
|
|
83
|
+
$spToken = (az account get-access-token --tenant $tenant --resource $spResource --query accessToken -o tsv --only-show-errors 2>&1).Trim()
|
|
84
|
+
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($spToken)) {
|
|
85
|
+
throw "Failed to get SharePoint token for host $spResource (exit $LASTEXITCODE). Run: az login --tenant $tenant"
|
|
86
|
+
}
|
|
87
|
+
$spHeaders = @{ Authorization = "Bearer $spToken"; Accept = 'application/json' }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Hosts commonly seen in this workspace: `microsoft-my.sharepoint.com`, `microsoft-my.sharepoint-df.com`, `microsofteur-my.sharepoint.com`, customer tenant `*.sharepoint.com`.
|
|
91
|
+
|
|
92
|
+
#### SharePoint URL traps — browser vs. canonical API
|
|
93
|
+
|
|
94
|
+
A raw browser-facing SharePoint URL is often the **wrong endpoint** for bearer-token retrieval:
|
|
95
|
+
|
|
96
|
+
- `/_layouts/15/Doc.aspx?...`, `?web=1`, and Office share links (`/:x:/`, `/:p:/`, `/:w:/`, `/:b:/`) are browser shells or share URLs — **not** the canonical file-read API.
|
|
97
|
+
- A raw `401` from those URLs does NOT prove Azure CLI auth is broken.
|
|
98
|
+
- **For share links** → prefer Microsoft Graph `shares/{shareId}/driveItem` with `Prefer: redeemSharingLink`.
|
|
99
|
+
- **For direct file paths** under `/personal/.../Documents/...` → prefer SharePoint REST `_api/web/GetFileByServerRelativePath(...)` on the exact host.
|
|
100
|
+
- If the canonical API then returns `403 accessDenied` or SharePoint `UnauthorizedAccessException`, treat it as a **real permission / sharing problem** — do not chase it as a token-host mismatch. Record `errors[]` with `signature: blocked-host-or-permissions-401` per `auth-and-retry.instructions.md §4`.
|
|
101
|
+
|
|
102
|
+
### 2.4 Microsoft Graph / OneNote
|
|
103
|
+
|
|
104
|
+
**Do not use Microsoft Graph for OneNote in this workspace.** Per `workiq-first.instructions.md`, OneNote is **WorkIQ-only**. Graph's OneNote endpoint repeatedly fails with 401/consent issues that don't represent real permission problems.
|
|
105
|
+
|
|
106
|
+
```powershell
|
|
107
|
+
if (-not (Get-Command workiq -ErrorAction SilentlyContinue)) {
|
|
108
|
+
throw "workiq CLI not found on PATH. OneNote queries cannot proceed. See: https://gim-home.github.io/kushi/getting-started/install-workiq/"
|
|
109
|
+
}
|
|
110
|
+
# Prefer narrowest WorkIQ query: one_sectionFileId > one_sectionName > project-alias keyword search.
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
For other Graph endpoints (Mail, Teams, Calendar) Kushi defers to the host's `m365_*` proxies; see `auth-and-retry.instructions.md §5` for the path cascade.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 3. ADO tenant validation (before any ADO call)
|
|
118
|
+
|
|
119
|
+
Allowed tenants are config-driven, not hardcoded. Validate before the first ADO call:
|
|
120
|
+
|
|
121
|
+
```powershell
|
|
122
|
+
# $account from Section 1
|
|
123
|
+
$adoConfig = Get-Content "$engagementRoot\.project-evidence\ado\config.yml" -Raw | ConvertFrom-Yaml
|
|
124
|
+
$allowedTenants = @($adoConfig.allowedTenantIds)
|
|
125
|
+
$currentTenant = $account.tenantId
|
|
126
|
+
if ($allowedTenants.Count -gt 0 -and $allowedTenants -notcontains $currentTenant) {
|
|
127
|
+
throw "Current tenant '$currentTenant' is not allowed for ADO. Allowed: $($allowedTenants -join ', '). Run: az login --tenant $($allowedTenants[0])"
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
If validation fails, record `errors[]` with `signature: ado-tenant-mismatch` and mark the ADO source `❌ skipped (auth)` per `auth-and-retry §4` — do not abort the whole run.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 4. Mandatory $LASTEXITCODE pattern
|
|
136
|
+
|
|
137
|
+
Check `$LASTEXITCODE` immediately after every `az` invocation. Never proceed with a null or empty result. Always pipe `2>&1` so stderr is captured into error messages, and use `--only-show-errors` on token calls to keep informational banners out of the token value:
|
|
138
|
+
|
|
139
|
+
```powershell
|
|
140
|
+
$result = az <command> --output json --only-show-errors 2>&1
|
|
141
|
+
if ($LASTEXITCODE -ne 0) {
|
|
142
|
+
throw "az command failed (exit $LASTEXITCODE): $result"
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 5. WorkIQ availability + retry (concrete)
|
|
149
|
+
|
|
150
|
+
Per `workiq-first.instructions.md` and `auth-and-retry.instructions.md §3`. Before the first WorkIQ call per run:
|
|
151
|
+
|
|
152
|
+
```powershell
|
|
153
|
+
if (-not (Get-Command workiq -ErrorAction SilentlyContinue)) {
|
|
154
|
+
throw "workiq CLI not found on PATH. WorkIQ queries cannot proceed. See install-workiq.md."
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Retry loop (transient errors only — throttling is NOT a retry case):
|
|
159
|
+
|
|
160
|
+
```powershell
|
|
161
|
+
$maxRetries = 2
|
|
162
|
+
$attempt = 0
|
|
163
|
+
$output = $null
|
|
164
|
+
do {
|
|
165
|
+
$attempt++
|
|
166
|
+
$output = (workiq ask -q $query 2>&1 | Out-String).Trim()
|
|
167
|
+
$isTransient = ($LASTEXITCODE -ne 0) -or ($output -match '^(Error:\s+Unexpected error|Server error)')
|
|
168
|
+
$isThrottled = ($output -match 'tooManyRequests|More than 3 retries performed|high demand')
|
|
169
|
+
if ($isThrottled) {
|
|
170
|
+
# Do NOT retry the same query. Narrow once if possible, else record and continue.
|
|
171
|
+
throw "WorkIQ throttled — record 'throttled-tooManyRequests' and move on per auth-and-retry §3."
|
|
172
|
+
}
|
|
173
|
+
if (-not $isTransient) { break }
|
|
174
|
+
if ($attempt -gt $maxRetries) {
|
|
175
|
+
throw "WorkIQ failed after $attempt attempt(s): $output"
|
|
176
|
+
}
|
|
177
|
+
Start-Sleep -Seconds (3 * $attempt)
|
|
178
|
+
} while ($true)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 6. Token reuse + mid-run 401 recovery
|
|
184
|
+
|
|
185
|
+
Per `auth-and-retry §2`: acquire each token once, reuse it. On HTTP 401 mid-run, **re-acquire once** and retry the failed call. If still 401, record `errors[]` with `signature: token-401-after-reacquire` and stop calling that service.
|
|
186
|
+
|
|
187
|
+
```powershell
|
|
188
|
+
# Mid-run 401 — re-acquire then retry once
|
|
189
|
+
$crmToken = (az account get-access-token --tenant $tenant --resource $crmBaseUrl --query accessToken -o tsv --only-show-errors 2>&1).Trim()
|
|
190
|
+
$crmHeaders.Authorization = "Bearer $crmToken"
|
|
191
|
+
# retry the failed call ONCE
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 7. Failure signature reference
|
|
197
|
+
|
|
198
|
+
Maintained alongside `auth-and-retry §3` (canonical) — quick lookup:
|
|
199
|
+
|
|
200
|
+
| Symptom | Likely cause | Recovery |
|
|
201
|
+
|---------|--------------|----------|
|
|
202
|
+
| `az account show` non-zero | Not logged in | `az login --tenant 72f988bf-86f1-41af-91ab-2d7cd011db47` |
|
|
203
|
+
| Token empty / whitespace | Expired session or parse issue | Re-login, re-acquire |
|
|
204
|
+
| HTTP 401 on API call | Token expired mid-run | Re-acquire once (§6), retry once |
|
|
205
|
+
| HTTP 401 on SharePoint URL | Token for wrong host | Re-acquire for exact URL origin (§2.3); if still 401, record `blocked-host-or-permissions-401` |
|
|
206
|
+
| Browser SP URL 401, but Graph shares / SP REST 403 | Wrong retrieval surface; canonical API exposed real permission failure | Stop retrying browser URL; classify as share/permission problem |
|
|
207
|
+
| OneNote via Graph 401/40001 | Wrong path | Switch to WorkIQ, narrowest query first (`one_sectionFileId`) |
|
|
208
|
+
| ADO tenant mismatch | Wrong-tenant session | `az login --tenant <allowed>` (§3) |
|
|
209
|
+
| Dataverse 400 OData | Invalid field / filter | Revert to known-good query; add filters one at a time |
|
|
210
|
+
| WorkIQ exits non-zero (transient) | Server hiccup | Retry pattern (§5), max 2 retries |
|
|
211
|
+
| WorkIQ `tooManyRequests` | Throttling | Narrow scope once; else record `throttled-tooManyRequests` and stop |
|
|
212
|
+
| WorkIQ "Sorry, I can't" | Blocked/copyright | Record `skipped-blocked`, continue |
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 8. Config source of truth
|
|
217
|
+
|
|
218
|
+
| Service | Config file (engagement-scoped) |
|
|
219
|
+
|---------|---------------------------------|
|
|
220
|
+
| Dynamics 365 / CRM | `<engagement-root>/.project-evidence/crm/config.yml` |
|
|
221
|
+
| Azure DevOps | `<engagement-root>/.project-evidence/ado/config.yml` |
|
|
222
|
+
| Microsoft Graph / M365 / OneNote | `<engagement-root>/.project-evidence/m365/m365-mutable.json` + `<engagement-root>/.project-evidence/m365/m365-auth.json` |
|
|
223
|
+
| WorkIQ CLI path | `~/.copilot/project-evidence.yml` (user-scoped) |
|
|
224
|
+
| Global integrations (optional) | `~/.copilot/integrations/{crm,ado}/` |
|
|
225
|
+
|
|
226
|
+
Always read tenant IDs, resource URLs, org URLs, and allowed-tenant lists from these files at the start of each run. **Never hardcode them in skills or prompts.** The only acceptable literal in instructions/prompts is the public Microsoft tenant ID `72f988bf-86f1-41af-91ab-2d7cd011db47` in the user-facing `az login --tenant ...` suggestion text.
|