@cleocode/core 2026.10.18 → 2026.10.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/dist/build-provenance.json +2 -2
  2. package/dist/check/pr-gate.js +1 -1
  3. package/dist/check/pr-gate.js.map +1 -1
  4. package/dist/cloud/nexus-vault.d.ts +4 -3
  5. package/dist/cloud/nexus-vault.d.ts.map +1 -1
  6. package/dist/cloud/nexus-vault.js +62 -22
  7. package/dist/cloud/nexus-vault.js.map +1 -1
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +11 -0
  10. package/dist/config.js.map +1 -1
  11. package/dist/docs/conduit-history-backfill.d.ts +210 -0
  12. package/dist/docs/conduit-history-backfill.d.ts.map +1 -0
  13. package/dist/docs/conduit-history-backfill.js +451 -0
  14. package/dist/docs/conduit-history-backfill.js.map +1 -0
  15. package/dist/docs/docs-ops.d.ts +33 -0
  16. package/dist/docs/docs-ops.d.ts.map +1 -1
  17. package/dist/docs/docs-ops.js +72 -0
  18. package/dist/docs/docs-ops.js.map +1 -1
  19. package/dist/docs/docs-update.d.ts.map +1 -1
  20. package/dist/docs/docs-update.js +71 -0
  21. package/dist/docs/docs-update.js.map +1 -1
  22. package/dist/docs/doctor.d.ts +18 -4
  23. package/dist/docs/doctor.d.ts.map +1 -1
  24. package/dist/docs/doctor.js +124 -3
  25. package/dist/docs/doctor.js.map +1 -1
  26. package/dist/docs/section-merge.d.ts +70 -9
  27. package/dist/docs/section-merge.d.ts.map +1 -1
  28. package/dist/docs/section-merge.js +167 -10
  29. package/dist/docs/section-merge.js.map +1 -1
  30. package/dist/docs/section-write-path.d.ts.map +1 -1
  31. package/dist/docs/section-write-path.js +23 -8
  32. package/dist/docs/section-write-path.js.map +1 -1
  33. package/dist/gateway-client/generated/types.gen.d.ts +4 -0
  34. package/dist/gateway-client/generated/types.gen.d.ts.map +1 -1
  35. package/dist/go/driver.d.ts +29 -145
  36. package/dist/go/driver.d.ts.map +1 -1
  37. package/dist/go/driver.js +75 -168
  38. package/dist/go/driver.js.map +1 -1
  39. package/dist/go/index.d.ts +1 -1
  40. package/dist/go/index.d.ts.map +1 -1
  41. package/dist/go/index.js +1 -1
  42. package/dist/go/index.js.map +1 -1
  43. package/dist/goal/close.d.ts +1 -1
  44. package/dist/goal/close.js +2 -2
  45. package/dist/goal/close.js.map +1 -1
  46. package/dist/goal/draft.d.ts +10 -3
  47. package/dist/goal/draft.d.ts.map +1 -1
  48. package/dist/goal/draft.js +14 -9
  49. package/dist/goal/draft.js.map +1 -1
  50. package/dist/goal/index.d.ts +9 -12
  51. package/dist/goal/index.d.ts.map +1 -1
  52. package/dist/goal/index.js +9 -12
  53. package/dist/goal/index.js.map +1 -1
  54. package/dist/goal/runtime.d.ts +126 -0
  55. package/dist/goal/runtime.d.ts.map +1 -0
  56. package/dist/goal/runtime.js +289 -0
  57. package/dist/goal/runtime.js.map +1 -0
  58. package/dist/goal/status.d.ts +39 -0
  59. package/dist/goal/status.d.ts.map +1 -0
  60. package/dist/goal/status.js +101 -0
  61. package/dist/goal/status.js.map +1 -0
  62. package/dist/hooks/project-runner.d.ts +8 -3
  63. package/dist/hooks/project-runner.d.ts.map +1 -1
  64. package/dist/hooks/project-runner.js +104 -14
  65. package/dist/hooks/project-runner.js.map +1 -1
  66. package/dist/hooks/project-state.js +1 -1
  67. package/dist/hooks/project-state.js.map +1 -1
  68. package/dist/internal.d.ts +7 -3
  69. package/dist/internal.d.ts.map +1 -1
  70. package/dist/internal.js +5 -3
  71. package/dist/internal.js.map +1 -1
  72. package/dist/lifecycle/engine-ops.d.ts.map +1 -1
  73. package/dist/lifecycle/engine-ops.js +9 -6
  74. package/dist/lifecycle/engine-ops.js.map +1 -1
  75. package/dist/memory/retrieval/observe.d.ts.map +1 -1
  76. package/dist/memory/retrieval/observe.js +4 -8
  77. package/dist/memory/retrieval/observe.js.map +1 -1
  78. package/dist/paths.d.ts.map +1 -1
  79. package/dist/paths.js +4 -1
  80. package/dist/paths.js.map +1 -1
  81. package/dist/playbooks/cantbook-profile.d.ts +3 -3
  82. package/dist/playbooks/cantbook-profile.js +3 -3
  83. package/dist/playbooks/pi-runner-wiring.d.ts +3 -3
  84. package/dist/playbooks/pi-runner-wiring.js +3 -3
  85. package/dist/release/ci-evidence.d.ts +28 -0
  86. package/dist/release/ci-evidence.d.ts.map +1 -1
  87. package/dist/release/ci-evidence.js +76 -20
  88. package/dist/release/ci-evidence.js.map +1 -1
  89. package/dist/release/open.d.ts +11 -0
  90. package/dist/release/open.d.ts.map +1 -1
  91. package/dist/release/open.js +49 -10
  92. package/dist/release/open.js.map +1 -1
  93. package/dist/release/preflight-skip.d.ts +50 -0
  94. package/dist/release/preflight-skip.d.ts.map +1 -1
  95. package/dist/release/preflight-skip.js +151 -34
  96. package/dist/release/preflight-skip.js.map +1 -1
  97. package/dist/release/reconcile.d.ts.map +1 -1
  98. package/dist/release/reconcile.js +6 -3
  99. package/dist/release/reconcile.js.map +1 -1
  100. package/dist/scaffold/ensure-config.d.ts +1 -1
  101. package/dist/scaffold/ensure-config.d.ts.map +1 -1
  102. package/dist/scaffold/ensure-config.js +2 -0
  103. package/dist/scaffold/ensure-config.js.map +1 -1
  104. package/dist/security/index.d.ts +1 -1
  105. package/dist/security/index.d.ts.map +1 -1
  106. package/dist/security/index.js +1 -1
  107. package/dist/security/index.js.map +1 -1
  108. package/dist/security/override-cap-setting.d.ts +180 -0
  109. package/dist/security/override-cap-setting.d.ts.map +1 -0
  110. package/dist/security/override-cap-setting.js +657 -0
  111. package/dist/security/override-cap-setting.js.map +1 -0
  112. package/dist/security/override-cap.d.ts +20 -29
  113. package/dist/security/override-cap.d.ts.map +1 -1
  114. package/dist/security/override-cap.js +47 -71
  115. package/dist/security/override-cap.js.map +1 -1
  116. package/dist/security/owner-override-auth.d.ts +58 -0
  117. package/dist/security/owner-override-auth.d.ts.map +1 -1
  118. package/dist/security/owner-override-auth.js +82 -1
  119. package/dist/security/owner-override-auth.js.map +1 -1
  120. package/dist/security/owner-override-authority.d.ts +67 -0
  121. package/dist/security/owner-override-authority.d.ts.map +1 -0
  122. package/dist/security/owner-override-authority.js +100 -0
  123. package/dist/security/owner-override-authority.js.map +1 -0
  124. package/dist/sessions/handoff.d.ts.map +1 -1
  125. package/dist/sessions/handoff.js +4 -5
  126. package/dist/sessions/handoff.js.map +1 -1
  127. package/dist/sessions/index.d.ts.map +1 -1
  128. package/dist/sessions/index.js +22 -25
  129. package/dist/sessions/index.js.map +1 -1
  130. package/dist/store/conduit-doc-history.d.ts +39 -7
  131. package/dist/store/conduit-doc-history.d.ts.map +1 -1
  132. package/dist/store/conduit-doc-history.js +62 -12
  133. package/dist/store/conduit-doc-history.js.map +1 -1
  134. package/dist/store/data-safety-central.d.ts.map +1 -1
  135. package/dist/store/data-safety-central.js +3 -2
  136. package/dist/store/data-safety-central.js.map +1 -1
  137. package/dist/store/json.d.ts.map +1 -1
  138. package/dist/store/json.js +7 -7
  139. package/dist/store/json.js.map +1 -1
  140. package/dist/store/portable-bundle-scan.d.ts +23 -1
  141. package/dist/store/portable-bundle-scan.d.ts.map +1 -1
  142. package/dist/store/portable-bundle-scan.js +85 -3
  143. package/dist/store/portable-bundle-scan.js.map +1 -1
  144. package/dist/store/portable-bundle.d.ts.map +1 -1
  145. package/dist/store/portable-bundle.js +6 -1
  146. package/dist/store/portable-bundle.js.map +1 -1
  147. package/dist/store/row-identity-registry.d.ts +5 -3
  148. package/dist/store/row-identity-registry.d.ts.map +1 -1
  149. package/dist/store/row-identity-registry.js +54 -38
  150. package/dist/store/row-identity-registry.js.map +1 -1
  151. package/dist/store/schema/cleo-project/conduit.d.ts.map +1 -1
  152. package/dist/store/schema/cleo-project/conduit.js +4 -3
  153. package/dist/store/schema/cleo-project/conduit.js.map +1 -1
  154. package/dist/store/schema/cleo-project/runtime.d.ts +32 -0
  155. package/dist/store/schema/cleo-project/runtime.d.ts.map +1 -1
  156. package/dist/store/schema/cleo-project/runtime.js +7 -3
  157. package/dist/store/schema/cleo-project/runtime.js.map +1 -1
  158. package/dist/store/session-store.d.ts +45 -0
  159. package/dist/store/session-store.d.ts.map +1 -1
  160. package/dist/store/session-store.js +96 -1
  161. package/dist/store/session-store.js.map +1 -1
  162. package/dist/store/sqlite.d.ts +2 -0
  163. package/dist/store/sqlite.d.ts.map +1 -1
  164. package/dist/store/sqlite.js +12 -0
  165. package/dist/store/sqlite.js.map +1 -1
  166. package/dist/store/sync/merge/rules.d.ts.map +1 -1
  167. package/dist/store/sync/merge/rules.js +0 -16
  168. package/dist/store/sync/merge/rules.js.map +1 -1
  169. package/dist/store/sync/sealer.d.ts.map +1 -1
  170. package/dist/store/sync/sealer.js +27 -1
  171. package/dist/store/sync/sealer.js.map +1 -1
  172. package/dist/store/sync/sequencing.d.ts +3 -1
  173. package/dist/store/sync/sequencing.d.ts.map +1 -1
  174. package/dist/store/sync/sequencing.js +5 -3
  175. package/dist/store/sync/sequencing.js.map +1 -1
  176. package/dist/store/sync/timestamp-canon.d.ts.map +1 -1
  177. package/dist/store/sync/timestamp-canon.js +4 -0
  178. package/dist/store/sync/timestamp-canon.js.map +1 -1
  179. package/dist/store/table-classification.d.ts.map +1 -1
  180. package/dist/store/table-classification.js +12 -8
  181. package/dist/store/table-classification.js.map +1 -1
  182. package/dist/store/twin-collapse.d.ts +21 -0
  183. package/dist/store/twin-collapse.d.ts.map +1 -1
  184. package/dist/store/twin-collapse.js +235 -18
  185. package/dist/store/twin-collapse.js.map +1 -1
  186. package/dist/system/health.d.ts +17 -0
  187. package/dist/system/health.d.ts.map +1 -1
  188. package/dist/system/health.js +96 -0
  189. package/dist/system/health.js.map +1 -1
  190. package/dist/tasks/affected-packages.d.ts +67 -2
  191. package/dist/tasks/affected-packages.d.ts.map +1 -1
  192. package/dist/tasks/affected-packages.js +149 -21
  193. package/dist/tasks/affected-packages.js.map +1 -1
  194. package/dist/tasks/affected-scope.d.ts.map +1 -1
  195. package/dist/tasks/affected-scope.js +91 -5
  196. package/dist/tasks/affected-scope.js.map +1 -1
  197. package/dist/tasks/changeset-plan.d.ts +33 -0
  198. package/dist/tasks/changeset-plan.d.ts.map +1 -0
  199. package/dist/tasks/changeset-plan.js +152 -0
  200. package/dist/tasks/changeset-plan.js.map +1 -0
  201. package/dist/tasks/complete.d.ts.map +1 -1
  202. package/dist/tasks/complete.js +9 -1
  203. package/dist/tasks/complete.js.map +1 -1
  204. package/dist/tasks/done-plan.d.ts.map +1 -1
  205. package/dist/tasks/done-plan.js +28 -14
  206. package/dist/tasks/done-plan.js.map +1 -1
  207. package/dist/tasks/done-record.d.ts.map +1 -1
  208. package/dist/tasks/done-record.js +5 -1
  209. package/dist/tasks/done-record.js.map +1 -1
  210. package/dist/tasks/evidence.d.ts.map +1 -1
  211. package/dist/tasks/evidence.js +149 -18
  212. package/dist/tasks/evidence.js.map +1 -1
  213. package/dist/tasks/gate-runner.d.ts.map +1 -1
  214. package/dist/tasks/gate-runner.js +76 -4
  215. package/dist/tasks/gate-runner.js.map +1 -1
  216. package/dist/tasks/qa-run-binding.d.ts +18 -0
  217. package/dist/tasks/qa-run-binding.d.ts.map +1 -1
  218. package/dist/tasks/qa-run-binding.js +41 -1
  219. package/dist/tasks/qa-run-binding.js.map +1 -1
  220. package/dist/tasks/test-run-binding.d.ts +62 -0
  221. package/dist/tasks/test-run-binding.d.ts.map +1 -1
  222. package/dist/tasks/test-run-binding.js +126 -21
  223. package/dist/tasks/test-run-binding.js.map +1 -1
  224. package/dist/tasks/tool-resolver.d.ts +7 -0
  225. package/dist/tasks/tool-resolver.d.ts.map +1 -1
  226. package/dist/tasks/tool-resolver.js +3 -1
  227. package/dist/tasks/tool-resolver.js.map +1 -1
  228. package/dist/validation/engine-ops.d.ts.map +1 -1
  229. package/dist/validation/engine-ops.js +24 -29
  230. package/dist/validation/engine-ops.js.map +1 -1
  231. package/migrations/drizzle-cleo-project/20261016120000_t12535-playbook-row-uids/migration.sql +24 -0
  232. package/package.json +13 -13
  233. package/schemas/project-context.schema.json +5 -0
  234. package/templates/CLEO-INJECTION.md +1 -1
  235. package/templates/CLEO-REFERENCE.md +42 -1
  236. package/templates/cleo-gitignore +3 -0
  237. package/templates/git-hooks/legacy-hashes.json +1 -0
  238. package/templates/git-hooks/pre-push +35 -7
  239. package/templates/workflows/release-prepare.yml.tmpl +82 -4
  240. package/dist/goal/advance-with-persist.d.ts +0 -80
  241. package/dist/goal/advance-with-persist.d.ts.map +0 -1
  242. package/dist/goal/advance-with-persist.js +0 -105
  243. package/dist/goal/advance-with-persist.js.map +0 -1
  244. package/dist/goal/arm.d.ts +0 -61
  245. package/dist/goal/arm.d.ts.map +0 -1
  246. package/dist/goal/arm.js +0 -65
  247. package/dist/goal/arm.js.map +0 -1
  248. package/dist/goal/continuation.d.ts +0 -66
  249. package/dist/goal/continuation.d.ts.map +0 -1
  250. package/dist/goal/continuation.js +0 -111
  251. package/dist/goal/continuation.js.map +0 -1
  252. package/dist/goal/judge.d.ts +0 -113
  253. package/dist/goal/judge.d.ts.map +0 -1
  254. package/dist/goal/judge.js +0 -193
  255. package/dist/goal/judge.js.map +0 -1
  256. package/dist/goal/loop.d.ts +0 -75
  257. package/dist/goal/loop.d.ts.map +0 -1
  258. package/dist/goal/loop.js +0 -132
  259. package/dist/goal/loop.js.map +0 -1
