kushi-agents 3.4.2 → 3.12.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.
Files changed (69) hide show
  1. package/README.md +33 -0
  2. package/package.json +15 -3
  3. package/plugin/agents/kushi.agent.md +155 -147
  4. package/plugin/instructions/ado-bootstrap-discovery.instructions.md +111 -0
  5. package/plugin/instructions/ado-engagement-tree.instructions.md +73 -0
  6. package/plugin/instructions/answer-from-evidence.instructions.md +1 -1
  7. package/plugin/instructions/auth-and-retry.instructions.md +51 -16
  8. package/plugin/instructions/azure-auth-patterns.instructions.md +13 -6
  9. package/plugin/instructions/bootstrap-status-format.instructions.md +113 -0
  10. package/plugin/instructions/capture-learnings.instructions.md +95 -0
  11. package/plugin/instructions/cleanup-on-resolution.instructions.md +69 -0
  12. package/plugin/instructions/crm-bootstrap-discovery.instructions.md +79 -0
  13. package/plugin/instructions/crm-internal-vs-confirmed.instructions.md +79 -0
  14. package/plugin/instructions/evidence-confidence-ladder.instructions.md +66 -0
  15. package/plugin/instructions/evidence-layout-canonical.instructions.md +115 -0
  16. package/plugin/instructions/evidence-thoroughness.instructions.md +82 -12
  17. package/plugin/instructions/full-view-gate.instructions.md +91 -0
  18. package/plugin/instructions/m365-id-registry.instructions.md +134 -0
  19. package/plugin/instructions/meetings-verbatim-required.instructions.md +176 -0
  20. package/plugin/instructions/run-reports.instructions.md +129 -0
  21. package/plugin/instructions/scope-boundaries.instructions.md +218 -0
  22. package/plugin/instructions/snapshot-vs-stream.instructions.md +2 -0
  23. package/plugin/instructions/update-ledger.instructions.md +132 -0
  24. package/plugin/instructions/verbatim-by-default.instructions.md +73 -0
  25. package/plugin/instructions/workiq-first.instructions.md +15 -31
  26. package/plugin/instructions/workiq-only.instructions.md +193 -0
  27. package/plugin/learnings/README.md +50 -0
  28. package/plugin/learnings/ado.md +45 -0
  29. package/plugin/learnings/crm.md +96 -0
  30. package/plugin/learnings/cross-cutting.md +36 -0
  31. package/plugin/learnings/email.md +33 -0
  32. package/plugin/learnings/meetings.md +30 -0
  33. package/plugin/learnings/misc.md +46 -0
  34. package/plugin/learnings/onenote.md +215 -0
  35. package/plugin/learnings/sharepoint.md +5 -0
  36. package/plugin/learnings/teams.md +5 -0
  37. package/plugin/plugin.json +22 -2
  38. package/plugin/prompts/apply-ado.prompt.md +14 -0
  39. package/plugin/prompts/propose-ado.prompt.md +12 -0
  40. package/plugin/reference-packs/fde/crm-field-manifest.md +165 -0
  41. package/plugin/skills/apply-ado-update/SKILL.md +125 -0
  42. package/plugin/skills/ask-project/SKILL.md +2 -0
  43. package/plugin/skills/bootstrap-project/SKILL.md +81 -3
  44. package/plugin/skills/propose-ado-update/SKILL.md +108 -0
  45. package/plugin/skills/pull-ado/SKILL.md +173 -23
  46. package/plugin/skills/pull-crm/SKILL.md +168 -15
  47. package/plugin/skills/pull-email/SKILL.md +139 -22
  48. package/plugin/skills/pull-meetings/SKILL.md +109 -25
  49. package/plugin/skills/pull-misc/README.md +84 -0
  50. package/plugin/skills/pull-misc/SKILL.md +257 -0
  51. package/plugin/skills/pull-misc/runner.mjs +280 -0
  52. package/plugin/skills/pull-onenote/README.md +90 -0
  53. package/plugin/skills/pull-onenote/SKILL.md +400 -51
  54. package/plugin/skills/pull-onenote/runner.mjs +356 -0
  55. package/plugin/skills/pull-onenote/scripts/recapture-section-url.mjs +295 -0
  56. package/plugin/skills/pull-onenote/write-snapshot.mjs +271 -0
  57. package/plugin/skills/pull-sharepoint/SKILL.md +44 -12
  58. package/plugin/skills/pull-teams/SKILL.md +40 -11
  59. package/plugin/skills/refresh-project/SKILL.md +33 -2
  60. package/plugin/skills/self-check/run.ps1 +186 -4
  61. package/plugin/templates/ado-update/discussion-comment.template.md +26 -0
  62. package/plugin/templates/ado-update/integrations-ado-writes.example.yml +49 -0
  63. package/plugin/templates/ado-update/proposed.template.md +78 -0
  64. package/plugin/templates/init/external-links.template.txt +30 -0
  65. package/plugin/templates/init/project-integrations.template.yml +57 -2
  66. package/plugin/templates/snapshot/meeting-verbatim.template.md +110 -0
  67. package/plugin/templates/snapshot/meetings-series-index.template.md +3 -1
  68. package/plugin/templates/snapshot/onenote-page.template.md +92 -23
  69. package/plugin/templates/weekly/meetings-stream.template.md +11 -6
