@claude-flow/cli 3.38.12 → 3.38.14

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 (141) hide show
  1. package/.claude/.proven-config-version +1 -0
  2. package/.claude/helpers/.helpers-version +1 -1
  3. package/.claude/helpers/helpers.manifest.json +2 -2
  4. package/.claude/helpers/statusline.cjs +0 -0
  5. package/.claude/proven-config.json +42 -0
  6. package/catalog-manifest.json +4 -4
  7. package/dist/src/mcp-tools/hooks-tools.js +6 -1
  8. package/dist/src/memory/memory-bridge.js +13 -2
  9. package/dist/src/ruvector/lattice-wasm.d.ts +14 -0
  10. package/dist/src/ruvector/lattice-wasm.js +144 -0
  11. package/dist/src/services/flywheel-receipt.d.ts +10 -0
  12. package/dist/src/services/flywheel-receipt.js +82 -7
  13. package/dist/src/services/flywheel-transaction.js +10 -1
  14. package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
  15. package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
  16. package/node_modules/@claude-flow/security/dist/input-validator.d.ts +6 -6
  17. package/package.json +1 -1
  18. package/plugins/ruflo-metaharness/.claude-flow/daemon-state.json +178 -0
  19. package/plugins/ruflo-metaharness/.claude-flow/daemon.pid +1 -0
  20. package/plugins/ruflo-metaharness/.claude-flow/data/pending-insights.jsonl +5 -0
  21. package/plugins/ruflo-metaharness/.claude-flow/logs/daemon.log +269 -0
  22. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783604774864_ozbujc_prompt.log +19 -0
  23. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783604774864_ozbujc_result.log +108 -0
  24. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783605513587_ulvmpb_prompt.log +19 -0
  25. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783605513587_ulvmpb_result.log +209 -0
  26. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783606368867_ahysui_prompt.log +19 -0
  27. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783606368867_ahysui_result.log +192 -0
  28. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783607120257_lh05rb_prompt.log +19 -0
  29. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783607120257_lh05rb_result.log +13 -0
  30. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783608020362_j3096j_prompt.log +19 -0
  31. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783608020362_j3096j_result.log +120 -0
  32. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783608776347_261b61_prompt.log +19 -0
  33. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783608776347_261b61_result.log +85 -0
  34. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783609621359_s5i6ye_prompt.log +19 -0
  35. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783609621359_s5i6ye_result.log +13 -0
  36. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783610087998_qihv9v_prompt.log +19 -0
  37. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783610087998_qihv9v_result.log +17 -0
  38. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783610773920_qlzmxo_prompt.log +19 -0
  39. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783610773920_qlzmxo_result.log +17 -0
  40. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783611376090_xpqf1z_prompt.log +19 -0
  41. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783611376090_xpqf1z_result.log +16 -0
  42. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783612097184_9rqfor_prompt.log +19 -0
  43. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783612097184_9rqfor_result.log +138 -0
  44. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783612811574_4u602j_prompt.log +19 -0
  45. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783612811574_4u602j_result.log +16 -0
  46. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783613750487_a46ttn_prompt.log +19 -0
  47. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783613750487_a46ttn_result.log +107 -0
  48. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783614289360_figwc0_prompt.log +19 -0
  49. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783614289360_figwc0_result.log +200 -0
  50. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783615067640_l7tm6a_prompt.log +19 -0
  51. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783615067640_l7tm6a_result.log +54 -0
  52. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783615825308_44nor5_prompt.log +19 -0
  53. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783615825308_44nor5_result.log +85 -0
  54. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783616524771_ut1ftw_prompt.log +19 -0
  55. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783616524771_ut1ftw_result.log +266 -0
  56. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783617323039_fs3x5a_prompt.log +19 -0
  57. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783617323039_fs3x5a_result.log +56 -0
  58. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783618049184_1f4yah_prompt.log +19 -0
  59. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783618049184_1f4yah_result.log +96 -0
  60. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783618793925_4ee7tf_prompt.log +19 -0
  61. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783618793925_4ee7tf_result.log +481 -0
  62. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783619642574_yjr4mm_prompt.log +19 -0
  63. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783619642574_yjr4mm_result.log +104 -0
  64. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783620392771_oduto0_prompt.log +19 -0
  65. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783620392771_oduto0_result.log +148 -0
  66. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783621183670_gd0p1x_prompt.log +19 -0
  67. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783621183670_gd0p1x_result.log +111 -0
  68. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783621738388_z4k48b_prompt.log +19 -0
  69. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783621738388_z4k48b_result.log +89 -0
  70. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783622493677_zwc35w_prompt.log +19 -0
  71. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783622493677_zwc35w_result.log +207 -0
  72. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783604894861_v6n3ut_prompt.log +14 -0
  73. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783604894861_v6n3ut_result.log +66 -0
  74. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783605934532_9h8ikb_prompt.log +14 -0
  75. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783605934532_9h8ikb_result.log +68 -0
  76. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783607181736_t12f4y_prompt.log +14 -0
  77. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783607181736_t12f4y_result.log +78 -0
  78. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783608341266_zhk0fl_prompt.log +14 -0
  79. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783608341266_zhk0fl_result.log +68 -0
  80. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783609420180_7xw817_prompt.log +14 -0
  81. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783609420180_7xw817_result.log +72 -0
  82. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783610535307_2pxofp_prompt.log +14 -0
  83. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783610535307_2pxofp_result.log +17 -0
  84. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783611437445_ofwnpb_prompt.log +14 -0
  85. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783611437445_ofwnpb_result.log +60 -0
  86. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783612556827_1cb112_prompt.log +14 -0
  87. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783612556827_1cb112_result.log +56 -0
  88. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783613579531_18xiax_prompt.log +14 -0
  89. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783613579531_18xiax_result.log +74 -0
  90. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783614650481_2zdz7w_prompt.log +14 -0
  91. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783614650481_2zdz7w_result.log +68 -0
  92. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783615742328_rjj69d_prompt.log +14 -0
  93. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783615742328_rjj69d_result.log +65 -0
  94. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783616767230_1iad99_prompt.log +14 -0
  95. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783616767230_1iad99_result.log +68 -0
  96. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783617783537_rqagku_prompt.log +14 -0
  97. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783617783537_rqagku_result.log +56 -0
  98. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783618817343_r1lbhr_prompt.log +14 -0
  99. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783618817343_r1lbhr_result.log +65 -0
  100. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783619907773_8msfw3_prompt.log +14 -0
  101. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783619907773_8msfw3_result.log +77 -0
  102. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783621009302_hybdum_prompt.log +14 -0
  103. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783621009302_hybdum_result.log +64 -0
  104. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783622083681_nznpnx_prompt.log +14 -0
  105. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783622083681_nznpnx_result.log +56 -0
  106. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783623208340_olsbaw_prompt.log +14 -0
  107. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783605134860_9jssz9_prompt.log +14 -0
  108. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783605134860_9jssz9_result.log +69 -0
  109. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783606516743_zftbaa_prompt.log +14 -0
  110. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783606516743_zftbaa_result.log +92 -0
  111. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783608038317_jrd66e_prompt.log +14 -0
  112. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783608038317_jrd66e_result.log +92 -0
  113. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783609388416_mc3zoe_prompt.log +14 -0
  114. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783609388416_mc3zoe_result.log +82 -0
  115. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783610821384_tzqvlb_prompt.log +14 -0
  116. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783610821384_tzqvlb_result.log +17 -0
  117. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783612023285_buygpo_prompt.log +14 -0
  118. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783612023285_buygpo_result.log +57 -0
  119. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783613599301_6f78cw_prompt.log +14 -0
  120. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783613599301_6f78cw_result.log +60 -0
  121. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783615000844_v95ues_prompt.log +14 -0
  122. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783615000844_v95ues_result.log +69 -0
  123. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783616341693_bl5d9o_prompt.log +14 -0
  124. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783616341693_bl5d9o_result.log +64 -0
  125. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783617832831_ha6s8d_prompt.log +14 -0
  126. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783617832831_ha6s8d_result.log +42 -0
  127. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783619384959_s5iiwf_prompt.log +14 -0
  128. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783619384959_s5iiwf_result.log +47 -0
  129. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783620946263_d7ovai_prompt.log +14 -0
  130. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783620946263_d7ovai_result.log +52 -0
  131. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783622473846_t839e5_prompt.log +14 -0
  132. package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783622473846_t839e5_result.log +96 -0
  133. package/plugins/ruflo-metaharness/.claude-flow/metrics/backup.json +7 -0
  134. package/plugins/ruflo-metaharness/.claude-flow/metrics/codebase-map.json +11 -0
  135. package/plugins/ruflo-metaharness/.claude-flow/metrics/consolidation.json +16 -0
  136. package/plugins/ruflo-metaharness/.claude-flow/metrics/harness-loop.json +83 -0
  137. package/plugins/ruflo-metaharness/.claude-flow/metrics/performance.json +55 -0
  138. package/plugins/ruflo-metaharness/.claude-flow/metrics/security-audit.json +206 -0
  139. package/plugins/ruflo-metaharness/.claude-flow/metrics/test-gaps.json +95 -0
  140. package/plugins/ruflo-metaharness/.claude-flow/neural/stats.json +6 -0
  141. package/plugins/ruflo-metaharness/skills/harness-gepa/SKILL.md +1 -1
