kenaz 0.3.18 → 0.3.19

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 (308) hide show
  1. package/core/.claude/commands/agent-worker.md +1 -1
  2. package/core/.claude/commands/architect.md +1 -2
  3. package/core/.claude/commands/spec.md +1 -1
  4. package/core/.claude/commands//347/224/242/345/223/201.md +1 -1
  5. package/core/.claude/hooks/__tests__/task-1977-dedupe-guard.test.js +8 -8
  6. package/core/.claude/skills/architect_agent/SKILL.md +11 -92
  7. package/core/.claude/skills/assessment_agent/SKILL.md +0 -1
  8. package/core/.claude/skills/backend_engineer/SKILL.md +3 -6
  9. package/core/.claude/skills/code_reviewer/SKILL.md +1 -1
  10. package/core/.claude/skills/code_reviewer/patterns.yaml +1 -1
  11. package/core/.claude/skills/designer/SKILL.md +3 -6
  12. package/core/.claude/skills/dev_agent/SKILL.md +30 -491
  13. package/core/.claude/skills/developer/SKILL.md +3 -6
  14. package/core/.claude/skills/feature_agent/SKILL.md +3 -17
  15. package/core/.claude/skills/product_manager/SKILL.md +29 -1064
  16. package/core/.claude/skills/qa_engineer/SKILL.md +12 -17
  17. package/core/.claude/skills/spec_optimizer/SKILL.md +25 -275
  18. package/core/.claude/skills/sre_agent/SKILL.md +3 -6
  19. package/core/.collaboration/README.md +2 -3
  20. package/core/.collaboration/core/task-api.js +5 -138
  21. package/core/.collaboration/docs/CLI.md +0 -2
  22. package/core/.failure/INDEX.yaml +6 -40
  23. package/core/.failure/agent-execution/FAIL-013-search-cwd-drift-and-case-mismatch.yaml +1 -1
  24. package/core/.failure/tools/README.md +9 -227
  25. package/core/.formation/bifrost/launch.js +1 -6
  26. package/core/.formation/bifrost/launch.test.js +1 -1
  27. package/core/.formation/bifrost/shared/ansuz-boot-policy.js +3 -3
  28. package/core/.formation/bifrost/shared/ansuz-boot-policy.test.js +1 -2
  29. package/core/.formation/tools/__tests__/m1-hmap-convid.test.js +1 -124
  30. package/core/.formation/tools/dispatch.js +1 -1
  31. package/core/.formation/tools/loader.js +17 -140
  32. package/core/.formation/tools/post-complete.js +0 -31
  33. package/core/.rule/INDEX.yaml +0 -18
  34. package/core/.rule/general/agent-workflow.yaml +14 -164
  35. package/core/.rule/general/project-self-containment.yaml +8 -19
  36. package/core/.rule/general/rust-verify-target.yaml +6 -5
  37. package/core/.rule/general/synapse-code-intelligence.yaml +3 -3
  38. package/core/.specialist/Amelia/capability.yaml +12 -12
  39. package/core/.specialist/Amelia/workflow.yaml +2 -2
  40. package/core/.specialist/Ansuz/SYSTEM_DOC.md +20 -578
  41. package/core/.specialist/Ansuz/cognitive_principles.yaml +2 -3
  42. package/core/.specialist/Ansuz/concepts/boot_rules_summary.yaml +0 -1
  43. package/core/.specialist/Ansuz/concepts/deep_knowledge_pointers.yaml +1 -2
  44. package/core/.specialist/Ansuz/core.yaml +6 -108
  45. package/core/.specialist/Ansuz/guards/lesson_guards.yaml +0 -3
  46. package/core/.specialist/Ansuz/identity_contract.yaml +0 -1
  47. package/core/.specialist/Ansuz/knowledge.yaml +0 -4
  48. package/core/.specialist/Ansuz/rule.yaml +0 -1
  49. package/core/.specialist/Ansuz/tools/handoff-session-identity.test.js +6 -150
  50. package/core/.specialist/tests/runtime-discovery/README.md +1 -1
  51. package/core/.specialist/tools/__tests__/task-2374-usage-counter.test.js +1 -12
  52. package/core/.specialist/tools/prompt-files.test.js +1 -0
  53. package/core/.specialist/tools/task-bridge.js +1 -66
  54. package/core/.system/README.md +11 -552
  55. package/core/.system/collaboration/README.md +10 -174
  56. package/core/CLAUDE.md +1 -6
  57. package/core/KENAZ_CORE_VERSION +1 -1
  58. package/core/dev/scripts/js-yaml-resolution.test.js +1 -1
  59. package/core/dev/scripts/run-rust-tests.js +9 -45
  60. package/core/dev/scripts/rust-verify-worktree.js +4 -4
  61. package/core/dev/scripts/verify-target-dir.js +6 -6
  62. package/core/plugins/kenaz/INVENTORY.md +8 -8
  63. package/core/plugins/kenaz/commands/architect.md +1 -2
  64. package/core/plugins/kenaz/commands/spec.md +1 -1
  65. package/core/plugins/kenaz/commands//347/224/242/345/223/201.md +1 -1
  66. package/core/plugins/kenaz/guidance.md +1 -1
  67. package/core/plugins/kenaz/scripts/sync.js +1 -1
  68. package/core/plugins/kenaz/skills/architect_agent/SKILL.md +11 -92
  69. package/core/plugins/kenaz/skills/assessment_agent/SKILL.md +0 -1
  70. package/core/plugins/kenaz/skills/backend_engineer/SKILL.md +3 -6
  71. package/core/plugins/kenaz/skills/code_reviewer/SKILL.md +1 -1
  72. package/core/plugins/kenaz/skills/code_reviewer/patterns.yaml +1 -1
  73. package/core/plugins/kenaz/skills/designer/SKILL.md +3 -6
  74. package/core/plugins/kenaz/skills/dev_agent/SKILL.md +30 -491
  75. package/core/plugins/kenaz/skills/developer/SKILL.md +3 -6
  76. package/core/plugins/kenaz/skills/feature_agent/SKILL.md +3 -17
  77. package/core/plugins/kenaz/skills/product_manager/SKILL.md +29 -1064
  78. package/core/plugins/kenaz/skills/qa_engineer/SKILL.md +12 -17
  79. package/core/plugins/kenaz/skills/spec_optimizer/SKILL.md +25 -275
  80. package/core/plugins/kenaz/skills/sre_agent/SKILL.md +3 -6
  81. package/package.json +6 -6
  82. package/core/.claude/commands/archive.md +0 -24
  83. package/core/.claude/commands/init.md +0 -112
  84. package/core/.claude/commands/legacy_dev.md +0 -18
  85. package/core/.claude/commands/product-review.md +0 -131
  86. package/core/.claude/commands/strategy.md +0 -141
  87. package/core/.claude/commands/sunny.md +0 -1
  88. package/core/.claude/commands//347/224/242/345/223/201/346/252/242/350/246/226.md +0 -129
  89. package/core/.claude/commands//351/200/262/345/272/246.md +0 -15
  90. package/core/.claude/hooks/__tests__/substrate.test.js +0 -122
  91. package/core/.claude/hooks/_substrate-paths.js +0 -95
  92. package/core/.claude/hooks/context-check.js +0 -51
  93. package/core/.claude/hooks/fatigue-check.js +0 -52
  94. package/core/.claude/hooks/handoff-snapshot-on-commit.js +0 -154
  95. package/core/.claude/hooks/handoff-snapshot-on-question.js +0 -158
  96. package/core/.claude/hooks/handoff-staleness.js +0 -197
  97. package/core/.claude/hooks/jev-thalamus-context.js +0 -17
  98. package/core/.claude/hooks/jev-thalamus-context.test.js +0 -41
  99. package/core/.claude/hooks/lease-release.js +0 -119
  100. package/core/.claude/hooks/lease-touch.js +0 -96
  101. package/core/.claude/hooks/reflex-feedback.js +0 -5
  102. package/core/.claude/hooks/session-init.ps1 +0 -49
  103. package/core/.claude/hooks/sound-event.js +0 -70
  104. package/core/.claude/hooks/sound-event.sh +0 -37
  105. package/core/.claude/hooks/stream-delta.js +0 -169
  106. package/core/.claude/no-hooks.settings.json +0 -3
  107. package/core/.claude/skills/legacy_dev/SKILL.md +0 -202
  108. package/core/.claude/skills/sunny/SKILL.md +0 -74
  109. package/core/.collaboration/cli/commands/assign.js +0 -39
  110. package/core/.collaboration/cli/commands/claim.js +0 -33
  111. package/core/.collaboration/cli/commands/complete.js +0 -44
  112. package/core/.collaboration/cli/commands/create.js +0 -110
  113. package/core/.collaboration/cli/commands/get.js +0 -83
  114. package/core/.collaboration/cli/commands/index.js +0 -52
  115. package/core/.collaboration/cli/commands/list.js +0 -51
  116. package/core/.collaboration/cli/commands/lock.js +0 -45
  117. package/core/.collaboration/cli/commands/rebuild.js +0 -39
  118. package/core/.collaboration/cli/commands/search.js +0 -61
  119. package/core/.collaboration/cli/commands/stats.js +0 -61
  120. package/core/.collaboration/cli/commands/transfer.js +0 -41
  121. package/core/.collaboration/cli/commands/unlock.js +0 -48
  122. package/core/.collaboration/cli/formatters/help-formatter.js +0 -70
  123. package/core/.collaboration/cli/formatters/index.js +0 -7
  124. package/core/.collaboration/cli/task-cli.js +0 -46
  125. package/core/.collaboration/cli/utils/arg-parser.js +0 -74
  126. package/core/.collaboration/cli/utils/git-user.js +0 -25
  127. package/core/.collaboration/cli/utils/index.js +0 -9
  128. package/core/.collaboration/cli/utils/output.js +0 -10
  129. package/core/.collaboration/config/default.yaml +0 -189
  130. package/core/.collaboration/core/conflict-resolver.js +0 -461
  131. package/core/.collaboration/core/conflict-resolver.test.js +0 -389
  132. package/core/.collaboration/core/migrate-legacy.js +0 -260
  133. package/core/.collaboration/docs/API.md +0 -787
  134. package/core/.collaboration/docs/ARCHITECTURE.md +0 -596
  135. package/core/.collaboration/docs/FAQ.md +0 -695
  136. package/core/.collaboration/docs/MIGRATION.md +0 -804
  137. package/core/.collaboration/docs/examples/README.md +0 -320
  138. package/core/.collaboration/docs/examples/advanced-queries.js +0 -261
  139. package/core/.collaboration/docs/examples/basic-usage.js +0 -184
  140. package/core/.collaboration/docs/examples/team-workflow.js +0 -352
  141. package/core/.failure/agent-execution/FAIL-001-agent-no-output.yaml +0 -391
  142. package/core/.failure/agent-execution/FAIL-007-pm-lead-wrong-team-invocation.yaml +0 -0
  143. package/core/.failure/agent-execution/FAIL-010-phase0-poem-format-skipped.yaml +0 -28
  144. package/core/.failure/agent-execution/FAIL-010-skipping-cognitive-checkpoint-after-verification.yaml +0 -59
  145. package/core/.failure/agent-execution/FAIL-012-grep-shortcut-during-investigation.yaml +0 -85
  146. package/core/.failure/architecture/FAIL-006-python-version-compatibility.yaml +0 -445
  147. package/core/.failure/pipeline/FAIL-003-queue-pollution.yaml +0 -335
  148. package/core/.failure/pipeline/README.md +0 -95
  149. package/core/.failure/retired/FAIL-009-skipping-synapse-pulse-before-code-change.yaml +0 -49
  150. package/core/.failure/tools/USAGE_GUIDE.md +0 -323
  151. package/core/.failure/tools/check_failures.ps1 +0 -387
  152. package/core/.failure/tools/log_failure.ps1 +0 -474
  153. package/core/.formation/.episode-input.json +0 -10
  154. package/core/.formation/.merge-lock +0 -1
  155. package/core/.formation/lead.yaml +0 -118
  156. package/core/.formation/legacy.bak/designer.yaml +0 -57
  157. package/core/.formation/legacy.bak/pm.yaml +0 -57
  158. package/core/.formation/legacy.bak/qa.yaml +0 -57
  159. package/core/.formation/legacy.bak/review.yaml +0 -62
  160. package/core/.formation/legacy.bak/scheduler.yaml +0 -129
  161. package/core/.formation/legacy.bak/sre.yaml +0 -57
  162. package/core/.formation/role-map.yaml +0 -274
  163. package/core/.formation/roles/_shared/modules/code_review.yaml +0 -98
  164. package/core/.formation/roles/_shared/modules/security.yaml +0 -103
  165. package/core/.formation/roles/_shared/modules/testing.yaml +0 -14
  166. package/core/.formation/roles/backend/role.yaml +0 -93
  167. package/core/.formation/roles/code_reviewer/role.yaml +0 -102
  168. package/core/.formation/roles/database_optimizer/role.yaml +0 -107
  169. package/core/.formation/roles/designer/role.yaml +0 -66
  170. package/core/.formation/roles/developer/role.yaml +0 -75
  171. package/core/.formation/roles/frontend/role.yaml +0 -134
  172. package/core/.formation/roles/incident_response/role.yaml +0 -89
  173. package/core/.formation/roles/qa/role.yaml +0 -89
  174. package/core/.formation/roles/security/role.yaml +0 -87
  175. package/core/.formation/roles/sre/role.yaml +0 -66
  176. package/core/.formation/skills/coordinator.yaml +0 -52
  177. package/core/.formation/solo/diagnosis.yaml +0 -78
  178. package/core/.formation/solo/strategy.yaml +0 -121
  179. package/core/.formation/tests/hello.txt +0 -2
  180. package/core/.formation/tools/coordinator.js +0 -350
  181. package/core/.formation/tools/jev-correction.js +0 -214
  182. package/core/.formation/tools/jev-correction.test.js +0 -178
  183. package/core/.formation/tools/log-reader.js +0 -232
  184. package/core/.formation/tools/revert-daemon-state.js +0 -162
  185. package/core/.formation/tools/test-drift.js +0 -148
  186. package/core/.formation/unit/heimdall.yaml +0 -96
  187. package/core/.formation/unit/plan.yaml +0 -67
  188. package/core/.formation/unit/think.yaml +0 -58
  189. package/core/.kenaz/MUNINN_SNAPSHOT9_EPISODE.json +0 -32
  190. package/core/.kenaz/MUNINN_SNAPSHOT9_RUNE.json +0 -23
  191. package/core/.kenaz/MUNINN_SNAPSHOT9_SUMMARY.md +0 -89
  192. package/core/.kenaz/ash_TASK_1428-20260507.json +0 -14
  193. package/core/.kenaz/muninn_episode_write.ps1 +0 -17
  194. package/core/.kenaz/muninn_snapshot11_rune_candidates.md +0 -63
  195. package/core/.kenaz/muninn_snapshot1_rune_candidates.md +0 -49
  196. package/core/.kenaz/runes_TASK_1428.json +0 -57
  197. package/core/.kenaz/tmp_heimdall_proposal_8d3e392.json +0 -13
  198. package/core/.kenaz/tmp_props.json +0 -1
  199. package/core/.kenaz/tmp_sounddrop_t1_prompt.txt +0 -371
  200. package/core/.rule/MECHANIZED.md +0 -61
  201. package/core/.rule/general/cortex-memory-recall.yaml +0 -446
  202. package/core/.rule/general/pipeline.yaml +0 -246
  203. package/core/.rule/general/sunny-access-control.yaml +0 -60
  204. package/core/.rule/general/system-invariants.yaml +0 -231
  205. package/core/.rule/pipeline/PPL-002-completed-task-archival.yaml +0 -528
  206. package/core/.rule/pipeline/auto-update.yaml +0 -282
  207. package/core/.rule/pipeline/task-execution.yaml +0 -365
  208. package/core/.specialist/Ansuz/concepts/phase_protocol.yaml +0 -65
  209. package/core/.specialist/Ansuz/experiments/boot_ablation_replay.js +0 -197
  210. package/core/.specialist/Ansuz/tools/bootstrap-anchors.js +0 -289
  211. package/core/.specialist/Ansuz/workflow.yaml +0 -1225
  212. package/core/.specialist/experiments/rg_benchmark.js +0 -523
  213. package/core/.specialist/experiments/rg_benchmark_results.json +0 -27
  214. package/core/.specialist/tools/jev-optional-context.js +0 -107
  215. package/core/.specialist/tools/jev-optional-context.test.js +0 -152
  216. package/core/.specialist/tools/memory-action-log.js +0 -839
  217. package/core/.specialist/tools/phase-gate.js +0 -3534
  218. package/core/.specialist/tools/phase-gate.test.js +0 -85
  219. package/core/.specialist/tools/precompute-runner.js +0 -444
  220. package/core/.specialist/tools/rule-engine.js +0 -621
  221. package/core/.specialist/tools/task-bridge-jev.test.js +0 -170
  222. package/core/.synapse/memory-shared.json.sync +0 -1
  223. package/core/.system/agent-skills/README.md +0 -724
  224. package/core/.system/cli-tools/README.md +0 -724
  225. package/core/.system/cortex/MODULES.md +0 -1046
  226. package/core/.system/dashboard/FRONTEND.md +0 -812
  227. package/core/.system/dashboard/README.md +0 -737
  228. package/core/.system/failure/README.md +0 -723
  229. package/core/.system/formation/LEGACY_SKILLS.md +0 -974
  230. package/core/.system/formation/POST_COMPLETE.md +0 -602
  231. package/core/.system/formation/README.md +0 -1094
  232. package/core/.system/formation/TOOLS_REFERENCE.md +0 -1145
  233. package/core/.system/kenaz/README.md +0 -787
  234. package/core/.system/memory/README.md +0 -719
  235. package/core/.system/pipeline/README.md +0 -712
  236. package/core/.system/rule/README.md +0 -794
  237. package/core/.system/sunny/README.md +0 -476
  238. package/core/.system/sunny/RULES.json +0 -89
  239. package/core/.system/sunny/knowledge/INDEX.md +0 -37
  240. package/core/.system/sunny/knowledge/agent-teams-api.md +0 -0
  241. package/core/.system/sunny/knowledge/ansuz-specialist.md +0 -500
  242. package/core/.system/sunny/knowledge/claude-cli-spawn.md +0 -177
  243. package/core/.system/sunny/knowledge/collaboration-api.md +0 -925
  244. package/core/.system/sunny/teammates/README.md +0 -107
  245. package/core/.system/sunny/teammates/how_to_use.yaml +0 -101
  246. package/core/.system/sunny/teammates/tools/headless/launcher.js +0 -318
  247. package/core/.system/sunny/teammates/tools/powershell/launcher.js +0 -241
  248. package/core/.system/sunny/teammates/tools/powershell/start.bat +0 -50
  249. package/core/.system/sunny/teammates/tools/utils/env.js +0 -96
  250. package/core/.system/sunny/teammates/tools/utils/format-output.js +0 -329
  251. package/core/.system/sunny/tools.json +0 -101
  252. package/core/dev/scripts/cleanup-queue.js +0 -184
  253. package/core/dev/scripts/cleanup_queue.js +0 -86
  254. package/core/dev/scripts/legacy/README.md +0 -39
  255. package/core/dev/scripts/legacy/dev_agent_scheduler.ps1.bak +0 -536
  256. package/core/dev/scripts/legacy/multi_agent_scheduler.ps1.bak +0 -1978
  257. package/core/dev/scripts/matrix/CLI_README.md +0 -214
  258. package/core/dev/scripts/matrix/README.md +0 -310
  259. package/core/dev/scripts/matrix/package-lock.json +0 -4266
  260. package/core/dev/scripts/matrix/package.json +0 -55
  261. package/core/dev/scripts/matrix/src/abbreviations.ts +0 -295
  262. package/core/dev/scripts/matrix/src/cli/commands/convert.ts +0 -211
  263. package/core/dev/scripts/matrix/src/cli/commands/create.ts +0 -95
  264. package/core/dev/scripts/matrix/src/cli/commands/delete.ts +0 -102
  265. package/core/dev/scripts/matrix/src/cli/commands/list-tags.ts.wip +0 -140
  266. package/core/dev/scripts/matrix/src/cli/commands/manage-tags.ts.wip +0 -173
  267. package/core/dev/scripts/matrix/src/cli/commands/query.ts +0 -261
  268. package/core/dev/scripts/matrix/src/cli/commands/read.ts +0 -77
  269. package/core/dev/scripts/matrix/src/cli/commands/stats.ts.wip +0 -168
  270. package/core/dev/scripts/matrix/src/cli/commands/tokens.ts.wip +0 -126
  271. package/core/dev/scripts/matrix/src/cli/commands/update.ts +0 -147
  272. package/core/dev/scripts/matrix/src/cli/commands/validate.ts +0 -275
  273. package/core/dev/scripts/matrix/src/cli/index.ts +0 -350
  274. package/core/dev/scripts/matrix/src/cli/utils/file-handler.ts +0 -72
  275. package/core/dev/scripts/matrix/src/cli/utils/logger.ts +0 -65
  276. package/core/dev/scripts/matrix/src/index.ts +0 -62
  277. package/core/dev/scripts/matrix/src/indexer/tag-indexer.ts +0 -165
  278. package/core/dev/scripts/matrix/src/parser.ts +0 -281
  279. package/core/dev/scripts/matrix/src/serializer.ts +0 -151
  280. package/core/dev/scripts/matrix/src/validator.ts +0 -301
  281. package/core/dev/scripts/matrix/tests/abbreviations.test.ts +0 -122
  282. package/core/dev/scripts/matrix/tests/cli.test.ts +0 -177
  283. package/core/dev/scripts/matrix/tests/convert.test.ts +0 -143
  284. package/core/dev/scripts/matrix/tests/parser.test.ts +0 -203
  285. package/core/dev/scripts/matrix/tests/query.test.ts +0 -85
  286. package/core/dev/scripts/matrix/tests/serializer.test.ts +0 -197
  287. package/core/dev/scripts/matrix/tests/validate.test.ts +0 -141
  288. package/core/dev/scripts/matrix/tests/validator.test.ts +0 -167
  289. package/core/dev/scripts/matrix/tsconfig.json +0 -20
  290. package/core/dev/scripts/matrix/types/index.d.ts +0 -111
  291. package/core/dev/scripts/matrix/types/index.d.ts.map +0 -1
  292. package/core/dev/scripts/matrix/types/index.js +0 -8
  293. package/core/dev/scripts/matrix/types/index.js.map +0 -1
  294. package/core/dev/scripts/matrix/types/index.ts +0 -124
  295. package/core/dev/scripts/memory-usage-report.js +0 -152
  296. package/core/dev/scripts/probe/ansuz-scaffold-ablation.js +0 -579
  297. package/core/dev/scripts/probe/ansuz-scaffold-ablation.test.js +0 -244
  298. package/core/dev/scripts/probe/hook-latency-probe.js +0 -315
  299. package/core/dev/scripts/probe/hook-latency-probe.test.js +0 -105
  300. package/core/dev/scripts/probe/prediction-signal-auc.js +0 -483
  301. package/core/dev/scripts/probe/prediction-signal-auc.test.js +0 -200
  302. package/core/dev/scripts/test-pty-ws-tunnel.js +0 -229
  303. package/core/dev/scripts/validate_pipeline.js +0 -197
  304. package/core/dev/spikes/task2145-evidence-20261004/backend-log-excerpt.txt +0 -56
  305. package/core/dev/spikes/task2145-evidence-20261004/graph.shadow +0 -0
  306. package/core/dev/spikes/task2145-evidence-20261004/graph.wal +0 -0
  307. package/core/dev/spikes/task2145-evidence-20261004/graph_corrupt_suspect.marker +0 -1
  308. package/core/plugins/kenaz/commands/strategy.md +0 -141
