testguard-cli 0.15.1__tar.gz → 0.17.0__tar.gz

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 (449) hide show
  1. testguard_cli-0.17.0/.github/scripts/install-smoke.mjs +347 -0
  2. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/CHANGELOG.md +82 -0
  3. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/PKG-INFO +197 -18
  4. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/README.md +196 -17
  5. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/action.yml +1 -1
  6. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/docs/testguard-explained.html +1 -1
  7. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/docs/testguard-explained.pdf +0 -0
  8. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/docs-canonical/DATA-MODEL.md +20 -6
  9. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/docs-canonical/REQUIREMENTS.md +5 -2
  10. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/docs-canonical/TEST-SPEC.md +13 -6
  11. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/package-lock.json +99 -117
  12. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/package.json +1 -1
  13. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
  14. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/packaging/homebrew/testguard.rb +2 -2
  15. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/pyproject.toml +1 -1
  16. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/GATE-SEMANTICS.md +316 -0
  17. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/README.md +27 -2
  18. testguard_cli-0.17.0/spec/conformance/annotations.test.mjs +47 -0
  19. testguard_cli-0.17.0/spec/conformance/authoring-input.test.mjs +76 -0
  20. testguard_cli-0.17.0/spec/conformance/draft-append.test.mjs +18 -0
  21. testguard_cli-0.17.0/spec/conformance/examples/annotations.json +9 -0
  22. testguard_cli-0.17.0/spec/conformance/examples/authoring-input.json +13 -0
  23. testguard_cli-0.17.0/spec/conformance/examples/evidence-resources.json +588 -0
  24. testguard_cli-0.17.0/spec/conformance/examples/status-resource-cost.json +119 -0
  25. testguard_cli-0.17.0/spec/conformance/invalid/evidence.resource-accounting.json +588 -0
  26. testguard_cli-0.17.0/spec/conformance/invalid/evidence.serial-worker-conflict.json +588 -0
  27. testguard_cli-0.17.0/spec/conformance/invalid/status.resource-accounting.json +119 -0
  28. testguard_cli-0.17.0/spec/conformance/origin-policy.test.mjs +71 -0
  29. testguard_cli-0.17.0/spec/conformance/origin-summaries.test.mjs +77 -0
  30. testguard_cli-0.17.0/spec/conformance/origins.test.mjs +66 -0
  31. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/schemas.test.mjs +44 -0
  32. testguard_cli-0.17.0/spec/lib/authoring-input.mjs +40 -0
  33. testguard_cli-0.17.0/spec/lib/origins.mjs +56 -0
  34. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/lib/validate.mjs +129 -1
  35. testguard_cli-0.17.0/spec/schemas/annotations.schema.json +65 -0
  36. testguard_cli-0.17.0/spec/schemas/authoring-input.schema.json +67 -0
  37. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/schemas/brief.schema.json +2 -0
  38. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/schemas/claims.schema.json +8 -8
  39. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/schemas/common.schema.json +73 -0
  40. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/schemas/evidence.schema.json +41 -8
  41. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/schemas/status.schema.json +31 -2
  42. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/brief/brief.mjs +11 -4
  43. testguard_cli-0.17.0/src/claims/annotation-placement.mjs +357 -0
  44. testguard_cli-0.17.0/src/claims/annotations.mjs +118 -0
  45. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/cli.mjs +40 -5
  46. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/commands/admit.mjs +21 -3
  47. testguard_cli-0.17.0/src/commands/annotations.mjs +67 -0
  48. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/commands/claims.mjs +46 -6
  49. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/commands/probe.mjs +50 -7
  50. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/commands/replay.mjs +7 -0
  51. testguard_cli-0.17.0/src/commands/scaffold.mjs +90 -0
  52. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/commands/sweep.mjs +7 -0
  53. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/evidence/writer.mjs +12 -4
  54. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/gate/changed.mjs +5 -1
  55. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/git.mjs +4 -4
  56. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/init/init.mjs +6 -0
  57. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/init/templates/SKILL.md +78 -0
  58. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/attribution.mjs +19 -0
  59. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/contention.mjs +20 -2
  60. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/cost.mjs +22 -9
  61. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/inject.mjs +2 -6
  62. testguard_cli-0.17.0/src/probe/origin-policy.mjs +69 -0
  63. testguard_cli-0.17.0/src/probe/policy-freshness.mjs +74 -0
  64. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/probe.mjs +74 -45
  65. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/runners/discovery.mjs +2 -0
  66. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/runners/jest.mjs +2 -2
  67. testguard_cli-0.17.0/src/probe/runners/lifecycle.mjs +58 -0
  68. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/runners/playwright.mjs +6 -2
  69. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/runners/python.mjs +10 -4
  70. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/runners/shared.mjs +12 -6
  71. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/runners/vitest.mjs +3 -3
  72. testguard_cli-0.17.0/src/probe/universe.mjs +47 -0
  73. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/worktree.mjs +3 -1
  74. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/render.mjs +17 -1
  75. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/replay/replay.mjs +8 -4
  76. testguard_cli-0.17.0/src/scaffold/admission.mjs +117 -0
  77. testguard_cli-0.17.0/src/scaffold/append.mjs +41 -0
  78. testguard_cli-0.17.0/src/scaffold/apply-append.mjs +71 -0
  79. testguard_cli-0.17.0/src/scaffold/fix-input.mjs +119 -0
  80. testguard_cli-0.17.0/src/scaffold/fix-paths.mjs +90 -0
  81. testguard_cli-0.17.0/src/scaffold/intent.mjs +2 -0
  82. testguard_cli-0.17.0/src/scaffold/open-append.mjs +44 -0
  83. testguard_cli-0.17.0/src/scaffold/plan-append.mjs +37 -0
  84. testguard_cli-0.17.0/src/scaffold/plan-intent-input.mjs +38 -0
  85. testguard_cli-0.17.0/src/scaffold/prepare-append.mjs +46 -0
  86. testguard_cli-0.17.0/src/scaffold/request.mjs +30 -0
  87. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/scaffold/scaffold.mjs +23 -10
  88. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/status/status.mjs +42 -10
  89. testguard_cli-0.17.0/src/sweep/pool.mjs +33 -0
  90. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/sweep/sweep.mjs +9 -4
  91. testguard_cli-0.17.0/test/anchor-admission.test.mjs +38 -0
  92. testguard_cli-0.17.0/test/annotation-advisory-limits.test.mjs +75 -0
  93. testguard_cli-0.17.0/test/annotation-advisory.test.mjs +118 -0
  94. testguard_cli-0.17.0/test/annotation-apply.test.mjs +328 -0
  95. testguard_cli-0.17.0/test/annotation-cli.test.mjs +201 -0
  96. testguard_cli-0.17.0/test/annotation-placement.test.mjs +190 -0
  97. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/attribution.test.mjs +40 -1
  98. testguard_cli-0.17.0/test/cli-unexpected-error.test.mjs +37 -0
  99. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/command-budget-write-boundaries.test.mjs +13 -0
  100. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/contention.test.mjs +8 -0
  101. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/cost.test.mjs +20 -3
  102. testguard_cli-0.17.0/test/discovery-config.test.mjs +182 -0
  103. testguard_cli-0.17.0/test/draft-admission.test.mjs +123 -0
  104. testguard_cli-0.17.0/test/draft-append-apply.test.mjs +146 -0
  105. testguard_cli-0.17.0/test/draft-append-cli.test.mjs +96 -0
  106. testguard_cli-0.17.0/test/draft-append-open.test.mjs +90 -0
  107. testguard_cli-0.17.0/test/draft-append-plan.test.mjs +75 -0
  108. testguard_cli-0.17.0/test/draft-append-prepare.test.mjs +130 -0
  109. testguard_cli-0.17.0/test/draft-append.test.mjs +85 -0
  110. testguard_cli-0.17.0/test/fault-defender-orchestration.test.mjs +44 -0
  111. testguard_cli-0.17.0/test/fault-defenders.test.mjs +95 -0
  112. testguard_cli-0.17.0/test/fix-input.test.mjs +108 -0
  113. testguard_cli-0.17.0/test/fix-inventory.test.mjs +156 -0
  114. testguard_cli-0.17.0/test/fix-nested.test.mjs +106 -0
  115. testguard_cli-0.17.0/test/fix-paths.test.mjs +76 -0
  116. testguard_cli-0.17.0/test/fix-projects.test.mjs +66 -0
  117. testguard_cli-0.17.0/test/gate-ref.test.mjs +104 -0
  118. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/gate.test.mjs +16 -127
  119. testguard_cli-0.17.0/test/helpers/gate-project.mjs +35 -0
  120. testguard_cli-0.17.0/test/helpers/probe-assertions.mjs +26 -0
  121. testguard_cli-0.17.0/test/helpers/release-project.mjs +36 -0
  122. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/init.test.mjs +36 -0
  123. testguard_cli-0.17.0/test/intent-authoring.test.mjs +57 -0
  124. testguard_cli-0.17.0/test/intent-document.test.mjs +81 -0
  125. testguard_cli-0.17.0/test/intent-input-cli.test.mjs +85 -0
  126. testguard_cli-0.17.0/test/intent-input-plan.test.mjs +114 -0
  127. testguard_cli-0.17.0/test/intent-input-request.test.mjs +48 -0
  128. testguard_cli-0.17.0/test/intent-project.test.mjs +54 -0
  129. testguard_cli-0.17.0/test/mcp-next.test.mjs +32 -0
  130. testguard_cli-0.17.0/test/mcp-protocol.test.mjs +69 -0
  131. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/mcp.test.mjs +0 -60
  132. testguard_cli-0.17.0/test/native-universe-hash.test.mjs +25 -0
  133. testguard_cli-0.17.0/test/native-universe.test.mjs +51 -0
  134. testguard_cli-0.17.0/test/negative-control-capabilities.test.mjs +35 -0
  135. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/negative-control.test.mjs +0 -29
  136. testguard_cli-0.17.0/test/origin-policy-admission.test.mjs +13 -0
  137. testguard_cli-0.17.0/test/origin-policy-cli.test.mjs +80 -0
  138. testguard_cli-0.17.0/test/origin-policy-projections.test.mjs +78 -0
  139. testguard_cli-0.17.0/test/origin-policy-request.test.mjs +26 -0
  140. testguard_cli-0.17.0/test/origin-policy.test.mjs +95 -0
  141. testguard_cli-0.17.0/test/origin-reporting.test.mjs +91 -0
  142. testguard_cli-0.17.0/test/origin-summary.test.mjs +69 -0
  143. testguard_cli-0.17.0/test/policy-freshness.test.mjs +112 -0
  144. testguard_cli-0.17.0/test/probe-control.test.mjs +18 -0
  145. testguard_cli-0.17.0/test/probe-dirty-admission.test.mjs +35 -0
  146. testguard_cli-0.17.0/test/probe-error-boundary.test.mjs +107 -0
  147. testguard_cli-0.17.0/test/probe-isolation.test.mjs +23 -0
  148. testguard_cli-0.17.0/test/probe-records.test.mjs +110 -0
  149. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/probe.fixture.test.mjs +23 -0
  150. testguard_cli-0.17.0/test/release-sync.test.mjs +127 -0
  151. testguard_cli-0.17.0/test/release-tap.test.mjs +81 -0
  152. testguard_cli-0.17.0/test/release-version.test.mjs +156 -0
  153. testguard_cli-0.17.0/test/replay-selection.test.mjs +105 -0
  154. testguard_cli-0.17.0/test/resource-safety.test.mjs +130 -0
  155. testguard_cli-0.17.0/test/resource-timing.test.mjs +67 -0
  156. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/runner-argv.test.mjs +4 -1
  157. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/runner-discovery.test.mjs +1 -1
  158. testguard_cli-0.17.0/test/runner-output-bounds.test.mjs +30 -0
  159. testguard_cli-0.17.0/test/runner-project-resolution.test.mjs +18 -0
  160. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/runner-python.test.mjs +1 -1
  161. testguard_cli-0.17.0/test/runner-report.test.mjs +83 -0
  162. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/runner-vitest.test.mjs +13 -4
  163. testguard_cli-0.17.0/test/runner-worker-argv.test.mjs +20 -0
  164. testguard_cli-0.17.0/test/scaffold-admitted-source.test.mjs +56 -0
  165. testguard_cli-0.17.0/test/scaffold-placeholder.test.mjs +40 -0
  166. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/scaffold.python.test.mjs +1 -1
  167. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/scaffold.test.mjs +2 -2
  168. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/status.test.mjs +98 -9
  169. testguard_cli-0.17.0/test/sweep-pool.test.mjs +82 -0
  170. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/sweep.fixture.test.mjs +14 -0
  171. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/vitest-config.test.mjs +2 -0
  172. testguard_cli-0.17.0/test/worker-policy.test.mjs +14 -0
  173. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/testguard.claims.json +1440 -51
  174. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/vitest.config.mjs +3 -0
  175. testguard_cli-0.15.1/.github/scripts/install-smoke.mjs +0 -83
  176. testguard_cli-0.15.1/src/claims/annotations.mjs +0 -59
  177. testguard_cli-0.15.1/src/commands/scaffold.mjs +0 -43
  178. testguard_cli-0.15.1/test/release-sync.test.mjs +0 -396
  179. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.agents/skills/testguard/SKILL.md +0 -0
  180. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.docguard.json +0 -0
  181. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.docguardignore +0 -0
  182. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.gitattributes +0 -0
  183. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  184. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  185. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  186. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/dependabot.yml +0 -0
  187. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/scripts/approve-held-runs.sh +0 -0
  188. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/scripts/build-one-pager.mjs +0 -0
  189. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/scripts/check-cost-budget.mjs +0 -0
  190. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/scripts/check-python-fixture.mjs +0 -0
  191. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/scripts/publish-homebrew-tap.sh +0 -0
  192. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/scripts/sync-release-version.mjs +0 -0
  193. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/workflows/auto-merge.yml +0 -0
  194. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/workflows/ci.yml +0 -0
  195. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/workflows/release.yml +0 -0
  196. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/workflows/scheduled-release.yml +0 -0
  197. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.github/workflows/supply-chain.yml +0 -0
  198. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.gitignore +0 -0
  199. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.npmignore +0 -0
  200. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.npmrc +0 -0
  201. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.pre-commit-config.yaml +0 -0
  202. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/.pre-commit-hooks.yaml +0 -0
  203. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/AGENTS.md +0 -0
  204. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/CLAUDE.md +0 -0
  205. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/CODE_OF_CONDUCT.md +0 -0
  206. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/CONTRIBUTING.md +0 -0
  207. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/DRIFT-LOG.md +0 -0
  208. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/LICENSE +0 -0
  209. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/PRIVACY.md +0 -0
  210. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/SECURITY.md +0 -0
  211. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/SUPPORT.md +0 -0
  212. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/bench/README.md +0 -0
  213. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/cli/testguard.mjs +0 -0
  214. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/docs-canonical/ARCHITECTURE.md +0 -0
  215. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/docs-canonical/ENVIRONMENT.md +0 -0
  216. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/docs-canonical/PRIOR-ART.md +0 -0
  217. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/docs-canonical/SECURITY.md +0 -0
  218. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer/.gitignore +0 -0
  219. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer/README.md +0 -0
  220. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer/expected.json +0 -0
  221. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer/package.json +0 -0
  222. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer/src/export.mjs +0 -0
  223. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer/src/redact.mjs +0 -0
  224. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
  225. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer/test/flaky.test.mjs +0 -0
  226. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer/test/redact.test.mjs +0 -0
  227. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer/testguard.claims.json +0 -0
  228. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer/vitest.config.mjs +0 -0
  229. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-jest/.gitignore +0 -0
  230. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-jest/README.md +0 -0
  231. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-jest/expected.json +0 -0
  232. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-jest/jest.config.js +0 -0
  233. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-jest/package.json +0 -0
  234. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-jest/src/export.js +0 -0
  235. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-jest/src/redact.js +0 -0
  236. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
  237. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
  238. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-jest/test/redact.test.js +0 -0
  239. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-jest/testguard.claims.json +0 -0
  240. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-playwright/.gitignore +0 -0
  241. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-playwright/README.md +0 -0
  242. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
  243. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
  244. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-playwright/expected.json +0 -0
  245. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-playwright/package-lock.json +0 -0
  246. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-playwright/package.json +0 -0
  247. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
  248. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
  249. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
  250. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
  251. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
  252. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-python/.gitignore +0 -0
  253. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-python/README.md +0 -0
  254. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-python/demo/__init__.py +0 -0
  255. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-python/demo/export.py +0 -0
  256. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-python/demo/orphan.py +0 -0
  257. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-python/demo/pipeline.py +0 -0
  258. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-python/demo/redact.py +0 -0
  259. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-python/expected.json +0 -0
  260. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-python/testguard.claims.json +0 -0
  261. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
  262. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
  263. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-python/tests/test_patched.py +0 -0
  264. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/fixtures/known-answer-python/tests/test_redact.py +0 -0
  265. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/baseline.json +0 -0
  266. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/brief.json +0 -0
  267. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/calibration-websec.json +0 -0
  268. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/calibration.json +0 -0
  269. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/claims-methods.json +0 -0
  270. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/claims.json +0 -0
  271. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
  272. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/evidence-negative-control.json +0 -0
  273. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/evidence-persistence-signal.json +0 -0
  274. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/evidence-provisional.json +0 -0
  275. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/evidence-python.json +0 -0
  276. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/evidence-scan-method.json +0 -0
  277. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/evidence.json +0 -0
  278. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/gate.json +0 -0
  279. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/ignore.json +0 -0
  280. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/replay.json +0 -0
  281. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/examples/status.json +0 -0
  282. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
  283. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/baseline.zero-count.json +0 -0
  284. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
  285. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
  286. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
  287. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
  288. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
  289. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
  290. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
  291. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
  292. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
  293. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
  294. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/claims.absolute-path.json +0 -0
  295. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
  296. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
  297. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
  298. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
  299. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/claims.noop-fault.json +0 -0
  300. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
  301. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
  302. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
  303. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
  304. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
  305. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
  306. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
  307. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
  308. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
  309. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
  310. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
  311. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
  312. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
  313. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
  314. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
  315. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
  316. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
  317. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.persistence-signal-not-a-defender.json +0 -0
  318. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.persistence-signal-on-a-non-survivor.json +0 -0
  319. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.persistence-signal-without-reason.json +0 -0
  320. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
  321. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
  322. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
  323. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
  324. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
  325. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
  326. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
  327. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
  328. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
  329. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
  330. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
  331. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
  332. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
  333. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
  334. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/ignore.short-reason.json +0 -0
  335. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
  336. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
  337. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/replay.nocover-when-no-test-file-remained.json +0 -0
  338. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/status.claim-without-file.json +0 -0
  339. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
  340. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
  341. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/status.partial-scope-counts-disagree.json +0 -0
  342. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
  343. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
  344. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
  345. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/lib/fingerprint.mjs +0 -0
  346. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/lib/wilson.mjs +0 -0
  347. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/schemas/baseline.schema.json +0 -0
  348. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/schemas/calibration.schema.json +0 -0
  349. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/schemas/concerns.schema.json +0 -0
  350. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/schemas/gate.schema.json +0 -0
  351. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/schemas/ignore.schema.json +0 -0
  352. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/schemas/replay.schema.json +0 -0
  353. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/spec/schemas/sweep.schema.json +0 -0
  354. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/admit/admit.mjs +0 -0
  355. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/baseline/baseline.mjs +0 -0
  356. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/claims/anchors.mjs +0 -0
  357. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/claims/load.mjs +0 -0
  358. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/claims/removed.mjs +0 -0
  359. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/command-budget.mjs +0 -0
  360. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/commands/baseline.mjs +0 -0
  361. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/commands/brief.mjs +0 -0
  362. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/commands/concerns.mjs +0 -0
  363. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/commands/gate.mjs +0 -0
  364. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/commands/init.mjs +0 -0
  365. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/commands/mcp.mjs +0 -0
  366. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/commands/status.mjs +0 -0
  367. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/mcp/server.mjs +0 -0
  368. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/mcp/tools.mjs +0 -0
  369. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/classify.mjs +0 -0
  370. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/discover.mjs +0 -0
  371. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/imports.mjs +0 -0
  372. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/independence.mjs +0 -0
  373. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/mocks.mjs +0 -0
  374. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/progress.mjs +0 -0
  375. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/pyimports.mjs +0 -0
  376. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/rank.mjs +0 -0
  377. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/runners/index.mjs +0 -0
  378. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
  379. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/runners/python/_testguard_report.py +0 -0
  380. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
  381. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/replay/label.mjs +0 -0
  382. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/scaffold/producers.mjs +0 -0
  383. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/scaffold/producers.python.mjs +0 -0
  384. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/status/surface.mjs +0 -0
  385. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/supply/concerns.mjs +0 -0
  386. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/supply/feedback.mjs +0 -0
  387. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/supply/persistence.mjs +0 -0
  388. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/supply/savepath.mjs +0 -0
  389. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/supply/select.mjs +0 -0
  390. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/util/glob.mjs +0 -0
  391. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/src/util/hash.mjs +0 -0
  392. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/admit-decision.test.mjs +0 -0
  393. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/admit.test.mjs +0 -0
  394. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/annotations.test.mjs +0 -0
  395. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/baseline.test.mjs +0 -0
  396. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/brief.test.mjs +0 -0
  397. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/calibration.test.mjs +0 -0
  398. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/claim-selection.test.mjs +0 -0
  399. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/claims-anchors.test.mjs +0 -0
  400. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/claims-since.test.mjs +0 -0
  401. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/classify.test.mjs +0 -0
  402. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/command-budget.test.mjs +0 -0
  403. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/discover.test.mjs +0 -0
  404. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/evidence-persistence-signal.test.mjs +0 -0
  405. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/gitlab-template.test.mjs +0 -0
  406. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/glob.test.mjs +0 -0
  407. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/imports.test.mjs +0 -0
  408. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/independence.test.mjs +0 -0
  409. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/inject.test.mjs +0 -0
  410. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/label.test.mjs +0 -0
  411. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/mocks.test.mjs +0 -0
  412. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/one-pager.test.mjs +0 -0
  413. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/probe-dirty-paths.test.mjs +0 -0
  414. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/probe-error.test.mjs +0 -0
  415. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/probe-native-manifest.test.mjs +0 -0
  416. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/probe-persistence-signal.test.mjs +0 -0
  417. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/probe-preconditions.test.mjs +0 -0
  418. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/probe.jest.test.mjs +0 -0
  419. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/probe.playwright.test.mjs +0 -0
  420. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/probe.python.test.mjs +0 -0
  421. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/progress.test.mjs +0 -0
  422. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/pyimports.test.mjs +0 -0
  423. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/rank-aliases.test.mjs +0 -0
  424. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/release-approval.test.mjs +0 -0
  425. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/replay-mixed-runners.test.mjs +0 -0
  426. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/replay-no-test-file-left.test.mjs +0 -0
  427. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/replay-python-defenders.test.mjs +0 -0
  428. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/replay-verdict.test.mjs +0 -0
  429. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/replay.test.mjs +0 -0
  430. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/runner-command.test.mjs +0 -0
  431. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/runner-playwright.test.mjs +0 -0
  432. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/runner-selection-blind.test.mjs +0 -0
  433. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/samples/playwright-report.json +0 -0
  434. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/samples/python-report-collect-error.json +0 -0
  435. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/samples/python-report.json +0 -0
  436. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/snapshot.test.mjs +0 -0
  437. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/supply-concerns.test.mjs +0 -0
  438. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/supply-feedback.test.mjs +0 -0
  439. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/supply-persistence.test.mjs +0 -0
  440. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/supply-savepath.test.mjs +0 -0
  441. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/supply-select.test.mjs +0 -0
  442. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/surface.test.mjs +0 -0
  443. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/sweep.test.mjs +0 -0
  444. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/worktree.test.mjs +0 -0
  445. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/test/writer.test.mjs +0 -0
  446. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/testguard.cost-budget.json +0 -0
  447. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/testguard.ignore.json +0 -0
  448. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/testguard_cli/__init__.py +0 -0
  449. {testguard_cli-0.15.1 → testguard_cli-0.17.0}/testguard_cli/wrapper.py +0 -0