@@ -354,12 +354,12 @@ if ($Deep) {
354
354
  Add-Finding D2 'Snapshot/Stream' 'warning' "Skill $($d.Name) doesn't cite snapshot-vs-stream.instructions.md" "Add a reference (e.g. 'per snapshot-vs-stream.instructions.md') in the skill body." $f 0
355
355
  }
356
356
  }
357
- # D3: pull-* must put WorkIQ first
357
+ # D3: pull-* must list WorkIQ in Tools section (order is the skill's choice; some skills correctly prefer REST/host first when WorkIQ summarizes — see pull-crm/email/onenote v3.7.x)
358
358
  foreach ($d in $skillDirs | Where-Object { $_.Name -like 'pull-*' }) {
359
359
  $f = Join-Path $d.FullName 'SKILL.md'
360
360
  $text = $mdText[$f]
361
- if ($text -notmatch '(?ms)1\.\s*\*\*WorkIQ\*\*') {
362
- Add-Finding D3 'WorkIQ-first' 'warning' "Skill $($d.Name) doesn't list **WorkIQ** as tool #1" "Restructure the Tools section so WorkIQ is item 1." $f 0
361
+ if ($text -notmatch '(?ms)\bWorkIQ\b') {
362
+ Add-Finding D3 'WorkIQ-listed' 'warning' "Skill $($d.Name) doesn't reference **WorkIQ** in its Tools section" "Add WorkIQ to the Tools section (order is per-skill; verbatim-required skills may correctly prefer REST/host first)." $f 0
363
363
  }
364
364
  }
365
365
  # D4: live install SKILL.md = agent file
@@ -450,6 +450,140 @@ if ($Deep) {
450
450
  }
451
451
  }
452
452
 