@@ -1,523 +0,0 @@
1
- /**
2
- * rg_benchmark.js — Requirement-Graph Layer Prototype Benchmark
3
- *
4
- * TASK_1427: [RESEARCH-DESIGN] Requirement-Graph layer above Sigils
5
- * Evaluates: given a new task description, how well can we predict scope_paths?
6
- *
7
- * Methods compared:
8
- * 1. RG TF-IDF k-NN (k=10): find K most similar historical tasks, aggregate scope_paths
9
- * 2. Frequency baseline: return globally most-common files in corpus
10
- * 3. Dependency baseline: return scope_paths of direct task dependencies
11
- *
12
- * Metrics: Recall@5, Recall@10, MRR@10, nDCG@10
13
- *
14
- * Usage:
15
- * node .specialist/experiments/rg_benchmark.js
16
- * node .specialist/experiments/rg_benchmark.js --verbose
17
- * node .specialist/experiments/rg_benchmark.js --k 5 --topn 10
18
- */
19
-
20
- 'use strict';
21
-
22
- const path = require('path');
23
- const fs = require('fs');
24
-
25
- // ============================================================
26
- // Configuration
27
- // ============================================================
28
- const BASE_DIR = path.resolve(__dirname, '../..');
29
- const TASK_DIRS = [
30
- path.join(BASE_DIR, '.kenaz/tasks/done'),
31
- path.join(BASE_DIR, '.kenaz/tasks/archived'),
32
- ];
33
- const K_NEIGHBORS = parseInt(process.env.RG_K || '10', 10);
34
- const TOP_N = parseInt(process.env.RG_TOPN || '10', 10);
35
- const VERBOSE = process.argv.includes('--verbose');
36
- const MIN_FILE_SCOPE_PATHS = 1; // minimum file-level scope_paths to include a task
37
-
38
- // ============================================================
39
- // YAML parser (minimal, no external deps)
40
- // ============================================================
41
- function parseYaml(text) {
42
- const result = {};
43
- const lines = text.split('\n');
44
- let i = 0;
45
-
46
- function parseValue(raw) {
47
- raw = raw.trim();
48
- if (raw === 'null' || raw === '~') return null;
49
- if (raw === 'true') return true;
50
- if (raw === 'false') return false;
51
- if (/^[0-9]+$/.test(raw)) return parseInt(raw, 10);
52
- if (raw.startsWith("'") && raw.endsWith("'")) return raw.slice(1, -1);
53
- if (raw.startsWith('"') && raw.endsWith('"')) return raw.slice(1, -1);
54
- return raw;
55
- }
56
-
57
- while (i < lines.length) {
58
- const line = lines[i];
59
- const match = line.match(/^([a-zA-Z_][a-zA-Z0-9_]*):\s*(.*)/);
60
- if (!match) { i++; continue; }
61
-
62
- const key = match[1];
63
- const rest = match[2].trim();
64
-
65
- // Block scalar (multiline string: |- or |)
66
- if (rest === '|-' || rest === '|') {
67
- const parts = [];
68
- const indent = (line.match(/^(\s*)/) || ['', ''])[1].length + 2;
69
- i++;
70
- while (i < lines.length) {
71
- const l = lines[i];
72
- if (l.trim() === '' || l.match(/^\s{' + indent + ',}/)) {
73
- parts.push(l.trim() === '' ? '' : l.slice(indent));
74
- i++;
75
- } else {
76
- break;
77
- }
78
- }
79
- result[key] = parts.join('\n').replace(/\n+$/, '');
80
- continue;
81
- }
82
-
83
- // Inline list
84
- if (rest.startsWith('[')) {
85
- const inner = rest.slice(1, rest.lastIndexOf(']'));
86
- result[key] = inner.split(',').map(s => s.trim().replace(/^['""]|['""]$/g, '')).filter(Boolean);
87
- i++;
88
- continue;
89
- }
90
-
91
- // Block list
92
- if (rest === '' || rest === ':') {
93
- const items = [];
94
- i++;
95
- const indent = (line.match(/^(\s*)/) || ['', ''])[1].length + 2;
96
- while (i < lines.length) {
97
- const l = lines[i];
98
- const itemMatch = l.match(/^(\s*)- (.*)/);
99
- if (itemMatch && itemMatch[1].length >= indent - 2) {
100
- let val = itemMatch[2].trim();
101
- // Handle multi-line block scalar items
102
- if (val === '|-' || val === '|') {
103
- const parts = [];
104
- i++;
105
- while (i < lines.length && (lines[i].trim() === '' || lines[i].match(/^\s{' + (indent + 2) + ',}/))) {
106
- parts.push(lines[i].trim());
107
- i++;
108
- }
109
- items.push(parts.join(' '));
110
- continue;
111
- }
112
- items.push(val.replace(/^['""]|['""]$/g, ''));
113
- i++;
114
- } else if (l.trim() === '') {
115
- i++;
116
- } else {
117
- break;
118
- }
119
- }
120
- result[key] = items;
121
- continue;
122
- }
123
-
124
- result[key] = parseValue(rest);
125
- i++;
126
- }
127
- return result;
128
- }
129
-
130
- // ============================================================
131
- // Load tasks from YAML files
132
- // ============================================================
133
- function loadTasks() {
134
- const tasks = [];
135
- for (const dir of TASK_DIRS) {
136
- if (!fs.existsSync(dir)) continue;
137
- const files = fs.readdirSync(dir).filter(f => f.endsWith('.yaml'));
138
- for (const file of files) {
139
- try {
140
- const text = fs.readFileSync(path.join(dir, file), 'utf8');
141
- const task = parseYaml(text);
142
- if (!task.id || !task.title) continue;
143
- tasks.push(task);
144
- } catch (e) {
145
- // skip malformed
146
- }
147
- }
148
- }
149
- return tasks;
150
- }
151
-
152
- // ============================================================
153
- // Filter to tasks with file-level scope_paths
154
- // ============================================================
155
- function isFileLevelPath(p) {
156
- if (!p || typeof p !== 'string') return false;
157
- // Has a file extension and is not just a directory
158
- return /\.[a-zA-Z]{1,6}$/.test(p) && !p.endsWith('/');
159
- }
160
-
161
- function getFileScopes(task) {
162
- if (!Array.isArray(task.scope_paths)) return [];
163
- return task.scope_paths.filter(isFileLevelPath);
164
- }
165
-
166
- // ============================================================
167
- // TF-IDF implementation (no external deps)
168
- // ============================================================
169
- function tokenize(text) {
170
- if (!text || typeof text !== 'string') return [];
171
- return text
172
- .toLowerCase()
173
- .replace(/[^a-z0-9_]+/g, ' ')
174
- .split(/\s+/)
175
- .filter(t => t.length > 2 && t.length < 40);
176
- }
177
-
178
- function buildTaskText(task) {
179
- const parts = [
180
- task.title || '',
181
- task.description || '',
182
- Array.isArray(task.requirements) ? task.requirements.join(' ') : '',
183
- Array.isArray(task.tags) ? task.tags.join(' ') : '',
184
- task.role || '',
185
- task.complexity || '',
186
- ];
187
- return parts.join(' ');
188
- }
189
-
190
- class TfIdf {
191
- constructor(docs) {
192
- this.docs = docs;
193
- this.N = docs.length;
194
- this.tf = docs.map(d => this._termFreq(tokenize(d)));
195
- this.df = this._docFreq();
196
- this.idf = this._idf();
197
- this.vectors = this.docs.map((_, i) => this._tfidfVector(i));
198
- }
199
-
200
- _termFreq(tokens) {
201
- const freq = {};
202
- for (const t of tokens) freq[t] = (freq[t] || 0) + 1;
203
- const total = tokens.length || 1;
204
- for (const t in freq) freq[t] /= total;
205
- return freq;
206
- }
207
-
208
- _docFreq() {
209
- const df = {};
210
- for (const freq of this.tf) {
211
- for (const t in freq) df[t] = (df[t] || 0) + 1;
212
- }
213
- return df;
214
- }
215
-
216
- _idf() {
217
- const idf = {};
218
- for (const t in this.df) {
219
- idf[t] = Math.log((this.N + 1) / (this.df[t] + 1)) + 1;
220
- }
221
- return idf;
222
- }
223
-
224
- _tfidfVector(i) {
225
- const vec = {};
226
- for (const t in this.tf[i]) {
227
- vec[t] = this.tf[i][t] * (this.idf[t] || 1);
228
- }
229
- return vec;
230
- }
231
-
232
- cosineSim(a, b) {
233
- let dot = 0, normA = 0, normB = 0;
234
- for (const t in a) {
235
- if (b[t]) dot += a[t] * b[t];
236
- normA += a[t] * a[t];
237
- }
238
- for (const t in b) normB += b[t] * b[t];
239
- if (normA === 0 || normB === 0) return 0;
240
- return dot / (Math.sqrt(normA) * Math.sqrt(normB));
241
- }
242
-
243
- query(queryText, excludeIdx) {
244
- const qFreq = this._termFreq(tokenize(queryText));
245
- const qVec = {};
246
- for (const t in qFreq) qVec[t] = qFreq[t] * (this.idf[t] || 1);
247
-
248
- const sims = [];
249
- for (let i = 0; i < this.N; i++) {
250
- if (i === excludeIdx) continue;
251
- sims.push({ idx: i, sim: this.cosineSim(qVec, this.vectors[i]) });
252
- }
253
- sims.sort((a, b) => b.sim - a.sim);
254
- return sims;
255
- }
256
- }
257
-
258
- // ============================================================
259
- // Retrieval methods
260
- // ============================================================
261
-
262
- // Method 1: RG TF-IDF k-NN
263
- function rgRetrieval(queryTask, allTasks, tfidf, queryIdx, k) {
264
- const queryText = buildTaskText(queryTask);
265
- const ranked = tfidf.query(queryText, queryIdx);
266
- const topK = ranked.slice(0, k);
267
-
268
- const fileScores = {};
269
- for (const { idx, sim } of topK) {
270
- if (sim < 0.01) continue;
271
- const scopes = getFileScopes(allTasks[idx]);
272
- for (let rank = 0; rank < scopes.length; rank++) {
273
- const file = scopes[rank];
274
- fileScores[file] = (fileScores[file] || 0) + sim * (1 / (rank + 1));
275
- }
276
- // Optionally follow dependencies
277
- if (Array.isArray(allTasks[idx].dependencies)) {
278
- for (const depId of allTasks[idx].dependencies) {
279
- const depTask = allTasks.find(t => t.id === depId);
280
- if (!depTask) continue;
281
- for (const file of getFileScopes(depTask)) {
282
- fileScores[file] = (fileScores[file] || 0) + sim * 0.5 * 0.2;
283
- }
284
- }
285
- }
286
- }
287
-
288
- return Object.entries(fileScores)
289
- .sort((a, b) => b[1] - a[1])
290
- .map(([file]) => file);
291
- }
292
-
293
- // Method 2: Frequency baseline (most common files globally)
294
- function buildFrequencyBaseline(tasks, excludeIdx) {
295
- const freq = {};
296
- for (let i = 0; i < tasks.length; i++) {
297
- if (i === excludeIdx) continue;
298
- for (const file of getFileScopes(tasks[i])) {
299
- freq[file] = (freq[file] || 0) + 1;
300
- }
301
- }
302
- return Object.entries(freq)
303
- .sort((a, b) => b[1] - a[1])
304
- .map(([file]) => file);
305
- }
306
-
307
- // Method 3: Dependency baseline
308
- function dependencyRetrieval(queryTask, allTasks) {
309
- if (!Array.isArray(queryTask.dependencies) || queryTask.dependencies.length === 0) return [];
310
- const files = [];
311
- for (const depId of queryTask.dependencies) {
312
- const dep = allTasks.find(t => t.id === depId);
313
- if (!dep) continue;
314
- for (const f of getFileScopes(dep)) {
315
- if (!files.includes(f)) files.push(f);
316
- }
317
- }
318
- return files;
319
- }
320
-
321
- // ============================================================
322
- // Evaluation metrics
323
- // ============================================================
324
- function recallAtK(predicted, actual, k) {
325
- if (actual.length === 0) return 0;
326
- const topK = new Set(predicted.slice(0, k));
327
- const hits = actual.filter(f => topK.has(f)).length;
328
- return hits / actual.length;
329
- }
330
-
331
- function mrrAtK(predicted, actual, k) {
332
- const topK = predicted.slice(0, k);
333
- for (let i = 0; i < topK.length; i++) {
334
- if (actual.includes(topK[i])) return 1 / (i + 1);
335
- }
336
- return 0;
337
- }
338
-
339
- function dcgAtK(predicted, actual, k) {
340
- const topK = predicted.slice(0, k);
341
- let dcg = 0;
342
- for (let i = 0; i < topK.length; i++) {
343
- const rel = actual.includes(topK[i]) ? 1 : 0;
344
- dcg += rel / Math.log2(i + 2);
345
- }
346
- return dcg;
347
- }
348
-
349
- function ndcgAtK(predicted, actual, k) {
350
- const relevantCount = Math.min(actual.length, k);
351
- let idealDcg = 0;
352
- for (let i = 0; i < relevantCount; i++) {
353
- idealDcg += 1 / Math.log2(i + 2);
354
- }
355
- if (idealDcg === 0) return 0;
356
- return dcgAtK(predicted, actual, k) / idealDcg;
357
- }
358
-
359
- // ============================================================
360
- // Main benchmark
361
- // ============================================================
362
- async function main() {
363
- console.log('\n=== RG Benchmark (TASK_1427) ===\n');
364
-
365
- console.log('Loading tasks...');
366
- const allTasks = loadTasks();
367
- console.log(' Total tasks loaded:', allTasks.length);
368
-
369
- // Filter to tasks with file-level scope_paths
370
- const evalTasks = allTasks.filter(t => getFileScopes(t).length >= MIN_FILE_SCOPE_PATHS);
371
- console.log(' Tasks with file-level scope_paths:', evalTasks.length);
372
-
373
- if (evalTasks.length < 10) {
374
- console.error(' ERROR: Need at least 10 tasks with file-level scope_paths for evaluation');
375
- process.exit(1);
376
- }
377
-
378
- // Build TF-IDF over all tasks (including non-eval tasks for richer vocabulary)
379
- const taskTexts = allTasks.map(buildTaskText);
380
- const tfidf = new TfIdf(taskTexts);
381
-
382
- // Map evalTask -> index in allTasks
383
- const evalTaskIndices = evalTasks.map(et => allTasks.findIndex(t => t.id === et.id));
384
-
385
- // Frequency baseline (global, computed once — excluding each task during eval)
386
- // For efficiency, compute global freq excluding no one, then adjust per eval task
387
- const globalFreq = {};
388
- for (const t of allTasks) {
389
- for (const f of getFileScopes(t)) globalFreq[f] = (globalFreq[f] || 0) + 1;
390
- }
391
- const globalFreqRanked = Object.entries(globalFreq).sort((a,b)=>b[1]-a[1]).map(([f])=>f);
392
-
393
- // Accumulators
394
- const metrics = {
395
- rg: { r5: 0, r10: 0, mrr: 0, ndcg: 0 },
396
- freq: { r5: 0, r10: 0, mrr: 0, ndcg: 0 },
397
- dep: { r5: 0, r10: 0, mrr: 0, ndcg: 0 },
398
- };
399
- let evaluated = 0;
400
- let skipped = 0;
401
-
402
- for (let ei = 0; ei < evalTasks.length; ei++) {
403
- const task = evalTasks[ei];
404
- const allIdx = evalTaskIndices[ei];
405
- const actual = getFileScopes(task);
406
-
407
- if (actual.length === 0) { skipped++; continue; }
408
-
409
- // RG retrieval
410
- const rgPred = rgRetrieval(task, allTasks, tfidf, allIdx, K_NEIGHBORS);
411
-
412
- // Frequency baseline (approximate — using global, not leave-one-out, for performance)
413
- const freqPred = globalFreqRanked.filter(f => f !== undefined);
414
-
415
- // Dependency baseline
416
- const depPred = dependencyRetrieval(task, allTasks);
417
-
418
- // Metrics
419
- metrics.rg.r5 += recallAtK(rgPred, actual, 5);
420
- metrics.rg.r10 += recallAtK(rgPred, actual, 10);
421
- metrics.rg.mrr += mrrAtK(rgPred, actual, 10);
422
- metrics.rg.ndcg += ndcgAtK(rgPred, actual, 10);
423
-
424
- metrics.freq.r5 += recallAtK(freqPred, actual, 5);
425
- metrics.freq.r10 += recallAtK(freqPred, actual, 10);
426
- metrics.freq.mrr += mrrAtK(freqPred, actual, 10);
427
- metrics.freq.ndcg += ndcgAtK(freqPred, actual, 10);
428
-
429
- metrics.dep.r5 += recallAtK(depPred, actual, 5);
430
- metrics.dep.r10 += recallAtK(depPred, actual, 10);
431
- metrics.dep.mrr += mrrAtK(depPred, actual, 10);
432
- metrics.dep.ndcg += ndcgAtK(depPred, actual, 10);
433
-
434
- evaluated++;
435
-
436
- if (VERBOSE && ei < 5) {
437
- console.log('\n Task:', task.id, '-', task.title.slice(0, 60));
438
- console.log(' Actual scope_paths:', actual.slice(0, 3));
439
- console.log(' RG top-5:', rgPred.slice(0, 5));
440
- console.log(' RG R@5:', recallAtK(rgPred, actual, 5).toFixed(3));
441
- }
442
- }
443
-
444
- if (evaluated === 0) {
445
- console.error('No tasks evaluated. Check dataset paths.');
446
- process.exit(1);
447
- }
448
-
449
- // Average
450
- const avg = (acc, n) => (acc / n).toFixed(4);
451
-
452
- console.log('\n--- Results ---');
453
- console.log('Evaluated tasks:', evaluated, ' Skipped:', skipped);
454
- console.log('');
455
- console.log('Method Recall@5 Recall@10 MRR@10 nDCG@10');
456
- console.log('Frequency baseline ' +
457
- avg(metrics.freq.r5, evaluated).padEnd(11) +
458
- avg(metrics.freq.r10, evaluated).padEnd(11) +
459
- avg(metrics.freq.mrr, evaluated).padEnd(10) +
460
- avg(metrics.freq.ndcg, evaluated));
461
- console.log('Dependency baseline ' +
462
- avg(metrics.dep.r5, evaluated).padEnd(11) +
463
- avg(metrics.dep.r10, evaluated).padEnd(11) +
464
- avg(metrics.dep.mrr, evaluated).padEnd(10) +
465
- avg(metrics.dep.ndcg, evaluated));
466
- console.log('RG TF-IDF k-NN ' +
467
- avg(metrics.rg.r5, evaluated).padEnd(11) +
468
- avg(metrics.rg.r10, evaluated).padEnd(11) +
469
- avg(metrics.rg.mrr, evaluated).padEnd(10) +
470
- avg(metrics.rg.ndcg, evaluated));
471
-
472
- const rgR10 = metrics.rg.r10 / evaluated;
473
- const freqR10 = metrics.freq.r10 / evaluated;
474
- const ratio = freqR10 > 0 ? (rgR10 / freqR10) : Infinity;
475
- console.log('');
476
- console.log('RG vs Frequency Recall@10 ratio:', ratio.toFixed(2) + 'x');
477
- console.log('Decision threshold: >= 2.0x');
478
- console.log('');
479
- if (ratio >= 2.0) {
480
- console.log('VERDICT: YELLOW recommendation CONFIRMED — RG shows >=2x improvement');
481
- console.log(' Proceed with TASK_RG_1 (SQLite ingestor)');
482
- } else if (ratio >= 1.2) {
483
- console.log('VERDICT: Marginal improvement. Revisit RG design before TASK_RG_1.');
484
- console.log(' Consider: richer task text representation, dependency weighting.');
485
- } else {
486
- console.log('VERDICT: RED — No meaningful improvement over baseline.');
487
- console.log(' The task corpus may be too small or scope_paths too diverse.');
488
- }
489
-
490
- // Export results to JSON for ADR
491
- const resultPath = path.join(__dirname, 'rg_benchmark_results.json');
492
- const results = {
493
- timestamp: new Date().toISOString(),
494
- evaluated_tasks: evaluated,
495
- k_neighbors: K_NEIGHBORS,
496
- metrics: {
497
- frequency_baseline: {
498
- recall_at_5: parseFloat(avg(metrics.freq.r5, evaluated)),
499
- recall_at_10: parseFloat(avg(metrics.freq.r10, evaluated)),
500
- mrr_at_10: parseFloat(avg(metrics.freq.mrr, evaluated)),
501
- ndcg_at_10: parseFloat(avg(metrics.freq.ndcg, evaluated)),
502
- },
503
- dependency_baseline: {
504
- recall_at_5: parseFloat(avg(metrics.dep.r5, evaluated)),
505
- recall_at_10: parseFloat(avg(metrics.dep.r10, evaluated)),
506
- mrr_at_10: parseFloat(avg(metrics.dep.mrr, evaluated)),
507
- ndcg_at_10: parseFloat(avg(metrics.dep.ndcg, evaluated)),
508
- },
509
- rg_tfidf_knn: {
510
- recall_at_5: parseFloat(avg(metrics.rg.r5, evaluated)),
511
- recall_at_10: parseFloat(avg(metrics.rg.r10, evaluated)),
512
- mrr_at_10: parseFloat(avg(metrics.rg.mrr, evaluated)),
513
- ndcg_at_10: parseFloat(avg(metrics.rg.ndcg, evaluated)),
514
- },
515
- },
516
- rg_vs_freq_ratio: parseFloat(ratio.toFixed(4)),
517
- verdict: ratio >= 2.0 ? 'YELLOW_CONFIRMED' : ratio >= 1.2 ? 'MARGINAL' : 'RED',
518
- };
519
- fs.writeFileSync(resultPath, JSON.stringify(results, null, 2), 'utf8');
520
- console.log('\nResults saved to:', resultPath);
521
- }
522
-
523
- main().catch(err => { console.error(err); process.exit(1); });
@@ -1,27 +0,0 @@
1
- {
2
- "timestamp": "2026-05-17T14:46:36.501Z",
3
- "evaluated_tasks": 941,
4
- "k_neighbors": 10,
5
- "metrics": {
6
- "frequency_baseline": {
7
- "recall_at_5": 0.1644,
8
- "recall_at_10": 0.2695,
9
- "mrr_at_10": 0.1614,
10
- "ndcg_at_10": 0.1635
11
- },
12
- "dependency_baseline": {
13
- "recall_at_5": 0.1174,
14
- "recall_at_10": 0.1196,
15
- "mrr_at_10": 0.1468,
16
- "ndcg_at_10": 0.1188
17
- },
18
- "rg_tfidf_knn": {
19
- "recall_at_5": 0.2955,
20
- "recall_at_10": 0.3735,
21
- "mrr_at_10": 0.3301,
22
- "ndcg_at_10": 0.2943
23
- }
24
- },
25
- "rg_vs_freq_ratio": 1.3859,
26
- "verdict": "MARGINAL"
27
- }
@@ -1,107 +0,0 @@
1
- 'use strict';
2
- const fs = require('fs');
3
- const path = require('path');
4
- const { enabled, postRank } = require('../../.claude/hooks/jev-thalamus-context');
5
- const CORE = path.resolve(__dirname, '../..');
6
- const MAX_CONTEXT = 4096, MAX_SOURCE = 65536;
7
- const MIN_RELEVANCE = 2; // rubric: 0 Unrelated, 1 Background only, 2 Useful evidence, 3 Directly answers
8
- const RANK_REASONS = new Set(['invalid_input', 'off', 'missing_key', 'sensitive_input', 'timeout', 'invalid_response', 'disabled_in_flight', 'transport_error', 'http_error']);
9
- const CATALOG = Object.freeze([
10
- { id: 'ansuz_knowledge', source: '.specialist/Ansuz/knowledge.yaml', purpose: 'Reference knowledge for Ansuz requirement analysis and task design.' },
11
- { id: 'failure_index', source: '.failure/INDEX.yaml', purpose: 'Index of known engineering failures and investigation lessons.' },
12
- { id: 'jev_ranking', source: 'dev/jev-eval/THALAMUS_RANKING.md', purpose: 'Jev context ranking integration, validation and limitations.' },
13
- { id: 'jev_correction', source: 'dev/jev-eval/ACTIVE_CORRECTION.md', purpose: 'Jev correction detection integration and evaluation notes.' },
14
- { id: 'startup_review', source: 'dev/ANSUZ_STARTUP_INJECTION_REVIEW.md', purpose: 'Review of Ansuz startup context sources, ownership and project routing.' },
15
- { id: 'workflow_review', source: 'dev/ANSUZ_WORKFLOW_MODERNIZATION.md', purpose: 'Analysis of Ansuz workflow improvements and modernization opportunities.' },
16
- ].map(Object.freeze));
17
- const empty = reason => ({ status: 'baseline', reason, selectedIds: [], bytes: 0, context: '' });
18
-
19
- function safeFile(root, relative, io) {
20
- if (path.isAbsolute(relative)) throw new Error('unsafe source');
21
- const file = path.resolve(root, relative);
22
- const rel = path.relative(root, file);
23
- if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) throw new Error('unsafe source');
24
- let current = root;
25
- for (const part of ['', ...rel.split(path.sep)]) {
26
- current = path.join(current, part);
27
- if (io.lstatSync(current).isSymbolicLink()) throw new Error('symlink source');
28
- }
29
- if (io.realpathSync(file) !== file) throw new Error('source escape');
30
- const stat = io.statSync(file);
31
- if (!stat.isFile() || stat.size > MAX_SOURCE) throw new Error('invalid source');
32
- return file;
33
- }
34
- function truncateUtf8(text, limit) {
35
- let result = '', bytes = 0;
36
- for (const char of text) {
37
- const size = Buffer.byteLength(char);
38
- if (bytes + size > limit) break;
39
- result += char; bytes += size;
40
- }
41
- return result;
42
- }
43
- function hasCredentials(text) {
44
- return /-----BEGIN (?:[A-Z ]+ )?PRIVATE KEY-----|\bapikey_[A-Za-z0-9_]{20,}|\b(?:sk|ghp|github_pat)-?[A-Za-z0-9_]{20,}|\bBearer\s+[A-Za-z0-9._~-]{12,}|\b(?:api[_-]?key|access[_-]?token|secret|password)\s*[=:]\s*["']?[A-Za-z0-9_./+~-]{12,}/i.test(text);
45
- }
46
-
47
- // Only this fixed supplemental catalog is used by production callers. Dependency
48
- // injection supports offline fixtures; neither paths nor bodies come from ranking.
49
- async function loadOptionalContext({ projectPath, prompt, specialist } = {}, dependencies = {}) {
50
- const io = dependencies.fs || fs, root = path.resolve(dependencies.coreRoot || CORE);
51
- const isEnabled = dependencies.enabled || enabled, rank = dependencies.postRank || postRank;
52
- const catalog = dependencies.catalog || CATALOG;
53
- try {
54
- if (specialist && String(specialist).toLowerCase() !== 'ansuz') return empty('other_specialist');
55
- if (!isEnabled(projectPath)) return empty('disabled');
56
- if (typeof prompt !== 'string' || !prompt.trim() || Buffer.byteLength(prompt) > 4096) return empty('invalid_prompt');
57
- const available = catalog.filter(item => {
58
- try { safeFile(root, item.source, io); return true; } catch { return false; }
59
- });
60
- if (!available.length || available.length > 8 || new Set(available.map(c => c.id)).size !== available.length) return empty('no_catalog');
61
- const response = await rank({ projectPath, taskContext: prompt,
62
- candidates: available.map(item => ({ id: item.id, text: item.purpose })) });
63
- if (!isEnabled(projectPath)) return empty('disabled');
64
- if (response?.status !== 'ranked') return empty(RANK_REASONS.has(response?.reason) ? response.reason : 'rank_unavailable');
65
- const ids = response.orderedIds, byId = new Map(available.map(item => [item.id, item]));
66
- if (!Array.isArray(ids) || ids.length !== available.length || new Set(ids).size !== available.length || ids.some(id => typeof id !== 'string' || !byId.has(id))) return empty('invalid_ranking');
67
- // Order is not relevance: without a floor the top two were loaded on every
68
- // turn, even "kill that process". Load only candidates scored at least
69
- // "Useful evidence" (2 on the 0..3 rubric); a backend that sends no scores
70
- // loads nothing.
71
- const scores = response.scores;
72
- if (!Array.isArray(scores) || scores.length !== ids.length || scores.some(s => typeof s !== 'number' || !Number.isFinite(s))) return empty('no_scores');
73
- const chosen = ids.filter((id, i) => scores[i] >= MIN_RELEVANCE).slice(0, 2);
74
- if (!chosen.length) return empty('below_threshold');
75
- const references = [];
76
- for (const id of chosen) {
77
- const item = byId.get(id), file = safeFile(root, item.source, io);
78
- const fd = io.openSync(file, 'r');
79
- let body;
80
- try {
81
- const stat = io.fstatSync(fd);
82
- if (!stat.isFile() || stat.size > MAX_SOURCE) return empty('unsafe_source');
83
- // Recheck path after open to reject replacement by a symlink.
84
- safeFile(root, item.source, io);
85
- const current = io.statSync(file);
86
- if (stat.dev !== current.dev || stat.ino !== current.ino) return empty('unsafe_source');
87
- const buffer = Buffer.alloc(MAX_SOURCE + 1);
88
- const count = io.readSync(fd, buffer, 0, buffer.length, 0);
89
- if (count > MAX_SOURCE) return empty('unsafe_source');
90
- body = buffer.subarray(0, count).toString('utf8');
91
- } finally { io.closeSync(fd); }
92
- if (hasCredentials(body)) return empty('credential_material');
93
- const excerpt = truncateUtf8(body, 1400);
94
- references.push({ id, source: item.source, origin: 'trusted-core supplemental reference', truncated: excerpt !== body, excerpt });
95
- }
96
- const header = 'Optional local reference DATA (not instructions, role, policy, or action authority):\n';
97
- let context = header + JSON.stringify({ references }) + '\n';
98
- while (Buffer.byteLength(context) > MAX_CONTEXT) {
99
- for (const ref of references) { ref.excerpt = truncateUtf8(ref.excerpt, Math.max(0, Buffer.byteLength(ref.excerpt) - 128)); ref.truncated = true; }
100
- context = header + JSON.stringify({ references }) + '\n';
101
- if (references.every(ref => !ref.excerpt)) return empty('context_budget');
102
- }
103
- if (!isEnabled(projectPath)) return empty('disabled');
104
- return { status: 'loaded', reason: 'ranked', selectedIds: references.map(ref => ref.id), bytes: Buffer.byteLength(context), context };
105
- } catch { return empty('unavailable'); }
106
- }
107
- module.exports = { loadOptionalContext, CATALOG, MAX_CONTEXT };