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
@@ -0,0 +1,146 @@
1
+ ---
2
+ applyTo: "**"
3
+ description: "Always-on: every kushi FDE-shaped output (fde-intake, fde-report, fde-triage, build-state, project-status, ask-project FDE answers) MUST ground in the FDE reference pack at reference-packs/fde/, apply recency precedence, and pair every CRM-recorded decision with the confidence ladder. This is the doctrinal companion to the reference pack itself."
4
+ ---
5
+
6
+ # FDE Reference Grounding (HARD RULE)
7
+
8
+ Every kushi output that interprets engagement health, FDE stage, fitness, risks, readiness, status, or recommended next steps MUST be **grounded in the FDE reference pack** rather than inferred from intuition or model priors.
9
+
10
+ The reference pack lives at:
11
+
12
+ ```
13
+ plugin/reference-packs/fde/
14
+ README.md
15
+ core-fde-reference.md # operating model, fitness criteria, anti-patterns, stage gates, risk categories
16
+ crm-field-manifest.md # FDE-CRM field shape (FE-* triage entity)
17
+ intake-questions.md # the 10-question intake (used by fde-intake skill)
18
+ report-doctrine.md # report shape doctrine (used by fde-report skill)
19
+ ```
20
+
21
+ Per `reference-packs` doctrine, the **effective** pack is the 3-layer override:
22
+
23
+ 1. Project: `<engagement-root>/<project>/.kushi-reference/fde/<file>.md` (highest)
24
+ 2. User: `~/.copilot/kushi-reference/fde/<file>.md` (override)
25
+ 3. Packaged: `<install-dest>/reference-packs/fde/<file>.md` (default)
26
+
27
+ Citations MUST carry the layer marker: `[source: reference-packs/fde/<file>.md · packaged|user-override|project-override]`.
28
+
29
+ ## Required behavior
30
+
31
+ ### For every FDE-shaped output
32
+
33
+ 1. **Read the pack first.** Before drafting any FDE-shaped artifact, read `reference-packs/fde/README.md` and enumerate the human-readable files. Treat the folder as ONE reference set; any file may contribute.
34
+ 2. **Score against the 10-criterion FDE Fitness Checklist** from `core-fde-reference.md`:
35
+ - use case fit, platform commitment, C-level sponsor, hypervelocity culture, co-build commitment, funding clarity, measurable outcomes, contracting speed, business owner engagement, non-standard scope.
36
+ - Report which criteria are met, unclear, at risk — never just an overall thumbs-up.
37
+ 3. **Map every risk to one of the eight FDE risk categories** (also from `core-fde-reference.md`):
38
+ - Competitive & Strategic Fit, Funding & Commercial, Contracting / Procurement, Sponsorship, Hypervelocity / Culture, Scope / Use Case Fit, Resourcing (MS side), Handover / Sustainability.
39
+ - Free-form risk taxonomies are a defect.
40
+ 4. **Flag FDE anti-patterns explicitly** when evidence suggests drift toward what FDE is not: advisory-only, long governance cycles, platform-agnostic, staff-aug, out-of-box use case.
41
+ 5. **Use CRM `Status` as a primary lifecycle signal** when available. Translate it via the CRM Triage Plan mapping in `core-fde-reference.md`. Distinguish carefully between `Completed`, `Withdrawn`, `On Hold`, discovery-stage, and active-delivery statuses.
42
+ 6. **Call out mismatches explicitly** when structured system state, older notes, or older artifacts imply a different reality than newer evidence.
43
+
44
+ ### Recency precedence (every conclusion, not just status)
45
+
46
+ When dated sources conflict, newer evidence has more authority — and not only for status, but also for **conclusions, risk posture, momentum, blockers, ownership assumptions, and recommended next steps**.
47
+
48
+ Default precedence order (unless the user explicitly asks otherwise):
49
+
50
+ 1. latest CRM note entries
51
+ 2. latest meeting transcripts
52
+ 3. latest dated local project docs / assets in `<project>/Evidence/`
53
+ 4. latest external linked notes / docs (OneNote, SharePoint)
54
+ 5. older CRM notes and historical project artifacts
55
+
56
+ **When newer evidence supersedes an older conclusion or signal, say so directly** — do not blend the two into an averaged summary. If a recommendation from an older source is no longer appropriate because of newer evidence, state that it has been superseded; do not repeat it as active guidance.
57
+
58
+ Do not let stale evidence dominate synthesis. If an older source says a decision is open, a blocker is active, or a plan is current, but a newer dated source shows the issue was resolved or reversed, the newer source drives the narrative.
59
+
60
+ ### CRM field values vs confirmed facts
61
+
62
+ This rule is the FDE-specific application of `evidence-confidence-ladder.instructions.md`. Every materially important FDE response (funding, owner, scope decision, timeline, legal / commercial gate, SI lane) MUST carry one of:
63
+
64
+ - `internal-only` — CRM/ADO field recorded; no customer-facing confirmation in evidence
65
+ - `communicated` — customer or external stakeholder acknowledged in transcript / email / Teams
66
+ - `confirmed` — signed / approved / executed artifact on record
67
+
68
+ A CRM-only signal is **not** a confirmed fact. Do not say "ECIF is the selected funding model" based on a `statuscode` flip alone. Say "Internal team decision favors ECIF (CRM field updated <date>, `internal-only`); latest customer-facing transcript predates the change; no `communicated` evidence yet."
69
+
70
+ For living triage reports (`fde-triage` output), every row that drives a decision (`FDE Fit First`, `ECIF Confirmation Gate`, `Verification Evidence`) MUST classify the response as one of `CRM-only` / `Cross-verified` / `Conflicting evidence` and cite the confirming source + date for the upgrade past `CRM-only`.
71
+
72
+ ### Full customer view requires in-session source resolution
73
+
74
+ This grounding doctrine defines **how to interpret** evidence. It does NOT by itself satisfy the requirement to retrieve every applicable customer artifact.
75
+
76
+ Before drafting a "full view", "complete picture", or equivalent FDE-shaped output, the current session MUST attempt retrieval of every applicable source category that has a defined retrieval path:
77
+
78
+ 1. CRM (Dataverse / FDE triage entity)
79
+ 2. Meeting transcripts
80
+ 3. Email
81
+ 4. Teams chats
82
+ 5. OneNote
83
+ 6. SharePoint / external linked artifacts
84
+ 7. ADO (when an engagement WI exists)
85
+
86
+ For each, record one of: `retrieved` / `not present for project` / `attempted but blocked` (with reason). Per `status-taxonomy.instructions.md`, use the closed-set Status values. Per `fallback-status-reporting.instructions.md`, mark fallback recoveries explicitly.
87
+
88
+ Do not describe an output as a `full view`, `complete picture`, `comprehensive`, or equivalent unless these source categories were retrieved or explicitly attempted+dispositioned in the current session.
89
+
90
+ ### Workspace evidence boundary + source quotes
91
+
92
+ For every report, summary, recommendation, status response, or inferred conclusion:
93
+
94
+ 1. Use workspace evidence only (this repository's context, with priority on `<project>/Evidence/` and the configured reference pack).
95
+ 2. Do not invent or assume facts not present in retrieved evidence.
96
+ 3. Do not fill gaps with unstated assumptions. If evidence is missing, state `insufficient evidence in workspace context`.
97
+ 4. Quote sources for material claims using the kushi citation format: `[source: <relative-path> · <YYYY-MM-DD>]`. For reference-pack citations: `[source: reference-packs/fde/<file>.md · packaged|user-override|project-override]`.
98
+ 5. For every key conclusion, include at least one source line.
99
+ 6. If sources are ambiguous or conflicting, state so clearly and apply date-based recency precedence.
100
+ 7. Living triage reports MUST include a complete `Sources Used (Complete Ledger)` section per `citation-ledger.instructions.md`.
101
+
102
+ ### CRM notes integration
103
+
104
+ When kushi assists with CRM note authoring (manual today; `propose-crm-update` future), include a brief FDE fitness signal (1–2 sentences) whenever the note summarizes engagement status — e.g. "Funding model unconfirmed (criterion 6 at risk)" or "C-level sponsor accessible and engaged (criterion 3 met)."
105
+
106
+ ### Engagement authoring artifacts
107
+
108
+ - `fde-intake` 10-question output MUST use `reference-packs/fde/intake-questions.md` as its prompt scaffold; do not paraphrase the question wording.
109
+ - `fde-report` shapes (weekly / short / long / fitness / stage-readiness) MUST cite `reference-packs/fde/report-doctrine.md` and `core-fde-reference.md` for stage gates and fitness mapping.
110
+ - `fde-triage` bundle MUST include the FDE Fitness Checklist (file 01) and map every risk to the eight categories (file 03).
111
+
112
+ ### Status reports / readouts
113
+
114
+ - Treat CRM `Status` as one of the first facts established in the summary.
115
+ - Translate raw `statuscode` into plain-language stage meaning, not just the numeric label.
116
+ - Do not describe `Withdrawn` or `On Hold` engagements with active-delivery language.
117
+ - Do not describe `Technical Assessment` or `Assigned` as `In Progress` unless execution evidence supports the distinction and the mismatch is called out.
118
+ - Let the newer dated source drive the narrative; mark the older interpretation as superseded.
119
+
120
+ ### Inferred conclusions / recommendations / summaries
121
+
122
+ Apply the same recency precedence to conclusions about health, urgency, readiness, commitment level, decision ownership, and likely next actions. Older plans and recommendations are historical context when newer evidence changes the picture.
123
+
124
+ ## Anti-patterns (defects)
125
+
126
+ 1. Drafting an FDE-shaped output without reading the reference pack.
127
+ 2. Inventing risk categories beyond the eight defined.
128
+ 3. Treating a CRM field flip as a confirmed customer-facing decision.
129
+ 4. Blending older and newer dated evidence into an averaged summary instead of letting newer evidence supersede.
130
+ 5. Calling the output "comprehensive" / "full view" without running the in-session source-resolution sweep.
131
+ 6. Citing the FDE pack without the layer marker (`packaged | user-override | project-override`).
132
+
133
+ ## Always-on
134
+
135
+ This instruction applies to every kushi agent invocation and to any external consumer rendering kushi evidence into an FDE-shaped artifact.
136
+
137
+ ## References
138
+
139
+ - `reference-packs/fde/README.md` — the pack itself.
140
+ - `evidence-confidence-ladder.instructions.md` — the three-state confidence vocabulary used here.
141
+ - `citation-ledger.instructions.md` — citation format (Sources Used ledger).
142
+ - `evidence-thoroughness.instructions.md` — depth bar (orthogonal: depth applies inside scope).
143
+ - `scope-boundaries.instructions.md` — what's in scope (orthogonal: scope defines surface).
144
+ - `fallback-status-reporting.instructions.md` — how to report retrieval outcome.
145
+ - `status-taxonomy.instructions.md` — closed-set Status values.
146
+ - skills: `fde-intake`, `fde-report`, `fde-triage`, `build-state`, `project-status`, `ask-project` (FDE-shaped answers).
@@ -37,30 +37,37 @@ Map the response:
37
37
  > ✓ Identity: `Alex Smith <alex@microsoft.com>` (alias=`alex`). Edit `.kushi/config/user/project-evidence.yml` to override.
38
38
  3. **Continue** the prompt.
39
39
 
40
- ## Failure modes
40
+ ## Failure modes (kushi v4.4.1+: never block, never fallback to Graph)
41
+
42
+ Per `deferred-retry-on-workiq-fail.instructions.md`, identity resolution NEVER blocks the orchestrator and NEVER calls `m365_get_*` / Graph as a fallback. On WorkIQ failure, the agent writes a deferred-retry marker and continues with a derived alias so evidence still lands somewhere it can later be re-keyed.
41
43
 
42
44
  | Scenario | Behavior |
43
45
  |-----------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|
44
- | WorkIQ returns auth error | Block: "Sign in to WorkIQ first: `workiq accept-eula && workiq ask -q ping`". Do not proceed. |
45
- | WorkIQ returns empty / NO_RESULTS | Block: "WorkIQ could not resolve your identity. Try `workiq ask -q 'who am I'` and retry." |
46
- | WorkIQ binary missing | This should already have been caught by the installer's pre-flight. If reached, block with the same install hint. |
47
- | User has explicit non-placeholder values | Skip resolution entirely. Never overwrite user-set values. |
46
+ | WorkIQ returns auth error | Write deferred-retry marker (`source: identity`, `error_class: auth-error`). Echo: *" Identity unresolved (WorkIQ auth). Using alias=`<from-config-or-env>`; will retry on next refresh. Sign in with `workiq accept-eula && workiq ask -q ping` and re-run."* Continue. **Do NOT call `m365_get_my_profile` or Graph `/me`.** |
47
+ | WorkIQ returns empty / NO_RESULTS | Write marker (`error_class: empty`). Echo: *" Identity unresolved (WorkIQ empty). Using alias=`<env-user>`; will retry on next refresh."* Continue. |
48
+ | WorkIQ binary missing | Installer pre-flight should catch this. If reached at skill-time, write marker (`error_class: cli-missing`), echo the install hint, continue with alias=`<env-user>`. |
49
+ | User has explicit non-placeholder values | Skip resolution entirely. Never overwrite user-set values. No marker needed. |
48
50
  | Alias collision with another contributor | Bootstrap detects existing `Evidence/<alias>/` whose `contributors.yml` records a different email → ask the user to disambiguate (suggest `<alias>-<tenant-prefix>` e.g. `alex-ms`). |
49
51
 
52
+ **Derived alias fallback (when WorkIQ unavailable):** use `$env:USERNAME` (Windows) or `$env:USER` (POSIX) lowercased. Persist provisionally with `alias_resolved_from: env-fallback-pending-workiq`. The next refresh's deferred-retry drain will re-issue the WorkIQ probe and rename `Evidence/<env-alias>/` → `Evidence/<workiq-alias>/` if they differ.
53
+
50
54
  ## What NOT to do
51
55
 
52
56
  * Do NOT ask the user `What alias should Kushi use?`. The legacy onboarding prompt is gone.
53
- * Do NOT call `m365_*` / Graph as a fallback. WorkIQ is the single source of identity truth (`workiq-first.instructions.md`).
57
+ * Do NOT call `m365_get_my_profile`, `m365_*`, or Graph `/me` as a fallback. WorkIQ is the single source of identity truth. On failure, write a marker and use the env-fallback. See `workiq-only.instructions.md` and `deferred-retry-on-workiq-fail.instructions.md`.
58
+ * Do NOT block the bootstrap orchestrator on identity failure. Continue with the env-fallback alias; the marker drives the eventual reconciliation.
54
59
  * Do NOT resolve on every run. Once persisted, the config values are authoritative.
55
60
 
56
61
  ## Integration with other doctrine
57
62
 
58
- * `workiq-first.instructions.md` — identity resolution is the canonical example of WorkIQ-first. Add to the inventory.
63
+ * `workiq-only.instructions.md` — identity resolution is the canonical example of WorkIQ-only. M365 fallbacks are forbidden.
64
+ * `deferred-retry-on-workiq-fail.instructions.md` — on WorkIQ failure, mark + continue + inform; never block.
59
65
  * `bootstrap-project` SKILL — Step 0 is identity resolution. Step 1 is project context.
60
66
  * `tracking.instructions.md` — the resolved identity goes into the tracking artifact's frontmatter under `actor:`.
61
67
 
62
68
  ## References
63
69
 
64
- * `workiq-first.instructions.md` — the parent doctrine.
70
+ * `workiq-only.instructions.md` — the parent doctrine (supersedes legacy `workiq-first`).
71
+ * `deferred-retry-on-workiq-fail.instructions.md` — failure-mode contract.
65
72
  * `engagement-root-resolution.instructions.md` — `projects_root` resolution (separate from identity).
66
73
  * `templates/init/project-evidence.template.yml` — defaults to `<auto>` for these three fields.
@@ -12,7 +12,7 @@ priority: HARD
12
12
 
13
13
  ## The registry
14
14
 
15
- `<engagement-root>/.project-evidence/m365/m365-mutable.json#knownSections.<projectKey>` is the **single source of truth** for canonical M365 identifiers per project.
15
+ `<workspace>/.kushi/config/user/m365-mutable.json#knownSections.<projectKey>` is the **single source of truth** for canonical M365 identifiers per project.
16
16
 
17
17
  Schema (populate every key the source supports):
18
18
 
@@ -21,7 +21,7 @@ host-fallback Graph call, and every CRM/ADO probe MUST honor.
21
21
  Before issuing **any** WorkIQ query, `m365_*` host call, Graph REST call, or CRM/ADO
22
22
  REST call, the skill MUST resolve the boundary it is operating inside from
23
23
  `<engagement-root>/<project>/integrations.yml boundaries:` (per-source) or the
24
- global `<engagement-root>/.project-evidence/{crm,ado}/config.yml` (auth +
24
+ global `<workspace>/.kushi/config/shared/integrations.yml` (auth +
25
25
  tenant + org). The resolved boundary MUST be recorded in the evidence file's
26
26
  `## Source Basis` block, e.g.:
27
27
 
@@ -57,8 +57,8 @@ returns evidence over the same surface — no run-to-run drift.
57
57
 
58
58
  | Skill | Hard prerequisite | Failure message (verbatim) |
59
59
  |---|---|---|
60
- | `pull-crm` | `<engagement-root>/.project-evidence/crm/config.yml` exists with non-placeholder `environmentUrl` | `crm-config-missing — drop a filled config.yml at <engagement-root>/.project-evidence/crm/config.yml. See plugin/templates/init/crm-config.template.yml.` |
61
- | `pull-ado` | `<engagement-root>/.project-evidence/ado/config.yml` exists with non-placeholder `organization` AND `defaultProject` | `ado-config-missing — drop a filled config.yml at <engagement-root>/.project-evidence/ado/config.yml. See plugin/templates/init/ado-config.template.yml.` |
60
+ | `pull-crm` | `<workspace>/.kushi/config/shared/integrations.yml` exists with non-placeholder `crm.environmentUrl` | `crm-config-missing — fill the crm: block in <workspace>/.kushi/config/shared/integrations.yml. See plugin/templates/init/integrations.template.yml.` |
61
+ | `pull-ado` | `<workspace>/.kushi/config/shared/integrations.yml` exists with non-placeholder `ado.organization` AND `ado.defaultProject` | `ado-config-missing — fill the ado: block in <workspace>/.kushi/config/shared/integrations.yml. See plugin/templates/init/integrations.template.yml.` |
62
62
  | `propose-ado-update` | both above + `integrations.yml ado.engagement_id > 0` | per `propose-ado-update/SKILL.md` Prerequisites table |
63
63
 
64
64
  Pre-v3.7.0 behavior allowed `pull-crm` to "narrate from email" when the live
@@ -172,7 +172,7 @@ On first run for a new project, bootstrap MUST:
172
172
  3. For sources where bootstrap cannot auto-populate, write the source as
173
173
  **disabled** (`enabled: false`) with a one-liner in `OPEN-QUESTIONS-DRAFT.md`
174
174
  asking the user to fill the boundary and re-enable.
175
- 4. For CRM and ADO, ALSO check that the global `<engagement-root>/.project-evidence/{crm,ado}/config.yml`
175
+ 4. For CRM and ADO, ALSO check that the global `<workspace>/.kushi/config/shared/integrations.yml`
176
176
  exists and has non-placeholder values; if not, scaffold from
177
177
  `plugin/templates/init/{crm,ado}-config.template.yml` and park in
178
178
  `OPEN-QUESTIONS-DRAFT.md` with the path the user needs to fill.
@@ -0,0 +1,118 @@
1
+ ---
2
+ applyTo: "**"
3
+ description: "The single normalized vocabulary for Status + Retry-Signal values used in every kushi status artifact (bootstrap-status.md, refresh reports, run-log.yml, ask-project footers, fde-report cover lines). Replaces the ad-hoc strings that drifted across skills."
4
+ ---
5
+
6
+ # Status Taxonomy (HARD RULE)
7
+
8
+ Every place a kushi skill renders a per-source or per-task status MUST use a value from the closed taxonomy below. Pre-v4.4.2 skills used ad-hoc strings (`populated`, `unsynced`, `degraded`, `partial-cap`, `blocked-sandbox`, `no stream`, `half done`, `waiting`) — those are now mapped onto the canonical values here.
9
+
10
+ ## 1. Status values (closed set)
11
+
12
+ | Status | Meaning | Use when |
13
+ |---|---|---|
14
+ | `completed` | Direct path succeeded; full coverage. | Preferred path returned everything in scope. |
15
+ | `completed-via-fallback` | Alternate path recovered the result; full coverage. | Direct path failed but a documented fallback delivered the requested scope. See `fallback-status-reporting.instructions.md`. |
16
+ | `completed-with-coverage-gaps` | Result was produced but specific known gaps remain. | Some items in scope were not retrievable; gaps are enumerated. |
17
+ | `partial` | Less than the requested scope was delivered; rerun recommended. | Fewer items than the boundary called for; not all gaps are enumerable. |
18
+ | `failed` | Both primary and fallback paths failed; no usable evidence written. | Every documented path returned no usable result. |
19
+ | `blocked-auth` | Pre-flight or token acquisition failed; the source could not be queried. | `az account show` non-zero, `m365_status` not signed in, tenant mismatch, WorkIQ EULA pending, etc. |
20
+ | `blocked-config` | A required configuration value (per-project + global both empty) prevented querying. | `<source>-config-missing` or `<source>-boundary-missing` per `scope-boundaries.instructions.md` Rule 3. |
21
+ | `blocked-permission` | Auth succeeded but the principal lacks access to the target. | HTTP 403 / `accessDenied` / SharePoint `UnauthorizedAccessException` on the canonical API. |
22
+ | `blocked-throttled` | Service throttled the request; no narrower query succeeded. | `tooManyRequests`, `More than 3 retries performed`, `high demand` per `auth-and-retry §3`. |
23
+ | `unresolved` | Discovery returned no matches inside the configured boundary. | All resolution-order steps returned 0 candidates; user has not yet picked or widened. |
24
+ | `deferred` | WorkIQ failed after doubled-strict retry; deferred-retry marker written. | Per `deferred-retry-on-workiq-fail.instructions.md`. Next refresh drains the queue. |
25
+ | `not-applicable` | Source is not configured for this project. | Source enabled=false or boundary list empty by design (e.g. SharePoint when project has no team site). |
26
+ | `no-run-history` | Source has never been pulled for this project. | Used in backfill rows when creating status artifacts retroactively. |
27
+
28
+ ### Mapping from legacy strings
29
+
30
+ | Legacy ad-hoc string | Canonical value |
31
+ |---|---|
32
+ | `populated` | `completed` (or `completed-with-coverage-gaps` if `## Coverage Notes` flags gaps) |
33
+ | `unsynced` | `partial` (rerun recommended) OR `not-applicable` (if intentionally deferred) — pick by intent |
34
+ | `degraded` | `completed-with-coverage-gaps` |
35
+ | `degraded-list-only` | `completed-with-coverage-gaps` (note: bodies missing, index present) |
36
+ | `partial-cap` | `partial` |
37
+ | `partial-template` | `completed-with-coverage-gaps` |
38
+ | `blocked-sandbox` | `blocked-permission` (with reason: agent sandbox) |
39
+ | `throttled-tooManyRequests` | `blocked-throttled` |
40
+ | `cli-available` (preflight pass) | `completed` (preflight row only) |
41
+ | `resolved` (preflight pass) | `completed` (preflight row only) |
42
+ | `missing` (preflight) | `blocked-config` |
43
+ | `ado-not-complete` | `partial` (with reason) |
44
+ | `no-match` | `unresolved` |
45
+ | `❌ all paths failed` | `failed` |
46
+ | `❌ workiq-empty-after-retry` | `deferred` (marker written) OR `failed` (if no marker for permanent reasons) |
47
+
48
+ ## 2. Retry-Signal values (closed set)
49
+
50
+ | Retry Signal | Meaning |
51
+ |---|---|
52
+ | `none` | No retry needed; result is settled. |
53
+ | `watch` | Usable result exists but direct-path gaps are worth reviewing later. |
54
+ | `retry` | Rerun is recommended; the next `refresh <project>` should pick this up. |
55
+ | `user-action` | Retry requires a user step first (paste verbatim, widen boundary, `az login`, install workiq, sign in to m365). |
56
+
57
+ ### Default mapping (Status → Retry Signal)
58
+
59
+ | Status | Default Retry Signal |
60
+ |---|---|
61
+ | `completed` | `none` |
62
+ | `completed-via-fallback` | `watch` |
63
+ | `completed-with-coverage-gaps` | `watch` |
64
+ | `partial` | `retry` |
65
+ | `failed` | `retry` |
66
+ | `blocked-auth` | `user-action` |
67
+ | `blocked-config` | `user-action` |
68
+ | `blocked-permission` | `user-action` |
69
+ | `blocked-throttled` | `retry` (next run, after backoff) |
70
+ | `unresolved` | `user-action` (pick a candidate or widen boundary) |
71
+ | `deferred` | `retry` (auto-drained by refresh Step 2a) |
72
+ | `not-applicable` | `none` |
73
+ | `no-run-history` | `none` |
74
+
75
+ A skill MAY override the default — but it must include the override reason in the `notes` column.
76
+
77
+ ## 3. Required usage
78
+
79
+ 1. `bootstrap-status.md` Context Artifact Status table — `Status` column values MUST come from §1. A new `Retry` column (added v4.4.2) MUST contain a §2 value.
80
+ 2. `Evidence/run-log.yml#sources.<source>.last_status` — MUST be a §1 value.
81
+ 3. `Evidence/run-log.yml#sources.<source>.retry_signal` (new v4.4.2 field) — MUST be a §2 value.
82
+ 4. Refresh report (`Evidence/<alias>/refresh-reports/<ts>_refresh.md`) per-source summary table — same columns + same vocabulary.
83
+ 5. `fde-report` cover line — when summarizing source coverage, use these values; do not invent new ones.
84
+ 6. `ask-project` answer footer — when surfacing coverage caveats (`Source basis: completed-via-fallback`), use these values.
85
+
86
+ ## 4. Retry review behavior
87
+
88
+ When the user asks "what failed?", "anything to retry?", "redo errors", or "/refresh failed" (`refresh-project` accepts this argument variant):
89
+
90
+ 1. Read every `<project>/Evidence/<alias>/run-log.yml` and every `<project>/bootstrap-status.md` newest-first.
91
+ 2. Filter to rows where `retry_signal` is `retry` or `deferred` is the status.
92
+ 3. If the user named a time window (e.g. "past 2 days"), filter by run timestamp first.
93
+ 4. Offer or execute the per-source `pull-*` again — scoped to the original boundary + window (no scope widening).
94
+ 5. After retry, update the per-source row with the new Status + Retry Signal. Never delete history; append a new row newest-first.
95
+
96
+ ## 5. Backfill rule
97
+
98
+ If a project has artifacts (`bootstrap-status.md`, snapshots) but no normalized status row yet (pre-v4.4.2 history):
99
+
100
+ - Create a backfill row using the best available evidence.
101
+ - Set `Status` from the legacy-string mapping in §1.
102
+ - Set `Retry Signal` from §2 default mapping.
103
+ - Note `(backfill)` in the row's `Notes` column.
104
+ - Do not invent precision: if exact timestamps are unavailable, write the date and note `(time inferred)`.
105
+
106
+ ## 6. Non-destructive rule
107
+
108
+ - Status artifacts are append-only at the row level. Never delete a historical row to "clean up".
109
+ - Newest rows go at the top of every status table.
110
+ - When a status changes between runs, write a new row; do not edit the old row in place.
111
+
112
+ ## References
113
+
114
+ - `fallback-status-reporting.instructions.md` — when `completed-via-fallback` applies.
115
+ - `auth-and-retry.instructions.md §4` — `errors[]` schema (signatures map to Status via the legacy table above).
116
+ - `bootstrap-status-format.instructions.md` — the artifact that uses this vocabulary most prominently.
117
+ - `deferred-retry-on-workiq-fail.instructions.md` — `deferred` status semantics.
118
+ - `scope-boundaries.instructions.md` — `blocked-config` rules.
@@ -42,8 +42,8 @@ For every M365 source in scope:
42
42
 
43
43
  1. **WorkIQ FIRST and ONLY.** Issue the canonical query from the table below.
44
44
  2. **If WorkIQ returns insufficient content** (empty, truncated, or only a summary when the query asked for verbatim): retry ONCE with the doubled-strict prompt from the same row.
45
- 3. **If WorkIQ still fails or is unavailable**: ask the user to paste the data verbatim. This is a first-class evidence path, not a degradation.
46
- 4. **DO NOT attempt** `m365_get_transcript`, `m365_get_facilitator_notes`, `m365_list_meetings`, `m365_list_events`, `m365_search_files` (for content), `m365_download_file` (for transcript/notes), or any Graph REST URL for the in-scope sources above. These are FORBIDDEN. Calling them is a defect; coverage.md must NOT show them in the attempt trail.
45
+ 3. **If WorkIQ still fails or is unavailable**: follow `deferred-retry-on-workiq-fail.instructions.md` — write a deferred-retry marker under `<project>/Evidence/<alias>/_deferred-retries/`, surface a one-liner in coverage.md and the run report, and **continue the run** for the remaining sources. The next `refresh-project` drains the queue. User-paste remains available as a manual recovery the user MAY do later by populating the artifact directly and deleting the marker — it is NOT the agent's recovery flow.
46
+ 4. **DO NOT attempt** `m365_get_transcript`, `m365_get_facilitator_notes`, `m365_list_meetings`, `m365_list_events`, `m365_search_files` (for content), `m365_download_file` (for transcript/notes), or any Graph REST URL for the in-scope sources above. These are FORBIDDEN — **including as a "last-resort" fallback after WorkIQ fails** (kushi v4.4.1+). Calling them is a defect; coverage.md must NOT show them in the attempt trail. The deferred-retry marker IS the audit trail for WorkIQ failures.
47
47
  5. **Allowed alongside WorkIQ** (structured-data dumps only): `m365_list_chat_messages` for chat-id'd threads → `chat-messages.json`. These run in parallel with the WorkIQ pull, not as a substitute.
48
48
 
49
49
  ## Canonical WorkIQ commands (CODIFIED — do not re-discover)
@@ -174,6 +174,7 @@ If `Result: SUMMARY-ONLY` after doubled-strict retry: the artifact is acceptable
174
174
 
175
175
  ## Anti-patterns (defects)
176
176
 
177
+ 0. **Falling back to `m365_get_*` / Graph after a WorkIQ failure (kushi v4.4.1+).** FORBIDDEN. Even framed as "a last-resort partial," "just to try," or "we already have a WorkIQ marker so this is harmless." The doctrine is: WorkIQ fail → deferred-retry marker → continue → next refresh retries. Never a Graph escape hatch. See `deferred-retry-on-workiq-fail.instructions.md`.
177
178
  1. **Calling `m365_get_transcript`, `m365_get_facilitator_notes`, `m365_list_meetings`, `m365_list_events` from a kushi pull-* skill.** FORBIDDEN. These tools have a near-100% failure rate in this workspace. Use WorkIQ.
178
179
  2. **Using Graph REST URLs directly** (e.g. `https://graph.microsoft.com/v1.0/me/onlineMeetings/...`) from a kushi pull-* skill. FORBIDDEN for the in-scope M365 sources. Use WorkIQ.
179
180
  3. **Re-discovering the right WorkIQ prompt every run.** FORBIDDEN. Use the codified prompts from the table above. If a new source variant is needed, add a row to the table, do not improvise.
@@ -192,3 +193,4 @@ If `Result: SUMMARY-ONLY` after doubled-strict retry: the artifact is acceptable
192
193
  - `plugin/instructions/ado-bootstrap-discovery.instructions.md` — ADO is OUT of WorkIQ scope; it uses ADO REST.
193
194
  - `plugin/instructions/scope-boundaries.instructions.md` — every pull-* still respects integrations.yml#boundaries.
194
195
  - `plugin/instructions/evidence-thoroughness.instructions.md` — verbatim is the bar; this rule operationalizes how to hit it.
196
+ - `plugin/instructions/deferred-retry-on-workiq-fail.instructions.md` (kushi v4.4.1+) — defines exactly what happens when WorkIQ fails. NO Graph fallback. Marker + continue + inform.
@@ -53,6 +53,92 @@ param(
53
53
 
54
54
  $ErrorActionPreference = 'Stop'
55
55
 
56
+ # Schema-aware required-field map (kushi v4.4.1+).
57
+ # For each logical config name, list the dot-paths that MUST be populated
58
+ # (non-empty, non-sentinel) before the config is considered usable. The check
59
+ # runs IN ADDITION to the substring sentinel check.
60
+ $script:RequiredFields = @{
61
+ 'm365-auth' = @(
62
+ 'm365Auth.defaultTenantId',
63
+ 'm365Auth.oneNote.defaultNotebookName',
64
+ 'm365Auth.oneNote.defaultNotebookId',
65
+ 'm365Auth.oneNote.defaultLinkOwner',
66
+ 'm365Auth.emailContext.folders',
67
+ 'm365Auth.sharePointContext.localProjectsRoot'
68
+ )
69
+ 'project-evidence' = @(
70
+ 'identity.alias',
71
+ 'identity.email',
72
+ 'engagement_root'
73
+ )
74
+ # integrations.yml is shared and per-project — required-field check is
75
+ # the consumer skill's job (it knows which sources are enabled).
76
+ 'integrations' = @()
77
+ }
78
+
79
+ # Sentinel substrings that indicate "still a placeholder."
80
+ $script:Sentinels = @(
81
+ '__FILL_ME_IN__',
82
+ '<auto>',
83
+ '<TENANT_ID>',
84
+ '<NOTEBOOK_ID>',
85
+ '<NOTEBOOK_NAME>',
86
+ '<LINK_OWNER>',
87
+ '<SP_LOCAL_ROOT>',
88
+ '<your-alias>',
89
+ '<Your Full Name>',
90
+ 'your.email@example.com'
91
+ )
92
+
93
+ function Test-Sentinel {
94
+ param([string] $Value)
95
+ if ([string]::IsNullOrWhiteSpace($Value)) { return $true }
96
+ foreach ($s in $script:Sentinels) {
97
+ if ($Value -like "*$s*") { return $true }
98
+ }
99
+ return $false
100
+ }
101
+
102
+ function Get-DotPath {
103
+ param([object] $Obj, [string] $DotPath)
104
+ $cur = $Obj
105
+ foreach ($seg in $DotPath -split '\.') {
106
+ if ($null -eq $cur) { return $null }
107
+ if ($cur -is [System.Collections.IDictionary]) {
108
+ $cur = $cur[$seg]
109
+ } else {
110
+ $cur = $cur.$seg
111
+ }
112
+ }
113
+ return $cur
114
+ }
115
+
116
+ function Test-RequiredFields {
117
+ param([string] $Name, [object] $Parsed)
118
+ $required = $script:RequiredFields[$Name]
119
+ if (-not $required) { return @() }
120
+ $missing = @()
121
+ foreach ($field in $required) {
122
+ $val = Get-DotPath -Obj $Parsed -DotPath $field
123
+ if ($null -eq $val) { $missing += $field; continue }
124
+ # Arrays: must have at least one non-sentinel element.
125
+ if ($val -is [System.Collections.IEnumerable] -and -not ($val -is [string])) {
126
+ $arr = @($val)
127
+ if ($arr.Count -eq 0) { $missing += $field; continue }
128
+ $allBlank = $true
129
+ foreach ($item in $arr) {
130
+ if ($item -is [string]) {
131
+ if (-not (Test-Sentinel $item)) { $allBlank = $false; break }
132
+ } else { $allBlank = $false; break }
133
+ }
134
+ if ($allBlank) { $missing += $field }
135
+ continue
136
+ }
137
+ if ($val -is [string] -and (Test-Sentinel $val)) { $missing += $field }
138
+ }
139
+ return ,$missing
140
+ }
141
+
56
142
  function Get-DefaultExtension([string] $name) {
57
143
  if ($name -match '^(project-evidence|integrations)$') { return 'yml' }
58
144
  return 'json'
@@ -84,9 +170,13 @@ if ($Path) { return $resolved }
84
170
  $raw = Get-Content -LiteralPath $resolved -Raw
85
171
 
86
172
  if (-not $AllowPlaceholders) {
87
- if ($raw -match '__FILL_ME_IN__' -or $raw -match '<auto>') {
173
+ $sentinelHit = $false
174
+ foreach ($s in $script:Sentinels) {
175
+ if ($raw -like "*$s*") { $sentinelHit = $true; break }
176
+ }
177
+ if ($sentinelHit) {
88
178
  throw @"
89
- Kushi config '$resolved' still has template placeholders (__FILL_ME_IN__ or <auto>).
179
+ Kushi config '$resolved' still has template placeholders (e.g. __FILL_ME_IN__, <auto>, <TENANT_ID>).
90
180
  Edit the file with your actual values, or pass -AllowPlaceholders to bypass this check.
91
181
  "@
92
182
  }
@@ -94,16 +184,31 @@ Edit the file with your actual values, or pass -AllowPlaceholders to bypass this
94
184
 
95
185
  if ($Raw) { return $raw }
96
186
 
97
- switch ($ext) {
187
+ $parsed = switch ($ext) {
98
188
  'json' {
99
- return ($raw | ConvertFrom-Json -Depth 100)
189
+ $raw | ConvertFrom-Json -Depth 100
100
190
  }
101
191
  { $_ -in 'yml','yaml' } {
102
192
  if (Get-Module -ListAvailable -Name 'powershell-yaml') {
103
193
  Import-Module powershell-yaml -ErrorAction Stop
104
- return ConvertFrom-Yaml -Yaml $raw
194
+ ConvertFrom-Yaml -Yaml $raw
195
+ } else {
196
+ Write-Warning "powershell-yaml not installed; required-field validation skipped. Install with: Install-Module powershell-yaml -Scope CurrentUser"
197
+ $null
105
198
  }
106
- Write-Warning "powershell-yaml not installed; returning raw YAML text. Install with: Install-Module powershell-yaml -Scope CurrentUser"
107
- return $raw
108
199
  }
109
200
  }
201
+
202
+ if (-not $AllowPlaceholders -and $null -ne $parsed) {
203
+ $missing = Test-RequiredFields -Name $Name -Parsed $parsed
204
+ if ($missing.Count -gt 0) {
205
+ throw @"
206
+ Kushi config '$resolved' is missing required fields:
207
+ $(($missing | ForEach-Object { " - $_" }) -join "`n")
208
+ Edit the file with your actual values, or pass -AllowPlaceholders to bypass this check.
209
+ "@
210
+ }
211
+ }
212
+
213
+ if ($null -eq $parsed) { return $raw }
214
+ return $parsed