kushi-agents 4.4.0 → 4.4.3

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 (45) hide show
  1. package/bin/cli.mjs +2 -4
  2. package/package.json +1 -1
  3. package/plugin/agents/kushi.agent.md +5 -1
  4. package/plugin/instructions/ado-engagement-tree.instructions.md +1 -1
  5. package/plugin/instructions/az-auth-conditional.instructions.md +2 -2
  6. package/plugin/instructions/azure-auth-patterns.instructions.md +6 -6
  7. package/plugin/instructions/bootstrap-status-format.instructions.md +15 -2
  8. package/plugin/instructions/cleanup-on-resolution.instructions.md +1 -1
  9. package/plugin/instructions/communication-guidelines.instructions.md +79 -0
  10. package/plugin/instructions/crm-bootstrap-discovery.instructions.md +1 -1
  11. package/plugin/instructions/deferred-retry-on-workiq-fail.instructions.md +155 -0
  12. package/plugin/instructions/engagement-root-resolution.instructions.md +13 -10
  13. package/plugin/instructions/evidence-layout-canonical.instructions.md +2 -2
  14. package/plugin/instructions/fallback-status-reporting.instructions.md +104 -0
  15. package/plugin/instructions/fde-grounding.instructions.md +146 -0
  16. package/plugin/instructions/identity-resolution.instructions.md +15 -8
  17. package/plugin/instructions/m365-id-registry.instructions.md +1 -1
  18. package/plugin/instructions/scope-boundaries.instructions.md +4 -4
  19. package/plugin/instructions/status-taxonomy.instructions.md +118 -0
  20. package/plugin/instructions/workiq-only.instructions.md +4 -2
  21. package/plugin/lib/Get-KushiConfig.ps1 +112 -7
  22. package/plugin/prompts/bootstrap.prompt.md +64 -49
  23. package/plugin/skills/apply-ado-update/SKILL.md +2 -2
  24. package/plugin/skills/bootstrap-project/SKILL.md +10 -10
  25. package/plugin/skills/propose-ado-update/SKILL.md +3 -3
  26. package/plugin/skills/pull-ado/SKILL.md +27 -13
  27. package/plugin/skills/pull-crm/SKILL.md +20 -9
  28. package/plugin/skills/pull-email/SKILL.md +2 -2
  29. package/plugin/skills/pull-meetings/SKILL.md +1 -1
  30. package/plugin/skills/pull-onenote/scripts/recapture-section-url.mjs +2 -1
  31. package/plugin/skills/pull-onenote/write-snapshot.mjs +2 -1
  32. package/plugin/skills/pull-sharepoint/SKILL.md +1 -1
  33. package/plugin/skills/pull-teams/SKILL.md +1 -1
  34. package/plugin/skills/refresh-project/SKILL.md +21 -1
  35. package/plugin/skills/self-check/run.ps1 +24 -1
  36. package/plugin/templates/ado-update/integrations-ado-writes.example.yml +1 -1
  37. package/plugin/templates/init/integrations.template.yml +32 -19
  38. package/plugin/templates/init/m365-auth.template.json +5 -5
  39. package/plugin/templates/init/project-integrations.template.yml +2 -2
  40. package/plugin/templates/snapshot/onenote-page.template.md +3 -3
  41. package/src/config-loader.mjs +92 -5
  42. package/src/main.mjs +2 -30
  43. package/plugin/templates/init/ado-config.template.yml +0 -21
  44. package/plugin/templates/init/crm-config.template.yml +0 -16
  45. package/src/prompt.mjs +0 -42
