@aperant/framework 0.8.1 → 0.8.6

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 (308) hide show
  1. package/CHANGELOG.md +443 -1
  2. package/agents/apt-planner.md +39 -7
  3. package/dist/cli/commands/adr.mjs +1 -1
  4. package/dist/cli/commands/audit-branch-current.d.mts +25 -0
  5. package/dist/cli/commands/audit-branch-current.d.mts.map +1 -0
  6. package/dist/cli/commands/audit-branch-current.mjs +252 -0
  7. package/dist/cli/commands/audit-branch-current.mjs.map +1 -0
  8. package/dist/cli/commands/audit.d.mts.map +1 -1
  9. package/dist/cli/commands/audit.mjs +13 -2
  10. package/dist/cli/commands/audit.mjs.map +1 -1
  11. package/dist/cli/commands/check-version.d.mts.map +1 -1
  12. package/dist/cli/commands/check-version.mjs +19 -1
  13. package/dist/cli/commands/check-version.mjs.map +1 -1
  14. package/dist/cli/commands/context.mjs +3 -3
  15. package/dist/cli/commands/context.mjs.map +1 -1
  16. package/dist/cli/commands/coverage-check.d.mts.map +1 -1
  17. package/dist/cli/commands/coverage-check.mjs +74 -7
  18. package/dist/cli/commands/coverage-check.mjs.map +1 -1
  19. package/dist/cli/commands/detect-runtime.d.mts +6 -0
  20. package/dist/cli/commands/detect-runtime.d.mts.map +1 -0
  21. package/dist/cli/commands/detect-runtime.mjs +142 -0
  22. package/dist/cli/commands/detect-runtime.mjs.map +1 -0
  23. package/dist/cli/commands/driver-doctor.d.mts +29 -0
  24. package/dist/cli/commands/driver-doctor.d.mts.map +1 -0
  25. package/dist/cli/commands/driver-doctor.mjs +291 -0
  26. package/dist/cli/commands/driver-doctor.mjs.map +1 -0
  27. package/dist/cli/commands/gate.mjs +14 -0
  28. package/dist/cli/commands/gate.mjs.map +1 -1
  29. package/dist/cli/commands/init.d.mts.map +1 -1
  30. package/dist/cli/commands/init.mjs +9 -6
  31. package/dist/cli/commands/init.mjs.map +1 -1
  32. package/dist/cli/commands/install-from-source.d.mts +2 -0
  33. package/dist/cli/commands/install-from-source.d.mts.map +1 -0
  34. package/dist/cli/commands/install-from-source.mjs +2 -0
  35. package/dist/cli/commands/install-from-source.mjs.map +1 -0
  36. package/dist/cli/commands/modes.mjs +1 -1
  37. package/dist/cli/commands/route.d.mts.map +1 -1
  38. package/dist/cli/commands/route.mjs +38 -1
  39. package/dist/cli/commands/route.mjs.map +1 -1
  40. package/dist/cli/commands/triage.mjs +1 -1
  41. package/dist/cli/config/load.d.mts.map +1 -1
  42. package/dist/cli/config/load.mjs +9 -3
  43. package/dist/cli/config/load.mjs.map +1 -1
  44. package/dist/cli/coordination/auto-emit-artifact-ready.mjs +1 -1
  45. package/dist/cli/coordination/event-schema.d.mts.map +1 -1
  46. package/dist/cli/coordination/event-schema.mjs +18 -0
  47. package/dist/cli/coordination/event-schema.mjs.map +1 -1
  48. package/dist/cli/coverage-check/user-outcomes.d.mts +84 -0
  49. package/dist/cli/coverage-check/user-outcomes.d.mts.map +1 -0
  50. package/dist/cli/coverage-check/user-outcomes.mjs +265 -0
  51. package/dist/cli/coverage-check/user-outcomes.mjs.map +1 -0
  52. package/dist/cli/dispatch.d.mts.map +1 -1
  53. package/dist/cli/dispatch.mjs +18 -1
  54. package/dist/cli/dispatch.mjs.map +1 -1
  55. package/dist/cli/gate/context.d.mts +1 -0
  56. package/dist/cli/gate/context.d.mts.map +1 -1
  57. package/dist/cli/gate/context.mjs +14 -1
  58. package/dist/cli/gate/context.mjs.map +1 -1
  59. package/dist/cli/gate/gates/consistency-check.d.mts +1 -0
  60. package/dist/cli/gate/gates/consistency-check.d.mts.map +1 -1
  61. package/dist/cli/gate/gates/consistency-check.mjs +1 -0
  62. package/dist/cli/gate/gates/consistency-check.mjs.map +1 -1
  63. package/dist/cli/gate/gates/review-clean.d.mts +1 -0
  64. package/dist/cli/gate/gates/review-clean.d.mts.map +1 -1
  65. package/dist/cli/gate/gates/review-clean.mjs +1 -0
  66. package/dist/cli/gate/gates/review-clean.mjs.map +1 -1
  67. package/dist/cli/gate/gates/verify-approved.d.mts +1 -0
  68. package/dist/cli/gate/gates/verify-approved.d.mts.map +1 -1
  69. package/dist/cli/gate/gates/verify-approved.mjs +1 -0
  70. package/dist/cli/gate/gates/verify-approved.mjs.map +1 -1
  71. package/dist/cli/gate/registry.d.mts.map +1 -1
  72. package/dist/cli/gate/registry.mjs +9 -1
  73. package/dist/cli/gate/registry.mjs.map +1 -1
  74. package/dist/cli/host/detect.mjs +1 -1
  75. package/dist/cli/host/detect.mjs.map +1 -1
  76. package/dist/cli/install/find-workspace-framework-version.d.mts +16 -0
  77. package/dist/cli/install/find-workspace-framework-version.d.mts.map +1 -0
  78. package/dist/cli/install/find-workspace-framework-version.mjs +85 -0
  79. package/dist/cli/install/find-workspace-framework-version.mjs.map +1 -0
  80. package/dist/cli/install/install-from-source.d.mts +10 -0
  81. package/dist/cli/install/install-from-source.d.mts.map +1 -0
  82. package/dist/cli/install/install-from-source.mjs +271 -0
  83. package/dist/cli/install/install-from-source.mjs.map +1 -0
  84. package/dist/cli/install/install-kernel.d.mts.map +1 -1
  85. package/dist/cli/install/install-kernel.mjs +24 -0
  86. package/dist/cli/install/install-kernel.mjs.map +1 -1
  87. package/dist/cli/install/legacy-paths.d.mts +33 -11
  88. package/dist/cli/install/legacy-paths.d.mts.map +1 -1
  89. package/dist/cli/install/legacy-paths.mjs +163 -39
  90. package/dist/cli/install/legacy-paths.mjs.map +1 -1
  91. package/dist/cli/install/runtime-detect.mjs +1 -1
  92. package/dist/cli/install/runtime-detect.mjs.map +1 -1
  93. package/dist/cli/install/runtime-migrate.d.mts +34 -14
  94. package/dist/cli/install/runtime-migrate.d.mts.map +1 -1
  95. package/dist/cli/install/runtime-migrate.mjs +63 -30
  96. package/dist/cli/install/runtime-migrate.mjs.map +1 -1
  97. package/dist/cli/install/transforms/codex.d.mts.map +1 -1
  98. package/dist/cli/install/transforms/codex.mjs +28 -17
  99. package/dist/cli/install/transforms/codex.mjs.map +1 -1
  100. package/dist/cli/skill-author/contract.mjs +2 -2
  101. package/dist/cli/skill-author/contract.mjs.map +1 -1
  102. package/dist/cli/verify-proof/audit.d.mts +34 -0
  103. package/dist/cli/verify-proof/audit.d.mts.map +1 -0
  104. package/dist/cli/verify-proof/audit.mjs +53 -0
  105. package/dist/cli/verify-proof/audit.mjs.map +1 -0
  106. package/dist/cli/verify-proof/exec.d.mts +20 -0
  107. package/dist/cli/verify-proof/exec.d.mts.map +1 -0
  108. package/dist/cli/verify-proof/exec.mjs +74 -0
  109. package/dist/cli/verify-proof/exec.mjs.map +1 -0
  110. package/dist/cli/verify-proof/idl/index.d.mts +2 -0
  111. package/dist/cli/verify-proof/idl/index.d.mts.map +1 -0
  112. package/dist/cli/verify-proof/idl/index.mjs +13 -0
  113. package/dist/cli/verify-proof/idl/index.mjs.map +1 -0
  114. package/dist/cli/verify-proof/idl/types.d.ts +10 -0
  115. package/dist/cli/verify-proof/idl/types.d.ts.map +1 -0
  116. package/dist/cli/verify-proof/idl/types.js +10 -0
  117. package/dist/cli/verify-proof/idl/types.js.map +1 -0
  118. package/dist/cli/verify-proof/manifest-validator.d.mts +19 -0
  119. package/dist/cli/verify-proof/manifest-validator.d.mts.map +1 -0
  120. package/dist/cli/verify-proof/manifest-validator.mjs +182 -0
  121. package/dist/cli/verify-proof/manifest-validator.mjs.map +1 -0
  122. package/dist/cli/verify-proof/resolver.d.mts +105 -0
  123. package/dist/cli/verify-proof/resolver.d.mts.map +1 -0
  124. package/dist/cli/verify-proof/resolver.mjs +320 -0
  125. package/dist/cli/verify-proof/resolver.mjs.map +1 -0
  126. package/dist/cli/verify-proof/runtime-detect.d.mts +30 -0
  127. package/dist/cli/verify-proof/runtime-detect.d.mts.map +1 -0
  128. package/dist/cli/verify-proof/runtime-detect.mjs +209 -0
  129. package/dist/cli/verify-proof/runtime-detect.mjs.map +1 -0
  130. package/dist/cli/verify-proof/trust.d.mts +70 -0
  131. package/dist/cli/verify-proof/trust.d.mts.map +1 -0
  132. package/dist/cli/verify-proof/trust.mjs +174 -0
  133. package/dist/cli/verify-proof/trust.mjs.map +1 -0
  134. package/dist/driver-sdk/conformance.d.ts +41 -0
  135. package/dist/driver-sdk/conformance.d.ts.map +1 -0
  136. package/dist/driver-sdk/conformance.js +123 -0
  137. package/dist/driver-sdk/conformance.js.map +1 -0
  138. package/dist/driver-sdk/errors.d.ts +75 -0
  139. package/dist/driver-sdk/errors.d.ts.map +1 -0
  140. package/dist/driver-sdk/errors.js +80 -0
  141. package/dist/driver-sdk/errors.js.map +1 -0
  142. package/dist/driver-sdk/idl.d.ts +221 -0
  143. package/dist/driver-sdk/idl.d.ts.map +1 -0
  144. package/dist/driver-sdk/idl.js +140 -0
  145. package/dist/driver-sdk/idl.js.map +1 -0
  146. package/dist/driver-sdk/index.d.ts +28 -0
  147. package/dist/driver-sdk/index.d.ts.map +1 -0
  148. package/dist/driver-sdk/index.js +28 -0
  149. package/dist/driver-sdk/index.js.map +1 -0
  150. package/dist/driver-sdk/manifest.d.ts +93 -0
  151. package/dist/driver-sdk/manifest.d.ts.map +1 -0
  152. package/dist/driver-sdk/manifest.js +12 -0
  153. package/dist/driver-sdk/manifest.js.map +1 -0
  154. package/dist/driver-sdk/retry.d.ts +33 -0
  155. package/dist/driver-sdk/retry.d.ts.map +1 -0
  156. package/dist/driver-sdk/retry.js +50 -0
  157. package/dist/driver-sdk/retry.js.map +1 -0
  158. package/dist/plugin/.claude-plugin/plugin.json +4 -1
  159. package/dist/plugin/agents/apt-planner.md +39 -7
  160. package/dist/plugin/skills/apt/SKILL.md +75 -6
  161. package/dist/plugin/skills/apt-caveman/SKILL.md +64 -0
  162. package/dist/plugin/skills/apt-debug/SKILL.md +36 -4
  163. package/dist/plugin/skills/apt-debug/appendices/diagnose-discipline.md +2 -11
  164. package/dist/plugin/skills/apt-discuss/SKILL.md +4 -4
  165. package/dist/plugin/skills/apt-discuss/appendices/grill-discipline.md +2 -11
  166. package/dist/plugin/skills/apt-discuss/appendices/zoom-out-helper.md +0 -8
  167. package/dist/plugin/skills/apt-execute/SKILL.md +27 -4
  168. package/dist/plugin/skills/apt-execute/appendices/tdd-mode.md +2 -10
  169. package/dist/plugin/skills/apt-fan-out/SKILL.md +36 -1
  170. package/dist/plugin/skills/apt-handoff/SKILL.md +228 -0
  171. package/dist/plugin/skills/apt-improve/DEEPENING.md +0 -8
  172. package/dist/plugin/skills/apt-improve/INTERFACE-DESIGN.md +0 -8
  173. package/dist/plugin/skills/apt-improve/LANGUAGE.md +0 -8
  174. package/dist/plugin/skills/apt-improve/SKILL.md +4 -4
  175. package/dist/plugin/skills/apt-plan/SKILL.md +73 -5
  176. package/dist/plugin/skills/apt-plan/adapters/conductor.md +1 -1
  177. package/dist/plugin/skills/apt-pr-review/SKILL.md +1 -1
  178. package/dist/plugin/skills/apt-prototype/LOGIC.md +0 -8
  179. package/dist/plugin/skills/apt-prototype/SKILL.md +4 -4
  180. package/dist/plugin/skills/apt-prototype/UI.md +0 -8
  181. package/dist/plugin/skills/apt-quick/SKILL.md +31 -1
  182. package/dist/plugin/skills/apt-research/SKILL.md +526 -0
  183. package/dist/plugin/skills/apt-research/appendices/budget-loop.md +397 -0
  184. package/dist/plugin/skills/apt-research/appendices/claim-graph-schema.md +206 -0
  185. package/dist/plugin/skills/apt-research/appendices/domain-tools.md +236 -0
  186. package/dist/plugin/skills/apt-resume/SKILL.md +54 -31
  187. package/dist/plugin/skills/apt-run/SKILL.md +114 -16
  188. package/dist/plugin/skills/apt-setup/SKILL.md +97 -8
  189. package/dist/plugin/skills/apt-ship/SKILL.md +165 -9
  190. package/dist/plugin/skills/apt-spar/SKILL.md +84 -23
  191. package/dist/plugin/skills/apt-triage/AGENT-BRIEF.md +0 -8
  192. package/dist/plugin/skills/apt-triage/OUT-OF-SCOPE.md +0 -7
  193. package/dist/plugin/skills/apt-triage/SKILL.md +2 -2
  194. package/dist/plugin/skills/apt-update/SKILL.md +74 -17
  195. package/dist/plugin/skills/apt-verify-proof/SKILL.md +87 -1
  196. package/dist/plugin/skills/apt-zoom-out/SKILL.md +3 -3
  197. package/dist/schemas/quick-task.d.ts +17 -17
  198. package/dist/schemas/quick-task.d.ts.map +1 -1
  199. package/dist/schemas/quick-task.js +2 -2
  200. package/dist/schemas/quick-task.js.map +1 -1
  201. package/dist/types/config.d.ts +33 -0
  202. package/dist/types/config.d.ts.map +1 -1
  203. package/dist/types/state.d.ts +1 -1
  204. package/dist/types/state.d.ts.map +1 -1
  205. package/dist/types/task-record.d.ts +1 -1
  206. package/dist/types/task-record.d.ts.map +1 -1
  207. package/drivers/.gitkeep +0 -0
  208. package/drivers/api/README.md +40 -0
  209. package/drivers/api/driver.mjs +59 -0
  210. package/drivers/api/manifest.json +26 -0
  211. package/drivers/browser/README.md +105 -0
  212. package/drivers/browser/driver.mjs +134 -0
  213. package/drivers/browser/manifest.json +35 -0
  214. package/drivers/cli/README.md +44 -0
  215. package/drivers/cli/driver.mjs +62 -0
  216. package/drivers/cli/manifest.json +28 -0
  217. package/drivers/electron/README.md +64 -0
  218. package/drivers/electron/driver.mjs +87 -0
  219. package/drivers/electron/manifest.json +37 -0
  220. package/package.json +138 -133
  221. package/prompts/planner.md +1 -1
  222. package/prompts/spec_writer.md +1 -1
  223. package/skills/apt/SKILL.md +75 -6
  224. package/skills/apt-caveman/SKILL.md +64 -0
  225. package/skills/apt-debug/SKILL.md +36 -4
  226. package/skills/apt-debug/appendices/diagnose-discipline.md +2 -11
  227. package/skills/apt-discuss/SKILL.md +4 -4
  228. package/skills/apt-discuss/appendices/grill-discipline.md +2 -11
  229. package/skills/apt-discuss/appendices/zoom-out-helper.md +0 -8
  230. package/skills/apt-execute/SKILL.md +27 -4
  231. package/skills/apt-execute/appendices/tdd-mode.md +2 -10
  232. package/skills/apt-fan-out/SKILL.md +36 -1
  233. package/skills/apt-handoff/SKILL.md +228 -0
  234. package/skills/apt-improve/DEEPENING.md +0 -8
  235. package/skills/apt-improve/INTERFACE-DESIGN.md +0 -8
  236. package/skills/apt-improve/LANGUAGE.md +0 -8
  237. package/skills/apt-improve/SKILL.md +4 -4
  238. package/skills/apt-plan/SKILL.md +73 -5
  239. package/skills/apt-plan/adapters/conductor.md +1 -1
  240. package/skills/apt-planner.md +17 -1
  241. package/skills/apt-pr-review/SKILL.md +1 -1
  242. package/skills/apt-prototype/LOGIC.md +0 -8
  243. package/skills/apt-prototype/SKILL.md +4 -4
  244. package/skills/apt-prototype/UI.md +0 -8
  245. package/skills/apt-quick/SKILL.md +31 -1
  246. package/skills/apt-research/SKILL.md +526 -0
  247. package/skills/apt-research/appendices/budget-loop.md +397 -0
  248. package/skills/apt-research/appendices/claim-graph-schema.md +206 -0
  249. package/skills/apt-research/appendices/domain-tools.md +236 -0
  250. package/skills/apt-resume/SKILL.md +54 -31
  251. package/skills/apt-run/SKILL.md +114 -16
  252. package/skills/apt-setup/SKILL.md +97 -8
  253. package/skills/apt-ship/SKILL.md +165 -9
  254. package/skills/apt-spar/SKILL.md +84 -23
  255. package/skills/apt-triage/AGENT-BRIEF.md +0 -8
  256. package/skills/apt-triage/OUT-OF-SCOPE.md +0 -7
  257. package/skills/apt-triage/SKILL.md +2 -2
  258. package/skills/apt-update/SKILL.md +74 -17
  259. package/skills/apt-verify-proof/SKILL.md +87 -1
  260. package/skills/apt-zoom-out/SKILL.md +3 -3
  261. package/src/cli/commands/adr.mjs +1 -1
  262. package/src/cli/commands/audit-branch-current.mjs +250 -0
  263. package/src/cli/commands/audit.mjs +13 -2
  264. package/src/cli/commands/check-version.mjs +20 -1
  265. package/src/cli/commands/context.mjs +3 -3
  266. package/src/cli/commands/coverage-check.mjs +126 -44
  267. package/src/cli/commands/detect-runtime.mjs +143 -0
  268. package/src/cli/commands/driver-doctor.mjs +294 -0
  269. package/src/cli/commands/gate.mjs +15 -0
  270. package/src/cli/commands/init.mjs +9 -6
  271. package/src/cli/commands/install-from-source.mjs +1 -0
  272. package/src/cli/commands/modes.mjs +1 -1
  273. package/src/cli/commands/route.mjs +42 -1
  274. package/src/cli/commands/triage.mjs +1 -1
  275. package/src/cli/config/load.mjs +9 -3
  276. package/src/cli/coordination/auto-emit-artifact-ready.mjs +1 -1
  277. package/src/cli/coordination/event-schema.mjs +19 -0
  278. package/src/cli/coverage-check/user-outcomes.mjs +273 -0
  279. package/src/cli/dispatch.mjs +17 -1
  280. package/src/cli/gate/context.mjs +20 -1
  281. package/src/cli/gate/gates/consistency-check.mjs +1 -0
  282. package/src/cli/gate/gates/review-clean.mjs +1 -0
  283. package/src/cli/gate/gates/verify-approved.mjs +1 -0
  284. package/src/cli/gate/registry.mjs +11 -1
  285. package/src/cli/host/detect.mjs +1 -1
  286. package/src/cli/install/find-workspace-framework-version.mjs +88 -0
  287. package/src/cli/install/install-from-source.mjs +276 -0
  288. package/src/cli/install/install-kernel.mjs +24 -0
  289. package/src/cli/install/legacy-paths.mjs +164 -39
  290. package/src/cli/install/runtime-detect.mjs +1 -1
  291. package/src/cli/install/runtime-migrate.mjs +63 -30
  292. package/src/cli/install/transforms/codex.mjs +26 -17
  293. package/src/cli/skill-author/contract.mjs +2 -2
  294. package/src/cli/verify-proof/.gitkeep +0 -0
  295. package/src/cli/verify-proof/audit.mjs +55 -0
  296. package/src/cli/verify-proof/exec.mjs +74 -0
  297. package/src/cli/verify-proof/idl/index.mjs +23 -0
  298. package/src/cli/verify-proof/idl/types.ts +42 -0
  299. package/src/cli/verify-proof/manifest-schema.json +212 -0
  300. package/src/cli/verify-proof/manifest-validator.mjs +184 -0
  301. package/src/cli/verify-proof/resolver.mjs +330 -0
  302. package/src/cli/verify-proof/runtime-detect.mjs +211 -0
  303. package/src/cli/verify-proof/trust.mjs +187 -0
  304. package/templates/adr-format.md +0 -8
  305. package/templates/aperant-claude-md-appendix.md +1 -1
  306. package/templates/context-format.md +1 -9
  307. package/templates/proof-verification.md +51 -8
  308. package/workflows/verify-proof.md +130 -19