@@ -34,9 +34,9 @@ import { getEffectiveHead } from '../worktree/effective-head.js';
34
34
  import { loadRecordedProjectRoots, rebaseLegacyEvidencePath } from './evidence-paths.js';
35
35
  import { HEAVY_HEAP_ENV, HEAVY_WORKERS_ENV, isHeavyTool } from './heavy-tool-env.js';
36
36
  import { DISABLE_ENV, describeMemoryLimit } from './heavy-tool-limit.js';
37
- import { qaRunUnionGapReason } from './qa-run-binding.js';
37
+ import { qaRunUnionGapReason, typecheckCoverageGap } from './qa-run-binding.js';
38
38
  import { computeCommitRevalidationKey, readCommitRevalidationEntry, writeCommitRevalidationEntry, } from './revalidation-cache.js';
39
- import { bindTestRunReport, coveredTestFiles, TEST_RUN_MAX_RECORDED_FILES, } from './test-run-binding.js';
39
+ import { bindTestRunReport, configSchemaCompositionGap, coveredTestFiles, TEST_RUN_MAX_RECORDED_FILES, } from './test-run-binding.js';
40
40
  import { captureHead, captureTree, resolveSpawnTimeoutMs, runToolCached, } from './tool-cache.js';
41
41
  import { CANONICAL_TOOLS, canonicalToolName, listValidToolNames, resolveToolCommand, } from './tool-resolver.js';