@@ -1,49 +1,64 @@
1
- ---
2
- name: bootstrap
3
- description: "First-time setup for a new project — scaffold folders, lay configs side-by-side, do an initial 30-day pull, build State."
4
- argument-hint: "Project name (fuzzy-matched under engagement-root); optional initial pull window"
5
- agent: kushi
6
- tools: [search, read/readFile, edit, agent, execute/runInTerminal, execute/getTerminalOutput, 'workiq/*']
7
- ---
8
-
9
- ## User Input
10
-
11
- ```text
12
- ${input:project:Project name, e.g. HCA}
13
- ${input:window:Optional initial pull window — last N days, since YYYY-MM-DD, or YYYY-MM-DD..YYYY-MM-DD (default 30 days)}
14
- ```
15
-
16
- # /bootstrap
17
-
18
- Route to `@Kushi bootstrap <project>`.
19
-
20
- Inputs the agent will resolve:
21
- - `<project>` — the engagement folder name (fuzzy-match under engagement-root if needed).
22
- - `<window>` — defaults to 30 days. Override with `last 60 days`, `since 2026-04-01`, or `2026-04-01..2026-05-01`.
23
-
24
- Reads:
25
- - `<workspace>/.kushi/config/user/project-evidence.yml` for alias + engagement-root.
26
- - `<workspace>/.kushi/config/user/m365-auth.json` for tenant + default notebook + mailbox-folder hints.
27
-
28
- **Step 0 — verify per-user config is filled.** Before any pull, call:
29
-
30
- ```powershell
31
- & "<install-dest>/lib/Get-KushiConfig.ps1" -Name 'project-evidence' -AllowPlaceholders | Out-Null # tolerates <auto>; identity-resolution fills it
32
- & "<install-dest>/lib/Get-KushiConfig.ps1" -Name 'm365-auth' # hard-fails on placeholders
33
- ```
34
-
35
- If `Get-KushiConfig -Name 'm365-auth'` throws (file missing or still `__FILL_ME_IN__`), STOP and prompt the user:
36
-
37
- > `<workspace>/.kushi/config/user/m365-auth.json` still has template placeholders. Open it and fill in your tenant ID, default OneNote notebook ID, mailbox folder names, and SharePoint root before re-running `bootstrap`.
38
-
39
- Continue only when both helper calls succeed.
40
-
41
- Produces:
42
- - `<engagement-root>/.project-evidence/m365/{m365-auth,m365-mutable}.json` (per-user filled)
43
- - `<engagement-root>/<project>/integrations.yml`
44
- - `<engagement-root>/<project>/Evidence/{contributors.yml, run-log.yml, <alias>/.settings.yml, <alias>/{email,teams,meetings,onenote,sharepoint,crm,ado}/{snapshot,stream}/}`
45
- - `<engagement-root>/<project>/State/{00..09}_*.md`
46
-
47
- Delegates to `bootstrap-project` skill. After scaffolding, calls each `pull-<source>` skill, then `build-state`.
48
-
49
- Tracking: see `tracking.instructions.md`. Write `<workspace>/.kushi/tracking/runs/{{YYYY-MM-DD}}-<project>-bootstrap.md` as the final step.
1
+ ---
2
+ name: bootstrap
3
+ description: "First-time setup for a new project — scaffold folders, lay configs side-by-side, do an initial 30-day pull, build State."
4
+ argument-hint: "Project name (fuzzy-matched under engagement-root); optional initial pull window"
5
+ agent: kushi
6
+ tools: [search, read/readFile, edit, agent, execute/runInTerminal, execute/getTerminalOutput, 'workiq/*']
7
+ ---
8
+
9
+ ## User Input
10
+
11
+ ```text
12
+ ${input:project:Project name, e.g. HCA}
13
+ ${input:window:Optional initial pull window — last N days, since YYYY-MM-DD, or YYYY-MM-DD..YYYY-MM-DD (default 30 days)}
14
+ ```
15
+
16
+ # /bootstrap
17
+
18
+ Route to `@Kushi bootstrap <project>`.
19
+
20
+ Inputs the agent will resolve:
21
+ - `<project>` — the engagement folder name (fuzzy-match under engagement-root if needed).
22
+ - `<window>` — defaults to 30 days. Override with `last 60 days`, `since 2026-04-01`, or `2026-04-01..2026-05-01`.
23
+
24
+ Reads:
25
+ - `<workspace>/.kushi/config/user/project-evidence.yml` for alias + engagement-root.
26
+ - `<workspace>/.kushi/config/user/m365-auth.json` for tenant + default notebook + mailbox-folder hints.
27
+ - `<workspace>/.kushi/config/shared/integrations.yml` for ADO/CRM org-level connections (shared per project).
28
+
29
+ **Step 0 — verify per-user config is filled** (the minimum the bootstrap skill needs before it can discover the rest). Before any pull, call:
30
+
31
+ ```powershell
32
+ & "<install-dest>/lib/Get-KushiConfig.ps1" -Name 'project-evidence' -AllowPlaceholders | Out-Null # tolerates <auto>; identity-resolution fills it
33
+ & "<install-dest>/lib/Get-KushiConfig.ps1" -Name 'm365-auth' # hard-fails on placeholders in required fields
34
+ ```
35
+
36
+ `Get-KushiConfig -Name 'm365-auth'` (kushi v4.4.1+) checks both: (a) no `__FILL_ME_IN__` / sentinel strings anywhere, AND (b) the required-field set is populated:
37
+ `defaultTenantId`, `oneNote.defaultNotebookName`, `oneNote.defaultNotebookId`, `oneNote.defaultLinkOwner`, `emailContext.folders[]` (≥ 1 entry), `sharePointContext.localProjectsRoot`.
38
+
39
+ If it throws, STOP and prompt the user with the exact missing-field list:
40
+
41
+ > ⚠ `<workspace>/.kushi/config/user/m365-auth.json` is missing required fields: `<list from the helper>`. Open it, fill in those values, then re-run `bootstrap`. Everything else (section IDs, calendar series, specific mailbox folder discovery, channel IDs, SharePoint site IDs, CRM `crmRecordId`, ADO work-item IDs) is the bootstrap skill's job to **discover via WorkIQ + per-source probes** — do NOT pre-fill those.
42
+
43
+ Continue only when both helper calls succeed (`project-evidence.yml` is allowed to still have `<auto>` for identity; identity-resolution.instructions.md fills it in Step 0 of the bootstrap skill).
44
+
45
+ **What the bootstrap SKILL discovers (you do NOT need to pre-fill any of this):**
46
+ - Identity (`alias`, `email`, `display_name`) — resolved via WorkIQ if `<auto>`.
47
+ - OneNote section identifiers (`one_sectionFileId`, `one_sectionGroupId`, `one_sectionOneNoteGuid`, `one_notebookSourceDoc`) discovered from the user-provided section name.
48
+ - Calendar series, meeting `joinUrl`s, Teams chat/channel hints, SharePoint site/web/list IDs — discovered per source.
49
+ - CRM account `crmRecordId` discovered via the 4-step Dataverse REST sequence.
50
+ - ADO area path / iteration / work-item IDs — discovered via ADO REST.
51
+
52
+ On WorkIQ failure during discovery, per `deferred-retry-on-workiq-fail.instructions.md`: the bootstrap skill writes a marker, surfaces it in the run report, and continues. **It does NOT call `m365_get_*` / Graph as a fallback.** The next `refresh` drains the queue.
53
+
54
+ Produces:
55
+ - `<workspace>/.kushi/config/user/{m365-auth,m365-mutable}.json` (per-user, hand-edited above + auto-populated by Step 4a)
56
+ - `<workspace>/.kushi/config/shared/integrations.yml` (shared, ADO/CRM connection blocks; per-project `boundaries:` lives in the project file below)
57
+ - `<engagement-root>/<project>/integrations.yml` (per-project boundaries + enabled-sources, shared via OneDrive)
58
+ - `<engagement-root>/<project>/Evidence/{contributors.yml, run-log.yml, <alias>/.settings.yml, <alias>/{email,teams,meetings,onenote,sharepoint,crm,ado}/{snapshot,stream}/, <alias>/_deferred-retries/}`
59
+ - `<engagement-root>/<project>/State/{00..09}_*.md` (full profile only)
60
+ - `<engagement-root>/<project>/bootstrap-status.md` (shared, multi-user safe)
61
+
62
+ Delegates to `bootstrap-project` skill. After scaffolding, calls each `pull-<source>` skill, then `build-state` (full profile only).
63
+
64
+ Tracking: see `tracking.instructions.md`. Write `<workspace>/.kushi/tracking/runs/{{YYYY-MM-DD}}-<project>-bootstrap.md` as the final step.
@@ -30,14 +30,14 @@ Belongs to the **`preview`** profile. Opt in via `npx kushi-agents --clawpilot -
30
30
 
31
31
  ## Deterministic config — do not invent paths
32
32
 
33
- Same as `propose-ado-update`. Reads ADO connection from `<engagement-root>/.project-evidence/ado/config.yml`, per-project `engagement_id` and `writes:` block from `<engagement-root>/<project>/integrations.yml ado:`. Never asks for paths the bootstrap layer already resolved.
33
+ Same as `propose-ado-update`. Reads ADO connection from `<workspace>/.kushi/config/shared/integrations.yml`, per-project `engagement_id` and `writes:` block from `<engagement-root>/<project>/integrations.yml ado:`. Never asks for paths the bootstrap layer already resolved.
34
34
 
35
35
  ## Pre-flight (HARD — do not bypass)
36
36
 
37
37
  1. **Resolve engagement root + project** per `engagement-root-resolution.instructions.md`.
38
38
  2. Confirm `<project>/integrations.yml ado.engagement_id > 0`. If 0 → abort with the same message `propose-ado-update` uses ("ADO Initiative not yet linked").
39
39
  3. Confirm `<project>/ado-updates/<YYYY-MM-DD>/proposed.md` exists. If not → tell user to run `@Kushi propose ado <project>` first. Never fabricate a proposal.
40
- 4. Per `azure-auth-patterns.instructions.md` — Section 1 (session pre-check) + Section 3 (ADO tenant validation, using `<engagement-root>/.project-evidence/ado/config.yml`) **must** complete green before any further step.
40
+ 4. Per `azure-auth-patterns.instructions.md` — Section 1 (session pre-check) + Section 3 (ADO tenant validation, using `<workspace>/.kushi/config/shared/integrations.yml`) **must** complete green before any further step.
41
41
 
42
42
  ## Steps (current preview-stub behavior)
43
43
 
@@ -66,7 +66,7 @@ Verify in order. Stop on hard failures.
66
66
  - `$env:LOCALAPPDATA\Programs\WorkIQ\workiq.cmd`
67
67
  - `$env:ProgramFiles\WorkIQ\workiq.cmd`
68
68
  If found, persist path. If not found, ask user for path (or to install). Test with `<workiq.cli_path> --help`. Without WorkIQ, M365 sources will all fail — STOP.
69
- 3. **Conditional az login** — only if `<engagement-root>/.project-evidence/crm/config.yml` OR `<engagement-root>/.project-evidence/ado/config.yml` exists. Per `az-auth-conditional.instructions.md`. Soft warning on failure, never blocking.
69
+ 3. **Conditional az login** — only if `<workspace>/.kushi/config/shared/integrations.yml` OR `<workspace>/.kushi/config/shared/integrations.yml` exists. Per `az-auth-conditional.instructions.md`. Soft warning on failure, never blocking.
70
70
  4. **Engagement-root resolution** — per `engagement-root-resolution.instructions.md`. Persist to `<workspace>/.kushi/config/user/project-evidence.yml engagement_root` if newly resolved.
71
71
 
72
72
  Display SETUP summary table with ✅ / ⚙️ / ❌ / ⚠️ / ➖ markers.
@@ -81,18 +81,18 @@ Required live files:
81
81
  |---|---|
82
82
  | `<workspace>/.kushi/config/user/project-evidence.yml` | `templates/init/project-evidence.template.yml` (seeded by installer) |
83
83
  | `<workspace>/.kushi/config/shared/integrations.yml` | `templates/init/integrations.template.yml` (seeded by installer) |
84
- | `<engagement-root>/.project-evidence/m365/m365-auth.json` | `templates/init/m365-auth.template.json` |
85
- | `<engagement-root>/.project-evidence/m365/m365-mutable.json` | `templates/init/m365-mutable.template.json` |
84
+ | `<workspace>/.kushi/config/user/m365-auth.json` | `templates/init/m365-auth.template.json` |
85
+ | `<workspace>/.kushi/config/user/m365-mutable.json` | `templates/init/m365-mutable.template.json` |
86
86
  | `<engagement-root>/<project>/integrations.yml` | `templates/init/project-integrations.template.yml` |
87
87
  | `<engagement-root>/<project>/Evidence/contributors.yml` | `templates/init/project-contributors.template.yml` |
88
88
  | `<engagement-root>/<project>/Evidence/<alias>/.settings.yml` | `templates/init/project-user-settings.template.yml` |
89
89
 
90
- Optional (only if user enables CRM/ADO):
90
+ Optional (only if user enables CRM/ADO — both go into `<workspace>/.kushi/config/shared/integrations.yml` `crm:` and `ado:` blocks, NOT separate files):
91
91
 
92
- | Live file | Template source |
92
+ | Live destination | Source |
93
93
  |---|---|
94
- | `<engagement-root>/.project-evidence/crm/config.yml` | `templates/init/crm-config.template.yml` |
95
- | `<engagement-root>/.project-evidence/ado/config.yml` | `templates/init/ado-config.template.yml` |
94
+ | `<workspace>/.kushi/config/shared/integrations.yml` `crm:` block | already seeded by `templates/init/integrations.template.yml` — fill in `crm.environmentUrl` + `crm.tenantId` |
95
+ | `<workspace>/.kushi/config/shared/integrations.yml` `ado:` block | already seeded by `templates/init/integrations.template.yml` — fill in `ado.organization` + `ado.project` |
96
96
 
97
97
  **Boundaries upgrade for existing projects (v3.7.0+):** If `<engagement-root>/<project>/integrations.yml` already exists but has no top-level `boundaries:` key, append the scaffolded `boundaries:` block from `templates/init/project-integrations.template.yml` (preserving every existing key). Then proceed to Step 4 to populate it.
98
98
 
@@ -121,15 +121,15 @@ The `State/` subtree is created **only on `full` profile**. On `standard`, only
121
121
 
122
122
  **Boundaries gate** (kushi v3.7.0+, per `scope-boundaries.instructions.md`): before dispatching any `pull-*`, read `<engagement-root>/<project>/integrations.yml#boundaries` and verify each enabled source has its required boundary key populated. For sources where bootstrap can auto-populate from existing `m365-mutable.json` discovery hints (e.g. a previously-discovered `section_file_id` lands in `boundaries.onenote.section_file_ids`), do so and continue. For sources where the boundary cannot be auto-populated, write the source as **disabled** in `integrations.yml` and add a one-liner to `<project>/OPEN-QUESTIONS-DRAFT.md` (or `State/09_open-questions.md` on `full` profile) asking the user to fill the boundary and re-enable.