@@ -1,8 +1,8 @@
1
1
  /**
2
- * commands/context.mjs — `apt-tools context write` (Pocock adoption ST-5, AC4).
2
+ * commands/context.mjs — `apt-tools context write`.
3
3
  *
4
4
  * Upserts a term-block into the project's CONTEXT.md (the persistent
5
- * project glossary). Format ported from Pocock's CONTEXT-FORMAT.md
5
+ * project glossary). Format ported from the Aperant CONTEXT-FORMAT.md
6
6
  * (see packages/framework/templates/context-format.md for the MIT
7
7
  * attribution + field semantics).
8
8
  *
@@ -192,7 +192,7 @@ export function cmdContext(subcommand, projectDir, extraArgs) {
192
192
  } else {
193
193
  source = `${HEADER}\n\n${HEADER_NOTE}\n\n`
194
194
  }
195
- // Ensure the file starts with the header even if it existed pre-Pocock.
195
+ // Ensure the file starts with the header even if it existed pre-vertical-slice.
196
196
  if (!source.startsWith(HEADER)) {
197
197
  source = `${HEADER}\n\n${HEADER_NOTE}\n\n${source}`
198
198
  }
@@ -1,10 +1,20 @@
1
1
  /**
2
2
  * commands/coverage-check.mjs — plan-to-spec verification coverage validator (C29).
3
+ *
4
+ * v0.8.6 adds a second gate: `## User Outcomes` parsing + gate (see
5
+ * coverage-check/user-outcomes.mjs). The new gate is ADDITIVE — it does
6
+ * NOT change the existing AC parsing branch and does NOT block when
7
+ * only Acceptance Criteria are present (outcomes and ACs are
8
+ * independent concerns per spec §ID-01). QUICK-track plans
9
+ * (`complexity: "simple"` OR no `vertical_slice_schema_version`) skip
10
+ * the User Outcomes branch entirely per Fast Path Guarantee (ID-08).
3
11
  */
