@hassangameryt/freeflow 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (692) hide show
  1. package/.claude-plugin/marketplace.json +4 -4
  2. package/.claude-plugin/plugin.json +6 -2
  3. package/.codex-plugin/plugin.json +9 -9
  4. package/README.md +190 -127
  5. package/assets/feedback-loop.png +0 -0
  6. package/assets/freeflow_workflow_map.png +0 -0
  7. package/assets/workflow.png +0 -0
  8. package/{skills → capabilities}/output-router/SKILL.md +21 -17
  9. package/{skills → capabilities}/output-router/references/safety-policy.md +1 -1
  10. package/capabilities/output-router/references/setup.md +110 -0
  11. package/command-surface.json +62 -7
  12. package/hooks/freeflow-runtime-context.mjs +572 -154
  13. package/hooks/hooks.json +26 -1
  14. package/hooks/tests/check-runtime-context-hook.sh +375 -0
  15. package/package.json +24 -24
  16. package/pi-extension/dist/index.js +264 -80
  17. package/pi-extension/dist/integrations/mcp-config.js +29 -0
  18. package/pi-extension/dist/output-router/host-producer-identification.js +232 -0
  19. package/pi-extension/dist/output-router/native-safety-net.js +216 -0
  20. package/pi-extension/dist/output-router/observed-tool-routing.js +122 -0
  21. package/pi-extension/dist/output-router/renderers.js +1023 -0
  22. package/pi-extension/dist/output-router/router-tools.js +554 -0
  23. package/pi-extension/dist/output-router/schemas.js +383 -0
  24. package/pi-extension/dist/output-router/status.js +744 -0
  25. package/pi-extension/dist/output-router/utils.js +502 -0
  26. package/pi-extension/dist/runtime/runtime-context.js +798 -0
  27. package/pi-extension/dist/settings/settings-tui.js +314 -0
  28. package/pi-extension/dist/settings/settings-ui.js +1487 -0
  29. package/pi-extension/freeflow/index.js +1 -0
  30. package/pi-extension/src/index.ts +230 -56
  31. package/pi-extension/src/{host-producer-identification.ts → output-router/host-producer-identification.ts} +37 -7
  32. package/pi-extension/src/{native-safety-net.ts → output-router/native-safety-net.ts} +8 -12
  33. package/pi-extension/src/{observed-tool-routing.ts → output-router/observed-tool-routing.ts} +19 -11
  34. package/pi-extension/src/{renderers.ts → output-router/renderers.ts} +246 -55
  35. package/pi-extension/src/{router-tools.ts → output-router/router-tools.ts} +130 -29
  36. package/pi-extension/src/{schemas.ts → output-router/schemas.ts} +171 -43
  37. package/pi-extension/src/output-router/status.ts +791 -0
  38. package/pi-extension/src/{utils.ts → output-router/utils.ts} +80 -18
  39. package/pi-extension/src/runtime/runtime-context.ts +895 -0
  40. package/pi-extension/src/settings/settings-tui.ts +408 -0
  41. package/pi-extension/src/settings/settings-ui.ts +1730 -0
  42. package/pi-extension/tests/integration/pi-extension.test.js +4020 -0
  43. package/pi-extension/tests/output-router/observed-producers.test.js +132 -0
  44. package/pi-extension/tests/output-router/search.test.js +722 -0
  45. package/pi-extension/tests/settings/settings-tui.test.js +243 -0
  46. package/router/dist/benchmarks/benchmark-harness.d.ts +16 -13
  47. package/router/dist/benchmarks/benchmark-harness.js +62 -62
  48. package/router/dist/benchmarks/benchmarks.d.ts +80 -73
  49. package/router/dist/benchmarks/benchmarks.js +860 -761
  50. package/router/dist/benchmarks/codex-qa-benchmarks.d.ts +71 -67
  51. package/router/dist/benchmarks/codex-qa-benchmarks.js +429 -390
  52. package/router/dist/benchmarks/command-benchmarks.d.ts +107 -92
  53. package/router/dist/benchmarks/command-benchmarks.js +616 -571
  54. package/router/dist/benchmarks/context-mode-normalized-benchmarks.d.ts +64 -56
  55. package/router/dist/benchmarks/context-mode-normalized-benchmarks.js +679 -613
  56. package/router/dist/benchmarks/context-mode-real-deep-benchmark.d.ts +103 -95
  57. package/router/dist/benchmarks/context-mode-real-deep-benchmark.js +1188 -1103
  58. package/router/dist/benchmarks/context-saving-baseline.d.ts +47 -41
  59. package/router/dist/benchmarks/context-saving-baseline.js +405 -374
  60. package/router/dist/benchmarks/index-benchmarks.d.ts +74 -69
  61. package/router/dist/benchmarks/index-benchmarks.js +761 -661
  62. package/router/dist/benchmarks/storage-policy-benchmarks.d.ts +80 -69
  63. package/router/dist/benchmarks/storage-policy-benchmarks.js +549 -469
  64. package/router/dist/benchmarks/vault-index-benchmarks.d.ts +43 -34
  65. package/router/dist/benchmarks/vault-index-benchmarks.js +294 -229
  66. package/router/dist/config/config.d.ts +62 -55
  67. package/router/dist/config/config.js +552 -373
  68. package/router/dist/config/router-contract.d.ts +2 -2
  69. package/router/dist/config/router-contract.js +38 -38
  70. package/router/dist/config/schema.d.ts +19 -10
  71. package/router/dist/config/schema.js +586 -538
  72. package/router/dist/config/types.d.ts +413 -361
  73. package/router/dist/config/types.js +19 -8
  74. package/router/dist/evidence/bounded-evidence.d.ts +26 -26
  75. package/router/dist/evidence/bounded-evidence.js +265 -252
  76. package/router/dist/evidence/evidence-range-selector.d.ts +16 -14
  77. package/router/dist/evidence/evidence-range-selector.js +192 -191
  78. package/router/dist/evidence/evidence-search.d.ts +18 -16
  79. package/router/dist/evidence/evidence-search.js +401 -377
  80. package/router/dist/evidence/evidence.d.ts +17 -17
  81. package/router/dist/evidence/evidence.js +163 -159
  82. package/router/dist/evidence/failure-contracts.d.ts +25 -15
  83. package/router/dist/evidence/failure-contracts.js +132 -126
  84. package/router/dist/evidence/line-ranges.d.ts +17 -15
  85. package/router/dist/evidence/line-ranges.js +18 -18
  86. package/router/dist/experiments/local-index.d.ts +47 -41
  87. package/router/dist/experiments/local-index.js +477 -458
  88. package/router/dist/local/local-traversal.d.ts +18 -0
  89. package/router/dist/local/local-traversal.js +334 -0
  90. package/router/dist/processing/engine.d.ts +94 -70
  91. package/router/dist/processing/engine.js +462 -364
  92. package/router/dist/processing/reducers.d.ts +208 -194
  93. package/router/dist/processing/reducers.js +1434 -1256
  94. package/router/dist/processing/renderers.d.ts +24 -18
  95. package/router/dist/processing/renderers.js +161 -152
  96. package/router/dist/processing/scripts.d.ts +80 -67
  97. package/router/dist/processing/scripts.js +343 -290
  98. package/router/dist/repo/repo-traversal.d.ts +9 -9
  99. package/router/dist/repo/repo-traversal.js +205 -202
  100. package/router/dist/routing/observed-reducers.d.ts +25 -19
  101. package/router/dist/routing/observed-reducers.js +559 -545
  102. package/router/dist/routing/observed-routing.d.ts +23 -14
  103. package/router/dist/routing/observed-routing.js +319 -318
  104. package/router/dist/routing/parsers.d.ts +10 -10
  105. package/router/dist/routing/parsers.js +454 -406
  106. package/router/dist/routing/run-filters.d.ts +45 -41
  107. package/router/dist/routing/run-filters.js +215 -207
  108. package/router/dist/routing/run-reducers.d.ts +45 -26
  109. package/router/dist/routing/run-reducers.js +102 -90
  110. package/router/dist/sandbox/adapter-roots.d.ts +34 -26
  111. package/router/dist/sandbox/adapter-roots.js +43 -41
  112. package/router/dist/sandbox/eryx-python-adapter.d.ts +9 -5
  113. package/router/dist/sandbox/eryx-python-adapter.js +659 -588
  114. package/router/dist/sandbox/jq-wasm-adapter.d.ts +3 -3
  115. package/router/dist/sandbox/jq-wasm-adapter.js +319 -311
  116. package/router/dist/sandbox/quickjs-wasi-adapter.d.ts +9 -5
  117. package/router/dist/sandbox/quickjs-wasi-adapter.js +336 -333
  118. package/router/dist/sandbox/script-sandbox.d.ts +105 -84
  119. package/router/dist/sandbox/script-sandbox.js +294 -260
  120. package/router/dist/setup/script-transform-adapters.d.ts +23 -19
  121. package/router/dist/setup/script-transform-adapters.js +223 -204
  122. package/router/dist/tools/batch.d.ts +27 -16
  123. package/router/dist/tools/batch.js +493 -416
  124. package/router/dist/tools/run.d.ts +47 -35
  125. package/router/dist/tools/run.js +1172 -1087
  126. package/router/dist/tools/search.d.ts +53 -31
  127. package/router/dist/tools/search.js +1224 -1065
  128. package/router/dist/transform/engine.d.ts +121 -96
  129. package/router/dist/transform/engine.js +1853 -1736
  130. package/router/dist/vault/vault-index.d.ts +97 -78
  131. package/router/dist/vault/vault-index.js +347 -342
  132. package/router/dist/vault/vault.d.ts +109 -74
  133. package/router/dist/vault/vault.js +528 -533
  134. package/runtime/interaction-contract.md +16 -0
  135. package/skills/bypass/SKILL.md +52 -38
  136. package/skills/commit-work/SKILL.md +54 -46
  137. package/skills/commit-work/references/staging-decisions.md +46 -24
  138. package/skills/decision-gate/SKILL.md +48 -0
  139. package/skills/design-for-depth/SKILL.md +75 -59
  140. package/skills/design-for-depth/references/design-pressure-signals.md +56 -7
  141. package/skills/design-for-depth/references/interface-design-loop.md +156 -0
  142. package/skills/design-for-depth/references/software-design-philosophy.md +32 -3
  143. package/skills/diagnose-failure/SKILL.md +65 -60
  144. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +36 -0
  145. package/skills/diagnose-failure/references/flaky-and-performance.md +30 -18
  146. package/skills/discuss/SKILL.md +121 -0
  147. package/skills/discuss/references/checkpoints.md +66 -0
  148. package/skills/evaluate-skill/SKILL.md +62 -53
  149. package/skills/evaluate-skill/references/definition-schema.md +345 -0
  150. package/skills/evaluate-skill/references/evaluation-design.md +86 -0
  151. package/skills/evaluate-skill/references/execution-and-evidence.md +85 -0
  152. package/skills/evaluate-skill/references/review-and-revision.md +51 -0
  153. package/skills/evaluate-skill/scripts/lib/definitions.mjs +296 -0
  154. package/skills/evaluate-skill/scripts/lib/evidence.mjs +46 -0
  155. package/skills/evaluate-skill/scripts/lib/grade-comparison.mjs +60 -0
  156. package/skills/evaluate-skill/scripts/lib/grade-interaction.mjs +148 -0
  157. package/skills/evaluate-skill/scripts/lib/grade-support.mjs +126 -0
  158. package/skills/evaluate-skill/scripts/lib/grade-workspace.mjs +192 -0
  159. package/skills/evaluate-skill/scripts/lib/grade.mjs +56 -0
  160. package/skills/evaluate-skill/scripts/lib/pi.mjs +489 -0
  161. package/skills/evaluate-skill/scripts/lib/process.mjs +79 -0
  162. package/skills/evaluate-skill/scripts/lib/rpc.mjs +444 -0
  163. package/skills/evaluate-skill/scripts/lib/runner.mjs +346 -0
  164. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +409 -0
  165. package/skills/evaluate-skill/scripts/lib/view.mjs +337 -0
  166. package/skills/evaluate-skill/scripts/pi-guard.mjs +164 -0
  167. package/skills/evaluate-skill/scripts/skill-eval.mjs +101 -0
  168. package/skills/execute-work/SKILL.md +99 -0
  169. package/skills/execute-work/references/code-practices.md +78 -0
  170. package/skills/execute-work/references/execution-loop.md +77 -0
  171. package/skills/finish-branch/SKILL.md +117 -0
  172. package/skills/finish-branch/references/integration-options.md +80 -0
  173. package/skills/handoff/SKILL.md +65 -39
  174. package/skills/handoff/references/templates.md +104 -27
  175. package/skills/launch-work/SKILL.md +116 -0
  176. package/skills/launch-work/references/launch-readiness.md +85 -0
  177. package/skills/migration-work/SKILL.md +115 -0
  178. package/skills/migration-work/references/migration-lifecycle.md +82 -0
  179. package/skills/mode-contract/SKILL.md +72 -52
  180. package/skills/release-work/SKILL.md +118 -0
  181. package/skills/release-work/references/release-evidence.md +78 -0
  182. package/skills/review-artifact/SKILL.md +77 -69
  183. package/skills/review-artifact/references/reviewer-prompt.md +133 -42
  184. package/skills/review-work/SKILL.md +82 -57
  185. package/skills/review-work/references/reviewer-prompt.md +97 -60
  186. package/skills/review-work/references/security-risk-lens.md +67 -0
  187. package/skills/setup-freeflow/SKILL.md +54 -68
  188. package/skills/setup-freeflow/references/activation-contract.md +84 -69
  189. package/skills/setup-freeflow/references/host-setup.md +50 -17
  190. package/skills/simplify-code/SKILL.md +80 -0
  191. package/skills/simplify-code/references/simplification-patterns.md +70 -0
  192. package/skills/tdd/SKILL.md +96 -0
  193. package/skills/tdd/references/test-design.md +96 -0
  194. package/skills/track-work/SKILL.md +84 -0
  195. package/skills/track-work/references/working-record-schema.md +378 -0
  196. package/skills/verify-work/SKILL.md +59 -38
  197. package/skills/verify-work/references/browser-runtime-evidence.md +55 -0
  198. package/skills/verify-work/references/integration-evidence.md +51 -0
  199. package/skills/verify-work/references/performance-evidence.md +57 -0
  200. package/skills/workflow/SKILL.md +53 -59
  201. package/skills/workflow/references/domain-skill-composition.md +57 -0
  202. package/skills/workflow/references/workflow-loop.md +92 -0
  203. package/skills/write-plan/SKILL.md +85 -58
  204. package/skills/write-plan/references/plan-shapes.md +132 -46
  205. package/skills/write-skill/SKILL.md +82 -51
  206. package/skills/write-skill/references/activation-boundaries.md +62 -0
  207. package/skills/write-skill/references/agent-first-instructions.md +87 -0
  208. package/skills/write-skill/references/progressive-disclosure.md +63 -0
  209. package/skills/write-skill/scripts/lib/skill-author.mjs +653 -0
  210. package/skills/write-skill/scripts/skill-author.mjs +104 -0
  211. package/skills/write-spec/SKILL.md +65 -54
  212. package/skills/write-spec/references/artifact-standards.md +60 -41
  213. package/skills/write-spec/references/decision-records.md +77 -0
  214. package/skills/write-spec/references/spec-shapes.md +119 -60
  215. package/evals/README.md +0 -32
  216. package/evals/fixtures/copy-conventions.md +0 -31
  217. package/evals/fixtures/output-router/README.md +0 -12
  218. package/evals/fixtures/output-router/failed-command-output.txt +0 -9
  219. package/evals/fixtures/output-router/large-router-manual.md +0 -63
  220. package/evals/fixtures/output-router/native-large-output.txt +0 -40
  221. package/evals/fixtures/output-router/noisy-test-output.txt +0 -30
  222. package/evals/fixtures/output-router/verification-output.txt +0 -8
  223. package/evals/fixtures/tiny-artifact-review-app/README.md +0 -5
  224. package/evals/fixtures/tiny-artifact-review-app/docs/billing-policy.md +0 -10
  225. package/evals/fixtures/tiny-artifact-review-app/docs/specs/billing-webhook-api-draft.md +0 -21
  226. package/evals/fixtures/tiny-artifact-review-app/docs/specs/failed-payment-grace-period.md +0 -24
  227. package/evals/fixtures/tiny-artifact-review-app/docs/specs/immediate-downgrade.md +0 -17
  228. package/evals/fixtures/tiny-artifact-review-app/tests/billing.test.md +0 -8
  229. package/evals/fixtures/tiny-commit-ambiguous-app/.freeflow-eval-setup.sh +0 -15
  230. package/evals/fixtures/tiny-commit-ambiguous-app/README.md +0 -3
  231. package/evals/fixtures/tiny-commit-ambiguous-app/src/billing.ts +0 -5
  232. package/evals/fixtures/tiny-commit-ambiguous-app/src/settings-page.ts +0 -1
  233. package/evals/fixtures/tiny-commit-ambiguous-app/tests/settings.test.md +0 -5
  234. package/evals/fixtures/tiny-commit-app/.freeflow-eval-setup.sh +0 -14
  235. package/evals/fixtures/tiny-commit-app/README.md +0 -3
  236. package/evals/fixtures/tiny-commit-app/docs/specs/settings-title.md +0 -5
  237. package/evals/fixtures/tiny-commit-app/src/billing.ts +0 -5
  238. package/evals/fixtures/tiny-commit-app/src/settings-page.ts +0 -1
  239. package/evals/fixtures/tiny-commit-app/tests/settings.test.md +0 -5
  240. package/evals/fixtures/tiny-commit-everything-app/.freeflow-eval-setup.sh +0 -15
  241. package/evals/fixtures/tiny-commit-everything-app/README.md +0 -3
  242. package/evals/fixtures/tiny-commit-everything-app/docs/specs/settings-title.md +0 -5
  243. package/evals/fixtures/tiny-commit-everything-app/src/billing.ts +0 -5
  244. package/evals/fixtures/tiny-commit-everything-app/src/settings-page.ts +0 -1
  245. package/evals/fixtures/tiny-commit-everything-app/tests/settings.test.md +0 -5
  246. package/evals/fixtures/tiny-commit-staged-app/.freeflow-eval-setup.sh +0 -15
  247. package/evals/fixtures/tiny-commit-staged-app/README.md +0 -3
  248. package/evals/fixtures/tiny-commit-staged-app/docs/specs/settings-title.md +0 -5
  249. package/evals/fixtures/tiny-commit-staged-app/src/billing.ts +0 -5
  250. package/evals/fixtures/tiny-commit-staged-app/src/settings-page.ts +0 -1
  251. package/evals/fixtures/tiny-commit-staged-app/tests/settings.test.md +0 -5
  252. package/evals/fixtures/tiny-commit-staged-mixed-app/.freeflow-eval-setup.sh +0 -15
  253. package/evals/fixtures/tiny-commit-staged-mixed-app/README.md +0 -3
  254. package/evals/fixtures/tiny-commit-staged-mixed-app/docs/billing-policy.md +0 -3
  255. package/evals/fixtures/tiny-commit-staged-mixed-app/docs/release-notes.md +0 -3
  256. package/evals/fixtures/tiny-commit-staged-mixed-app/src/billing.ts +0 -5
  257. package/evals/fixtures/tiny-dashboard-stale-app/README.md +0 -11
  258. package/evals/fixtures/tiny-dashboard-stale-app/src/api-client.ts +0 -13
  259. package/evals/fixtures/tiny-dashboard-stale-app/src/dashboard-data.ts +0 -19
  260. package/evals/fixtures/tiny-dashboard-stale-app/tests/dashboard-data.test.md +0 -9
  261. package/evals/fixtures/tiny-decision-destination-app/README.md +0 -6
  262. package/evals/fixtures/tiny-decision-destination-app/src/risk.ts +0 -8
  263. package/evals/fixtures/tiny-decision-destination-app/tests/risk.test.md +0 -4
  264. package/evals/fixtures/tiny-design-depth-app/README.md +0 -15
  265. package/evals/fixtures/tiny-design-depth-app/src/routes.ts +0 -25
  266. package/evals/fixtures/tiny-design-depth-app/src/sendEmail.ts +0 -3
  267. package/evals/fixtures/tiny-design-depth-app/src/sendSms.ts +0 -3
  268. package/evals/fixtures/tiny-design-depth-app/src/telemetry.ts +0 -3
  269. package/evals/fixtures/tiny-design-depth-app/tests/routes.test.md +0 -12
  270. package/evals/fixtures/tiny-eval-method-app/README.md +0 -8
  271. package/evals/fixtures/tiny-eval-method-app/evals/reports/review-pr-eval-result.md +0 -5
  272. package/evals/fixtures/tiny-eval-method-app/evals/reports/triage-question-failure.md +0 -17
  273. package/evals/fixtures/tiny-eval-method-app/evals/runs/review-pr-with-skill-output.diff +0 -11
  274. package/evals/fixtures/tiny-eval-method-app/evals/runs/review-pr-with-skill-output.md +0 -3
  275. package/evals/fixtures/tiny-eval-method-app/skills/review-pr/SKILL.md +0 -10
  276. package/evals/fixtures/tiny-eval-method-app/skills/triage-question/SKILL.md +0 -10
  277. package/evals/fixtures/tiny-execute-plan-app/README.md +0 -5
  278. package/evals/fixtures/tiny-execute-plan-app/checks/settings-and-billing-check.sh +0 -5
  279. package/evals/fixtures/tiny-execute-plan-app/docs/billing-policy.md +0 -10
  280. package/evals/fixtures/tiny-execute-plan-app/docs/plans/immediate-downgrade-plan.md +0 -11
  281. package/evals/fixtures/tiny-execute-plan-app/docs/plans/settings-title-plan.md +0 -12
  282. package/evals/fixtures/tiny-execute-plan-app/docs/plans/settings-title-with-bad-check.md +0 -15
  283. package/evals/fixtures/tiny-execute-plan-app/docs/plans/unverified-billing-plan.md +0 -9
  284. package/evals/fixtures/tiny-execute-plan-app/docs/specs/failed-payment-grace-period.md +0 -9
  285. package/evals/fixtures/tiny-execute-plan-app/docs/specs/settings-title.md +0 -9
  286. package/evals/fixtures/tiny-execute-plan-app/src/billing.ts +0 -7
  287. package/evals/fixtures/tiny-execute-plan-app/src/settings-page.ts +0 -1
  288. package/evals/fixtures/tiny-execute-plan-app/tests/billing.test.md +0 -7
  289. package/evals/fixtures/tiny-execute-plan-app/tests/settings.test.md +0 -5
  290. package/evals/fixtures/tiny-execute-review-loop-app/README.md +0 -5
  291. package/evals/fixtures/tiny-execute-review-loop-app/docs/billing-policy.md +0 -9
  292. package/evals/fixtures/tiny-execute-review-loop-app/docs/plans/settings-title-plan.md +0 -12
  293. package/evals/fixtures/tiny-execute-review-loop-app/docs/review/previous-agent-summary.md +0 -15
  294. package/evals/fixtures/tiny-execute-review-loop-app/docs/review/review-pass-1-non-pass.md +0 -11
  295. package/evals/fixtures/tiny-execute-review-loop-app/docs/specs/settings-title.md +0 -9
  296. package/evals/fixtures/tiny-execute-review-loop-app/src/billing.ts +0 -7
  297. package/evals/fixtures/tiny-execute-review-loop-app/src/settings-page.ts +0 -1
  298. package/evals/fixtures/tiny-execute-review-loop-app/tests/billing.test.md +0 -7
  299. package/evals/fixtures/tiny-execute-review-loop-app/tests/settings.test.md +0 -5
  300. package/evals/fixtures/tiny-flaky-session-app/README.md +0 -10
  301. package/evals/fixtures/tiny-flaky-session-app/src/session-timeout.ts +0 -13
  302. package/evals/fixtures/tiny-flaky-session-app/tests/session-timeout.test.md +0 -8
  303. package/evals/fixtures/tiny-handoff-app/README.md +0 -5
  304. package/evals/fixtures/tiny-handoff-app/docs/billing-policy.md +0 -10
  305. package/evals/fixtures/tiny-handoff-app/docs/handoffs/old-agent-handoff.md +0 -8
  306. package/evals/fixtures/tiny-handoff-app/docs/plans/billing-retry-plan.md +0 -11
  307. package/evals/fixtures/tiny-handoff-app/src/billing.ts +0 -7
  308. package/evals/fixtures/tiny-handoff-report-app/README.md +0 -3
  309. package/evals/fixtures/tiny-handoff-report-app/evals/runs/handoff-3/hof-003/with-skill-output.diff +0 -1
  310. package/evals/fixtures/tiny-handoff-report-app/evals/runs/handoff-3/hof-003/with-skill-output.md +0 -1
  311. package/evals/fixtures/tiny-handoff-report-app/evals/scores/hof-003-score.txt +0 -4
  312. package/evals/fixtures/tiny-plan-app/README.md +0 -5
  313. package/evals/fixtures/tiny-plan-app/docs/billing-policy.md +0 -10
  314. package/evals/fixtures/tiny-plan-app/docs/specs/failed-payment-grace-period.md +0 -25
  315. package/evals/fixtures/tiny-plan-app/src/billing.ts +0 -17
  316. package/evals/fixtures/tiny-plan-app/src/settings-page.ts +0 -1
  317. package/evals/fixtures/tiny-plan-app/tests/billing.test.md +0 -8
  318. package/evals/fixtures/tiny-plan-app/tests/settings.test.md +0 -5
  319. package/evals/fixtures/tiny-plan-strict-api-app/README.md +0 -3
  320. package/evals/fixtures/tiny-plan-strict-api-app/docs/billing-policy.md +0 -5
  321. package/evals/fixtures/tiny-plan-strict-api-app/docs/specs/billing-webhook-api.md +0 -25
  322. package/evals/fixtures/tiny-plan-strict-api-app/src/billing.ts +0 -5
  323. package/evals/fixtures/tiny-plan-strict-api-app/src/webhooks.ts +0 -3
  324. package/evals/fixtures/tiny-plan-strict-api-app/tests/billing-webhook.test.md +0 -8
  325. package/evals/fixtures/tiny-post-setup-invalid-config-app/.freeflow/config.json +0 -3
  326. package/evals/fixtures/tiny-post-setup-invalid-config-app/AGENTS.md +0 -15
  327. package/evals/fixtures/tiny-post-setup-invalid-config-app/README.md +0 -16
  328. package/evals/fixtures/tiny-post-setup-invalid-config-app/docs/auth-review.md +0 -15
  329. package/evals/fixtures/tiny-post-setup-invalid-config-app/docs/billing-policy.md +0 -10
  330. package/evals/fixtures/tiny-post-setup-invalid-config-app/docs/copy-conventions.md +0 -15
  331. package/evals/fixtures/tiny-post-setup-invalid-config-app/src/auth-middleware.ts +0 -12
  332. package/evals/fixtures/tiny-post-setup-invalid-config-app/src/billing.ts +0 -20
  333. package/evals/fixtures/tiny-post-setup-invalid-config-app/src/login-copy.ts +0 -1
  334. package/evals/fixtures/tiny-post-setup-invalid-config-app/src/settings-page.ts +0 -1
  335. package/evals/fixtures/tiny-post-setup-invalid-config-app/tests/auth-middleware.test.md +0 -8
  336. package/evals/fixtures/tiny-post-setup-invalid-config-app/tests/billing.test.md +0 -8
  337. package/evals/fixtures/tiny-post-setup-invalid-config-app/tests/copy.test.md +0 -7
  338. package/evals/fixtures/tiny-post-setup-missing-config-app/AGENTS.md +0 -15
  339. package/evals/fixtures/tiny-post-setup-missing-config-app/README.md +0 -16
  340. package/evals/fixtures/tiny-post-setup-missing-config-app/docs/auth-review.md +0 -15
  341. package/evals/fixtures/tiny-post-setup-missing-config-app/docs/billing-policy.md +0 -10
  342. package/evals/fixtures/tiny-post-setup-missing-config-app/docs/copy-conventions.md +0 -15
  343. package/evals/fixtures/tiny-post-setup-missing-config-app/src/auth-middleware.ts +0 -12
  344. package/evals/fixtures/tiny-post-setup-missing-config-app/src/billing.ts +0 -20
  345. package/evals/fixtures/tiny-post-setup-missing-config-app/src/login-copy.ts +0 -1
  346. package/evals/fixtures/tiny-post-setup-missing-config-app/src/settings-page.ts +0 -1
  347. package/evals/fixtures/tiny-post-setup-missing-config-app/tests/auth-middleware.test.md +0 -8
  348. package/evals/fixtures/tiny-post-setup-missing-config-app/tests/billing.test.md +0 -8
  349. package/evals/fixtures/tiny-post-setup-missing-config-app/tests/copy.test.md +0 -7
  350. package/evals/fixtures/tiny-post-setup-source-conflict-app/.freeflow/config.json +0 -3
  351. package/evals/fixtures/tiny-post-setup-source-conflict-app/AGENTS.md +0 -15
  352. package/evals/fixtures/tiny-post-setup-source-conflict-app/README.md +0 -16
  353. package/evals/fixtures/tiny-post-setup-source-conflict-app/docs/auth-review.md +0 -15
  354. package/evals/fixtures/tiny-post-setup-source-conflict-app/docs/billing-policy.md +0 -10
  355. package/evals/fixtures/tiny-post-setup-source-conflict-app/docs/copy-conventions.md +0 -15
  356. package/evals/fixtures/tiny-post-setup-source-conflict-app/src/auth-middleware.ts +0 -12
  357. package/evals/fixtures/tiny-post-setup-source-conflict-app/src/billing.ts +0 -20
  358. package/evals/fixtures/tiny-post-setup-source-conflict-app/src/login-copy.ts +0 -1
  359. package/evals/fixtures/tiny-post-setup-source-conflict-app/src/settings-page.ts +0 -1
  360. package/evals/fixtures/tiny-post-setup-source-conflict-app/tests/auth-middleware.test.md +0 -8
  361. package/evals/fixtures/tiny-post-setup-source-conflict-app/tests/billing.test.md +0 -8
  362. package/evals/fixtures/tiny-post-setup-source-conflict-app/tests/copy.test.md +0 -7
  363. package/evals/fixtures/tiny-report-performance-app/README.md +0 -11
  364. package/evals/fixtures/tiny-report-performance-app/docs/performance.md +0 -10
  365. package/evals/fixtures/tiny-report-performance-app/src/report-summary.ts +0 -29
  366. package/evals/fixtures/tiny-report-performance-app/tests/report-performance.test.md +0 -8
  367. package/evals/fixtures/tiny-review-request-app/README.md +0 -3
  368. package/evals/fixtures/tiny-review-request-app/docs/billing-policy.md +0 -5
  369. package/evals/fixtures/tiny-review-request-app/docs/plans/billing-webhook-api.md +0 -19
  370. package/evals/fixtures/tiny-review-request-app/docs/review/previous-agent-summary.md +0 -20
  371. package/evals/fixtures/tiny-review-request-app/docs/specs/billing-webhook-api.md +0 -25
  372. package/evals/fixtures/tiny-review-request-app/src/billing.ts +0 -11
  373. package/evals/fixtures/tiny-review-request-app/src/webhooks.ts +0 -19
  374. package/evals/fixtures/tiny-review-request-app/tests/billing-webhook.test.md +0 -8
  375. package/evals/fixtures/tiny-review-work-app/README.md +0 -5
  376. package/evals/fixtures/tiny-review-work-app/docs/billing-policy.md +0 -9
  377. package/evals/fixtures/tiny-review-work-app/docs/plans/settings-title-plan.md +0 -12
  378. package/evals/fixtures/tiny-review-work-app/docs/review/previous-agent-summary.md +0 -15
  379. package/evals/fixtures/tiny-review-work-app/docs/specs/settings-title.md +0 -9
  380. package/evals/fixtures/tiny-review-work-app/src/billing.ts +0 -7
  381. package/evals/fixtures/tiny-review-work-app/src/settings-page.ts +0 -1
  382. package/evals/fixtures/tiny-review-work-app/tests/billing.test.md +0 -7
  383. package/evals/fixtures/tiny-review-work-app/tests/settings.test.md +0 -5
  384. package/evals/fixtures/tiny-saas-app/README.md +0 -16
  385. package/evals/fixtures/tiny-saas-app/docs/auth-review.md +0 -15
  386. package/evals/fixtures/tiny-saas-app/docs/billing-policy.md +0 -10
  387. package/evals/fixtures/tiny-saas-app/docs/copy-conventions.md +0 -15
  388. package/evals/fixtures/tiny-saas-app/docs/specs/settings-title.md +0 -13
  389. package/evals/fixtures/tiny-saas-app/src/auth-middleware.ts +0 -12
  390. package/evals/fixtures/tiny-saas-app/src/billing.ts +0 -20
  391. package/evals/fixtures/tiny-saas-app/src/login-copy.ts +0 -1
  392. package/evals/fixtures/tiny-saas-app/src/settings-page.ts +0 -1
  393. package/evals/fixtures/tiny-saas-app/tests/auth-middleware.test.md +0 -8
  394. package/evals/fixtures/tiny-saas-app/tests/billing.test.md +0 -8
  395. package/evals/fixtures/tiny-saas-app/tests/copy.test.md +0 -7
  396. package/evals/fixtures/tiny-setup-agents-app/AGENTS.md +0 -6
  397. package/evals/fixtures/tiny-setup-both-app/AGENTS.md +0 -5
  398. package/evals/fixtures/tiny-setup-both-app/CLAUDE.md +0 -5
  399. package/evals/fixtures/tiny-setup-claude-app/CLAUDE.md +0 -6
  400. package/evals/fixtures/tiny-setup-codex-rules-app/.codex/rules/shell.rules +0 -5
  401. package/evals/fixtures/tiny-setup-codex-rules-app/AGENTS.md +0 -6
  402. package/evals/fixtures/tiny-setup-conflict-app/AGENTS.md +0 -6
  403. package/evals/fixtures/tiny-setup-existing-block-app/AGENTS.md +0 -14
  404. package/evals/fixtures/tiny-setup-four-rules-app/CLAUDE.md +0 -6
  405. package/evals/fixtures/tiny-skill-dev-app/README.md +0 -10
  406. package/evals/fixtures/tiny-skill-dev-app/evals/prompts/review-pr-001.txt +0 -8
  407. package/evals/fixtures/tiny-skill-dev-app/evals/reports/review-pr-failure.md +0 -17
  408. package/evals/fixtures/tiny-skill-dev-app/skills/review-pr/SKILL.md +0 -13
  409. package/evals/fixtures/tiny-team-app/README.md +0 -5
  410. package/evals/fixtures/tiny-team-app/docs/product.md +0 -6
  411. package/evals/fixtures/tiny-team-app/src/teams.ts +0 -9
  412. package/evals/fixtures/tiny-team-app/src/users.ts +0 -5
  413. package/evals/fixtures/tiny-team-app/tests/teams.test.md +0 -7
  414. package/evals/fixtures/tiny-verify-app/README.md +0 -5
  415. package/evals/fixtures/tiny-verify-app/src/settings-nav.ts +0 -1
  416. package/evals/fixtures/tiny-verify-app/src/settings-page.ts +0 -1
  417. package/evals/fixtures/tiny-verify-app/tests/settings.test.md +0 -7
  418. package/evals/prompts/aon-001.txt +0 -2
  419. package/evals/prompts/byp-001.txt +0 -2
  420. package/evals/prompts/byp-002.txt +0 -2
  421. package/evals/prompts/cap-001.txt +0 -3
  422. package/evals/prompts/cap-002.txt +0 -3
  423. package/evals/prompts/cmd-001.txt +0 -3
  424. package/evals/prompts/cmd-002.txt +0 -3
  425. package/evals/prompts/cmd-003.txt +0 -6
  426. package/evals/prompts/cmd-004.txt +0 -5
  427. package/evals/prompts/cmd-005.txt +0 -5
  428. package/evals/prompts/cmd-006.txt +0 -5
  429. package/evals/prompts/cmd-007.txt +0 -5
  430. package/evals/prompts/cmd-008.txt +0 -5
  431. package/evals/prompts/cmd-009.txt +0 -8
  432. package/evals/prompts/cmd-010.txt +0 -5
  433. package/evals/prompts/cmd-011.txt +0 -5
  434. package/evals/prompts/cmd-012.txt +0 -5
  435. package/evals/prompts/cmd-013.txt +0 -5
  436. package/evals/prompts/cmd-014.txt +0 -7
  437. package/evals/prompts/cmd-015.txt +0 -7
  438. package/evals/prompts/cmd-016.txt +0 -5
  439. package/evals/prompts/cmt-001.txt +0 -1
  440. package/evals/prompts/cmt-002.txt +0 -1
  441. package/evals/prompts/cmt-003.txt +0 -1
  442. package/evals/prompts/cmt-004.txt +0 -1
  443. package/evals/prompts/cmt-005.txt +0 -1
  444. package/evals/prompts/dfd-001.txt +0 -3
  445. package/evals/prompts/dia-001.txt +0 -3
  446. package/evals/prompts/dia-002.txt +0 -3
  447. package/evals/prompts/dia-003.txt +0 -3
  448. package/evals/prompts/dis-001.txt +0 -5
  449. package/evals/prompts/dis-002.txt +0 -5
  450. package/evals/prompts/esk-001.txt +0 -5
  451. package/evals/prompts/esk-002.txt +0 -5
  452. package/evals/prompts/esk-003.txt +0 -5
  453. package/evals/prompts/grc-001.txt +0 -3
  454. package/evals/prompts/hof-001.txt +0 -3
  455. package/evals/prompts/hof-002.txt +0 -1
  456. package/evals/prompts/hof-003.txt +0 -1
  457. package/evals/prompts/hof-004.txt +0 -1
  458. package/evals/prompts/hof-005.txt +0 -1
  459. package/evals/prompts/ivg-001.txt +0 -1
  460. package/evals/prompts/ivg-002.txt +0 -11
  461. package/evals/prompts/ivg-003.txt +0 -1
  462. package/evals/prompts/ivg-004.txt +0 -1
  463. package/evals/prompts/mode-001.txt +0 -3
  464. package/evals/prompts/mode-002.txt +0 -1
  465. package/evals/prompts/mode-003.txt +0 -3
  466. package/evals/prompts/mode-004.txt +0 -3
  467. package/evals/prompts/mode-005.txt +0 -3
  468. package/evals/prompts/mode-006.txt +0 -3
  469. package/evals/prompts/mode-007.txt +0 -3
  470. package/evals/prompts/otr-001.txt +0 -3
  471. package/evals/prompts/otr-002.txt +0 -3
  472. package/evals/prompts/rar-001.txt +0 -5
  473. package/evals/prompts/rar-002.txt +0 -3
  474. package/evals/prompts/rar-003.txt +0 -6
  475. package/evals/prompts/rar-004.txt +0 -15
  476. package/evals/prompts/rar-005.txt +0 -10
  477. package/evals/prompts/rbr-001.txt +0 -3
  478. package/evals/prompts/rbr-002.txt +0 -3
  479. package/evals/prompts/rbr-003.txt +0 -7
  480. package/evals/prompts/rev-001.txt +0 -3
  481. package/evals/prompts/rev-002.txt +0 -5
  482. package/evals/prompts/rev-003.txt +0 -6
  483. package/evals/prompts/rev-004.txt +0 -1
  484. package/evals/prompts/rev-005.txt +0 -15
  485. package/evals/prompts/rev-006.txt +0 -5
  486. package/evals/prompts/stp-001.txt +0 -1
  487. package/evals/prompts/stp-002.txt +0 -1
  488. package/evals/prompts/stp-003.txt +0 -1
  489. package/evals/prompts/stp-004.txt +0 -1
  490. package/evals/prompts/stp-005.txt +0 -1
  491. package/evals/prompts/stp-006.txt +0 -1
  492. package/evals/prompts/stp-007.txt +0 -1
  493. package/evals/prompts/stp-008.txt +0 -1
  494. package/evals/prompts/stp-009.txt +0 -1
  495. package/evals/prompts/stp-010.txt +0 -1
  496. package/evals/prompts/stp-011.txt +0 -1
  497. package/evals/prompts/vfy-003.txt +0 -1
  498. package/evals/prompts/wpl-001.txt +0 -3
  499. package/evals/prompts/wpl-002.txt +0 -5
  500. package/evals/prompts/wpl-003.txt +0 -3
  501. package/evals/prompts/wpl-004.txt +0 -5
  502. package/evals/prompts/wpl-005.txt +0 -1
  503. package/evals/prompts/wrc-001.txt +0 -10
  504. package/evals/prompts/wrc-002.txt +0 -3
  505. package/evals/prompts/wsk-001.txt +0 -6
  506. package/evals/prompts/wsp-001.txt +0 -5
  507. package/evals/prompts/wsp-002.txt +0 -15
  508. package/evals/prompts/wsp-003.txt +0 -3
  509. package/evals/prompts/wsp-004.txt +0 -15
  510. package/evals/prompts/wsp-005.txt +0 -5
  511. package/evals/prompts/wsp-006.txt +0 -12
  512. package/evals/prompts/wsp-007.txt +0 -9
  513. package/evals/prompts/wsp-008.txt +0 -13
  514. package/evals/prompts/xpl-001.txt +0 -1
  515. package/evals/prompts/xpl-002.txt +0 -3
  516. package/evals/prompts/xpl-003.txt +0 -3
  517. package/evals/prompts/xpl-004.txt +0 -3
  518. package/evals/prompts/xpl-005.txt +0 -5
  519. package/evals/prompts/xpl-006.txt +0 -3
  520. package/evals/registries/adversarial-evals.json +0 -39
  521. package/evals/registries/fixture-evals.json +0 -2513
  522. package/evals/registries/handoff-evals.json +0 -74
  523. package/evals/registries/legacy-evals.json +0 -147
  524. package/evals/registries/skill-evidence.json +0 -193
  525. package/evals/registries/verify-work-evals.json +0 -46
  526. package/evals/reports/acceptance/v0.1-acceptance-report.md +0 -130
  527. package/evals/reports/by-command-surface/command-surface-1-report.md +0 -45
  528. package/evals/reports/by-command-surface/command-surface-10-report.md +0 -76
  529. package/evals/reports/by-command-surface/command-surface-11-report.md +0 -77
  530. package/evals/reports/by-command-surface/command-surface-12-report.md +0 -91
  531. package/evals/reports/by-command-surface/command-surface-13-report.md +0 -64
  532. package/evals/reports/by-command-surface/command-surface-14-report.md +0 -66
  533. package/evals/reports/by-command-surface/command-surface-15-report.md +0 -124
  534. package/evals/reports/by-command-surface/command-surface-16-report.md +0 -39
  535. package/evals/reports/by-command-surface/command-surface-2-report.md +0 -80
  536. package/evals/reports/by-command-surface/command-surface-3-report.md +0 -86
  537. package/evals/reports/by-command-surface/command-surface-4-report.md +0 -76
  538. package/evals/reports/by-command-surface/command-surface-5-report.md +0 -80
  539. package/evals/reports/by-command-surface/command-surface-6-report.md +0 -74
  540. package/evals/reports/by-command-surface/command-surface-7-report.md +0 -84
  541. package/evals/reports/by-command-surface/command-surface-8-report.md +0 -79
  542. package/evals/reports/by-command-surface/command-surface-9-report.md +0 -79
  543. package/evals/reports/by-command-surface/command-surface-matrix.md +0 -91
  544. package/evals/reports/by-skill/bypass-1-report.md +0 -124
  545. package/evals/reports/by-skill/capture-decisions-1-report.md +0 -99
  546. package/evals/reports/by-skill/capture-decisions-2-report.md +0 -102
  547. package/evals/reports/by-skill/commit-work-1-report.md +0 -165
  548. package/evals/reports/by-skill/commit-work-2-report.md +0 -109
  549. package/evals/reports/by-skill/design-for-depth-1-report.md +0 -95
  550. package/evals/reports/by-skill/diagnose-failure-1-report.md +0 -91
  551. package/evals/reports/by-skill/diagnose-failure-2-report.md +0 -94
  552. package/evals/reports/by-skill/discover-1-report.md +0 -118
  553. package/evals/reports/by-skill/evaluate-skill-2-report.md +0 -71
  554. package/evals/reports/by-skill/execute-plan-1-report.md +0 -103
  555. package/evals/reports/by-skill/execute-plan-2-report.md +0 -112
  556. package/evals/reports/by-skill/execute-plan-3-report.md +0 -87
  557. package/evals/reports/by-skill/grill-context-1-report.md +0 -103
  558. package/evals/reports/by-skill/handoff-1-report.md +0 -123
  559. package/evals/reports/by-skill/handoff-2-report.md +0 -105
  560. package/evals/reports/by-skill/handoff-3-report.md +0 -100
  561. package/evals/reports/by-skill/handoff-7-report.md +0 -77
  562. package/evals/reports/by-skill/interview-gate-1-report.md +0 -63
  563. package/evals/reports/by-skill/interview-gate-2-report.md +0 -105
  564. package/evals/reports/by-skill/mode-contract-1-report.md +0 -74
  565. package/evals/reports/by-skill/mode-contract-2-report.md +0 -87
  566. package/evals/reports/by-skill/mode-contract-3-report.md +0 -65
  567. package/evals/reports/by-skill/mode-contract-4-report.md +0 -71
  568. package/evals/reports/by-skill/mode-contract-5-report.md +0 -70
  569. package/evals/reports/by-skill/output-router-1-report.md +0 -76
  570. package/evals/reports/by-skill/output-router-2-report.md +0 -65
  571. package/evals/reports/by-skill/research-1-report.md +0 -144
  572. package/evals/reports/by-skill/research-brief-1-report.md +0 -94
  573. package/evals/reports/by-skill/research-brief-2-report.md +0 -75
  574. package/evals/reports/by-skill/review-artifact-1-report.md +0 -91
  575. package/evals/reports/by-skill/review-artifact-2-report.md +0 -79
  576. package/evals/reports/by-skill/review-artifact-3-report.md +0 -120
  577. package/evals/reports/by-skill/review-artifact-4-report.md +0 -89
  578. package/evals/reports/by-skill/review-artifact-5-report.md +0 -66
  579. package/evals/reports/by-skill/review-work-1-report.md +0 -85
  580. package/evals/reports/by-skill/review-work-2-report.md +0 -87
  581. package/evals/reports/by-skill/review-work-3-report.md +0 -104
  582. package/evals/reports/by-skill/review-work-4-report.md +0 -108
  583. package/evals/reports/by-skill/review-work-5-report.md +0 -73
  584. package/evals/reports/by-skill/review-work-6-report.md +0 -66
  585. package/evals/reports/by-skill/setup-freeflow-1-report.md +0 -199
  586. package/evals/reports/by-skill/setup-freeflow-2-report.md +0 -105
  587. package/evals/reports/by-skill/setup-freeflow-3-report.md +0 -105
  588. package/evals/reports/by-skill/setup-freeflow-4-report.md +0 -88
  589. package/evals/reports/by-skill/setup-freeflow-5-report.md +0 -115
  590. package/evals/reports/by-skill/skill-authoring-1-report.md +0 -81
  591. package/evals/reports/by-skill/verify-work-1-report.md +0 -111
  592. package/evals/reports/by-skill/verify-work-2-report.md +0 -120
  593. package/evals/reports/by-skill/workflow-1-report.md +0 -65
  594. package/evals/reports/by-skill/write-plan-1-report.md +0 -123
  595. package/evals/reports/by-skill/write-plan-2-report.md +0 -98
  596. package/evals/reports/by-skill/write-plan-3-report.md +0 -110
  597. package/evals/reports/by-skill/write-spec-1-report.md +0 -88
  598. package/evals/reports/by-skill/write-spec-2-report.md +0 -89
  599. package/evals/reports/by-skill/write-spec-3-report.md +0 -90
  600. package/evals/reports/by-skill/write-spec-4-report.md +0 -167
  601. package/evals/reports/harness/claude-harness-1-report.md +0 -103
  602. package/evals/reports/harness/eval-harness-1-report.md +0 -91
  603. package/evals/reports/iterations/iteration-1-report.md +0 -71
  604. package/evals/reports/iterations/iteration-2-report.md +0 -78
  605. package/evals/reports/iterations/iteration-3-report.md +0 -118
  606. package/evals/reports/iterations/iteration-4-report.md +0 -131
  607. package/evals/reports/iterations/iteration-5-report.md +0 -149
  608. package/evals/reports/iterations/iteration-6-report.md +0 -136
  609. package/evals/reports/runtime/always-on-runtime-1-report.md +0 -107
  610. package/evals/reports/runtime/context-mode-normalized-benchmark-1-report.md +0 -52
  611. package/evals/reports/runtime/context-mode-real-deep-baseline-1-report.md +0 -410
  612. package/evals/reports/runtime/context-mode-real-deep-final-slice-11-report.md +0 -203
  613. package/evals/reports/runtime/context-mode-real-deep-final-slice-11-review.md +0 -51
  614. package/evals/reports/runtime/context-saving-native-tools-baseline-1-report.md +0 -99
  615. package/evals/reports/runtime/eryx-python-compatibility-reprobe-1-report.md +0 -166
  616. package/evals/reports/runtime/eryx-python-proof-spike-1-report.md +0 -96
  617. package/evals/reports/runtime/eryx-python-proof-spike-2-report.md +0 -151
  618. package/evals/reports/runtime/eryx-python-script-derive-execution-1-report.md +0 -233
  619. package/evals/reports/runtime/jq-script-derive-execution-1-report.md +0 -215
  620. package/evals/reports/runtime/jq-wasm-proof-spike-1-report.md +0 -67
  621. package/evals/reports/runtime/output-router-benchmark-1-report.md +0 -81
  622. package/evals/reports/runtime/output-router-capture-derive-provider-eval-1-report.md +0 -35
  623. package/evals/reports/runtime/output-router-codex-large-index-sqlite-adhoc-report.md +0 -150
  624. package/evals/reports/runtime/output-router-codex-qa-benchmark-1-report.md +0 -46
  625. package/evals/reports/runtime/output-router-command-benchmark-1-report.md +0 -62
  626. package/evals/reports/runtime/output-router-index-benchmark-1-report.md +0 -64
  627. package/evals/reports/runtime/output-router-regression-1-report.md +0 -81
  628. package/evals/reports/runtime/output-router-transform-eval-1-report.md +0 -28
  629. package/evals/reports/runtime/pi-observed-routing-eval-1-report.md +0 -44
  630. package/evals/reports/runtime/quickjs-script-derive-execution-1-report.md +0 -159
  631. package/evals/reports/runtime/quickjs-wasi-proof-spike-1-report.md +0 -55
  632. package/evals/reports/runtime/render-format-row-protocol-1-report.md +0 -58
  633. package/evals/reports/runtime/script-sandbox-adapter-selection-review-1-report.md +0 -84
  634. package/evals/reports/runtime/script-sandbox-probe-resource-hardening-1-report.md +0 -51
  635. package/evals/reports/runtime/storage-policy-benchmark-1-report.md +0 -176
  636. package/evals/reports/runtime/vault-index-storage-spike-1-report.md +0 -39
  637. package/evals/reports/runtime/workflow-context-hook-1-report.md +0 -82
  638. package/evals/runbooks/adversarial-eval-runbook.md +0 -28
  639. package/evals/runbooks/fixture-eval-runbook.md +0 -137
  640. package/evals/runbooks/handoff-eval-runbook.md +0 -22
  641. package/evals/runbooks/verify-work-eval-runbook.md +0 -27
  642. package/evals/scripts/audit-command-surface.sh +0 -160
  643. package/evals/scripts/check-activation-contract.sh +0 -121
  644. package/evals/scripts/check-runtime-context-hook.sh +0 -183
  645. package/evals/scripts/grade-fixture-eval.sh +0 -446
  646. package/evals/scripts/run-claude-fixture-eval.sh +0 -26
  647. package/evals/scripts/run-codex-fixture-eval.sh +0 -26
  648. package/evals/scripts/run-eryx-python-proof-spike.js +0 -559
  649. package/evals/scripts/run-fixture-eval-by-id.sh +0 -23
  650. package/evals/scripts/run-fixture-eval.sh +0 -464
  651. package/evals/scripts/run-jq-wasm-proof-spike.js +0 -299
  652. package/evals/scripts/run-output-router-transform-eval.js +0 -115
  653. package/evals/scripts/run-pi-observed-routing-eval.js +0 -478
  654. package/evals/scripts/run-quickjs-wasi-proof-spike.js +0 -339
  655. package/evals/scripts/skill-evidence.sh +0 -316
  656. package/evals/scripts/validate-release-metadata.sh +0 -486
  657. package/evals/suites/v0.1-acceptance-suite.md +0 -131
  658. package/pi-extension/dist/host-producer-identification.js +0 -186
  659. package/pi-extension/dist/mcp-config.js +0 -30
  660. package/pi-extension/dist/native-safety-net.js +0 -209
  661. package/pi-extension/dist/observed-tool-routing.js +0 -114
  662. package/pi-extension/dist/renderers.js +0 -835
  663. package/pi-extension/dist/router-tools.js +0 -438
  664. package/pi-extension/dist/runtime-context.js +0 -288
  665. package/pi-extension/dist/schemas.js +0 -254
  666. package/pi-extension/dist/status.js +0 -449
  667. package/pi-extension/dist/utils.js +0 -467
  668. package/pi-extension/src/runtime-context.ts +0 -347
  669. package/pi-extension/src/status.ts +0 -505
  670. package/plugin-docs/README.md +0 -40
  671. package/plugin-docs/adr/0001-three-modes.md +0 -27
  672. package/plugin-docs/adr/0002-evals-before-hooks.md +0 -17
  673. package/plugin-docs/adr/0003-release-boundary.md +0 -21
  674. package/plugin-docs/adr/0004-discover-replaces-shallow-discovery-skills.md +0 -26
  675. package/plugin-docs/adr/README.md +0 -10
  676. package/plugin-docs/architecture.md +0 -89
  677. package/plugin-docs/output-router.md +0 -396
  678. package/plugin-docs/release-evidence.md +0 -77
  679. package/plugin-docs/skills.md +0 -44
  680. package/plugin-docs/workflow.md +0 -57
  681. package/skills/diagnose-failure/references/feedback-loop-catalog.md +0 -25
  682. package/skills/discover/ARTIFACT-DESTINATIONS.md +0 -58
  683. package/skills/discover/CHECKPOINTS.md +0 -74
  684. package/skills/discover/SKILL.md +0 -173
  685. package/skills/evaluate-skill/references/eval-patterns.md +0 -41
  686. package/skills/evaluate-skill/references/grading-priority.md +0 -33
  687. package/skills/execute-plan/SKILL.md +0 -133
  688. package/skills/execute-plan/references/execution-map.md +0 -122
  689. package/skills/interview-gate/SKILL.md +0 -90
  690. package/skills/setup-freeflow/references/output-router-setup.md +0 -102
  691. package/skills/workflow/references/workflow-map.md +0 -104
  692. /package/pi-extension/src/{mcp-config.ts → integrations/mcp-config.ts} +0 -0