@@ -0,0 +1,69 @@
1
+ [2026-07-09T16:39:01.688Z] RESULT
2
+ ============================================================
3
+ {
4
+ "success": true,
5
+ "output": "Good, all claims verified against source. I have enough to write an accurate, verified report with skeletons matching this repo's actual testing convention (the custom `assert()`/`approx()` harness used in `test-similarity.mjs`, not `node:test`).\n\n## Test Coverage Analysis — `plugins/ruflo-metaharness`\n\nThe plugin has 34 `.mjs` scripts (~6,900 LOC), no test runner/framework — existing `test-*.mjs` files are self-contained scripts using a hand-rolled `assert()`/`approx()` counter (see `test-similarity.mjs`), invoked directly with `node`. Only two modules (`_similarity.mjs`, `_harness.mjs`) export functions and get true unit coverage; everything else is either integration-tested via `spawnSync` subprocess (`test-mcp-tools.mjs`, `test-parallel-pipeline.mjs`, `test-pipeline-roundtrip.mjs`, `test-graceful-degradation.mjs`, `test-with-openrouter.mjs`) or **not tested at all**.\n\n### 1. Correctness bug hiding behind the coverage gap (highest priority)\n\n**`threat-model.mjs:9`** — `const SEVERITY_RANK = { clean: 0, low: 1, medium: 2, high: 3 }` is a **local, stale copy**. `_harness.mjs:261` documents (iter 63) that this exact class of bug was fixed everywhere else by centralizing on `rankSeverity()`, which safely maps `critical → 4` and returns `0` for unknowns instead of `undefined`. `threat-model.mjs` never migrated:\n- Line 37: `SEVERITY_RANK[worst] >= threshold` — if the upstream harness ever reports `worst: \"critical\"`, this is `undefined >= threshold` → **always `false`** → a critical finding silently never triggers the alert/exit-1, the exact NaN-compare hazard the shared module exists to prevent.\n- Line 23-26 also rejects `--fail-on critical` as invalid input, even though the underlying tool can emit that severity.\n\nNo test exists to catch this because `threat-model.mjs` exports nothing.\n\n### 2. Untested modules (zero exports, zero coverage)\n\n`evolve.mjs`, `audit-list.mjs`, `oia-audit.mjs`, `mint.mjs`, `gepa.mjs`, `audit-trend.mjs`, `drift-from-history.mjs`, `security-bench.mjs`, `learn.mjs`, `redblue.mjs`, `threat-model.mjs` — confirmed via `grep '^export'`: **none** export their internal functions, so pure logic is untestable except by black-box CLI subprocess. Highest-value functions currently untestable in isolation:\n- `evolve.mjs`: `looksLikeGepaTranscript`, `extractGepaTranscripts`, `summarizeTraces`, `buildDiagnosis` (the entire `--diagnose` feature, lines 146–274) and `safetyChecks()` bound validation\n- `security-bench.mjs`: `parseSecurityBenchMarkdown()`, `safetyChecks()`\n- `audit-list.mjs`: `parseDurationMs()`\n- `audit-trend.mjs`: `loadRecord()` malformed-JSON / missing-file branches\n- `mint.mjs`: `safetyChecks()` — path-escape guards (`targetAbs === repoRoot`, `startsWith(repoRoot + '/')`) are exactly the kind of security-relevant logic that should never rely on subprocess-only testing\n\n### 3. Edge cases not covered\n\n- Malformed/missing JSON: `audit-trend.mjs:loadRecord()` (bad `--baseline`/`--current` file), `gepa.mjs:readJsonFile()`\n- Empty/zero-record paths: `drift-from-history.mjs` \"no records found\", `audit-list.mjs` empty namespace\n- CLI arg-parsing enums never exercised with invalid input: `--fail-on`, `--sandbox` (`evolve.mjs` only allows `real|mock|agent`), `--mutator`, `--selection`, `redblue` unknown subcommand (`redblue.mjs:182`), `mint` missing `--name`/`--template` (`mint.mjs:38-45`)\n- `mint.mjs` path-escape guard itself (target == repo root, target inside repo root) — untested despite being a safety-critical guard\n- `redblue.mjs` report-JSON parse failure at line 220-225 (upstream wrote a corrupt/truncated report file)\n\n### 4. Missing error-handling tests\n\n- `audit-list.mjs` memory-command failures silently returning `[]`/`null` on non-zero exit\n- `oia-audit.mjs:persist()` store-failure path\n- `redblue.mjs` corrupt report JSON (line 220-225, shown above)\n- `ensureCachedInstall()` in `_invoke.mjs` — cache-dir-create-failed branch\n\n### 5. Integration test gaps\n\nNothing drives the real `npx ruflo metaharness <sub>` CLI dispatcher or the `commands/` slash-command layer — every existing integration test invokes `scripts/*.mjs` directly with `node`. `mint`, `redblue`, `gepa`, `learn` have **no** integration coverage at all — not even the ADR-150 graceful-degradation contract check (`degraded: true` on `MODULE_NOT_FOUND`) that the other scripts get via `test-graceful-degradation.mjs`.\n\n---\n\n## Test skeletons\n\nFollowing this repo's actual convention (custom assert harness, not `node:test`):\n\n```js\n// scripts/test-threat-model-severity.mjs — regression test for the SEVERITY_RANK drift bug\n//\n// USAGE: node scripts/test-threat-model-severity.mjs\n\nimport { readFileSync } from 'node:fs';\nimport { spawnSync } from 'node:child_process';\nimport { SEVERITY_RANK as SHARED } from './_harness.mjs';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failed++; }\n}\n\n// (a) static check: threat-model.mjs must not carry its own stale rank map\nconst src = readFileSync(new URL('./threat-model.mjs', import.meta.url), 'utf-8');\nassert(\n !/const SEVERITY_RANK = \\{/.test(src),\n 'threat-model.mjs has no local SEVERITY_RANK literal (uses shared rankSeverity instead)'\n);\n\n// (b) behavioral check: --fail-on critical must be accepted once fixed\nconst r = spawnSync('node', ['scripts/threat-model.mjs', '--fail-on', 'critical', '--path', '/nonexistent'], { encoding: 'utf-8' });\nassert(r.status !== 2 || !/must be one of clean\\|low\\|medium\\|high/.test(r.stderr || ''),\n '--fail-on critical is accepted (not rejected as invalid enum)');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) process.exit(1);\n```\n\n```js\n// scripts/test-mint-safety.mjs — unit tests for mint.mjs path-escape guards\n// Requires exporting `safetyChecks` from mint.mjs (currently module-private).\n//\n// USAGE: node scripts/test-mint-safety.mjs\n\nimport { spawnSync } from 'node:child_process';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failed++; }\n}\n\nconst cwd = process.cwd();\n\n// missing --name\nlet r = spawnSync('node', ['scripts/mint.mjs', '--template', 'minimal'], { encoding: 'utf-8' });\nassert(r.status === 2 && /--name is required/.test(r.stderr), 'rejects missing --name');\n\n// missing --template\nr = spawnSync('node', ['scripts/mint.mjs', '--name', 'foo'], { encoding: 'utf-8' });\nassert(r.status === 2 && /--template is required/.test(r.stderr), 'rejects missing --template');\n\n// refuses project root as target\nr = spawnSync('node', ['scripts/mint.mjs', '--name', 'foo', '--template', 'minimal', '--target', cwd], { encoding: 'utf-8' });\nassert(r.status === 2 && /refusing to write to project root/.test(r.stderr), 'refuses target === repo root');\n\n// refuses a path inside the repo root\nr = spawnSync('node', ['scripts/mint.mjs', '--name', 'foo', '--template', 'minimal', '--target', `${cwd}/subdir`], { encoding: 'utf-8' });\nassert(r.status === 2 && /refusing to write inside the calling repo root/.test(r.stderr), 'refuses target inside repo root');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) process.exit(1);\n```\n\n```js\n// scripts/test-evolve-diagnose.mjs — unit tests for evolve.mjs's --diagnose helpers\n// Requires exporting looksLikeGepaTranscript/extractGepaTranscripts/summarizeTraces\n// from evolve.mjs (currently module-private, untestable in isolation today).\n//\n// USAGE: node scripts/test-evolve-diagnose.mjs\n\nimport { looksLikeGepaTranscript, extractGepaTranscripts, summarizeTraces } from './evolve.mjs';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failed++; }\n}\n\nassert(looksLikeGepaTranscript([{ actionRaw: 'x' }]) === true, 'accepts actionRaw-shaped entries');\nassert(looksLikeGepaTranscript([{ obs: 'y' }]) === true, 'accepts obs-shaped entries');\nassert(looksLikeGepaTranscript([]) === false, 'rejects empty array');\nassert(looksLikeGepaTranscript([{ foo: 1 }]) === false, 'rejects entries missing actionRaw/obs');\nassert(looksLikeGepaTranscript(null) === false, 'rejects non-array input without throwing');\n\nassert(\n extractGepaTranscripts({ transcript: [{ actionRaw: 'x' }] }).length === 1,\n 'extracts record.transcript when gepa-shaped'\n);\nassert(\n extractGepaTranscripts({ traces: [{ taskId: 't1', transcript: [{ obs: 'y' }] }] }).length === 1,\n 'extracts per-trace transcript with taskId preserved'\n);\nassert(\n extractGepaTranscripts({}).length === 0,\n 'returns empty array, not throw, when no transcript-shaped data exists'\n);\n\nconst summary = summarizeTraces({ traces: [{ exitCode: 1 }, { exitCode: 0, timedOut: true }, { exitCode: 0, blockedActions: ['a', 'b'] }] });\nassert(summary.tasks === 3, 'counts total tasks');\nassert(summary.failed === 1, 'counts non-zero exitCode as failed');\nassert(summary.timedOut === 1, 'counts timedOut entries');\nassert(summary.blockedActions === 2, 'sums blockedActions across traces');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) process.exit(1);\n```\n\n```js\n// scripts/test-redblue-argv.mjs — CLI arg-parsing edge cases (no `export` needed, pure subprocess)\n//\n// USAGE: node scripts/test-redblue-argv.mjs\n\nimport { spawnSync } from 'node:child_process';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failed++; }\n}\n\nlet r = spawnSync('node', ['scripts/redblue.mjs', 'bogus-subcommand'], { encoding: 'utf-8' });\nassert(r.status === 2 && /unknown subcommand/.test(r.stderr), 'rejects unknown subcommand with exit 2');\n\nr = spawnSync('node', ['scripts/redblue.mjs'], { encoding: 'utf-8' });\nassert(r.status === 0, 'no subcommand prints help and exits 0');\n\nr = spawnSync('node', ['scripts/redblue.mjs', '--help'], { encoding: 'utf-8' });\nassert(r.status === 0 && /Subcommands:/.test(r.stdout), '--help prints usage');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) process.exit(1);\n```\n\n---\n\n**Priority order to actually fix, not just cover:**\n1. `threat-model.mjs` `SEVERITY_RANK` drift — this is a live correctness bug (critical findings never trigger `--fail-on`), fix by importing `rankSeverity`/`SEVERITY_RANK` from `_harness.mjs` and widening the `--fail-on` enum to include `critical`.\n2. Export the pure-logic functions in `evolve.mjs`, `mint.mjs`, `security-bench.mjs`, `audit-list.mjs`, `audit-trend.mjs` so they're unit-testable without subprocess overhead — currently every one of these 8+ modules is untestable in isolation by construction.\n3. Then land the skeletons above plus graceful-degradation integration coverage for `mint`/`redblue`/`gepa`/`learn` to match the other 8 scripts.\n\nI found a pre-existing `testgaps` worker run in `.claude-flow/metrics/test-gaps.json` covering similar ground — I independently verified its core claims against the source (confirmed the `SEVERITY_RANK` bug and the zero-export pattern are real) and corrected its test skeletons to match this repo's actual convention (custom assert harness, not `node:test`, which isn't used anywhere in this plugin).\n",
6
+ "parsedOutput": {
7
+ "sections": [
8
+ {
9
+ "title": "Test Coverage Analysis — `plugins/ruflo-metaharness`",
10
+ "content": "\nThe plugin has 34 `.mjs` scripts (~6,900 LOC), no test runner/framework — existing `test-*.mjs` files are self-contained scripts using a hand-rolled `assert()`/`approx()` counter (see `test-similarity.mjs`), invoked directly with `node`. Only two modules (`_similarity.mjs`, `_harness.mjs`) export functions and get true unit coverage; everything else is either integration-tested via `spawnSync` subprocess (`test-mcp-tools.mjs`, `test-parallel-pipeline.mjs`, `test-pipeline-roundtrip.mjs`, `test-graceful-degradation.mjs`, `test-with-openrouter.mjs`) or **not tested at all**.\n\n",
11
+ "level": 2
12
+ },
13
+ {
14
+ "title": "1. Correctness bug hiding behind the coverage gap (highest priority)",
15
+ "content": "\n**`threat-model.mjs:9`** — `const SEVERITY_RANK = { clean: 0, low: 1, medium: 2, high: 3 }` is a **local, stale copy**. `_harness.mjs:261` documents (iter 63) that this exact class of bug was fixed everywhere else by centralizing on `rankSeverity()`, which safely maps `critical → 4` and returns `0` for unknowns instead of `undefined`. `threat-model.mjs` never migrated:\n- Line 37: `SEVERITY_RANK[worst] >= threshold` — if the upstream harness ever reports `worst: \"critical\"`, this is `undefined >= threshold` → **always `false`** → a critical finding silently never triggers the alert/exit-1, the exact NaN-compare hazard the shared module exists to prevent.\n- Line 23-26 also rejects `--fail-on critical` as invalid input, even though the underlying tool can emit that severity.\n\nNo test exists to catch this because `threat-model.mjs` exports nothing.\n\n",
16
+ "level": 3
17
+ },
18
+ {
19
+ "title": "2. Untested modules (zero exports, zero coverage)",
20
+ "content": "\n`evolve.mjs`, `audit-list.mjs`, `oia-audit.mjs`, `mint.mjs`, `gepa.mjs`, `audit-trend.mjs`, `drift-from-history.mjs`, `security-bench.mjs`, `learn.mjs`, `redblue.mjs`, `threat-model.mjs` — confirmed via `grep '^export'`: **none** export their internal functions, so pure logic is untestable except by black-box CLI subprocess. Highest-value functions currently untestable in isolation:\n- `evolve.mjs`: `looksLikeGepaTranscript`, `extractGepaTranscripts`, `summarizeTraces`, `buildDiagnosis` (the entire `--diagnose` feature, lines 146–274) and `safetyChecks()` bound validation\n- `security-bench.mjs`: `parseSecurityBenchMarkdown()`, `safetyChecks()`\n- `audit-list.mjs`: `parseDurationMs()`\n- `audit-trend.mjs`: `loadRecord()` malformed-JSON / missing-file branches\n- `mint.mjs`: `safetyChecks()` — path-escape guards (`targetAbs === repoRoot`, `startsWith(repoRoot + '/')`) are exactly the kind of security-relevant logic that should never rely on subprocess-only testing\n\n",
21
+ "level": 3
22
+ },
23
+ {
24
+ "title": "3. Edge cases not covered",
25
+ "content": "\n- Malformed/missing JSON: `audit-trend.mjs:loadRecord()` (bad `--baseline`/`--current` file), `gepa.mjs:readJsonFile()`\n- Empty/zero-record paths: `drift-from-history.mjs` \"no records found\", `audit-list.mjs` empty namespace\n- CLI arg-parsing enums never exercised with invalid input: `--fail-on`, `--sandbox` (`evolve.mjs` only allows `real|mock|agent`), `--mutator`, `--selection`, `redblue` unknown subcommand (`redblue.mjs:182`), `mint` missing `--name`/`--template` (`mint.mjs:38-45`)\n- `mint.mjs` path-escape guard itself (target == repo root, target inside repo root) — untested despite being a safety-critical guard\n- `redblue.mjs` report-JSON parse failure at line 220-225 (upstream wrote a corrupt/truncated report file)\n\n",
26
+ "level": 3
27
+ },
28
+ {
29
+ "title": "4. Missing error-handling tests",
30
+ "content": "\n- `audit-list.mjs` memory-command failures silently returning `[]`/`null` on non-zero exit\n- `oia-audit.mjs:persist()` store-failure path\n- `redblue.mjs` corrupt report JSON (line 220-225, shown above)\n- `ensureCachedInstall()` in `_invoke.mjs` — cache-dir-create-failed branch\n\n",
31
+ "level": 3
32
+ },
33
+ {
34
+ "title": "5. Integration test gaps",
35
+ "content": "\nNothing drives the real `npx ruflo metaharness <sub>` CLI dispatcher or the `commands/` slash-command layer — every existing integration test invokes `scripts/*.mjs` directly with `node`. `mint`, `redblue`, `gepa`, `learn` have **no** integration coverage at all — not even the ADR-150 graceful-degradation contract check (`degraded: true` on `MODULE_NOT_FOUND`) that the other scripts get via `test-graceful-degradation.mjs`.\n\n---\n\n",
36
+ "level": 3
37
+ },
38
+ {
39
+ "title": "Test skeletons",
40
+ "content": "Following this repo's actual convention (custom assert harness, not `node:test`):\n\n```js\n// scripts/test-threat-model-severity.mjs — regression test for the SEVERITY_RANK drift bug\n//\n// USAGE: node scripts/test-threat-model-severity.mjs\n\nimport { readFileSync } from 'node:fs';\nimport { spawnSync } from 'node:child_process';\nimport { SEVERITY_RANK as SHARED } from './_harness.mjs';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failed++; }\n}\n\n// (a) static check: threat-model.mjs must not carry its own stale rank map\nconst src = readFileSync(new URL('./threat-model.mjs', import.meta.url), 'utf-8');\nassert(\n !/const SEVERITY_RANK = \\{/.test(src),\n 'threat-model.mjs has no local SEVERITY_RANK literal (uses shared rankSeverity instead)'\n);\n\n// (b) behavioral check: --fail-on critical must be accepted once fixed\nconst r = spawnSync('node', ['scripts/threat-model.mjs', '--fail-on', 'critical', '--path', '/nonexistent'], { encoding: 'utf-8' });\nassert(r.status !== 2 || !/must be one of clean\\|low\\|medium\\|high/.test(r.stderr || ''),\n '--fail-on critical is accepted (not rejected as invalid enum)');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) process.exit(1);\n```\n\n```js\n// scripts/test-mint-safety.mjs — unit tests for mint.mjs path-escape guards\n// Requires exporting `safetyChecks` from mint.mjs (currently module-private).\n//\n// USAGE: node scripts/test-mint-safety.mjs\n\nimport { spawnSync } from 'node:child_process';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failed++; }\n}\n\nconst cwd = process.cwd();\n\n// missing --name\nlet r = spawnSync('node', ['scripts/mint.mjs', '--template', 'minimal'], { encoding: 'utf-8' });\nassert(r.status === 2 && /--name is required/.test(r.stderr), 'rejects missing --name');\n\n// missing --template\nr = spawnSync('node', ['scripts/mint.mjs', '--name', 'foo'], { encoding: 'utf-8' });\nassert(r.status === 2 && /--template is required/.test(r.stderr), 'rejects missing --template');\n\n// refuses project root as target\nr = spawnSync('node', ['scripts/mint.mjs', '--name', 'foo', '--template', 'minimal', '--target', cwd], { encoding: 'utf-8' });\nassert(r.status === 2 && /refusing to write to project root/.test(r.stderr), 'refuses target === repo root');\n\n// refuses a path inside the repo root\nr = spawnSync('node', ['scripts/mint.mjs', '--name', 'foo', '--template', 'minimal', '--target', `${cwd}/subdir`], { encoding: 'utf-8' });\nassert(r.status === 2 && /refusing to write inside the calling repo root/.test(r.stderr), 'refuses target inside repo root');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) process.exit(1);\n```\n\n```js\n// scripts/test-evolve-diagnose.mjs — unit tests for evolve.mjs's --diagnose helpers\n// Requires exporting looksLikeGepaTranscript/extractGepaTranscripts/summarizeTraces\n// from evolve.mjs (currently module-private, untestable in isolation today).\n//\n// USAGE: node scripts/test-evolve-diagnose.mjs\n\nimport { looksLikeGepaTranscript, extractGepaTranscripts, summarizeTraces } from './evolve.mjs';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failed++; }\n}\n\nassert(looksLikeGepaTranscript([{ actionRaw: 'x' }]) === true, 'accepts actionRaw-shaped entries');\nassert(looksLikeGepaTranscript([{ obs: 'y' }]) === true, 'accepts obs-shaped entries');\nassert(looksLikeGepaTranscript([]) === false, 'rejects empty array');\nassert(looksLikeGepaTranscript([{ foo: 1 }]) === false, 'rejects entries missing actionRaw/obs');\nassert(looksLikeGepaTranscript(null) === false, 'rejects non-array input without throwing');\n\nassert(\n extractGepaTranscripts({ transcript: [{ actionRaw: 'x' }] }).length === 1,\n 'extracts record.transcript when gepa-shaped'\n);\nassert(\n extractGepaTranscripts({ traces: [{ taskId: 't1', transcript: [{ obs: 'y' }] }] }).length === 1,\n 'extracts per-trace transcript with taskId preserved'\n);\nassert(\n extractGepaTranscripts({}).length === 0,\n 'returns empty array, not throw, when no transcript-shaped data exists'\n);\n\nconst summary = summarizeTraces({ traces: [{ exitCode: 1 }, { exitCode: 0, timedOut: true }, { exitCode: 0, blockedActions: ['a', 'b'] }] });\nassert(summary.tasks === 3, 'counts total tasks');\nassert(summary.failed === 1, 'counts non-zero exitCode as failed');\nassert(summary.timedOut === 1, 'counts timedOut entries');\nassert(summary.blockedActions === 2, 'sums blockedActions across traces');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) process.exit(1);\n```\n\n```js\n// scripts/test-redblue-argv.mjs — CLI arg-parsing edge cases (no `export` needed, pure subprocess)\n//\n// USAGE: node scripts/test-redblue-argv.mjs\n\nimport { spawnSync } from 'node:child_process';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failed++; }\n}\n\nlet r = spawnSync('node', ['scripts/redblue.mjs', 'bogus-subcommand'], { encoding: 'utf-8' });\nassert(r.status === 2 && /unknown subcommand/.test(r.stderr), 'rejects unknown subcommand with exit 2');\n\nr = spawnSync('node', ['scripts/redblue.mjs'], { encoding: 'utf-8' });\nassert(r.status === 0, 'no subcommand prints help and exits 0');\n\nr = spawnSync('node', ['scripts/redblue.mjs', '--help'], { encoding: 'utf-8' });\nassert(r.status === 0 && /Subcommands:/.test(r.stdout), '--help prints usage');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) process.exit(1);\n```\n\n---\n\n**Priority order to actually fix, not just cover:**\n1. `threat-model.mjs` `SEVERITY_RANK` drift — this is a live correctness bug (critical findings never trigger `--fail-on`), fix by importing `rankSeverity`/`SEVERITY_RANK` from `_harness.mjs` and widening the `--fail-on` enum to include `critical`.\n2. Export the pure-logic functions in `evolve.mjs`, `mint.mjs`, `security-bench.mjs`, `audit-list.mjs`, `audit-trend.mjs` so they're unit-testable without subprocess overhead — currently every one of these 8+ modules is untestable in isolation by construction.\n3. Then land the skeletons above plus graceful-degradation integration coverage for `mint`/`redblue`/`gepa`/`learn` to match the other 8 scripts.\n\nI found a pre-existing `testgaps` worker run in `.claude-flow/metrics/test-gaps.json` covering similar ground — I independently verified its core claims against the source (confirmed the `SEVERITY_RANK` bug and the zero-export pattern are real) and corrected its test skeletons to match this repo's actual convention (custom assert harness, not `node:test`, which isn't used anywhere in this plugin).",
41
+ "level": 2
42
+ }
43
+ ],
44
+ "codeBlocks": [
45
+ {
46
+ "language": "js",
47
+ "code": "// scripts/test-threat-model-severity.mjs — regression test for the SEVERITY_RANK drift bug\n//\n// USAGE: node scripts/test-threat-model-severity.mjs\n\nimport { readFileSync } from 'node:fs';\nimport { spawnSync } from 'node:child_process';\nimport { SEVERITY_RANK as SHARED } from './_harness.mjs';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failed++; }\n}\n\n// (a) static check: threat-model.mjs must not carry its own stale rank map\nconst src = readFileSync(new URL('./threat-model.mjs', import.meta.url), 'utf-8');\nassert(\n !/const SEVERITY_RANK = \\{/.test(src),\n 'threat-model.mjs has no local SEVERITY_RANK literal (uses shared rankSeverity instead)'\n);\n\n// (b) behavioral check: --fail-on critical must be accepted once fixed\nconst r = spawnSync('node', ['scripts/threat-model.mjs', '--fail-on', 'critical', '--path', '/nonexistent'], { encoding: 'utf-8' });\nassert(r.status !== 2 || !/must be one of clean\\|low\\|medium\\|high/.test(r.stderr || ''),\n '--fail-on critical is accepted (not rejected as invalid enum)');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) process.exit(1);"
48
+ },
49
+ {
50
+ "language": "js",
51
+ "code": "// scripts/test-mint-safety.mjs — unit tests for mint.mjs path-escape guards\n// Requires exporting `safetyChecks` from mint.mjs (currently module-private).\n//\n// USAGE: node scripts/test-mint-safety.mjs\n\nimport { spawnSync } from 'node:child_process';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failed++; }\n}\n\nconst cwd = process.cwd();\n\n// missing --name\nlet r = spawnSync('node', ['scripts/mint.mjs', '--template', 'minimal'], { encoding: 'utf-8' });\nassert(r.status === 2 && /--name is required/.test(r.stderr), 'rejects missing --name');\n\n// missing --template\nr = spawnSync('node', ['scripts/mint.mjs', '--name', 'foo'], { encoding: 'utf-8' });\nassert(r.status === 2 && /--template is required/.test(r.stderr), 'rejects missing --template');\n\n// refuses project root as target\nr = spawnSync('node', ['scripts/mint.mjs', '--name', 'foo', '--template', 'minimal', '--target', cwd], { encoding: 'utf-8' });\nassert(r.status === 2 && /refusing to write to project root/.test(r.stderr), 'refuses target === repo root');\n\n// refuses a path inside the repo root\nr = spawnSync('node', ['scripts/mint.mjs', '--name', 'foo', '--template', 'minimal', '--target', `${cwd}/subdir`], { encoding: 'utf-8' });\nassert(r.status === 2 && /refusing to write inside the calling repo root/.test(r.stderr), 'refuses target inside repo root');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) process.exit(1);"
52
+ },
53
+ {
54
+ "language": "js",
55
+ "code": "// scripts/test-evolve-diagnose.mjs — unit tests for evolve.mjs's --diagnose helpers\n// Requires exporting looksLikeGepaTranscript/extractGepaTranscripts/summarizeTraces\n// from evolve.mjs (currently module-private, untestable in isolation today).\n//\n// USAGE: node scripts/test-evolve-diagnose.mjs\n\nimport { looksLikeGepaTranscript, extractGepaTranscripts, summarizeTraces } from './evolve.mjs';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failed++; }\n}\n\nassert(looksLikeGepaTranscript([{ actionRaw: 'x' }]) === true, 'accepts actionRaw-shaped entries');\nassert(looksLikeGepaTranscript([{ obs: 'y' }]) === true, 'accepts obs-shaped entries');\nassert(looksLikeGepaTranscript([]) === false, 'rejects empty array');\nassert(looksLikeGepaTranscript([{ foo: 1 }]) === false, 'rejects entries missing actionRaw/obs');\nassert(looksLikeGepaTranscript(null) === false, 'rejects non-array input without throwing');\n\nassert(\n extractGepaTranscripts({ transcript: [{ actionRaw: 'x' }] }).length === 1,\n 'extracts record.transcript when gepa-shaped'\n);\nassert(\n extractGepaTranscripts({ traces: [{ taskId: 't1', transcript: [{ obs: 'y' }] }] }).length === 1,\n 'extracts per-trace transcript with taskId preserved'\n);\nassert(\n extractGepaTranscripts({}).length === 0,\n 'returns empty array, not throw, when no transcript-shaped data exists'\n);\n\nconst summary = summarizeTraces({ traces: [{ exitCode: 1 }, { exitCode: 0, timedOut: true }, { exitCode: 0, blockedActions: ['a', 'b'] }] });\nassert(summary.tasks === 3, 'counts total tasks');\nassert(summary.failed === 1, 'counts non-zero exitCode as failed');\nassert(summary.timedOut === 1, 'counts timedOut entries');\nassert(summary.blockedActions === 2, 'sums blockedActions across traces');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) process.exit(1);"
56
+ },
57
+ {
58
+ "language": "js",
59
+ "code": "// scripts/test-redblue-argv.mjs — CLI arg-parsing edge cases (no `export` needed, pure subprocess)\n//\n// USAGE: node scripts/test-redblue-argv.mjs\n\nimport { spawnSync } from 'node:child_process';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failed++; }\n}\n\nlet r = spawnSync('node', ['scripts/redblue.mjs', 'bogus-subcommand'], { encoding: 'utf-8' });\nassert(r.status === 2 && /unknown subcommand/.test(r.stderr), 'rejects unknown subcommand with exit 2');\n\nr = spawnSync('node', ['scripts/redblue.mjs'], { encoding: 'utf-8' });\nassert(r.status === 0, 'no subcommand prints help and exits 0');\n\nr = spawnSync('node', ['scripts/redblue.mjs', '--help'], { encoding: 'utf-8' });\nassert(r.status === 0 && /Subcommands:/.test(r.stdout), '--help prints usage');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) process.exit(1);"
60
+ }
61
+ ]
62
+ },
63
+ "durationMs": 140844,
64
+ "model": "sonnet",
65
+ "sandboxMode": "permissive",
66
+ "workerType": "testgaps",
67
+ "timestamp": "2026-07-09T16:39:01.688Z",
68
+ "executionId": "testgaps_1783615000844_v95ues"
69
+ }
@@ -0,0 +1,14 @@
1
+ [2026-07-09T16:59:01.693Z] PROMPT
2
+ ============================================================
3
+ Analyze test coverage and identify gaps:
4
+ - Find untested functions and classes
5
+ - Identify edge cases not covered
6
+ - Suggest new test scenarios
7
+ - Check for missing error handling tests
8
+ - Identify integration test gaps
9
+
10
+ For each gap, provide a test skeleton.
11
+
12
+ ## Instructions
13
+
14
+ Analyze the codebase and provide your response following the format specified in the task.
@@ -0,0 +1,64 @@
1
+ [2026-07-09T17:03:52.819Z] RESULT
2
+ ============================================================
3
+ {
4
+ "success": true,
5
+ "output": "## Test Coverage Analysis — `ruflo-metaharness`\n\nThe plugin has no formal test framework — coverage comes from 6 ad-hoc `test-*.mjs` scripts (real `assert()`/exit-code based) plus `scripts/smoke.sh` (123 grep-based structural checks, not runtime tests). Of 34 scripts, **6 have zero test coverage** and the most load-bearing shared module (`_invoke.mjs`) is never unit-tested at all.\n\n### 1. Untested functions/classes (no coverage in any of the 6 test files)\n\n| File | Status |\n|---|---|\n| `_invoke.mjs` | **Never unit-tested.** `findLocalPackageDir`, `satisfiesTildeRange`, `ensureCachedInstall`, `importOptionalLibrary`, `classifyDegraded`, `injectJson`, `parseTrailingJson` — only touched transitively via subprocess drills that force the single \"not-available\" branch |\n| `_darwin.mjs` | No unit test. `runDarwinAsync`'s `onProgress`/`signal` cancellation path is completely unexercised |\n| `_redblue.mjs` | No unit test at all (not even in the degradation drill) |\n| `mint.mjs` | Zero test coverage — including its root/self-repo write-refusal safety check |\n| `audit-list.mjs` | Zero test coverage |\n| `redblue.mjs`, `learn.mjs`, `gepa.mjs` | Zero test coverage — missing even from `test-graceful-degradation.mjs`'s skill list |\n| `router-parallel-analyze.mjs` | CLI tested end-to-end, but internal `median()`, `pctile()`, `mean()` never unit-tested directly (no verification of interpolation, even-length arrays, single-element arrays) |\n| `threat-model.mjs` | Has its own **stale, divergent** `SEVERITY_RANK` literal (missing `info/warn/error/critical` keys present in the shared `_harness.mjs` version) — this drift is invisible to every existing test |\n\n### 2. Edge cases not covered\n\n- `_invoke.mjs::satisfiesTildeRange` — no test for cross-major mismatch (`~0.3.0` vs `1.0.0`), pre-release suffixes (`0.3.0-beta.1`), or malformed version strings\n- `_invoke.mjs::parseTrailingJson` — the greedy whole-span fallback branch (used when no clean trailing `{...}` block exists) is never exercised\n- `_invoke.mjs::classifyDegraded` — `null` exitCode (timeout) vs `DEGRADED_RX` stderr match vs healthy are three distinct branches; only the stderr-match path is drilled\n- `_similarity.mjs::perDimensionBreakdown()` — only key-presence is checked in `test-similarity.mjs`, not the actual per-dimension numeric contribution math\n- `_similarity.mjs::similarity()` — no test with genuinely malformed input (`null`, missing `score`/`genome` keys entirely, non-object)\n- `oia-audit.mjs` composite worst-severity — no test of tie-breaking when two components report equal severity from different sources\n- `mint.mjs` — the upstream `--target`-ignored workaround (cwd/basename trick) has no regression test; a re-fix upstream could silently break it\n- `evolve.mjs --diagnose` — `buildDiagnosis`/`extractGepaTranscripts`/`looksLikeGepaTranscript` transcript-classification heuristics have no fixture-based test\n\n### 3. Missing error handling tests\n\n- **`threat-model.mjs`**: an unrecognized severity value (e.g. upstream adds a `\"warn\"` finding) falls through the local `SEVERITY_RANK` to `undefined`, producing a `NaN` comparison in the `--fail-on` gate instead of failing safe. No test catches this.\n- **`_invoke.mjs::ensureCachedInstall`**: no test of the `npm install` subprocess itself failing (disk full, permission denied, corrupt cache dir) as distinct from \"package unreachable\"\n- **`redblue.mjs`**: no test of the upstream `isMain` npx-symlink workaround failing to resolve a real path (falls back to what?)\n- **`test-graceful-degradation.mjs`** itself documents (in comments) two known-but-unfixed bugs — `mint.mjs`'s degraded payload possibly missing the `\"degraded\": true` marker, and `drift-from-history.mjs` returning exit 2 instead of documented exit 3 on upstream-absent — neither has a regression test asserting the *correct* behavior once fixed\n- **`gepa.mjs`**: no test of `analyzeTranscript` receiving a non-GEPA-shaped transcript file (malformed JSON, wrong schema)\n\n### 4. Integration test gaps\n\n- **`redblue.mjs`, `learn.mjs`, `gepa.mjs`, `mint.mjs`, `audit-list.mjs`** are absent from `test-graceful-degradation.mjs`'s subprocess drill — none of the \"does this script behave correctly when upstream is completely unreachable\" guarantees are verified for them\n- **`test-mcp-tools.mjs` silently no-ops** or when the sibling monorepo's compiled `dist/` isn't present (plugin-only checkout) — this means the 15-tool MCP handler contract goes completely unverified in that environment, with `exit 0` masking zero assertions run\n- **`_darwin.mjs`/`_redblue.mjs` \"already cached\" and \"local install found\" resolution paths** are never exercised — only \"must install fresh\" and \"unreachable\" are covered\n- **`audit-trend.mjs` ↔ `_similarity.mjs` composition** is only tested via `test-pipeline-roundtrip.mjs`'s self-match/synthetic-mutation cases — no test of genuinely divergent real-world audit pairs (e.g. different repo entirely, comparing incompatible genomes)\n- **`test-with-openrouter.mjs`** bypasses `_harness.mjs`'s pinned-version resolution entirely (uses `metaharness@latest`) — it looks like an integration test of the harness CLI surface but never actually validates the plugin's own caching/pin logic under real network conditions\n\n---\n\n### Test skeletons\n\n**A. `_invoke.mjs` unit tests (highest-priority gap — currently zero coverage)**\n\n```javascript\n#!/usr/bin/env node\n// test-invoke.mjs — unit tests for the shared subprocess/plumbing layer\n// (_invoke.mjs). Currently untested despite being imported by\n// _harness.mjs, _darwin.mjs, _redblue.mjs, and gepa.mjs.\n//\n// USAGE\n// node scripts/test-invoke.mjs\n// EXIT\n// 0 all pass, 1 at least one failure\n\nimport {\n classifyDegraded, injectJson, parseTrailingJson,\n satisfiesTildeRange, cacheBaseDir,\n} from './_invoke.mjs';\n\nlet passed = 0, failed = 0;\nconst failures = [];\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failures.push(label); failed++; }\n}\n\n// ── classifyDegraded ──────────────────────────────────────────────\n{\n const timeout = classifyDegraded('', null, 'harness');\n assert(timeout.degraded === true, 'classifyDegraded: null exitCode (timeout) is degraded');\n\n const notFound = classifyDegraded('npm ERR! 404 Not Found', 1, 'harness');\n assert(notFound.degraded === true, 'classifyDegraded: DEGRADED_RX stderr match is degraded');\n\n const healthy = classifyDegraded('', 0, 'harness');\n assert(healthy.degraded === false, 'classifyDegraded: clean exit is not degraded');\n\n const nonzeroButHealthy = classifyDegraded('some real validation error', 1, 'harness');\n assert(nonzeroButHealthy.degraded === false,\n 'classifyDegraded: nonzero exit without DEGRADED_RX match is NOT treated as degraded');\n}\n\n// ── injectJson ─────────────────────────────────────────────────────\n{\n assert(injectJson(['score', '.'], true).includes('--json'),\n 'injectJson: appends --json when wantJson=true and absent');\n assert(!injectJson(['score', '.'], false).includes('--json'),\n 'injectJson: does not append when wantJson=false');\n const already = injectJson(['score', '.', '--json'], true);\n assert(already.filter(a => a === '--json').length === 1,\n 'injectJson: does not duplicate --json if already present');\n}\n\n// ── parseTrailingJson ────────────────────────────────────────────\n{\n const clean = parseTrailingJson('some log line\\n{\"a\":1}');\n assert(clean && clean.a === 1, 'parseTrailingJson: extracts trailing JSON block');\n\n const noisy = parseTrailingJson('{\"a\":1}\\nnot json trailing garbage');\n assert(noisy === null || typeof noisy === 'object',\n 'parseTrailingJson: gracefully handles non-JSON trailing content (greedy fallback)');\n\n assert(parseTrailingJson('') === null, 'parseTrailingJson: empty string returns null, not throw');\n assert(parseTrailingJson('no json here at all') === null,\n 'parseTrailingJson: no JSON present returns null');\n}\n\n// ── satisfiesTildeRange ──────────────────────────────────────────\n{\n assert(satisfiesTildeRange('0.3.2', '~0.3.0') === true,\n 'satisfiesTildeRange: same major.minor, patch >= pin is satisfied');\n assert(satisfiesTildeRange('0.3.0', '~0.3.0') === true,\n 'satisfiesTildeRange: exact match is satisfied');\n assert(satisfiesTildeRange('0.4.0', '~0.3.0') === false,\n 'satisfiesTildeRange: different minor is NOT satisfied');\n assert(satisfiesTildeRange('1.3.0', '~0.3.0') === false,\n 'satisfiesTildeRange: different major is NOT satisfied');\n assert(satisfiesTildeRange('0.3.0-beta.1', '~0.3.0') === false,\n 'satisfiesTildeRange: pre-release suffix does not satisfy plain pin');\n assert(satisfiesTildeRange('not-a-version', '~0.3.0') === false,\n 'satisfiesTildeRange: malformed version string fails closed, not throw');\n}\n\n// ── cacheBaseDir ──────────────────────────────────────────────────\n{\n const original = process.env.RUFLO_METAHARNESS_CACHE_BASE;\n delete process.env.RUFLO_METAHARNESS_CACHE_BASE;\n assert(cacheBaseDir().includes('.ruflo'), 'cacheBaseDir: defaults under ~/.ruflo');\n process.env.RUFLO_METAHARNESS_CACHE_BASE = '/tmp/custom-cache';\n assert(cacheBaseDir() === '/tmp/custom-cache', 'cacheBaseDir: honors env override test seam');\n if (original === undefined) delete process.env.RUFLO_METAHARNESS_CACHE_BASE;\n else process.env.RUFLO_METAHARNESS_CACHE_BASE = original;\n}\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) { console.log('Failures:', failures.join(', ')); process.exit(1); }\nprocess.exit(0);\n```\n\n**B. `threat-model.mjs` — regression test for the divergent `SEVERITY_RANK` literal**\n\n```javascript\n#!/usr/bin/env node\n// test-threat-model-severity.mjs — regression guard for the iter-63\n// SEVERITY_RANK unification. threat-model.mjs still carries its own\n// local literal that is missing info/warn/error/critical keys present\n// in the shared _harness.mjs version — this drift was never caught\n// by an existing test.\n//\n// USAGE: node scripts/test-threat-model-severity.mjs\n\nimport { readFileSync } from 'node:fs';\nimport { SEVERITY_RANK as SHARED_RANK } from './_harness.mjs';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) { cond ? (console.log(` ✓ ${label}`), passed++)\n : (console.log(` ✗ ${label}`), failed++); }\n\nconst src = readFileSync(new URL('./threat-model.mjs', import.meta.url), 'utf8');\n\n// Fails today (documents the known gap) — once threat-model.mjs is fixed\n// to import SEVERITY_RANK from _harness.mjs, this assertion should hold.\nconst importsShared = /import\\s*\\{[^}]*SEVERITY_RANK[^}]*\\}\\s*from\\s*['\"]\\.\\/_harness\\.mjs['\"]/.test(src);\nassert(importsShared,\n 'threat-model.mjs imports the shared SEVERITY_RANK instead of a local literal');\n\n// Guards against future drift even if a local literal is reintroduced elsewhere.\nconst sharedKeys = Object.keys(SHARED_RANK).sort();\nassert(sharedKeys.includes('info') && sharedKeys.includes('critical'),\n 'shared SEVERITY_RANK in _harness.mjs still covers the full severity vocabulary');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nprocess.exit(failed > 0 ? 1 : 0);\n```\n\n**C. Extend `test-graceful-degradation.mjs`'s skill coverage list**\n\n```javascript\n// Add to the SKILLS array in test-graceful-degradation.mjs — currently\n// missing redblue.mjs, learn.mjs, gepa.mjs, mint.mjs, audit-list.mjs.\nconst ADDITIONAL_SKILLS = [\n { script: 'mint.mjs', args: ['--name', 'tmp-test', '--template', 'vertical:coding'] },\n { script: 'audit-list.mjs', args: ['--limit', '5'] },\n { script: 'learn.mjs', args: ['--host', 'claude-code', '--model', 'haiku'] },\n { script: 'gepa.mjs', args: ['--op', 'genome'] },\n { script: 'redblue.mjs', args: ['run', '--mock-judge', '--tests', '1'] },\n];\n// Each should assert: exit code in [0], stdout parses as JSON, and\n// stdout contains `\"degraded\": true` when RUFLO_METAHARNESS_SKIP_LOCAL=1\n// + unreachable registry are set — same pattern as the existing 8 skills.\n```\n\n**D. `router-parallel-analyze.mjs` internal stats helpers (currently only reachable via full CLI)**\n\n```javascript\n#!/usr/bin/env node\n// test-router-stats.mjs — direct unit tests for median/pctile/mean.\n// These are currently unexported and only reachable through main()'s\n// aggregate CLI output — export them from router-parallel-analyze.mjs\n// for direct testability, then add:\n\n// After exporting: import { median, pctile, mean } from './router-parallel-analyze.mjs';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) { cond ? (console.log(` ✓ ${label}`), passed++)\n : (console.log(` ✗ ${label}`), failed++); }\n\n// median()\n// assert(median([1,2,3]) === 2, 'median: odd-length array');\n// assert(median([1,2,3,4]) === 2.5, 'median: even-length array interpolates');\n// assert(median([5]) === 5, 'median: single-element array');\n// assert(median([]) === undefined || Number.isNaN(median([])), 'median: empty array does not throw');\n\n// pctile()\n// assert(pctile([1,2,3,4,5,6,7,8,9,10], 0.5) === median([1,2,3,4,5,6,7,8,9,10]), 'pctile(0.5) matches median');\n// assert(pctile([1,2,3,4,5,6,7,8,9,10], 0.95) >= 9, 'pctile(0.95) is near the top of the range');\n// assert(pctile([1], 0.95) === 1, 'pctile: single-element array returns that element regardless of pct');\n\n// mean()\n// assert(mean([2,4,6]) === 4, 'mean: simple average');\n// assert(mean([]) === 0 || Number.isNaN(mean([])), 'mean: empty array does not throw');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nprocess.exit(failed > 0 ? 1 : 0);\n```\n\nNone of these were written to disk — they're skeletons for you to place under `scripts/` if you want to close the gaps. The single highest-leverage fix is **(A)** since `_invoke.mjs` underpins every upstream-CLI-wrapping script in the plugin and currently has no direct test at all; **(B)** is a one-line correctness bug (`threat-model.mjs`'s stale severity map) worth fixing regardless of the test.\n",
6
+ "parsedOutput": {
7
+ "sections": [
8
+ {
9
+ "title": "Test Coverage Analysis — `ruflo-metaharness`",
10
+ "content": "\nThe plugin has no formal test framework — coverage comes from 6 ad-hoc `test-*.mjs` scripts (real `assert()`/exit-code based) plus `scripts/smoke.sh` (123 grep-based structural checks, not runtime tests). Of 34 scripts, **6 have zero test coverage** and the most load-bearing shared module (`_invoke.mjs`) is never unit-tested at all.\n\n",
11
+ "level": 2
12
+ },
13
+ {
14
+ "title": "1. Untested functions/classes (no coverage in any of the 6 test files)",
15
+ "content": "\n| File | Status |\n|---|---|\n| `_invoke.mjs` | **Never unit-tested.** `findLocalPackageDir`, `satisfiesTildeRange`, `ensureCachedInstall`, `importOptionalLibrary`, `classifyDegraded`, `injectJson`, `parseTrailingJson` — only touched transitively via subprocess drills that force the single \"not-available\" branch |\n| `_darwin.mjs` | No unit test. `runDarwinAsync`'s `onProgress`/`signal` cancellation path is completely unexercised |\n| `_redblue.mjs` | No unit test at all (not even in the degradation drill) |\n| `mint.mjs` | Zero test coverage — including its root/self-repo write-refusal safety check |\n| `audit-list.mjs` | Zero test coverage |\n| `redblue.mjs`, `learn.mjs`, `gepa.mjs` | Zero test coverage — missing even from `test-graceful-degradation.mjs`'s skill list |\n| `router-parallel-analyze.mjs` | CLI tested end-to-end, but internal `median()`, `pctile()`, `mean()` never unit-tested directly (no verification of interpolation, even-length arrays, single-element arrays) |\n| `threat-model.mjs` | Has its own **stale, divergent** `SEVERITY_RANK` literal (missing `info/warn/error/critical` keys present in the shared `_harness.mjs` version) — this drift is invisible to every existing test |\n\n",
16
+ "level": 3
17
+ },
18
+ {
19
+ "title": "2. Edge cases not covered",
20
+ "content": "\n- `_invoke.mjs::satisfiesTildeRange` — no test for cross-major mismatch (`~0.3.0` vs `1.0.0`), pre-release suffixes (`0.3.0-beta.1`), or malformed version strings\n- `_invoke.mjs::parseTrailingJson` — the greedy whole-span fallback branch (used when no clean trailing `{...}` block exists) is never exercised\n- `_invoke.mjs::classifyDegraded` — `null` exitCode (timeout) vs `DEGRADED_RX` stderr match vs healthy are three distinct branches; only the stderr-match path is drilled\n- `_similarity.mjs::perDimensionBreakdown()` — only key-presence is checked in `test-similarity.mjs`, not the actual per-dimension numeric contribution math\n- `_similarity.mjs::similarity()` — no test with genuinely malformed input (`null`, missing `score`/`genome` keys entirely, non-object)\n- `oia-audit.mjs` composite worst-severity — no test of tie-breaking when two components report equal severity from different sources\n- `mint.mjs` — the upstream `--target`-ignored workaround (cwd/basename trick) has no regression test; a re-fix upstream could silently break it\n- `evolve.mjs --diagnose` — `buildDiagnosis`/`extractGepaTranscripts`/`looksLikeGepaTranscript` transcript-classification heuristics have no fixture-based test\n\n",
21
+ "level": 3
22
+ },
23
+ {
24
+ "title": "3. Missing error handling tests",
25
+ "content": "\n- **`threat-model.mjs`**: an unrecognized severity value (e.g. upstream adds a `\"warn\"` finding) falls through the local `SEVERITY_RANK` to `undefined`, producing a `NaN` comparison in the `--fail-on` gate instead of failing safe. No test catches this.\n- **`_invoke.mjs::ensureCachedInstall`**: no test of the `npm install` subprocess itself failing (disk full, permission denied, corrupt cache dir) as distinct from \"package unreachable\"\n- **`redblue.mjs`**: no test of the upstream `isMain` npx-symlink workaround failing to resolve a real path (falls back to what?)\n- **`test-graceful-degradation.mjs`** itself documents (in comments) two known-but-unfixed bugs — `mint.mjs`'s degraded payload possibly missing the `\"degraded\": true` marker, and `drift-from-history.mjs` returning exit 2 instead of documented exit 3 on upstream-absent — neither has a regression test asserting the *correct* behavior once fixed\n- **`gepa.mjs`**: no test of `analyzeTranscript` receiving a non-GEPA-shaped transcript file (malformed JSON, wrong schema)\n\n",
26
+ "level": 3
27
+ },
28
+ {
29
+ "title": "4. Integration test gaps",
30
+ "content": "\n- **`redblue.mjs`, `learn.mjs`, `gepa.mjs`, `mint.mjs`, `audit-list.mjs`** are absent from `test-graceful-degradation.mjs`'s subprocess drill — none of the \"does this script behave correctly when upstream is completely unreachable\" guarantees are verified for them\n- **`test-mcp-tools.mjs` silently no-ops** or when the sibling monorepo's compiled `dist/` isn't present (plugin-only checkout) — this means the 15-tool MCP handler contract goes completely unverified in that environment, with `exit 0` masking zero assertions run\n- **`_darwin.mjs`/`_redblue.mjs` \"already cached\" and \"local install found\" resolution paths** are never exercised — only \"must install fresh\" and \"unreachable\" are covered\n- **`audit-trend.mjs` ↔ `_similarity.mjs` composition** is only tested via `test-pipeline-roundtrip.mjs`'s self-match/synthetic-mutation cases — no test of genuinely divergent real-world audit pairs (e.g. different repo entirely, comparing incompatible genomes)\n- **`test-with-openrouter.mjs`** bypasses `_harness.mjs`'s pinned-version resolution entirely (uses `metaharness@latest`) — it looks like an integration test of the harness CLI surface but never actually validates the plugin's own caching/pin logic under real network conditions\n\n---\n\n",
31
+ "level": 3
32
+ },
33
+ {
34
+ "title": "Test skeletons",
35
+ "content": "**A. `_invoke.mjs` unit tests (highest-priority gap — currently zero coverage)**\n\n```javascript\n#!/usr/bin/env node\n// test-invoke.mjs — unit tests for the shared subprocess/plumbing layer\n// (_invoke.mjs). Currently untested despite being imported by\n// _harness.mjs, _darwin.mjs, _redblue.mjs, and gepa.mjs.\n//\n// USAGE\n// node scripts/test-invoke.mjs\n// EXIT\n// 0 all pass, 1 at least one failure\n\nimport {\n classifyDegraded, injectJson, parseTrailingJson,\n satisfiesTildeRange, cacheBaseDir,\n} from './_invoke.mjs';\n\nlet passed = 0, failed = 0;\nconst failures = [];\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failures.push(label); failed++; }\n}\n\n// ── classifyDegraded ──────────────────────────────────────────────\n{\n const timeout = classifyDegraded('', null, 'harness');\n assert(timeout.degraded === true, 'classifyDegraded: null exitCode (timeout) is degraded');\n\n const notFound = classifyDegraded('npm ERR! 404 Not Found', 1, 'harness');\n assert(notFound.degraded === true, 'classifyDegraded: DEGRADED_RX stderr match is degraded');\n\n const healthy = classifyDegraded('', 0, 'harness');\n assert(healthy.degraded === false, 'classifyDegraded: clean exit is not degraded');\n\n const nonzeroButHealthy = classifyDegraded('some real validation error', 1, 'harness');\n assert(nonzeroButHealthy.degraded === false,\n 'classifyDegraded: nonzero exit without DEGRADED_RX match is NOT treated as degraded');\n}\n\n// ── injectJson ─────────────────────────────────────────────────────\n{\n assert(injectJson(['score', '.'], true).includes('--json'),\n 'injectJson: appends --json when wantJson=true and absent');\n assert(!injectJson(['score', '.'], false).includes('--json'),\n 'injectJson: does not append when wantJson=false');\n const already = injectJson(['score', '.', '--json'], true);\n assert(already.filter(a => a === '--json').length === 1,\n 'injectJson: does not duplicate --json if already present');\n}\n\n// ── parseTrailingJson ────────────────────────────────────────────\n{\n const clean = parseTrailingJson('some log line\\n{\"a\":1}');\n assert(clean && clean.a === 1, 'parseTrailingJson: extracts trailing JSON block');\n\n const noisy = parseTrailingJson('{\"a\":1}\\nnot json trailing garbage');\n assert(noisy === null || typeof noisy === 'object',\n 'parseTrailingJson: gracefully handles non-JSON trailing content (greedy fallback)');\n\n assert(parseTrailingJson('') === null, 'parseTrailingJson: empty string returns null, not throw');\n assert(parseTrailingJson('no json here at all') === null,\n 'parseTrailingJson: no JSON present returns null');\n}\n\n// ── satisfiesTildeRange ──────────────────────────────────────────\n{\n assert(satisfiesTildeRange('0.3.2', '~0.3.0') === true,\n 'satisfiesTildeRange: same major.minor, patch >= pin is satisfied');\n assert(satisfiesTildeRange('0.3.0', '~0.3.0') === true,\n 'satisfiesTildeRange: exact match is satisfied');\n assert(satisfiesTildeRange('0.4.0', '~0.3.0') === false,\n 'satisfiesTildeRange: different minor is NOT satisfied');\n assert(satisfiesTildeRange('1.3.0', '~0.3.0') === false,\n 'satisfiesTildeRange: different major is NOT satisfied');\n assert(satisfiesTildeRange('0.3.0-beta.1', '~0.3.0') === false,\n 'satisfiesTildeRange: pre-release suffix does not satisfy plain pin');\n assert(satisfiesTildeRange('not-a-version', '~0.3.0') === false,\n 'satisfiesTildeRange: malformed version string fails closed, not throw');\n}\n\n// ── cacheBaseDir ──────────────────────────────────────────────────\n{\n const original = process.env.RUFLO_METAHARNESS_CACHE_BASE;\n delete process.env.RUFLO_METAHARNESS_CACHE_BASE;\n assert(cacheBaseDir().includes('.ruflo'), 'cacheBaseDir: defaults under ~/.ruflo');\n process.env.RUFLO_METAHARNESS_CACHE_BASE = '/tmp/custom-cache';\n assert(cacheBaseDir() === '/tmp/custom-cache', 'cacheBaseDir: honors env override test seam');\n if (original === undefined) delete process.env.RUFLO_METAHARNESS_CACHE_BASE;\n else process.env.RUFLO_METAHARNESS_CACHE_BASE = original;\n}\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) { console.log('Failures:', failures.join(', ')); process.exit(1); }\nprocess.exit(0);\n```\n\n**B. `threat-model.mjs` — regression test for the divergent `SEVERITY_RANK` literal**\n\n```javascript\n#!/usr/bin/env node\n// test-threat-model-severity.mjs — regression guard for the iter-63\n// SEVERITY_RANK unification. threat-model.mjs still carries its own\n// local literal that is missing info/warn/error/critical keys present\n// in the shared _harness.mjs version — this drift was never caught\n// by an existing test.\n//\n// USAGE: node scripts/test-threat-model-severity.mjs\n\nimport { readFileSync } from 'node:fs';\nimport { SEVERITY_RANK as SHARED_RANK } from './_harness.mjs';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) { cond ? (console.log(` ✓ ${label}`), passed++)\n : (console.log(` ✗ ${label}`), failed++); }\n\nconst src = readFileSync(new URL('./threat-model.mjs', import.meta.url), 'utf8');\n\n// Fails today (documents the known gap) — once threat-model.mjs is fixed\n// to import SEVERITY_RANK from _harness.mjs, this assertion should hold.\nconst importsShared = /import\\s*\\{[^}]*SEVERITY_RANK[^}]*\\}\\s*from\\s*['\"]\\.\\/_harness\\.mjs['\"]/.test(src);\nassert(importsShared,\n 'threat-model.mjs imports the shared SEVERITY_RANK instead of a local literal');\n\n// Guards against future drift even if a local literal is reintroduced elsewhere.\nconst sharedKeys = Object.keys(SHARED_RANK).sort();\nassert(sharedKeys.includes('info') && sharedKeys.includes('critical'),\n 'shared SEVERITY_RANK in _harness.mjs still covers the full severity vocabulary');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nprocess.exit(failed > 0 ? 1 : 0);\n```\n\n**C. Extend `test-graceful-degradation.mjs`'s skill coverage list**\n\n```javascript\n// Add to the SKILLS array in test-graceful-degradation.mjs — currently\n// missing redblue.mjs, learn.mjs, gepa.mjs, mint.mjs, audit-list.mjs.\nconst ADDITIONAL_SKILLS = [\n { script: 'mint.mjs', args: ['--name', 'tmp-test', '--template', 'vertical:coding'] },\n { script: 'audit-list.mjs', args: ['--limit', '5'] },\n { script: 'learn.mjs', args: ['--host', 'claude-code', '--model', 'haiku'] },\n { script: 'gepa.mjs', args: ['--op', 'genome'] },\n { script: 'redblue.mjs', args: ['run', '--mock-judge', '--tests', '1'] },\n];\n// Each should assert: exit code in [0], stdout parses as JSON, and\n// stdout contains `\"degraded\": true` when RUFLO_METAHARNESS_SKIP_LOCAL=1\n// + unreachable registry are set — same pattern as the existing 8 skills.\n```\n\n**D. `router-parallel-analyze.mjs` internal stats helpers (currently only reachable via full CLI)**\n\n```javascript\n#!/usr/bin/env node\n// test-router-stats.mjs — direct unit tests for median/pctile/mean.\n// These are currently unexported and only reachable through main()'s\n// aggregate CLI output — export them from router-parallel-analyze.mjs\n// for direct testability, then add:\n\n// After exporting: import { median, pctile, mean } from './router-parallel-analyze.mjs';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) { cond ? (console.log(` ✓ ${label}`), passed++)\n : (console.log(` ✗ ${label}`), failed++); }\n\n// median()\n// assert(median([1,2,3]) === 2, 'median: odd-length array');\n// assert(median([1,2,3,4]) === 2.5, 'median: even-length array interpolates');\n// assert(median([5]) === 5, 'median: single-element array');\n// assert(median([]) === undefined || Number.isNaN(median([])), 'median: empty array does not throw');\n\n// pctile()\n// assert(pctile([1,2,3,4,5,6,7,8,9,10], 0.5) === median([1,2,3,4,5,6,7,8,9,10]), 'pctile(0.5) matches median');\n// assert(pctile([1,2,3,4,5,6,7,8,9,10], 0.95) >= 9, 'pctile(0.95) is near the top of the range');\n// assert(pctile([1], 0.95) === 1, 'pctile: single-element array returns that element regardless of pct');\n\n// mean()\n// assert(mean([2,4,6]) === 4, 'mean: simple average');\n// assert(mean([]) === 0 || Number.isNaN(mean([])), 'mean: empty array does not throw');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nprocess.exit(failed > 0 ? 1 : 0);\n```\n\nNone of these were written to disk — they're skeletons for you to place under `scripts/` if you want to close the gaps. The single highest-leverage fix is **(A)** since `_invoke.mjs` underpins every upstream-CLI-wrapping script in the plugin and currently has no direct test at all; **(B)** is a one-line correctness bug (`threat-model.mjs`'s stale severity map) worth fixing regardless of the test.",
36
+ "level": 3
37
+ }
38
+ ],
39
+ "codeBlocks": [
40
+ {
41
+ "language": "javascript",
42
+ "code": "#!/usr/bin/env node\n// test-invoke.mjs — unit tests for the shared subprocess/plumbing layer\n// (_invoke.mjs). Currently untested despite being imported by\n// _harness.mjs, _darwin.mjs, _redblue.mjs, and gepa.mjs.\n//\n// USAGE\n// node scripts/test-invoke.mjs\n// EXIT\n// 0 all pass, 1 at least one failure\n\nimport {\n classifyDegraded, injectJson, parseTrailingJson,\n satisfiesTildeRange, cacheBaseDir,\n} from './_invoke.mjs';\n\nlet passed = 0, failed = 0;\nconst failures = [];\nfunction assert(cond, label) {\n if (cond) { console.log(` ✓ ${label}`); passed++; }\n else { console.log(` ✗ ${label}`); failures.push(label); failed++; }\n}\n\n// ── classifyDegraded ──────────────────────────────────────────────\n{\n const timeout = classifyDegraded('', null, 'harness');\n assert(timeout.degraded === true, 'classifyDegraded: null exitCode (timeout) is degraded');\n\n const notFound = classifyDegraded('npm ERR! 404 Not Found', 1, 'harness');\n assert(notFound.degraded === true, 'classifyDegraded: DEGRADED_RX stderr match is degraded');\n\n const healthy = classifyDegraded('', 0, 'harness');\n assert(healthy.degraded === false, 'classifyDegraded: clean exit is not degraded');\n\n const nonzeroButHealthy = classifyDegraded('some real validation error', 1, 'harness');\n assert(nonzeroButHealthy.degraded === false,\n 'classifyDegraded: nonzero exit without DEGRADED_RX match is NOT treated as degraded');\n}\n\n// ── injectJson ─────────────────────────────────────────────────────\n{\n assert(injectJson(['score', '.'], true).includes('--json'),\n 'injectJson: appends --json when wantJson=true and absent');\n assert(!injectJson(['score', '.'], false).includes('--json'),\n 'injectJson: does not append when wantJson=false');\n const already = injectJson(['score', '.', '--json'], true);\n assert(already.filter(a => a === '--json').length === 1,\n 'injectJson: does not duplicate --json if already present');\n}\n\n// ── parseTrailingJson ────────────────────────────────────────────\n{\n const clean = parseTrailingJson('some log line\\n{\"a\":1}');\n assert(clean && clean.a === 1, 'parseTrailingJson: extracts trailing JSON block');\n\n const noisy = parseTrailingJson('{\"a\":1}\\nnot json trailing garbage');\n assert(noisy === null || typeof noisy === 'object',\n 'parseTrailingJson: gracefully handles non-JSON trailing content (greedy fallback)');\n\n assert(parseTrailingJson('') === null, 'parseTrailingJson: empty string returns null, not throw');\n assert(parseTrailingJson('no json here at all') === null,\n 'parseTrailingJson: no JSON present returns null');\n}\n\n// ── satisfiesTildeRange ──────────────────────────────────────────\n{\n assert(satisfiesTildeRange('0.3.2', '~0.3.0') === true,\n 'satisfiesTildeRange: same major.minor, patch >= pin is satisfied');\n assert(satisfiesTildeRange('0.3.0', '~0.3.0') === true,\n 'satisfiesTildeRange: exact match is satisfied');\n assert(satisfiesTildeRange('0.4.0', '~0.3.0') === false,\n 'satisfiesTildeRange: different minor is NOT satisfied');\n assert(satisfiesTildeRange('1.3.0', '~0.3.0') === false,\n 'satisfiesTildeRange: different major is NOT satisfied');\n assert(satisfiesTildeRange('0.3.0-beta.1', '~0.3.0') === false,\n 'satisfiesTildeRange: pre-release suffix does not satisfy plain pin');\n assert(satisfiesTildeRange('not-a-version', '~0.3.0') === false,\n 'satisfiesTildeRange: malformed version string fails closed, not throw');\n}\n\n// ── cacheBaseDir ──────────────────────────────────────────────────\n{\n const original = process.env.RUFLO_METAHARNESS_CACHE_BASE;\n delete process.env.RUFLO_METAHARNESS_CACHE_BASE;\n assert(cacheBaseDir().includes('.ruflo'), 'cacheBaseDir: defaults under ~/.ruflo');\n process.env.RUFLO_METAHARNESS_CACHE_BASE = '/tmp/custom-cache';\n assert(cacheBaseDir() === '/tmp/custom-cache', 'cacheBaseDir: honors env override test seam');\n if (original === undefined) delete process.env.RUFLO_METAHARNESS_CACHE_BASE;\n else process.env.RUFLO_METAHARNESS_CACHE_BASE = original;\n}\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nif (failed > 0) { console.log('Failures:', failures.join(', ')); process.exit(1); }\nprocess.exit(0);"
43
+ },
44
+ {
45
+ "language": "javascript",
46
+ "code": "#!/usr/bin/env node\n// test-threat-model-severity.mjs — regression guard for the iter-63\n// SEVERITY_RANK unification. threat-model.mjs still carries its own\n// local literal that is missing info/warn/error/critical keys present\n// in the shared _harness.mjs version — this drift was never caught\n// by an existing test.\n//\n// USAGE: node scripts/test-threat-model-severity.mjs\n\nimport { readFileSync } from 'node:fs';\nimport { SEVERITY_RANK as SHARED_RANK } from './_harness.mjs';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) { cond ? (console.log(` ✓ ${label}`), passed++)\n : (console.log(` ✗ ${label}`), failed++); }\n\nconst src = readFileSync(new URL('./threat-model.mjs', import.meta.url), 'utf8');\n\n// Fails today (documents the known gap) — once threat-model.mjs is fixed\n// to import SEVERITY_RANK from _harness.mjs, this assertion should hold.\nconst importsShared = /import\\s*\\{[^}]*SEVERITY_RANK[^}]*\\}\\s*from\\s*['\"]\\.\\/_harness\\.mjs['\"]/.test(src);\nassert(importsShared,\n 'threat-model.mjs imports the shared SEVERITY_RANK instead of a local literal');\n\n// Guards against future drift even if a local literal is reintroduced elsewhere.\nconst sharedKeys = Object.keys(SHARED_RANK).sort();\nassert(sharedKeys.includes('info') && sharedKeys.includes('critical'),\n 'shared SEVERITY_RANK in _harness.mjs still covers the full severity vocabulary');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nprocess.exit(failed > 0 ? 1 : 0);"
47
+ },
48
+ {
49
+ "language": "javascript",
50
+ "code": "// Add to the SKILLS array in test-graceful-degradation.mjs — currently\n// missing redblue.mjs, learn.mjs, gepa.mjs, mint.mjs, audit-list.mjs.\nconst ADDITIONAL_SKILLS = [\n { script: 'mint.mjs', args: ['--name', 'tmp-test', '--template', 'vertical:coding'] },\n { script: 'audit-list.mjs', args: ['--limit', '5'] },\n { script: 'learn.mjs', args: ['--host', 'claude-code', '--model', 'haiku'] },\n { script: 'gepa.mjs', args: ['--op', 'genome'] },\n { script: 'redblue.mjs', args: ['run', '--mock-judge', '--tests', '1'] },\n];\n// Each should assert: exit code in [0], stdout parses as JSON, and\n// stdout contains `\"degraded\": true` when RUFLO_METAHARNESS_SKIP_LOCAL=1\n// + unreachable registry are set — same pattern as the existing 8 skills."
51
+ },
52
+ {
53
+ "language": "javascript",
54
+ "code": "#!/usr/bin/env node\n// test-router-stats.mjs — direct unit tests for median/pctile/mean.\n// These are currently unexported and only reachable through main()'s\n// aggregate CLI output — export them from router-parallel-analyze.mjs\n// for direct testability, then add:\n\n// After exporting: import { median, pctile, mean } from './router-parallel-analyze.mjs';\n\nlet passed = 0, failed = 0;\nfunction assert(cond, label) { cond ? (console.log(` ✓ ${label}`), passed++)\n : (console.log(` ✗ ${label}`), failed++); }\n\n// median()\n// assert(median([1,2,3]) === 2, 'median: odd-length array');\n// assert(median([1,2,3,4]) === 2.5, 'median: even-length array interpolates');\n// assert(median([5]) === 5, 'median: single-element array');\n// assert(median([]) === undefined || Number.isNaN(median([])), 'median: empty array does not throw');\n\n// pctile()\n// assert(pctile([1,2,3,4,5,6,7,8,9,10], 0.5) === median([1,2,3,4,5,6,7,8,9,10]), 'pctile(0.5) matches median');\n// assert(pctile([1,2,3,4,5,6,7,8,9,10], 0.95) >= 9, 'pctile(0.95) is near the top of the range');\n// assert(pctile([1], 0.95) === 1, 'pctile: single-element array returns that element regardless of pct');\n\n// mean()\n// assert(mean([2,4,6]) === 4, 'mean: simple average');\n// assert(mean([]) === 0 || Number.isNaN(mean([])), 'mean: empty array does not throw');\n\nconsole.log(`\\n${passed} passed, ${failed} failed`);\nprocess.exit(failed > 0 ? 1 : 0);"
55
+ }
56
+ ]
57
+ },
58
+ "durationMs": 291126,
59
+ "model": "sonnet",
60
+ "sandboxMode": "permissive",
61
+ "workerType": "testgaps",
62
+ "timestamp": "2026-07-09T17:03:52.819Z",
63
+ "executionId": "testgaps_1783616341693_bl5d9o"
64
+ }
@@ -0,0 +1,14 @@
1
+ [2026-07-09T17:23:52.831Z] PROMPT
2
+ ============================================================
3
+ Analyze test coverage and identify gaps:
4
+ - Find untested functions and classes
5
+ - Identify edge cases not covered
6
+ - Suggest new test scenarios
7
+ - Check for missing error handling tests
8
+ - Identify integration test gaps
9
+
10
+ For each gap, provide a test skeleton.
11
+
12
+ ## Instructions
13
+
14
+ Analyze the codebase and provide your response following the format specified in the task.
@@ -0,0 +1,42 @@
1
+ [2026-07-09T17:29:44.995Z] RESULT
2
+ ============================================================
3
+ {
4
+ "success": true,
5
+ "output": "## Test Coverage Analysis — `plugins/ruflo-metaharness/scripts/`\n\n**Bottom line:** ~7,000 lines across 34 `.mjs` files, no `package.json`, no test framework (no jest/vitest/`node:test` config). The `test-*.mjs` files are hand-rolled assertion scripts, not a real suite — coverage is uneven and several high-risk files (`evolve.mjs`, `mint.mjs` safety checks, `_invoke.mjs` core plumbing) have **zero direct unit tests**.\n\n### Coverage by file (best → worst)\n\n| File | Coverage | Key gap |\n|---|---|---|\n| `_similarity.mjs` | Strong — near-complete unit tests | `perDimension` contribution formulas type-checked, not value-asserted |\n| `audit-trend.mjs` | Strong e2e via `test-pipeline-roundtrip.mjs` | `loadRecord` file-not-found / invalid-JSON paths untested |\n| `router-parallel-analyze.mjs` | Good | `<30` samples, malformed JSONL, missing-file branches untested |\n| `drift-from-history.mjs` | Good (fast paths) | Default slow path (real `audit-list.mjs` call), zero-history branch untested |\n| `oia-audit.mjs` | Decent (dry-run heavy) | `persist()`'s real memory-store call and its failure path **never** executed; partial-degradation (1-4 of 5 sub-audits fail) untested |\n| `mcp-scan.mjs`, `similarity.mjs`, `score.mjs`, `genome.mjs` | Shallow — positive path only | `--format table`, invalid `--fail-on`, alert-triggered exit-1 paths untested |\n| `threat-model.mjs`, `audit-list.mjs` | Weak — grep-only in `smoke.sh` | Pure functions (`parseDurationMs`) never called directly; a **divergent duplicate `SEVERITY_RANK`** in `threat-model.mjs:9` (missing keys vs. `_harness.mjs`) has no drift test |\n| `security-bench.mjs` | Weak | `parseSecurityBenchMarkdown` — a 3-regex markdown parser — has **zero** unit tests (same bug class the iter-50 fix addressed for `parseMcpScanText`, but unprotected here) |\n| `mint.mjs` | Weak | `safetyChecks()` — the \"refuse to write to project root / repo root\" guard, explicitly documented as safety-critical — **completely untested** |\n| `redblue.mjs`, `learn.mjs`, `gepa.mjs` | Weak | Only 1 of ~4 subcommands/ops exercised each; arg-guards and error branches untested |\n| `bench.mjs` | **Zero** functional coverage | Only `node --check` syntax validation |\n| `evolve.mjs` (404 lines, most complex file) | **Near zero** | The entire `--diagnose` subsystem (`buildDiagnosis`, `extractGepaTranscripts`, `summarizeTraces`, `looksLikeGepaTranscript`) — ~130 lines of failure-classification logic — has no test at all |\n| `_invoke.mjs` (shared plumbing, 10 exports) | **Zero direct tests** | `classifyDegraded` — the single most load-bearing function in the whole degradation contract (timeout vs. not-available vs. healthy) — untested in isolation. `parseTrailingJson`'s \"LAST JSON block wins\" behavior, an explicitly-documented prior bug fix, has **no regression test** |\n| `_darwin.mjs` | **Zero direct tests** | `runDarwinAsync`'s timeout-kill and `onProgress` streaming paths untested; used by `evolve.mjs`/`security-bench.mjs`, both safety-critical |\n\n### Meta-testing gap\n`smoke.sh` verifies `test-*.mjs` scripts via `grep -qE \"(N+) passed, 0 failed\"` — a floor-check on the printed count, not real assertion inspection. A test file that silently stops asserting (but still prints \"0 failed\") would pass CI undetected.\n\n### Integration gap\n`test-mcp-tools.mjs` covers the MCP-tool-handler → script path, but **silently no-ops** if `dist/` isn't built (exits 0 with a SKIPPED message). The plain CLI path (`ruflo metaharness score`) is only checked by `smoke.sh` for \"does the dispatcher map mention the filename\" — never actually invoked end-to-end.\n\n### Top 3 gaps worth fixing first\n1. **`_invoke.mjs::classifyDegraded` + `parseTrailingJson`** — foundational, every script depends on these; a `scripts/test-invoke.mjs` with `node:test` would be cheap and high-value (skeleton above).\n2. **`mint.mjs::safetyChecks`** — a documented safety invariant (never scaffold into project root/repo root) with no regression test guarding it.\n3. **`evolve.mjs`'s `--diagnose` subsystem** — most complex untested logic in the plugin; would need exporting 4 currently-module-local helpers first.\n\nFull file-by-file breakdown with file:line references and 8 ready-to-use `node:test` skeletons (covering `_invoke.mjs`, `mint.mjs` safety, `security-bench.mjs` parser, `evolve.mjs` diagnosis, `audit-trend.mjs` edge cases, `router-parallel-analyze.mjs`, `_darwin.mjs` async) is above. Want me to actually write any of these test files, or export the currently-unexported helpers (`buildDiagnosis`, `parseSecurityBenchMarkdown`) so they can be tested?\n",
6
+ "parsedOutput": {
7
+ "sections": [
8
+ {
9
+ "title": "Test Coverage Analysis — `plugins/ruflo-metaharness/scripts/`",
10
+ "content": "\n**Bottom line:** ~7,000 lines across 34 `.mjs` files, no `package.json`, no test framework (no jest/vitest/`node:test` config). The `test-*.mjs` files are hand-rolled assertion scripts, not a real suite — coverage is uneven and several high-risk files (`evolve.mjs`, `mint.mjs` safety checks, `_invoke.mjs` core plumbing) have **zero direct unit tests**.\n\n",
11
+ "level": 2
12
+ },
13
+ {
14
+ "title": "Coverage by file (best → worst)",
15
+ "content": "\n| File | Coverage | Key gap |\n|---|---|---|\n| `_similarity.mjs` | Strong — near-complete unit tests | `perDimension` contribution formulas type-checked, not value-asserted |\n| `audit-trend.mjs` | Strong e2e via `test-pipeline-roundtrip.mjs` | `loadRecord` file-not-found / invalid-JSON paths untested |\n| `router-parallel-analyze.mjs` | Good | `<30` samples, malformed JSONL, missing-file branches untested |\n| `drift-from-history.mjs` | Good (fast paths) | Default slow path (real `audit-list.mjs` call), zero-history branch untested |\n| `oia-audit.mjs` | Decent (dry-run heavy) | `persist()`'s real memory-store call and its failure path **never** executed; partial-degradation (1-4 of 5 sub-audits fail) untested |\n| `mcp-scan.mjs`, `similarity.mjs`, `score.mjs`, `genome.mjs` | Shallow — positive path only | `--format table`, invalid `--fail-on`, alert-triggered exit-1 paths untested |\n| `threat-model.mjs`, `audit-list.mjs` | Weak — grep-only in `smoke.sh` | Pure functions (`parseDurationMs`) never called directly; a **divergent duplicate `SEVERITY_RANK`** in `threat-model.mjs:9` (missing keys vs. `_harness.mjs`) has no drift test |\n| `security-bench.mjs` | Weak | `parseSecurityBenchMarkdown` — a 3-regex markdown parser — has **zero** unit tests (same bug class the iter-50 fix addressed for `parseMcpScanText`, but unprotected here) |\n| `mint.mjs` | Weak | `safetyChecks()` — the \"refuse to write to project root / repo root\" guard, explicitly documented as safety-critical — **completely untested** |\n| `redblue.mjs`, `learn.mjs`, `gepa.mjs` | Weak | Only 1 of ~4 subcommands/ops exercised each; arg-guards and error branches untested |\n| `bench.mjs` | **Zero** functional coverage | Only `node --check` syntax validation |\n| `evolve.mjs` (404 lines, most complex file) | **Near zero** | The entire `--diagnose` subsystem (`buildDiagnosis`, `extractGepaTranscripts`, `summarizeTraces`, `looksLikeGepaTranscript`) — ~130 lines of failure-classification logic — has no test at all |\n| `_invoke.mjs` (shared plumbing, 10 exports) | **Zero direct tests** | `classifyDegraded` — the single most load-bearing function in the whole degradation contract (timeout vs. not-available vs. healthy) — untested in isolation. `parseTrailingJson`'s \"LAST JSON block wins\" behavior, an explicitly-documented prior bug fix, has **no regression test** |\n| `_darwin.mjs` | **Zero direct tests** | `runDarwinAsync`'s timeout-kill and `onProgress` streaming paths untested; used by `evolve.mjs`/`security-bench.mjs`, both safety-critical |\n\n",
16
+ "level": 3
17
+ },
18
+ {
19
+ "title": "Meta-testing gap",
20
+ "content": "`smoke.sh` verifies `test-*.mjs` scripts via `grep -qE \"(N+) passed, 0 failed\"` — a floor-check on the printed count, not real assertion inspection. A test file that silently stops asserting (but still prints \"0 failed\") would pass CI undetected.\n\n",
21
+ "level": 3
22
+ },
23
+ {
24
+ "title": "Integration gap",
25
+ "content": "`test-mcp-tools.mjs` covers the MCP-tool-handler → script path, but **silently no-ops** if `dist/` isn't built (exits 0 with a SKIPPED message). The plain CLI path (`ruflo metaharness score`) is only checked by `smoke.sh` for \"does the dispatcher map mention the filename\" — never actually invoked end-to-end.\n\n",
26
+ "level": 3
27
+ },
28
+ {
29
+ "title": "Top 3 gaps worth fixing first",
30
+ "content": "1. **`_invoke.mjs::classifyDegraded` + `parseTrailingJson`** — foundational, every script depends on these; a `scripts/test-invoke.mjs` with `node:test` would be cheap and high-value (skeleton above).\n2. **`mint.mjs::safetyChecks`** — a documented safety invariant (never scaffold into project root/repo root) with no regression test guarding it.\n3. **`evolve.mjs`'s `--diagnose` subsystem** — most complex untested logic in the plugin; would need exporting 4 currently-module-local helpers first.\n\nFull file-by-file breakdown with file:line references and 8 ready-to-use `node:test` skeletons (covering `_invoke.mjs`, `mint.mjs` safety, `security-bench.mjs` parser, `evolve.mjs` diagnosis, `audit-trend.mjs` edge cases, `router-parallel-analyze.mjs`, `_darwin.mjs` async) is above. Want me to actually write any of these test files, or export the currently-unexported helpers (`buildDiagnosis`, `parseSecurityBenchMarkdown`) so they can be tested?",
31
+ "level": 3
32
+ }
33
+ ],
34
+ "codeBlocks": []
35
+ },
36
+ "durationMs": 352164,
37
+ "model": "sonnet",
38
+ "sandboxMode": "permissive",
39
+ "workerType": "testgaps",
40
+ "timestamp": "2026-07-09T17:29:44.995Z",
41
+ "executionId": "testgaps_1783617832831_ha6s8d"
42
+ }
@@ -0,0 +1,14 @@
1
+ [2026-07-09T17:49:44.960Z] PROMPT
2
+ ============================================================
3
+ Analyze test coverage and identify gaps:
4
+ - Find untested functions and classes
5
+ - Identify edge cases not covered
6
+ - Suggest new test scenarios
7
+ - Check for missing error handling tests
8
+ - Identify integration test gaps
9
+
10
+ For each gap, provide a test skeleton.
11
+
12
+ ## Instructions
13
+
14
+ Analyze the codebase and provide your response following the format specified in the task.
@@ -0,0 +1,47 @@
1
+ [2026-07-09T17:55:46.253Z] RESULT
2
+ ============================================================
3
+ {
4
+ "success": true,
5
+ "output": "## Test Coverage Gap Analysis — `ruflo-metaharness`\n\nThe plugin has no formal test framework — just 26 `.mjs` scripts (~6,900 lines) and 6 ad-hoc `assert`-based `test-*.mjs` runners, plus a `smoke.sh` that only does static grep + `node --check` (never actually executes scripts like `mint.mjs`).\n\n### Untested / weakly-tested functions\n- **`_invoke.mjs`** (the shared dependency of everything) has **zero direct unit tests**: `classifyDegraded`, `injectJson`, `parseTrailingJson`, `satisfiesTildeRange`, `findLocalPackageDir`, `ensureCachedInstall`, `importOptionalLibrary` (the plugin's only `throw` path, at `_invoke.mjs:204`), `makeDegradedEmitter`.\n- **`_darwin.mjs`** and **`_redblue.mjs`**: only their *degraded* branches are ever exercised — the success/happy-path branches (`runDarwin`, `runDarwinAsync`, `runRedblue`) have never been run against a real or mocked binary.\n- **`_harness.mjs`**: `parseMcpScanText`/`rankSeverity` are well-tested; `resolveMetaharnessBins`, `execBin`/`execBinAsync`, `metaharnessResolution` are not.\n- Best-covered module: `_similarity.mjs` (extensively tested), though weight-validation and NaN/negative-input handling are still gaps.\n- CLI scripts with essentially no coverage: `mint.mjs` (grep-only), `gepa.mjs` (validate/render/analyze ops), `learn.mjs`, `redblue.mjs` (init/run/patch/report), `audit-list.mjs`, `security-bench.mjs`'s markdown parser.\n\n### Key edge cases missed\n- NaN/negative/out-of-range numbers in similarity/scoring math (`_similarity.mjs`, `genome.mjs`).\n- Path traversal / non-UTF8 content in user-supplied file args (`audit-trend.mjs --baseline/--current`, `similarity.mjs --a/--b`, `gepa.mjs --path/--transcript`).\n- Concurrent partial-failure races in `oia-audit.mjs::runAllParallel` and `drift-from-history.mjs`'s `Promise.all`.\n- Graceful-degradation drill explicitly omits `gepa.mjs`, `learn.mjs`, `redblue.mjs`, and `mint.mjs` from its skill list — their exit-0/`degraded:true` contract is unverified.\n\n### Missing error-handling tests (highest-value finding)\n`audit-list.mjs`, `audit-trend.mjs`, `oia-audit.mjs::persist`, and `similarity.mjs` all call `spawnSync('npx', ...)` for memory-CLI access **without a timeout** — inconsistent with every other subprocess call in the plugin (which sets `timeoutMs`) — and no test has ever caught this. A hung `npx` would block these calls indefinitely.\n\n**A live regression bug was found during this analysis**: `threat-model.mjs:9` redeclares a stale local `SEVERITY_RANK` map missing `info`/`warn`/`error`/`critical` — the exact bug class that iter 62/63 fixed everywhere else by centralizing the map in `_harness.mjs`. Every sibling script (`mcp-scan.mjs`, `oia-audit.mjs`, `audit-trend.mjs`) imports the shared map; `threat-model.mjs` was missed. If upstream ever emits `worst: \"warn\"` or `\"critical\"`, `--fail-on` silently fails to trigger (`undefined >= threshold` → `false`).\n\n### Integration gaps\n- `drift-from-history.mjs`'s `--baseline-since` variant and its no-flag default path are never exercised end-to-end.\n- `redblue.mjs`'s `init`/`run`/`patch`/`report` subcommands (including the documented $0 `--mock-judge` CI path) have never been run by any test — only `attack` once, through the MCP layer.\n- MCP tool surface vs. raw CLI surface isn't symmetric: some scripts (`mint.mjs`, `audit-list.mjs`) have no MCP tool *and* no CLI-level test — a double gap.\n\n### Test skeletons\nThe full report includes 9 ready-to-adapt skeletons (matching the existing plain-`assert` idiom) for: `_invoke.mjs` primitives, `_darwin.mjs::runDarwin`, a `threat-model.mjs` severity-rank regression lock, a static check that all memory-CLI `spawnSync` calls set a timeout, `gepa.mjs` op dispatch, `security-bench.mjs`'s markdown parser, `redblue.mjs` subcommand dispatch, `oia-audit.mjs::persist` failure path, and `drift-from-history.mjs --baseline-since`.\n\nWant me to write any of these test files (e.g. `scripts/test-invoke.mjs` or the `threat-model.mjs` severity-rank fix) into the repo?\n",
6
+ "parsedOutput": {
7
+ "sections": [
8
+ {
9
+ "title": "Test Coverage Gap Analysis — `ruflo-metaharness`",
10
+ "content": "\nThe plugin has no formal test framework — just 26 `.mjs` scripts (~6,900 lines) and 6 ad-hoc `assert`-based `test-*.mjs` runners, plus a `smoke.sh` that only does static grep + `node --check` (never actually executes scripts like `mint.mjs`).\n\n",
11
+ "level": 2
12
+ },
13
+ {
14
+ "title": "Untested / weakly-tested functions",
15
+ "content": "- **`_invoke.mjs`** (the shared dependency of everything) has **zero direct unit tests**: `classifyDegraded`, `injectJson`, `parseTrailingJson`, `satisfiesTildeRange`, `findLocalPackageDir`, `ensureCachedInstall`, `importOptionalLibrary` (the plugin's only `throw` path, at `_invoke.mjs:204`), `makeDegradedEmitter`.\n- **`_darwin.mjs`** and **`_redblue.mjs`**: only their *degraded* branches are ever exercised — the success/happy-path branches (`runDarwin`, `runDarwinAsync`, `runRedblue`) have never been run against a real or mocked binary.\n- **`_harness.mjs`**: `parseMcpScanText`/`rankSeverity` are well-tested; `resolveMetaharnessBins`, `execBin`/`execBinAsync`, `metaharnessResolution` are not.\n- Best-covered module: `_similarity.mjs` (extensively tested), though weight-validation and NaN/negative-input handling are still gaps.\n- CLI scripts with essentially no coverage: `mint.mjs` (grep-only), `gepa.mjs` (validate/render/analyze ops), `learn.mjs`, `redblue.mjs` (init/run/patch/report), `audit-list.mjs`, `security-bench.mjs`'s markdown parser.\n\n",
16
+ "level": 3
17
+ },
18
+ {
19
+ "title": "Key edge cases missed",
20
+ "content": "- NaN/negative/out-of-range numbers in similarity/scoring math (`_similarity.mjs`, `genome.mjs`).\n- Path traversal / non-UTF8 content in user-supplied file args (`audit-trend.mjs --baseline/--current`, `similarity.mjs --a/--b`, `gepa.mjs --path/--transcript`).\n- Concurrent partial-failure races in `oia-audit.mjs::runAllParallel` and `drift-from-history.mjs`'s `Promise.all`.\n- Graceful-degradation drill explicitly omits `gepa.mjs`, `learn.mjs`, `redblue.mjs`, and `mint.mjs` from its skill list — their exit-0/`degraded:true` contract is unverified.\n\n",
21
+ "level": 3
22
+ },
23
+ {
24
+ "title": "Missing error-handling tests (highest-value finding)",
25
+ "content": "`audit-list.mjs`, `audit-trend.mjs`, `oia-audit.mjs::persist`, and `similarity.mjs` all call `spawnSync('npx', ...)` for memory-CLI access **without a timeout** — inconsistent with every other subprocess call in the plugin (which sets `timeoutMs`) — and no test has ever caught this. A hung `npx` would block these calls indefinitely.\n\n**A live regression bug was found during this analysis**: `threat-model.mjs:9` redeclares a stale local `SEVERITY_RANK` map missing `info`/`warn`/`error`/`critical` — the exact bug class that iter 62/63 fixed everywhere else by centralizing the map in `_harness.mjs`. Every sibling script (`mcp-scan.mjs`, `oia-audit.mjs`, `audit-trend.mjs`) imports the shared map; `threat-model.mjs` was missed. If upstream ever emits `worst: \"warn\"` or `\"critical\"`, `--fail-on` silently fails to trigger (`undefined >= threshold` → `false`).\n\n",
26
+ "level": 3
27
+ },
28
+ {
29
+ "title": "Integration gaps",
30
+ "content": "- `drift-from-history.mjs`'s `--baseline-since` variant and its no-flag default path are never exercised end-to-end.\n- `redblue.mjs`'s `init`/`run`/`patch`/`report` subcommands (including the documented $0 `--mock-judge` CI path) have never been run by any test — only `attack` once, through the MCP layer.\n- MCP tool surface vs. raw CLI surface isn't symmetric: some scripts (`mint.mjs`, `audit-list.mjs`) have no MCP tool *and* no CLI-level test — a double gap.\n\n",
31
+ "level": 3
32
+ },
33
+ {
34
+ "title": "Test skeletons",
35
+ "content": "The full report includes 9 ready-to-adapt skeletons (matching the existing plain-`assert` idiom) for: `_invoke.mjs` primitives, `_darwin.mjs::runDarwin`, a `threat-model.mjs` severity-rank regression lock, a static check that all memory-CLI `spawnSync` calls set a timeout, `gepa.mjs` op dispatch, `security-bench.mjs`'s markdown parser, `redblue.mjs` subcommand dispatch, `oia-audit.mjs::persist` failure path, and `drift-from-history.mjs --baseline-since`.\n\nWant me to write any of these test files (e.g. `scripts/test-invoke.mjs` or the `threat-model.mjs` severity-rank fix) into the repo?",
36
+ "level": 3
37
+ }
38
+ ],
39
+ "codeBlocks": []
40
+ },
41
+ "durationMs": 361294,
42
+ "model": "sonnet",
43
+ "sandboxMode": "permissive",
44
+ "workerType": "testgaps",
45
+ "timestamp": "2026-07-09T17:55:46.253Z",
46
+ "executionId": "testgaps_1783619384959_s5iiwf"
47
+ }
@@ -0,0 +1,14 @@
1
+ [2026-07-09T18:15:46.263Z] PROMPT
2
+ ============================================================
3
+ Analyze test coverage and identify gaps:
4
+ - Find untested functions and classes
5
+ - Identify edge cases not covered
6
+ - Suggest new test scenarios
7
+ - Check for missing error handling tests
8
+ - Identify integration test gaps
9
+
10
+ For each gap, provide a test skeleton.
11
+
12
+ ## Instructions
13
+
14
+ Analyze the codebase and provide your response following the format specified in the task.
@@ -0,0 +1,52 @@
1
+ [2026-07-09T18:21:13.824Z] RESULT
2
+ ============================================================
3
+ {
4
+ "success": true,
5
+ "output": "## Test Coverage Analysis — `plugins/ruflo-metaharness`\n\nThis plugin has no formal test framework (no package.json, no jest/vitest) — coverage comes from ad hoc `test-*.mjs` scripts using a custom `assert(cond, label)` + `process.exit()` pattern across ~6,900 lines in `scripts/`. Full findings below.\n\n### 1. Untested functions and classes\n\n**`_invoke.mjs`** — the shared plumbing used by ~15 scripts has **zero direct unit tests**: `classifyDegraded`, `injectJson`, `parseTrailingJson`, `satisfiesTildeRange`, `findLocalPackageDir`, `ensureCachedInstall`, `importOptionalLibrary`, `makeDegradedEmitter` are only exercised indirectly (and only the degraded branch, via subprocess string-matching).\n\n**`_harness.mjs`** — `runMetaharness`/`runHarness`/`runMetaharnessAsync`/`runHarnessAsync`/`metaharnessResolution`/`emitDegradedJsonAndExit` are untested as functions (only side effects checked transitively).\n\n**CLI scripts with zero or degraded-only coverage**: `mint.mjs` (its `safetyChecks()` root-refusal logic is completely unverified — notable since it's write-capable), `redblue.mjs` (only `attack` generically probed; `init`/`run`/`patch`/`report` untested), `learn.mjs`, `gepa.mjs`'s `validate`/`render`/`analyze` ops, `audit-list.mjs` (never invoked directly by any test), `evolve.mjs`'s pure helpers (`looksLikeGepaTranscript`, `extractGepaTranscripts`, `summarizeTraces` — zero-cost, zero-subprocess, yet untested), `security-bench.mjs`'s `parseSecurityBenchMarkdown` (a 3-regex parser with no analog to the test that exists for its sibling `bench-parse-mcp-scan.mjs`).\n\n`_spike-similarity.mjs` has its own built-in invariant checks but isn't wired into any `test-*.mjs` — a regression would only surface if someone runs it manually.\n\nExample skeleton (pattern repeats for each untested export):\n```js\n// test-invoke.mjs — zero-coverage core plumbing\nimport { classifyDegraded, injectJson, parseTrailingJson, satisfiesTildeRange } from './_invoke.mjs';\nlet passed=0, failed=0;\nfunction assert(c,l){ console.log(` ${c?'✓':'✗'} ${l}`); c?passed++:failed++; }\nassert(classifyDegraded('', null, 'x').reason === 'x-timeout', 'null exitCode -> timeout');\nassert(parseTrailingJson('progress {\"a\":1}\\ndone {\"b\":2}').b === 2, 'grabs LAST json block');\nassert(satisfiesTildeRange('0.3.2', '~0.3.0') === true, 'tilde range patch match');\nprocess.exit(failed ? 1 : 0);\n```\n\n### 2. Edge cases not covered\n\n- `_similarity.mjs` `cosine`/`jaccard`: no test for `NaN`/`Infinity` inputs, negative scores, or duplicate `agent_topology` entries.\n- `parseMcpScanText`: no test for malformed `Result:` lines (non-numeric count → `NaN` propagates unasserted) or CRLF endings.\n- `mcp-scan.mjs --fail-on medium|low` and its invalid-value exit-2 path: never exercised (only default `high` tested).\n- `router-parallel-analyze.mjs`: no test for a partially-corrupt JSONL file (one malformed line would throw synchronously in `.map(JSON.parse)`).\n- `audit-trend.mjs`'s `verdict: 'unavailable'` fallback (pre-iter-38 records without fingerprints) is never triggered by any test.\n\n### 3. Missing error handling tests\n\n- **Likely latent bug, not just a test gap**: `audit-list.mjs`, `audit-trend.mjs`, and `similarity.mjs`'s `memList()`/`memRetrieve()` `spawnSync` calls have **no `timeout` option**, unlike every other subprocess call in this plugin family — a hung `npx ... memory list` would hang the script forever.\n- `oia-audit.mjs`'s `persist()` failure path (`payload.persisted.error`) is never forced/verified.\n- `mint.mjs`'s non-degraded failure branch (`metaharness new` exiting non-zero) is never simulated.\n- 9 of ~18 CLI scripts (`redblue`, `learn`, `gepa`, `mint`, `drift-from-history`, `audit-trend`, `audit-list`, `similarity`, `router-parallel-analyze`) are **excluded** from `test-graceful-degradation.mjs`'s skill list entirely — no automated \"upstream unavailable → exit 0 + degraded:true\" check for them.\n\n### 4. Integration test gaps\n\n- `drift-from-history.mjs`'s **default slow path** (no `--baseline-key`/`--baseline-file`, which actually calls `audit-list.mjs`) — the doc-advertised \"composes 3 primitives\" workflow — is never exercised; every existing test uses a fast-path flag that skips it.\n- `redblue.mjs`'s full `init → run → patch → report` lifecycle has no end-to-end test.\n- Of 15 registered MCP tools, only ~4 get real positive-data-shape assertions in `test-mcp-tools.mjs`; the rest (`redblue`, `learn`, `gepa`, `evolve`, `bench`, `security_bench`, `score`, `genome`, `threat_model`, `audit_list`) only get generic \"didn't throw\" checks.\n- The actual production \"weekly cron\" workflow (`oia-audit` persist → `audit-list` → `audit-trend` diff on **real**, non-fixture, non-dry-run data) has never been driven end-to-end.\n\n### 5. Top 15 prioritized new test scenarios (risk/value order)\n\n1. Unit-test `_invoke.mjs` directly — shared plumbing for ~15 scripts, currently zero direct coverage.\n2. Add `timeout` to `audit-list.mjs`/`audit-trend.mjs`/`similarity.mjs`'s `memList`/`memRetrieve` calls + regression test (hang risk).\n3. Fix `threat-model.mjs:9`'s local, out-of-sync `SEVERITY_RANK` literal (missing `info`/`warn`/`critical`/`error` vs. the shared one in `_harness.mjs`) — same bug class iter-63 fixed elsewhere.\n4. E2E test of `drift-from-history.mjs`'s default slow path.\n5. Add `mint.mjs` (write-capable, zero coverage) to the test suite — start with `safetyChecks()` in isolation.\n6. Unit-test `evolve.mjs`'s pure helpers (zero subprocess cost, currently untested).\n7. Unit-test `security-bench.mjs`'s `parseSecurityBenchMarkdown` against a realistic fixture.\n8. Extend `test-graceful-degradation.mjs` to cover the 9 currently-excluded scripts.\n9. Deepen `test-mcp-tools.mjs` Phase 4 with positive-data assertions for the 10 shallow-covered tools.\n10. Test `parseMcpScanText` against malformed `Result:` lines and CRLF input.\n11. Test `oia-audit.mjs`'s persist-failure surfacing.\n12. Real (non-dry-run) `oia-audit → audit-list → audit-trend` chain test.\n13. Test `_similarity.mjs` numeric edge cases (NaN/Infinity/negative/duplicates).\n14. Test `router-parallel-analyze.mjs` against partially-corrupt JSONL.\n15. Wire `_spike-similarity.mjs`'s invariant checks into the automated suite so CI catches ADR-152 regressions.\n\nNote: item 3 is a genuine bug find (not just missing coverage) — `threat-model.mjs` duplicates `SEVERITY_RANK` locally instead of importing the shared one, and its copy is missing values. Worth fixing regardless of test-writing priority. Want me to dig into that one now, or start writing any of these test skeletons for real?\n",
6
+ "parsedOutput": {
7
+ "sections": [
8
+ {
9
+ "title": "Test Coverage Analysis — `plugins/ruflo-metaharness`",
10
+ "content": "\nThis plugin has no formal test framework (no package.json, no jest/vitest) — coverage comes from ad hoc `test-*.mjs` scripts using a custom `assert(cond, label)` + `process.exit()` pattern across ~6,900 lines in `scripts/`. Full findings below.\n\n",
11
+ "level": 2
12
+ },
13
+ {
14
+ "title": "1. Untested functions and classes",
15
+ "content": "\n**`_invoke.mjs`** — the shared plumbing used by ~15 scripts has **zero direct unit tests**: `classifyDegraded`, `injectJson`, `parseTrailingJson`, `satisfiesTildeRange`, `findLocalPackageDir`, `ensureCachedInstall`, `importOptionalLibrary`, `makeDegradedEmitter` are only exercised indirectly (and only the degraded branch, via subprocess string-matching).\n\n**`_harness.mjs`** — `runMetaharness`/`runHarness`/`runMetaharnessAsync`/`runHarnessAsync`/`metaharnessResolution`/`emitDegradedJsonAndExit` are untested as functions (only side effects checked transitively).\n\n**CLI scripts with zero or degraded-only coverage**: `mint.mjs` (its `safetyChecks()` root-refusal logic is completely unverified — notable since it's write-capable), `redblue.mjs` (only `attack` generically probed; `init`/`run`/`patch`/`report` untested), `learn.mjs`, `gepa.mjs`'s `validate`/`render`/`analyze` ops, `audit-list.mjs` (never invoked directly by any test), `evolve.mjs`'s pure helpers (`looksLikeGepaTranscript`, `extractGepaTranscripts`, `summarizeTraces` — zero-cost, zero-subprocess, yet untested), `security-bench.mjs`'s `parseSecurityBenchMarkdown` (a 3-regex parser with no analog to the test that exists for its sibling `bench-parse-mcp-scan.mjs`).\n\n`_spike-similarity.mjs` has its own built-in invariant checks but isn't wired into any `test-*.mjs` — a regression would only surface if someone runs it manually.\n\nExample skeleton (pattern repeats for each untested export):\n```js\n// test-invoke.mjs — zero-coverage core plumbing\nimport { classifyDegraded, injectJson, parseTrailingJson, satisfiesTildeRange } from './_invoke.mjs';\nlet passed=0, failed=0;\nfunction assert(c,l){ console.log(` ${c?'✓':'✗'} ${l}`); c?passed++:failed++; }\nassert(classifyDegraded('', null, 'x').reason === 'x-timeout', 'null exitCode -> timeout');\nassert(parseTrailingJson('progress {\"a\":1}\\ndone {\"b\":2}').b === 2, 'grabs LAST json block');\nassert(satisfiesTildeRange('0.3.2', '~0.3.0') === true, 'tilde range patch match');\nprocess.exit(failed ? 1 : 0);\n```\n\n",
16
+ "level": 3
17
+ },
18
+ {
19
+ "title": "2. Edge cases not covered",
20
+ "content": "\n- `_similarity.mjs` `cosine`/`jaccard`: no test for `NaN`/`Infinity` inputs, negative scores, or duplicate `agent_topology` entries.\n- `parseMcpScanText`: no test for malformed `Result:` lines (non-numeric count → `NaN` propagates unasserted) or CRLF endings.\n- `mcp-scan.mjs --fail-on medium|low` and its invalid-value exit-2 path: never exercised (only default `high` tested).\n- `router-parallel-analyze.mjs`: no test for a partially-corrupt JSONL file (one malformed line would throw synchronously in `.map(JSON.parse)`).\n- `audit-trend.mjs`'s `verdict: 'unavailable'` fallback (pre-iter-38 records without fingerprints) is never triggered by any test.\n\n",
21
+ "level": 3
22
+ },
23
+ {
24
+ "title": "3. Missing error handling tests",
25
+ "content": "\n- **Likely latent bug, not just a test gap**: `audit-list.mjs`, `audit-trend.mjs`, and `similarity.mjs`'s `memList()`/`memRetrieve()` `spawnSync` calls have **no `timeout` option**, unlike every other subprocess call in this plugin family — a hung `npx ... memory list` would hang the script forever.\n- `oia-audit.mjs`'s `persist()` failure path (`payload.persisted.error`) is never forced/verified.\n- `mint.mjs`'s non-degraded failure branch (`metaharness new` exiting non-zero) is never simulated.\n- 9 of ~18 CLI scripts (`redblue`, `learn`, `gepa`, `mint`, `drift-from-history`, `audit-trend`, `audit-list`, `similarity`, `router-parallel-analyze`) are **excluded** from `test-graceful-degradation.mjs`'s skill list entirely — no automated \"upstream unavailable → exit 0 + degraded:true\" check for them.\n\n",
26
+ "level": 3
27
+ },
28
+ {
29
+ "title": "4. Integration test gaps",
30
+ "content": "\n- `drift-from-history.mjs`'s **default slow path** (no `--baseline-key`/`--baseline-file`, which actually calls `audit-list.mjs`) — the doc-advertised \"composes 3 primitives\" workflow — is never exercised; every existing test uses a fast-path flag that skips it.\n- `redblue.mjs`'s full `init → run → patch → report` lifecycle has no end-to-end test.\n- Of 15 registered MCP tools, only ~4 get real positive-data-shape assertions in `test-mcp-tools.mjs`; the rest (`redblue`, `learn`, `gepa`, `evolve`, `bench`, `security_bench`, `score`, `genome`, `threat_model`, `audit_list`) only get generic \"didn't throw\" checks.\n- The actual production \"weekly cron\" workflow (`oia-audit` persist → `audit-list` → `audit-trend` diff on **real**, non-fixture, non-dry-run data) has never been driven end-to-end.\n\n",
31
+ "level": 3
32
+ },
33
+ {
34
+ "title": "5. Top 15 prioritized new test scenarios (risk/value order)",
35
+ "content": "1. Unit-test `_invoke.mjs` directly — shared plumbing for ~15 scripts, currently zero direct coverage.\n2. Add `timeout` to `audit-list.mjs`/`audit-trend.mjs`/`similarity.mjs`'s `memList`/`memRetrieve` calls + regression test (hang risk).\n3. Fix `threat-model.mjs:9`'s local, out-of-sync `SEVERITY_RANK` literal (missing `info`/`warn`/`critical`/`error` vs. the shared one in `_harness.mjs`) — same bug class iter-63 fixed elsewhere.\n4. E2E test of `drift-from-history.mjs`'s default slow path.\n5. Add `mint.mjs` (write-capable, zero coverage) to the test suite — start with `safetyChecks()` in isolation.\n6. Unit-test `evolve.mjs`'s pure helpers (zero subprocess cost, currently untested).\n7. Unit-test `security-bench.mjs`'s `parseSecurityBenchMarkdown` against a realistic fixture.\n8. Extend `test-graceful-degradation.mjs` to cover the 9 currently-excluded scripts.\n9. Deepen `test-mcp-tools.mjs` Phase 4 with positive-data assertions for the 10 shallow-covered tools.\n10. Test `parseMcpScanText` against malformed `Result:` lines and CRLF input.\n11. Test `oia-audit.mjs`'s persist-failure surfacing.\n12. Real (non-dry-run) `oia-audit → audit-list → audit-trend` chain test.\n13. Test `_similarity.mjs` numeric edge cases (NaN/Infinity/negative/duplicates).\n14. Test `router-parallel-analyze.mjs` against partially-corrupt JSONL.\n15. Wire `_spike-similarity.mjs`'s invariant checks into the automated suite so CI catches ADR-152 regressions.\n\nNote: item 3 is a genuine bug find (not just missing coverage) — `threat-model.mjs` duplicates `SEVERITY_RANK` locally instead of importing the shared one, and its copy is missing values. Worth fixing regardless of test-writing priority. Want me to dig into that one now, or start writing any of these test skeletons for real?",
36
+ "level": 3
37
+ }
38
+ ],
39
+ "codeBlocks": [
40
+ {
41
+ "language": "js",
42
+ "code": "// test-invoke.mjs — zero-coverage core plumbing\nimport { classifyDegraded, injectJson, parseTrailingJson, satisfiesTildeRange } from './_invoke.mjs';\nlet passed=0, failed=0;\nfunction assert(c,l){ console.log(` ${c?'✓':'✗'} ${l}`); c?passed++:failed++; }\nassert(classifyDegraded('', null, 'x').reason === 'x-timeout', 'null exitCode -> timeout');\nassert(parseTrailingJson('progress {\"a\":1}\\ndone {\"b\":2}').b === 2, 'grabs LAST json block');\nassert(satisfiesTildeRange('0.3.2', '~0.3.0') === true, 'tilde range patch match');\nprocess.exit(failed ? 1 : 0);"
43
+ }
44
+ ]
45
+ },
46
+ "durationMs": 327560,
47
+ "model": "sonnet",
48
+ "sandboxMode": "permissive",
49
+ "workerType": "testgaps",
50
+ "timestamp": "2026-07-09T18:21:13.823Z",
51
+ "executionId": "testgaps_1783620946263_d7ovai"
52
+ }
@@ -0,0 +1,14 @@
1
+ [2026-07-09T18:41:13.847Z] PROMPT
2
+ ============================================================
3
+ Analyze test coverage and identify gaps:
4
+ - Find untested functions and classes
5
+ - Identify edge cases not covered
6
+ - Suggest new test scenarios
7
+ - Check for missing error handling tests
8
+ - Identify integration test gaps
9
+
10
+ For each gap, provide a test skeleton.
11
+
12
+ ## Instructions
13
+
14
+ Analyze the codebase and provide your response following the format specified in the task.