4
12
 
5
13
  import { existsSync, readFileSync } from 'node:fs'
6
- import { join, resolve } from 'node:path'
14
+ import { dirname, join, resolve } from 'node:path'
15
+ import { fileURLToPath } from 'node:url'
7
16
  import { parseAcceptanceCriteria, resolveSpecPath } from '../config/coverage-ac.mjs'
17
+ import { parseUserOutcomes, userOutcomesGateResult } from '../coverage-check/user-outcomes.mjs'
8
18
  import { err, ok } from '../util/result.mjs'
9
19
 
10
20
  export function cmdCoverageCheck(subcommand, projectDir, extraArgs) {
@@ -125,31 +135,64 @@ export function cmdCoverageCheck(subcommand, projectDir, extraArgs) {
125
135
  }
126
136
  }
127
137
 
138
+ // Build the User Outcomes payload — additive, ID-08 QUICK-exempt.
139
+ // Plans with `complexity: "simple"` OR no
140
+ // `vertical_slice_schema_version` are QUICK-track per the Fast
141
+ // Path Guarantee — skip the gate entirely (the payload's
142
+ // `user_outcomes` key is OMITTED).
143
+ const isQuickTrack = plan.complexity === 'simple' || !plan.vertical_slice_schema_version
144
+ let userOutcomesPayload = null
145
+ if (!isQuickTrack && resolvedSpec.path) {
146
+ const specContentForOutcomes = readFileSync(resolvedSpec.path, 'utf-8')
147
+ const parsedOutcomes = parseUserOutcomes(specContentForOutcomes)
148
+ const frameworkVersion = readFrameworkVersion()
149
+ const gateResult = userOutcomesGateResult(parsedOutcomes, frameworkVersion)
150
+ userOutcomesPayload = {
151
+ header_present: parsedOutcomes.header_present,
152
+ body_kind: parsedOutcomes.body_kind,
153
+ outcomes_count: parsedOutcomes.outcomes.length,
154
+ gate_result: gateResult,
155
+ }
156
+ if (parsedOutcomes.epic) userOutcomesPayload.epic = parsedOutcomes.epic
157
+ if (parsedOutcomes.validation_warnings && parsedOutcomes.validation_warnings.length > 0) {
158
+ userOutcomesPayload.validation_warnings = parsedOutcomes.validation_warnings
159
+ }
160
+ if (gateResult === 'warn') {
161
+ userOutcomesPayload.migration_deadline =
162
+ '## User Outcomes section becomes a hard block in v0.9.0 — add it now, or use the empty-with-note escape (`_No user-observable changes — pure refactor._`) for refactor-only specs.'
163
+ }
164
+ }
165
+
128
166
  // Zero-criteria guard — do NOT silently report ok when there is