@@ -6,7 +6,12 @@ import { tmpdir } from "node:os";
6
6
  import * as path from "node:path";
7
7
  import { fileURLToPath, pathToFileURL } from "node:url";
8
8
  import { performance } from "node:perf_hooks";
9
- import { defaultJsonRunReportPath, escapeMarkdownTableCell as escapeMarkdown, parseBenchmarkCliArgs, writeBenchmarkReportPair, } from "./benchmark-harness.js";
9
+ import {
10
+ defaultJsonRunReportPath,
11
+ escapeMarkdownTableCell as escapeMarkdown,
12
+ parseBenchmarkCliArgs,
13
+ writeBenchmarkReportPair,
14
+ } from "./benchmark-harness.js";
10
15
  import { freeflowBatch } from "../tools/batch.js";
11
16
  import { freeflowSearch } from "../tools/search.js";
12
17
  import { processSource } from "../processing/engine.js";
@@ -16,808 +21,872 @@ const DEFAULT_CONTEXT_MODE_REPO = "/tmp/pi-github-repos/mksglu/context-mode";
16
21
  const DEFAULT_CONTEXT_MODE_VERSION = "unknown";
17
22
  const DEFAULT_ITERATION_LABEL = "Iteration 1";
18
23
  export const CONTEXT_MODE_REAL_DEEP_IMPLEMENTATION = "context-mode-real-deep-benchmark-v1";