123
123
 
124
- For CRM and ADO additionally verify the global config files exist (`<engagement-root>/.project-evidence/{crm,ado}/config.yml`) with non-placeholder values; if missing, scaffold from `templates/init/{crm,ado}-config.template.yml` and park in Open Questions with the path and template reference. **Do NOT auto-improvise** by inferring a tenant/org or by narrating CRM evidence from email — both are explicit anti-patterns in v3.7.0.
124
+ For CRM and ADO additionally verify the shared connection block exists in `<workspace>/.kushi/config/shared/integrations.yml` (`crm:` block with `environmentUrl` + `tenantId`, OR `ado:` block with `organization` + `project`) with non-placeholder values; if missing, prompt the user to fill those two/four fields directly (no separate template files — they live in `templates/init/integrations.template.yml`) and park in Open Questions with the path. **Do NOT auto-improvise** by inferring a tenant/org or by narrating CRM evidence from email — both are explicit anti-patterns in v3.7.0.
125
125
 
126
- **CRM discovery is REQUIRED before declaring `disabled` (kushi v3.11.0+, per `crm-bootstrap-discovery.instructions.md`).** If `<engagement-root>/.project-evidence/crm/config.yml` exists and `az` auth succeeds, bootstrap MUST run the full 4-step Dataverse REST resolution sequence from `pull-crm/SKILL.md#resolution-order-when-crmrecordid-is-unset` (title-first → all matching accounts → wide-text → recent-slice → ask user) against the live endpoint before writing `boundaries.crm.disabled: true`. Any other path that sets `disabled: true` is a defect. If steps 1–4 all return 0, present the top 5 candidates from step 4 to the user before final disposition. Log the full attempt trail (queries + counts + outcome) to the bootstrap refresh-report under `## CRM resolution attempts`. If auth fails or Dataverse is unreachable, leave the boundary empty with `reason: 'crm-auth-unavailable-<date>'` — NOT `disabled: true` — so the next refresh retries.
126
+ **CRM discovery is REQUIRED before declaring `disabled` (kushi v3.11.0+, per `crm-bootstrap-discovery.instructions.md`).** If `<workspace>/.kushi/config/shared/integrations.yml` exists and `az` auth succeeds, bootstrap MUST run the full 4-step Dataverse REST resolution sequence from `pull-crm/SKILL.md#resolution-order-when-crmrecordid-is-unset` (title-first → all matching accounts → wide-text → recent-slice → ask user) against the live endpoint before writing `boundaries.crm.disabled: true`. Any other path that sets `disabled: true` is a defect. If steps 1–4 all return 0, present the top 5 candidates from step 4 to the user before final disposition. Log the full attempt trail (queries + counts + outcome) to the bootstrap refresh-report under `## CRM resolution attempts`. If auth fails or Dataverse is unreachable, leave the boundary empty with `reason: 'crm-auth-unavailable-<date>'` — NOT `disabled: true` — so the next refresh retries.
127
127
 