129
167
  // nothing to verify. This is the motivating bug: zero criteria
130
168
  // trivially pass unmapped.length === 0, which produced a false
131
169
  // green signal. Treat it as incomplete with an explicit reason.
132
170
  if (criteria.length === 0) {
133
- return ok({
134
- status: 'incomplete',
135
- command: 'coverage-validate',
136
- criteria_count: 0,
137
- mapped_count: 0,
138
- unmapped_criteria: [],
139
- coverage: [],
140
- subtask_verifications: plan.subtasks.map((st) => ({
141
- id: st.id,
142
- title: st.title || st.id,
143
- verification: st.verification || '',
144
- acceptance_criteria: st.acceptance_criteria || [],
145
- })),
146
- reason: 'no-criteria-found',
147
- suggestion:
148
- 'Add acceptance criteria to spec.md under ## Acceptance Criteria, or add plan.acceptance_criteria to implementation_plan.json',
149
- sources_checked: sourcesChecked,
150
- spec_path: resolvedSpec.path,
151
- autonomy_action: autonomyLevelEarly <= 1 ? 'stop-and-ask' : 'generate-stubs',
152
- })
171
+ return ok(
172
+ withUserOutcomes(
173
+ {
174
+ status: 'incomplete',
175
+ command: 'coverage-validate',
176
+ criteria_count: 0,
177
+ mapped_count: 0,
178
+ unmapped_criteria: [],
179
+ coverage: [],
180
+ subtask_verifications: plan.subtasks.map((st) => ({
181
+ id: st.id,
182
+ title: st.title || st.id,
183
+ verification: st.verification || '',
184
+ acceptance_criteria: st.acceptance_criteria || [],
185
+ })),
186
+ reason: 'no-criteria-found',
187
+ suggestion:
188
+ 'Add acceptance criteria to spec.md under ## Acceptance Criteria, or add plan.acceptance_criteria to implementation_plan.json',
189
+ sources_checked: sourcesChecked,
190
+ spec_path: resolvedSpec.path,
191
+ autonomy_action: autonomyLevelEarly <= 1 ? 'stop-and-ask' : 'generate-stubs',
192
+ },
193
+ userOutcomesPayload,
194
+ ),
195
+ )
153
196
  }
