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.
- package/README.md +33 -0
- package/package.json +15 -3
- package/plugin/agents/kushi.agent.md +155 -147
- package/plugin/instructions/ado-bootstrap-discovery.instructions.md +111 -0
- package/plugin/instructions/ado-engagement-tree.instructions.md +73 -0
- package/plugin/instructions/answer-from-evidence.instructions.md +1 -1
- package/plugin/instructions/auth-and-retry.instructions.md +51 -16
- package/plugin/instructions/azure-auth-patterns.instructions.md +13 -6
- package/plugin/instructions/bootstrap-status-format.instructions.md +113 -0
- package/plugin/instructions/capture-learnings.instructions.md +95 -0
- package/plugin/instructions/cleanup-on-resolution.instructions.md +69 -0
- package/plugin/instructions/crm-bootstrap-discovery.instructions.md +79 -0
- package/plugin/instructions/crm-internal-vs-confirmed.instructions.md +79 -0
- package/plugin/instructions/evidence-confidence-ladder.instructions.md +66 -0
- package/plugin/instructions/evidence-layout-canonical.instructions.md +115 -0
- package/plugin/instructions/evidence-thoroughness.instructions.md +82 -12
- package/plugin/instructions/full-view-gate.instructions.md +91 -0
- package/plugin/instructions/m365-id-registry.instructions.md +134 -0
- package/plugin/instructions/meetings-verbatim-required.instructions.md +176 -0
- package/plugin/instructions/run-reports.instructions.md +129 -0
- package/plugin/instructions/scope-boundaries.instructions.md +218 -0
- package/plugin/instructions/snapshot-vs-stream.instructions.md +2 -0
- package/plugin/instructions/update-ledger.instructions.md +132 -0
- package/plugin/instructions/verbatim-by-default.instructions.md +73 -0
- package/plugin/instructions/workiq-first.instructions.md +15 -31
- package/plugin/instructions/workiq-only.instructions.md +193 -0
- package/plugin/learnings/README.md +50 -0
- package/plugin/learnings/ado.md +45 -0
- package/plugin/learnings/crm.md +96 -0
- package/plugin/learnings/cross-cutting.md +36 -0
- package/plugin/learnings/email.md +33 -0
- package/plugin/learnings/meetings.md +30 -0
- package/plugin/learnings/misc.md +46 -0
- package/plugin/learnings/onenote.md +215 -0
- package/plugin/learnings/sharepoint.md +5 -0
- package/plugin/learnings/teams.md +5 -0
- package/plugin/plugin.json +22 -2
- package/plugin/prompts/apply-ado.prompt.md +14 -0
- package/plugin/prompts/propose-ado.prompt.md +12 -0
- package/plugin/reference-packs/fde/crm-field-manifest.md +165 -0
- package/plugin/skills/apply-ado-update/SKILL.md +125 -0
- package/plugin/skills/ask-project/SKILL.md +2 -0
- package/plugin/skills/bootstrap-project/SKILL.md +81 -3
- package/plugin/skills/propose-ado-update/SKILL.md +108 -0
- package/plugin/skills/pull-ado/SKILL.md +173 -23
- package/plugin/skills/pull-crm/SKILL.md +168 -15
- package/plugin/skills/pull-email/SKILL.md +139 -22
- package/plugin/skills/pull-meetings/SKILL.md +109 -25
- package/plugin/skills/pull-misc/README.md +84 -0
- package/plugin/skills/pull-misc/SKILL.md +257 -0
- package/plugin/skills/pull-misc/runner.mjs +280 -0
- package/plugin/skills/pull-onenote/README.md +90 -0
- package/plugin/skills/pull-onenote/SKILL.md +400 -51
- package/plugin/skills/pull-onenote/runner.mjs +356 -0
- package/plugin/skills/pull-onenote/scripts/recapture-section-url.mjs +295 -0
- package/plugin/skills/pull-onenote/write-snapshot.mjs +271 -0
- package/plugin/skills/pull-sharepoint/SKILL.md +44 -12
- package/plugin/skills/pull-teams/SKILL.md +40 -11
- package/plugin/skills/refresh-project/SKILL.md +33 -2
- package/plugin/skills/self-check/run.ps1 +186 -4
- package/plugin/templates/ado-update/discussion-comment.template.md +26 -0
- package/plugin/templates/ado-update/integrations-ado-writes.example.yml +49 -0
- package/plugin/templates/ado-update/proposed.template.md +78 -0
- package/plugin/templates/init/external-links.template.txt +30 -0
- package/plugin/templates/init/project-integrations.template.yml +57 -2
- package/plugin/templates/snapshot/meeting-verbatim.template.md +110 -0
- package/plugin/templates/snapshot/meetings-series-index.template.md +3 -1
- package/plugin/templates/snapshot/onenote-page.template.md +92 -23
- 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
|
|
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)
|
|
362
|
-
Add-Finding D3 'WorkIQ-
|
|
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
|
-
#
|
|
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
|
|
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
|
|