128
128
  #### Step 4a — Discovery & registry persistence (REQUIRED, kushi v3.7.8+, per `m365-id-registry.instructions.md`)
129
129
 
130
130
  **Doctrine: bootstrap discovers, refresh consumes.** Bootstrap is the ONLY phase that probes WorkIQ to resolve canonical M365 identifiers. Refresh runs MUST read these from `m365-mutable.json#knownSections.<project>` and pass them into the index extractor verbatim. Refresh must NEVER re-discover — that is the source of "OneNote works for me sometimes" non-determinism.
131
131
 
132
- For each enabled source, resolve and persist the canonical lookup keys into `<engagement-root>/.project-evidence/m365/m365-mutable.json#knownSections.<projectKey>`. The schema is fixed — populate every key the source supports:
132
+ For each enabled source, resolve and persist the canonical lookup keys into `<workspace>/.kushi/config/user/m365-mutable.json#knownSections.<projectKey>`. The schema is fixed — populate every key the source supports:
133
133
 
134
134
  ```jsonc
135
135
  "knownSections": {
@@ -21,7 +21,7 @@ This skill does **NOT** create a new top-level config file. It reads from the co
21
21
 
22
22
  | What | Where | Maintained by |
23
23
  |---|---|---|
24
- | ADO tenant + org + apiVersion + auth strategy | `<engagement-root>/.project-evidence/ado/config.yml` | `bootstrap-project` (global, one-time) |
24
+ | ADO tenant + org + apiVersion + auth strategy | `<workspace>/.kushi/config/shared/integrations.yml` | `bootstrap-project` (global, one-time) |
25
25
  | Per-project Initiative ID (`engagement_id`), area, title filter, discovery hints | `<engagement-root>/<project>/integrations.yml` under `ado:` | `bootstrap-project` + `pull-ado` (auto-discovers and persists) |
26
26
  | Per-project **writes block** (allowlist, autoApply per field, strategy, fieldRefName) | `<engagement-root>/<project>/integrations.yml` under `ado.writes:` | First run of this skill scaffolds the block from the template, then user edits |
27
27
 
@@ -41,7 +41,7 @@ Refuse to produce `proposed.md` and tell the user exactly what's missing if any
41
41
  |---|---|
42
42
  | `<project>/integrations.yml` exists | "Project not bootstrapped. Run `@Kushi bootstrap <project>` first." |
43
43
  | `ado.engagement_id` > 0 | "ADO Initiative not yet linked for `<project>` (engagement_id is 0). pull-ado will auto-discover on next refresh; once `engagement_id` is set in `integrations.yml`, retry." |
44
- | `<engagement-root>/.project-evidence/ado/config.yml` exists with non-placeholder `organization` | "Global ADO config missing or has placeholder org. Run `@Kushi bootstrap <project>` to scaffold, then fill `.project-evidence/ado/config.yml`." |
44
+ | `<workspace>/.kushi/config/shared/integrations.yml` exists with non-placeholder `organization` | "Global ADO config missing or has placeholder org. Run `@Kushi bootstrap <project>` to scaffold, then fill `.kushi/config/shared/integrations.yml`." |
45
45
  | At least one `Evidence/_Consolidated/<date>_consolidated.md` exists | "No consolidated evidence yet for `<project>`. Run `@Kushi refresh <project>` then `consolidate` first." |
46
46
  | Latest consolidated file ≤ 8 days old | Continue but flag `stale-evidence` at top of `proposed.md`. |
47
47
 
@@ -76,7 +76,7 @@ Refuse to produce `proposed.md` and tell the user exactly what's missing if any
76
76
  ## What this skill does NOT do
77
77
 
78
78
  - Does NOT call any ADO `PATCH` or `POST` endpoint.
79
- - Does NOT create or modify the global `.project-evidence/ado/config.yml`.
79
+ - Does NOT create or modify the global `.kushi/config/shared/integrations.yml`.
80
80
  - Does NOT touch `<project>/integrations.yml` other than appending the `ado.writes:` sub-block on first run (preserving every existing key).
81
81
  - Does NOT re-pull evidence (uses the most recent `_Consolidated/` file as-is).
82
82
  - Does NOT write to the ledger (the ledger is written only by `apply-ado-update`).
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: "pull-ado"
3
- version: "2.3.1"
3
+ version: "2.3.2"
4
4
  description: "Pull ADO evidence as an engagement TREE (parent Engagement + every child WI). Per-item discussion comments + revision history (verbatim). Deterministic resolution; no parent-only summaries. Per ado-engagement-tree.instructions.md."
5
5
  ---
6
6
 
@@ -27,9 +27,9 @@ WorkIQ-first per `workiq-first.instructions.md` — but **for ADO, REST is prefe
27
27
  - `<project>` — already-resolved project name.
28
28
  - `<alias>` — current contributor.
29
29
  - `<window>` — date range. For snapshot: ignored (always full re-fetch). For stream: `(from, to)`.
30
- - (read) `<engagement-root>/.project-evidence/ado/config.yml` — connection.
30
+ - (read) `<workspace>/.kushi/config/shared/integrations.yml` — connection.
31
31
  - (read) `<engagement-root>/<project>/integrations.yml#ado` — per-project pinned IDs.
