@piagent/platform 1.5.5 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/CHANGELOG.md +166 -0
  2. package/README.md +25 -21
  3. package/README.vi.md +34 -5
  4. package/adapters/web-frontend/profile.json +2 -4
  5. package/architecture/layers.json +18 -3
  6. package/benchmarks/deep-logic-v1/SPECIALIST_PROTOCOL.vi.md +122 -0
  7. package/benchmarks/deep-logic-v1/grade.mjs +272 -10
  8. package/benchmarks/deep-logic-v1/project/apps/admin/src/billing-summary.js +3 -0
  9. package/benchmarks/deep-logic-v1/project/benchmark-contract/public-smoke.test.js +166 -0
  10. package/benchmarks/deep-logic-v1/project/package.json +1 -1
  11. package/benchmarks/deep-logic-v1/project/packages/billing/src/close-period.js +3 -0
  12. package/benchmarks/deep-logic-v1/project/packages/billing/src/plan-timeline.js +3 -0
  13. package/benchmarks/deep-logic-v1/project/test/smoke.test.js +7 -1
  14. package/benchmarks/deep-logic-v1/prompts/budgeted-context-graph.md +2 -0
  15. package/benchmarks/deep-logic-v1/prompts/fair-dependency-scheduler.md +2 -0
  16. package/benchmarks/deep-logic-v1/prompts/layered-policy-resolution.md +2 -0
  17. package/benchmarks/deep-logic-v1/prompts/resumable-stream-assembly.md +3 -1
  18. package/benchmarks/deep-logic-v1/prompts/revision-event-reconciliation.md +2 -0
  19. package/benchmarks/deep-logic-v1/prompts/temporal-usage-billing-close.md +123 -0
  20. package/benchmarks/deep-logic-v1/prompts/transactional-config-merge.md +2 -0
  21. package/benchmarks/deep-logic-v1/suite.json +51 -3
  22. package/benchmarks/deep-logic-v1/variant.mjs +22 -1
  23. package/benchmarks/production-v1/spend-control.v1.json +92 -0
  24. package/benchmarks/production-v1/suite.json +46 -3
  25. package/catalog/capabilities.json +18 -18
  26. package/docs/authority-manifest.md +1 -1
  27. package/docs/auto-delegation-policy.md +38 -73
  28. package/docs/command-reference-vietnamese.md +27 -35
  29. package/docs/context-engine.md +12 -4
  30. package/docs/context-window-policy.md +37 -1
  31. package/docs/distribution-standard.md +7 -7
  32. package/docs/en/architecture.md +27 -13
  33. package/docs/languages.json +4 -4
  34. package/docs/model-options.md +8 -5
  35. package/docs/operator-manual-vietnamese.md +27 -45
  36. package/docs/project-adapters.md +2 -2
  37. package/docs/publishing-for-teams.md +10 -0
  38. package/docs/quality-benchmark.md +427 -62
  39. package/docs/release-install-policy.md +10 -9
  40. package/docs/runtime-harness-standard.md +3 -3
  41. package/docs/runtime-policy-design.md +4 -4
  42. package/docs/runtime-quality-baseline.md +13 -6
  43. package/docs/security-threat-model.md +2 -2
  44. package/docs/subagent-orchestration-capabilities.md +19 -51
  45. package/docs/subagents-and-multiagent.md +21 -47
  46. package/docs/task-implementation-contract.md +3 -3
  47. package/docs/task-lifecycle-vietnamese.md +3 -3
  48. package/docs/team-onboarding.md +2 -2
  49. package/docs/usage-observability.md +7 -2
  50. package/docs/vi/architecture.md +27 -13
  51. package/docs-site/architecture.html +1 -1
  52. package/docs-site/benchmark.html +89 -95
  53. package/docs-site/commands.html +1 -1
  54. package/docs-site/content/benchmark.html +88 -94
  55. package/docs-site/content/en/benchmark.html +134 -90
  56. package/docs-site/content/en/index.html +19 -19
  57. package/docs-site/content/en/runtime.html +4 -4
  58. package/docs-site/content/en/whats-new.html +12 -1
  59. package/docs-site/content/en/workflows.html +2 -2
  60. package/docs-site/content/index.html +19 -19
  61. package/docs-site/content/runtime.html +4 -4
  62. package/docs-site/content/whats-new.html +12 -1
  63. package/docs-site/content/workflows.html +2 -2
  64. package/docs-site/context-engine.html +1 -1
  65. package/docs-site/en/architecture.html +1 -1
  66. package/docs-site/en/benchmark.html +135 -91
  67. package/docs-site/en/commands.html +1 -1
  68. package/docs-site/en/context-engine.html +1 -1
  69. package/docs-site/en/index.html +20 -20
  70. package/docs-site/en/mcp-approval.html +1 -1
  71. package/docs-site/en/mcp-auth.html +1 -1
  72. package/docs-site/en/mcp-servers.html +1 -1
  73. package/docs-site/en/mcp.html +1 -1
  74. package/docs-site/en/permissions.html +1 -1
  75. package/docs-site/en/profiles.html +1 -1
  76. package/docs-site/en/quickstart.html +1 -1
  77. package/docs-site/en/runtime.html +5 -5
  78. package/docs-site/en/scope.html +1 -1
  79. package/docs-site/en/security.html +1 -1
  80. package/docs-site/en/team.html +1 -1
  81. package/docs-site/en/whats-new.html +13 -2
  82. package/docs-site/en/workflows.html +3 -3
  83. package/docs-site/index.html +20 -20
  84. package/docs-site/mcp-approval.html +1 -1
  85. package/docs-site/mcp-auth.html +1 -1
  86. package/docs-site/mcp-servers.html +1 -1
  87. package/docs-site/mcp.html +1 -1
  88. package/docs-site/permissions.html +1 -1
  89. package/docs-site/profiles.html +1 -1
  90. package/docs-site/quickstart.html +1 -1
  91. package/docs-site/runtime.html +5 -5
  92. package/docs-site/scope.html +1 -1
  93. package/docs-site/security.html +1 -1
  94. package/docs-site/team.html +1 -1
  95. package/docs-site/whats-new.html +13 -2
  96. package/docs-site/workflows.html +3 -3
  97. package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
  98. package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
  99. package/evals/fixtures/codex-relative-efficiency-v1.invalid.json +4 -0
  100. package/evals/fixtures/codex-relative-efficiency-v1.valid.json +94 -0
  101. package/evals/fixtures/helper-request.invalid.json +1 -1
  102. package/evals/fixtures/helper-request.valid.json +1 -1
  103. package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
  104. package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
  105. package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +7 -0
  106. package/evals/fixtures/task-authority-snapshot.valid.json +4 -4
  107. package/evals/fs4-readiness-matrix.v1.json +2 -2
  108. package/evals/fs5-causal-arm.v1.json +3 -3
  109. package/evals/fs5-pilot-protocol.v1.json +3 -3
  110. package/evals/fs5-pilot-protocol.v2.json +8 -8
  111. package/evals/fs5-pilot-protocol.v3.json +8 -8
  112. package/evals/fs5-pilot-protocol.v4.json +7 -6
  113. package/evals/fs5-pilot-protocol.v5.json +9 -7
  114. package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
  115. package/evals/runtime-conformance-v1/README.md +43 -0
  116. package/evals/runtime-conformance-v1/evaluate-paid-canary.mjs +198 -0
  117. package/evals/runtime-conformance-v1/lane.json +57 -0
  118. package/evals/runtime-conformance-v1/paid-canary.json +31 -0
  119. package/evals/runtime-conformance-v1/runner.mjs +456 -0
  120. package/package.json +3 -1
  121. package/packages/piagent-core/README.md +13 -4
  122. package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
  123. package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
  124. package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
  125. package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
  126. package/packages/piagent-core/benchmark/benchmark-codex-relative-efficiency.js +698 -0
  127. package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
  128. package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
  129. package/packages/piagent-core/benchmark/benchmark-core.js +736 -664
  130. package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
  131. package/packages/piagent-core/benchmark/benchmark-forensics.js +441 -4
  132. package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
  133. package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
  134. package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
  135. package/packages/piagent-core/benchmark/benchmark-preflight.js +5 -1
  136. package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
  137. package/packages/piagent-core/benchmark/benchmark-provider-free-evidence.js +345 -0
  138. package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
  139. package/packages/piagent-core/benchmark/benchmark-record-validation.js +252 -0
  140. package/packages/piagent-core/benchmark/benchmark-report.js +130 -12
  141. package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
  142. package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
  143. package/packages/piagent-core/benchmark/benchmark-runtime-causal.js +244 -0
  144. package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +979 -0
  145. package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
  146. package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
  147. package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
  148. package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
  149. package/packages/piagent-core/benchmark/benchmark-suite.js +149 -2
  150. package/packages/piagent-core/benchmark/benchmark-summary-support.js +380 -0
  151. package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
  152. package/packages/piagent-core/benchmark/benchmark-usage.js +322 -0
  153. package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
  154. package/packages/piagent-core/capabilities/runtime-integrity.js +3 -0
  155. package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
  156. package/packages/piagent-core/extensions/acceptance-behavior-proof.js +806 -0
  157. package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
  158. package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
  159. package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
  160. package/packages/piagent-core/extensions/acceptance-receipt.js +123 -59
  161. package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
  162. package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
  163. package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
  164. package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
  165. package/packages/piagent-core/extensions/context-engine.js +149 -156
  166. package/packages/piagent-core/extensions/context-evidence.js +48 -0
  167. package/packages/piagent-core/extensions/context-import-links.js +296 -0
  168. package/packages/piagent-core/extensions/context-index-policy.js +4 -0
  169. package/packages/piagent-core/extensions/context-query-paths.js +57 -0
  170. package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
  171. package/packages/piagent-core/extensions/criterion-context-pack.js +435 -7
  172. package/packages/piagent-core/extensions/criterion-graph.js +82 -9
  173. package/packages/piagent-core/extensions/fallback-base-policy.ts +101 -0
  174. package/packages/piagent-core/extensions/guard-types.ts +6 -0
  175. package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
  176. package/packages/piagent-core/extensions/piagent-guard.ts +420 -260
  177. package/packages/piagent-core/extensions/task-contract-view.js +17 -3
  178. package/packages/piagent-core/extensions/task-lifecycle.js +39 -2
  179. package/packages/piagent-core/extensions/task-state.js +109 -142
  180. package/packages/piagent-core/extensions/verification-intelligence.js +52 -13
  181. package/packages/piagent-core/extensions/workspace-evidence-roots.js +193 -0
  182. package/packages/piagent-core/package.json +1 -1
  183. package/packages/piagent-core/policies/base-policy.json +44 -2
  184. package/packages/piagent-core/policy/authority-manifest.v1.json +3 -3
  185. package/packages/piagent-core/prompts/be-to-fe.md +2 -8
  186. package/packages/piagent-core/prompts/plan.md +1 -1
  187. package/packages/piagent-core/prompts/platform-improve.md +2 -6
  188. package/packages/piagent-core/prompts/review.md +2 -7
  189. package/packages/piagent-core/prompts/scout.md +1 -1
  190. package/packages/piagent-core/prompts/task.md +4 -3
  191. package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
  192. package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
  193. package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
  194. package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
  195. package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
  196. package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
  197. package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
  198. package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
  199. package/packages/piagent-core/runtime/hooks/completion-hook.ts +12 -12
  200. package/packages/piagent-core/runtime/hooks/input-hook.ts +17 -1
  201. package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
  202. package/packages/piagent-core/runtime/hooks/session-start-hook.ts +107 -3
  203. package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
  204. package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +134 -166
  205. package/packages/piagent-core/runtime/hooks/tool-result-value-helpers.ts +66 -0
  206. package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +4 -2
  207. package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
  208. package/packages/piagent-core/runtime/inspection/bounded-emission-guard.ts +286 -0
  209. package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +37 -7
  210. package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
  211. package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
  212. package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +27 -4
  213. package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +70 -0
  214. package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +73 -9
  215. package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
  216. package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
  217. package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
  218. package/packages/piagent-core/runtime/orchestration/helper-lifecycle.ts +16 -12
  219. package/packages/piagent-core/runtime/orchestration/minimal-delegation-policy.ts +71 -0
  220. package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +28 -20
  221. package/packages/piagent-core/runtime/orchestration/role-policy.ts +27 -9
  222. package/packages/piagent-core/runtime/orchestration/subagent-tool-policy.ts +329 -0
  223. package/packages/piagent-core/runtime/policy/block-remedy.ts +0 -4
  224. package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
  225. package/packages/piagent-core/runtime/product/efficiency-metrics.ts +7 -2
  226. package/packages/piagent-core/runtime/quality/edit-freshness-guard.ts +102 -0
  227. package/packages/piagent-core/runtime/quality/exact-output-contract.ts +22 -11
  228. package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
  229. package/packages/piagent-core/runtime/quality/performance-assurance.ts +9 -5
  230. package/packages/piagent-core/runtime/recovery/continuation-budget.ts +1 -1
  231. package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
  232. package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
  233. package/packages/piagent-core/runtime/recovery/handoff-projection.ts +4 -1
  234. package/packages/piagent-core/runtime/recovery/pristine-task-policy-refresh.ts +91 -0
  235. package/packages/piagent-core/runtime/recovery/recovery-policy.ts +7 -4
  236. package/packages/piagent-core/runtime/recovery/resume-state.ts +112 -14
  237. package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +60 -13
  238. package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
  239. package/packages/piagent-core/runtime/recovery/task-evidence-coverage-refresh.ts +217 -0
  240. package/packages/piagent-core/runtime/registration/context-commands.ts +82 -7
  241. package/packages/piagent-core/runtime/registration/knowledge-tools.ts +9 -5
  242. package/packages/piagent-core/runtime/registration/operator-request-intake.ts +49 -0
  243. package/packages/piagent-core/runtime/registration/policy-tools.ts +50 -15
  244. package/packages/piagent-core/runtime/registration/session-commands.ts +1 -1
  245. package/packages/piagent-core/runtime/registration/task-completion-tools.ts +61 -25
  246. package/packages/piagent-core/runtime/registration/task-evidence-tools.ts +21 -12
  247. package/packages/piagent-core/runtime/registration/task-start-guidance.ts +169 -0
  248. package/packages/piagent-core/runtime/registration/task-start-manifest.ts +54 -0
  249. package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
  250. package/packages/piagent-core/runtime/registration/task-start-tool.ts +108 -103
  251. package/packages/piagent-core/runtime/runtime-limits.ts +16 -2
  252. package/packages/piagent-core/runtime/session/adaptive-context-analysis.ts +461 -0
  253. package/packages/piagent-core/runtime/session/adaptive-context-governor.ts +464 -0
  254. package/packages/piagent-core/runtime/session/adaptive-context-ledger.ts +265 -0
  255. package/packages/piagent-core/runtime/session/message-signals.ts +7 -3
  256. package/packages/piagent-core/runtime/session/operator-request-carry.ts +44 -0
  257. package/packages/piagent-core/runtime/session/runtime-state.ts +161 -5
  258. package/packages/piagent-core/runtime/session/source-checkout-read-grants.ts +35 -0
  259. package/packages/piagent-core/runtime/session/system-prompt.ts +76 -14
  260. package/packages/piagent-core/runtime/session/task-contract-presentation.ts +39 -0
  261. package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
  262. package/packages/piagent-core/runtime/tools/phase-tools.ts +8 -8
  263. package/packages/piagent-core/runtime/tools/tool-groups.ts +12 -2
  264. package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
  265. package/packages/piagent-core/runtime/verification/exact-verifier-reuse.ts +101 -0
  266. package/packages/piagent-core/runtime/workflows/runtime-intake-compaction.ts +164 -0
  267. package/packages/piagent-core/runtime/workflows/task-intake.ts +207 -38
  268. package/packages/piagent-core/security/sensitive-data.js +13 -218
  269. package/packages/piagent-core/security/sensitive-project-file.js +42 -0
  270. package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
  271. package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
  272. package/packages/piagent-core/security/sensitive-text.js +234 -0
  273. package/packages/piagent-core/subagents/piagent-oracle.md +5 -3
  274. package/packages/piagent-core/subagents/piagent-planner.md +6 -4
  275. package/packages/piagent-core/subagents/piagent-reviewer.md +5 -3
  276. package/packages/piagent-core/subagents/piagent-scout.md +4 -2
  277. package/packages/piagent-core/subagents/piagent-worker.md +7 -5
  278. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-B4XehTCU.js} +1 -1
  279. package/packages/piagent-webui/dist/client/assets/index-CgOEyRPG.js +176 -0
  280. package/packages/piagent-webui/dist/client/index.html +1 -1
  281. package/packages/piagent-webui/extension/piagent-webui.ts +3 -1
  282. package/packages/piagent-webui/extension/same-session-bridge.ts +1 -1
  283. package/packages/piagent-webui/extension/session-stream-adapter.ts +33 -10
  284. package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
  285. package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
  286. package/packages/piagent-webui/gateway/gateway-session-stream.ts +122 -20
  287. package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
  288. package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
  289. package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
  290. package/packages/piagent-webui/gateway/session-operation-lifecycle.ts +237 -0
  291. package/packages/piagent-webui/gateway/session-operation-retry-control.ts +38 -0
  292. package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
  293. package/packages/piagent-webui/gateway/session-operation-watchdog.ts +165 -0
  294. package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
  295. package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +141 -125
  296. package/packages/piagent-webui/ownership/session-lease-store.ts +5 -2
  297. package/packages/piagent-webui/server/core-inspection-provider.ts +2 -1
  298. package/packages/piagent-webui/server/loopback-server.ts +8 -0
  299. package/packages/piagent-webui/server/transcript-projection.ts +113 -9
  300. package/packages/piagent-webui/shared/text-visibility.ts +10 -0
  301. package/schemas/codex-relative-efficiency-v1.schema.json +155 -0
  302. package/schemas/piagent-webui/catalog-v1.json +9 -0
  303. package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
  304. package/schemas/piagent-webui/runtime-event-v2.schema.json +15 -5
  305. package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
  306. package/schemas/piagent-webui/subagent-tree-v1.schema.json +11 -1
  307. package/schemas/project-profile.schema.json +1 -1
  308. package/schemas/task-contract.schema.json +27 -1
  309. package/scripts/benchmark-runner-core.mjs +367 -363
  310. package/scripts/benchmark-runner-finalization.mjs +313 -0
  311. package/scripts/benchmark-runner-host-readiness.mjs +94 -0
  312. package/scripts/benchmark-runner-support.mjs +200 -0
  313. package/scripts/benchmark-session.mjs +95 -41
  314. package/scripts/configure-model-scope.sh +4 -3
  315. package/scripts/configure-subagents.sh +42 -34
  316. package/scripts/helper-evaluation.mjs +2 -2
  317. package/scripts/install-global.sh +2 -2
  318. package/scripts/pi-usage-history.mjs +37 -10
  319. package/scripts/setup.sh +2 -2
  320. package/scripts/team-doctor.sh +1 -1
  321. package/scripts/verify-local.sh +10 -1
  322. package/templates/global/settings.json +2 -1
  323. package/packages/piagent-webui/dist/client/assets/index-BZRFw1u6.js +0 -174
