kushi-agents 5.0.4 → 5.2.0

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 (43) hide show
  1. package/README.md +78 -0
  2. package/bin/cli.mjs +201 -1
  3. package/package.json +2 -2
  4. package/plugin/agents/kushi.agent.md +6 -2
  5. package/plugin/instructions/hooks.instructions.md +84 -0
  6. package/plugin/instructions/living-wiki.instructions.md +88 -0
  7. package/plugin/instructions/log-format.instructions.md +78 -0
  8. package/plugin/instructions/otel.instructions.md +75 -0
  9. package/plugin/instructions/parallel-execution.instructions.md +81 -0
  10. package/plugin/instructions/schema-evolve.instructions.md +73 -0
  11. package/plugin/instructions/wiki-lint.instructions.md +110 -0
  12. package/plugin/skills/_shared/Append-StateLog.ps1 +73 -0
  13. package/plugin/skills/_shared/Emit-OtelSpan.ps1 +111 -0
  14. package/plugin/skills/_shared/Invoke-Hooks.ps1 +177 -0
  15. package/plugin/skills/_shared/Update-StateIndex.ps1 +47 -0
  16. package/plugin/skills/_shared/hook-templates/console-debug.ps1 +15 -0
  17. package/plugin/skills/_shared/hook-templates/teams-notify.ps1 +47 -0
  18. package/plugin/skills/ask-project/SKILL.md +30 -0
  19. package/plugin/skills/build-state/SKILL.md +18 -2
  20. package/plugin/skills/lint-state/.created-by-skill-creator +0 -0
  21. package/plugin/skills/lint-state/SKILL.md +98 -0
  22. package/plugin/skills/lint-state/evals/evals.json +34 -0
  23. package/plugin/skills/lint-state/lint.ps1 +218 -0
  24. package/plugin/skills/refresh-project/SKILL.md +8 -4
  25. package/plugin/skills/schema-evolve/.created-by-skill-creator +0 -0
  26. package/plugin/skills/schema-evolve/SKILL.md +106 -0
  27. package/plugin/skills/schema-evolve/evals/evals.json +37 -0
  28. package/plugin/skills/self-check/SKILL.md +12 -55
  29. package/plugin/skills/self-check/references/algorithm.md +55 -0
  30. package/plugin/skills/self-check/run.ps1 +225 -3
  31. package/plugin/skills/skill-checker/check-skill.ps1 +1 -1
  32. package/plugin/skills/teach/.created-by-skill-creator +0 -0
  33. package/plugin/skills/teach/SKILL.md +77 -0
  34. package/plugin/skills/teach/evals/evals.json +37 -0
  35. package/plugin/templates/state/answers.README.md +7 -0
  36. package/plugin/templates/state/hot.template.md +12 -0
  37. package/plugin/templates/state/review-queue.template.md +10 -0
  38. package/src/eval-runner.test.mjs +1 -1
  39. package/src/hooks-dispatcher.test.mjs +135 -0
  40. package/src/otel-emit.test.mjs +73 -0
  41. package/src/parallel-refresh.test.mjs +50 -0
  42. package/src/schema-evolve.test.mjs +78 -0
  43. package/src/teach.test.mjs +45 -0