42
42
  import { detectStaticVacuity, probeTscFileCount, VACUOUS_TOOL_FIX } from './tool-vacuity.js';
@@ -213,7 +213,7 @@ siblingCommitSha, context) {
213
213
  case 'qa-run':
214
214
  return validateQaRun(parsed.path, roots, taskId ?? context?.task.id);
215
215
  case 'tool':
216
- return validateTool(parsed.tool, roots, context);
216
+ return validateTool(parsed.tool, roots, context, siblingCommitSha);
217
217
  case 'url':
218
218
  return validateUrl(parsed.url);
219
219
  case 'note':
@@ -1371,6 +1371,7 @@ async function runAffectedTests(tool, run, roots, taskId) {
1371
1371
  affectedPackages: run.packages,
1372
1372
  affectedProjects: run.projects,
1373
1373
  ...(run.testFiles ? { affectedTestFiles: run.testFiles } : {}),
1374
+ ...(run.configPaths ? { configPaths: run.configPaths } : {}),
1374
1375
  ...(run.untested.length > 0 ? { untestedPackages: run.untested } : {}),
1375
1376
  ...toolRunAtomFields(result),
1376
1377
  },
@@ -1663,12 +1664,16 @@ async function validateTestRun(path, roots, taskId) {
1663
1664
  const identity = treeHash && headSha ? { treeHash, headSha } : null;
1664
1665
  let untestedPackages = [];
1665
1666
  let runHeadSha;
1667
+ let configPaths = [];
1668
+ let typecheckDirs = [];
1666
1669
  if (identity) {
1667
1670
  const binding = bindTestRunReport(parsed, abs, bindRoot, testFiles);
1668
1671
  if (!binding.ok)
1669
1672
  return { ok: false, reason: binding.reason, codeName: binding.codeName };
1670
1673
  untestedPackages = binding.untestedPackages;
1671
1674
  runHeadSha = binding.runHeadSha;
1675
+ configPaths = binding.configPaths ?? [];
1676
+ typecheckDirs = binding.typecheckDirs ?? [];
1672
1677
  }
1673
1678
  return {
1674
1679
  ok: true,
@@ -1692,6 +1697,8 @@ async function validateTestRun(path, roots, taskId) {
1692
1697
  }
1693
1698
  : {}),
1694
1699
  ...(untestedPackages.length > 0 ? { untestedPackages } : {}),
1700
+ ...(configPaths.length > 0 ? { configPaths } : {}),
1701
+ ...(typecheckDirs.length > 0 ? { typecheckDirs } : {}),
1695
1702
  ...(capture.runtimeExcluded.length > 0
1696
1703
  ? { runtimeExcluded: [...capture.runtimeExcluded] }
1697
1704
  : {}),
@@ -1784,6 +1791,7 @@ async function validateQaRun(path, roots, taskId) {
1784
1791
  ? { uncoveredPackages: gaps.uncoveredPackages }
1785
1792
  : {}),
1786
1793
  ...(gaps?.nonCodeChange ? { nonCodeChange: true } : {}),
1794
+ ...(gaps?.configPaths ? { configPaths: gaps.configPaths } : {}),
1787
1795
  },
1788
1796
  };