453
+ # D9: v3.7.6 learnings register — required files present + non-empty
454
+ $learnDir = Join-Path $pluginDir 'learnings'
455
+ if (-not (Test-Path $learnDir)) {
456
+ Add-Finding D9 'Learnings register' 'warning' "plugin/learnings/ directory missing — required by capture-learnings.instructions.md (v3.7.6)" "Create plugin/learnings/ with README + per-source files." $learnDir 0
457
+ } else {
458
+ $expectedLearnFiles = @('README.md','ado.md','crm.md','email.md','teams.md','meetings.md','onenote.md','sharepoint.md','cross-cutting.md')
459
+ foreach ($lf in $expectedLearnFiles) {
460
+ $lp = Join-Path $learnDir $lf
461
+ if (-not (Test-Path $lp)) {
462
+ Add-Finding D9 'Learnings register' 'warning' "Missing learnings file: $lf" "Create plugin/learnings/$lf with at minimum the section template from README.md" $lp 0
463
+ } elseif ((Get-Item $lp).Length -lt 50) {
464
+ Add-Finding D9 'Learnings register' 'warning' "Learnings file $lf is empty or stub" "Seed with at least one capture-template entry." $lp 0
465
+ }
466
+ }
467
+ }
468
+
469
+ # D10: v3.7.6 doctrine instructions — required files present + applyTo glob set
470
+ $reqInstructions = @(
471
+ 'capture-learnings.instructions.md',
472
+ 'cleanup-on-resolution.instructions.md',
473
+ 'run-reports.instructions.md',
474
+ 'verbatim-by-default.instructions.md'
475
+ )
476
+ $instrDir = Join-Path $pluginDir 'instructions'
477
+ foreach ($ri in $reqInstructions) {
478
+ $ip = Join-Path $instrDir $ri
479
+ if (-not (Test-Path $ip)) {
480
+ Add-Finding D10 'v3.7.6 doctrine' 'warning' "Required v3.7.6 instruction missing: $ri" "Create plugin/instructions/$ri with applyTo + HARD rule body." $ip 0
481
+ } else {
482
+ $fm = Get-Frontmatter $ip
483
+ if (-not $fm.applyTo) {
484
+ Add-Finding D10 'v3.7.6 doctrine' 'warning' "$ri missing applyTo in frontmatter" "Add ``applyTo: ""**""`` to frontmatter." $ip 0
485
+ }
486
+ }
487
+ }
488
+
489
+ # D11: pull-* skills SKILL.md cite verbatim-by-default contract in front blockquote
490
+ $pullSkillDirs = Get-ChildItem -Path (Join-Path $pluginDir 'skills') -Directory | Where-Object { $_.Name -like 'pull-*' -or $_.Name -in @('bootstrap-project','refresh-project') }
491
+ foreach ($sd in $pullSkillDirs) {
492
+ $sf = Join-Path $sd.FullName 'SKILL.md'
493
+ if (Test-Path $sf) {
494
+ $sc = Get-Content $sf -Raw
495
+ if ($sc -notmatch 'verbatim-by-default') {
496
+ Add-Finding D11 'Verbatim contract' 'warning' "$($sd.Name)/SKILL.md does not reference verbatim-by-default doctrine" "Add a one-line cite in the front blockquote: 'Honors verbatim-by-default.instructions.md (v3.7.6).'" $sf 0
497
+ }
498
+ if ($sc -notmatch 'capture-learnings|cleanup-on-resolution|run-reports') {
499
+ Add-Finding D11 'v3.7.6 contracts' 'warning' "$($sd.Name)/SKILL.md does not reference v3.7.6 process contracts (capture-learnings / cleanup-on-resolution / run-reports)" "Add the v3.7.6 contracts blockquote near the top of SKILL.md." $sf 0
500
+ }
501
+ }
502
+ }
503
+
504
+ # D12: discover-once / consume-deterministically — pull-* skills cite m365-id-registry; pull-onenote carries v2.5.0 retry-registry + EULA contracts (kushi v3.7.9)
505
+ $idRegistryInst = Join-Path $pluginDir 'instructions\m365-id-registry.instructions.md'
506
+ if (-not (Test-Path $idRegistryInst)) {
507
+ Add-Finding D12 'ID registry doctrine' 'warning' "plugin/instructions/m365-id-registry.instructions.md is missing" "Restore the m365-id-registry instruction from kushi v3.7.8." $idRegistryInst 0
508
+ }
509
+ $registryConsumers = @(
510
+ @{ name='bootstrap-project'; required=@('m365-id-registry','knownSections') },
511
+ @{ name='refresh-project'; required=@('m365-id-registry','knownSections') },
512
+ @{ name='pull-onenote'; required=@('m365-id-registry','one_pages', 'webPageId', 'auth-required', 'playwright-profile') },
513
+ @{ name='pull-misc'; required=@('m365-id-registry','external-links', 'misc_links', 'placeholder', 'delegated') }
514
+ )
515
+ foreach ($rc in $registryConsumers) {
516
+ $sf = Join-Path $pluginDir "skills\$($rc.name)\SKILL.md"
517
+ if (Test-Path $sf) {
518
+ $sc = Get-Content $sf -Raw
519
+ foreach ($needle in $rc.required) {
520
+ if ($sc -notmatch [regex]::Escape($needle)) {
521
+ Add-Finding D12 'ID registry contract' 'warning' "$($rc.name)/SKILL.md is missing required token '$needle'" "Add the canonical reference per m365-id-registry.instructions.md (and pull-onenote v2.5.0 contract for retry registry / EULA pre-flight)." $sf 0
522
+ }
523
+ }
524
+ }
525
+ }
526
+
527
+
528
+ # D13: meetings verbatim/ folder required per meeting block (v3.10.0)
529
+ # Walks every Evidence/*/meetings/stream/*.md, extracts per-meeting headings,
530
+ # and verifies a sibling Evidence/*/meetings/verbatim/<YYYY-MM-DD-HHMM>_<slug>/
531
+ # directory exists with at least one non-empty content file beyond captured-at.txt.
532
+ $verbatimInst = Join-Path $pluginDir 'instructions\meetings-verbatim-required.instructions.md'
533
+ if (-not (Test-Path $verbatimInst)) {
534
+ Add-Finding D13 'Meetings verbatim doctrine' 'warning' "plugin/instructions/meetings-verbatim-required.instructions.md is missing" "Restore the meetings-verbatim-required instruction from kushi v3.10.0." $verbatimInst 0
535
+ }
536
+ # Walk known engagement roots to find Evidence/*/meetings/stream/*.md files
537
+ $engagementRoots = @()
538
+ $envRoot = $env:KUSHI_ENGAGEMENT_ROOT
539
+ if ($envRoot -and (Test-Path $envRoot)) { $engagementRoots += $envRoot }
540
+ $defaultEngRoot = Join-Path $env:USERPROFILE 'OneDrive - Microsoft\ISE\Engagement Assets'
541
+ if (Test-Path $defaultEngRoot) { $engagementRoots += $defaultEngRoot }
542
+ foreach ($engRoot in ($engagementRoots | Select-Object -Unique)) {
543
+ $streamFiles = Get-ChildItem -Path $engRoot -Recurse -Filter '*_meetings-stream.md' -ErrorAction SilentlyContinue |
544
+ Where-Object { $_.FullName -match '\\Evidence\\[^\\]+\\(meetings|Meetings)\\stream\\' }
545
+ foreach ($sf in $streamFiles) {
546
+ $aliasDir = Split-Path (Split-Path (Split-Path $sf.FullName -Parent) -Parent) -Parent
547
+ $verbatimDir = Join-Path (Split-Path (Split-Path $sf.FullName -Parent) -Parent) 'verbatim'
548
+ $content = Get-Content $sf.FullName -Raw
549
+ # Look for per-meeting headings, accepting either order:
550
+ # "## <title> — <YYYY-MM-DD>" (templates/weekly default)
551
+ # "## <YYYY-MM-DD> — <title>" (date-first variant used in some streams)
552
+ $meetingHeads = [regex]::Matches($content, '(?m)^#{2,3}\s+(?:(?<title>.+?)\s+—\s+(?<date>\d{4}-\d{2}-\d{2})|(?<date2>\d{4}-\d{2}-\d{2})\s+—\s+(?<title2>.+?))(?:\s*\(|\s*,|\s*$)')
553
+ foreach ($mh in $meetingHeads) {
554
+ $title = if ($mh.Groups['title'].Success) { $mh.Groups['title'].Value.Trim() } else { $mh.Groups['title2'].Value.Trim() }
555
+ $date = if ($mh.Groups['date'].Success) { $mh.Groups['date'].Value } else { $mh.Groups['date2'].Value }
556
+ # Strip trailing "(HH:MM–HH:MM)" or similar
557
+ $title = ($title -replace '\s*\([^)]*\)\s*$','').Trim()
558
+ # Skip placeholder/template heads
559
+ if ($title -match '^\{\{' -or $title -match 'NEXT_MEETING_TITLE') { continue }
560
+ $slug = ($title.ToLower() -replace '[^a-z0-9]+','-').Trim('-')
561
+ if ($slug.Length -gt 60) { $slug = $slug.Substring(0,60).TrimEnd('-') }
562
+ $expectedPattern = "$date-*_$slug"
563
+ $matches = if (Test-Path $verbatimDir) {
564
+ Get-ChildItem -Path $verbatimDir -Directory -Filter $expectedPattern -ErrorAction SilentlyContinue
565
+ } else { @() }
566
+ if ($matches.Count -eq 0) {
567
+ Add-Finding D13 'Meetings verbatim' 'warning' "Per-meeting block '$title' ($date) in $($sf.Name) has no sibling verbatim/<$date-HHMM>_$slug/ directory" "Re-run pull-meetings Half A for this meeting per meetings-verbatim-required.instructions.md. Verbatim folder must contain chat-messages.json + .md + transcript.* (or transcript-source.md) + recording-url.txt + captured-at.txt + coverage.md." $sf.FullName 0
568
+ } else {
569
+ foreach ($vd in $matches) {
570
+ $vdFiles = Get-ChildItem -Path $vd.FullName -File -ErrorAction SilentlyContinue
571
+ $contentFiles = @($vdFiles | Where-Object { $_.Name -ne 'captured-at.txt' -and $_.Name -ne 'coverage.md' -and $_.Length -gt 0 })
572
+ if ($contentFiles.Count -eq 0) {
573
+ Add-Finding D13 'Meetings verbatim' 'warning' "Verbatim folder $($vd.Name) is empty (only captured-at.txt / coverage.md)" "Capture cascade failed silently. Re-run pull-meetings Half A; if all paths still fail, document in coverage.md and ask user to paste." $vd.FullName 0
574
+ } else {
575
+ # D13.c — transcript-class file required (transcript.vtt | transcript.txt | transcript-source.md)
576
+ $transcriptFiles = @($vdFiles | Where-Object { $_.Name -in @('transcript.vtt','transcript.txt','transcript-source.md') -and $_.Length -gt 0 })
577
+ if ($transcriptFiles.Count -eq 0) {
578
+ Add-Finding D13 'Meetings verbatim' 'warning' "Verbatim folder $($vd.Name) has chat but NO transcript-class file (transcript.vtt | transcript.txt | transcript-source.md)" "Run the exhaustive transcript cascade per meetings-verbatim-required.instructions.md: m365_get_transcript → Graph REST /onlineMeetings/.../transcripts/.../content → m365_get_facilitator_notes → WorkIQ strict full-text pull → recording download. Chat alone is NOT a transcript. If all paths fail, document in coverage.md and prompt user to paste — paste lands in transcript.txt." $vd.FullName 0
579
+ }
580
+ }
581
+ }
582
+ }
583
+ }
584
+ }
585
+ }
586
+
453
587
  # D8: live templates/fde/ matches repo