32
- - (read) `<engagement-root>/.project-evidence/m365/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints.
32
+ - (read) `<workspace>/.kushi/config/user/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints.
33
33
  - (read) `<engagement-root>/<project>/Evidence/<alias>/.settings.yml` — per-(project x user) overrides.
34
34
 
35
35
  ## Resolution hints (pinned in mutable)
@@ -40,28 +40,42 @@ WorkIQ-first per `workiq-first.instructions.md` — but **for ADO, REST is prefe
40
40
 
41
41
  ## Hard prerequisites (REQUIRED — see `scope-boundaries.instructions.md` Rule 3)
42
42
 
43
- This skill is **HARD-fail** without both:
43
+ This skill is **HARD-fail** without ALL of the following (effective-config rule):
44
44
 
45
- 1. Global config: `<engagement-root>/.project-evidence/ado/config.yml` exists with non-placeholder `organization` AND `defaultProject`.
46
- 2. Per-project boundary: `<engagement-root>/<project>/integrations.yml#boundaries.ado.area_paths` is non-empty (OR `boundaries.ado.work_item_ids` is pinned).
45
+ 1. A resolvable `organization` from EITHER:
46
+ - per-project override: `<engagement-root>/<project>/integrations.yml#sources.ado.organization_override`, OR
47
+ - global default: `<workspace>/.kushi/config/shared/integrations.yml#ado.organization` (non-placeholder).
48
+ 2. A resolvable `defaultProject` from EITHER:
49
+ - per-project override: `<engagement-root>/<project>/integrations.yml#sources.ado.project_override`, OR
50
+ - global default: `<workspace>/.kushi/config/shared/integrations.yml#ado.project` (non-placeholder).
51
+ 3. Per-project boundary: `<engagement-root>/<project>/integrations.yml#boundaries.ado.area_paths` is non-empty OR `boundaries.ado.work_item_ids` is pinned OR `sources.ado.engagement_id` is pinned (pinned WI IDs are sufficient — they bypass area-path discovery).
47
52
 
48
- If either is missing, refuse with the exact message:
53
+ **Resolution order at runtime:**
54
+ ```
55
+ effective.organization = project.sources.ado.organization_override ?? global.ado.organization
56
+ effective.project = project.sources.ado.project_override ?? global.ado.project
57
+ effective.tenant_id = project.sources.ado.tenant_id_override ?? global.ado.tenant_id
58
+ ```
59
+
60
+ If any required value resolves to null/placeholder, refuse with:
49
61
 
50
62
  ```
51
- ado-config-missing — drop a filled config.yml at
52
- <engagement-root>/.project-evidence/ado/config.yml. See template at
53
- plugin/templates/init/ado-config.template.yml. Then add boundaries.ado.area_paths
54
- to <project>/integrations.yml.
63
+ ado-config-missing — fill the ado: block in
64
+ <workspace>/.kushi/config/shared/integrations.yml, OR set the corresponding
65
+ sources.ado.*_override in <project>/integrations.yml. Then ensure
66
+ boundaries.ado.area_paths (or work_item_ids, or sources.ado.engagement_id)
67
+ is pinned.
55
68
  ```
56
69
 
57
70
  Discovery loops (`title_contains`, `tags_contains`, `custom_iscrm_id`) are still
58
71
  permitted — but ONLY inside `boundaries.ado.area_paths`. There is no
59
- tenant-wide title-fuzzy scan in v3.7.0+.
72
+ tenant-wide title-fuzzy scan in v3.7.0+. If `sources.ado.engagement_id` is
73
+ already pinned, skip discovery entirely and proceed to Step 2 (tree expansion).
60
74
 
61
75
  ## Auth (deterministic, no improvisation)
62
76
 
63
77
  ```powershell
64
- $cfg = Get-Content "<engagement-root>/.project-evidence/ado/config.yml" | ConvertFrom-Yaml
78
+ $cfg = Get-Content "<workspace>/.kushi/config/shared/integrations.yml" | ConvertFrom-Yaml
65
79
  $tok = az account get-access-token `
66
80
  --resource $cfg.azDevOpsResourceId `
67
81
  --tenant $cfg.tenantId `
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: "pull-crm"
3
- version: "2.3.1"
3
+ version: "2.3.2"
4
4
  description: "Pull CRM (Dataverse) evidence (snapshot: engagement record VERBATIM long-text fields + ALL annotations with formatted values; stream: notes/activities/status changes). Per-record explicit $select + formatted-value annotations. FDE entity sets use crm-field-manifest.md. v2.3.0: exhaustive 4-step resolution sequence (title → account[s] → wide-text → recent-slice → ask). Anti-patterns codified: no statecode filter, new_companyname is NOT a field, never disable from one shallow probe."
5
5
  ---
6
6
 
@@ -27,7 +27,7 @@ WorkIQ-first per `workiq-first.instructions.md`. Thoroughness per `evidence-thor
27
27
  - `<project>` — already-resolved project name.
28
28
  - `<alias>` — current contributor.
29
29
  - `<window>` — date range. For snapshot: ignored (always full re-fetch). For stream: `(from, to)`.
30
- - (read) `<engagement-root>/.project-evidence/m365/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints.
30
+ - (read) `<workspace>/.kushi/config/user/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints.
31
31
  - (read) `<engagement-root>/<project>/Evidence/<alias>/.settings.yml` — per-(project x user) overrides.
32
32
 
33
33
  ## Resolution hints (pinned in mutable)
@@ -38,20 +38,31 @@ WorkIQ-first per `workiq-first.instructions.md`. Thoroughness per `evidence-thor
38
38
 