154
197
 
155
198
  // Classify verification methods for each subtask (D-19)
@@ -221,32 +264,71 @@ export function cmdCoverageCheck(subcommand, projectDir, extraArgs) {
221
264
  }
222
265
 
223
266
  if (unmapped.length > 0) {
224
- return ok({
225
- status: 'incomplete',
226
- command: 'coverage-validate',
227
- criteria_count: criteria.length,
228
- mapped_count: mappedCount,
229
- unmapped_criteria: unmapped.map((u) => u.criterion),
230
- coverage,
231
- subtask_verifications: subtaskVerifications,
232
- suggestion: 'generate test stubs',
233
- autonomy_action: autonomyLevel <= 1 ? 'stop-and-ask' : 'generate-stubs',
234
- spec_path: resolvedSpec.path,
235
- sources_checked: sourcesChecked,
236
- })
267
+ return ok(
268
+ withUserOutcomes(
269
+ {
270
+ status: 'incomplete',
271
+ command: 'coverage-validate',
272
+ criteria_count: criteria.length,
273
+ mapped_count: mappedCount,
274
+ unmapped_criteria: unmapped.map((u) => u.criterion),
275
+ coverage,
276
+ subtask_verifications: subtaskVerifications,
277
+ suggestion: 'generate test stubs',
278
+ autonomy_action: autonomyLevel <= 1 ? 'stop-and-ask' : 'generate-stubs',
279
+ spec_path: resolvedSpec.path,
280
+ sources_checked: sourcesChecked,
281
+ },
282
+ userOutcomesPayload,
283
+ ),
284
+ )
237
285
  }
238
- return ok({
239
- status: 'ok',
240
- command: 'coverage-validate',
241
- criteria_count: criteria.length,
242
- mapped_count: mappedCount,
243
- coverage,
244
- subtask_verifications: subtaskVerifications,
245
- spec_path: resolvedSpec.path,
246
- sources_checked: sourcesChecked,
247
- })
286
+ return ok(
287
+ withUserOutcomes(
288
+ {
289
+ status: 'ok',
290
+ command: 'coverage-validate',
291
+ criteria_count: criteria.length,
292
+ mapped_count: mappedCount,
293
+ coverage,
294
+ subtask_verifications: subtaskVerifications,
295
+ spec_path: resolvedSpec.path,
296
+ sources_checked: sourcesChecked,
297
+ },
298
+ userOutcomesPayload,
299
+ ),
300
+ )
248
301
  }
249
302
  default:
250
303
  return err(`Unknown coverage-check subcommand: ${subcommand}. Available: validate`)
251
304
  }
252
305
  }