1789
1797
  }
@@ -1814,31 +1822,107 @@ async function typedGateInputs(storeRoot, taskId, bindRoot) {
1814
1822
  return [];
1815
1823
  }
1816
1824
  }
1825
+ /** Run git in `cwd`, returning trimmed stdout, or null on failure. */
1826
+ function gitOutput(cwd, args) {
1827
+ try {
1828
+ return execFileSync('git', args, {
1829
+ cwd,
1830
+ encoding: 'utf-8',
1831
+ stdio: ['ignore', 'pipe', 'ignore'],
1832
+ }).trim();
1833
+ }
1834
+ catch {
1835
+ return null;
1836
+ }
1837
+ }
1838
+ /**
1839
+ * The task's own merged change at `sha` (T13781, review of #2302): `sha` must
1840
+ * be the merge commit of a `pr:` atom, or one of the `commit:` atoms, recorded
1841
+ * on the task's `implemented` gate. A PR's change is its changed paths, read at
1842
+ * its merge; the commits' change is the union of their own diffs, read at the
1843
+ * newest. Any other commit is refused, however its diff looks.
1844
+ */
1845
+ function taskOwnChange(root, context, sha) {
1846
+ const implemented = context?.task.verification?.evidence?.implemented?.atoms ?? [];
1847
+ const pr = implemented.find((a) => a.kind === 'pr' && a.mergeCommitSha === sha && (a.changedPaths?.length ?? 0) > 0);
1848
+ if (pr)
1849
+ return { ok: true, files: [...(pr.changedPaths ?? [])].sort(), read: sha };
1850
+ const commits = implemented.flatMap((a) => (a.kind === 'commit' ? [a.sha] : []));
1851
+ if (!commits.includes(sha))
1852
+ return {
1853
+ ok: false,
1854
+ reason: `tool:config-schema judges a merged change only at the task's own commit: ${sha.slice(0, 12)} ` +
1855
+ "is neither a commit: nor a pr: merge recorded on this task's implemented gate. Record " +
1856
+ 'implemented first, then name one of its commit:/pr: atoms with tool:config-schema.',
1857
+ };
1858
+ const files = new Set();
1859
+ for (const commit of commits) {
1860
+ const diff = gitOutput(root, ['diff', '--name-only', '--no-renames', `${commit}^1`, commit]);
1861
+ if (diff === null)
1862
+ return {
1863
+ ok: false,
1864
+ reason: `tool:config-schema cannot read the change of ${commit.slice(0, 12)} in ${root}.`,
1865
+ };
1866
+ for (const path of diff.split('\n').filter(Boolean))
1867
+ files.add(path);
1868
+ }
1869
+ const newest = gitOutput(root, ['rev-list', '-1', '--date-order', ...commits]);
1870
+ if (!newest)
1871
+ return { ok: false, reason: `tool:config-schema cannot order the task's commits in ${root}.` };
1872
+ return { ok: true, files: [...files].sort(), read: newest };
1873
+ }
1817
1874
  /**
1818
1875
  * `tool:config-schema` (T13758): CLEO validates each changed CLEO-owned config
1819
- * file against its CLEO schema. It speaks only for a change made of those
1820
- * files alone, and is pinned to the tree like a qa-run receipt.
1876
+ * file against its CLEO schema, pinned to the tree like a qa-run receipt.
1877
+ *
1878
+ * On a change that also touches other paths (T13778) it validates the config
1879
+ * files and records the rest as `otherPaths`; the gate then requires another
1880
+ * result for them (`checkTaskEvidenceContext`). With no change against the
1881
+ * default branch (already merged, T13781) it judges the merge commit(s) of the
1882
+ * write's `pr:` atoms, else its `commit:` atom, reading each file at that
1883
+ * commit, and records `configSchemaCommit` instead of a tree pin.
1821
1884
  */