39
39
  ## Hard prerequisites (REQUIRED — see `scope-boundaries.instructions.md` Rule 3)
40
40
 
41
- This skill is **HARD-fail** without both:
41
+ This skill is **HARD-fail** without BOTH (effective-config rule):
42
42
 
43
- 1. Global config: `<engagement-root>/.project-evidence/crm/config.yml` exists with non-placeholder `environmentUrl`.
43
+ 1. A resolvable Dataverse `environment_url` from EITHER:
44
+ - per-project override: `<engagement-root>/<project>/integrations.yml#sources.crm.environment_url_override` (preferred — survives global config drift), OR
45
+ - global default: `<workspace>/.kushi/config/shared/integrations.yml#crm.environment_url` (non-placeholder).
44
46
  2. Per-project boundary: `<engagement-root>/<project>/integrations.yml#boundaries.crm.record_ids` OR `boundaries.crm.request_ids` is non-empty.
45
47
 
46
- If either is missing, refuse with the exact message:
48
+ **Resolution order at runtime:**
49
+ ```
50
+ effective.environment_url = project.sources.crm.environment_url_override ?? global.crm.environment_url
51
+ effective.entity_set = project.sources.crm.entity_set_override ?? global.crm.default_entity_set
52
+ effective.tenant_id = project.sources.crm.tenant_id_override ?? global.crm.tenant_id
53
+ ```
54
+
55
+ If `effective.environment_url` is null/placeholder, refuse with:
47
56
 
48
57
  ```
49
58
  crm-config-missing — drop a filled config.yml at
50
- <engagement-root>/.project-evidence/crm/config.yml. See template at
51
- plugin/templates/init/crm-config.template.yml. Then add boundaries.crm.record_ids
52
- (or request_ids) to <project>/integrations.yml.
59
+ <workspace>/.kushi/config/shared/integrations.yml (crm: block), OR set
60
+ sources.crm.environment_url_override in <project>/integrations.yml.
61
+ Then ensure boundaries.crm.record_ids (or request_ids) is non-empty.
53
62
  ```
54
63
 
64
+ If `effective.environment_url` resolves but `boundaries.crm.record_ids` AND `boundaries.crm.request_ids` are both empty, refuse with the analogous `crm-boundary-missing` message.
65
+
55
66
  **No "narrate from email" fallback exists in v3.7.0+.** CRM evidence comes from CRM
56
67
  or it is absent — Coverage Notes will say so explicitly. Synthesis across sources
57
68
  happens at consolidation, not at pull.