454
588
  $liveFdeTpl = Join-Path $env:USERPROFILE '.copilot\m-skills\kushi\templates\fde'
455
589
  $repoFdeTpl = Join-Path $pluginDir 'templates\fde'
@@ -473,6 +607,54 @@ if ($Deep) {
473
607
  }
474
608
  }
475
609
  }
610
+
611
+ # D14: evidence-layout-canonical — no non-canonical source-output folders under <project>/
612
+ # Per evidence-layout-canonical.instructions.md: every per-source artifact MUST live under
613
+ # <project>/Evidence/<alias>/<source>/. Sibling folders under <project>/ that contain
614
+ # source-output markdown (email-context/, notes/, _Weekly Summaries/, <source>/, ...) are defects.
615
+ $layoutInst = Join-Path $pluginDir 'instructions\evidence-layout-canonical.instructions.md'
616
+ if (-not (Test-Path $layoutInst)) {
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
+ }
619
+ $allowedSiblings = @('Evidence','State','Reports','.kushi','.kushi-reference','.project-evidence','.vscode','.git')
620
+ $sourceTokens = @(
621
+ 'email-context','email-summary','email-summaries',
622
+ 'teams-context','teams-summary','teams-summaries',
623
+ 'meeting-context','meetings-context','meeting-notes','meetings-notes',
624
+ 'onenote-context','onenote-notes','notes',
625
+ 'sharepoint-context','sp-context',
626
+ 'crm-context','ado-context',
627
+ '_Weekly Summaries','_WeeklySummaries','weekly-summaries',
628
+ 'email','teams','meetings','onenote','sharepoint','crm','ado'
629
+ )
630
+ $layoutEngRoots = @()
631
+ if ($env:KUSHI_ENGAGEMENT_ROOT -and (Test-Path $env:KUSHI_ENGAGEMENT_ROOT)) { $layoutEngRoots += $env:KUSHI_ENGAGEMENT_ROOT }
632
+ $layoutDefaultRoot = Join-Path $env:USERPROFILE 'OneDrive - Microsoft\ISE\Engagement Assets'
633
+ if (Test-Path $layoutDefaultRoot) { $layoutEngRoots += $layoutDefaultRoot }
634
+ foreach ($engRoot in ($layoutEngRoots | Select-Object -Unique)) {
635
+ $projectDirs = Get-ChildItem -Path $engRoot -Directory -ErrorAction SilentlyContinue |
636
+ Where-Object { Test-Path (Join-Path $_.FullName 'Evidence') }
637
+ foreach ($pd in $projectDirs) {
638
+ $siblings = Get-ChildItem -Path $pd.FullName -Directory -ErrorAction SilentlyContinue |
639
+ Where-Object { $allowedSiblings -notcontains $_.Name }
640
+ foreach ($sib in $siblings) {
641
+ $sibName = $sib.Name
642
+ $isSourceLike = $false
643
+ foreach ($tok in $sourceTokens) {
644
+ if ($sibName -ieq $tok) { $isSourceLike = $true; break }
645
+ }
646
+ if (-not $isSourceLike) {
647
+ # Heuristic: also flag folders containing *-summary.md / *-stream.md / current-state.md / index.md
648
+ $mdHits = Get-ChildItem -Path $sib.FullName -Recurse -File -Include '*-summary.md','*-stream.md','current-state.md','index.md' -ErrorAction SilentlyContinue | Select-Object -First 1
649
+ if ($mdHits) { $isSourceLike = $true }
650
+ }
651
+ if ($isSourceLike) {
652
+ $canonical = "$($pd.FullName)\Evidence\<alias>\<source>\(snapshot|stream|_legacy_${sibName}_pre-bootstrap)\"
653
+ Add-Finding D14 'Evidence layout' 'warning' "Non-canonical source folder '$sibName' under project '$($pd.Name)' — violates evidence-layout-canonical.instructions.md" "Move contents to $canonical (preserve mtimes; do not delete). Log under '## Layout migrations' in next refresh report; add runs: entry with type: layout-migration in Evidence/run-log.yml." $sib.FullName 0
654
+ }
655
+ }
656
+ }
657
+ }
476
658
  }