@@ -0,0 +1,347 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Prove the package works AS INSTALLED, not as checked out: pack it, install
4
+ * the tarball into a scratch project with production dependencies only, and
5
+ * run the CLI from there. This is the gate that would have caught 0.1.0,
6
+ * which shipped with its schema validator's dependency declared as a
7
+ * devDependency and crashed on startup for every npm/npx/pip/brew user.
8
+ */
9
+ import { execFileSync } from 'node:child_process';
10
+ import { mkdtempSync, mkdirSync, rmSync, cpSync, readdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
11
+ import { tmpdir, devNull } from 'node:os';
12
+ import { join, resolve } from 'node:path';
13
+
14
+ const root = resolve(process.argv[1], '..', '..', '..');
15
+ const run = (cmd, args, cwd) => execFileSync(cmd, args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit'] });
16
+ const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
17
+
18
+ const scratch = mkdtempSync(join(tmpdir(), 'testguard-install-smoke-'));
19
+ const annotationRecovery = [];
20
+ try {
21
+ const packDir = join(scratch, 'pack');
22
+ mkdirSync(packDir);
23
+ run(npm, ['pack', '--pack-destination', packDir, '--ignore-scripts'], root);
24
+ const tgz = join(packDir, readdirSync(packDir).find((f) => f.endsWith('.tgz')));
25
+
26
+ const consumer = join(scratch, 'consumer');
27
+ cpSync(join(root, 'fixtures', 'known-answer'), consumer, { recursive: true, filter: (s) => !/node_modules|\.flake-counter|\.testguard/.test(s) });
28
+ run(npm, ['init', '-y'], consumer);
29
+ run(npm, ['install', '--omit=dev', '--ignore-scripts', '--no-audit', '--no-fund', tgz], consumer);
30
+
31
+ const bin = join(consumer, 'node_modules', '.bin', process.platform === 'win32' ? 'testguard.cmd' : 'testguard');
32
+ const version = run(bin, ['--version'], consumer).trim();
33
+ const emptyProject = join(scratch, 'empty-adoption');
34
+ mkdirSync(emptyProject);
35
+ writeFileSync(join(emptyProject, 'testguard.claims.json'), JSON.stringify({ schemaVersion: 1, claims: [] }));
36
+ const emptyStatus = JSON.parse(run(bin, ['probe', emptyProject, '--allow-empty', '--json'], consumer));
37
+ if (emptyStatus.state !== 'no-claims' || emptyStatus.run || existsSync(join(emptyProject, '.testguard', 'evidence.json'))) throw new Error('installed empty-adoption probe invented verification evidence');
38
+ const replayHelp = run(bin, ['replay', '--help'], consumer);
39
+ if (!replayHelp.includes('--since') || replayHelp.includes('--node-modules')) throw new Error('installed command help is not command-specific');
40
+ writeFileSync(join(emptyProject, 'vite.config.mjs'), "export default { base: '/', plugins: [{ configureServer(s) { s.middlewares.use('/__rooms', () => {}); const parts = '/__room'.split('/'); } }] };\n");
41
+ const installedDiscovery = join(consumer, 'node_modules', 'testguard-cli', 'src', 'probe', 'runners', 'discovery.mjs');
42
+ run(process.execPath, ['--input-type=module', '-e',
43
+ "import { pathToFileURL } from 'node:url'; const { hashDiscoveryConfigs } = await import(pathToFileURL(process.argv[1])); hashDiscoveryConfigs(process.argv[2], ['vite.config.mjs']);",
44
+ installedDiscovery, emptyProject,
45
+ ], consumer);
46
+ const installedRunner = join(consumer, 'node_modules', 'testguard-cli', 'src', 'probe', 'runners', 'shared.mjs');
47
+ run(process.execPath, ['--input-type=module', '-e',
48
+ `import { pathToFileURL } from 'node:url'; const { parseReport } = await import(pathToFileURL(process.argv[1]));
49
+ const result = parseReport({ success: false, numTotalTests: 2, numFailedTests: 2, testResults: [{ name: 'test.mjs', status: 'failed', assertionResults: [
50
+ { status: 'failed', failureMessages: ['AssertionError: expected /specify timed out/'] },
51
+ { status: 'failed', failureMessages: ['Error: Test timed out in 1000ms.'] },
52
+ ] }] }, 1);
53
+ if (result.timeouts !== 1 || result.run.assertionFailures !== 1) throw new Error('installed runner confuses assertion text with timeout identity');`,
54
+ installedRunner,
55
+ ], consumer);
56
+ const claims = run(bin, ['claims', '.'], consumer);
57
+ const draft = JSON.parse(run(bin, ['scaffold', 'src/redact.mjs', '--json'], consumer));
58
+ const installedValidator = join(consumer, 'node_modules', 'testguard-cli', 'spec', 'lib', 'validate.mjs');
59
+ run(process.execPath, ['--input-type=module', '-e',
60
+ `import { pathToFileURL } from 'node:url';
61
+ const { intentInputRequest } = await import(new URL('../../src/scaffold/request.mjs', pathToFileURL(process.argv[1])));
62
+ const doc = intentInputRequest({ command: 'scaffold', values: { 'from-document': ['requirements.md'] }, suppliedOptions: ['from-document', 'json'] });
63
+ if (JSON.stringify(doc) !== JSON.stringify({ kind: 'document', file: 'requirements.md' }) || !Object.isFrozen(doc)) throw new Error('installed intent request promotes or mutates input');
64
+ let refused = false;
65
+ try { intentInputRequest({ command: 'scaffold', values: { 'from-fix': ['HEAD'] } }); } catch (error) { refused = /full-object-id/.test(error.message); }
66
+ if (!refused) throw new Error('installed intent request accepts symbolic fix reference');`,
67
+ installedValidator,
68
+ ], consumer);
69
+ const fixProject = join(scratch, 'fix-input');
70
+ mkdirSync(fixProject);
71
+ const fixGit = args => run('git', ['-c', 'user.name=fixture', '-c', 'user.email=fixture@example.invalid', '-c', `core.hooksPath=${devNull}`, ...args], fixProject).trim();
72
+ fixGit(['init', '-q']);
73
+ writeFileSync(join(fixProject, 'guard.mjs'), 'export const x = 1;');
74
+ mkdirSync(join(fixProject, 'child'));
75
+ writeFileSync(join(fixProject, 'child', 'testguard.claims.json'), 'untrusted historical marker');
76
+ fixGit(['add', '.']); fixGit(['commit', '-qm', 'Initial fixture']);
77
+ const fixParent = fixGit(['rev-parse', 'HEAD']);
78
+ writeFileSync(join(fixProject, 'guard.mjs'), 'export const x = 2;');
79
+ fixGit(['add', '.']); fixGit(['commit', '-qm', 'Supplied fix metadata']);
80
+ const fixCommit = fixGit(['rev-parse', 'HEAD']);
81
+ writeFileSync(join(fixProject, 'placeholder.mjs'), 'export function guard(x) {\n if (!x) return false;\n}\n');
82
+ const placeholderDraft = JSON.parse(run(bin, ['scaffold', 'placeholder.mjs', '--json'], fixProject));
83
+ if (!placeholderDraft.claims.length || placeholderDraft.claims.some(claim => !/^TODO-CLAIM-[1-9][0-9]*$/.test(claim.id))) throw new Error('installed scaffold still emits finished file-derived default IDs');
84
+ const rootInputCli = JSON.parse(run(bin, ['scaffold', '--from-fix', fixCommit, '--json'], fixProject));
85
+ if (rootInputCli.input.scope !== 'project-root' || rootInputCli.input.commit !== fixCommit || rootInputCli.verification !== 'not-performed') throw new Error('installed root input CLI lost fixed identity or truthful verification');
86
+ run(process.execPath, ['--input-type=module', '-e',
87
+ `import { pathToFileURL } from 'node:url';
88
+ const { readFixCommitMetadata, readFixCommitInventory } = await import(new URL('../../src/scaffold/fix-input.mjs', pathToFileURL(process.argv[1])));
89
+ const metadata = readFixCommitMetadata({ projectDir: process.argv[2], commit: process.argv[3] });
90
+ if (metadata.commit !== process.argv[3] || metadata.parent !== process.argv[4] || metadata.subject !== 'Supplied fix metadata' || metadata.claims !== undefined || !Object.isFrozen(metadata)) throw new Error('installed fix metadata failed identity or no-claims boundary');
91
+ const scoped = readFixCommitInventory({ projectDir: process.argv[2], commit: process.argv[3] });
92
+ const { planIntentInput } = await import(new URL('../../src/scaffold/plan-intent-input.mjs', pathToFileURL(process.argv[1])));
93
+ const plan = planIntentInput({ projectDir: process.argv[2], values: { 'from-fix': [process.argv[3]] } });
94
+ if (plan.doc.input.scope !== 'project-root' || plan.doc.input.commit !== metadata.commit || plan.doc.input.parent !== metadata.parent || plan.doc.input.counts.supported !== 1 || plan.doc.verification !== 'not-performed' || JSON.stringify(JSON.parse(plan.output)) !== JSON.stringify(plan.doc) || !Object.isFrozen(plan.doc.input.paths[0])) throw new Error('installed root input planner lost validated identity or scope');
95
+ if (scoped.commit !== metadata.commit || scoped.parent !== metadata.parent || scoped.counts.total !== 1 || scoped.counts.supported !== 1 || scoped.paths[0].file !== 'guard.mjs' || !Object.isFrozen(scoped) || !Object.isFrozen(scoped.paths) || scoped.claims !== undefined) throw new Error('installed scoped inventory failed identity or no-claims boundary');
96
+ const { parseFixChangedPaths, parseFixProjectMarkers } = await import(new URL('../../src/scaffold/fix-paths.mjs', pathToFileURL(process.argv[1])));
97
+ const { execFileSync } = await import('node:child_process');
98
+ const raw = execFileSync('git', ['--no-lazy-fetch', '--no-replace-objects', 'diff-tree', '--raw', '--no-abbrev', '-z', '--no-renames', '--no-ext-diff', '--no-textconv', '-r', '--no-commit-id', metadata.parent, metadata.commit, '--'], { cwd: process.argv[2], timeout: 5000, maxBuffer: 128 * 1024 });
99
+ const inventory = parseFixChangedPaths(raw);
100
+ if (inventory.counts.total !== 1 || inventory.counts.supported !== 1 || inventory.paths[0].file !== 'guard.mjs' || inventory.claims !== undefined || !Object.isFrozen(inventory.paths)) throw new Error('installed raw fix decoder lost complete identity or no-claims boundary');
101
+ const tree = id => execFileSync('git', ['--no-lazy-fetch', '--no-replace-objects', 'ls-tree', '-r', '--name-only', '-z', '--full-tree', id], { cwd: process.argv[2], timeout: 5000, maxBuffer: 128 * 1024 });
102
+ const projects = parseFixProjectMarkers(tree(metadata.parent), tree(metadata.commit));
103
+ if (projects.length !== 1 || projects[0] !== 'child' || !Object.isFrozen(projects) || projects.claims !== undefined) throw new Error('installed historical project decoder lost immutable delegation');`,
104
+ installedValidator, fixProject, fixCommit, fixParent,
105
+ ], consumer);
106
+ const nestedFixProject = join(fixProject, 'nested[1]');
107
+ mkdirSync(nestedFixProject);
108
+ writeFileSync(join(nestedFixProject, 'testguard.claims.json'), '{"schemaVersion":1,"claims":[]}');
109
+ writeFileSync(join(nestedFixProject, 'guard.mjs'), 'export const x = 1;');
110
+ fixGit(['add', '.']); fixGit(['commit', '-qm', 'Nested fixture']);
111
+ const nestedFixParent = fixGit(['rev-parse', 'HEAD']);
112
+ writeFileSync(join(nestedFixProject, 'guard.mjs'), 'export const x = 2;');
113
+ fixGit(['add', '.']); fixGit(['commit', '-qm', 'Nested fix']);
114
+ const nestedFixCommit = fixGit(['rev-parse', 'HEAD']);
115
+ const nestedInputCli = JSON.parse(run(bin, ['scaffold', '--from-fix', nestedFixCommit, '--json'], nestedFixProject));
116
+ if (nestedInputCli.input.scope !== 'selected-nested-project' || nestedInputCli.input.counts.supported !== 1 || nestedInputCli.input.paths[0].file !== 'guard.mjs') throw new Error('installed nested input CLI lost literal project scope');
117
+ writeFileSync(join(fixProject, 'intent.md'), 'UNTRUSTED INPUT TEXT');
118
+ const documentInputCli = run(bin, ['scaffold', '--from-document', 'intent.md', '--json'], fixProject);
119
+ if (JSON.parse(documentInputCli).input.bytes !== Buffer.byteLength('UNTRUSTED INPUT TEXT') || documentInputCli.includes('UNTRUSTED INPUT TEXT')) throw new Error('installed document input CLI leaked text or lost size');
120
+ const humanInputCli = run(bin, ['scaffold', '--from-document', 'intent.md'], fixProject);
121
+ if (!humanInputCli.includes('verification not performed') || !humanInputCli.includes('independent intent') || humanInputCli.includes('UNTRUSTED INPUT TEXT') || existsSync(join(fixProject, '.testguard'))) throw new Error('installed human input CLI lost read-only or truthful boundary');
122
+ run(process.execPath, ['--input-type=module', '-e',
123
+ `import { pathToFileURL } from 'node:url';
124
+ const { readNestedFixCommitInventory } = await import(new URL('../../src/scaffold/fix-input.mjs', pathToFileURL(process.argv[1])));
125
+ const inventory = readNestedFixCommitInventory({ projectDir: process.argv[2], commit: process.argv[3] });
126
+ const { planIntentInput } = await import(new URL('../../src/scaffold/plan-intent-input.mjs', pathToFileURL(process.argv[1])));
127
+ const plan = planIntentInput({ projectDir: process.argv[2], values: { 'from-fix': [process.argv[3]] } });
128
+ if (plan.doc.input.scope !== 'selected-nested-project' || plan.doc.input.commit !== inventory.commit || plan.doc.input.parent !== inventory.parent || plan.doc.input.paths[0].file !== 'guard.mjs' || plan.doc.input.counts.total !== 1 || plan.doc.verification !== 'not-performed' || !Object.isFrozen(plan.doc.input.counts)) throw new Error('installed nested input planner lost validated literal scope');
129
+ if (inventory.commit !== process.argv[3] || inventory.parent !== process.argv[4] || inventory.counts.total !== 1 || inventory.counts.supported !== 1 || inventory.paths[0].file !== 'guard.mjs' || inventory.claims !== undefined || !Object.isFrozen(inventory) || !Object.isFrozen(inventory.paths[0])) throw new Error('installed nested inventory lost literal scope or immutable identity');`,
130
+ installedValidator, nestedFixProject, nestedFixCommit, nestedFixParent,
131
+ ], consumer);
132
+ const annotationProject = join(scratch, 'annotation-authoring');
133
+ mkdirSync(annotationProject);
134
+ writeFileSync(join(annotationProject, 'guard.mjs'), 'export const allowed = true;\n');
135
+ const annotationClaims = JSON.stringify({ schemaVersion: 1, claims: [{ id: 'A-1', statement: 'The intended condition holds.', severity: 'high', source: { kind: 'manual' }, producedBy: { producer: 'human' }, faults: [{ id: 'F1', description: 'Break condition.', faultClass: 'other', file: 'guard.mjs', find: 'true', replace: 'false', producedBy: { producer: 'human' } }] }] });
136
+ writeFileSync(join(annotationProject, 'testguard.claims.json'), annotationClaims);
137
+ const annotationInspection = JSON.parse(run(bin, ['claims', annotationProject, '--json'], consumer));
138
+ if (annotationInspection.annotationAdvisory.missingIds.join(',') !== 'A-1' || !annotationInspection.annotationAdvisory.notes.some((n) => n.includes('testguard claims --annotate'))) throw new Error('installed claims omitted source-link advice');
139
+ let annotationStatusText;
140
+ try { annotationStatusText = run(bin, ['status', annotationProject, '--json'], consumer); }
141
+ catch (error) { if (error.status !== 2) throw error; annotationStatusText = error.stdout; }
142
+ const annotationStatus = JSON.parse(annotationStatusText);
143
+ if (annotationStatus.origins?.basis !== 'declared' || annotationStatus.origins.claims.total !== 1 || annotationStatus.origins.claims.byKind.manual !== 1) throw new Error('installed status omitted current declared origins');
144
+ if (annotationStatus.state !== 'unprobed' || annotationStatus.next.action !== 'probe' || !annotationStatus.notes.some((n) => n.includes('ANNOTATION ADVISORY'))) throw new Error('installed annotation advice changed or omitted verification guidance');
145
+ const annotationPreview = JSON.parse(run(bin, ['claims', annotationProject, '--annotate', '--json'], consumer));
146
+ if (annotationPreview.state !== 'preview' || readFileSync(join(annotationProject, 'guard.mjs'), 'utf8') !== 'export const allowed = true;\n') throw new Error('installed annotation preview wrote source or failed');
147
+ const annotationApply = JSON.parse(run(bin, ['claims', annotationProject, '--annotate', '--apply', '--json'], consumer));
148
+ if (annotationApply.outcome?.recoveryDir) annotationRecovery.push(annotationApply.outcome.recoveryDir);
149
+ if (annotationApply.state !== 'applied' || annotationApply.outcome?.changed.join(',') !== 'guard.mjs' || readFileSync(join(annotationProject, 'guard.mjs'), 'utf8') !== '// @claim A-1\nexport const allowed = true;\n' || readFileSync(join(annotationProject, 'testguard.claims.json'), 'utf8') !== annotationClaims || existsSync(join(annotationProject, '.testguard'))) throw new Error('installed annotation apply failed its source/claims/evidence boundary');
150
+ run(process.execPath, ['--input-type=module', '-e',
151
+ `import { pathToFileURL } from 'node:url'; const { validate } = await import(pathToFileURL(process.argv[1]));
152
+ for (const doc of JSON.parse(process.argv[2])) if (!validate('annotations', doc).ok) throw new Error('installed authoring result failed its own validator');`,
153
+ installedValidator, JSON.stringify([annotationPreview, annotationApply]),
154
+ ], consumer);
155
+ const candidateEvidence = join(root, 'spec', 'conformance', 'examples', 'evidence.json');
156
+ const validatesCandidateEvidence = run(process.execPath, [
157
+ '--input-type=module',
158
+ '-e',
159
+ `import { pathToFileURL } from 'node:url'; import { readFileSync, writeFileSync, rmSync, mkdirSync } from 'node:fs'; import { createHash } from 'node:crypto';
160
+ const { validate } = await import(pathToFileURL(process.argv[1]));
161
+ const candidate = JSON.parse(readFileSync(process.argv[2], 'utf8'));
162
+ for (const r of candidate.records) {
163
+ r.defenders.selectionSource = r.defenders.requested.length ? 'claim' : 'discovery';
164
+ if (!r.defenders.requested.length) r.defenders.discovered = true;
165
+ }
166
+ candidate.run.workers = 1;
167
+ candidate.run.measurements = { elapsedMs: 12, runnerMs: 9, overheadMs: 3, runnerInvocations: 6 };
168
+ const result = validate('evidence', candidate);
169
+ if (!result.ok) throw new Error(result.errors.map((error) => \`${'${error.path}: ${error.message}'}\`).join('; '));
170
+ candidate.run.measurements.overheadMs = 4;
171
+ if (validate('evidence', candidate).ok) throw new Error('installed package accepted inconsistent resource timing');
172
+ candidate.run.measurements.overheadMs = 3;
173
+ const claims = JSON.parse(readFileSync(process.argv[4], 'utf8'));
174
+ claims.claims[0].faults[0].defendedBy = [];
175
+ if (!validate('claims', claims).ok) throw new Error('installed package rejected a fault discovery override');
176
+ claims.claims[0].faults[0].defendedBy = ['test/unit.test.mjs'];
177
+ if (!validate('claims', claims).ok) throw new Error('installed package rejected a fault defender override');
178
+ const { appendDraft } = await import(new URL('../../src/scaffold/append.mjs', pathToFileURL(process.argv[1])));
179
+ const appendProposals = structuredClone(claims);
180
+ appendProposals.claims = [appendProposals.claims[0]];
181
+ appendProposals.claims[0].faults = [{ ...appendProposals.claims[0].faults[0], file: 'src/append-new.mjs' }];
182
+ const originalDraft = JSON.stringify(claims);
183
+ const appended = appendDraft({ draft: claims, proposals: appendProposals, claimId: claims.claims[0].id });
184
+ if (!validate('claims', appended).ok || appended.claims[0].faults.length !== claims.claims[0].faults.length + 1 || JSON.stringify(claims) !== originalDraft || JSON.stringify(appended.claims[0].source) !== JSON.stringify(claims.claims[0].source)) throw new Error('installed append lost preservation or admission');
185
+ const repeated = appendDraft({ draft: appended, proposals: appendProposals, claimId: claims.claims[0].id });
186
+ if (JSON.stringify(repeated) !== JSON.stringify(appended)) throw new Error('installed append is not idempotent');
187
+ const { poolDrafts } = await import(new URL('../../src/sweep/pool.mjs', pathToFileURL(process.argv[1])));
188
+ const pooledDrafts = poolDrafts([claims, structuredClone(claims)]);
189
+ if (!validate('claims', { schemaVersion: 1, claims: pooledDrafts.flatMap((d) => d.claims) }).ok || pooledDrafts.flatMap((d) => d.claims).length !== claims.claims.length * 2 || JSON.stringify(claims) !== originalDraft) throw new Error('installed sweep pool lost identity or preservation');
190
+ const { scaffoldFile } = await import(new URL('../../src/scaffold/scaffold.mjs', pathToFileURL(process.argv[1])));
191
+ const admittedArgs = { projectDir: process.cwd(), file: 'missing-admitted-source.mjs', source: ['export function guard(x) {', ' if (!x) throw new Error("absent");', '}'].join(String.fromCharCode(10)) };
192
+ const admitted = scaffoldFile({ ...admittedArgs, maxProposals: 10 });
193
+ if (!validate('claims', admitted.doc).ok || admitted.stats.proposals < 1) throw new Error('installed scaffold did not scan admitted source');
194
+ if (scaffoldFile({ ...admittedArgs, source: '', maxProposals: 0 }).stats.proposals !== 0) throw new Error('installed scaffold reread empty source');
195
+ let refusedLimit = false;
196
+ try { scaffoldFile({ ...admittedArgs, maxProposals: 0 }); } catch (error) { refusedLimit = error.message.includes('proposal limit'); }
197
+ if (!refusedLimit) throw new Error('installed scaffold ignored proposal limit');
198
+ const { admitDraftAppend, admitIntentDocument, admitIntentProject } = await import(new URL('../../src/scaffold/admission.mjs', pathToFileURL(process.argv[1])));
199
+ const intentProjectRoot = process.cwd() + '/intent-project-marker';
200
+ mkdirSync(intentProjectRoot); const markerBytes = JSON.stringify({ schemaVersion: 1, claims: [] });
201
+ writeFileSync(intentProjectRoot + '/testguard.claims.json', markerBytes);
202
+ const intentProject = admitIntentProject({ projectDir: intentProjectRoot });
203
+ if (intentProject.marker.hash !== createHash('sha256').update(markerBytes).digest('hex') || !Object.isFrozen(intentProject) || !Object.isFrozen(intentProject.marker) || !Object.isFrozen(intentProject.marker.identity) || intentProject.claims !== undefined || intentProject.marker.source !== undefined || readFileSync(intentProjectRoot + '/testguard.claims.json', 'utf8') !== markerBytes) throw new Error('installed project marker admission lost bounded identity or no-claims boundary');
204
+ writeFileSync('intent-input.md', 'Absent input must be rejected.');
205
+ const intentInput = admitIntentDocument({ projectDir: process.cwd(), file: 'intent-input.md' });
206
+ if (intentInput.source !== 'Absent input must be rejected.' || !Object.isFrozen(intentInput) || !Object.isFrozen(intentInput.identity) || intentInput.claims !== undefined || readFileSync('intent-input.md', 'utf8') !== intentInput.source) throw new Error('installed document admission lost text, immutability or no-claims boundary');
207
+ const inputReport = { schemaVersion: 1, tool: { name: 'testguard', version: 'test' }, purpose: 'authoring-input', verification: 'not-performed', next: 'supply-independent-intent', input: { kind: 'document', file: intentInput.file, hash: intentInput.hash, bytes: intentInput.identity.size } };
208
+ if (!validate('authoring-input', inputReport).ok) throw new Error('installed input report contract rejected admitted metadata');
209
+ const { planIntentInput } = await import(new URL('../../src/scaffold/plan-intent-input.mjs', pathToFileURL(process.argv[1])));
210
+ const documentPlan = planIntentInput({ projectDir: process.cwd(), values: { 'from-document': ['intent-input.md'] }, toolVersion: 'test' });
211
+ if (JSON.stringify(documentPlan.doc) !== JSON.stringify(inputReport) || JSON.stringify(JSON.parse(documentPlan.output)) !== JSON.stringify(inputReport) || documentPlan.output.includes(intentInput.source) || !Object.isFrozen(documentPlan) || !Object.isFrozen(documentPlan.doc.input)) throw new Error('installed document planner lost metadata-only validated projection');
212
+ const { writeSpecDoc, readSpecDoc } = await import(new URL('../../src/evidence/writer.mjs', pathToFileURL(process.argv[1])));
213
+ let inputReportBytes;
214
+ writeSpecDoc('authoring-input', 'unused.json', inputReport, { publish: bytes => { inputReportBytes = bytes; } });
215
+ if (JSON.stringify(readSpecDoc('authoring-input', 'unused.json', { source: inputReportBytes })) !== JSON.stringify(inputReport) || inputReportBytes.includes(intentInput.source)) throw new Error('installed input report lost validation or leaked document text');
216
+ const optimisticReport = { ...inputReport, verification: 'killed' };
217
+ if (validate('authoring-input', optimisticReport).ok) throw new Error('installed input report admits an optimistic verification label');
218
+ let refusedPrivateDocument = false;
219
+ try { admitIntentDocument({ projectDir: process.cwd(), file: '.local/secret.md' }); } catch (error) { refusedPrivateDocument = error.message.includes('excluded-document'); }
220
+ if (!refusedPrivateDocument) throw new Error('installed document admission accepted private input');
221
+ writeFileSync('append-admission-draft.json', JSON.stringify(claims));
222
+ writeFileSync('append-admission-source.mjs', admittedArgs.source);
223
+ const admittedInputs = admitDraftAppend({ projectDir: process.cwd(), draftPath: 'append-admission-draft.json', files: ['append-admission-source.mjs'], claimId: claims.claims[0].id });
224
+ if (!validate('claims', admittedInputs.draft.doc).ok || admittedInputs.sources[0].source !== admittedArgs.source || readFileSync('append-admission-draft.json', 'utf8') !== JSON.stringify(claims)) throw new Error('installed draft admission lost validated input or wrote the draft');
225
+ const { planDraftAppend } = await import(new URL('../../src/scaffold/plan-append.mjs', pathToFileURL(process.argv[1])));
226
+ const planRequest = { projectDir: process.cwd(), draftPath: 'append-admission-draft.json', files: ['append-admission-source.mjs'], claimId: claims.claims[0].id };
227
+ const planned = planDraftAppend(planRequest);
228
+ if (!validate('claims', planned.doc).ok || !planned.changed || planned.stats.appended < 1 || readFileSync('append-admission-draft.json', 'utf8') !== JSON.stringify(claims)) throw new Error('installed append planner lost proposals or published during preview');
229
+ const { prepareDraftAppend } = await import(new URL('../../src/scaffold/prepare-append.mjs', pathToFileURL(process.argv[1])));
230
+ const preparedDraftBytes = readFileSync('append-admission-draft.json', 'utf8');
231
+ const prepared = prepareDraftAppend({ request: planRequest, plan: planned });
232
+ try {
233
+ if (!prepared.ready || readFileSync('append-admission-draft.json', 'utf8') !== preparedDraftBytes || readFileSync(prepared.recoveryDir + '/draft.original', 'utf8') !== preparedDraftBytes) throw new Error('installed append preparation changed draft or lost recovery');
234
+ prepared.assertOwned();
235
+ } finally {
236
+ const released = prepared.release();
237
+ rmSync(prepared.recoveryDir, { recursive: true, force: true });
238
+ if (!released.released) throw new Error('installed append preparation lost lock ownership');
239
+ }
240
+ const { openDraftAppend } = await import(new URL('../../src/scaffold/open-append.mjs', pathToFileURL(process.argv[1])));
241
+ const openedDraft = openDraftAppend({ request: planRequest, plan: planned });
242
+ try {
243
+ openedDraft.assertCurrent();
244
+ if (typeof openedDraft.fd !== 'number' || readFileSync('append-admission-draft.json', 'utf8') !== preparedDraftBytes) throw new Error('installed append descriptor changed the draft');
245
+ } finally {
246
+ const released = openedDraft.release();
247
+ rmSync(openedDraft.recoveryDir, { recursive: true, force: true });
248
+ if (!released.released) throw new Error('installed append descriptor release failed');
249
+ }
250
+ const { applyDraftAppend } = await import(new URL('../../src/scaffold/apply-append.mjs', pathToFileURL(process.argv[1])));
251
+ const appliedDraft = applyDraftAppend({ request: planRequest, plan: planned });
252
+ try {
253
+ if (!appliedDraft.ok || appliedDraft.state !== 'updated' || readFileSync('append-admission-draft.json', 'utf8') !== planned.output) throw new Error('installed append publication failed');
254
+ const repeatedDraft = applyDraftAppend({ request: planRequest, plan: planDraftAppend(planRequest) });
255
+ if (!repeatedDraft.ok || repeatedDraft.state !== 'unchanged' || repeatedDraft.recoveryDir) throw new Error('installed append repeat wrote or lost idempotence');
256
+ } finally { if (appliedDraft.recoveryDir) rmSync(appliedDraft.recoveryDir, { recursive: true, force: true }); }
257
+ if (planDraftAppend(planRequest).changed) throw new Error('installed append planner lost repeat idempotence');
258
+ for (const kind of ['spec', 'adr', 'annotation', 'comment', 'manual', 'doc', 'bug', 'incident', 'review', 'inferred']) {
259
+ claims.claims[0].source.kind = kind; candidate.records[0].claim.source.kind = kind;
260
+ if (!validate('claims', claims).ok || !validate('evidence', candidate).ok) throw new Error('installed package rejected a declared origin: ' + kind);
261
+ }
262
+ const { recordedOriginSummary } = await import(new URL('./origins.mjs', pathToFileURL(process.argv[1])));
263
+ candidate.origins = recordedOriginSummary(candidate.records);
264
+ if (!validate('evidence', candidate).ok) throw new Error('installed package rejected its recorded origin summary');
265
+ const { buildBrief } = await import(new URL('../../src/brief/brief.mjs', pathToFileURL(process.argv[1])));
266
+ const brief = buildBrief(candidate, undefined, { max: 0 });
267
+ if (!validate('brief', brief).ok || brief.origins.records.total !== candidate.records.length || !brief.text.includes('declared origins, not authenticated independence')) throw new Error('installed brief lost full recorded origins or caveat');
268
+ const oldKind = Object.keys(candidate.origins.claims.byKind).find((kind) => candidate.origins.claims.byKind[kind] > 0);
269
+ candidate.origins.claims.byKind[oldKind]--; candidate.origins.claims.byKind.bug++;
270
+ if (validate('evidence', candidate).ok) throw new Error('installed package accepted an invented origin split');
271
+ delete candidate.origins;
272
+ candidate.originPolicy = {
273
+ state: 'unavailable', eligibleKinds: ['spec'],
274
+ claims: new Set(candidate.records.map((r) => r.claim.id)).size,
275
+ faults: candidate.records.length, ineligibleClaims: [],
276
+ nonKilledFaults: [...new Map(candidate.records.filter((r) => r.verdict !== 'killed').map((r) => [JSON.stringify([r.claim.id, r.subject.id]), { claimId: r.claim.id, faultId: r.subject.id }])).values()],
277
+ unavailableReasons: ['stale-inputs'],
278
+ };
279
+ if (!validate('evidence', candidate).ok) throw new Error('installed package rejected the unavailable origin policy contract');
280
+ const policyBrief = buildBrief(candidate, undefined, { max: 0 });
281
+ if (!validate('brief', policyBrief).ok || JSON.stringify(policyBrief.originPolicy) !== JSON.stringify(candidate.originPolicy) || !policyBrief.text.includes('not current freshness') || policyBrief.text.includes('Every probed claim is defended')) throw new Error('installed brief lost policy scope or invented capped defense');
282
+ candidate.originPolicy.state = 'passed';
283
+ if (validate('evidence', candidate).ok) throw new Error('installed package accepted an optimistic origin policy');
284
+ delete candidate.originPolicy;
285
+ const { originPolicyRequest } = await import(new URL('../../src/probe/origin-policy.mjs', pathToFileURL(process.argv[1])));
286
+ if (JSON.stringify(originPolicyRequest({ 'require-origin': ['spec,bug', 'spec'] })) !== JSON.stringify(['bug', 'spec'])) throw new Error('installed candidate lost origin-policy request admission');
287
+ let refusedPartialPolicy = false;
288
+ try { originPolicyRequest({ 'require-origin': ['spec'], claim: ['C-1'] }); } catch (e) { refusedPartialPolicy = e instanceof TypeError; }
289
+ if (!refusedPartialPolicy) throw new Error('installed candidate accepted a partial origin policy request');
290
+ const { hashNativeTestUniverse } = await import(new URL('../../src/probe/universe.mjs', pathToFileURL(process.argv[1])));
291
+ const { createDiscoveryManifest } = await import(new URL('../../src/probe/runners/discovery.mjs', pathToFileURL(process.argv[1])));
292
+ const nativeManifest = createDiscoveryManifest({ runner: 'vitest', version: '1', files: ['test/a.test.mjs'] });
293
+ const initialUniverse = hashNativeTestUniverse(new Map([[{ name: 'vitest' }, nativeManifest]]));
294
+ const changedManifest = createDiscoveryManifest({ runner: 'vitest', version: '1', files: ['test/a.test.mjs', 'test/b.test.mjs'] });
295
+ if (initialUniverse === hashNativeTestUniverse(new Map([[{ name: 'vitest' }, changedManifest]]))) throw new Error('installed package ignored native universe membership');
296
+ const { originPolicyInputsFresh } = await import(new URL('../../src/probe/policy-freshness.mjs', pathToFileURL(process.argv[1])));
297
+ if (originPolicyInputsFresh({ claims, evidence: candidate }) !== false) throw new Error('installed package admitted missing runtime policy bindings');
298
+ const { main: candidateMain } = await import(new URL('../../src/cli.mjs', pathToFileURL(process.argv[1])));
299
+ writeFileSync('append-cli-draft.json', JSON.stringify(claims));
300
+ const appendCliArgs = ['scaffold', 'append-admission-source.mjs', '--into', 'append-cli-draft.json', '--claim', claims.claims[0].id];
301
+ const appendPreview = [];
302
+ const appendPreviewCode = await candidateMain([...appendCliArgs, '--json'], { out: s => appendPreview.push(s), err: s => { throw new Error(s); } });
303
+ if (appendPreviewCode !== 0 || !validate('claims', JSON.parse(appendPreview.join(''))).ok || readFileSync('append-cli-draft.json', 'utf8') !== JSON.stringify(claims)) throw new Error('installed append CLI preview failed or wrote');
304
+ const appendCliOutput = [];
305
+ try {
306
+ const appendCliCode = await candidateMain(appendCliArgs, { out: s => appendCliOutput.push(s), err: s => { throw new Error(s); } });
307
+ if (appendCliCode !== 0 || !appendCliOutput.some(s => s.startsWith('unproven draft updated:')) || !validate('claims', JSON.parse(readFileSync('append-cli-draft.json', 'utf8'))).ok) throw new Error('installed append CLI update failed');
308
+ } finally {
309
+ for (const line of appendCliOutput) if (line.startsWith('private recovery: ')) rmSync(line.slice('private recovery: '.length), { recursive: true, force: true });
310
+ }
311
+ const policyErrors = [];
312
+ const policyCode = await candidateMain(['probe', '--require-origin', 'spec', '--confirm', '1', '--claims', '/nonexistent/policy-smoke.claims.json'], { out: () => { throw new Error('invalid policy emitted output'); }, err: (s) => policyErrors.push(s) });
313
+ if (policyCode !== 3 || !policyErrors.some((s) => s.includes('--require-origin'))) throw new Error('installed candidate lost CLI origin-policy admission');
314
+ claims.claims[0].source.kind = 'authenticated'; candidate.records[0].claim.source.kind = 'authenticated';
315
+ if (validate('claims', claims).ok || validate('evidence', candidate).ok) throw new Error('installed package accepted an unsupported origin');
316
+ const gate = JSON.parse(readFileSync(process.argv[3], 'utf8'));
317
+ gate.nested = [{ project: 'backend', files: ['backend/src/guard.mjs'] }]; gate.changed++;
318
+ const nestedResult = validate('gate', gate);
319
+ if (!nestedResult.ok) throw new Error('installed package rejected the nested-project gate contract');
320
+ process.stdout.write('ok');`,
321
+ installedValidator,
322
+ candidateEvidence,
323
+ join(root, 'spec', 'conformance', 'examples', 'gate.json'),
324
+ join(root, 'spec', 'conformance', 'examples', 'claims.json'),
325
+ ], consumer).trim();
326
+ if (!draft.claims?.length) throw new Error('scaffold produced no claims from the installed tarball');
327
+ const suppliedClaims = JSON.parse(readFileSync(join(consumer, 'testguard.claims.json'), 'utf8')).claims;
328
+ let inferredDrafts = 0;
329
+ for (const claim of draft.claims) {
330
+ const supplied = suppliedClaims.find((c) => c.id === claim.id);
331
+ if (supplied) {
332
+ if (claim.statement !== supplied.statement || JSON.stringify(claim.source) !== JSON.stringify(supplied.source)) throw new Error('installed scaffold overwrote supplied intent');
333
+ } else {
334
+ inferredDrafts++;
335
+ if (claim.source.kind !== 'inferred' || !claim.statement.includes('intended observable behavior')) throw new Error('installed scaffold lost inferred intent handoff');
336
+ }
337
+ }
338
+ if (!inferredDrafts) throw new Error('installed smoke did not exercise a new inferred draft');
339
+ if (!/^\d+\.\d+\.\d+/.test(version)) throw new Error(`unexpected --version output: ${version}`);
340
+ if (!/^\d+ claims in /.test(claims)) throw new Error(`claims did not list the fixture:\n${claims}`);
341
+ if (validatesCandidateEvidence !== 'ok') throw new Error('installed package did not validate candidate-format evidence');
342
+ const deps = Object.keys(JSON.parse(run(npm, ['ls', '--omit=dev', '--json', '--depth=0'], consumer)).dependencies ?? {});
343
+ console.log(`install smoke OK — testguard ${version} runs from the installed tarball; consumer deps: ${deps.join(', ')}`);
344
+ } finally {
345
+ for (const dir of annotationRecovery) rmSync(dir, { recursive: true, force: true });
346
+ rmSync(scratch, { recursive: true, force: true });
347
+ }
@@ -7,6 +7,88 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.17.0] - 2026-10-01
11
+
12
+ Automated weekly release — everything merged since `v0.16.0`.
13
+
14
+ ### Changed
15
+
16
+ - feat: integrate adoption guidance and machine-safe probing (#189)
17
+ - chore(deps-dev): bump vitest from 5.0.1 to 5.0.2 in the all-npm group (#187)
18
+ - chore(homebrew): sha256 for v0.16.0 (#186)
19
+
20
+
21
+ ### Added
22
+
23
+ - Unannotated scaffold defaults use unfinished `TODO-CLAIM-N` identifiers rather than file/function-shaped claim names. Allocation avoids supplied IDs; explicit IDs/annotations and existing evidence remain unchanged, while sweep pooling preserves distinct candidates.
24
+
25
+ - Read-only `scaffold --from-document <local-text-path>` and `--from-fix <full-commit-ID>` inspection, backed by the closed `authoring-input` report contract. Validation retains historical partition counts and rejects unsafe/private paths, inconsistent identities and optimistic verification labels. Stdout-only output emits no document text or claims; input inspection requires independent intent and is not verification.
26
+
27
+ - Explicit `scaffold <source...> --into <existing-draft.json> --claim <ID>` appends mechanically proposed faults while preserving supplied intent and existing faults. `--json` previews without writes; ordinary scaffold remains compatible. Draft updates use bounded admission, owned locking and private recovery, and refuse stale/unsafe inputs or unconfirmed writes rather than claiming verified coverage.
28
+
29
+ - Sweep keeps equal-basename and repeated-annotation drafts distinct before selection, assigning collision-free pooled claim IDs without changing fault content, metadata or defenders.
30
+
31
+ - New mechanical scaffold drafts declare `inferred` origin and ask for intended observable behavior from a requirement, ADR, bug or incident. CLI/status/skill guidance explains the source handoff without authenticating references or promoting annotations/tests into independent intent. Existing supplied scaffold metadata is preserved.
32
+
33
+ - Explicit `probe --require-origin <kinds>` checks declared origins on complete confirmed current-project probes, rechecks native universe and file bindings, and reports failed/unavailable policy without suppressing survivors through baselines or severity floors. Kinds do not authenticate independence; custom/static universes are unavailable. Offline status audits current declarations without certifying freshness; brief retains labeled recorded-run policy before caps and filtering.
34
+
35
+ - Candidate evidence/status/brief schemas accept a closed optional declared-origin policy result, with unsuppressed fault identities, refusal reasons, consistent states and probe exit codes. This is an audit contract, not authenticated independence.
36
+
37
+ - Claim sources share a closed origin vocabulary across claims and evidence, adding `doc`, `bug`, `incident`, `review` and `inferred`. Status reports current declared origins; evidence and briefs summarize all recorded origins before filtering/caps, separating distinct claims from records and reporting conflicting declarations. Human output labels these as unauthenticated declarations. Labels do not change default gates; origin policy requires explicit selection.
38
+
39
+ - Claims/status missing-source-link advisories identify distinct claim IDs and suggest read-only annotation preview without changing verification state, next action or exit codes; annotation-sourced claims retain their existing drift error.
40
+
41
+ - Explicit `claims --annotate` read-only file-header preview and `--apply` authoring for JavaScript/TypeScript and Python, with claim selection, bounded all-target admission, owned locking, private recoverable originals and truthful partial-failure reporting. Authoring JSON uses a closed `annotations` contract; placement is not verification or authentication of intent.
42
+
43
+ ### Fixed
44
+
45
+ - Clarify that cold probes and changed discovery/configuration can require full verification; unchanged target and defender files alone do not guarantee a cheap CI run.
46
+
47
+ - Status marks evidence stale when a recorded automatic-discovery dependency (including a negative candidate, barrel or resolver config) changes or becomes unavailable. Previously unchanged defender lists could hide this drift. Native test-universe policy freshness remains pending.
48
+
49
+ - Annotation scanning and placement ID admission avoid excessive regex backtracking on long alphanumeric tokens without a hyphen; status advice uses bounded, failure-aware scanning so unrelated source failures cannot prevent its verification decision.
50
+
51
+ - Claim statement, severity, source and producer edits now invalidate cached probe evidence and appear as stale in status. Old kills cannot silently carry an edited requirement or provenance declaration.
52
+
53
+ ### Changed
54
+
55
+ - Reduce repeated self-probe setup with focused defenders for 40 existing fault checks, retaining the complete native integration regressions, all 327 fault recipes, three-run confirmation and existing cost budgets.
56
+ - Bound built-in runner concurrency to one worker by default, with `--workers` overrides, and cap outer regression workers at two (one on a two-core machine).
57
+ - Drain noisy runner stdout, bound diagnostics and report reads, and terminate owned runners before restoring mutations and scratch worktrees on cancellation.
58
+ - Run the project-pinned Playwright CLI directly instead of spawning npm for each confirmation, and reset Python interpreter caches at measurement boundaries.
59
+ - Record actual probe elapsed time and unique fresh runner invocations separately from historical attributed costs; preserve all confirmation and cost gates, and invalidate reuse when native worker policy changes.
60
+
61
+ ## [0.16.0] - 2026-09-30
62
+
63
+ Automated weekly release — everything merged since `v0.15.1`.
64
+
65
+ ### Changed
66
+
67
+ - feat: fault-specific defenders, provenance and cost reporting (#184)
68
+ - Add advisory AI update guidance and stabilize cleanup regression (#183)
69
+ - chore(homebrew): sha256 for v0.15.1 (#182)
70
+
71
+
72
+ ### Added
73
+
74
+ - Fault-specific `defendedBy` overrides, with explicit empty-list discovery,
75
+ evidence selection provenance, per-fault cost reporting and prior-kill
76
+ narrowing warnings (#90). Selection changes invalidate reuse and status;
77
+ admission and changed-file coverage use the actual selected defenders.
78
+
79
+ ### Changed
80
+
81
+ - New AI integrations include permission-aware, once-per-session update advice
82
+ that identifies the actual CLI and requests approval before any upgrade.
83
+ CLI commands and session-start hooks remain offline. Existing customized
84
+ instructions remain untouched; safe manual refresh is documented.
85
+
86
+ ### Fixed
87
+
88
+ - The daemon-cleanup regression test now requires explicit startup and
89
+ post-cleanup release signals instead of assuming a detached process starts
90
+ and writes within fixed sleeps. Timeout and cleanup semantics are unchanged.
91
+
10
92
  ## [0.15.1] - 2026-09-30
11
93
 
12
94
  Automated weekly release — everything merged since `v0.15.0`.