306
+
307
+ /**
308
+ * Attach the `user_outcomes` payload to a coverage-check envelope when
309
+ * present (i.e. STANDARD/DEEP plan with a spec on disk). QUICK plans
310
+ * pass `null` and the key is OMITTED from the envelope entirely — Fast
311
+ * Path Guarantee (ID-08).
312
+ */
313
+ function withUserOutcomes(envelope, userOutcomesPayload) {
314
+ if (userOutcomesPayload === null) return envelope
315
+ return { ...envelope, user_outcomes: userOutcomesPayload }
316
+ }
317
+
318
+ /**
319
+ * Read the running kernel's framework version from its own package.json.
320
+ * Used to gate the `## User Outcomes` missing-header behavior — warn in
321
+ * v0.8.x, hard-block from v0.9.0 forward. Returns `'0.0.0'` on failure
322
+ * so the gate fails-soft to warn (callers treat unknown version as
323
+ * pre-0.9.0).
324
+ */
325
+ function readFrameworkVersion() {
326
+ try {
327
+ const here = dirname(fileURLToPath(import.meta.url))
328
+ const pkgPath = resolve(here, '..', '..', '..', 'package.json')
329
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
330
+ return typeof pkg.version === 'string' ? pkg.version : '0.0.0'
331
+ } catch {
332
+ return '0.0.0'
333
+ }
334
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * commands/detect-runtime.mjs — `apt-tools detect-runtime <projectDir>`.
3
+ *
4
+ * Exposes runtime-detect.mjs at the CLI surface so adopters can preview
5
+ * which driver Aperant will pick for /apt:verify-proof.
6
+ *
7
+ * Returns:
8
+ * { status: 'ok', command: 'detect-runtime', projectDir,
9
+ * runtime: { is_electron, is_tauri, is_nextjs, is_web_frontend, ... },
10
+ * selected_driver_id: string | null,
11
+ * fallback_attempts: [{ driver_id, reason }] }
12
+ */
13
+
14
+ import { existsSync, readdirSync, readFileSync } from 'node:fs'
15
+ import { join, resolve } from 'node:path'
16
+ import { fileURLToPath } from 'node:url'
17
+ import { err, ok } from '../util/result.mjs'
18
+ import { resolveDriver } from '../verify-proof/resolver.mjs'
19
+ import { loadRuntimeCapabilities } from '../verify-proof/runtime-detect.mjs'
20
+
21
+ /** Default required capabilities — the smallest set that proves a driver is usable. */
22
+ const DEFAULT_REQUIRED_CAPABILITIES = ['screenshot']
23
+
24
+ /**
25
+ * Discover every driver manifest bundled with @aperant/framework.
26
+ * @param {string} frameworkRoot — absolute path to packages/framework or .aperant/deps/.../@aperant/framework
27
+ * @returns {Array<object>}
28
+ */
29
+ function discoverBundledDrivers(frameworkRoot) {
30
+ const dir = join(frameworkRoot, 'drivers')
31
+ if (!existsSync(dir)) return []
32
+ const manifests = []
33
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
34
+ if (!entry.isDirectory()) continue
35
+ const manifestPath = join(dir, entry.name, 'manifest.json')
36
+ if (!existsSync(manifestPath)) continue
37
+ try {
38
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'))
39
+ manifests.push({ ...parsed, locality: 'bundled' })
40
+ } catch {
41
+ /* skip malformed manifests */
42
+ }
43
+ }
44
+ return manifests
45
+ }
46
+
47
+ /**
48
+ * Discover every user-installed driver manifest at .aperant/drivers/.
49
+ * @param {string} projectDir
50
+ * @returns {Array<object>}
51
+ */
52
+ function discoverUserDrivers(projectDir) {
53
+ const dir = join(projectDir, '.aperant/drivers')
54
+ if (!existsSync(dir)) return []
55
+ const manifests = []
56
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
57
+ if (!entry.isDirectory()) continue
58
+ const manifestPath = join(dir, entry.name, 'manifest.json')
59
+ if (!existsSync(manifestPath)) continue
60
+ try {
61
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'))
62
+ manifests.push({ ...parsed, locality: 'user' })
63
+ } catch {
64
+ /* skip malformed manifests */
65
+ }
66
+ }
67
+ return manifests
68
+ }
69
+
70
+ /**
71
+ * @param {string} projectDir
72
+ * @param {string[]} [_extra]
73
+ */
74
+ export function cmdDetectRuntime(projectDir, _extra = []) {
75
+ if (!projectDir) return err('Usage: apt-tools detect-runtime <project-dir>')
76
+ const target = resolve(projectDir)
77
+
78
+ // Read user config (used both for the monorepo walk + override).
79
+ let monorepoGlobs
80
+ let override = []
81
+ const configPath = join(target, '.aperant/config.json')
82
+ if (existsSync(configPath)) {
83
+ try {
84
+ const cfg = JSON.parse(readFileSync(configPath, 'utf-8'))
85
+ if (Array.isArray(cfg?.verification?.monorepo_globs)) {
86
+ monorepoGlobs = cfg.verification.monorepo_globs.filter((x) => typeof x === 'string')
87
+ }
88
+ if (Array.isArray(cfg?.verification?.runtimes)) {
89
+ override = cfg.verification.runtimes.filter((x) => typeof x === 'string')
90
+ }
91
+ } catch {
92
+ /* malformed config — ignore */
93
+ }
94
+ }
95
+
96
+ // Detect runtime capabilities from project's package.json + (optionally)
97
+ // monorepo `apps/*` walk. Default behaviour matches the canonical
98
+ // Aperant layout — adopters opt out via verification.monorepo_globs: [].
99
+ const runtime = loadRuntimeCapabilities(
100
+ target,
101
+ monorepoGlobs !== undefined ? { monorepo_globs: monorepoGlobs } : undefined,
102
+ )
103
+
104
+ // Discover bundled + user drivers.
105
+ // __dirname-equivalent under ESM: walk up from this file to the framework
106
+ // package root. fileURLToPath is portable across POSIX + Windows;
107
+ // new URL(...).pathname returns '/C:/...' on Windows which resolve()
108
+ // does not handle correctly.
109
+ const frameworkRoot = fileURLToPath(new URL('../../..', import.meta.url))
110
+ const bundled = discoverBundledDrivers(frameworkRoot)
111
+ const user = discoverUserDrivers(target)
112
+ const drivers = [...bundled, ...user]
113
+
114
+ // Try resolving with the default minimum required capability.
115
+ let selected = null
116
+ let fallback_attempts = []
117
+ try {
118
+ const result = resolveDriver({
119
+ drivers,
120
+ runtime,
121
+ required_capabilities: DEFAULT_REQUIRED_CAPABILITIES,
122
+ override,
123
+ })
124
+ selected = result.selected.driverId
125
+ fallback_attempts = result.fallback_attempts
126
+ } catch (resolveErr) {
127
+ fallback_attempts = resolveErr && Array.isArray(resolveErr.attempts) ? resolveErr.attempts : []
128
+ }
129
+
130
+ return ok({
131
+ status: 'ok',
132
+ command: 'detect-runtime',
133
+ projectDir: target,
134
+ runtime,
135
+ discovered_drivers: drivers.map((d) => ({
136
+ driverId: d.driverId,
137
+ version: d.version,
138
+ locality: d.locality,
139
+ })),
140
+ selected_driver_id: selected,
141
+ fallback_attempts,
142
+ })
143
+ }
@@ -0,0 +1,294 @@
1
+ /**
2
+ * commands/driver-doctor.mjs — `apt-tools driver doctor <projectDir>`.
3
+ *
4
+ * Validates every discovered driver against:
5
+ * 1. The manifest JSON-Schema (manifest-validator.mjs).
6
+ * 2. IDL conformance — every declared capability has a function export.
7
+ * NOTE: this step necessarily imports driver.mjs, which RUNS the
8
+ * driver's top-level module code. For that reason we trust-gate the
9
+ * import: bundled drivers (first-party signed, sitting under
10
+ * packages/framework/drivers/) are always imported; user drivers
11
+ * under .aperant/drivers/ are imported only when policy + TOFU say
12
+ * it is safe. A user driver that fails the gate is reported with
13
+ * `idl_conformant: false` and `idl_skipped_reason: <decision>`
14
+ * rather than executed.
15
+ * 3. Preflight commands (declared as structured local_command per
16
+ * ID-03). For each preflight whose declared transport is the
17
+ * structured `{ binary, args[] }` shape, we run it via execFile
18
+ * (exec.mjs — no shell) and record pass/fail + missing_deps.
19
+ * Preflights without a structured `command` (declaration-only) are
20
+ * marked `pending` — there is nothing to execute.
21
+ *
22
+ * Emits a JSON envelope listing pass/fail per driver. Third-party driver
23
+ * authors invoke this before publishing.
24
+ */
25
+
26
+ import { existsSync, readdirSync, readFileSync } from 'node:fs'
27
+ import { join, resolve } from 'node:path'
28
+ import { fileURLToPath } from 'node:url'
29
+ import { err, ok } from '../util/result.mjs'
30
+ import { execLocalCommand } from '../verify-proof/exec.mjs'
31
+ import { IDL_VERB_NAMES } from '../verify-proof/idl/index.mjs'
32
+ import { validateManifest } from '../verify-proof/manifest-validator.mjs'
33
+ import { decide, readPolicy, readTofuLock } from '../verify-proof/trust.mjs'
34
+
35
+ /**
36
+ * @typedef {Object} DriverDoctorReport
37
+ * @property {string} driverId
38
+ * @property {string} version
39
+ * @property {string} locality
40
+ * @property {boolean} manifest_valid
41
+ * @property {Array<{path: string, message: string}>} manifest_errors
42
+ * @property {boolean} idl_conformant
43
+ * @property {string[]} missing_exports
44
+ * @property {string[]} unknown_capabilities
45
+ * @property {string} [idl_skipped_reason] — present when conformance was skipped because the trust gate blocked driver.mjs import
46
+ * @property {'pending' | 'pass' | 'fail' | 'skipped'} preflight_status
47
+ * @property {string[]} missing_deps
48
+ * @property {string} [publisher_warning] — surfaced when a non-first-party manifest claims first_party_signed (v1 has no signature verification)
49
+ */
50
+
51
+ /**
52
+ * Scan a drivers directory for manifest.json files. Returns
53
+ * (manifest, dir, locality) tuples.
54
+ * @param {string} rootDir
55
+ * @param {'bundled' | 'user'} locality
56
+ */
57
+ function scanDriversDir(rootDir, locality) {
58
+ const out = []
59
+ if (!existsSync(rootDir)) return out
60
+ for (const entry of readdirSync(rootDir, { withFileTypes: true })) {
61
+ if (!entry.isDirectory()) continue
62
+ const driverDir = join(rootDir, entry.name)
63
+ const manifestPath = join(driverDir, 'manifest.json')
64
+ if (!existsSync(manifestPath)) continue
65
+ let manifest
66
+ try {
67
+ manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'))
68
+ } catch (e) {
69
+ out.push({ malformed: true, dir: driverDir, locality, parse_error: String(e) })
70
+ continue
71
+ }
72
+ out.push({ manifest, dir: driverDir, locality })
73
+ }
74
+ return out
75
+ }
76
+
77
+ /**
78
+ * @param {object} manifest
79
+ * @param {string} driverDir
80
+ * @returns {Promise<{idl_conformant: boolean, missing_exports: string[], unknown_capabilities: string[]}>}
81
+ */
82
+ async function checkIdlConformance(manifest, driverDir) {
83
+ const driverPath = join(driverDir, 'driver.mjs')
84
+ if (!existsSync(driverPath)) {
85
+ return {
86
+ idl_conformant: false,
87
+ missing_exports: ['driver.mjs not found'],
88
+ unknown_capabilities: [],
89
+ }
90
+ }
91
+ const missing_exports = []
92
+ const unknown_capabilities = []
93
+ const idlSet = new Set(IDL_VERB_NAMES)
94
+ try {
95
+ const mod = await import(driverPath)
96
+ for (const cap of manifest.capabilities ?? []) {
97
+ if (!idlSet.has(cap)) {
98
+ unknown_capabilities.push(cap)
99
+ continue
100
+ }
101
+ if (typeof mod[cap] !== 'function') {
102
+ missing_exports.push(cap)
103
+ }
104
+ }
105
+ } catch (e) {
106
+ return {
107
+ idl_conformant: false,
108
+ missing_exports: [`driver.mjs import failed: ${String(e)}`],
109
+ unknown_capabilities: [],
110
+ }
111
+ }
112
+ return {
113
+ idl_conformant: missing_exports.length === 0 && unknown_capabilities.length === 0,
114
+ missing_exports,
115
+ unknown_capabilities,
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Run a driver's preflight checks. Each declared preflight with a
121
+ * structured `command: { binary, args[] }` is executed via execFile
122
+ * (no shell). Preflights without a structured command are reported as
123
+ * `pending` — there is nothing to execute. Empty preflight list is
124
+ * `skipped`.
125
+ *
126
+ * @param {object} manifest
127
+ * @returns {Promise<{preflight_status: 'pending'|'pass'|'fail'|'skipped', missing_deps: string[]}>}
128
+ */
129
+ async function runPreflight(manifest) {
130
+ const preflight = Array.isArray(manifest.preflight) ? manifest.preflight : []
131
+ if (preflight.length === 0) return { preflight_status: 'skipped', missing_deps: [] }
132
+
133
+ const missing_deps = []
134
+ let executed = 0
135
+ let failed = false
136
+
137
+ for (const check of preflight) {
138
+ // Only execute when the preflight declares a structured local-command
139
+ // shape. Anything else (no command, or non-structured) is left as
140
+ // declaration-only — caller can drive it explicitly.
141
+ const cmd = check?.command
142
+ if (
143
+ cmd === undefined ||
144
+ cmd === null ||
145
+ typeof cmd !== 'object' ||
146
+ typeof cmd.binary !== 'string' ||
147
+ !Array.isArray(cmd.args) ||
148
+ cmd.args.some((a) => typeof a !== 'string')
149
+ ) {
150
+ continue
151
+ }
152
+ executed++
153
+ try {
154
+ await execLocalCommand({ transport: { local_command: cmd } }, [], { timeout: 5000 })
155
+ } catch {
156
+ failed = true
157
+ if (check.required !== false) {
158
+ missing_deps.push(check.name ?? cmd.binary)
159
+ }
160
+ }
161
+ }
162
+
163
+ if (executed === 0) return { preflight_status: 'pending', missing_deps: [] }
164
+ return { preflight_status: failed ? 'fail' : 'pass', missing_deps }
165
+ }
166
+
167
+ /**
168
+ * Trust-gate the driver.mjs import. Bundled drivers (locality='bundled')
169
+ * always pass — they live in packages/framework/drivers/ and are first-
170
+ * party. User drivers go through the policy + TOFU decision; only
171
+ * `allow` permits the dynamic import.
172
+ *
173
+ * @param {object} manifest
174
+ * @param {'bundled'|'user'} locality
175
+ * @param {string} projectDir
176
+ * @returns {{ok: true} | {ok: false, reason: string}}
177
+ */
178
+ function trustGate(manifest, locality, projectDir) {
179
+ if (locality === 'bundled') return { ok: true }
180
+ const policy = readPolicy(projectDir)
181
+ const tofu = readTofuLock(projectDir)
182
+ const decision = decide(manifest, tofu, policy)
183
+ if (decision.decision === 'allow') return { ok: true }
184
+ return { ok: false, reason: decision.reason }
185
+ }
186
+
187
+ /**
188
+ * v1 has no signature verification (spec §Out of Scope). A non-Aperant
189
+ * publisher claiming `first_party_signed` is suspicious — surface a
190
+ * warning so the operator notices.
191
+ *
192
+ * @param {object} manifest
193
+ * @returns {string | undefined}
194
+ */
195
+ function publisherWarning(manifest) {
196
+ const tier = manifest?.trust?.tier
197
+ if (tier !== 'first_party_signed') return undefined
198
+ const npmScope = manifest?.trust?.publisher?.npm_scope
199
+ const githubOrg = manifest?.trust?.publisher?.github_org
200
+ const looksLikeAperant = npmScope === '@aperant' || githubOrg === 'Mikalsen-AI'
201
+ if (looksLikeAperant) return undefined
202
+ return `trust.tier=first_party_signed but publisher is not @aperant — v1 does not verify signatures; treat tier as advisory`
203
+ }
204
+
205
+ /**
206
+ * @param {string} projectDir
207
+ * @param {string[]} [_extra]
208
+ */
209
+ export async function cmdDriverDoctor(projectDir, _extra = []) {
210
+ if (!projectDir) return err('Usage: apt-tools driver doctor <project-dir>')
211
+ const target = resolve(projectDir)
212
+
213
+ // fileURLToPath is portable across POSIX + Windows; new URL(...).pathname
214
+ // returns '/C:/...' on Windows which resolve() does not handle correctly.
215
+ const frameworkRoot = fileURLToPath(new URL('../../..', import.meta.url))
216
+ const bundled = scanDriversDir(join(frameworkRoot, 'drivers'), 'bundled')
217
+ const user = scanDriversDir(join(target, '.aperant/drivers'), 'user')
218
+ const discovered = [...bundled, ...user]
219
+
220
+ const drivers = []
221
+ for (const d of discovered) {
222
+ if (d.malformed) {
223
+ drivers.push({
224
+ driverId: '(malformed)',
225
+ version: '?',
226
+ locality: d.locality,
227
+ manifest_valid: false,
228
+ manifest_errors: [{ path: '/', message: d.parse_error }],
229
+ idl_conformant: false,
230
+ missing_exports: [],
231
+ unknown_capabilities: [],
232
+ preflight_status: 'skipped',
233
+ missing_deps: [],
234
+ })
235
+ continue
236
+ }
237
+
238
+ const manifest = d.manifest
239
+ const schemaResult = validateManifest(manifest)
240
+ const manifest_valid = schemaResult.valid === true
241
+ const manifest_errors = schemaResult.valid === false ? schemaResult.errors : []
242
+
243
+ let idlResult = { idl_conformant: false, missing_exports: [], unknown_capabilities: [] }
244
+ let idl_skipped_reason
245
+ if (manifest_valid) {
246
+ const gate = trustGate(manifest, d.locality, target)
247
+ if (gate.ok) {
248
+ idlResult = await checkIdlConformance(manifest, d.dir)
249
+ } else {
250
+ // Trust gate denied. Do NOT dynamic-import the driver — that
251
+ // would execute arbitrary third-party code under the audit
252
+ // command. Report idl_conformant=false with the gate reason
253
+ // so the operator sees exactly why conformance was not run.
254
+ idl_skipped_reason = `trust gate denied: ${gate.reason}`
255
+ }
256
+ }
257
+
258
+ let preflightResult = { preflight_status: 'skipped', missing_deps: [] }
259
+ if (manifest_valid) {
260
+ preflightResult = await runPreflight(manifest)
261
+ }
262
+
263
+ const entry = {
264
+ driverId: manifest.driverId ?? '(unknown)',
265
+ version: manifest.version ?? '?',
266
+ locality: d.locality,
267
+ manifest_valid,
268
+ manifest_errors,
269
+ idl_conformant: idlResult.idl_conformant,
270
+ missing_exports: idlResult.missing_exports,
271
+ unknown_capabilities: idlResult.unknown_capabilities,
272
+ preflight_status: preflightResult.preflight_status,
273
+ missing_deps: preflightResult.missing_deps,
274
+ }
275
+ if (idl_skipped_reason !== undefined) entry.idl_skipped_reason = idl_skipped_reason
276
+ const warning = publisherWarning(manifest)
277
+ if (warning !== undefined) entry.publisher_warning = warning
278
+ drivers.push(entry)
279
+ }
280
+
281
+ const all_pass = drivers.every((d) => d.manifest_valid && d.idl_conformant)
282
+ return ok({
283
+ status: 'ok',
284
+ command: 'driver-doctor',
285
+ projectDir: target,
286
+ drivers,
287
+ summary: {
288
+ total: drivers.length,
289
+ pass: drivers.filter((d) => d.manifest_valid && d.idl_conformant).length,
290
+ fail: drivers.filter((d) => !d.manifest_valid || !d.idl_conformant).length,
291
+ overall: all_pass ? 'pass' : 'fail',
292
+ },
293
+ })
294
+ }
@@ -135,6 +135,20 @@ async function cmdGateCheck(projectDir, extraArgs) {
135
135
  const cache = cacheMod ? cacheMod.readCache(ctx.taskDir) : {}
136
136
 
137
137
  for (const gate of gates) {
138
+ // Applicability check fires BEFORE cache lookup or check() execution.
139
+ // Gates that don't apply to the active profile (lite vs full-pipeline)
140
+ // short-circuit with a skip verdict. Skip results are intentionally
141
+ // NOT cached (see status: 'skip' branch below), so a track change on
142
+ // the same task immediately reflects in the next gate-check.
143
+ if (typeof gate.applies_to_profile === 'function' && gate.applies_to_profile(ctx) === false) {
144
+ skipped.push({
145
+ id: gate.id,
146
+ severity: gate.severity,
147
+ reason: `profile-not-applicable: ${ctx.profile}`,
148
+ })
149
+ continue
150
+ }
151
+
138
152
  let verdict
139
153
  let cached = false
140
154
  let inputHash = null
@@ -244,6 +258,7 @@ async function cmdGateCheck(projectDir, extraArgs) {
244
258
  command: 'gate-check',
245
259
  skill,
246
260
  task_id: taskId,
261
+ profile: ctx.profile,
247
262
  passed: passed.map((p) => p.id),
248
263
  warned: warned.map((w) => ({ id: w.id, reason: w.reason })),
249
264
  skipped: skipped.map((s) => ({ id: s.id, reason: s.reason })),