@@ -0,0 +1,218 @@
1
+ <#
2
+ .SYNOPSIS
3
+ lint-state — runs wiki-lint checks against a project's State/ directory.
4
+
5
+ .DESCRIPTION
6
+ Implements the finding classes defined in wiki-lint.instructions.md:
7
+ - contradiction-flagged
8
+ - stale-claim
9
+ - orphan-page
10
+ - missing-cross-ref
11
+ - data-gap
12
+
13
+ Writes a dated lint report to State/reports/, appends to State/log.md, updates
14
+ State/index.md "Last touched" pointer.
15
+
16
+ .PARAMETER StateDir
17
+ Path to the State/ directory to lint.
18
+
19
+ .PARAMETER GraphFile
20
+ Optional path to project-graph.json for missing-cross-ref checks.
21
+
22
+ .PARAMETER Quiet
23
+ Suppress console output (for programmatic use).
24
+ #>
25
+ [CmdletBinding()]
26
+ param(
27
+ [Parameter(Mandatory)][string]$StateDir,
28
+ [string]$GraphFile,
29
+ [switch]$Quiet
30
+ )
31
+
32
+ $ErrorActionPreference = 'Stop'
33
+ $findings = @()
34
+
35
+ # --- Helpers ---
36
+ function Add-LintFinding {
37
+ param([string]$Class, [string]$Severity, [string]$Entity, [string]$Description, [string]$File, [int]$Line, [string]$Fix)
38
+ $script:findings += [PSCustomObject]@{
39
+ class = $Class
40
+ severity = $Severity
41
+ entity = $Entity
42
+ description = $Description
43
+ file = $File
44
+ line = $Line
45
+ fix = $Fix
46
+ }
47
+ }
48
+
49
+ # --- Check 1: contradiction-flagged ---
50
+ $stateFiles = Get-ChildItem -Path $StateDir -Recurse -Filter '*.md' -ErrorAction SilentlyContinue
51
+ foreach ($f in $stateFiles) {
52
+ $lines = Get-Content -Path $f.FullName -ErrorAction SilentlyContinue
53
+ for ($i = 0; $i -lt $lines.Count; $i++) {
54
+ if ($lines[$i] -match '^\s*>\s*\[!warning\]\s*Contradicted') {
55
+ $entity = $f.BaseName -replace '-', ' '
56
+ Add-LintFinding 'contradiction-flagged' 'warning' $entity "Unresolved contradiction callout" $f.FullName ($i + 1) "Review both values, pick the correct one. Delete callout pair and keep winner, or add <!-- kushi:human-override --> to prevent auto-resolve."
57
+ }
58
+ }
59
+ }
60
+
61
+ # --- Check 2: stale-claim ---
62
+ $staleDays = 60
63
+ $now = Get-Date
64
+ foreach ($f in $stateFiles) {
65
+ $content = Get-Content -Raw $f.FullName -ErrorAction SilentlyContinue
66
+ if (-not $content) { continue }
67
+ $citations = [regex]::Matches($content, '\[source:[^\]]*\xB7\s*(\d{4}-\d{2}-\d{2})\]')
68
+ if ($citations.Count -eq 0) { continue }
69
+ $newestDate = $null
70
+ foreach ($m in $citations) {
71
+ try {
72
+ $d = [datetime]::ParseExact($m.Groups[1].Value, 'yyyy-MM-dd', $null)
73
+ if (-not $newestDate -or $d -gt $newestDate) { $newestDate = $d }
74
+ } catch {}
75
+ }
76
+ if ($newestDate -and ($now - $newestDate).TotalDays -gt $staleDays) {
77
+ $entity = $f.BaseName -replace '-', ' '
78
+ Add-LintFinding 'stale-claim' 'warning' $entity "Newest citation is $([math]::Round(($now - $newestDate).TotalDays)) days old (threshold: $staleDays)" $f.FullName 0 "Run '@Kushi refresh <project>' then '@Kushi state <project>' to pull fresh evidence. If claim is still valid, add a corroborating citation from a recent source."
79
+ }
80
+ }
81
+
82
+ # --- Check 3: orphan-page ---
83
+ $indexFile = Join-Path $StateDir 'index.md'
84
+ $indexContent = ''
85
+ if (Test-Path $indexFile) { $indexContent = Get-Content -Raw $indexFile }
86
+ $allPageContent = ''
87
+ foreach ($f in $stateFiles) {
88
+ if ($f.FullName -ne (Resolve-Path $indexFile -ErrorAction SilentlyContinue)) {
89
+ $allPageContent += (Get-Content -Raw $f.FullName -ErrorAction SilentlyContinue) + "`n"
90
+ }
91
+ }
92
+ foreach ($f in $stateFiles) {
93
+ $content = Get-Content -Raw $f.FullName -ErrorAction SilentlyContinue
94
+ if ($content -notmatch 'kushi_state_page:\s*true') { continue }
95
+ $slug = $f.BaseName
96
+ $relPath = $f.FullName.Replace($StateDir, '').TrimStart('\', '/') -replace '\\', '/'
97
+ if ($indexContent -notmatch [regex]::Escape($slug) -and $allPageContent -notmatch [regex]::Escape($slug)) {
98
+ Add-LintFinding 'orphan-page' 'warning' $slug "Page not linked from index.md or any sibling" $f.FullName 0 "Add [[$(Split-Path $relPath -Parent)/$slug]] to index.md under the correct category heading, or delete this page if generated in error."
99
+ }
100
+ }
101
+
102
+ # --- Check 4: missing-cross-ref ---
103
+ if ($GraphFile -and (Test-Path $GraphFile)) {
104
+ try {
105
+ $graph = Get-Content -Raw $GraphFile | ConvertFrom-Json
106
+ $entityNames = @()
107
+ if ($graph.nodes) { $entityNames = $graph.nodes | ForEach-Object { $_.name } | Where-Object { $_ } }
108
+ foreach ($f in $stateFiles) {
109
+ $content = Get-Content -Raw $f.FullName -ErrorAction SilentlyContinue
110
+ if ($content -notmatch 'kushi_state_page:\s*true') { continue }
111
+ foreach ($name in $entityNames) {
112
+ if ($name.Length -lt 3) { continue }
113
+ if ($content -match [regex]::Escape($name) -and $content -notmatch "entity_ids:.*$([regex]::Escape($name))" -and $content -notmatch "related:.*$([regex]::Escape($name))") {
114
+ # Check frontmatter more carefully
115
+ if ($content -match '(?ms)^---\r?\n(.*?)\r?\n---') {
116
+ $fm = $Matches[1]
117
+ if ($fm -notmatch [regex]::Escape($name)) {
118
+ Add-LintFinding 'missing-cross-ref' 'info' ($f.BaseName -replace '-',' ') "Entity '$name' mentioned in body but not in frontmatter" $f.FullName 0 "Add to related: array in front-matter."
119
+ }
120
+ }
121
+ }
122
+ }
123
+ }
124
+ } catch {
125
+ Write-Warning "Could not parse graph file: $_"
126
+ }
127
+ }
128
+
129
+ # --- Check 5: data-gap ---
130
+ foreach ($f in $stateFiles) {
131
+ $content = Get-Content -Raw $f.FullName -ErrorAction SilentlyContinue
132
+ if ($content -notmatch 'kushi_state_page:\s*true') { continue }
133
+ $lines = Get-Content -Path $f.FullName
134
+ for ($i = 0; $i -lt $lines.Count; $i++) {
135
+ if ($lines[$i] -match '^#{2,3}\s+(.+)$') {
136
+ $heading = $Matches[1]
137
+ # Look at content until next heading or EOF
138
+ $bodyLines = @()
139
+ for ($j = $i + 1; $j -lt $lines.Count; $j++) {
140
+ if ($lines[$j] -match '^#{2,3}\s+') { break }
141
+ if ($lines[$j].Trim() -and $lines[$j].Trim() -ne '<!-- TODO -->' -and $lines[$j].Trim() -ne '<!-- TODO(retrofit) -->') {
142
+ $bodyLines += $lines[$j]
143
+ }
144
+ }
145
+ if ($bodyLines.Count -lt 2 -and $heading -notmatch '^(State Log|State Index|Review Queue)') {
146
+ Add-LintFinding 'data-gap' 'info' ($f.BaseName -replace '-',' ') "Section '$heading' has no meaningful content" $f.FullName ($i + 1) "Run '@Kushi refresh <project>' to pull evidence for this section, or remove the header if not applicable."
147
+ }
148
+ }
149
+ }
150
+ }
151
+
152
+ # --- Generate report ---
153
+ $reportsDir = Join-Path $StateDir 'reports'
154
+ if (-not (Test-Path $reportsDir)) { New-Item -Path $reportsDir -ItemType Directory -Force | Out-Null }
155
+
156
+ $dateStr = Get-Date -Format 'yyyy-MM-dd'
157
+ $reportFile = Join-Path $reportsDir "lint-$dateStr.md"
158
+ $timestamp = Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ'
159
+
160
+ $classCounts = @{}
161
+ foreach ($f2 in $findings) {
162
+ if (-not $classCounts.ContainsKey($f2.class)) { $classCounts[$f2.class] = 0 }
163
+ $classCounts[$f2.class]++
164
+ }
165
+
166
+ $report = @"
167
+ ---
168
+ generated_at: "$timestamp"
169
+ generated_by: "lint-state v1.0.0"
170
+ findings_count: $($findings.Count)
171
+ ---
172
+
173
+ # Lint Report — $dateStr
174
+
175
+ ## Summary
176
+
177
+ | Class | Count | Severity |
178
+ |---|---|---|
179
+ "@
180
+
181
+ $classOrder = @('contradiction-flagged','stale-claim','orphan-page','missing-cross-ref','data-gap')
182
+ foreach ($cls in $classOrder) {
183
+ $count = if ($classCounts.ContainsKey($cls)) { $classCounts[$cls] } else { 0 }
184
+ $sev = if ($cls -match 'contradiction|stale|orphan') { 'warning' } else { 'info' }
185
+ $report += "| $cls | $count | $sev |`n"
186
+ }
187
+
188
+ $report += "`n**Total findings: $($findings.Count)**`n`n## Findings`n"
189
+
190
+ foreach ($f2 in $findings) {
191
+ $report += "`n### $($f2.class): $($f2.entity) — $($f2.description)`n`n"
192
+ $report += "**File:** ``$($f2.file)```n"
193
+ if ($f2.line -gt 0) { $report += "**Line:** $($f2.line)`n" }
194
+ $report += "**Fix:** $($f2.fix)`n"
195
+ }
196
+
197
+ Set-Content -Path $reportFile -Value $report -Encoding utf8NoBOM
198
+
199
+ # --- Update log + index ---
200
+ $sharedDir = Join-Path $PSScriptRoot '..\_shared'
201
+ $appendLog = Join-Path $sharedDir 'Append-StateLog.ps1'
202
+ $updateIndex = Join-Path $sharedDir 'Update-StateIndex.ps1'
203
+
204
+ if (Test-Path $appendLog) {
205
+ & $appendLog -StateDir $StateDir -Op 'lint-state' -Title "Lint pass ($($findings.Count) findings)" -Summary "Classes: $(($classCounts.Keys | Sort-Object | ForEach-Object { "$_=$($classCounts[$_])" }) -join ', ')" -Sources "State/**/*.md"
206
+ }
207
+ if (Test-Path $updateIndex) {
208
+ & $updateIndex -StateDir $StateDir -Op 'lint-state'
209
+ }
210
+
211
+ # --- Console output ---
212
+ if (-not $Quiet) {
213
+ $breakdown = ($classCounts.Keys | Sort-Object | ForEach-Object { "$_=$($classCounts[$_])" }) -join ', '
214
+ Write-Host "lint-state: $($findings.Count) findings ($breakdown). Report: $reportFile"
215
+ }
216
+
217
+ # Return findings as output for programmatic use
218
+ $findings
@@ -91,6 +91,8 @@ After drain, proceed to Step 2 even if some markers remain — the orchestrator
91
91
 
92
92
  ### Step 2 — Per-source dispatch
93
93
 
94
+ > **v5.2.0 parallel dispatch**: By default, enabled sources run in parallel (up to `parallel.max_workers`, default 4). Pass `--sequential` to force serial execution. See `parallel-execution.instructions.md`.
95
+
94
96
  For each enabled source (or just the requested one), call its `pull-<source>` skill with the effective window.
95
97
 
96
98
  **OneNote pre-dispatch gate (kushi v3.10.0+, HARD):** Before invoking `pull-onenote`, run the browser-URL completeness check:
@@ -200,10 +202,12 @@ Re-running refresh with the same window is safe:
200
202
  - "pull `<X>` since `<date>`"
201
203
  - "refresh `<X>` last `<N>` days"
202
204
  - "backfill `<X>` from `<from>` to `<to>`"
203
- ## References (v4.4.7)
204
-
205
- - Name → ID resolution (any source) follows `..\..\instructions\fuzzy-disambiguation.instructions.md`.
206
- - After each per-source pull, run the gate per `..\..\instructions\per-source-verification-gate.instructions.md` (retry once → FOLLOW-UPS.md on failure).
205
+ ## References (v4.4.7)
206
+
207
+ - Name → ID resolution (any source) follows `..\..\instructions\fuzzy-disambiguation.instructions.md`.
208
+ - After each per-source pull, run the gate per `..\..\instructions\per-source-verification-gate.instructions.md` (retry once → FOLLOW-UPS.md on failure).
209
+ - Parallel dispatch follows `..\..\instructions\parallel-execution.instructions.md` (v5.2.0+).
210
+ - Post-pull hooks fired per source via `..\..\skills\_shared\Invoke-Hooks.ps1` (v5.2.0+).
207
211
 
208
212
 
209
213
  ## Issue Recovery
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: "schema-evolve"
3
+ version: "1.0.0"
4
+ description: "USE WHEN the user says 'from now on always do X', 'remember this rule', 'kushi remember <rule>', or wants to teach kushi a project-specific convention that persists across runs. DO NOT USE for one-time instructions (just answer inline) or for modifying doctrine (edit the .instructions.md file directly). Capability: captures user-stated conventions to Evidence/<alias>/State/CLAUDE.md, reads them back at the start of build-state/ask-project/refresh runs."
5
+ ---
6
+
7
+ # Skill: schema-evolve
8
+
9
+ Captures user-stated project conventions and persists them to `Evidence/<alias>/State/CLAUDE.md` so future runs respect them automatically.
10
+
11
+ ## Triggers
12
+
13
+ - "from now on always do X for this project"
14
+ - "remember that Y means Z"
15
+ - `kushi remember <rule>` (CLI verb)
16
+ - "for this project, always treat A as B"
17
+ - "never include X in summaries for this project"
18
+
19
+ ## Inputs
20
+
21
+ - `<rule>` — free-text convention statement.
22
+ - `<project>` — engagement name (resolved from context or explicit).
23
+
24
+ ## Procedure
25
+
26
+ 1. **Resolve project** — identify the target project via fuzzy match or current context.
27
+ 2. **Parse rule** — extract the convention from user's statement. Normalize to declarative form.
28
+ 3. **Validate against doctrine** — check if the rule conflicts with hard doctrine (WorkIQ-only, CSC format, etc.). If conflict: warn user, explain why, do NOT persist.
29
+ 4. **Write to CLAUDE.md** — append the rule with timestamp + scope metadata to `Evidence/<alias>/State/CLAUDE.md`.
30
+ 5. **Confirm** — echo back the persisted rule and explain when it will be applied.
31
+
32
+ ## Step checklist
33
+
34
+ - [ ] Step 1 — Resolve project + State path
35
+ - [ ] Step 2 — Parse + validate rule
36
+ - [ ] Step 3 — Append to CLAUDE.md
37
+ - [ ] Step 4 — Confirm to user
38
+
39
+ ### Step 1 — Resolve project
40
+
41
+ Resolve `<engagement-root>/<project>/Evidence/<alias>/State/`. If CLAUDE.md doesn't exist, create it with the v5.0.0 header:
42
+
43
+ ```markdown
44
+ ---
45
+ kushi_state_page: true
46
+ ---
47
+
48
+ # CLAUDE.md — Project Conventions
49
+
50
+ Rules captured via `schema-evolve` skill. Read by build-state, ask-project, refresh-project at run start.
51
+ ```
52
+
53
+ ### Step 2 — Parse + validate
54
+
55
+ Normalize user's statement:
56
+ - "from now on always X" → "Always X"
57
+ - "never do Y" → "Never Y"
58
+ - "treat A as B" → "Treat A as B"
59
+
60
+ Validate: if the rule would override hard doctrine (`workiq-only`, `verbatim-by-default`, `csc` format), reject with explanation.
61
+
62
+ ### Step 3 — Append to CLAUDE.md
63
+
64
+ Append after existing content:
65
+
66
+ ```markdown
67
+ ## Rule: <short-title>
68
+
69
+ - **Added**: <ISO-8601 timestamp>
70
+ - **Scope**: project
71
+ - **Source**: user (natural language)
72
+
73
+ <normalized rule text>
74
+ ```
75
+
76
+ ### Step 4 — Confirm
77
+
78
+ Print:
79
+ > ✅ Rule captured: "<rule>"
80
+ > Applied to: <project> (project scope)
81
+ > Active in: build-state, ask-project, refresh-project
82
+
83
+ ## Gotchas
84
+
85
+ 1. **Hard doctrine override**: Rules CANNOT override WorkIQ-only, CSC format, verbatim-by-default, or evidence layout. Warn and refuse.
86
+ 2. **Duplicate rules**: If a near-identical rule exists, update the existing one (bump timestamp) rather than duplicating.
87
+ 3. **CLAUDE.md missing**: Create it with proper frontmatter on first rule capture.
88
+ 4. **No evidence modification**: Rules affect interpretation/presentation only — never modify raw evidence files.
89
+ 5. **Scope creep**: v5.2.0 is project-scope only. Don't promise global scope.
90
+
91
+ ## Validation loop
92
+
93
+ After writing:
94
+ 1. Re-read `CLAUDE.md` — verify the new rule appears.
95
+ 2. Parse all rules — verify valid structure (heading, metadata, text).
96
+ 3. Run self-check: `pwsh plugin/skills/self-check/run.ps1 -Targeted schema-evolve`
97
+
98
+ ## References
99
+
100
+ - `schema-evolve.instructions.md` — doctrine for convention persistence
101
+ - `karpathy-state-layout.instructions.md` — CLAUDE.md role in State layout
102
+ - `living-wiki.instructions.md` — incremental State maintenance
103
+
104
+ ## Issue Recovery
105
+
106
+ When this skill exposes a reusable defect (doctrine gap, missing cross-reference), apply the [Issue Recovery Rule](../../instructions/issue-recovery.instructions.md): fix the smallest correct repo-owned artifact first, prefer durable fixes over per-run workarounds, then re-run the narrowest failed check. Do NOT use memory as a substitute for correcting the workflow surface.
@@ -0,0 +1,37 @@
1
+ {
2
+ "skill": "schema-evolve",
3
+ "cases": [
4
+ {
5
+ "id": "schema-evolve-capture",
6
+ "name": "Captures a user rule to CLAUDE.md",
7
+ "input": "kushi remember always use 'HCA' not 'Healthcare Accelerator' in summaries",
8
+ "expected_assertions": [
9
+ { "type": "contains", "value": "CLAUDE.md" },
10
+ { "type": "contains", "value": "Rule" },
11
+ { "type": "contains", "value": "HCA" }
12
+ ],
13
+ "grader_type": "script"
14
+ },
15
+ {
16
+ "id": "schema-evolve-doctrine-conflict",
17
+ "name": "Rejects rule that conflicts with hard doctrine",
18
+ "input": "kushi remember never use WorkIQ, always use Graph API directly",
19
+ "expected_assertions": [
20
+ { "type": "contains", "value": "conflict" },
21
+ { "type": "contains", "value": "WorkIQ" },
22
+ { "type": "not_contains", "value": "Rule captured" }
23
+ ],
24
+ "grader_type": "script"
25
+ },
26
+ {
27
+ "id": "schema-evolve-natural-language",
28
+ "name": "Auto-detects convention from natural language",
29
+ "input": "from now on for this project always treat John as the EM",
30
+ "expected_assertions": [
31
+ { "type": "contains", "value": "John" },
32
+ { "type": "contains", "value": "rule" }
33
+ ],
34
+ "grader_type": "script"
35
+ }
36
+ ]
37
+ }
@@ -71,6 +71,11 @@ Checks split into **core** (always run) and **deep** (opt-in).
71
71
  | D32.multi-host | Multi-host install integrity | validates `src/multi-host.mjs` exports + `bin/cli.mjs` flag handling, then performs a temp-dir dry-run install for BOTH supported hosts (Clawpilot + VS Code Chat) under a fake `$HOME` in `$env:TEMP`. Asserts SKILL.md + agent file + skills/ + prompts/ + skills-metadata.json with a kushi entry are present, then asserts a clean uninstall. NEVER touches the real `~/.copilot/` or `~/.vscode/`. See `multi-host-install.instructions.md`. |
72
72
  | D33.evals | Skill evals framework integrity | every `plugin/skills/<name>/` (except `eval`) ships `evals/evals.json` with ≥ 2 cases and ≥ 1 assertion per case; the runner (`plugin/skills/eval/run-evals.ps1`) and schema (`plugin/skills/eval/evals.schema.json`) are present; `evals/baseline.json` exists (warn-only). Six sub-checks: `D33.evals-exist`, `D33.evals-schema`, `D33.evals-min-cases`, `D33.evals-have-assertions`, `D33.eval-runner-exists`, `D33.baseline-exists`. See `skill-evals.instructions.md`. |
73
73
  | D34.creator-conformance | skill-creator + skill-checker harness integrity (v5.0.4+) | validates `scaffold.ps1` + `check-skill.ps1` ship and are parseable; every skill carrying the `.created-by-skill-creator` marker passes `check-skill --lint` clean; `check-skill --all --retrofit --dry-run` shows no non-additive gaps; the dogfood report at `docs/audits/v5.0.4-skill-creator-dogfood.md` is fresh (≤14 days). Five sub-checks: `D34.skill-creator-exists`, `D34.skill-checker-exists`, `D34.creator-output-conforms`, `D34.retrofit-clean`, `D34.dogfood-report-fresh`. See `skill-authoring.instructions.md`. |
74
+ | D35.log | State log (v5.1.0+) | `State/log.md` exists, `## [` headings match canonical format, timestamps reverse-chronological. Sub-checks: `D35.log-exists`, `D35.log-format`, `D35.log-monotonic`. |
75
+ | D36.contradictions | Contradictions (v5.1.0+) | `> [!warning] Contradicted` callouts well-formed, `_review-queue.md` fresh, `<!-- kushi:auto -->` fences balanced. Sub-checks: `D36.callout-syntax`, `D36.review-queue-fresh`, `D36.no-silent-overwrite`. |
76
+ | D37.hooks | Hooks system (v5.2.0+) | `hooks.instructions.md` exists, `Invoke-Hooks.ps1` helper exists, hook-templates/ has ≥2 templates, any `hooks-log.md` uses canonical heading format. Sub-checks: `D37.hooks-doctrine-exists`, `D37.hooks-helper-exists`, `D37.hooks-templates-exist`, `D37.hooks-log-format`. |
77
+ | D38.parallel | Parallel execution (v5.2.0+) | `parallel-execution.instructions.md` exists, `refresh-project/SKILL.md` references parallel dispatch, doctrine documents deterministic output ordering. Sub-checks: `D38.parallel-doctrine-exists`, `D38.refresh-supports-parallel`, `D38.deterministic-order`. |
78
+ | D39.otel | OpenTelemetry export (v5.2.0+) | `otel.instructions.md` exists, `Emit-OtelSpan.ps1` helper exists, helper short-circuits when `KUSHI_OTEL_ENDPOINT` is unset. Sub-checks: `D39.otel-doctrine-exists`, `D39.otel-helper-exists`, `D39.otel-noop-when-unset`. |
74
79
  | **CSC weekly-layout checks (kushi v4.9.0)** | | gated on `Resolve-EngagementRoots` — no-ops on the kushi repo itself. |
75
80
  | D11.csc | CSC entity coverage + depth | every `Evidence/<alias>/<source>/weekly/*-csc.md` has ≥ 1 entity heading; per-source minimum bullet count + populated-section count (meetings 25/6, email 8/4, teams 6/3, onenote 10/4, sharepoint 8/3, crm 12/5, ado 8/4). Coverage-Notes-only blocks (low-signal escape) are exempt. |
76
81
  | D12.csc | CSC section order | every entity block's `###` section headings appear in the canonical order: Participants → Topics → Q&A → Who Said What → Decisions → Dates & Numbers → Action Items → Next Steps → Open Questions → Risks → Customer Asks → Artifacts → Coverage Notes. |
@@ -83,7 +88,7 @@ Checks split into **core** (always run) and **deep** (opt-in).
83
88
  | D19.csc | CSC legacy write guard | WARN if `snapshot/` or `stream/` contains a file modified after the v4.9.0 release date (2026-05-26). Reads of legacy layouts remain supported. |
84
89
  | **v5.0.0 cross-source / state / dashboard / tour checks** | | gated on `Resolve-EngagementRoots` — no-ops on the kushi repo itself. |
85
90
  | D20.graph | Entity-graph well-formed | `Evidence/_graph/project-graph.json`: valid JSON, has `schema/project/generated_at/nodes/edges`, every edge `kind` is in the closed taxonomy (`references`/`decides`/`action-item-tracks`/`discusses`/`produced-by`/`follow-up-of`/`same-thread`/`participant-of`), every edge endpoint resolves to a node. See `entity-graph.instructions.md`. |
86
- | D21.state | Karpathy State layout | when any `State/**/*.md` has front-matter `kushi_state_page: true`, the sibling `index.md`, `log.md`, `CLAUDE.md`, `AGENTS.md` MUST exist, `CLAUDE.md` and `AGENTS.md` MUST be byte-identical, and every v5 page MUST live under one of the closed-set category folders (`people/`, `opportunities/`, `adoworkitems/`, `decisions/`, `risks/`, `customerasks/`, `meetings/`, `artifacts/`). See `karpathy-state-layout.instructions.md`. |
91
+ | D21.state | Karpathy State layout | `kushi_state_page: true` pages require sibling `index.md`, `log.md`, `CLAUDE.md`, `AGENTS.md`; pages live under closed-set category folders. See `karpathy-state-layout.instructions.md`. |
87
92
  | D22.dashboard | Dashboard produced + substituted | when `project-graph.json` exists, `<project>/dashboard.html` MUST exist, MUST NOT still contain the literal `__GRAPH_JSON__` placeholder (would indicate embedder failure), and MUST reference Cytoscape. See `dashboard-artifact.instructions.md`. |
88
93
  | D23.tour | Tour citations resolve | every `- **Cite:** [`<path>`]` row in `<project>/State/tour.md` MUST resolve to a real file under `<project>/Evidence/`. See `guided-tour.instructions.md`. |
89
94
 
@@ -105,59 +110,7 @@ Checks split into **core** (always run) and **deep** (opt-in).
105
110
 
106
111
  ## Algorithm (per check)
107
112
 
108
- ### C1 Frontmatter completeness
109
-
110
- For each `plugin/skills/<dir>/SKILL.md`: parse YAML frontmatter (text between leading `---` lines). Required keys: `name`, `version`, `description`.
111
-
112
- > ⚠️ self-check C1 — Skill `<dir>` is missing required frontmatter key `<key>`. Add it before the first `---` separator.
113
-
114
- ### C2 — Skill name matches directory
115
-
116
- For each skill: `frontmatter.name` (case-insensitive) must equal directory name.
117
-
118
- > ⚠️ self-check C2 — Directory `<dir>/` declares `name: "<name>"`. Rename one or the other so they match.
119
-
120
- ### C3 — Skills inventoried in agent
121
-
122
- `plugin/agents/kushi.agent.md` must contain a backtick-wrapped reference to every skill directory name. Look in any markdown table column or any inline `\`<name>\`` token.
123
-
124
- > ⚠️ self-check C3 — Skill `<dir>` is not listed in the kushi agent. Suggested row for the routing table:
125
- >
126
- > `| <verb> | <dir> | <one-line description from frontmatter> |`
127
-
128
- ### C4 — Prompts route to real skills
129
-
130
- For each `plugin/prompts/*.prompt.md`: extract any reference of the form `` `<skill-name>` `` or `delegates to <skill-name>` (case-insensitive). Each must exist as a directory under `plugin/skills/`.
131
-
132
- > ⚠️ self-check C4 — Prompt `<file>` references skill `<name>` which does not exist under `plugin/skills/`. Either create the skill or update the prompt.
133
-
134
- ### C5 — Instructions referenced
135
-
136
- For each `plugin/instructions/*.instructions.md`: at least one SKILL.md OR `plugin/agents/kushi.agent.md` must reference it by filename (with or without path).
137
-
138
- > ⚠️ self-check C5 — Instruction `<file>` is not referenced anywhere. Either delete it or add a "## References" line to the relevant skill(s).
139
-
140
- ### C6 — Cross-links resolve
141
-
142
- For every markdown link with a relative target (`](./...)`, `](../...)`, or `](path/...)` not starting with `http`) inside `plugin/**/*.md`: target must exist on disk relative to the file containing it.
143
-
144
- > ⚠️ self-check C6 — `<source-file>:<line>` links to `<target>` which does not exist. Update or remove the link.
145
-
146
- ### C7 — Verbs table matches prompts
147
-
148
- `README.md` contains a "Verbs" / "Quick start" table. Every `plugin/prompts/<verb>.prompt.md` must have a row in that table; every row in the table must have a corresponding prompt file.
149
-
150
- > ⚠️ self-check C7 — Verb `<v>` exists as `plugin/prompts/<v>.prompt.md` but is missing from README's verbs table. Suggested row:
151
- >
152
- > `| \`<v>\` | <default window> | <one-line description> |`
153
-
154
- ### C8 — Distribution layout matches docs
155
-
156
- `docs/reference/where-things-live.md` contains an ASCII tree of `plugin/`. Every top-level subdirectory and notable file under `plugin/` must appear; every entry in the tree must exist on disk.
157
-
158
- > ⚠️ self-check C8 — `plugin/<x>` exists but is missing from the docs/reference/where-things-live.md tree. Add an entry under section "1. This repo".
159
-
160
- ### D-checks: see `run.ps1` for the exact regexes.
113
+ > Load on trigger: see [`references/algorithm.md`](references/algorithm.md) for per-check pseudocode and finding templates (C1–C8, D-checks).
161
114
 
162
115
  ## Validation rules
163
116
 
@@ -202,4 +155,8 @@ This skill is not invoked by `@Kushi <verb>` — it's a meta-skill. Triggers:
202
155
  ## References
203
156
 
204
157
  - `instructions/citation-ledger.instructions.md` (findings cite source files / line numbers)
205
- - `instructions/side-by-side-config.instructions.md` (D6 enforces this rule for skills that read/write user config)
158
+ - `instructions/side-by-side-config.instructions.md` (D6 enforces this rule for skills that read/write user config)
159
+ - `instructions/hooks.instructions.md` (D37 validates hooks system)
160
+ - `instructions/parallel-execution.instructions.md` (D38 validates parallel dispatch)
161
+ - `instructions/otel.instructions.md` (D39 validates OTel export)
162
+ - `instructions/schema-evolve.instructions.md` (D39 validates schema evolution)
@@ -0,0 +1,55 @@
1
+ # Algorithm (per check)
2
+
3
+ ### C1 — Frontmatter completeness
4
+
5
+ For each `plugin/skills/<dir>/SKILL.md`: parse YAML frontmatter (text between leading `---` lines). Required keys: `name`, `version`, `description`.
6
+
7
+ > ⚠️ self-check C1 — Skill `<dir>` is missing required frontmatter key `<key>`. Add it before the first `---` separator.
8
+
9
+ ### C2 — Skill name matches directory
10
+
11
+ For each skill: `frontmatter.name` (case-insensitive) must equal directory name.
12
+
13
+ > ⚠️ self-check C2 — Directory `<dir>/` declares `name: "<name>"`. Rename one or the other so they match.
14
+
15
+ ### C3 — Skills inventoried in agent
16
+
17
+ `plugin/agents/kushi.agent.md` must contain a backtick-wrapped reference to every skill directory name. Look in any markdown table column or any inline `` `<name>` `` token.
18
+
19
+ > ⚠️ self-check C3 — Skill `<dir>` is not listed in the kushi agent. Suggested row for the routing table:
20
+ >
21
+ > `| <verb> | <dir> | <one-line description from frontmatter> |`
22
+
23
+ ### C4 — Prompts route to real skills
24
+
25
+ For each `plugin/prompts/*.prompt.md`: extract any reference of the form `` `<skill-name>` `` or `delegates to <skill-name>` (case-insensitive). Each must exist as a directory under `plugin/skills/`.
26
+
27
+ > ⚠️ self-check C4 — Prompt `<file>` references skill `<name>` which does not exist under `plugin/skills/`. Either create the skill or update the prompt.
28
+
29
+ ### C5 — Instructions referenced
30
+
31
+ For each `plugin/instructions/*.instructions.md`: at least one SKILL.md OR `plugin/agents/kushi.agent.md` must reference it by filename (with or without path).
32
+
33
+ > ⚠️ self-check C5 — Instruction `<file>` is not referenced anywhere. Either delete it or add a "## References" line to the relevant skill(s).
34
+
35
+ ### C6 — Cross-links resolve
36
+
37
+ For every markdown link with a relative target (`](./...)`, `](../...)`, or `](path/...)` not starting with `http`) inside `plugin/**/*.md`: target must exist on disk relative to the file containing it.
38
+
39
+ > ⚠️ self-check C6 — `<source-file>:<line>` links to `<target>` which does not exist. Update or remove the link.
40
+
41
+ ### C7 — Verbs table matches prompts
42
+
43
+ `README.md` contains a "Verbs" / "Quick start" table. Every `plugin/prompts/<verb>.prompt.md` must have a row in that table; every row in the table must have a corresponding prompt file.
44
+
45
+ > ⚠️ self-check C7 — Verb `<v>` exists as `plugin/prompts/<v>.prompt.md` but is missing from README's verbs table. Suggested row:
46
+ >
47
+ > `| \`<v>\` | <default window> | <one-line description> |`
48
+
49
+ ### C8 — Distribution layout matches docs
50
+
51
+ `docs/reference/where-things-live.md` contains an ASCII tree of `plugin/`. Every top-level subdirectory and notable file under `plugin/` must appear; every entry in the tree must exist on disk.
52
+
53
+ > ⚠️ self-check C8 — `plugin/<x>` exists but is missing from the docs/reference/where-things-live.md tree. Add an entry under section "1. This repo".
54
+
55
+ ### D-checks: see `run.ps1` for the exact regexes.