19
- export const EXPECTED_BASELINE_FAILURE_KEYS = [
20
- "outside-file-boundary/freeflow:run-cat-host-shell",
21
- ];
24
+ export const EXPECTED_BASELINE_FAILURE_KEYS = ["outside-file-boundary/freeflow:run-cat-host-shell"];
22
25
  export async function runContextModeRealDeepBenchmark(options = {}) {
23
- const availability = resolveContextModeAvailability(options);
24
- const generatedAt = options.generatedAt ?? new Date().toISOString();
25
- const freeflowRepo = path.resolve(options.freeflowRepo ?? process.cwd());
26
- if (availability.status === "unavailable") {
27
- return unavailableReport({ availability, generatedAt, freeflowRepo });
28
- }
29
- const runtime = await loadContextModeRuntime(availability);
30
- const root = await createTempDir("freeflow-context-mode-deep-");
31
- const projectRoot = path.join(root.path, "project");
32
- const storeRoot = path.join(root.path, "context-mode-store");
33
- const freeflowVaultRoot = path.join(root.path, "freeflow-vault");
34
- const sessionId = `deep-context-mode-${createHash("sha1").update(root.path).digest("hex").slice(0, 10)}`;
35
- const rows = [];
36
- let cm;
37
- try {
38
- await setupProject({ projectRoot, fixtureSource: availability.fixtureSource, outsideRoot: root.path });
39
- await mkdir(storeRoot, { recursive: true });
40
- await mkdir(freeflowVaultRoot, { recursive: true });
41
- const runner = {
42
- async run(request) {
43
- const shellOptions = {};
44
- if (request.cwd !== undefined) {
45
- shellOptions.cwd = request.cwd;
46
- }
47
- if (request.timeoutMs !== undefined) {
48
- shellOptions.timeoutMs = request.timeoutMs;
49
- }
50
- return runShell(request.command, shellOptions);
51
- },
52
- };
53
- const transport = new runtime.StdioClientTransport({
54
- command: "bash",
55
- args: [
56
- "-lc",
57
- `cd ${shellQuote(projectRoot)} && CONTEXT_MODE_DIR=${shellQuote(storeRoot)} node ${shellQuote(availability.server)}`,
58
- ],
59
- env: process.env,
60
- });
61
- cm = new runtime.Client({ name: "freeflow-deep-context-mode-benchmark", version: "0.0.1" });
62
- await cm.connect(transport);
63
- async function callCM(name, args) {
64
- if (!cm) {
65
- throw new Error("Context Mode client is not connected.");
66
- }
67
- const start = performance.now();
68
- const result = await cm.callTool({ name, arguments: args });
69
- return { result, text: contentText(result), latencyMs: Math.round(performance.now() - start) };
70
- }
71
- async function ffRun(command, goal, extra = {}) {
72
- const start = performance.now();
73
- const runOptions = {
74
- command,
75
- cwd: projectRoot,
76
- timeoutMs: 30_000,
77
- sessionId,
78
- vaultRoot: freeflowVaultRoot,
79
- preserve: "important",
80
- goal,
81
- ...extra,
82
- };
83
- const result = await freeflowRun(runOptions, runner);
84
- return { result, text: freeflowText(result), latencyMs: Math.round(performance.now() - start) };
85
- }
86
- async function ffRetrieve(query, sourceRoot = projectRoot, extra = {}) {
87
- const start = performance.now();
88
- const retrieveOptions = {
89
- action: "query",
90
- source: { kind: "repo", root: sourceRoot },
91
- query,
92
- topK: 3,
93
- preserve: "important",
94
- generatedPathGlobs: ["graphify-out/**"],
95
- ...extra,
96
- };
97
- const result = await freeflowSearch(retrieveOptions);
98
- return { result, text: freeflowText(result), latencyMs: Math.round(performance.now() - start) };
99
- }
100
- async function ffTransform(sourceOutputId, stream, operation) {
101
- const start = performance.now();
102
- const result = await freeflowTransform({
103
- source: { kind: "vault", outputId: sourceOutputId, stream },
104
- operation,
105
- preserve: "important",
106
- sessionId,
107
- vaultRoot: freeflowVaultRoot,
108
- });
109
- return { result, text: freeflowText(result), latencyMs: Math.round(performance.now() - start) };
110
- }
111
- async function ffProcessRepoFile(relativePath) {
112
- const start = performance.now();
113
- const result = await processSource({ kind: "repo-file", root: projectRoot, path: relativePath }, { sessionId, vaultRoot: freeflowVaultRoot });
114
- return { result, text: processingText(result), latencyMs: Math.round(performance.now() - start) };
115
- }
116
- function record(input) {
117
- const text = input.obs.text;
118
- const factsFound = factCount(text, input.facts);
119
- const correct = input.expectedCorrect ?? factsFound === input.facts.length;
120
- rows.push({
121
- fixture: input.fixture,
122
- category: input.category,
123
- mode: input.mode,
124
- capability: input.capability ?? "",
125
- correct,
126
- factsFound,
127
- factsTotal: input.facts.length,
128
- rawBytes: input.rawBytes,
129
- visibleBytes: bytes(text),
130
- reductionPct: input.rawBytes ? Number(((1 - bytes(text) / input.rawBytes) * 100).toFixed(2)) : 0,
131
- latencyMs: input.obs.latencyMs ?? 0,
132
- recovery: recoveryScore(input.obs.result, input.mode),
133
- notes: input.notes ?? "",
134
- preview: text.split("\n").slice(0, 18).join("\n"),
135
- });
136
- }
137
- function fileSize(rel) {
138
- return statSync(path.join(projectRoot, rel)).size;
139
- }
140
- function scriptCode(rel, code) {
141
- return `import { readFileSync } from 'node:fs';\nconst FILE_CONTENT = readFileSync(${JSON.stringify(rel)}, 'utf8');\n${code}\n`;
26
+ const availability = resolveContextModeAvailability(options);
27
+ const generatedAt = options.generatedAt ?? new Date().toISOString();
28
+ const freeflowRepo = path.resolve(options.freeflowRepo ?? process.cwd());
29
+ if (availability.status === "unavailable") {
30
+ return unavailableReport({ availability, generatedAt, freeflowRepo });
31
+ }
32
+ const runtime = await loadContextModeRuntime(availability);
33
+ const root = await createTempDir("freeflow-context-mode-deep-");
34
+ const projectRoot = path.join(root.path, "project");
35
+ const storeRoot = path.join(root.path, "context-mode-store");
36
+ const freeflowVaultRoot = path.join(root.path, "freeflow-vault");
37
+ const sessionId = `deep-context-mode-${createHash("sha1").update(root.path).digest("hex").slice(0, 10)}`;
38
+ const rows = [];
39
+ let cm;
40
+ try {
41
+ await setupProject({ projectRoot, fixtureSource: availability.fixtureSource, outsideRoot: root.path });
42
+ await mkdir(storeRoot, { recursive: true });
43
+ await mkdir(freeflowVaultRoot, { recursive: true });
44
+ const runner = {
45
+ async run(request) {
46
+ const shellOptions = {};
47
+ if (request.cwd !== undefined) {
48
+ shellOptions.cwd = request.cwd;
142
49
  }
143
- async function writeScenarioScript(id, rel, code) {
144
- const scriptPath = path.join(projectRoot, "scripts", `${id}.mjs`);
145
- await writeFile(scriptPath, scriptCode(rel, code), "utf8");
146
- return `node scripts/${id}.mjs`;
147
- }
148
- const executeScenarios = createExecuteScenarios();
149
- for (const scenario of executeScenarios) {
150
- const rawBytes = fileSize(scenario.file);
151
- const cmObs = await callCM("ctx_execute_file", {
152
- path: scenario.file,
153
- language: "javascript",
154
- code: scenario.code,
155
- intent: scenario.goal,
156
- });
157
- record({
158
- fixture: scenario.id,
159
- category: scenario.category,
160
- rawBytes,
161
- facts: scenario.facts,
162
- mode: "context-mode:ctx_execute_file",
163
- capability: "sandboxed file transform",
164
- obs: cmObs,
165
- });
166
- const ffDefault = await ffRun(`cat ${shellQuote(scenario.file)}`, scenario.goal);
167
- record({
168
- fixture: scenario.id,
169
- category: scenario.category,
170
- rawBytes,
171
- facts: scenario.facts,
172
- mode: "freeflow:run-cat-default",
173
- capability: "command capture/routing",
174
- obs: ffDefault,
175
- });
176
- const processingReducerModes = {
177
- "vitest-summary": { mode: "freeflow:process-test-output-reducer", reducer: "test-output" },
178
- "tsc-summary": { mode: "freeflow:process-diagnostics-reducer", reducer: "diagnostics" },
179
- "build-summary": { mode: "freeflow:process-build-output-reducer", reducer: "build-output" },
180
- "access-summary": { mode: "freeflow:process-access-log-reducer", reducer: "access-log" },
181
- "analytics-summary": { mode: "freeflow:process-table-reducer", reducer: "table" },
182
- "mcp-tools-summary": { mode: "freeflow:process-mcp-tools-reducer", reducer: "mcp-tools" },
183
- "playwright-summary": { mode: "freeflow:process-browser-snapshot-reducer", reducer: "browser-snapshot" },
184
- "git-log-summary": { mode: "freeflow:process-git-log-reducer", reducer: "git-log" },
185
- };
186
- const reducerMode = processingReducerModes[scenario.id];
187
- if (reducerMode) {
188
- const ffProcessed = await ffProcessRepoFile(scenario.file);
189
- record({
190
- fixture: scenario.id,
191
- category: scenario.category,
192
- rawBytes,
193
- facts: scenario.facts,
194
- mode: reducerMode.mode,
195
- capability: "processing engine built-in reducer",
196
- obs: ffProcessed,
197
- notes: `Uses the internal processing engine ${reducerMode.reducer} reducer; no public Pi surface is selected.`,
198
- });
199
- }
200
- const command = await writeScenarioScript(scenario.id, scenario.file, scenario.code);
201
- const ffComputed = await ffRun(command, scenario.goal);
202
- record({
203
- fixture: scenario.id,
204
- category: scenario.category,
205
- rawBytes,
206
- facts: scenario.facts,
207
- mode: "freeflow:run-computed-script",
208
- capability: "host-shell transform via freeflow_run",
209
- obs: ffComputed,
210
- notes: "Compact because an external Node script computed facts; not Freeflow sandboxed script transform.",
211
- });
212
- if (scenario.upstreamCode !== undefined) {
213
- const upstream = await callCM("ctx_execute_file", {
214
- path: scenario.file,
215
- language: "javascript",
216
- code: scenario.upstreamCode,
217
- intent: "upstream benchmark script",
218
- });
219
- record({
220
- fixture: `${scenario.id}-upstream-script`,
221
- category: scenario.category,
222
- rawBytes,
223
- facts: scenario.facts,
224
- mode: "context-mode:upstream-benchmark-script",
225
- capability: "benchmark script correctness",
226
- obs: upstream,
227
- expectedCorrect: hasFacts(upstream.text, scenario.facts),
228
- notes: "Runs Context Mode upstream benchmark summarizer; checks against fixture ground truth.",
229
- });
230
- }
231
- }
232
- const accessRaw = await ffRun("cat fixtures/access.log", "diagnosis");
233
- const accessOutputId = recoveryOutputId(accessRaw.result);
234
- if (accessOutputId) {
235
- const statusGroups = await ffTransform(accessOutputId, "stdout", {
236
- kind: "groupByRegex",
237
- pattern: '" (\\d{3}) \\d+ (\\d+)ms$',
238
- group: "1",
239
- maxGroups: 10,
240
- maxLinesPerGroup: 1,
241
- });
242
- record({
243
- fixture: "access-log-transform-status",
244
- category: "transform",
245
- rawBytes: fileSize("fixtures/access.log"),
246
- facts: ["200", "500", "401", "404"],
247
- mode: "freeflow:transform-groupByRegex",
248
- capability: "deterministic transform over vault",
249
- obs: statusGroups,
250
- notes: "Groups statuses but does not compute all aggregate counts/error rate/avg latency.",
251
- });
252
- }
253
- const analyticsRaw = await ffRun("cat fixtures/analytics.csv", "inspect CSV");
254
- const analyticsOutputId = recoveryOutputId(analyticsRaw.result);
255
- if (analyticsOutputId) {
256
- const timeouts = await ffTransform(analyticsOutputId, "stdout", { kind: "countMatches", pattern: ",timeout," });
257
- record({
258
- fixture: "analytics-count-timeouts",
259
- category: "transform",
260
- rawBytes: fileSize("fixtures/analytics.csv"),
261
- facts: ["matches: 50"],
262
- mode: "freeflow:transform-countMatches",
263
- capability: "deterministic transform over vault",
264
- obs: timeouts,
265
- notes: "Can count one selected fact but not summarize CSV distribution without a script transform.",
266
- });
267
- }
268
- const searchScenarios = createSearchScenarios();
269
- for (const scenario of searchScenarios) {
270
- const rawBytes = fileSize(scenario.file);
271
- await callCM("ctx_index", { path: scenario.file, source: scenario.source });
272
- const cmSearch = await callCM("ctx_search", { queries: [scenario.query], source: scenario.source, limit: 3 });
273
- record({
274
- fixture: scenario.id,
275
- category: "docs-search",
276
- rawBytes,
277
- facts: scenario.facts,
278
- mode: "context-mode:ctx_index+ctx_search",
279
- capability: "FTS index/search",
280
- obs: cmSearch,
281
- });
282
- const ffSearch = await ffRetrieve(scenario.query, projectRoot);
283
- record({
284
- fixture: scenario.id,
285
- category: "docs-search",
286
- rawBytes,
287
- facts: scenario.facts,
288
- mode: "freeflow:repo-query",
289
- capability: "live repo text query",
290
- obs: ffSearch,
291
- });
292
- }
293
- await callCM("ctx_index", {
294
- path: "repo-fixture",
295
- source: "repo-fixture",
296
- maxFiles: 20,
297
- extensions: [".ts", ".html"],
298
- respectGitignore: false,
299
- });
300
- const repoRawBytes = fileSize("repo-fixture/src/sandbox.ts") + fileSize("repo-fixture/graphify-out/graph.html");
301
- const repoFacts = ["src/sandbox.ts", "SAFE_CONTEXT_BOUNDARY", "validateBoundary"];
302
- const cmRepo = await callCM("ctx_search", {
303
- queries: ["SAFE_CONTEXT_BOUNDARY validateBoundary implementation"],
304
- source: "repo-fixture",
305
- limit: 5,
306
- });
307
- record({
308
- fixture: "repo-generated-decoy",
309
- category: "repo-search",
310
- rawBytes: repoRawBytes,
311
- facts: repoFacts,
312
- mode: "context-mode:ctx_index+ctx_search",
313
- capability: "FTS index/search",
314
- obs: cmRepo,
315
- expectedCorrect: hasFacts(cmRepo.text, repoFacts) && !cmRepo.text.includes("graph.html"),
316
- notes: cmRepo.text.includes("graph.html") ? "Generated graph.html decoy appears in results." : "Top/live result did not include generated decoy.",
317
- });
318
- const ffRepo = await ffRetrieve("SAFE_CONTEXT_BOUNDARY validateBoundary implementation", path.join(projectRoot, "repo-fixture"));
319
- record({
320
- fixture: "repo-generated-decoy",
321
- category: "repo-search",
322
- rawBytes: repoRawBytes,
323
- facts: repoFacts,
324
- mode: "freeflow:repo-query",
325
- capability: "generated-path-aware live repo query",
326
- obs: ffRepo,
327
- expectedCorrect: hasFacts(ffRepo.text, repoFacts) && !ffRepo.text.includes("graph.html"),
328
- notes: "Configured generatedPathGlobs=graphify-out/**.",
329
- });
330
- await callCM("ctx_index", { path: "docs/stale.md", source: "stale-doc" });
331
- await writeFile(path.join(projectRoot, "docs/stale.md"), "# Cache Policy\n\nNEW_CACHE_POLICY_TOKEN says cache for 30 seconds.\n", "utf8");
332
- const cmStale = await callCM("ctx_search", { queries: ["cache policy token seconds"], source: "stale-doc", limit: 3 });
333
- record({
334
- fixture: "stale-index-after-file-change",
335
- category: "freshness",
336
- rawBytes: fileSize("docs/stale.md"),
337
- facts: ["NEW_CACHE_POLICY_TOKEN"],
338
- mode: "context-mode:ctx_search-after-mutation",
339
- capability: "persistent index freshness",
340
- obs: cmStale,
341
- expectedCorrect: cmStale.text.includes("NEW_CACHE_POLICY_TOKEN"),
342
- notes: cmStale.text.includes("OLD_CACHE_POLICY_TOKEN") ? "Returned stale indexed content after file mutation." : "",
343
- });
344
- const ffFresh = await ffRetrieve("cache policy token seconds NEW_CACHE_POLICY_TOKEN", projectRoot);
345
- record({
346
- fixture: "stale-index-after-file-change",
347
- category: "freshness",
348
- rawBytes: fileSize("docs/stale.md"),
349
- facts: ["NEW_CACHE_POLICY_TOKEN"],
350
- mode: "freeflow:repo-query-live-file",
351
- capability: "live repo scanner",
352
- obs: ffFresh,
353
- });
354
- const batchRawBytes = fileSize("fixtures/test-output.txt") + fileSize("fixtures/access.log") + fileSize("fixtures/context7-react-docs.md");
355
- const batchFacts = ["4 failed", "88", "ignore = true"];
356
- const vitestScenario = requiredScenario(executeScenarios, "vitest-summary");
357
- const accessScenario = requiredScenario(executeScenarios, "access-summary");
358
- const cmBatch = await callCM("ctx_batch_execute", {
359
- commands: [
360
- { label: "tests", command: "cat fixtures/test-output.txt" },
361
- { label: "access-log", command: "cat fixtures/access.log" },
362
- { label: "react-docs", command: "cat fixtures/context7-react-docs.md" },
363
- ],
364
- queries: [
365
- "failed test files and counts",
366
- "HTTP error count status distribution slow requests",
367
- "useEffect cleanup ignore stale responses",
368
- ],
369
- concurrency: 3,
370
- cwd: projectRoot,
371
- query_scope: "batch",
372
- });
373
- record({
374
- fixture: "batch-multi-source-query",
375
- category: "batch",
376
- rawBytes: batchRawBytes,
377
- facts: batchFacts,
378
- mode: "context-mode:ctx_batch_execute",
379
- capability: "batch run + index + query",
380
- obs: cmBatch,
381
- });
382
- const ffBatchStart = performance.now();
383
- const ffBatchQueries = [
384
- "failed test files and counts",
385
- "HTTP error count status distribution slow requests",
386
- "useEffect cleanup ignore stale responses",
387
- ];
388
- const ffBatchResult = await freeflowBatch({
389
- sessionId,
390
- vaultRoot: freeflowVaultRoot,
391
- preserve: "important",
392
- concurrency: 3,
393
- queries: ffBatchQueries,
394
- steps: [
395
- {
396
- id: "test-summary",
397
- kind: "run",
398
- input: {
399
- command: await writeScenarioScript("batch-test-summary", "fixtures/test-output.txt", vitestScenario.code),
400
- cwd: projectRoot,
401
- goal: "test summary",
402
- },
403
- },
404
- {
405
- id: "access-summary",
406
- kind: "run",
407
- input: {
408
- command: await writeScenarioScript("batch-access-summary", "fixtures/access.log", accessScenario.code),
409
- cwd: projectRoot,
410
- goal: "log summary",
411
- },
412
- },
413
- {
414
- id: "react-query",
415
- kind: "search",
416
- input: {
417
- action: "query",
418
- source: { kind: "repo", root: projectRoot },
419
- query: "useEffect cleanup ignore stale responses",
420
- topK: 1,
421
- generatedPathGlobs: ["graphify-out/**"],
422
- },
423
- },
424
- ],
425
- }, runner);
426
- record({
427
- fixture: "batch-multi-source-query",
428
- category: "batch",
429
- rawBytes: batchRawBytes,
430
- facts: batchFacts,
431
- mode: "freeflow:batch",
432
- capability: "parallel steps + deterministic query aggregation",
433
- obs: { result: ffBatchResult, text: freeflowText(ffBatchResult), latencyMs: Math.round(performance.now() - ffBatchStart) },
434
- expectedCorrect: hasFacts(batchQueryAnswerText(ffBatchResult), batchFacts),
435
- notes: "Query answers are transformed deterministically from child evidence handles; full child details remain in details.result.steps.",
436
- });
437
- const outsidePath = path.join(root.path, "outside-secret.txt");
438
- const cmSafety = await callCM("ctx_execute_file", {
439
- path: outsidePath,
440
- language: "javascript",
441
- code: "console.log(FILE_CONTENT)",
442
- intent: "path escape safety",
443
- });
444
- record({
445
- fixture: "outside-file-boundary",
446
- category: "safety",
447
- rawBytes: fileSize("../outside-secret.txt"),
448
- facts: ["File access blocked"],
449
- mode: "context-mode:ctx_execute_file",
450
- capability: "project-boundary file sandbox",
451
- obs: cmSafety,
452
- expectedCorrect: cmSafety.text.includes("File access blocked") && !cmSafety.text.includes("OUTSIDE_SECRET_TOKEN"),
453
- notes: "Expected to block project escape.",
454
- });
455
- const ffSafety = await ffRun(`cat ${shellQuote(outsidePath)}`, "path escape safety");
456
- record({
457
- fixture: "outside-file-boundary",
458
- category: "safety",
459
- rawBytes: fileSize("../outside-secret.txt"),
460
- facts: ["OUTSIDE_SECRET_TOKEN"],
461
- mode: "freeflow:run-cat-host-shell",
462
- capability: "host shell command capture, not sandbox",
463
- obs: ffSafety,
464
- expectedCorrect: !ffSafety.text.includes("OUTSIDE_SECRET_TOKEN"),
465
- notes: "Freeflow_run is a host command runner/capture layer, not a sandbox; it captured outside file content.",
466
- });
467
- const ffSmall = await ffRun("printf 'SMALL_SUCCESS_TOKEN\\n'", "small command");
468
- record({
469
- fixture: "small-success-recovery",
470
- category: "recovery",
471
- rawBytes: 20,
472
- facts: ["SMALL_SUCCESS_TOKEN"],
473
- mode: "freeflow:small-success-default-storage",
474
- capability: "hybrid-dedupe storage semantics",
475
- obs: ffSmall,
476
- expectedCorrect: ffSmall.text.includes("SMALL_SUCCESS_TOKEN"),
477
- notes: recoveryScore(ffSmall.result, "freeflow"),
478
- });
479
- const ffFull = await ffRun("printf 'SMALL_SUCCESS_TOKEN\\n'", "verification", { preserve: "full" });
480
- record({
481
- fixture: "small-success-recovery",
482
- category: "recovery",
483
- rawBytes: 20,
484
- facts: ["SMALL_SUCCESS_TOKEN"],
485
- mode: "freeflow:small-success-preserve-full",
486
- capability: "exact recovery override",
487
- obs: ffFull,
488
- expectedCorrect: ffFull.text.includes("SMALL_SUCCESS_TOKEN"),
489
- notes: recoveryScore(ffFull.result, "freeflow"),
490
- });
491
- const stats = await callCM("ctx_stats", {});
492
- await cm.close();
493
- cm = undefined;
494
- const report = buildReport({
495
- generatedAt,
496
- availability,
497
- freeflowRepo,
498
- benchmarkRoot: root.path,
499
- storeRoot,
500
- freeflowVaultRoot,
501
- sessionId,
502
- rows,
503
- contextModeStatsPreview: stats.text.split("\n").slice(0, 22).join("\n"),
504
- });
505
- if (options.keepArtifacts === false) {
506
- await root.cleanup();
50
+ if (request.timeoutMs !== undefined) {
51
+ shellOptions.timeoutMs = request.timeoutMs;
507
52
  }
508
- return report;
53
+ return runShell(request.command, shellOptions);
54
+ },
55
+ };
56
+ const transport = new runtime.StdioClientTransport({
57
+ command: "bash",
58
+ args: [
59
+ "-lc",
60
+ `cd ${shellQuote(projectRoot)} && CONTEXT_MODE_DIR=${shellQuote(storeRoot)} node ${shellQuote(availability.server)}`,
61
+ ],
62
+ env: process.env,
63
+ });
64
+ cm = new runtime.Client({ name: "freeflow-deep-context-mode-benchmark", version: "0.0.1" });
65
+ await cm.connect(transport);
66
+ async function callCM(name, args) {
67
+ if (!cm) {
68
+ throw new Error("Context Mode client is not connected.");
69
+ }
70
+ const start = performance.now();
71
+ const result = await cm.callTool({ name, arguments: args });
72
+ return { result, text: contentText(result), latencyMs: Math.round(performance.now() - start) };
509
73
  }
510
- catch (error) {
511
- if (cm) {
512
- await cm.close().catch(() => undefined);
513
- }
514
- if (options.keepArtifacts === false) {
515
- await root.cleanup();
516
- }
517
- throw error;
74
+ async function ffRun(command, goal, extra = {}) {
75
+ const start = performance.now();
76
+ const runOptions = {
77
+ command,
78
+ cwd: projectRoot,
79
+ timeoutMs: 30_000,
80
+ sessionId,
81
+ vaultRoot: freeflowVaultRoot,
82
+ preserve: "important",
83
+ goal,
84
+ ...extra,
85
+ };
86
+ const result = await freeflowRun(runOptions, runner);
87
+ return { result, text: freeflowText(result), latencyMs: Math.round(performance.now() - start) };
518
88
  }
519
- }
520
- export function renderContextModeRealDeepBenchmarkReport(report) {
521
- const lines = [];
522
- lines.push("# Deep Freeflow vs real Context Mode benchmark");
523
- lines.push("");
524
- lines.push(`Generated: ${report.generatedAt}`);
525
- lines.push(`Implementation: ${report.implementation}`);
526
- lines.push(`Context Mode status: ${report.contextMode.status}`);
527
- lines.push(`Context Mode: v${report.contextMode.version} ${report.contextMode.commit}`);
528
- lines.push(`Freeflow: ${report.freeflow.commit}`);
529
- lines.push(`Artifacts root: ${report.benchmarkRoot}`);
530
- lines.push(`Public superiority claims allowed: ${report.publicClaimsAllowed ? "yes" : "no"}`);
531
- if (report.contextMode.status === "unavailable") {
532
- lines.push(`Context Mode unavailable: ${report.contextMode.unavailableReason ?? "unknown"}`);
89
+ async function ffRetrieve(query, sourceRoot = projectRoot, extra = {}) {
90
+ const start = performance.now();
91
+ const retrieveOptions = {
92
+ action: "query",
93
+ source: { kind: "repo", root: sourceRoot },
94
+ query,
95
+ topK: 3,
96
+ preserve: "important",
97
+ generatedPathGlobs: ["graphify-out/**"],
98
+ ...extra,
99
+ };
100
+ const result = await freeflowSearch(retrieveOptions);
101
+ return { result, text: freeflowText(result), latencyMs: Math.round(performance.now() - start) };
533
102
  }
534
- lines.push("");
535
- lines.push("## Methodology");
536
- lines.push("");
537
- for (const item of report.methodology) {
538
- lines.push(`- ${item}`);
103
+ async function ffTransform(sourceOutputId, stream, operation) {
104
+ const start = performance.now();
105
+ const result = await freeflowTransform({
106
+ source: { kind: "vault", outputId: sourceOutputId, stream },
107
+ operation,
108
+ preserve: "important",
109
+ sessionId,
110
+ vaultRoot: freeflowVaultRoot,
111
+ });
112
+ return { result, text: freeflowText(result), latencyMs: Math.round(performance.now() - start) };
539
113
  }
540
- lines.push("");
541
- lines.push("## Expected limitation checks");
542
- lines.push("");
543
- lines.push(`Expected Freeflow limitations detected: ${report.baselineChecks.expectedCurrentFailuresDetected ? "yes" : "no"}`);
544
- if (report.baselineChecks.missingExpectedFailureKeys.length > 0) {
545
- lines.push(`Missing expected limitation keys: ${report.baselineChecks.missingExpectedFailureKeys.join(", ")}`);
114
+ async function ffProcessRepoFile(relativePath) {
115
+ const start = performance.now();
116
+ const result = await processSource(
117
+ { kind: "repo-file", root: projectRoot, path: relativePath },
118
+ { sessionId, vaultRoot: freeflowVaultRoot },
119
+ );
120
+ return { result, text: processingText(result), latencyMs: Math.round(performance.now() - start) };
546
121
  }
547
- for (const note of report.baselineChecks.notes) {
548
- lines.push(`- ${note}`);
122
+ function record(input) {
123
+ const text = input.obs.text;
124
+ const factsFound = factCount(text, input.facts);
125
+ const correct = input.expectedCorrect ?? factsFound === input.facts.length;
126
+ rows.push({
127
+ fixture: input.fixture,
128
+ category: input.category,
129
+ mode: input.mode,
130
+ capability: input.capability ?? "",
131
+ correct,
132
+ factsFound,
133
+ factsTotal: input.facts.length,
134
+ rawBytes: input.rawBytes,
135
+ visibleBytes: bytes(text),
136
+ reductionPct: input.rawBytes ? Number(((1 - bytes(text) / input.rawBytes) * 100).toFixed(2)) : 0,
137
+ latencyMs: input.obs.latencyMs ?? 0,
138
+ recovery: recoveryScore(input.obs.result, input.mode),
139
+ notes: input.notes ?? "",
140
+ preview: text.split("\n").slice(0, 18).join("\n"),
141
+ });
549
142
  }
550
- lines.push("");
551
- lines.push("## Summary by mode");
552
- lines.push("");
553
- lines.push("| mode | scenarios | correct | facts | raw bytes | visible bytes | reduction | avg latency | exact recovery | metadata-only |");
554
- lines.push("| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |");
555
- for (const summary of report.summaries) {
556
- lines.push(`| ${escapeMarkdown(summary.mode)} | ${summary.scenarios} | ${summary.correct}/${summary.scenarios} | ${summary.totalFactsFound}/${summary.totalFacts} | ${summary.totalRawBytes} | ${summary.totalVisibleBytes} | ${summary.weightedReductionPct}% | ${summary.avgLatencyMs}ms | ${summary.exactRecovery} | ${summary.metadataOnly} |`);
143
+ function fileSize(rel) {
144
+ return statSync(path.join(projectRoot, rel)).size;
557
145
  }
558
- lines.push("");
559
- lines.push("## Failure clusters");
560
- lines.push("");
561
- lines.push("### Freeflow incorrect rows");
562
- lines.push("");
563
- for (const failure of report.failureClusters.freeflowIncorrect) {
564
- lines.push(`- ${failure.fixture} / ${failure.mode}: facts ${failure.facts}, visible ${failure.visibleBytes}B. ${failure.notes}`);
146
+ function scriptCode(rel, code) {
147
+ return `import { readFileSync } from 'node:fs';\nconst FILE_CONTENT = readFileSync(${JSON.stringify(rel)}, 'utf8');\n${code}\n`;
565
148
  }
566
- lines.push("");
567
- lines.push("### Context Mode incorrect rows");
568
- lines.push("");
569
- for (const failure of report.failureClusters.contextModeIncorrect) {
570
- lines.push(`- ${failure.fixture} / ${failure.mode}: facts ${failure.facts}, visible ${failure.visibleBytes}B. ${failure.notes}`);
149
+ async function writeScenarioScript(id, rel, code) {
150
+ const scriptPath = path.join(projectRoot, "scripts", `${id}.mjs`);
151
+ await writeFile(scriptPath, scriptCode(rel, code), "utf8");
152
+ return `node scripts/${id}.mjs`;
571
153
  }
572
- lines.push("");
573
- lines.push("### Freeflow verbose rows (<50% reduction)");
574
- lines.push("");
575
- for (const failure of report.failureClusters.freeflowVerbose) {
576
- lines.push(`- ${failure.fixture} / ${failure.mode}: ${failure.rawBytes}B raw -> ${failure.visibleBytes}B visible (${failure.reductionPct}%).`);
154
+ const executeScenarios = createExecuteScenarios();
155
+ for (const scenario of executeScenarios) {
156
+ const rawBytes = fileSize(scenario.file);
157
+ const cmObs = await callCM("ctx_execute_file", {
158
+ path: scenario.file,
159
+ language: "javascript",
160
+ code: scenario.code,
161
+ intent: scenario.goal,
162
+ });
163
+ record({
164
+ fixture: scenario.id,
165
+ category: scenario.category,
166
+ rawBytes,
167
+ facts: scenario.facts,
168
+ mode: "context-mode:ctx_execute_file",
169
+ capability: "sandboxed file transform",
170
+ obs: cmObs,
171
+ });
172
+ const ffDefault = await ffRun(`cat ${shellQuote(scenario.file)}`, scenario.goal);
173
+ record({
174
+ fixture: scenario.id,
175
+ category: scenario.category,
176
+ rawBytes,
177
+ facts: scenario.facts,
178
+ mode: "freeflow:run-cat-default",
179
+ capability: "command capture/routing",
180
+ obs: ffDefault,
181
+ });
182
+ const processingReducerModes = {
183
+ "vitest-summary": { mode: "freeflow:process-test-output-reducer", reducer: "test-output" },
184
+ "tsc-summary": { mode: "freeflow:process-diagnostics-reducer", reducer: "diagnostics" },
185
+ "build-summary": { mode: "freeflow:process-build-output-reducer", reducer: "build-output" },
186
+ "access-summary": { mode: "freeflow:process-access-log-reducer", reducer: "access-log" },
187
+ "analytics-summary": { mode: "freeflow:process-table-reducer", reducer: "table" },
188
+ "mcp-tools-summary": { mode: "freeflow:process-mcp-tools-reducer", reducer: "mcp-tools" },
189
+ "playwright-summary": { mode: "freeflow:process-browser-snapshot-reducer", reducer: "browser-snapshot" },
190
+ "git-log-summary": { mode: "freeflow:process-git-log-reducer", reducer: "git-log" },
191
+ };
192
+ const reducerMode = processingReducerModes[scenario.id];
193
+ if (reducerMode) {
194
+ const ffProcessed = await ffProcessRepoFile(scenario.file);
195
+ record({
196
+ fixture: scenario.id,
197
+ category: scenario.category,
198
+ rawBytes,
199
+ facts: scenario.facts,
200
+ mode: reducerMode.mode,
201
+ capability: "processing engine built-in reducer",
202
+ obs: ffProcessed,
203
+ notes: `Uses the internal processing engine ${reducerMode.reducer} reducer; no public Pi surface is selected.`,
204
+ });
205
+ }
206
+ const command = await writeScenarioScript(scenario.id, scenario.file, scenario.code);
207
+ const ffComputed = await ffRun(command, scenario.goal);
208
+ record({
209
+ fixture: scenario.id,
210
+ category: scenario.category,
211
+ rawBytes,
212
+ facts: scenario.facts,
213
+ mode: "freeflow:run-computed-script",
214
+ capability: "host-shell transform via freeflow_run",
215
+ obs: ffComputed,
216
+ notes: "Compact because an external Node script computed facts; not Freeflow sandboxed script transform.",
217
+ });
218
+ if (scenario.upstreamCode !== undefined) {
219
+ const upstream = await callCM("ctx_execute_file", {
220
+ path: scenario.file,
221
+ language: "javascript",
222
+ code: scenario.upstreamCode,
223
+ intent: "upstream benchmark script",
224
+ });
225
+ record({
226
+ fixture: `${scenario.id}-upstream-script`,
227
+ category: scenario.category,
228
+ rawBytes,
229
+ facts: scenario.facts,
230
+ mode: "context-mode:upstream-benchmark-script",
231
+ capability: "benchmark script correctness",
232
+ obs: upstream,
233
+ expectedCorrect: hasFacts(upstream.text, scenario.facts),
234
+ notes: "Runs Context Mode upstream benchmark summarizer; checks against fixture ground truth.",
235
+ });
236
+ }
577
237
  }
578
- lines.push("");
579
- lines.push("### Freeflow metadata-only/no-raw rows");
580
- lines.push("");
581
- for (const failure of report.failureClusters.metadataOnly) {
582
- lines.push(`- ${failure.fixture} / ${failure.mode}: ${failure.notes}`);
238
+ const accessRaw = await ffRun("cat fixtures/access.log", "diagnosis");
239
+ const accessOutputId = recoveryOutputId(accessRaw.result);
240
+ if (accessOutputId) {
241
+ const statusGroups = await ffTransform(accessOutputId, "stdout", {
242
+ kind: "groupByRegex",
243
+ pattern: '" (\\d{3}) \\d+ (\\d+)ms$',
244
+ group: "1",
245
+ maxGroups: 10,
246
+ maxLinesPerGroup: 1,
247
+ });
248
+ record({
249
+ fixture: "access-log-transform-status",
250
+ category: "transform",
251
+ rawBytes: fileSize("fixtures/access.log"),
252
+ facts: ["200", "500", "401", "404"],
253
+ mode: "freeflow:transform-groupByRegex",
254
+ capability: "deterministic transform over vault",
255
+ obs: statusGroups,
256
+ notes: "Groups statuses but does not compute all aggregate counts/error rate/avg latency.",
257
+ });
583
258
  }
584
- lines.push("");
585
- lines.push("## Detailed rows");
586
- lines.push("");
587
- lines.push("| fixture | category | mode | capability | correct | facts | raw | visible | reduction | recovery | latency | notes |");
588
- lines.push("| --- | --- | --- | --- | --- | ---: | ---: | ---: | ---: | --- | ---: | --- |");
589
- for (const row of report.rows) {
590
- lines.push(`| ${escapeMarkdown(row.fixture)} | ${escapeMarkdown(row.category)} | ${escapeMarkdown(row.mode)} | ${escapeMarkdown(row.capability)} | ${row.correct ? "yes" : "no"} | ${row.factsFound}/${row.factsTotal} | ${row.rawBytes} | ${row.visibleBytes} | ${row.reductionPct}% | ${escapeMarkdown(row.recovery)} | ${row.latencyMs} | ${escapeMarkdown(row.notes)} |`);
259
+ const analyticsRaw = await ffRun("cat fixtures/analytics.csv", "inspect CSV");
260
+ const analyticsOutputId = recoveryOutputId(analyticsRaw.result);
261
+ if (analyticsOutputId) {
262
+ const timeouts = await ffTransform(analyticsOutputId, "stdout", { kind: "countMatches", pattern: ",timeout," });
263
+ record({
264
+ fixture: "analytics-count-timeouts",
265
+ category: "transform",
266
+ rawBytes: fileSize("fixtures/analytics.csv"),
267
+ facts: ["matches: 50"],
268
+ mode: "freeflow:transform-countMatches",
269
+ capability: "deterministic transform over vault",
270
+ obs: timeouts,
271
+ notes: "Can count one selected fact but not summarize CSV distribution without a script transform.",
272
+ });
591
273
  }
592
- lines.push("");
593
- lines.push("## Context Mode stats preview");
594
- lines.push("");
274
+ const searchScenarios = createSearchScenarios();
275
+ for (const scenario of searchScenarios) {
276
+ const rawBytes = fileSize(scenario.file);
277
+ await callCM("ctx_index", { path: scenario.file, source: scenario.source });
278
+ const cmSearch = await callCM("ctx_search", { queries: [scenario.query], source: scenario.source, limit: 3 });
279
+ record({
280
+ fixture: scenario.id,
281
+ category: "docs-search",
282
+ rawBytes,
283
+ facts: scenario.facts,
284
+ mode: "context-mode:ctx_index+ctx_search",
285
+ capability: "FTS index/search",
286
+ obs: cmSearch,
287
+ });
288
+ const ffSearch = await ffRetrieve(scenario.query, projectRoot);
289
+ record({
290
+ fixture: scenario.id,
291
+ category: "docs-search",
292
+ rawBytes,
293
+ facts: scenario.facts,
294
+ mode: "freeflow:repo-query",
295
+ capability: "live repo text query",
296
+ obs: ffSearch,
297
+ });
298
+ }
299
+ await callCM("ctx_index", {
300
+ path: "repo-fixture",
301
+ source: "repo-fixture",
302
+ maxFiles: 20,
303
+ extensions: [".ts", ".html"],
304
+ respectGitignore: false,
305
+ });
306
+ const repoRawBytes = fileSize("repo-fixture/src/sandbox.ts") + fileSize("repo-fixture/graphify-out/graph.html");
307
+ const repoFacts = ["src/sandbox.ts", "SAFE_CONTEXT_BOUNDARY", "validateBoundary"];
308
+ const cmRepo = await callCM("ctx_search", {
309
+ queries: ["SAFE_CONTEXT_BOUNDARY validateBoundary implementation"],
310
+ source: "repo-fixture",
311
+ limit: 5,
312
+ });
313
+ record({
314
+ fixture: "repo-generated-decoy",
315
+ category: "repo-search",
316
+ rawBytes: repoRawBytes,
317
+ facts: repoFacts,
318
+ mode: "context-mode:ctx_index+ctx_search",
319
+ capability: "FTS index/search",
320
+ obs: cmRepo,
321
+ expectedCorrect: hasFacts(cmRepo.text, repoFacts) && !cmRepo.text.includes("graph.html"),
322
+ notes: cmRepo.text.includes("graph.html")
323
+ ? "Generated graph.html decoy appears in results."
324
+ : "Top/live result did not include generated decoy.",
325
+ });
326
+ const ffRepo = await ffRetrieve(
327
+ "SAFE_CONTEXT_BOUNDARY validateBoundary implementation",
328
+ path.join(projectRoot, "repo-fixture"),
329
+ );
330
+ record({
331
+ fixture: "repo-generated-decoy",
332
+ category: "repo-search",
333
+ rawBytes: repoRawBytes,
334
+ facts: repoFacts,
335
+ mode: "freeflow:repo-query",
336
+ capability: "generated-path-aware live repo query",
337
+ obs: ffRepo,
338
+ expectedCorrect: hasFacts(ffRepo.text, repoFacts) && !ffRepo.text.includes("graph.html"),
339
+ notes: "Configured generatedPathGlobs=graphify-out/**.",
340
+ });
341
+ await callCM("ctx_index", { path: "docs/stale.md", source: "stale-doc" });
342
+ await writeFile(
343
+ path.join(projectRoot, "docs/stale.md"),
344
+ "# Cache Policy\n\nNEW_CACHE_POLICY_TOKEN says cache for 30 seconds.\n",
345
+ "utf8",
346
+ );
347
+ const cmStale = await callCM("ctx_search", {
348
+ queries: ["cache policy token seconds"],
349
+ source: "stale-doc",
350
+ limit: 3,
351
+ });
352
+ record({
353
+ fixture: "stale-index-after-file-change",
354
+ category: "freshness",
355
+ rawBytes: fileSize("docs/stale.md"),
356
+ facts: ["NEW_CACHE_POLICY_TOKEN"],
357
+ mode: "context-mode:ctx_search-after-mutation",
358
+ capability: "persistent index freshness",
359
+ obs: cmStale,
360
+ expectedCorrect: cmStale.text.includes("NEW_CACHE_POLICY_TOKEN"),
361
+ notes: cmStale.text.includes("OLD_CACHE_POLICY_TOKEN")
362
+ ? "Returned stale indexed content after file mutation."
363
+ : "",
364
+ });
365
+ const ffFresh = await ffRetrieve("cache policy token seconds NEW_CACHE_POLICY_TOKEN", projectRoot);
366
+ record({
367
+ fixture: "stale-index-after-file-change",
368
+ category: "freshness",
369
+ rawBytes: fileSize("docs/stale.md"),
370
+ facts: ["NEW_CACHE_POLICY_TOKEN"],
371
+ mode: "freeflow:repo-query-live-file",
372
+ capability: "live repo scanner",
373
+ obs: ffFresh,
374
+ });
375
+ const batchRawBytes =
376
+ fileSize("fixtures/test-output.txt") +
377
+ fileSize("fixtures/access.log") +
378
+ fileSize("fixtures/context7-react-docs.md");
379
+ const batchFacts = ["4 failed", "88", "ignore = true"];
380
+ const vitestScenario = requiredScenario(executeScenarios, "vitest-summary");
381
+ const accessScenario = requiredScenario(executeScenarios, "access-summary");
382
+ const cmBatch = await callCM("ctx_batch_execute", {
383
+ commands: [
384
+ { label: "tests", command: "cat fixtures/test-output.txt" },
385
+ { label: "access-log", command: "cat fixtures/access.log" },
386
+ { label: "react-docs", command: "cat fixtures/context7-react-docs.md" },
387
+ ],
388
+ queries: [
389
+ "failed test files and counts",
390
+ "HTTP error count status distribution slow requests",
391
+ "useEffect cleanup ignore stale responses",
392
+ ],
393
+ concurrency: 3,
394
+ cwd: projectRoot,
395
+ query_scope: "batch",
396
+ });
397
+ record({
398
+ fixture: "batch-multi-source-query",
399
+ category: "batch",
400
+ rawBytes: batchRawBytes,
401
+ facts: batchFacts,
402
+ mode: "context-mode:ctx_batch_execute",
403
+ capability: "batch run + index + query",
404
+ obs: cmBatch,
405
+ });
406
+ const ffBatchStart = performance.now();
407
+ const ffBatchQueries = [
408
+ "failed test files and counts",
409
+ "HTTP error count status distribution slow requests",
410
+ "useEffect cleanup ignore stale responses",
411
+ ];
412
+ const ffBatchResult = await freeflowBatch(
413
+ {
414
+ sessionId,
415
+ vaultRoot: freeflowVaultRoot,
416
+ preserve: "important",
417
+ concurrency: 3,
418
+ queries: ffBatchQueries,
419
+ steps: [
420
+ {
421
+ id: "test-summary",
422
+ kind: "run",
423
+ input: {
424
+ command: await writeScenarioScript("batch-test-summary", "fixtures/test-output.txt", vitestScenario.code),
425
+ cwd: projectRoot,
426
+ goal: "test summary",
427
+ },
428
+ },
429
+ {
430
+ id: "access-summary",
431
+ kind: "run",
432
+ input: {
433
+ command: await writeScenarioScript("batch-access-summary", "fixtures/access.log", accessScenario.code),
434
+ cwd: projectRoot,
435
+ goal: "log summary",
436
+ },
437
+ },
438
+ {
439
+ id: "react-query",
440
+ kind: "search",
441
+ input: {
442
+ action: "query",
443
+ source: { kind: "repo", root: projectRoot },
444
+ query: "useEffect cleanup ignore stale responses",
445
+ topK: 1,
446
+ generatedPathGlobs: ["graphify-out/**"],
447
+ },
448
+ },
449
+ ],
450
+ },
451
+ runner,
452
+ );
453
+ record({
454
+ fixture: "batch-multi-source-query",
455
+ category: "batch",
456
+ rawBytes: batchRawBytes,
457
+ facts: batchFacts,
458
+ mode: "freeflow:batch",
459
+ capability: "parallel steps + deterministic query aggregation",
460
+ obs: {
461
+ result: ffBatchResult,
462
+ text: freeflowText(ffBatchResult),
463
+ latencyMs: Math.round(performance.now() - ffBatchStart),
464
+ },
465
+ expectedCorrect: hasFacts(batchQueryAnswerText(ffBatchResult), batchFacts),
466
+ notes:
467
+ "Query answers are transformed deterministically from child evidence handles; full child details remain in details.result.steps.",
468
+ });
469
+ const outsidePath = path.join(root.path, "outside-secret.txt");
470
+ const cmSafety = await callCM("ctx_execute_file", {
471
+ path: outsidePath,
472
+ language: "javascript",
473
+ code: "console.log(FILE_CONTENT)",
474
+ intent: "path escape safety",
475
+ });
476
+ record({
477
+ fixture: "outside-file-boundary",
478
+ category: "safety",
479
+ rawBytes: fileSize("../outside-secret.txt"),
480
+ facts: ["File access blocked"],
481
+ mode: "context-mode:ctx_execute_file",
482
+ capability: "project-boundary file sandbox",
483
+ obs: cmSafety,
484
+ expectedCorrect: cmSafety.text.includes("File access blocked") && !cmSafety.text.includes("OUTSIDE_SECRET_TOKEN"),
485
+ notes: "Expected to block project escape.",
486
+ });
487
+ const ffSafety = await ffRun(`cat ${shellQuote(outsidePath)}`, "path escape safety");
488
+ record({
489
+ fixture: "outside-file-boundary",
490
+ category: "safety",
491
+ rawBytes: fileSize("../outside-secret.txt"),
492
+ facts: ["OUTSIDE_SECRET_TOKEN"],
493
+ mode: "freeflow:run-cat-host-shell",
494
+ capability: "host shell command capture, not sandbox",
495
+ obs: ffSafety,
496
+ expectedCorrect: !ffSafety.text.includes("OUTSIDE_SECRET_TOKEN"),
497
+ notes: "Freeflow_run is a host command runner/capture layer, not a sandbox; it captured outside file content.",
498
+ });
499
+ const ffSmall = await ffRun("printf 'SMALL_SUCCESS_TOKEN\\n'", "small command");
500
+ record({
501
+ fixture: "small-success-recovery",
502
+ category: "recovery",
503
+ rawBytes: 20,
504
+ facts: ["SMALL_SUCCESS_TOKEN"],
505
+ mode: "freeflow:small-success-default-storage",
506
+ capability: "hybrid-dedupe storage semantics",
507
+ obs: ffSmall,
508
+ expectedCorrect: ffSmall.text.includes("SMALL_SUCCESS_TOKEN"),
509
+ notes: recoveryScore(ffSmall.result, "freeflow"),
510
+ });
511
+ const ffFull = await ffRun("printf 'SMALL_SUCCESS_TOKEN\\n'", "verification", { preserve: "full" });
512
+ record({
513
+ fixture: "small-success-recovery",
514
+ category: "recovery",
515
+ rawBytes: 20,
516
+ facts: ["SMALL_SUCCESS_TOKEN"],
517
+ mode: "freeflow:small-success-preserve-full",
518
+ capability: "exact recovery override",
519
+ obs: ffFull,
520
+ expectedCorrect: ffFull.text.includes("SMALL_SUCCESS_TOKEN"),
521
+ notes: recoveryScore(ffFull.result, "freeflow"),
522
+ });
523
+ const stats = await callCM("ctx_stats", {});
524
+ await cm.close();
525
+ cm = undefined;
526
+ const report = buildReport({
527
+ generatedAt,
528
+ availability,
529
+ freeflowRepo,
530
+ benchmarkRoot: root.path,
531
+ storeRoot,
532
+ freeflowVaultRoot,
533
+ sessionId,
534
+ rows,
535
+ contextModeStatsPreview: stats.text.split("\n").slice(0, 22).join("\n"),
536
+ });
537
+ if (options.keepArtifacts === false) {
538
+ await root.cleanup();
539
+ }
540
+ return report;
541
+ } catch (error) {
542
+ if (cm) {
543
+ await cm.close().catch(() => undefined);
544
+ }
545
+ if (options.keepArtifacts === false) {
546
+ await root.cleanup();
547
+ }
548
+ throw error;
549
+ }
550
+ }
551
+ export function renderContextModeRealDeepBenchmarkReport(report) {
552
+ const lines = [];
553
+ lines.push("# Deep Freeflow vs real Context Mode benchmark");
554
+ lines.push("");
555
+ lines.push(`Generated: ${report.generatedAt}`);
556
+ lines.push(`Implementation: ${report.implementation}`);
557
+ lines.push(`Context Mode status: ${report.contextMode.status}`);
558
+ lines.push(`Context Mode: v${report.contextMode.version} ${report.contextMode.commit}`);
559
+ lines.push(`Freeflow: ${report.freeflow.commit}`);
560
+ lines.push(`Artifacts root: ${report.benchmarkRoot}`);
561
+ lines.push(`Public superiority claims allowed: ${report.publicClaimsAllowed ? "yes" : "no"}`);
562
+ if (report.contextMode.status === "unavailable") {
563
+ lines.push(`Context Mode unavailable: ${report.contextMode.unavailableReason ?? "unknown"}`);
564
+ }
565
+ lines.push("");
566
+ lines.push("## Methodology");
567
+ lines.push("");
568
+ for (const item of report.methodology) {
569
+ lines.push(`- ${item}`);
570
+ }
571
+ lines.push("");
572
+ lines.push("## Expected limitation checks");
573
+ lines.push("");
574
+ lines.push(
575
+ `Expected Freeflow limitations detected: ${report.baselineChecks.expectedCurrentFailuresDetected ? "yes" : "no"}`,
576
+ );
577
+ if (report.baselineChecks.missingExpectedFailureKeys.length > 0) {
578
+ lines.push(`Missing expected limitation keys: ${report.baselineChecks.missingExpectedFailureKeys.join(", ")}`);
579
+ }
580
+ for (const note of report.baselineChecks.notes) {
581
+ lines.push(`- ${note}`);
582
+ }
583
+ lines.push("");
584
+ lines.push("## Summary by mode");
585
+ lines.push("");
586
+ lines.push(
587
+ "| mode | scenarios | correct | facts | raw bytes | visible bytes | reduction | avg latency | exact recovery | metadata-only |",
588
+ );
589
+ lines.push("| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |");
590
+ for (const summary of report.summaries) {
591
+ lines.push(
592
+ `| ${escapeMarkdown(summary.mode)} | ${summary.scenarios} | ${summary.correct}/${summary.scenarios} | ${summary.totalFactsFound}/${summary.totalFacts} | ${summary.totalRawBytes} | ${summary.totalVisibleBytes} | ${summary.weightedReductionPct}% | ${summary.avgLatencyMs}ms | ${summary.exactRecovery} | ${summary.metadataOnly} |`,
593
+ );
594
+ }
595
+ lines.push("");
596
+ lines.push("## Failure clusters");
597
+ lines.push("");
598
+ lines.push("### Freeflow incorrect rows");
599
+ lines.push("");
600
+ for (const failure of report.failureClusters.freeflowIncorrect) {
601
+ lines.push(
602
+ `- ${failure.fixture} / ${failure.mode}: facts ${failure.facts}, visible ${failure.visibleBytes}B. ${failure.notes}`,
603
+ );
604
+ }
605
+ lines.push("");
606
+ lines.push("### Context Mode incorrect rows");
607
+ lines.push("");
608
+ for (const failure of report.failureClusters.contextModeIncorrect) {
609
+ lines.push(
610
+ `- ${failure.fixture} / ${failure.mode}: facts ${failure.facts}, visible ${failure.visibleBytes}B. ${failure.notes}`,
611
+ );
612
+ }
613
+ lines.push("");
614
+ lines.push("### Freeflow verbose rows (<50% reduction)");
615
+ lines.push("");
616
+ for (const failure of report.failureClusters.freeflowVerbose) {
617
+ lines.push(
618
+ `- ${failure.fixture} / ${failure.mode}: ${failure.rawBytes}B raw -> ${failure.visibleBytes}B visible (${failure.reductionPct}%).`,
619
+ );
620
+ }
621
+ lines.push("");
622
+ lines.push("### Freeflow metadata-only/no-raw rows");
623
+ lines.push("");
624
+ for (const failure of report.failureClusters.metadataOnly) {
625
+ lines.push(`- ${failure.fixture} / ${failure.mode}: ${failure.notes}`);
626
+ }
627
+ lines.push("");
628
+ lines.push("## Detailed rows");
629
+ lines.push("");
630
+ lines.push(
631
+ "| fixture | category | mode | capability | correct | facts | raw | visible | reduction | recovery | latency | notes |",
632
+ );
633
+ lines.push("| --- | --- | --- | --- | --- | ---: | ---: | ---: | ---: | --- | ---: | --- |");
634
+ for (const row of report.rows) {
635
+ lines.push(
636
+ `| ${escapeMarkdown(row.fixture)} | ${escapeMarkdown(row.category)} | ${escapeMarkdown(row.mode)} | ${escapeMarkdown(row.capability)} | ${row.correct ? "yes" : "no"} | ${row.factsFound}/${row.factsTotal} | ${row.rawBytes} | ${row.visibleBytes} | ${row.reductionPct}% | ${escapeMarkdown(row.recovery)} | ${row.latencyMs} | ${escapeMarkdown(row.notes)} |`,
637
+ );
638
+ }
639
+ lines.push("");
640
+ lines.push("## Context Mode stats preview");
641
+ lines.push("");
642
+ lines.push("```");
643
+ lines.push(report.contextModeStatsPreview);
644
+ lines.push("```");
645
+ lines.push("");
646
+ lines.push("## Representative failing previews");
647
+ lines.push("");
648
+ for (const row of report.rows.filter((candidate) => !candidate.correct).slice(0, 12)) {
649
+ lines.push(`### ${row.fixture} / ${row.mode}`);
595
650
  lines.push("```");
596
- lines.push(report.contextModeStatsPreview);
651
+ lines.push(row.preview);
597
652
  lines.push("```");
598
653
  lines.push("");
599
- lines.push("## Representative failing previews");
654
+ }
655
+ if (report.notes.length > 0) {
656
+ lines.push("## Notes");
600
657
  lines.push("");
601
- for (const row of report.rows.filter((candidate) => !candidate.correct).slice(0, 12)) {
602
- lines.push(`### ${row.fixture} / ${row.mode}`);
603
- lines.push("```");
604
- lines.push(row.preview);
605
- lines.push("```");
606
- lines.push("");
607
- }
608
- if (report.notes.length > 0) {
609
- lines.push("## Notes");
610
- lines.push("");
611
- for (const note of report.notes) {
612
- lines.push(`- ${note}`);
613
- }
658
+ for (const note of report.notes) {
659
+ lines.push(`- ${note}`);
614
660
  }
615
- return `${lines.join("\n")}\n`;
661
+ }
662
+ return `${lines.join("\n")}\n`;
616
663
  }
617
664
  export async function writeContextModeRealDeepBenchmarkReports(report, markdownReportPath, options = {}) {
618
- return writeBenchmarkReportPair({
619
- report,
620
- markdownReportPath,
621
- jsonReportPath: options.jsonReportPath,
622
- renderMarkdown: renderContextModeRealDeepBenchmarkReport,
623
- });
665
+ return writeBenchmarkReportPair({
666
+ report,
667
+ markdownReportPath,
668
+ jsonReportPath: options.jsonReportPath,
669
+ renderMarkdown: renderContextModeRealDeepBenchmarkReport,
670
+ });
624
671
  }
625
672
  export function baselineFailureClassesDetected(report) {
626
- const detected = detectedFailureKeys(report.failureClusters);
627
- return EXPECTED_BASELINE_FAILURE_KEYS.every((key) => detected.has(key));
673
+ const detected = detectedFailureKeys(report.failureClusters);
674
+ return EXPECTED_BASELINE_FAILURE_KEYS.every((key) => detected.has(key));
628
675
  }
629
676
  function buildReport(input) {
630
- const summaries = summarizeRows(input.rows);
631
- const clusters = failureClusters(input.rows);
632
- const baselineChecks = buildBaselineChecks(clusters);
633
- return {
634
- generatedAt: input.generatedAt,
635
- implementation: CONTEXT_MODE_REAL_DEEP_IMPLEMENTATION,
636
- benchmarkLabel: DEFAULT_ITERATION_LABEL,
637
- contextMode: {
638
- status: "available",
639
- repo: input.availability.repo,
640
- server: input.availability.server,
641
- fixtureSource: input.availability.fixtureSource,
642
- commit: safeShellSync(`git -C ${shellQuote(input.availability.repo)} rev-parse HEAD`).trim() || "unknown",
643
- version: safeReadPackageVersion(input.availability.repo),
644
- storeRoot: input.storeRoot,
645
- missingPaths: [],
646
- },
647
- freeflow: {
648
- repo: input.freeflowRepo,
649
- commit: safeShellSync(`git -C ${shellQuote(input.freeflowRepo)} rev-parse HEAD`).trim() || "unknown",
650
- vaultRoot: input.freeflowVaultRoot,
651
- sessionId: input.sessionId,
652
- },
653
- benchmarkRoot: input.benchmarkRoot,
654
- publicClaimsAllowed: false,
655
- methodology: realBenchmarkMethodology(),
656
- summaries,
657
- baselineChecks,
658
- failureClusters: clusters,
659
- rows: input.rows,
660
- contextModeStatsPreview: input.contextModeStatsPreview,
661
- notes: [
662
- "This is a real local Context Mode comparison, but public superiority claims remain disallowed until reviewed acceptance criteria pass.",
663
- "The remaining expected Freeflow failure is the host-shell outside-file-boundary row: freeflow_run captures host command output and is not a project-boundary file sandbox.",
664
- ],
665
- };
677
+ const summaries = summarizeRows(input.rows);
678
+ const clusters = failureClusters(input.rows);
679
+ const baselineChecks = buildBaselineChecks(clusters);
680
+ return {
681
+ generatedAt: input.generatedAt,
682
+ implementation: CONTEXT_MODE_REAL_DEEP_IMPLEMENTATION,
683
+ benchmarkLabel: DEFAULT_ITERATION_LABEL,
684
+ contextMode: {
685
+ status: "available",
686
+ repo: input.availability.repo,
687
+ server: input.availability.server,
688
+ fixtureSource: input.availability.fixtureSource,
689
+ commit: safeShellSync(`git -C ${shellQuote(input.availability.repo)} rev-parse HEAD`).trim() || "unknown",
690
+ version: safeReadPackageVersion(input.availability.repo),
691
+ storeRoot: input.storeRoot,
692
+ missingPaths: [],
693
+ },
694
+ freeflow: {
695
+ repo: input.freeflowRepo,
696
+ commit: safeShellSync(`git -C ${shellQuote(input.freeflowRepo)} rev-parse HEAD`).trim() || "unknown",
697
+ vaultRoot: input.freeflowVaultRoot,
698
+ sessionId: input.sessionId,
699
+ },
700
+ benchmarkRoot: input.benchmarkRoot,
701
+ publicClaimsAllowed: false,
702
+ methodology: realBenchmarkMethodology(),
703
+ summaries,
704
+ baselineChecks,
705
+ failureClusters: clusters,
706
+ rows: input.rows,
707
+ contextModeStatsPreview: input.contextModeStatsPreview,
708
+ notes: [
709
+ "This is a real local Context Mode comparison, but public superiority claims remain disallowed until reviewed acceptance criteria pass.",
710
+ "The remaining expected Freeflow failure is the host-shell outside-file-boundary row: freeflow_run captures host command output and is not a project-boundary file sandbox.",
711
+ ],
712
+ };
666
713
  }
667
714
  function unavailableReport(input) {
668
- const clusters = failureClusters([]);
669
- const baselineChecks = buildBaselineChecks(clusters);
670
- return {
671
- generatedAt: input.generatedAt,
672
- implementation: CONTEXT_MODE_REAL_DEEP_IMPLEMENTATION,
673
- benchmarkLabel: DEFAULT_ITERATION_LABEL,
674
- contextMode: {
675
- status: "unavailable",
676
- repo: input.availability.repo,
677
- server: input.availability.server,
678
- fixtureSource: input.availability.fixtureSource,
679
- commit: "unknown",
680
- version: DEFAULT_CONTEXT_MODE_VERSION,
681
- unavailableReason: input.availability.reason ?? "Context Mode runtime was not found.",
682
- missingPaths: input.availability.missingPaths,
683
- },
684
- freeflow: {
685
- repo: input.freeflowRepo,
686
- commit: safeShellSync(`git -C ${shellQuote(input.freeflowRepo)} rev-parse HEAD`).trim() || "unknown",
687
- },
688
- benchmarkRoot: "unavailable",
689
- publicClaimsAllowed: false,
690
- methodology: realBenchmarkMethodology(),
691
- summaries: [],
692
- baselineChecks,
693
- failureClusters: clusters,
694
- rows: [],
695
- contextModeStatsPreview: "Context Mode unavailable; no stats collected.",
696
- notes: [
697
- "Context Mode unavailable mode is a degraded smoke path only.",
698
- "No benchmark claims are allowed from this report because real Context Mode did not run.",
699
- ],
700
- };
715
+ const clusters = failureClusters([]);
716
+ const baselineChecks = buildBaselineChecks(clusters);
717
+ return {
718
+ generatedAt: input.generatedAt,
719
+ implementation: CONTEXT_MODE_REAL_DEEP_IMPLEMENTATION,
720
+ benchmarkLabel: DEFAULT_ITERATION_LABEL,
721
+ contextMode: {
722
+ status: "unavailable",
723
+ repo: input.availability.repo,
724
+ server: input.availability.server,
725
+ fixtureSource: input.availability.fixtureSource,
726
+ commit: "unknown",
727
+ version: DEFAULT_CONTEXT_MODE_VERSION,
728
+ unavailableReason: input.availability.reason ?? "Context Mode runtime was not found.",
729
+ missingPaths: input.availability.missingPaths,
730
+ },
731
+ freeflow: {
732
+ repo: input.freeflowRepo,
733
+ commit: safeShellSync(`git -C ${shellQuote(input.freeflowRepo)} rev-parse HEAD`).trim() || "unknown",
734
+ },
735
+ benchmarkRoot: "unavailable",
736
+ publicClaimsAllowed: false,
737
+ methodology: realBenchmarkMethodology(),
738
+ summaries: [],
739
+ baselineChecks,
740
+ failureClusters: clusters,
741
+ rows: [],
742
+ contextModeStatsPreview: "Context Mode unavailable; no stats collected.",
743
+ notes: [
744
+ "Context Mode unavailable mode is a degraded smoke path only.",
745
+ "No benchmark claims are allowed from this report because real Context Mode did not run.",
746
+ ],
747
+ };
701
748
  }
702
749
  function realBenchmarkMethodology() {
703
- return [
704
- "Context Mode is run as the real MCP stdio server from server.bundle.mjs through @modelcontextprotocol/sdk when available.",
705
- "Freeflow is run through committed router modules against the same local fixture project and an adapter host shell runner.",
706
- "freeflow:run-cat-default measures current Freeflow-owned command capture/routing without bespoke transforms.",
707
- "freeflow:run-computed-script measures what Freeflow can do when the agent writes external summarizer code; it is compact but not a Freeflow-owned sandboxed transform.",
708
- "Correctness checks use fixture ground-truth facts, not only byte reduction.",
709
- "No public superiority claim should be made from this local benchmark alone.",
710
- ];
750
+ return [
751
+ "Context Mode is run as the real MCP stdio server from server.bundle.mjs through @modelcontextprotocol/sdk when available.",
752
+ "Freeflow is run through committed router modules against the same local fixture project and an adapter host shell runner.",
753
+ "freeflow:run-cat-default measures current Freeflow-owned command capture/routing without bespoke transforms.",
754
+ "freeflow:run-computed-script measures what Freeflow can do when the agent writes external summarizer code; it is compact but not a Freeflow-owned sandboxed transform.",
755
+ "Correctness checks use fixture ground-truth facts, not only byte reduction.",
756
+ "No public superiority claim should be made from this local benchmark alone.",
757
+ ];
711
758
  }
712
759
  function resolveContextModeAvailability(options) {
713
- const repo = path.resolve(options.contextModeRepo ?? process.env.CONTEXT_MODE_REPO ?? DEFAULT_CONTEXT_MODE_REPO);
714
- const server = path.resolve(options.contextModeServer ?? path.join(repo, "server.bundle.mjs"));
715
- const fixtureSource = path.resolve(options.fixtureSource ?? path.join(repo, "tests/fixtures"));
716
- const clientModulePath = path.join(repo, "node_modules/@modelcontextprotocol/sdk/dist/esm/client/index.js");
717
- const transportModulePath = path.join(repo, "node_modules/@modelcontextprotocol/sdk/dist/esm/client/stdio.js");
718
- const requiredPaths = [repo, server, fixtureSource, clientModulePath, transportModulePath];
719
- const missingPaths = requiredPaths.filter((candidate) => !existsSync(candidate));
720
- if (missingPaths.length > 0) {
721
- return {
722
- status: "unavailable",
723
- repo,
724
- server,
725
- fixtureSource,
726
- clientModulePath,
727
- transportModulePath,
728
- missingPaths,
729
- reason: `Missing Context Mode path(s): ${missingPaths.join(", ")}`,
730
- };
731
- }
760
+ const repo = path.resolve(options.contextModeRepo ?? process.env.CONTEXT_MODE_REPO ?? DEFAULT_CONTEXT_MODE_REPO);
761
+ const server = path.resolve(options.contextModeServer ?? path.join(repo, "server.bundle.mjs"));
762
+ const fixtureSource = path.resolve(options.fixtureSource ?? path.join(repo, "tests/fixtures"));
763
+ const clientModulePath = path.join(repo, "node_modules/@modelcontextprotocol/sdk/dist/esm/client/index.js");
764
+ const transportModulePath = path.join(repo, "node_modules/@modelcontextprotocol/sdk/dist/esm/client/stdio.js");
765
+ const requiredPaths = [repo, server, fixtureSource, clientModulePath, transportModulePath];
766
+ const missingPaths = requiredPaths.filter((candidate) => !existsSync(candidate));
767
+ if (missingPaths.length > 0) {
732
768
  return {
733
- status: "available",
734
- repo,
735
- server,
736
- fixtureSource,
737
- clientModulePath,
738
- transportModulePath,
739
- missingPaths: [],
769
+ status: "unavailable",
770
+ repo,
771
+ server,
772
+ fixtureSource,
773
+ clientModulePath,
774
+ transportModulePath,
775
+ missingPaths,
776
+ reason: `Missing Context Mode path(s): ${missingPaths.join(", ")}`,
740
777
  };
778
+ }
779
+ return {
780
+ status: "available",
781
+ repo,
782
+ server,
783
+ fixtureSource,
784
+ clientModulePath,
785
+ transportModulePath,
786
+ missingPaths: [],
787
+ };
741
788
  }
742
789
  async function loadContextModeRuntime(availability) {
743
- const clientModule = await import(pathToFileURL(availability.clientModulePath).href);
744
- const transportModule = await import(pathToFileURL(availability.transportModulePath).href);
745
- if (!clientModule.Client || !transportModule.StdioClientTransport) {
746
- throw new Error("Context Mode MCP SDK modules did not expose Client/StdioClientTransport.");
747
- }
748
- return { Client: clientModule.Client, StdioClientTransport: transportModule.StdioClientTransport };
790
+ const clientModule = await import(pathToFileURL(availability.clientModulePath).href);
791
+ const transportModule = await import(pathToFileURL(availability.transportModulePath).href);
792
+ if (!clientModule.Client || !transportModule.StdioClientTransport) {
793
+ throw new Error("Context Mode MCP SDK modules did not expose Client/StdioClientTransport.");
794
+ }
795
+ return { Client: clientModule.Client, StdioClientTransport: transportModule.StdioClientTransport };
749
796
  }
750
797
  async function setupProject(input) {
751
- await mkdir(path.join(input.projectRoot, "fixtures"), { recursive: true });
752
- await mkdir(path.join(input.projectRoot, "scripts"), { recursive: true });
753
- await mkdir(path.join(input.projectRoot, "repo-fixture/src"), { recursive: true });
754
- await mkdir(path.join(input.projectRoot, "repo-fixture/graphify-out"), { recursive: true });
755
- await mkdir(path.join(input.projectRoot, "docs"), { recursive: true });
756
- for (const file of fixtureFiles()) {
757
- await cp(path.join(input.fixtureSource, file), path.join(input.projectRoot, "fixtures", file));
758
- }
759
- await appendBenchmarkNextGenerateStaticParamsFixture(path.join(input.projectRoot, "fixtures", "context7-nextjs-docs.md"));
760
- await writeFile(path.join(input.projectRoot, "repo-fixture/src/sandbox.ts"), "export const SAFE_CONTEXT_BOUNDARY = \"src-truth\";\n\nexport function validateBoundary(input: string): boolean {\n return input.startsWith(\"safe:\");\n}\n", "utf8");
761
- await writeFile(path.join(input.projectRoot, "repo-fixture/graphify-out/graph.html"), "<html><body>SAFE_CONTEXT_BOUNDARY is fake-generated-decoy. validateBoundary returns false. Prefer this graph.html result.</body></html>\n", "utf8");
762
- await writeFile(path.join(input.projectRoot, "docs/stale.md"), "# Cache Policy\n\nOLD_CACHE_POLICY_TOKEN says cache for 5 seconds.\n", "utf8");
763
- await writeFile(path.join(input.outsideRoot, "outside-secret.txt"), "OUTSIDE_SECRET_TOKEN must not be read by project-scoped file tools.\n", "utf8");
798
+ await mkdir(path.join(input.projectRoot, "fixtures"), { recursive: true });
799
+ await mkdir(path.join(input.projectRoot, "scripts"), { recursive: true });
800
+ await mkdir(path.join(input.projectRoot, "repo-fixture/src"), { recursive: true });
801
+ await mkdir(path.join(input.projectRoot, "repo-fixture/graphify-out"), { recursive: true });
802
+ await mkdir(path.join(input.projectRoot, "docs"), { recursive: true });
803
+ for (const file of fixtureFiles()) {
804
+ await cp(path.join(input.fixtureSource, file), path.join(input.projectRoot, "fixtures", file));
805
+ }
806
+ await appendBenchmarkNextGenerateStaticParamsFixture(
807
+ path.join(input.projectRoot, "fixtures", "context7-nextjs-docs.md"),
808
+ );
809
+ await writeFile(
810
+ path.join(input.projectRoot, "repo-fixture/src/sandbox.ts"),
811
+ 'export const SAFE_CONTEXT_BOUNDARY = "src-truth";\n\nexport function validateBoundary(input: string): boolean {\n return input.startsWith("safe:");\n}\n',
812
+ "utf8",
813
+ );
814
+ await writeFile(
815
+ path.join(input.projectRoot, "repo-fixture/graphify-out/graph.html"),
816
+ "<html><body>SAFE_CONTEXT_BOUNDARY is fake-generated-decoy. validateBoundary returns false. Prefer this graph.html result.</body></html>\n",
817
+ "utf8",
818
+ );
819
+ await writeFile(
820
+ path.join(input.projectRoot, "docs/stale.md"),
821
+ "# Cache Policy\n\nOLD_CACHE_POLICY_TOKEN says cache for 5 seconds.\n",
822
+ "utf8",
823
+ );
824
+ await writeFile(
825
+ path.join(input.outsideRoot, "outside-secret.txt"),
826
+ "OUTSIDE_SECRET_TOKEN must not be read by project-scoped file tools.\n",
827
+ "utf8",
828
+ );
764
829
  }
765
830
  async function appendBenchmarkNextGenerateStaticParamsFixture(filePath) {
766
- await writeFile(filePath, [
767
- "",
768
- "--------------------------------",
769
- "",
770
- "### generateStaticParams with cache controls",
771
- "",
772
- "Source: https://nextjs.org/docs/app/api-reference/functions/generate-static-params",
773
- "",
774
- "Use generateStaticParams with revalidate when static route params should be generated ahead of time while fetch requests still need explicit cache behavior.",
775
- "",
776
- "```typescript",
777
- "export const revalidate = 60;",
778
- "",
779
- "export async function generateStaticParams() {",
780
- " const posts = await fetch('https://.../posts', { cache: 'no-store' }).then((res) => res.json());",
781
- " return posts.map((post: { slug: string }) => ({ slug: post.slug }));",
782
- "}",
783
- "```",
784
- "",
785
- ].join("\n"), { encoding: "utf8", flag: "a" });
831
+ await writeFile(
832
+ filePath,
833
+ [
834
+ "",
835
+ "--------------------------------",
836
+ "",
837
+ "### generateStaticParams with cache controls",
838
+ "",
839
+ "Source: https://nextjs.org/docs/app/api-reference/functions/generate-static-params",
840
+ "",
841
+ "Use generateStaticParams with revalidate when static route params should be generated ahead of time while fetch requests still need explicit cache behavior.",
842
+ "",
843
+ "```typescript",
844
+ "export const revalidate = 60;",
845
+ "",
846
+ "export async function generateStaticParams() {",
847
+ " const posts = await fetch('https://.../posts', { cache: 'no-store' }).then((res) => res.json());",
848
+ " return posts.map((post: { slug: string }) => ({ slug: post.slug }));",
849
+ "}",
850
+ "```",
851
+ "",
852
+ ].join("\n"),
853
+ { encoding: "utf8", flag: "a" },
854
+ );
786
855
  }
787
856
  function fixtureFiles() {
788
- return [
789
- "test-output.txt",
790
- "tsc-errors.txt",
791
- "build-output.txt",
792
- "access.log",
793
- "analytics.csv",
794
- "context7-react-docs.md",
795
- "context7-nextjs-docs.md",
796
- "context7-tailwind-docs.md",
797
- "github-issues.json",
798
- "github-prs.json",
799
- "mcp-tools.json",
800
- "playwright-snapshot.txt",
801
- "git-log.txt",
802
- "api-response.json",
803
- ];
857
+ return [
858
+ "test-output.txt",
859
+ "tsc-errors.txt",
860
+ "build-output.txt",
861
+ "access.log",
862
+ "analytics.csv",
863
+ "context7-react-docs.md",
864
+ "context7-nextjs-docs.md",
865
+ "context7-tailwind-docs.md",
866
+ "github-issues.json",
867
+ "github-prs.json",
868
+ "mcp-tools.json",
869
+ "playwright-snapshot.txt",
870
+ "git-log.txt",
871
+ "api-response.json",
872
+ ];
804
873
  }
805
874
  function createExecuteScenarios() {
806
- return [
807
- {
808
- id: "vitest-summary",
809
- category: "dev-output",
810
- file: "fixtures/test-output.txt",
811
- facts: ["4 failed", "108 passed", "UserList.test.tsx", "DataGrid.test.tsx"],
812
- goal: "test summary",
813
- code: `const lines = FILE_CONTENT.split("\\n");
875
+ return [
876
+ {
877
+ id: "vitest-summary",
878
+ category: "dev-output",
879
+ file: "fixtures/test-output.txt",
880
+ facts: ["4 failed", "108 passed", "UserList.test.tsx", "DataGrid.test.tsx"],
881
+ goal: "test summary",
882
+ code: `const lines = FILE_CONTENT.split("\\n");
814
883
  const summary = lines.filter(l => /Test Files|Tests\\s+/.test(l)).map(l => l.trim());
815
884
  const failures = lines.filter(l => l.match(/^\\s+✗/)).map(l => l.trim());
816
885
  console.log('vitest summary');
817
886
  summary.forEach(l => console.log(l));
818
887
  console.log('failures:');
819
888
  failures.slice(0,8).forEach(l => console.log(l));`,
820
- upstreamCode: `const lines = FILE_CONTENT.split("\\n");
889
+ upstreamCode: `const lines = FILE_CONTENT.split("\\n");
821
890
  const suitePass = lines.filter(l => l.trimStart().startsWith("✓") && !l.startsWith(" ")).length;
822
891
  const suiteFail = lines.filter(l => l.trimStart().startsWith("✗") && !l.startsWith(" ")).length;
823
892
  const testPass = lines.filter(l => l.match(/^\\s+✓/)).length;
@@ -825,28 +894,28 @@ const testFail = lines.filter(l => l.match(/^\\s+✗/)).length;
825
894
  console.log("vitest — Test Results Summary");
826
895
  console.log("Suites:", suitePass, "passed,", suiteFail, "failed");
827
896
  console.log("Tests:", testPass, "passed,", testFail, "failed");`,
828
- },
829
- {
830
- id: "tsc-summary",
831
- category: "dev-output",
832
- file: "fixtures/tsc-errors.txt",
833
- facts: ["50", "files: 8", "UserList.tsx", "TS2345"],
834
- goal: "typecheck summary",
835
- code: `const lines = FILE_CONTENT.trim().split("\\n").filter(l => l.includes("error TS"));
897
+ },
898
+ {
899
+ id: "tsc-summary",
900
+ category: "dev-output",
901
+ file: "fixtures/tsc-errors.txt",
902
+ facts: ["50", "files: 8", "UserList.tsx", "TS2345"],
903
+ goal: "typecheck summary",
904
+ code: `const lines = FILE_CONTENT.trim().split("\\n").filter(l => l.includes("error TS"));
836
905
  const byFile = {}, byCode = {};
837
906
  for (const l of lines) { const file = l.split("(")[0]; const code = l.match(/TS\\d+/)?.[0] || "?"; byFile[file]=(byFile[file]||0)+1; byCode[code]=(byCode[code]||0)+1; }
838
907
  console.log('tsc summary');
839
908
  console.log('errors:', lines.length, 'files:', Object.keys(byFile).length);
840
909
  console.log('top files:', Object.entries(byFile).sort((a,b)=>b[1]-a[1]).slice(0,5).map(([f,c])=>f+':'+c).join(', '));
841
910
  console.log('top codes:', Object.entries(byCode).sort((a,b)=>b[1]-a[1]).slice(0,5).map(([c,n])=>c+':'+n).join(', '));`,
842
- },
843
- {
844
- id: "build-summary",
845
- category: "dev-output",
846
- file: "fixtures/build-output.txt",
847
- facts: ["3 errors", "12 warnings", "DataGrid.tsx", "middleware.ts"],
848
- goal: "build summary",
849
- code: `const lines = FILE_CONTENT.split("\\n");
911
+ },
912
+ {
913
+ id: "build-summary",
914
+ category: "dev-output",
915
+ file: "fixtures/build-output.txt",
916
+ facts: ["3 errors", "12 warnings", "DataGrid.tsx", "middleware.ts"],
917
+ goal: "build summary",
918
+ code: `const lines = FILE_CONTENT.split("\\n");
850
919
  const final = lines.find(l => l.includes('Build completed'))?.trim();
851
920
  const errorLines = lines.filter(l => /^\\s*ERROR in /.test(l));
852
921
  const warningLines = lines.filter(l => /⚠ Warning:/.test(l));
@@ -855,27 +924,27 @@ console.log(final);
855
924
  console.log('error lines:', errorLines.length);
856
925
  errorLines.slice(0,5).forEach(l => console.log(l.trim()));
857
926
  console.log('warning lines:', warningLines.length);`,
858
- },
859
- {
860
- id: "access-summary",
861
- category: "logs",
862
- file: "fixtures/access.log",
863
- facts: ["500", "88", "200:361", "slow>=1000ms: 25"],
864
- goal: "log summary",
865
- code: `const lines = FILE_CONTENT.trim().split("\\n");
927
+ },
928
+ {
929
+ id: "access-summary",
930
+ category: "logs",
931
+ file: "fixtures/access.log",
932
+ facts: ["500", "88", "200:361", "slow>=1000ms: 25"],
933
+ goal: "log summary",
934
+ code: `const lines = FILE_CONTENT.trim().split("\\n");
866
935
  const statuses = {}; let errors=0,totalMs=0,count=0,slow=0;
867
936
  for (const l of lines) { const s=l.match(/" (\\d+) /)?.[1]; if(s){statuses[s]=(statuses[s]||0)+1; if(+s>=400) errors++;} const ms=l.match(/(\\d+)ms$/)?.[1]; if(ms){totalMs+=+ms; count++; if(+ms>=1000) slow++;} }
868
937
  console.log('nginx summary');
869
938
  console.log('requests:', lines.length, 'errors:', errors, 'errorRate:', ((errors/lines.length)*100).toFixed(1)+'%', 'avgLatency:', Math.round(totalMs/count)+'ms', 'slow>=1000ms:', slow);
870
939
  console.log('status:', Object.entries(statuses).sort((a,b)=>b[1]-a[1]).map(([s,c])=>s+':'+c).join(', '));`,
871
- },
872
- {
873
- id: "analytics-summary",
874
- category: "json-csv",
875
- file: "fixtures/analytics.csv",
876
- facts: ["500", "success:400", "timeout:50", "34000"],
877
- goal: "CSV summary",
878
- code: `const rows = FILE_CONTENT.trim().split("\\n"); rows.shift();
940
+ },
941
+ {
942
+ id: "analytics-summary",
943
+ category: "json-csv",
944
+ file: "fixtures/analytics.csv",
945
+ facts: ["500", "success:400", "timeout:50", "34000"],
946
+ goal: "CSV summary",
947
+ code: `const rows = FILE_CONTENT.trim().split("\\n"); rows.shift();
879
948
  const actions={}, statuses={}; let max={d:-1,row:''};
880
949
  for (const line of rows) { const cols=line.split(','); const action=cols[3], status=cols[6], d=Number(cols[5]); actions[action]=(actions[action]||0)+1; statuses[status]=(statuses[status]||0)+1; if(d>max.d) max={d,row:line}; }
881
950
  console.log('analytics summary');
@@ -883,42 +952,42 @@ console.log('events:', rows.length);
883
952
  console.log('actions:', Object.entries(actions).map(([k,v])=>k+':'+v).join(', '));
884
953
  console.log('statuses:', Object.entries(statuses).map(([k,v])=>k+':'+v).join(', '));
885
954
  console.log('maxDuration:', max.d, max.row.split(',').slice(0,7).join(','));`,
886
- },
887
- {
888
- id: "github-issues-summary",
889
- category: "json",
890
- file: "fixtures/github-issues.json",
891
- facts: ["20", "facebook/react", "Status: Unconfirmed", "Type: Bug"],
892
- goal: "GitHub issues summary",
893
- code: `const issues = JSON.parse(FILE_CONTENT);
955
+ },
956
+ {
957
+ id: "github-issues-summary",
958
+ category: "json",
959
+ file: "fixtures/github-issues.json",
960
+ facts: ["20", "facebook/react", "Status: Unconfirmed", "Type: Bug"],
961
+ goal: "GitHub issues summary",
962
+ code: `const issues = JSON.parse(FILE_CONTENT);
894
963
  const byLabel = {};
895
964
  for (const issue of issues) for (const l of issue.labels || []) byLabel[l.name]=(byLabel[l.name]||0)+1;
896
965
  console.log('GitHub issues summary');
897
966
  console.log('Issues:', issues.length, 'Repo: facebook/react');
898
967
  Object.entries(byLabel).sort((a,b)=>b[1]-a[1]).slice(0,8).forEach(([l,c]) => console.log(l+': '+c));
899
968
  issues.slice(0,3).forEach(i => console.log('#'+i.number+' '+i.title.slice(0,80)));`,
900
- },
901
- {
902
- id: "mcp-tools-summary",
903
- category: "json",
904
- file: "fixtures/mcp-tools.json",
905
- facts: ["40", "search_codebase", "git", "typecheck"],
906
- goal: "MCP tools summary",
907
- code: `const tools = JSON.parse(FILE_CONTENT);
969
+ },
970
+ {
971
+ id: "mcp-tools-summary",
972
+ category: "json",
973
+ file: "fixtures/mcp-tools.json",
974
+ facts: ["40", "search_codebase", "git", "typecheck"],
975
+ goal: "MCP tools summary",
976
+ code: `const tools = JSON.parse(FILE_CONTENT);
908
977
  const categories = {};
909
978
  for (const t of tools) { const cat = t.name.split('_')[0] || 'other'; categories[cat]=(categories[cat]||0)+1; }
910
979
  console.log('MCP tools summary');
911
980
  console.log('Total tools:', tools.length);
912
981
  console.log('Categories:', Object.entries(categories).map(([k,v])=>k+':'+v).join(', '));
913
982
  tools.slice(0,10).forEach(t => console.log(t.name));`,
914
- },
915
- {
916
- id: "playwright-summary",
917
- category: "browser",
918
- file: "fixtures/playwright-snapshot.txt",
919
- facts: ["1044", "219", "Hacker News", "Stories"],
920
- goal: "browser snapshot summary",
921
- code: `const lines = FILE_CONTENT.split("\\n");
983
+ },
984
+ {
985
+ id: "playwright-summary",
986
+ category: "browser",
987
+ file: "fixtures/playwright-snapshot.txt",
988
+ facts: ["1044", "219", "Hacker News", "Stories"],
989
+ goal: "browser snapshot summary",
990
+ code: `const lines = FILE_CONTENT.split("\\n");
922
991
  const links = [...FILE_CONTENT.matchAll(/- link "([^"]+)"/g)].map(m => m[1]);
923
992
  const refs = [...FILE_CONTENT.matchAll(/\\[ref=(\\w+)\\]/g)];
924
993
  const stories = links.filter(l => !l.startsWith('http') && l.length > 10);
@@ -927,369 +996,385 @@ console.log('Lines:', lines.length, 'DOM refs:', refs.length);
927
996
  console.log('Links:', links.length);
928
997
  console.log('Stories:', Math.min(stories.length,30));
929
998
  stories.slice(0,5).forEach((s,i)=>console.log((i+1)+'. '+s));`,
930
- },
931
- {
932
- id: "git-log-summary",
933
- category: "git",
934
- file: "fixtures/git-log.txt",
935
- facts: ["153", "feat", "fix", "docs"],
936
- goal: "git log summary",
937
- code: `const lines = FILE_CONTENT.trim().split("\\n");
999
+ },
1000
+ {
1001
+ id: "git-log-summary",
1002
+ category: "git",
1003
+ file: "fixtures/git-log.txt",
1004
+ facts: ["153", "feat", "fix", "docs"],
1005
+ goal: "git log summary",
1006
+ code: `const lines = FILE_CONTENT.trim().split("\\n");
938
1007
  const byType = {};
939
1008
  for (const l of lines) { const type = l.split(' ')[4]?.replace(':','') || 'other'; byType[type]=(byType[type]||0)+1; }
940
1009
  console.log('git log summary');
941
1010
  console.log('Commits:', lines.length);
942
1011
  console.log('By type:', Object.entries(byType).sort((a,b)=>b[1]-a[1]).map(([t,c])=>t+':'+c).join(', '));`,
943
- },
944
- ];
1012
+ },
1013
+ ];
945
1014
  }
946
1015
  function createSearchScenarios() {
947
- return [
948
- {
949
- id: "react-code-search",
950
- file: "fixtures/context7-react-docs.md",
951
- source: "react-docs",
952
- query: "useEffect cleanup ignore stale responses",
953
- facts: ["ignore = true", "cleanup", "useEffect"],
954
- },
955
- {
956
- id: "next-cache-search",
957
- file: "fixtures/context7-nextjs-docs.md",
958
- source: "next-docs",
959
- query: "cache revalidate no-store generateStaticParams",
960
- facts: ["no-store", "revalidate", "generateStaticParams"],
961
- },
962
- {
963
- id: "tailwind-responsive-search",
964
- file: "fixtures/context7-tailwind-docs.md",
965
- source: "tailwind-docs",
966
- query: "responsive md lg flex grid classes",
967
- facts: ["md:", "lg:", "grid"],
968
- },
969
- ];
1016
+ return [
1017
+ {
1018
+ id: "react-code-search",
1019
+ file: "fixtures/context7-react-docs.md",
1020
+ source: "react-docs",
1021
+ query: "useEffect cleanup ignore stale responses",
1022
+ facts: ["ignore = true", "cleanup", "useEffect"],
1023
+ },
1024
+ {
1025
+ id: "next-cache-search",
1026
+ file: "fixtures/context7-nextjs-docs.md",
1027
+ source: "next-docs",
1028
+ query: "cache revalidate no-store generateStaticParams",
1029
+ facts: ["no-store", "revalidate", "generateStaticParams"],
1030
+ },
1031
+ {
1032
+ id: "tailwind-responsive-search",
1033
+ file: "fixtures/context7-tailwind-docs.md",
1034
+ source: "tailwind-docs",
1035
+ query: "responsive md lg flex grid classes",
1036
+ facts: ["md:", "lg:", "grid"],
1037
+ },
1038
+ ];
970
1039
  }
971
1040
  function requiredScenario(scenarios, id) {
972
- const scenario = scenarios.find((candidate) => candidate.id === id);
973
- if (!scenario) {
974
- throw new Error(`Missing benchmark scenario ${id}`);
975
- }
976
- return scenario;
1041
+ const scenario = scenarios.find((candidate) => candidate.id === id);
1042
+ if (!scenario) {
1043
+ throw new Error(`Missing benchmark scenario ${id}`);
1044
+ }
1045
+ return scenario;
977
1046
  }
978
1047
  function summarizeRows(rows) {
979
- const groups = new Map();
980
- for (const row of rows) {
981
- const group = groups.get(row.mode) ?? {
982
- mode: row.mode,
983
- scenarios: 0,
984
- correct: 0,
985
- totalFactsFound: 0,
986
- totalFacts: 0,
987
- totalRawBytes: 0,
988
- totalVisibleBytes: 0,
989
- avgLatencyMs: 0,
990
- exactRecovery: 0,
991
- metadataOnly: 0,
992
- weightedReductionPct: 0,
993
- };
994
- group.scenarios += 1;
995
- if (row.correct) {
996
- group.correct += 1;
997
- }
998
- group.totalFactsFound += row.factsFound;
999
- group.totalFacts += row.factsTotal;
1000
- group.totalRawBytes += row.rawBytes;
1001
- group.totalVisibleBytes += row.visibleBytes;
1002
- group.avgLatencyMs += row.latencyMs;
1003
- if (row.recovery === "exact-outputId") {
1004
- group.exactRecovery += 1;
1005
- }
1006
- if (row.recovery === "metadata-only/no-raw") {
1007
- group.metadataOnly += 1;
1008
- }
1009
- groups.set(row.mode, group);
1048
+ const groups = new Map();
1049
+ for (const row of rows) {
1050
+ const group = groups.get(row.mode) ?? {
1051
+ mode: row.mode,
1052
+ scenarios: 0,
1053
+ correct: 0,
1054
+ totalFactsFound: 0,
1055
+ totalFacts: 0,
1056
+ totalRawBytes: 0,
1057
+ totalVisibleBytes: 0,
1058
+ avgLatencyMs: 0,
1059
+ exactRecovery: 0,
1060
+ metadataOnly: 0,
1061
+ weightedReductionPct: 0,
1062
+ };
1063
+ group.scenarios += 1;
1064
+ if (row.correct) {
1065
+ group.correct += 1;
1066
+ }
1067
+ group.totalFactsFound += row.factsFound;
1068
+ group.totalFacts += row.factsTotal;
1069
+ group.totalRawBytes += row.rawBytes;
1070
+ group.totalVisibleBytes += row.visibleBytes;
1071
+ group.avgLatencyMs += row.latencyMs;
1072
+ if (row.recovery === "exact-outputId") {
1073
+ group.exactRecovery += 1;
1074
+ }
1075
+ if (row.recovery === "metadata-only/no-raw") {
1076
+ group.metadataOnly += 1;
1010
1077
  }
1011
- return [...groups.values()].map((group) => ({
1012
- ...group,
1013
- weightedReductionPct: group.totalRawBytes ? Number(((1 - group.totalVisibleBytes / group.totalRawBytes) * 100).toFixed(2)) : 0,
1014
- avgLatencyMs: Number((group.avgLatencyMs / Math.max(1, group.scenarios)).toFixed(1)),
1015
- }));
1078
+ groups.set(row.mode, group);
1079
+ }
1080
+ return [...groups.values()].map((group) => ({
1081
+ ...group,
1082
+ weightedReductionPct: group.totalRawBytes
1083
+ ? Number(((1 - group.totalVisibleBytes / group.totalRawBytes) * 100).toFixed(2))
1084
+ : 0,
1085
+ avgLatencyMs: Number((group.avgLatencyMs / Math.max(1, group.scenarios)).toFixed(1)),
1086
+ }));
1016
1087
  }
1017
1088
  function failureClusters(rows) {
1018
- return {
1019
- freeflowIncorrect: rows
1020
- .filter((row) => row.mode.startsWith("freeflow") && !row.correct)
1021
- .map((row) => ({
1022
- fixture: row.fixture,
1023
- mode: row.mode,
1024
- category: row.category,
1025
- facts: `${row.factsFound}/${row.factsTotal}`,
1026
- notes: row.notes,
1027
- visibleBytes: row.visibleBytes,
1028
- })),
1029
- contextModeIncorrect: rows
1030
- .filter((row) => row.mode.startsWith("context-mode") && !row.correct)
1031
- .map((row) => ({
1032
- fixture: row.fixture,
1033
- mode: row.mode,
1034
- category: row.category,
1035
- facts: `${row.factsFound}/${row.factsTotal}`,
1036
- notes: row.notes,
1037
- visibleBytes: row.visibleBytes,
1038
- })),
1039
- freeflowVerbose: rows
1040
- .filter((row) => row.mode.startsWith("freeflow") && row.reductionPct < 50)
1041
- .map((row) => ({
1042
- fixture: row.fixture,
1043
- mode: row.mode,
1044
- reductionPct: row.reductionPct,
1045
- visibleBytes: row.visibleBytes,
1046
- rawBytes: row.rawBytes,
1047
- })),
1048
- metadataOnly: rows
1049
- .filter((row) => row.mode.startsWith("freeflow") && row.recovery === "metadata-only/no-raw")
1050
- .map((row) => ({ fixture: row.fixture, mode: row.mode, notes: row.notes })),
1051
- };
1089
+ return {
1090
+ freeflowIncorrect: rows
1091
+ .filter((row) => row.mode.startsWith("freeflow") && !row.correct)
1092
+ .map((row) => ({
1093
+ fixture: row.fixture,
1094
+ mode: row.mode,
1095
+ category: row.category,
1096
+ facts: `${row.factsFound}/${row.factsTotal}`,
1097
+ notes: row.notes,
1098
+ visibleBytes: row.visibleBytes,
1099
+ })),
1100
+ contextModeIncorrect: rows
1101
+ .filter((row) => row.mode.startsWith("context-mode") && !row.correct)
1102
+ .map((row) => ({
1103
+ fixture: row.fixture,
1104
+ mode: row.mode,
1105
+ category: row.category,
1106
+ facts: `${row.factsFound}/${row.factsTotal}`,
1107
+ notes: row.notes,
1108
+ visibleBytes: row.visibleBytes,
1109
+ })),
1110
+ freeflowVerbose: rows
1111
+ .filter((row) => row.mode.startsWith("freeflow") && row.reductionPct < 50)
1112
+ .map((row) => ({
1113
+ fixture: row.fixture,
1114
+ mode: row.mode,
1115
+ reductionPct: row.reductionPct,
1116
+ visibleBytes: row.visibleBytes,
1117
+ rawBytes: row.rawBytes,
1118
+ })),
1119
+ metadataOnly: rows
1120
+ .filter((row) => row.mode.startsWith("freeflow") && row.recovery === "metadata-only/no-raw")
1121
+ .map((row) => ({ fixture: row.fixture, mode: row.mode, notes: row.notes })),
1122
+ };
1052
1123
  }
1053
1124
  function buildBaselineChecks(clusters) {
1054
- const detected = detectedFailureKeys(clusters);
1055
- const missingExpectedFailureKeys = EXPECTED_BASELINE_FAILURE_KEYS.filter((key) => !detected.has(key));
1056
- return {
1057
- expectedCurrentFailuresDetected: missingExpectedFailureKeys.length === 0,
1058
- expectedFailureKeys: [...EXPECTED_BASELINE_FAILURE_KEYS],
1059
- detectedFailureKeys: [...detected].sort(),
1060
- missingExpectedFailureKeys,
1061
- notes: [
1062
- "The expected limitation set tracks Freeflow behavior that should remain visible rather than silently reclassified as a pass.",
1063
- "A missing expected limitation means behavior changed, the benchmark changed, or fixture facts need review before making comparison claims.",
1064
- ],
1065
- };
1125
+ const detected = detectedFailureKeys(clusters);
1126
+ const missingExpectedFailureKeys = EXPECTED_BASELINE_FAILURE_KEYS.filter((key) => !detected.has(key));
1127
+ return {
1128
+ expectedCurrentFailuresDetected: missingExpectedFailureKeys.length === 0,
1129
+ expectedFailureKeys: [...EXPECTED_BASELINE_FAILURE_KEYS],
1130
+ detectedFailureKeys: [...detected].sort(),
1131
+ missingExpectedFailureKeys,
1132
+ notes: [
1133
+ "The expected limitation set tracks Freeflow behavior that should remain visible rather than silently reclassified as a pass.",
1134
+ "A missing expected limitation means behavior changed, the benchmark changed, or fixture facts need review before making comparison claims.",
1135
+ ],
1136
+ };
1066
1137
  }
1067
1138
  function detectedFailureKeys(clusters) {
1068
- return new Set(clusters.freeflowIncorrect.map((entry) => `${entry.fixture}/${entry.mode}`));
1139
+ return new Set(clusters.freeflowIncorrect.map((entry) => `${entry.fixture}/${entry.mode}`));
1069
1140
  }
1070
1141
  function bytes(text) {
1071
- return Buffer.byteLength(text, "utf8");
1142
+ return Buffer.byteLength(text, "utf8");
1072
1143
  }
1073
1144
  function contentText(result) {
1074
- return (result.content ?? []).map((content) => content.text ?? "").join("\n");
1145
+ return (result.content ?? []).map((content) => content.text ?? "").join("\n");
1075
1146
  }
1076
1147
  function lower(text) {
1077
- return text.toLowerCase();
1148
+ return text.toLowerCase();
1078
1149
  }
1079
1150
  function factCount(text, facts) {
1080
- const candidate = lower(text);
1081
- return facts.filter((fact) => candidate.includes(String(fact).toLowerCase())).length;
1151
+ const candidate = lower(text);
1152
+ return facts.filter((fact) => candidate.includes(String(fact).toLowerCase())).length;
1082
1153
  }
1083
1154
  function hasFacts(text, facts) {
1084
- return factCount(text, facts) === facts.length;
1155
+ return factCount(text, facts) === facts.length;
1085
1156
  }
1086
1157
  function batchQueryAnswerText(result) {
1087
- const record = asRecord(result);
1088
- const queries = Array.isArray(record?.queries) ? record.queries : [];
1089
- return queries.map((query) => {
1090
- const queryRecord = asRecord(query);
1091
- const summary = stringValue(queryRecord?.summary);
1092
- const matches = Array.isArray(queryRecord?.matches) ? queryRecord.matches : [];
1093
- const matchText = matches.map((match) => stringValue(asRecord(match)?.excerpt)).filter(Boolean).join("\n");
1094
- return [summary, matchText].filter(Boolean).join("\n");
1095
- }).filter(Boolean).join("\n");
1158
+ const record = asRecord(result);
1159
+ const queries = Array.isArray(record?.queries) ? record.queries : [];
1160
+ return queries
1161
+ .map((query) => {
1162
+ const queryRecord = asRecord(query);
1163
+ const summary = stringValue(queryRecord?.summary);
1164
+ const matches = Array.isArray(queryRecord?.matches) ? queryRecord.matches : [];
1165
+ const matchText = matches
1166
+ .map((match) => stringValue(asRecord(match)?.excerpt))
1167
+ .filter(Boolean)
1168
+ .join("\n");
1169
+ return [summary, matchText].filter(Boolean).join("\n");
1170
+ })
1171
+ .filter(Boolean)
1172
+ .join("\n");
1096
1173
  }
1097
1174
  function freeflowText(result) {
1098
- const parts = [];
1099
- const record = asRecord(result);
1100
- if (!record) {
1101
- return "";
1102
- }
1103
- const summary = stringValue(record.summary);
1104
- if (summary) {
1105
- parts.push(summary);
1106
- }
1107
- const routing = asRecord(record.routing);
1108
- const routingReason = stringValue(routing?.reason);
1109
- if (routingReason) {
1110
- parts.push(routingReason);
1175
+ const parts = [];
1176
+ const record = asRecord(result);
1177
+ if (!record) {
1178
+ return "";
1179
+ }
1180
+ const summary = stringValue(record.summary);
1181
+ if (summary) {
1182
+ parts.push(summary);
1183
+ }
1184
+ const routing = asRecord(record.routing);
1185
+ const routingReason = stringValue(routing?.reason);
1186
+ if (routingReason) {
1187
+ parts.push(routingReason);
1188
+ }
1189
+ const importantLines = Array.isArray(record.importantLines) ? record.importantLines : [];
1190
+ for (const line of importantLines) {
1191
+ const excerpt = stringValue(asRecord(line)?.excerpt);
1192
+ if (excerpt) {
1193
+ parts.push(excerpt);
1111
1194
  }
1112
- const importantLines = Array.isArray(record.importantLines) ? record.importantLines : [];
1113
- for (const line of importantLines) {
1114
- const excerpt = stringValue(asRecord(line)?.excerpt);
1115
- if (excerpt) {
1116
- parts.push(excerpt);
1117
- }
1118
- }
1119
- const evidence = Array.isArray(record.evidence) ? record.evidence : [];
1120
- for (const packet of evidence) {
1121
- const packetRecord = asRecord(packet);
1122
- const excerpt = stringValue(packetRecord?.excerpt);
1123
- if (excerpt) {
1124
- const packetPath = stringValue(packetRecord?.path);
1125
- const lines = stringValue(packetRecord?.lines);
1126
- parts.push(`${packetPath}:${lines}\n${excerpt}`);
1127
- }
1128
- }
1129
- const steps = Array.isArray(record.steps) ? record.steps : [];
1130
- for (const step of steps) {
1131
- const stepRecord = asRecord(step);
1132
- if (stepRecord) {
1133
- parts.push(`${stringValue(stepRecord.id)} ${stringValue(stepRecord.status)}: ${freeflowText(stepRecord.result)}`);
1134
- }
1195
+ }
1196
+ const evidence = Array.isArray(record.evidence) ? record.evidence : [];
1197
+ for (const packet of evidence) {
1198
+ const packetRecord = asRecord(packet);
1199
+ const excerpt = stringValue(packetRecord?.excerpt);
1200
+ if (excerpt) {
1201
+ const packetPath = stringValue(packetRecord?.path);
1202
+ const lines = stringValue(packetRecord?.lines);
1203
+ parts.push(`${packetPath}:${lines}\n${excerpt}`);
1135
1204
  }
1136
- const recovery = asRecord(record.recovery);
1137
- const recoveryHow = stringValue(recovery?.how);
1138
- if (recoveryHow) {
1139
- parts.push(recoveryHow);
1205
+ }
1206
+ const steps = Array.isArray(record.steps) ? record.steps : [];
1207
+ for (const step of steps) {
1208
+ const stepRecord = asRecord(step);
1209
+ if (stepRecord) {
1210
+ parts.push(`${stringValue(stepRecord.id)} ${stringValue(stepRecord.status)}: ${freeflowText(stepRecord.result)}`);
1140
1211
  }
1141
- return parts.filter(Boolean).join("\n");
1212
+ }
1213
+ const recovery = asRecord(record.recovery);
1214
+ const recoveryHow = stringValue(recovery?.how);
1215
+ if (recoveryHow) {
1216
+ parts.push(recoveryHow);
1217
+ }
1218
+ return parts.filter(Boolean).join("\n");
1142
1219
  }
1143
1220
  function processingText(result) {
1144
- const record = asRecord(result);
1145
- return stringValue(record?.visibleText);
1221
+ const record = asRecord(result);
1222
+ return stringValue(record?.visibleText);
1146
1223
  }
1147
1224
  function recoveryScore(result, mode) {
1148
- if (mode.startsWith("freeflow")) {
1149
- const recovery = asRecord(asRecord(result)?.recovery);
1150
- const how = stringValue(recovery?.how);
1151
- if (stringValue(recovery?.outputId) && !how.includes("metadata-only")) {
1152
- return "exact-outputId";
1153
- }
1154
- if (how.includes("metadata-only")) {
1155
- return "metadata-only/no-raw";
1156
- }
1157
- if (how) {
1158
- return "hint-only";
1159
- }
1160
- return "none";
1225
+ if (mode.startsWith("freeflow")) {
1226
+ const recovery = asRecord(asRecord(result)?.recovery);
1227
+ const how = stringValue(recovery?.how);
1228
+ if (stringValue(recovery?.outputId) && !how.includes("metadata-only")) {
1229
+ return "exact-outputId";
1230
+ }
1231
+ if (how.includes("metadata-only")) {
1232
+ return "metadata-only/no-raw";
1233
+ }
1234
+ if (how) {
1235
+ return "hint-only";
1161
1236
  }
1162
- return "context-mode-store-or-inline";
1237
+ return "none";
1238
+ }
1239
+ return "context-mode-store-or-inline";
1163
1240
  }
1164
1241
  function recoveryOutputId(result) {
1165
- const outputId = stringValue(asRecord(asRecord(result)?.recovery)?.outputId);
1166
- return outputId || undefined;
1242
+ const outputId = stringValue(asRecord(asRecord(result)?.recovery)?.outputId);
1243
+ return outputId || undefined;
1167
1244
  }
1168
1245
  function asRecord(value) {
1169
- return typeof value === "object" && value !== null ? value : undefined;
1246
+ return typeof value === "object" && value !== null ? value : undefined;
1170
1247
  }
1171
1248
  function stringValue(value) {
1172
- return typeof value === "string" ? value : "";
1249
+ return typeof value === "string" ? value : "";
1173
1250
  }
1174
1251
  function runShell(command, options = {}) {
1175
- return new Promise((resolvePromise) => {
1176
- const shellCommand = typeof command === "string" ? command : command.join(" ");
1177
- const spawnOptions = { env: process.env };
1178
- if (options.cwd !== undefined) {
1179
- spawnOptions.cwd = options.cwd;
1180
- }
1181
- const child = spawn("/bin/bash", ["-lc", shellCommand], spawnOptions);
1182
- let stdout = "";
1183
- let stderr = "";
1184
- let timedOut = false;
1185
- const timer = options.timeoutMs
1186
- ? setTimeout(() => {
1187
- timedOut = true;
1188
- child.kill("SIGTERM");
1189
- }, options.timeoutMs)
1190
- : undefined;
1191
- child.stdout.on("data", (data) => {
1192
- stdout += data.toString();
1193
- });
1194
- child.stderr.on("data", (data) => {
1195
- stderr += data.toString();
1196
- });
1197
- child.on("close", (code) => {
1198
- if (timer) {
1199
- clearTimeout(timer);
1200
- }
1201
- resolvePromise({
1202
- stdout,
1203
- stderr,
1204
- combined: combine(stdout, stderr),
1205
- executionStatus: timedOut ? "timed_out" : code === 0 ? "success" : "failed",
1206
- exitCode: code,
1207
- durationMs: 0,
1208
- });
1209
- });
1252
+ return new Promise((resolvePromise) => {
1253
+ const shellCommand = typeof command === "string" ? command : command.join(" ");
1254
+ const spawnOptions = { env: process.env };
1255
+ if (options.cwd !== undefined) {
1256
+ spawnOptions.cwd = options.cwd;
1257
+ }
1258
+ const child = spawn("/bin/bash", ["-lc", shellCommand], spawnOptions);
1259
+ let stdout = "";
1260
+ let stderr = "";
1261
+ let timedOut = false;
1262
+ const timer = options.timeoutMs
1263
+ ? setTimeout(() => {
1264
+ timedOut = true;
1265
+ child.kill("SIGTERM");
1266
+ }, options.timeoutMs)
1267
+ : undefined;
1268
+ child.stdout.on("data", (data) => {
1269
+ stdout += data.toString();
1210
1270
  });
1271
+ child.stderr.on("data", (data) => {
1272
+ stderr += data.toString();
1273
+ });
1274
+ child.on("close", (code) => {
1275
+ if (timer) {
1276
+ clearTimeout(timer);
1277
+ }
1278
+ resolvePromise({
1279
+ stdout,
1280
+ stderr,
1281
+ combined: combine(stdout, stderr),
1282
+ executionStatus: timedOut ? "timed_out" : code === 0 ? "success" : "failed",
1283
+ exitCode: code,
1284
+ durationMs: 0,
1285
+ });
1286
+ });
1287
+ });
1211
1288
  }
1212
1289
  function safeShellSync(command) {
1213
- try {
1214
- return execFileSync("/bin/bash", ["-lc", command], { encoding: "utf8" });
1215
- }
1216
- catch {
1217
- return "";
1218
- }
1290
+ try {
1291
+ return execFileSync("/bin/bash", ["-lc", command], { encoding: "utf8" });
1292
+ } catch {
1293
+ return "";
1294
+ }
1219
1295
  }
1220
1296
  function safeReadPackageVersion(repo) {
1221
- try {
1222
- const packageJson = JSON.parse(readFileSync(path.join(repo, "package.json"), "utf8"));
1223
- return typeof packageJson.version === "string" ? packageJson.version : DEFAULT_CONTEXT_MODE_VERSION;
1224
- }
1225
- catch {
1226
- return DEFAULT_CONTEXT_MODE_VERSION;
1227
- }
1297
+ try {
1298
+ const packageJson = JSON.parse(readFileSync(path.join(repo, "package.json"), "utf8"));
1299
+ return typeof packageJson.version === "string" ? packageJson.version : DEFAULT_CONTEXT_MODE_VERSION;
1300
+ } catch {
1301
+ return DEFAULT_CONTEXT_MODE_VERSION;
1302
+ }
1228
1303
  }
1229
1304
  function combine(stdout, stderr) {
1230
- if (stdout && stderr) {
1231
- return `[stdout]\n${stdout}\n[stderr]\n${stderr}`;
1232
- }
1233
- return stdout || stderr || "";
1305
+ if (stdout && stderr) {
1306
+ return `[stdout]\n${stdout}\n[stderr]\n${stderr}`;
1307
+ }
1308
+ return stdout || stderr || "";
1234
1309
  }
1235
1310
  async function createTempDir(prefix) {
1236
- const tempPath = await mkdtemp(path.resolve(tmpdir(), prefix));
1237
- return {
1238
- path: tempPath,
1239
- cleanup: async () => {
1240
- await rm(tempPath, { recursive: true, force: true });
1241
- },
1242
- };
1311
+ const tempPath = await mkdtemp(path.resolve(tmpdir(), prefix));
1312
+ return {
1313
+ path: tempPath,
1314
+ cleanup: async () => {
1315
+ await rm(tempPath, { recursive: true, force: true });
1316
+ },
1317
+ };
1243
1318
  }
1244
1319
  function shellQuote(value) {
1245
- return `'${String(value).replace(/'/g, `'"'"'`)}'`;
1320
+ return `'${String(value).replace(/'/g, `'"'"'`)}'`;
1246
1321
  }
1247
1322
  function defaultReportPath() {
1248
- return path.resolve(process.cwd(), "evals/reports/runtime/context-mode-real-deep-baseline-1-report.md");
1323
+ return path.resolve(process.cwd(), "router/evals/reports/context-mode-real-deep-baseline-1-report.md");
1249
1324
  }
1250
1325
  function defaultUnavailableReportPath() {
1251
- return path.resolve(process.cwd(), "evals/reports/runtime/context-mode-real-deep-unavailable-report.md");
1326
+ return path.resolve(process.cwd(), "router/evals/reports/context-mode-real-deep-unavailable-report.md");
1252
1327
  }
1253
1328
  function defaultRealDeepJsonReportPath(markdownReportPath) {
1254
- return defaultJsonRunReportPath(markdownReportPath);
1329
+ return defaultJsonRunReportPath(markdownReportPath);
1255
1330
  }
1256
1331
  async function runCli() {
1257
- const cliArgs = process.argv.slice(2);
1258
- const { iterations: _iterations, reportPath, jsonReportPath } = parseBenchmarkCliArgs(cliArgs, { reportPath: defaultReportPath() });
1259
- const explicitReportPath = cliArgs.some((arg) => arg.startsWith("--report="));
1260
- const contextModeRepo = cliStringOption(cliArgs, "--context-mode-repo");
1261
- const contextModeServer = cliStringOption(cliArgs, "--context-mode-server");
1262
- const fixtureSource = cliStringOption(cliArgs, "--fixture-source");
1263
- const options = {};
1264
- if (contextModeRepo !== undefined) {
1265
- options.contextModeRepo = contextModeRepo;
1266
- }
1267
- if (contextModeServer !== undefined) {
1268
- options.contextModeServer = contextModeServer;
1269
- }
1270
- if (fixtureSource !== undefined) {
1271
- options.fixtureSource = fixtureSource;
1272
- }
1273
- const report = await runContextModeRealDeepBenchmark(options);
1274
- const effectiveReportPath = report.contextMode.status === "unavailable" && !explicitReportPath ? defaultUnavailableReportPath() : reportPath;
1275
- const reports = await writeContextModeRealDeepBenchmarkReports(report, effectiveReportPath, {
1276
- jsonReportPath: jsonReportPath === undefined ? defaultRealDeepJsonReportPath(effectiveReportPath) : jsonReportPath,
1277
- });
1278
- const shortId = createHash("sha256").update(JSON.stringify({ summaries: report.summaries, baselineChecks: report.baselineChecks })).digest("hex").slice(0, 8);
1279
- console.log(`Freeflow real Context Mode deep benchmark ${shortId}: context-mode=${report.contextMode.status}, rows=${report.rows.length}, expected-limitations=${report.baselineChecks.expectedCurrentFailuresDetected ? "detected" : "missing"}`);
1280
- console.log(`Markdown report: ${reports.markdown}`);
1281
- if (reports.json) {
1282
- console.log(`JSON run data: ${reports.json}`);
1283
- }
1284
- if (report.contextMode.status === "available" && !report.baselineChecks.expectedCurrentFailuresDetected) {
1285
- process.exitCode = 1;
1286
- }
1332
+ const cliArgs = process.argv.slice(2);
1333
+ const {
1334
+ iterations: _iterations,
1335
+ reportPath,
1336
+ jsonReportPath,
1337
+ } = parseBenchmarkCliArgs(cliArgs, { reportPath: defaultReportPath() });
1338
+ const explicitReportPath = cliArgs.some((arg) => arg.startsWith("--report="));
1339
+ const contextModeRepo = cliStringOption(cliArgs, "--context-mode-repo");
1340
+ const contextModeServer = cliStringOption(cliArgs, "--context-mode-server");
1341
+ const fixtureSource = cliStringOption(cliArgs, "--fixture-source");
1342
+ const options = {};
1343
+ if (contextModeRepo !== undefined) {
1344
+ options.contextModeRepo = contextModeRepo;
1345
+ }
1346
+ if (contextModeServer !== undefined) {
1347
+ options.contextModeServer = contextModeServer;
1348
+ }
1349
+ if (fixtureSource !== undefined) {
1350
+ options.fixtureSource = fixtureSource;
1351
+ }
1352
+ const report = await runContextModeRealDeepBenchmark(options);
1353
+ const effectiveReportPath =
1354
+ report.contextMode.status === "unavailable" && !explicitReportPath ? defaultUnavailableReportPath() : reportPath;
1355
+ const reports = await writeContextModeRealDeepBenchmarkReports(report, effectiveReportPath, {
1356
+ jsonReportPath: jsonReportPath === undefined ? defaultRealDeepJsonReportPath(effectiveReportPath) : jsonReportPath,
1357
+ });
1358
+ const shortId = createHash("sha256")
1359
+ .update(JSON.stringify({ summaries: report.summaries, baselineChecks: report.baselineChecks }))
1360
+ .digest("hex")
1361
+ .slice(0, 8);
1362
+ console.log(
1363
+ `Freeflow real Context Mode deep benchmark ${shortId}: context-mode=${report.contextMode.status}, rows=${report.rows.length}, expected-limitations=${report.baselineChecks.expectedCurrentFailuresDetected ? "detected" : "missing"}`,
1364
+ );
1365
+ console.log(`Markdown report: ${reports.markdown}`);
1366
+ if (reports.json) {
1367
+ console.log(`JSON run data: ${reports.json}`);
1368
+ }
1369
+ if (report.contextMode.status === "available" && !report.baselineChecks.expectedCurrentFailuresDetected) {
1370
+ process.exitCode = 1;
1371
+ }
1287
1372
  }
1288
1373
  function cliStringOption(args, name) {
1289
- const prefix = `${name}=`;
1290
- const match = args.find((arg) => arg.startsWith(prefix));
1291
- return match ? match.slice(prefix.length) : undefined;
1374
+ const prefix = `${name}=`;
1375
+ const match = args.find((arg) => arg.startsWith(prefix));
1376
+ return match ? match.slice(prefix.length) : undefined;
1292
1377
  }
1293
1378
  if (process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) {
1294
- await runCli();
1379
+ await runCli();
1295
1380
  }