@@ -195,7 +206,7 @@ If a week file already exists, MERGE (dedupe by event ID, append new events, kee
195
206
 
196
207
  ## Tools (in order)
197
208
 
198
- 1. **Dataverse REST Web API** (preferred for snapshot — gives explicit `$select` + `$expand` control) via `az account get-access-token` against the configured tenant. Read connection from `<engagement-root>/.project-evidence/crm/config.yml`.
209
+ 1. **Dataverse REST Web API** (preferred for snapshot — gives explicit `$select` + `$expand` control) via `az account get-access-token` against the configured tenant. Read connection from `<workspace>/.kushi/config/shared/integrations.yml`.
199
210
  2. **WorkIQ** — only when REST is unavailable; phrase queries to demand verbatim long-text + every annotation (see Step B template above). WorkIQ summarizes by default, so use this path only as fallback.
200
211
  3. **Graph REST** — last resort, soft-fail per `az-auth-conditional.instructions.md`.
201
212
  4. **Ask user** — paste verbatim source content if all above fail.
@@ -26,7 +26,7 @@ Thoroughness per `evidence-thoroughness.instructions.md`; runtime detector + aut
26
26
  - `<project>` — already-resolved project name.
27
27
  - `<alias>` — current contributor.
28
28
  - `<window>` — date range. For snapshot: ignored (always full re-fetch). For stream: `(from, to)`.
29
- - (read) `<engagement-root>/.project-evidence/m365/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints.
29
+ - (read) `<workspace>/.kushi/config/user/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints.
30
30
  - (read) `<engagement-root>/<project>/Evidence/<alias>/.settings.yml` — per-(project x user) overrides.
31
31
 
32
32
  ## Resolution hints (pinned in mutable)
@@ -43,7 +43,7 @@ This skill REFUSES to query unless `<engagement-root>/<project>/integrations.yml
43
43
  - `boundaries.email.subject_keywords` — optional narrowing.
44
44
  - `boundaries.date_window_days` — defaults to 30 if absent.
45
45
 
46
- Every WorkIQ ask, every `m365_search_emails` / `m365_list_emails` call, every Graph fallback MUST be scoped to those mailboxes + (if set) sender_domains + subject_keywords. Empty hits inside the boundary → write Coverage Notes citing the limiting key; do NOT widen the scope.
46
+ Every WorkIQ ask MUST be scoped to those mailboxes + (if set) sender_domains + subject_keywords. Empty hits inside the boundary → write Coverage Notes citing the limiting key; do NOT widen the scope. (`m365_search_emails` / `m365_list_emails` / any Graph call is FORBIDDEN per `workiq-only.instructions.md`; on WorkIQ failure, write a deferred-retry marker per `deferred-retry-on-workiq-fail.instructions.md` and continue.)
47
47
 
48
48
  Refusal message when boundary is missing:
49
49
 
@@ -30,7 +30,7 @@ Auth + retry + error logging per `auth-and-retry.instructions.md`. WorkIQ-only p
30
30
  - `<project>` — already-resolved project name.
31
31
  - `<alias>` — current contributor.
32
32
  - `<window>` — date range. For snapshot: ignored (always full re-fetch). For stream: `(from, to)`.
33
- - (read) `<engagement-root>/.project-evidence/m365/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints (`calendarContext.subjectKeywords`, `calendarContext.knownSeries`).
33
+ - (read) `<workspace>/.kushi/config/user/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints (`calendarContext.subjectKeywords`, `calendarContext.knownSeries`).
34
34
 
35
35
  ## Discovery (snapshot pass — WorkIQ ONLY per `workiq-only.instructions.md`)
36
36
 
@@ -49,7 +49,8 @@ if (!PROJECT || !ENGAGEMENT_ROOT) {
49
49
  process.exit(2);
50
50
  }
51
51
 
52
- const REG_PATH = path.join(ENGAGEMENT_ROOT, '.project-evidence', 'm365', 'm365-mutable.json');
52
+ const WORKSPACE = args.workspace || process.cwd();
53
+ const REG_PATH = path.join(WORKSPACE, '.kushi', 'config', 'user', 'm365-mutable.json');
53
54
  if (!fs.existsSync(REG_PATH)) {
54
55
  console.error(`[recapture-section-url] Registry not found: ${REG_PATH}`);
55
56
  process.exit(3);
@@ -54,7 +54,8 @@ const TIMEOUT_MS = args.timeout || '120000';
54
54
  const PROJECT = args.project;
55
55
  const ROOT = args['engagement-root'];
56
56
  const ALIAS = args.alias || 'ushak';
57
- const MUTABLE_PATH = args.mutable || path.join(ROOT || '.', '.project-evidence', 'm365', 'm365-mutable.json');
57
+ const WORKSPACE = args.workspace || process.cwd();
58
+ const MUTABLE_PATH = args.mutable || path.join(WORKSPACE, '.kushi', 'config', 'user', 'm365-mutable.json');
58
59
 
59
60
  if ((!JSON_PATH && !SECTION_URL) || !PROJECT || !ROOT) {
60
61
  console.error('Usage: (--json <runner-output> | --section-url <url>) --project <name> --engagement-root <root> [--alias ushak] [--mutable <path>] [--timeout 120000]');
@@ -28,7 +28,7 @@ Thoroughness per `evidence-thoroughness.instructions.md`; runtime detector + aut
28
28
  - `<project>` — already-resolved project name.
29
29
  - `<alias>` — current contributor.
30
30
  - `<window>` — date range. For snapshot: ignored (always full re-fetch). For stream: `(from, to)`.
31
- - (read) `<engagement-root>/.project-evidence/m365/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints.
31
+ - (read) `<workspace>/.kushi/config/user/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints.
32
32
  - (read) `<engagement-root>/<project>/Evidence/<alias>/.settings.yml` — per-(project x user) overrides.
33
33
 
34
34
  ## Resolution hints (pinned in mutable)
@@ -28,7 +28,7 @@ Thoroughness per `evidence-thoroughness.instructions.md`; runtime detector + aut
28
28
  - `<project>` — already-resolved project name.
29
29
  - `<alias>` — current contributor.
30
30
  - `<window>` — date range. For snapshot: ignored (always full re-fetch). For stream: `(from, to)`.
31
- - (read) `<engagement-root>/.project-evidence/m365/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints.
31
+ - (read) `<workspace>/.kushi/config/user/m365-mutable.json m365Mutable.knownSections.<project>` — pinned hints.
32
32
  - (read) `<engagement-root>/<project>/Evidence/<alias>/.settings.yml` — per-(project x user) overrides.
33
33
 
34
34
  ## Resolution hints (pinned in mutable)
@@ -8,7 +8,7 @@ description: "Incremental refresh for an already-bootstrapped project. Reads run
8
8
 
9
9
  > **Verbatim-by-default**: This orchestrator MUST dispatch every enabled `pull-<source>` skill whose boundary in `integrations.yml#boundaries.<source>` is satisfied. Skipping a source silently is a defect. See `verbatim-by-default.instructions.md`.
10
10
  >
11
- > **Discover once, consume deterministically**: Refresh MUST read canonical M365 IDs from `<engagement-root>/.project-evidence/m365/m365-mutable.json#knownSections.<projectKey>` and pass them verbatim into each `pull-*`'s index-extractor query. **Refresh never re-discovers.** If the project entry is missing or a per-source key is empty, re-dispatch through `bootstrap-project` for that source's discovery only, then resume. See `m365-id-registry.instructions.md`.
11
+ > **Discover once, consume deterministically**: Refresh MUST read canonical M365 IDs from `<workspace>/.kushi/config/user/m365-mutable.json#knownSections.<projectKey>` and pass them verbatim into each `pull-*`'s index-extractor query. **Refresh never re-discovers.** If the project entry is missing or a per-source key is empty, re-dispatch through `bootstrap-project` for that source's discovery only, then resume. See `m365-id-registry.instructions.md`.
12
12
  >
13
13
  > **Per-user refresh report REQUIRED**: At end of run, write `<project>/Evidence/<alias>/refresh-reports/<YYYY-MM-DD-HHmm>_refresh.md` per `run-reports.instructions.md`. Even on a no-op run. Cite the registry: `Resolved IDs sourced from m365-mutable.json#knownSections.<projectKey>`.
14
14
  >
@@ -50,6 +50,26 @@ Profile is read from `kushi-install.json#profile` next to the agent file. Defaul
50
50
  - Else if `sources.<src>.watermark` exists → use `(watermark, today)`.
51
51
  - Else (first refresh after bootstrap, or new source) → fallback to `last 7 days`.
52
52
 
53
+ ### Step 2a — Drain deferred-retry queue (REQUIRED, kushi v4.4.1+, per `deferred-retry-on-workiq-fail.instructions.md`)
54
+
55
+ **Before per-source dispatch**, drain any markers that previous runs deferred. The queue lives at:
56
+
57
+ ```
58
+ <engagement-root>/<project>/Evidence/<alias>/_deferred-retries/*.yml
59
+ ```
60
+
61
+ For each marker (chronological order):
62
+
63
+ 1. Load the YAML marker. Parse `source`, `target`, `window`, `workiq.command`.
64
+ 2. Re-issue the canonical WorkIQ query (NOT the doubled-strict — give the first prompt another chance first; if it fails, then doubled-strict).
65
+ 3. If success → write the artifact to its canonical `Evidence/<alias>/<source>/{snapshot,stream}/` path per the source's normal write rules. Delete the marker. Append a `drained:` entry to the refresh report's `## Deferred-retry drain` section.
66
+ 4. If failure → increment `attempts`, update `last_attempt_at`, leave marker in place. Append a `still-deferred:` entry to the report.
67
+ 5. If `attempts >= 5` after this run → also promote to a row in `<project>/OPEN-QUESTIONS-DRAFT.md` (or `State/09_open-questions.md` on `full` profile) per the doctrine's escalation rule.
68
+
69
+ **NEVER call `m365_get_*` / Graph as a fallback during drain.** A drain failure is just another deferral.
70
+
71
+ After drain, proceed to Step 2 even if some markers remain — the orchestrator never blocks on deferred-retry failures.
72
+
53
73
  ### Step 2 — Per-source dispatch
54
74
 
55
75
  For each enabled source (or just the requested one), call its `pull-<source>` skill with the effective window.
@@ -616,7 +616,7 @@ if ($Deep) {
616
616
  if (-not (Test-Path $layoutInst)) {
617
617
  Add-Finding D14 'Evidence layout' 'warning' "plugin/instructions/evidence-layout-canonical.instructions.md is missing" "Restore the canonical-layout doctrine from kushi v3.12.1." $layoutInst 0
618
618
  }
619
- $allowedSiblings = @('Evidence','State','Reports','.kushi','.kushi-reference','.project-evidence','.vscode','.git')
619
+ $allowedSiblings = @('Evidence','State','Reports','.kushi','.kushi-reference','.vscode','.git')
620
620
  $sourceTokens = @(
621
621
  'email-context','email-summary','email-summaries',
622
622
  'teams-context','teams-summary','teams-summaries',
@@ -655,6 +655,29 @@ if ($Deep) {
655
655
  }
656
656
  }
657
657
  }
658
+
659
+ # D15: legacy path regression guard — no `.project-evidence/{m365,crm,ado}` refs outside CHANGELOG/learnings.
660
+ # v4.4.0+ moved per-user config to `<workspace>/.kushi/config/{user,shared}/`. Any plugin/docs file
661
+ # still referencing the legacy `<engagement-root>/.project-evidence/(m365|crm|ado)` path will mislead
662
+ # skills and authors. CHANGELOG and learnings/ legitimately mention the legacy path for history.
663
+ $legacyPattern = '\.project-evidence[\\/](?:m365|crm|ado)'
664
+ $legacyTargets = @(
665
+ (Join-Path $Root 'plugin'),
666
+ (Join-Path $Root 'docs')
667
+ )
668
+ foreach ($target in $legacyTargets) {
669
+ if (-not (Test-Path $target)) { continue }
670
+ $legacyHits = Get-ChildItem -Path $target -Recurse -File -Include '*.md','*.ps1','*.mjs','*.json','*.yml','*.yaml' -ErrorAction SilentlyContinue |
671
+ Where-Object { $_.FullName -notmatch '[\\/]learnings[\\/]' -and $_.Name -ne 'CHANGELOG.md' } |
672
+ Select-String -Pattern $legacyPattern -ErrorAction SilentlyContinue
673
+ foreach ($m in $legacyHits) {
674
+ # Allow lines that explicitly mark as historical breadcrumb (contain "legacy" or "v4.4.0+" or "was")
675
+ if ($m.Line -match '(?i)\blegacy\b|v4\.4\.0\+|\bwas\b|\breplaces?\b|\bdeprecated\b|\bhistorical\b') { continue }
676
+ $msg = 'Legacy .project-evidence/{m365|crm|ado} reference — v4.4.0+ moved per-user config to <workspace>/.kushi/config/{user,shared}/'
677
+ $fix = "Replace with the new path; if intentionally documenting history, add the word 'legacy' or 'v4.4.0+ replaces' on the same line."
678
+ Add-Finding D15 'Legacy paths' 'warning' $msg $fix $m.Path $m.LineNumber
679
+ }
680
+ }
658
681
  }