1822
- async function validateConfigSchema(roots, context) {
1823
- const { cleoConfigSchemaFor, isCleoConfigOnlyChange, scopedChangedPaths } = await import('./affected-packages.js');
1885
+ async function validateConfigSchema(roots, context, siblingCommitSha) {
1886
+ const { cleoConfigSchemaFor, scopedChangedPaths } = await import('./affected-packages.js');
1824
1887
  const root = context?.task.id
1825
1888
  ? (await import('./change-set.js')).resolveChangeSetRoot(roots.storeRoot, context.task.id).root
1826
1889
  : roots.executionRoot;
1827
- const changed = scopedChangedPaths(root)?.paths ?? [];
1828
- if (!isCleoConfigOnlyChange(changed)) {
1829
- const other = changed.filter((p) => cleoConfigSchemaFor(p) === null);
1890
+ let changed = scopedChangedPaths(root)?.paths ?? [];
1891
+ // T13781: a merged change is judged at the commit(s) the write names, and
1892
+ // only when they are the task's OWN change (review of #2302): the change is
1893
+ // what the task merged, never an arbitrary commit's diff.
1894
+ const prs = (context?.artifactPrs ?? []).filter((p) => (p.changedPaths?.length ?? 0) > 0);
1895
+ let anchors = [];
1896
+ let commitOf = (_path) => undefined;
1897
+ if (changed.length === 0 && prs.length > 0) {
1898
+ // pr: atoms of this write: the gate refuses one not linked to the task.
1899
+ anchors = prs.map((p) => p.mergeCommitSha);
1900
+ changed = [...new Set(prs.flatMap((p) => p.changedPaths ?? []))].sort();
1901
+ commitOf = (path) => prMergeCommitForPath(prs, path);
1902
+ }
1903
+ else if (changed.length === 0 && siblingCommitSha) {
1904
+ const own = taskOwnChange(root, context, siblingCommitSha);
1905
+ if (!own.ok)
1906
+ return { ok: false, codeName: 'E_EVIDENCE_INSUFFICIENT', reason: own.reason };
1907
+ anchors = [own.read];
1908
+ changed = own.files;
1909
+ commitOf = () => own.read;
1910
+ }
1911
+ const config = changed.filter((p) => cleoConfigSchemaFor(p) !== null);
1912
+ if (config.length === 0) {
1830
1913
  return {
1831
1914
  ok: false,
1832
1915
  codeName: 'E_EVIDENCE_INSUFFICIENT',
1833
1916
  reason: changed.length === 0
1834
- ? 'tool:config-schema needs a change to judge: no change against the default branch was found.'
1835
- : `tool:config-schema speaks only for a change made of CLEO config alone (.cleo/project-context.json, .cleo/config.json); this change also touches ${other.slice(0, 5).join(', ')}.`,
1917
+ ? 'tool:config-schema needs a change to judge: no change against the default branch was found. ' +
1918
+ 'For an already-merged change, name it in the same write: --evidence "commit:<sha>;tool:config-schema" or "pr:<n>;tool:config-schema".'
1919
+ : `tool:config-schema validates CLEO config (.cleo/project-context.json, .cleo/config.json); this change touches none (${changed.slice(0, 5).join(', ')}).`,
1836
1920
  };
1837
1921
  }
1838
1922
  const { resolveSchemaPath } = await import('../schema-management.js');
1839
1923
  const { checkSchema } = await import('../json-schema-validator.js');
1840
1924
  const failures = [];
1841
- for (const path of changed) {
1925
+ for (const path of config) {
1842
1926
  const schemaName = cleoConfigSchemaFor(path);
1843
1927
  const schemaPath = resolveSchemaPath(schemaName);
1844
1928
  if (!schemaPath)
@@ -1849,7 +1933,15 @@ async function validateConfigSchema(roots, context) {
1849
1933
  };
1850
1934
  let data;
1851
1935
  try {
1852
- data = JSON.parse(await readFile(resolvePath(root, path), 'utf-8'));
1936
+ const at = anchors.length > 0 ? commitOf(path) : undefined;
1937
+ const text = at
1938
+ ? gitOutput(root, ['show', `${at}:${path}`])
1939
+ : await readFile(resolvePath(root, path), 'utf-8');
1940
+ if (text === null) {
1941
+ failures.push(`${path}: not readable at ${at?.slice(0, 12)}`);
1942
+ continue;
1943
+ }
1944
+ data = JSON.parse(text);
1853
1945
  }
1854
1946
  catch (err) {
1855
1947
  failures.push(`${path}: ${err instanceof Error ? err.message : String(err)}`);
@@ -1865,6 +1957,20 @@ async function validateConfigSchema(roots, context) {
1865
1957
  codeName: 'E_EVIDENCE_TOOL_FAILED',
1866
1958
  reason: `tool:config-schema: CLEO's schema check failed:\n - ${failures.join('\n - ')}`,
1867
1959
  };
1960
+ const otherPaths = changed.filter((p) => cleoConfigSchemaFor(p) === null);
1961
+ const rest = otherPaths.length > 0 ? { otherPaths } : {};
1962
+ if (anchors.length > 0)
1963
+ return {
1964
+ ok: true,
1965
+ atom: {
1966
+ kind: 'tool',
1967
+ tool: 'config-schema',
1968
+ exitCode: 0,
1969
+ configSchema: config,
1970
+ configSchemaCommit: anchors.at(-1),
1971
+ ...rest,
1972
+ },
1973
+ };
1868
1974
  const treeHash = (await captureTree(root)).tree;
1869
1975
  if (!treeHash)
1870
1976
  return {
@@ -1874,12 +1980,19 @@ async function validateConfigSchema(roots, context) {
1874
1980
  };
1875
1981
  return {
1876
1982
  ok: true,
1877
- atom: { kind: 'tool', tool: 'config-schema', exitCode: 0, configSchema: changed, treeHash },
1983
+ atom: {
1984
+ kind: 'tool',
1985
+ tool: 'config-schema',
1986
+ exitCode: 0,
1987
+ configSchema: config,
1988
+ treeHash,
1989
+ ...rest,
1990
+ },
1878
1991
  };
1879
1992
  }
1880
- async function validateTool(tool, roots, context) {
1993
+ async function validateTool(tool, roots, context, siblingCommitSha) {
1881
1994
  if (tool === 'config-schema')
1882
- return validateConfigSchema(roots, context);
1995
+ return validateConfigSchema(roots, context, siblingCommitSha);
1883
1996
  // T13745: no test reads a non-code-only change (docs, editor settings), so
1884
1997
  // the gate is satisfied by absence, recorded with the paths, never by a
1885
1998
  // suite. Pinned to the tree like a test-run: once the tree moves (code
@@ -1944,7 +2057,12 @@ async function validateTool(tool, roots, context) {
1944
2057
  const { storeRoot: projectRoot, executionRoot } = roots;
1945
2058
  // T12633: project-context lives in the store; package.json scripts and
1946
2059
  // tsconfig describe the code under test, so they are read where it runs.
1947
- const resolution = resolveToolCommand(tool, projectRoot, { executionRoot });
2060
+ // T13779: a change that edits project-context.json is judged by the default branch's commands.
2061
+ const { evidenceProjectContext } = await import('./affected-packages.js');
2062
+ const resolution = resolveToolCommand(tool, projectRoot, {
2063
+ executionRoot,
2064
+ projectContext: evidenceProjectContext(projectRoot, executionRoot),
2065
+ });
1948
2066
  // T12083: the project has no such toolchain. This is a fact about the
1949
2067
  // project, not a failure of the work — a plain JavaScript project cannot
1950
2068
  // typecheck, and demanding it means no task in that project can ever be
@@ -2761,6 +2879,19 @@ export function checkTaskEvidenceContext(context, gate, atoms) {
2761
2879
  (atom.kind === 'tool' && (!atom.notApplicable || atom.nonCodeChange === true)))) {
2762
2880
  return `Code task ${context.task.id} requires an actual verification result for ${gate}; absence of a toolchain is not a passing result.`;
2763
2881
  }
2882
+ // T13778: CLEO config and the rest of a change are each covered by their own result.
2883
+ if (gate === 'testsPassed' || gate === 'qaPassed') {
2884
+ const gap = configSchemaCompositionGap(atoms);
2885
+ if (gap !== null)
2886
+ return gap;
2887
+ }
2888
+ // T13783: generated-client dependents a test-run left to a typecheck need one.
2889
+ if (gate === 'testsPassed' && !atoms.some((atom) => atom.kind === 'ci')) {
2890
+ const dirs = atoms.flatMap((atom) => atom.kind === 'test-run' ? (atom.typecheckDirs ?? []) : []);
2891
+ const gap = typecheckCoverageGap(atoms, dirs);
2892
+ if (gap !== null)
2893
+ return gap;
2894
+ }
2764
2895
  // T13427: qa-run receipts stand for qaPassed only as a pair — a typecheck
2765
2896
  // and a lint, each by receipt or by a tool: atom (a not-applicable tool:
2766
2897
  // records a toolchain the project lacks). Merged CI stands alone.