@@ -1,9 +1,11 @@
1
1
  import crypto from "node:crypto";
2
2
  import path from "node:path";
3
3
  import { changedFileAcceptanceCorpus, emptyAcceptanceCorpus, isAcceptanceTestPath } from "./acceptance-language-adapters.js";
4
+ import { durableContextEvidenceEntries, hasDurableContextEvidence } from "./context-evidence.js";
4
5
  import { matchesAnyPath, normalizePathCandidate } from "./policy-core.js";
5
6
  import { baselineReturnRepresentationConflicts, returnRepresentationGuidance } from "./return-contract.js";
6
7
  import { hasLengthPrefixedIdentityKey, tenantAssertionSignals } from "./tenant-contract.js";
8
+ import { criterionBehaviorProofDisposition, criterionRequiresBehavioralProof, durableBehaviorProofRequired, genericCriterionEvidence, genericFallbackEvidence, isVerificationOnlyCriterion, verifierCommandsCoverTests } from "./acceptance-behavior-proof.js";
7
9
  import { latestObservedVerificationEvidence, meaningfulVerificationCommands, verificationEvidenceProvesStableTree } from "./verification-intelligence.js";
8
10
  import {
9
11
  acceptanceContractProofGuidance,
@@ -33,7 +35,7 @@ const EVIDENCE_FIELDS = new Set(["kind", "summary", "paths", "command", "exitCod
33
35
  const PROVENANCE_FIELDS = new Set(["assurance", "disposition", "repairCount", "retryCount", "finalRecoveryDisposition", "failureRef", "recoveryRef", "handoffRef", "recordedAt"]);
34
36
  const FAILURE_REF_FIELDS = new Set(["evidenceDigest", "category", "captureRef"]);
35
37
  const RECOVERY_REF_FIELDS = new Set(["policyVersion", "action", "reasonCodes"]);
36
- const HELPER_USAGE_FIELDS = new Set(["mode", "used", "reasonCodes", "helpers", "recordedAt"]);
38
+ const HELPER_USAGE_FIELDS = new Set(["mode", "used", "decision", "projectedSavingsRatio", "reasonCodes", "helpers", "recordedAt"]);
37
39
  const HELPER_ENTRY_FIELDS = new Set(["role", "disposition", "requestRef", "outputDigest", "calls", "tokens"]);
38
40
  const HELPER_ROLES = new Set(["retriever", "scout", "planner", "worker", "reviewer", "oracle", "researcher"]);
39
41
  const RECEIPT_DISPOSITIONS = new Set(["first-pass-success", "repaired-success", "blocked", "partial", "failed", "pending"]);
@@ -42,7 +44,7 @@ const RECOVERY_ACTIONS = new Set(["repair", "retry", "fresh-session", "ask-opera
42
44
  const FAILURE_CATEGORIES = new Set(["passed", "compile-typecheck", "test-assertion", "lint-format", "dependency-config", "environment", "provider-network", "permission-policy", "scope-protected-path", "flaky-infrastructure", "unknown"]);
43
45
  const HASH = /^[a-f0-9]{64}$/;
44
46
  const SAFE_REF = /^[a-z0-9.][a-z0-9:._/-]{0,511}$/i;
45
- const READ_ONLY_BOUNDARY = /\b(?:read-only|no edits?|do not edit(?: files?| source| project| repo)?|do not change (?:files?|source|project|repo)|do not mutate (?:files?|source|project|repo|workspace)|khong sua(?: file| source| project)?|khong edit(?: file| source| project)?|khong doi(?: file| source| project)?)\b/;
47
+ const READ_ONLY_BOUNDARY = /\b(?:read-only|no\s+(?:code|source|project|file|workspace|repo(?:sitory)?)?\s*(?:edits?|changes?|mutations?)|do not\s+(?:edit|change|modify|mutate|touch)(?: files?| source| project| repo| workspace)?|leave\s+(?:the\s+)?(?:source|project|repo|workspace)\s+(?:unchanged|unmodified)|khong\s+(?:sua|edit|thay\s+doi)(?: file| source| project)?)\b/;
46
48
 
47
49
  function sha256(value) {
48
50
  return crypto.createHash("sha256").update(String(value ?? "")).digest("hex");
@@ -79,11 +81,9 @@ function normalizedText(value) {
79
81
  .replace(/[\u0300-\u036f]/g, "")
80
82
  .toLowerCase();
81
83
  }
82
-
83
84
  function includesAny(text, patterns) {
84
85
  return patterns.some((pattern) => pattern.test(text));
85
86
  }
86
-
87
87
  function pathBoundaryMentioned(text, patterns) {
88
88
  const value = normalizedText(text);
89
89
  return uniqueStrings(patterns).some((pattern) => {
@@ -94,32 +94,70 @@ function pathBoundaryMentioned(text, patterns) {
94
94
  return value.includes(lower) || (Boolean(basename) && value.includes(basename));
95
95
  });
96
96
  }
97
-
97
+ function readOnlyBoundaryTiedToPattern(text, patterns) {
98
+ return normalizedText(text)
99
+ .split(/(?:\r?\n+|[.!?;]+|\s+[—–]\s+)/)
100
+ .some((clause) => READ_ONLY_BOUNDARY.test(clause) && pathBoundaryMentioned(clause, patterns));
101
+ }
98
102
  function changedFilesRespectBoundaries(changedFiles, boundaryPatterns) {
99
103
  const patterns = uniqueStrings(boundaryPatterns);
100
104
  if (patterns.length === 0) return true;
101
105
  return uniqueStrings(changedFiles).every((file) => !matchesAnyPath(file, patterns));
102
106
  }
107
+ function acceptanceReadOnlyInferenceOptions(input, text) {
108
+ const boundaryPatterns = uniqueStrings([
109
+ ...(Array.isArray(input?.outOfScope) ? input.outOfScope : []),
110
+ ...(Array.isArray(input?.protectedPaths) ? input.protectedPaths : [])
111
+ ]);
112
+ return {
113
+ mutationPolicy: input?.mutationPolicy,
114
+ readOnlyBoundary: boundaryPatterns.length > 0
115
+ && readOnlyBoundaryTiedToPattern(text, boundaryPatterns)
116
+ };
117
+ }
103
118
 
104
- export function inferAcceptanceObligations(text, changeMode = "source-change") {
119
+ export function inferAcceptanceObligations(text, changeMode = "source-change", options = {}) {
105
120
  const value = normalizedText(text);
121
+ const accessControlText = value
122
+ .replace(/`[^`\n]*`/g, " ")
123
+ .replace(/\b(?:[a-z0-9_.@-]+\/)+[a-z0-9_.@/-]+\b/g, " ");
106
124
  const obligations = [];
107
- if (changeMode === "read-only" || READ_ONLY_BOUNDARY.test(value)) {
125
+ if (changeMode === "read-only" || options.mutationPolicy === "forbidden" || options.readOnlyBoundary === true) {
108
126
  obligations.push("read-only-evidence");
109
127
  }
110
- const explicitAuthorization = includesAny(value, [
111
- /\bauth(?:orization)?\b/, /\bunauthoriz(?:ed|ation)\b/, /\bpermission\b/, /\brole\b/, /\badmin\b/, /\bdeny\b/, /\bdenied\b/
128
+ const actorAccessControl = includesAny(accessControlText, [
129
+ /\b(?:admins?|roles?|owners?|users?|callers?|resources?)\b[\s\S]{0,80}\b(?:access|allow(?:ed)?|deny|denied|block(?:ed)?|forbid(?:den)?|manag(?:e|es|ed|ing))\b/,
130
+ /\b(?:access|allow(?:ed)?|deny|denied|block(?:ed)?|forbid(?:den)?|manag(?:e|es|ed|ing))\b[\s\S]{0,80}\b(?:admins?|roles?|owners?|users?|callers?|resources?)\b/
112
131
  ]);
113
- const ownerAccessControl = /\bowner\b/.test(value) && includesAny(value, [
132
+ const explicitAuthorization = includesAny(accessControlText, [
133
+ /\bauth(?:orization)?\b/, /\bunauthoriz(?:ed|ation)\b/, /\bpermission\b/
134
+ ]) || actorAccessControl;
135
+ const ownerAccessControl = /\bowner\b/.test(accessControlText) && includesAny(accessControlText, [
114
136
  /\busers?\b/, /\bresources?\b/, /\baccess\b/, /\bmanag(?:e|es|ed|ing)\b/, /\bauth(?:orization)?\b/, /\bpermission\b/, /\broles?\b/
115
137
  ]);
116
138
  if (explicitAuthorization || ownerAccessControl) {
117
139
  obligations.push("authorization-deny-case");
118
140
  }
119
- if (includesAny(value, [/\btenant\b/, /\bcross-tenant\b/, /\bsame tenant\b/, /\btenantid\b/])) {
120
- if (includesAny(value, [/\bcache\b/, /\bcache[- ]?key\b/, /\bstorage\b/, /\bcollision\b/, /\bentity\b/, /\bsame tuple\b/])) {
141
+ const tenantMentioned = includesAny(value, [/\btenants?\b/, /\bcross[- ]tenant\b/, /\bsame[- ]tenant\b/, /\btenantid\b/]);
142
+ const tenantStorage = tenantMentioned && includesAny(value, [
143
+ /\bcache\b/, /\bcache[- ]?key\b/, /\bstorage\b/, /\bcollision\b/, /\bentity\b/, /\bsame tuple\b/
144
+ ]);
145
+ const strongAuthorization = includesAny(accessControlText, [
146
+ /\bauth(?:orization)?\b/, /\bunauthoriz(?:ed|ation)\b/, /\bpermission\b/
147
+ ]);
148
+ const tenantActorAccess = tenantMentioned && actorAccessControl;
149
+ const explicitTenantAccessBoundary = includesAny(value, [
150
+ /\bcross[- ]tenant\b/, /\bsame[- ]tenant\b/, /\btenant boundary\b/,
151
+ /\btenantid\b[\s\S]{0,100}\b(?:equal|match|same non-empty|access|allow|deny|block|forbid)/,
152
+ /\b(?:access|allow|deny|block|forbid)[\s\S]{0,100}\btenantid\b/
153
+ ]) && includesAny(value, [
154
+ /\baccess\b/, /\ballow(?:ed)?\b/, /\b(?:deny|denied)\b/, /\bblock(?:ed)?\b/, /\bforbid(?:den)?\b/,
155
+ /\bequal\b/, /\bmatch(?:es|ed|ing)?\b/, /\bsame non-empty\b/
156
+ ]);
157
+ if (tenantMentioned) {
158
+ if (tenantStorage) {
121
159
  obligations.push("tenant-storage-isolation");
122
- } else {
160
+ } else if (strongAuthorization || ownerAccessControl || tenantActorAccess || explicitTenantAccessBoundary) {
123
161
  obligations.push("tenant-boundary");
124
162
  }
125
163
  }
@@ -167,7 +205,6 @@ const GENERATED_ACCEPTANCE_TEXTS = new Set([
167
205
  "authorization-deny-case", "tenant-boundary", "tenant-storage-isolation", "invalid-input-rejection", "boundary-case",
168
206
  "backward-compatibility", "read-only-evidence", "verification-evidence"
169
207
  ].map((obligation) => generatedCriterionForObligation(obligation)));
170
-
171
208
  function acceptanceTaskText(task) {
172
209
  return [
173
210
  task?.summary,
@@ -203,8 +240,9 @@ function semanticConflictReasons(obligation, task, corpus, criterion) {
203
240
 
204
241
  /**
205
242
  * Surface a concrete existing return contract before implementation starts.
206
- * This is intentionally source-derived and bounded to exact in-scope files;
207
- * it does not guess from benchmark scenario names or hidden expectations.
243
+ * This is intentionally source-derived and bounded to exact known files; it
244
+ * does not turn the task's initial focus into write authority or guess from
245
+ * benchmark scenario names and hidden expectations.
208
246
  */
209
247
  export function acceptanceBaselineGuidance(task, options = {}) {
210
248
  if (!options.cwd) return [];
@@ -223,7 +261,11 @@ export function acceptanceSemanticConflicts(task, options = {}) {
223
261
  : { ...emptyAcceptanceCorpus(changedFiles.length > 0 ? changedFiles : ["inline.js"]), sourceText: normalizedText(options.sourceText), allText: normalizedText(options.sourceText) };
224
262
  if (!corpus.adapter.proofCapable) return [];
225
263
  const obligations = task?.acceptanceReceipt?.criteria?.map((criterion) => criterion.obligation)
226
- ?? inferAcceptanceObligations(acceptanceTaskText(task), task?.changeMode);
264
+ ?? inferAcceptanceObligations(
265
+ acceptanceTaskText(task),
266
+ task?.changeMode,
267
+ acceptanceReadOnlyInferenceOptions(task, acceptanceTaskText(task))
268
+ );
227
269
  return uniqueStrings([
228
270
  ...obligations.flatMap((obligation) => semanticConflictReasons(obligation, task, corpus)),
229
271
  ...(options.cwd ? baselineReturnRepresentationConflicts(acceptanceTaskText(task), options.cwd, corpus.sourceFiles) : [])
@@ -234,7 +276,6 @@ function criterionId(text, obligation, index) {
234
276
  const prefix = compactId(obligation ?? "criterion").slice(0, 24);
235
277
  return `ac-${String(index + 1).padStart(2, "0")}-${prefix}-${sha256(text).slice(0, 8)}`.slice(0, 80);
236
278
  }
237
-
238
279
  export function buildAcceptanceReceipt(input = {}) {
239
280
  const changeMode = input.changeMode === "read-only" ? "read-only" : "source-change";
240
281
  const baseText = [
@@ -242,12 +283,20 @@ export function buildAcceptanceReceipt(input = {}) {
242
283
  input.expectedOutput,
243
284
  ...(Array.isArray(input.acceptanceCriteria) ? input.acceptanceCriteria : [])
244
285
  ].filter(Boolean).join("\n");
245
- const obligations = inferAcceptanceObligations(baseText, changeMode);
286
+ const obligations = inferAcceptanceObligations(
287
+ baseText,
288
+ changeMode,
289
+ acceptanceReadOnlyInferenceOptions(input, baseText)
290
+ );
246
291
  const texts = uniqueStrings(Array.isArray(input.acceptanceCriteria) ? input.acceptanceCriteria : []);
247
292
  const generatedObligations = new Map();
248
293
  for (const obligation of obligations) {
249
294
  const generated = generatedCriterionForObligation(obligation);
250
- if (!texts.some((text) => inferAcceptanceObligations(text, changeMode).includes(obligation))) {
295
+ if (!texts.some((text) => inferAcceptanceObligations(
296
+ text,
297
+ changeMode,
298
+ acceptanceReadOnlyInferenceOptions(input, text)
299
+ ).includes(obligation))) {
251
300
  texts.push(generated);
252
301
  generatedObligations.set(generated, obligation);
253
302
  }
@@ -258,17 +307,23 @@ export function buildAcceptanceReceipt(input = {}) {
258
307
  : generatedCriterionForObligation("verification-evidence"));
259
308
  }
260
309
  const acceptanceCriteria = texts.slice(0, MAX_CRITERIA);
310
+ const durableBehaviorProof = durableBehaviorProofRequired(baseText, changeMode);
261
311
  const criteria = acceptanceCriteria.map((text, index) => {
262
- const inferred = inferAcceptanceObligations(text, changeMode);
312
+ const inferred = inferAcceptanceObligations(
313
+ text,
314
+ changeMode,
315
+ acceptanceReadOnlyInferenceOptions(input, text)
316
+ );
263
317
  const obligation = generatedObligations.get(text)
264
318
  ?? inferred.find((item) => item !== "verification-evidence" && item !== "backward-compatibility")
265
- ?? inferred[0]
319
+ ?? (isVerificationOnlyCriterion(text) ? "verification-evidence" : undefined)
320
+ ?? (inferred.includes("backward-compatibility") ? "backward-compatibility" : undefined)
266
321
  ?? "requested-behavior";
267
322
  return {
268
323
  id: criterionId(text, obligation, index),
269
324
  hash: sha256(text),
270
325
  obligation,
271
- priority: CRITICAL_OBLIGATIONS.has(obligation) ? "critical" : "normal",
326
+ priority: CRITICAL_OBLIGATIONS.has(obligation) || (durableBehaviorProof && obligation !== "verification-evidence") ? "critical" : "normal",
272
327
  status: "pending",
273
328
  evidence: []
274
329
  };
@@ -327,20 +382,30 @@ export function normalizeAcceptanceReceipt(value) {
327
382
 
328
383
  function normalizeHelperUsage(value) {
329
384
  if (!isRecord(value) || !["off", "recommend", "on"].includes(value.mode)) return undefined;
330
- const helpers = Array.isArray(value.helpers) ? value.helpers.slice(0, 3).filter(isRecord).map((item) => ({
385
+ const rawHelpers = Array.isArray(value.helpers) ? value.helpers.slice(0, 3).filter(isRecord) : [];
386
+ const helpers = rawHelpers.map((item) => ({
331
387
  role: HELPER_ROLES.has(item.role) ? item.role : "scout",
332
388
  disposition: compactId(item.disposition ?? "unknown"),
333
389
  requestRef: HASH.test(String(item.requestRef)) ? item.requestRef : sha256(item.requestRef),
334
390
  outputDigest: item.outputDigest === null || HASH.test(String(item.outputDigest)) ? item.outputDigest : null,
335
391
  calls: Number.isInteger(item.calls) ? Math.max(0, Math.min(100, item.calls)) : 0,
336
392
  tokens: Number.isInteger(item.tokens) ? Math.max(0, Math.min(100_000_000, item.tokens)) : 0
337
- })) : [];
338
- return { mode: value.mode, used: helpers.some((item) => !["solo", "recommend", "unavailable", "blocked"].includes(item.disposition)), reasonCodes: uniqueStrings(value.reasonCodes).map(compactId).slice(0, 16), helpers, recordedAt: validTimestamp(value.recordedAt) ? value.recordedAt : new Date().toISOString() };
393
+ }));
394
+ const used = helpers.some((item) => !["solo", "recommend", "unavailable", "blocked"].includes(item.disposition));
395
+ const helperDecision = rawHelpers.find((item) => ["dispatch", "skip"].includes(item.decision))?.decision;
396
+ const decision = ["dispatch", "skip"].includes(value.decision) ? value.decision : helperDecision ?? (used ? "dispatch" : "skip");
397
+ const rawProjectedSavings = typeof value.projectedSavingsRatio === "number"
398
+ ? value.projectedSavingsRatio
399
+ : rawHelpers.find((item) => typeof item.projectedSavingsRatio === "number")?.projectedSavingsRatio;
400
+ const projectedSavingsRatio = typeof rawProjectedSavings === "number" && Number.isFinite(rawProjectedSavings)
401
+ ? Math.max(-10, Math.min(1, rawProjectedSavings))
402
+ : null;
403
+ return { mode: value.mode, used, decision, projectedSavingsRatio, reasonCodes: uniqueStrings(value.reasonCodes).map(compactId).slice(0, 16), helpers, recordedAt: validTimestamp(value.recordedAt) ? value.recordedAt : new Date().toISOString() };
339
404
  }
340
405
 
341
406
  export function applyAcceptanceHelperUsage(task, input = {}) {
342
407
  const receipt = normalizeAcceptanceReceipt(task?.acceptanceReceipt); if (!receipt) return task;
343
- const helperUsage = normalizeHelperUsage({ mode: input.mode ?? "off", reasonCodes: input.reasonCodes ?? [], helpers: input.helpers ?? [], recordedAt: input.recordedAt ?? new Date().toISOString() });
408
+ const helperUsage = normalizeHelperUsage({ mode: input.mode ?? "off", decision: input.decision, projectedSavingsRatio: input.projectedSavingsRatio, reasonCodes: input.reasonCodes ?? [], helpers: input.helpers ?? [], recordedAt: input.recordedAt ?? new Date().toISOString() });
344
409
  return { ...task, acceptanceReceipt: { ...receipt, criteria: structuredClone(task.acceptanceReceipt.criteria), helperUsage } };
345
410
  }
346
411
 
@@ -609,8 +674,8 @@ function hasStructuredCompositeIdentity(sourceText) {
609
674
  return jsonTuple || nestedLookup || hasLengthPrefixedIdentityKey(text);
610
675
  }
611
676
 
612
- function focusedContractEvidence(obligation, task, corpus, verifierEvidence, criterion) {
613
- if (!verifierEvidence || corpus.sourceFiles.length === 0 || corpus.testFiles.length === 0) return undefined;
677
+ function focusedContractEvidence(obligation, task, corpus, verifierEvidence, criterion, cwd) {
678
+ if (!verifierEvidence || corpus.sourceFiles.length === 0 || !verifierCommandsCoverTests(task, corpus.testFiles, cwd)) return undefined;
614
679
  const targets = namedCodeTargets(task, criterion, corpus.sourceText);
615
680
  const targetCoverage = targets.length === 0 || targets.every((target) => (
616
681
  new RegExp(`\\b${escapeRegex(target.toLowerCase())}\\b`).test(corpus.testText)
@@ -657,7 +722,7 @@ function hasCurrentPassingVerifier(task, currentWorkingTreeDigest) {
657
722
  });
658
723
  }
659
724
 
660
- function evidenceForObligation(obligation, task, corpus, currentWorkingTreeDigest, criterion) {
725
+ function evidenceForObligation(obligation, task, corpus, currentWorkingTreeDigest, criterion, cwd) {
661
726
  const passingVerifier = hasCurrentPassingVerifier(task, currentWorkingTreeDigest);
662
727
  const latestVerifyEvidence = latestObservedVerificationEvidence(task?.verifyEvidence);
663
728
  const verifyEvidence = meaningfulVerificationCommands(task?.verifyCommands ?? [])
@@ -674,14 +739,14 @@ function evidenceForObligation(obligation, task, corpus, currentWorkingTreeDiges
674
739
  if (semanticConflictReasons(obligation, task, corpus, criterion).length > 0) return undefined;
675
740
 
676
741
  if (obligation === "read-only-evidence") {
677
- const readOnlyOk = task.changeMode === "read-only"
742
+ const readOnlyEvidence = durableContextEvidenceEntries(task);
743
+ const readOnlyOk = (task.changeMode === "read-only" || task.mutationPolicy === "forbidden")
678
744
  && corpus.files.length === 0
679
- && Array.isArray(task.contextManifest)
680
- && task.contextManifest.length > 0;
745
+ && hasDurableContextEvidence(task);
681
746
  if (readOnlyOk) return {
682
747
  kind: "read-only-context",
683
748
  summary: "Read-only context was observed and no project files changed.",
684
- paths: task.contextManifest.map((item) => item.path).slice(0, 8)
749
+ paths: readOnlyEvidence.map((item) => item.path).slice(0, 8)
685
750
  };
686
751
  if (task.changeMode === "source-change") {
687
752
  const boundaryPatterns = uniqueStrings([...(task.outOfScope ?? []), ...(task.protectedPaths ?? [])]);
@@ -692,11 +757,10 @@ function evidenceForObligation(obligation, task, corpus, currentWorkingTreeDiges
692
757
  ].filter(Boolean).join("\n");
693
758
  const combinedText = `${taskText}\n${corpus.allText}`;
694
759
  const sourceChangeReadOnlyOk = corpus.files.length > 0
695
- && READ_ONLY_BOUNDARY.test(normalizedText(combinedText))
760
+ && boundaryPatterns.length > 0
761
+ && readOnlyBoundaryTiedToPattern(combinedText, boundaryPatterns)
696
762
  && changedFilesRespectBoundaries(corpus.files, boundaryPatterns)
697
- && (boundaryPatterns.length === 0 || pathBoundaryMentioned(combinedText, boundaryPatterns))
698
- && Array.isArray(task.contextManifest)
699
- && task.contextManifest.length > 0;
763
+ && hasDurableContextEvidence(task);
700
764
  if (sourceChangeReadOnlyOk) {
701
765
  return {
702
766
  kind: "source-change-read-only-boundary",
@@ -709,23 +773,21 @@ function evidenceForObligation(obligation, task, corpus, currentWorkingTreeDiges
709
773
  return undefined;
710
774
  }
711
775
 
712
- if (obligation === "verification-evidence" || obligation === "requested-behavior" || obligation === "backward-compatibility") {
713
- if (!passingVerifier) return undefined;
714
- return verifierEvidence;
715
- }
776
+ const generic = genericCriterionEvidence({ obligation, task, criterion, taskText: acceptanceTaskText(task), corpus, verifierEvidence, passingVerifier, cwd });
777
+ if (generic.handled) return generic.evidence;
716
778
 
717
779
  if (!corpus.adapter.proofCapable) return undefined;
718
780
 
719
781
  if (obligation === "tenant-boundary") {
720
- return focusedContractEvidence(obligation, task, corpus, verifierEvidence, criterion);
782
+ return focusedContractEvidence(obligation, task, corpus, verifierEvidence, criterion, cwd);
721
783
  }
722
784
 
723
785
  if (obligation === "tenant-storage-isolation") {
724
- return focusedContractEvidence(obligation, task, corpus, verifierEvidence, criterion);
786
+ return focusedContractEvidence(obligation, task, corpus, verifierEvidence, criterion, cwd);
725
787
  }
726
788
 
727
789
  if (obligation === "authorization-deny-case") {
728
- return focusedContractEvidence(obligation, task, corpus, verifierEvidence, criterion);
790
+ return focusedContractEvidence(obligation, task, corpus, verifierEvidence, criterion, cwd);
729
791
  }
730
792
 
731
793
  if (obligation === "invalid-input-rejection") {
@@ -738,7 +800,7 @@ function evidenceForObligation(obligation, task, corpus, currentWorkingTreeDiges
738
800
  namedTargets: namedCodeTargets(task, criterion, corpus.sourceText),
739
801
  provenanceTargets: explicitlyCallableTargets(task, criterion, corpus.sourceText)
740
802
  });
741
- if (passingVerifier && corpus.sourceFiles.length > 0 && corpus.testFiles.length > 0 && sourceOk && testOk) {
803
+ if (passingVerifier && corpus.sourceFiles.length > 0 && verifierCommandsCoverTests(task, corpus.testFiles, cwd) && sourceOk && testOk) {
742
804
  return {
743
805
  ...verifierEvidence,
744
806
  kind: "verifier-backed-focused-test",
@@ -756,7 +818,7 @@ function evidenceForObligation(obligation, task, corpus, currentWorkingTreeDiges
756
818
  const sourceOk = /math\.ceil|ceil|clamp|min|max|round|zero|return\s+0|\?\?|nullish|falsey|falsy|default|retrylimit|enabled|label/.test(corpus.sourceText)
757
819
  || expirySourceOk
758
820
  || (passingVerifier && corpus.sourceFiles.length > 0 && testOk);
759
- if (passingVerifier && corpus.sourceFiles.length > 0 && corpus.testFiles.length > 0 && sourceOk && testOk) {
821
+ if (passingVerifier && corpus.sourceFiles.length > 0 && verifierCommandsCoverTests(task, corpus.testFiles, cwd) && sourceOk && testOk) {
760
822
  return {
761
823
  ...verifierEvidence,
762
824
  kind: "verifier-backed-focused-test",
@@ -767,12 +829,12 @@ function evidenceForObligation(obligation, task, corpus, currentWorkingTreeDiges
767
829
  return undefined;
768
830
  }
769
831
 
770
- return passingVerifier ? verifierEvidence : undefined;
832
+ return genericFallbackEvidence({ behavioral: generic.behavioral, corpus, verifierEvidence, passingVerifier });
771
833
  }
772
834
 
773
835
  export function refreshAcceptanceReceipt(task, options = {}) {
774
836
  const receipt = normalizeAcceptanceReceipt(task?.acceptanceReceipt);
775
- if (!receipt) return { task, missing: [], criticalMissing: [], changed: false };
837
+ if (!receipt) return { task, missing: [], criticalMissing: [], adapterAbstained: [], changed: false };
776
838
  const cwd = options.cwd;
777
839
  const changedFiles = uniqueStrings(options.changedFiles ?? task.changedFiles ?? task.observedChangedFiles ?? []);
778
840
  const currentWorkingTreeDigest = options.currentWorkingTreeDigest;
@@ -787,7 +849,7 @@ export function refreshAcceptanceReceipt(task, options = {}) {
787
849
  criterion.evidence = [];
788
850
  const evidence = task?.workingTreeDigestAlgorithm === WORKING_TREE_DIGEST_ALGORITHM
789
851
  && isCurrentWorkingTreeDigest(currentWorkingTreeDigest)
790
- ? evidenceForObligation(criterion.obligation, task, corpus, currentWorkingTreeDigest, criterion)
852
+ ? evidenceForObligation(criterion.obligation, task, corpus, currentWorkingTreeDigest, criterion, cwd)
791
853
  : undefined;
792
854
  if (evidence) addEvidence(criterion, evidence, recordedAt);
793
855
  const nextEvidence = (criterion.evidence ?? []).map(evidenceKey);
@@ -795,17 +857,16 @@ export function refreshAcceptanceReceipt(task, options = {}) {
795
857
  continue;
796
858
  }
797
859
  if (criterion.status === "satisfied") continue;
798
- const evidence = evidenceForObligation(criterion.obligation, task, corpus, currentWorkingTreeDigest, criterion);
860
+ const evidence = evidenceForObligation(criterion.obligation, task, corpus, currentWorkingTreeDigest, criterion, cwd);
799
861
  if (evidence) changed = addEvidence(criterion, evidence, recordedAt) || changed;
800
862
  }
801
863
  const missing = receipt.criteria.filter((criterion) => criterion.status !== "satisfied");
802
- const criticalMissing = missing.filter((criterion) => criterion.priority === "critical");
864
+ const passingVerifier = hasCurrentPassingVerifier(task, currentWorkingTreeDigest);
865
+ const adapterAbstained = missing.filter((criterion) => criterionBehaviorProofDisposition({ obligation: criterion.obligation, task, criterion, taskText: acceptanceTaskText(task), corpus, passingVerifier }) === "unknown");
866
+ const criticalMissing = missing.filter((criterion) => (criterion.priority === "critical" || criterionRequiresBehavioralProof(task, criterion, acceptanceTaskText(task))
867
+ ));
803
868
  return {
804
- task: { ...task, acceptanceReceipt: receipt },
805
- receipt,
806
- missing,
807
- criticalMissing,
808
- changed
869
+ task: { ...task, acceptanceReceipt: receipt }, receipt, missing, criticalMissing, adapterAbstained, changed
809
870
  };
810
871
  }
811
872
 
@@ -827,9 +888,12 @@ export function acceptanceCriticalRecoveryProjection(task, options = {}) {
827
888
  const verifierCurrent = hasCurrentPassingVerifier(task, currentWorkingTreeDigest);
828
889
  const projections = [];
829
890
  for (const criterion of receipt.criteria.filter((item) => item.priority === "critical")) {
891
+ const disposition = criterionBehaviorProofDisposition({ obligation: criterion.obligation, task, criterion, taskText: acceptanceTaskText(task), corpus, passingVerifier: verifierCurrent });
830
892
  const criterionText = (Array.isArray(task?.acceptanceCriteria) ? task.acceptanceCriteria : [])
831
893
  .find((text) => typeof text === "string" && sha256(text) === criterion.hash && !GENERATED_ACCEPTANCE_TEXTS.has(text));
832
- if (!criterionText || evidenceForObligation(criterion.obligation, task, corpus, currentWorkingTreeDigest, criterion)) continue;
894
+ if (!criterionText) continue;
895
+ if (disposition === "unknown") { projections.push({ criterionId: criterion.id, criterionHash: criterion.hash, criterionText: criterionText.slice(0, 700), targets: [], missingDimensions: ["adapter-linkage"], proofHints: ["Add a focused executable test with a direct relative import to the changed source, or configure a deterministic language adapter that resolves this helper/barrel path; then rerun the exact verifier."] }); continue; }
896
+ if (evidenceForObligation(criterion.obligation, task, corpus, currentWorkingTreeDigest, criterion, cwd)) continue;
833
897
  const targets = namedCodeTargets(task, criterion, corpus.sourceText);
834
898
  const missingDimensions = [];
835
899
  if (!verifierCurrent) missingDimensions.push("current-verifier");
@@ -879,7 +943,7 @@ export function acceptanceReceiptValidationErrors(value) {
879
943
  }
880
944
  if (value.helperUsage !== undefined) {
881
945
  const usage = value.helperUsage;
882
- if (!isRecord(usage) || hasUnsupportedField(usage, HELPER_USAGE_FIELDS) || !["off", "recommend", "on"].includes(usage.mode) || typeof usage.used !== "boolean" || !Array.isArray(usage.reasonCodes) || usage.reasonCodes.length > 16 || usage.reasonCodes.some((item) => typeof item !== "string" || compactId(item) !== item) || !Array.isArray(usage.helpers) || usage.helpers.length > 3 || !validTimestamp(usage.recordedAt)) errors.push("acceptanceReceipt helperUsage is invalid");
946
+ if (!isRecord(usage) || hasUnsupportedField(usage, HELPER_USAGE_FIELDS) || !["off", "recommend", "on"].includes(usage.mode) || typeof usage.used !== "boolean" || (usage.decision !== undefined && !["dispatch", "skip"].includes(usage.decision)) || (usage.projectedSavingsRatio !== undefined && usage.projectedSavingsRatio !== null && (typeof usage.projectedSavingsRatio !== "number" || !Number.isFinite(usage.projectedSavingsRatio) || usage.projectedSavingsRatio < -10 || usage.projectedSavingsRatio > 1)) || !Array.isArray(usage.reasonCodes) || usage.reasonCodes.length > 16 || usage.reasonCodes.some((item) => typeof item !== "string" || compactId(item) !== item) || !Array.isArray(usage.helpers) || usage.helpers.length > 3 || !validTimestamp(usage.recordedAt)) errors.push("acceptanceReceipt helperUsage is invalid");
883
947
  else for (const helper of usage.helpers) if (!isRecord(helper) || hasUnsupportedField(helper, HELPER_ENTRY_FIELDS) || !HELPER_ROLES.has(helper.role) || typeof helper.disposition !== "string" || compactId(helper.disposition) !== helper.disposition || !HASH.test(String(helper.requestRef)) || (helper.outputDigest !== null && !HASH.test(String(helper.outputDigest))) || !Number.isInteger(helper.calls) || helper.calls < 0 || helper.calls > 100 || !Number.isInteger(helper.tokens) || helper.tokens < 0 || helper.tokens > 100_000_000) errors.push("acceptanceReceipt helper entry is invalid");
884
948
  }
885
949
  if (!Array.isArray(value.criteria) || value.criteria.length === 0 || value.criteria.length > MAX_CRITERIA) {