659
682
 
660
683
  # === Output ===
@@ -4,7 +4,7 @@
4
4
  # read from the SAME integrations.yml that pull-ado already uses — no separate config file.
5
5
  #
6
6
  # Global ADO connection (tenantId, organization, apiVersion, auth) lives at:
7
- # <engagement-root>/.project-evidence/ado/config.yml
7
+ # <workspace>/.kushi/config/shared/integrations.yml
8
8
  # Do NOT duplicate those keys here.
9
9
 
10
10
  ado:
@@ -1,19 +1,32 @@
1
- # Kushi — optional global integrations defaults.
2
- #
3
- # Seeded by the installer on first install. Never overwritten on upgrade.
4
- # Per-project `integrations.yml` (inside the engagement folder) always
5
- # takes precedence over this file.
6
- #
7
- # Most users can leave this file as-is. Fill it in only if you want a
8
- # default CRM / ADO configuration that applies across all projects.
9
-
10
- # Microsoft Dataverse (CRM) defaults.
11
- # crm:
12
- # environment_url: 'https://<org>.crm.dynamics.com'
13
- # tenant_id: '72f988bf-86f1-41af-91ab-2d7cd011db47' # Microsoft tenant
14
-
15
- # Azure DevOps defaults.
16
- # ado:
17
- # organization: 'https://dev.azure.com/<org>'
18
- # project: '<default-project>'
19
- # tenant_id: '72f988bf-86f1-41af-91ab-2d7cd011db47' # Microsoft tenant
1
+ # Kushi — optional global integrations defaults.
2
+ #
3
+ # Seeded by the installer on first install. Never overwritten on upgrade.
4
+ # Per-project `integrations.yml` (inside the engagement folder) always
5
+ # takes precedence over this file. Effective-config rule:
6
+ # effective.X = project.override ?? global.X (hard-fail only if both empty)
7
+ #
8
+ # Most users can leave this file as-is. Uncomment + edit only if you want a
9
+ # default CRM / ADO configuration that applies across all projects.
10
+ #
11
+ # Microsoft (MSFT) tenant defaults shown below. If you work in the Microsoft
12
+ # tenant (most Industry Solutions FDEs), these values are correct as-is.
13
+
14
+ # Microsoft Dataverse (CRM) defaults.
15
+ # Per-project overrides in <engagement-root>/<project>/integrations.yml under
16
+ # sources.crm.environment_url_override / entity_set_override always take
17
+ # precedence (e.g. FDE intake projects point at iscrm.crm.dynamics.com /
18
+ # new_frontierengineeringtriages).
19
+ # crm:
20
+ # environment_url: 'https://microsoftsales.crm.dynamics.com'
21
+ # tenant_id: '72f988bf-86f1-41af-91ab-2d7cd011db47' # Microsoft tenant
22
+ # default_entity_set: 'opportunities'
23
+ # az_resource_id: 'https://microsoftsales.crm.dynamics.com'
24
+
25
+ # Azure DevOps defaults.
26
+ # Per-project pin in <engagement-root>/<project>/integrations.yml sources.ado
27
+ # (engagement_id, area_paths) overrides the discovery defaults here.
28
+ # ado:
29
+ # organization: 'https://dev.azure.com/IndustrySolutions'
30
+ # project: 'IS Engagements'
31
+ # tenant_id: '72f988bf-86f1-41af-91ab-2d7cd011db47' # Microsoft tenant
32
+ # az_resource_id: '499b84ac-1321-427f-aa17-267ca6975798' # ADO well-known resource id