477
659
 
478
660
  # === Output ===
@@ -514,4 +696,4 @@ if ($Json) {
514
696
  }
515
697
 
516
698
  if ($StrictExit -and $findings.Count -gt 0) { exit 1 }
517
- exit 0
699
+ exit 0
@@ -0,0 +1,26 @@
1
+ **Kushi weekly update — {{consolidatedDate}}**
2
+
3
+ **TL;DR:** {{tldr}}
4
+
5
+ **Decisions this week**
6
+
7
+ {{decisionsList}}
8
+
9
+ **Action items**
10
+
11
+ {{actionItemsList}}
12
+
13
+ **Risks / blockers**
14
+
15
+ {{risksList}}
16
+
17
+ **Asks**
18
+
19
+ {{asksList}}
20
+
21
+ **Evidence pack**
22
+
23
+ - [Consolidated week of {{consolidatedDate}}]({{consolidatedLink}})
24
+ - {{numCitedSources}} cited sources
25
+
26
+ — Generated by Kushi v{{kushiVersion}} · proposed {{generatedDate}} · ledger:ado-updates/{{generatedDate}}/ledger.jsonl
@@ -0,0 +1,49 @@
1
+ # Per-project ADO write block — append this to `<project>/integrations.yml` under the existing `ado:` key.
2
+ # This template is the "writes" sub-block ONLY; the existing `engagement_id`, `area_path`, `tag`,
3
+ # `title_filter`, `discovery_hints` keys stay as they are. propose-ado-update + apply-ado-update
4
+ # read from the SAME integrations.yml that pull-ado already uses — no separate config file.
5
+ #
6
+ # Global ADO connection (tenantId, organization, apiVersion, auth) lives at:
7
+ # <engagement-root>/.project-evidence/ado/config.yml
8
+ # Do NOT duplicate those keys here.
9
+
10
+ ado:
11
+ # --- existing keys (managed by pull-ado / bootstrap) ---
12
+ engagement_id: 12345 # REQUIRED for writes. Must be a real ADO Initiative work-item ID.
13
+ area_path: ''
14
+ tag: ''
15
+ title_filter: '<project>'
16
+
17
+ # --- new "writes" sub-block (read by propose-ado-update + apply-ado-update) ---
18
+ writes:
19
+ statusSummary:
20
+ enabled: true # produce a field-update item in proposed.md
21
+ fieldRefName: "Custom.FDEStatusSummary"
22
+ # Strategy options:
23
+ # append-month (default) — append "<MMM YYYY>: <one-liner>" if no entry exists for this month
24
+ # replace — single-line replacement
25
+ # rolling-6 — keep last 6 month lines, prepend new
26
+ strategy: "append-month"
27
+ # autoApply: when true AND confidence == "high" AND value matches the strategy pattern,
28
+ # apply-ado-update will write without interactive approval (logged as auto:<rule>).
29
+ # Safe to start true for append-month: format is narrow and the test-on-rev guards drift.
30
+ autoApply: true
31
+
32
+ discussionComment:
33
+ enabled: true # produce a comment-update item in proposed.md
34
+ # autoApply: discussion comments are content-rich and harder to validate by pattern.
35
+ # Default false — flip to true only after several weeks of clean dry-runs.
36
+ autoApply: false
37
+
38
+ approvals:
39
+ # defaultGate: "review" (always prompt) | "auto" (apply if every item meets autoApply rules above)
40
+ defaultGate: "review"
41
+ # notifyOnApply: post a one-line summary to the user via Teams DM after a successful apply run.
42
+ # Options: "teams" | "off"
43
+ notifyOnApply: "teams"
44
+
45
+ # Hard allowlist — apply-ado-update will refuse to write any field reference name not in this list,
46
+ # even if it appears in proposed.md. This is the last-line guard against a malformed proposal.
47
+ allowlist:
48
+ fields:
49
+ - "Custom.FDEStatusSummary"
@@ -0,0 +1,78 @@
1
+ # Proposed ADO update — {{project}}
2
+
3
+ > Generated by `propose-ado-update` v{{kushiVersion}} · {{generatedDate}}
4
+ > Source: `<project>/Evidence/_Consolidated/{{consolidatedDate}}_consolidated.md`
5
+ > Initiative (from `integrations.yml ado.engagement_id`): [#{{engagementId}}]({{adoLink}})
6
+ >
7
+ > **No ADO writes have happened.** Review below, then run `@Kushi apply ado {{project}}` to (in v0.1.0-preview) write a planned.jsonl, or (when the write path lands) actually apply the approved items.
8
+
9
+ {{staleEvidenceWarning}}
10
+
11
+ ---
12
+
13
+ ## 1. Field update — `{{fieldRefName}}`
14
+
15
+ **Strategy:** `{{strategy}}` · **Confidence:** `{{fieldConfidence}}` · **Auto-apply eligible:** `{{fieldAutoApplyEligible}}`
16
+
17
+ **Current value (live from ADO):**
18
+
19
+ ```text
20
+ {{currentSummary}}
21
+ ```
22
+
23
+ **Proposed new value:**
24
+
25
+ ```text
26
+ {{proposedSummary}}
27
+ ```
28
+
29
+ **Diff:**
30
+
31
+ ```diff
32
+ {{summaryDiff}}
33
+ ```
34
+
35
+ **Citations:**
36
+
37
+ {{fieldCitations}}
38
+
39
+ ---
40
+
41
+ ## 2. Discussion comment
42
+
43
+ **Confidence:** `{{commentConfidence}}` · **Auto-apply eligible:** `{{commentAutoApplyEligible}}`
44
+
45
+ ```markdown
46
+ {{discussionCommentRendered}}
47
+ ```
48
+
49
+ **Per-bullet citations:**
50
+
51
+ {{commentCitations}}
52
+
53
+ ---
54
+
55
+ ## Apply
56
+
57
+ ```text
58
+ @Kushi apply ado {{project}}
59
+ ```
60
+
61
+ This will (in v0.1.0-preview):
62
+
63
+ - Re-fetch live ADO state and verify no drift since this proposal.
64
+ - Check for duplicate Kushi comments on the same work item this week.
65
+ - Prompt for approval (or apply auto-allowlisted items per `.kushi/ado-update.yml`).
66
+ - Write `ado-updates/{{generatedDate}}/planned.jsonl` (preview-stub) — no real ADO calls.
67
+
68
+ When the write path lands in a follow-up release:
69
+
70
+ - Apply approved items via ADO REST (PATCH for field, POST for comment).
71
+ - Append to `ado-updates/{{generatedDate}}/ledger.jsonl` with a reverse op for every applied write.
72
+ - Optionally post a one-line Teams DM summary to the approver.
73
+
74
+ ---
75
+
76
+ ## Stats
77
+
78
+ `{{numFieldItems}} field · {{numCommentItems}} comment · {{numCitedBullets}} cited bullets · {{highCount}} high / {{medCount}} medium / {{lowCount}} low confidence`
@@ -0,0 +1,30 @@
1
+ # External Context Links - <PROJECT_NAME>
2
+ # One link per line:
3
+ # <type>|<owner>|<title>|<url-or-path>|<notes>
4
+ #
5
+ # Supported types (kushi v3.10.0+):
6
+ # onenote — delegated to pull-onenote
7
+ # sharepoint — delegated to pull-sharepoint
8
+ # ado — delegated to pull-ado
9
+ # loop — Microsoft Loop page (browser capture via persisted profile)
10
+ # web — public webpage (HTTP fetch + Readability)
11
+ # confluence — anonymous Confluence page (HTTP fetch)
12
+ # learn — learn.microsoft.com (HTTP)
13
+ # docs — generic docs site (HTTP)
14
+ # pdf — public PDF (HTTP fetch + extract)
15
+ # github — GitHub repo or file (HTTP, raw)
16
+ # file — local file under <engagement-root>/<project>/ (relative path)
17
+ #
18
+ # Rules:
19
+ # - Keep owner as a real person or team name
20
+ # - Keep title short and stable for source attribution
21
+ # - Use project-relative paths for type=file
22
+ # - Lines starting with # are comments and ignored
23
+ # - URLs containing <PASTE_*_URL> or <TODO*> are flagged as `placeholder`
24
+ #
25
+ # Example entries (delete after seeding real ones):
26
+ # onenote|Usha Krishnan|<Project> OneNote Section|<PASTE_ONENOTE_URL>|Section discovered via WorkIQ
27
+ # sharepoint|<Owner>|<Title>|<PASTE_SHAREPOINT_URL>|Workshop deck / planning doc
28
+ # loop|<Owner>|<Loop Page Title>|<PASTE_LOOP_URL>|Live planning page in Loop
29
+ # web|Microsoft|Relevant Product Documentation|<PASTE_PUBLIC_URL>|External reference material
30
+ # file|<Owner>|Action Register|action-register.md|Master action list with owner and due date
@@ -1,9 +1,61 @@
1
1
  # Per-project integration filter. Lives at: <project>\integrations.yml
2
- # Tells the skill how to find THIS project's records in the (global) CRM and ADO.
3
- # Connection / auth / field mappings are NOT here those are global.
2
+ # Tells the skill how to find THIS project's records in the (global) CRM and ADO,
3
+ # AND declares the explicit boundaries inside which Kushi is allowed to query.
4
+ #
5
+ # Connection / auth / field mappings are NOT here — those are global at:
6
+ # <engagement-root>/.project-evidence/crm/config.yml
7
+ # <engagement-root>/.project-evidence/ado/config.yml
8
+ #
9
+ # Boundaries doctrine: see plugin/instructions/scope-boundaries.instructions.md.
10
+ # Required boundary keys MUST be filled or the source is disabled (not silently widened).
4
11
 
5
12
  project: '{{PROJECT_NAME}}'
6
13
 
14
+ # ─────────────────────────────────────────────────────────────────────────────
15
+ # BOUNDARIES — partial-determinism contract (kushi v3.7.0+)
16
+ # Every WorkIQ / m365 / Graph / REST query in every pull-* skill is scoped to
17
+ # the values below. Empty required key for a source = source disabled.
18
+ # ─────────────────────────────────────────────────────────────────────────────
19
+ boundaries:
20
+ date_window_days: 30 # default lookback window for streams
21
+
22
+ email:
23
+ mailboxes: [] # required: e.g. ["Inbox", "Inbox/FDE Intake"]
24
+ sender_domains: [] # optional: narrow by sender domain
25
+ subject_keywords: [] # optional: additional narrowing
26
+
27
+ teams:
28
+ chat_ids: [] # required: pinned chat IDs (no fuzzy discovery)
29
+ channel_ids: [] # required for channel evidence (empty = none)
30
+
31
+ meetings:
32
+ series_join_urls: [] # required: pinned join URLs (not subject-keyword-fuzzy)
33
+ organizer_emails: [] # optional
34
+
35
+ onenote:
36
+ section_file_ids: [] # required (one of these): drive-item IDs of section files (.one) — narrowest, IDs survive renames
37
+ section_group_ids: [] # required (one of these): drive-item IDs of section GROUPS (folders containing sections); pull-onenote enumerates every section + page within
38
+ notebooks: [] # optional: broaden if neither sections nor section groups known
39
+
40
+ sharepoint:
41
+ local_folders: [] # required: e.g. ["C:\\…\\<Project>"]
42
+ site_urls: [] # optional: online-only docs
43
+ drive_ids: [] # optional: Graph-direct access
44
+
45
+ crm:
46
+ entity_sets: [] # optional: overrides global defaultEntitySet
47
+ record_ids: [] # required if crm enabled: pinned record GUIDs
48
+ request_ids: [] # OR pinned FE-XXXXX IDs
49
+
50
+ ado:
51
+ area_paths: [] # required if ado enabled: ["ISE-Engagements\\<Project>"]
52
+ iteration_paths: [] # optional
53
+ initiative_query: '' # optional: saved WIQL query ID
54
+ work_item_ids: [] # optional: pin specific work items
55
+
56
+ # ─────────────────────────────────────────────────────────────────────────────
57
+ # CRM resolution hints (consumed once boundaries.crm is satisfied)
58
+ # ─────────────────────────────────────────────────────────────────────────────
7
59
  crm:
8
60
  # Provide ONE of these:
9
61
  record_id: '' # e.g. 00000000-0000-0000-0000-000000000000 (preferred)
@@ -12,6 +64,9 @@ crm:
12
64
  # Optional: override default app context
13
65
  # app_id: 833534b8-5db0-4036-a029-4e4e4d7d4315
14
66
 
67
+ # ─────────────────────────────────────────────────────────────────────────────
68
+ # ADO resolution hints (consumed once boundaries.ado is satisfied)
69
+ # ─────────────────────────────────────────────────────────────────────────────
15
70
  ado:
16
71
  # Provide ONE primary locator (more specific = better):
17
72
  engagement_id: 0 # ADO Engagement work item ID (preferred)
@@ -0,0 +1,110 @@
1
+ # Meeting Verbatim Folder — Template
2
+
3
+ > **This is a verbatim/ folder template.** It defines what files belong inside `Evidence/<alias>/meetings/verbatim/<YYYY-MM-DD-HHMM>_<slug>/` per `meetings-verbatim-required.instructions.md`. The actual folder contains files, not this README.
4
+
5
+ ## Required files
6
+
7
+ ### `captured-at.txt`
8
+ ```
9
+ started_at: 2026-05-18T09:43:12-04:00
10
+ completed_at: 2026-05-18T09:43:47-04:00
11
+ kushi_version: 3.10.0
12
+ capture_paths_attempted:
13
+ - workiq: returned summary (transcript-source.md)
14
+ - m365_get_transcript: failed (Tool execution failed)
15
+ - m365_list_chat_messages: ok (22 messages → chat-messages.json)
16
+ - attachments: 1 image downloaded
17
+ - copilot_recap: not posted to chat
18
+ final_status: ok-summary-and-chat (no raw VTT)
19
+ ```
20
+
21
+ ### `coverage.md`
22
+ ```markdown
23
+ # Coverage — <meeting title> — <date>
24
+
25
+ ## What was captured
26
+ - Chat thread (N messages, all bodies verbatim) — chat-messages.json + chat-messages.md
27
+ - WorkIQ-derived Copilot summary (NOT a verbatim transcript) — transcript-source.md
28
+ - Recording URL (may expire — capture content while live) — recording-url.txt
29
+ - Shared chat attachments (M files) — attachments/
30
+
31
+ ## What was attempted but unavailable
32
+ - Raw VTT transcript via m365_get_transcript — Tool execution failed
33
+ - Copilot recap card on chat — not posted
34
+
35
+ ## Expiry risk
36
+ - Recording URL points to SharePoint; subject to tenant retention (default 60 days). Capture content NOW; do not assume the URL will still resolve.
37
+ - Chat messages persist with the Teams chat object — lower expiry risk but still subject to user/admin retention.
38
+
39
+ ## Source basis classification (for stream/ citation)
40
+ `reconstructed from meeting chat + WorkIQ summary (no raw transcript)`
41
+ ```
42
+
43
+ ### `chat-messages.json`
44
+ Raw `m365_list_chat_messages` response. Do NOT re-pretty-print; preserve API field names and order.
45
+
46
+ ### `chat-messages.md`
47
+ ```markdown
48
+ # Chat verbatim — <meeting title> — <chat id>
49
+
50
+ > Captured <ISO timestamp>. <N> messages. Full bodies verbatim.
51
+
52
+ ---
53
+
54
+ ## [2026-05-13T15:32:11-04:00] Usha Krishnan
55
+ <full message body verbatim, HTML-stripped to markdown if possible, otherwise raw text>
56
+
57
+ ## [2026-05-13T15:34:02-04:00] Sachin Gandhi
58
+ <...>
59
+
60
+ (... one heading-block per message, chronological ...)
61
+ ```
62
+
63
+ ## Conditional files
64
+
65
+ ### `transcript.vtt`
66
+ Raw VTT bytes returned by `m365_get_transcript` or any tool that yields the actual transcript. Do not edit, do not summarize.
67
+
68
+ ### `transcript.txt`
69
+ Plain-text transcript when only plain text is available. Same shape: verbatim, no summarization.
70
+
71
+ ### `transcript-source.md`
72
+ When the only available "transcript" is a WorkIQ Copilot summary (not raw turns):
73
+
74
+ ```markdown
75
+ # Transcript source — <meeting title> — <date>
76
+
77
+ > **WARNING: This is NOT a verbatim transcript.** This is a Copilot-generated summary returned by WorkIQ as the only available transcript-derived artifact. Treat it as best-effort evidence, not as ground truth. Raw VTT was not retrievable on <date> via <method>.
78
+
79
+ ---
80
+
81
+ <WorkIQ markdown output verbatim, untouched>
82
+ ```
83
+
84
+ ### `recording-url.txt`
85
+ One URL per line. Include the SharePoint Stream URL plus any redirect URLs found in chat. Example:
86
+ ```
87
+ https://microsoft-my.sharepoint-df.com/personal/.../Recordings/<file>.mp4?web=1
88
+ ```
89
+
90
+ ### `recap-card.md`
91
+ Verbatim text of any Copilot recap card posted to the chat. Preserve markdown.
92
+
93
+ ### `attachments/`
94
+ Subfolder; one file per attachment. Use the original filename. If two attachments share a name, append `-2`, `-3`. Captured via `m365_download_file` against the URL in the chat message.
95
+
96
+ ## Validation
97
+
98
+ > This template documents a FOLDER, not a single file. Per meetings-verbatim-required.instructions.md and horoughness-detector.instructions.md, the skill validates the verbatim folder it produces (not this README). Checklist:
99
+
100
+ - [ ] captured-at.txt exists with started_at + completed_at + final_status
101
+ - [ ] At least one of: transcript.vtt, transcript.txt, transcript-source.md (transcript-class file — chat alone is NOT a transcript)
102
+ - [ ] If transcript-source.md used: WARNING header is present and coverage.md confirms VTT + plain-text paths were attempted first
103
+ - [ ] chat-messages.md rendered when chat-messages.json present (verbatim bodies, chronological, one heading per message)
104
+ - [ ] recording-url.txt present when meeting had a recording; recording.mp4 present if size < 200MB
105
+ - [ ] facilitator-notes.md present when m365_get_facilitator_notes returned content
106
+ - [ ] attachments/ populated when chat referenced any shared files
107
+ - [ ] coverage.md explains every attempted capture path (success + failure) and final source-basis classification
108
+ - [ ] captured-at.txt logs every transcript cascade step taken (a, b, c, d, e) — proves cascade was exhaustive, not first-success
109
+ - [ ] no-link-only-stubs (no file is a single URL pointing offsite when the content was retrievable)
110
+ - [ ] sibling stream/ per-meeting block cites this folder by relative path, prefers transcript-class file over chat for source citations
@@ -20,7 +20,9 @@
20
20
 
21
21
  ## Notes
22
22
 
23
- - Per-meeting transcripts and decisions are in `stream/<week>_meetings-stream.md`.
23
+ - Per-meeting curated blocks (attendees, decisions, transcript walk-through) are in `stream/<week>_meetings-stream.md`.
24
+ - Per-meeting **raw immutable verbatim** (chat-messages.json/md, transcript.vtt or transcript-source.md, recording-url.txt, attachments/) lives in `verbatim/<YYYY-MM-DD-HHMM>_<slug>/` siblings. Required per `meetings-verbatim-required.instructions.md` (v3.10.0) because meetings expire (tenant retention purges recordings + VTT).
25
+ - The curated stream blocks cite the verbatim files; the two are not interchangeable.
24
26
 
25
27
  ## Validation
26
28