project-tiny-context-harness 0.2.84 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/README.md +163 -428
  2. package/assets/README.md +283 -455
  3. package/assets/README.zh-CN.md +199 -123
  4. package/assets/agents/AGENTS_CORE.md +54 -67
  5. package/assets/context_templates/context.toml +3 -0
  6. package/assets/context_templates/global.md +6 -6
  7. package/assets/github/harness.yml +2 -2
  8. package/assets/skills/context_development_engineer/SKILL.md +91 -107
  9. package/assets/skills/context_product_plan/SKILL.md +18 -28
  10. package/assets/skills/context_surface_contract/SKILL.md +35 -38
  11. package/assets/skills/context_uiux_design/SKILL.md +31 -30
  12. package/assets/skills/long-task-workflow/SKILL.md +76 -0
  13. package/assets/skills/long-task-workflow/agents/openai.yaml +4 -0
  14. package/assets/skills/long-task-workflow/references/authority-lifecycle.md +41 -0
  15. package/assets/skills/long-task-workflow/references/contract-authoring.md +62 -0
  16. package/assets/skills/long-task-workflow/references/evidence-design.md +45 -0
  17. package/assets/skills/normal-long-task/SKILL.md +12 -489
  18. package/assets/skills/source-plan-authoring/SKILL.md +379 -0
  19. package/assets/tools/validate_context.py +293 -127
  20. package/dist/cli.js +3 -1
  21. package/dist/commands/check-modularity.js +33 -10
  22. package/dist/commands/composite-campaign.d.ts +1 -0
  23. package/dist/commands/composite-campaign.js +8 -0
  24. package/dist/commands/composite-long-task.d.ts +1 -6
  25. package/dist/commands/composite-long-task.js +7 -157
  26. package/dist/commands/delivery-set.d.ts +1 -0
  27. package/dist/commands/delivery-set.js +8 -0
  28. package/dist/commands/disable.d.ts +1 -0
  29. package/dist/commands/disable.js +15 -0
  30. package/dist/commands/enable.d.ts +1 -0
  31. package/dist/commands/enable.js +14 -0
  32. package/dist/commands/export-context-args.js +13 -4
  33. package/dist/commands/export-context.js +18 -7
  34. package/dist/commands/index.js +42 -30
  35. package/dist/commands/init.js +12 -6
  36. package/dist/commands/long-task-authoring.d.ts +3 -0
  37. package/dist/commands/long-task-authoring.js +92 -0
  38. package/dist/commands/long-task-explain.d.ts +1 -0
  39. package/dist/commands/long-task-explain.js +52 -0
  40. package/dist/commands/long-task.d.ts +1 -0
  41. package/dist/commands/long-task.js +226 -0
  42. package/dist/commands/package-source.js +2 -2
  43. package/dist/commands/upgrade.js +10 -10
  44. package/dist/index.d.ts +11 -1
  45. package/dist/index.js +8 -0
  46. package/dist/lib/config.js +34 -8
  47. package/dist/lib/context-default-footprint.d.ts +16 -0
  48. package/dist/lib/context-default-footprint.js +107 -0
  49. package/dist/lib/context-export.js +105 -41
  50. package/dist/lib/context-graph-snapshot.d.ts +18 -0
  51. package/dist/lib/context-graph-snapshot.js +171 -0
  52. package/dist/lib/context-manifest-schema.d.ts +28 -0
  53. package/dist/lib/context-manifest-schema.js +150 -0
  54. package/dist/lib/context-manifest.js +28 -20
  55. package/dist/lib/context-templates.js +5 -5
  56. package/dist/lib/design-md.js +1 -1
  57. package/dist/lib/doctor.js +54 -1
  58. package/dist/lib/harness-root.js +25 -8
  59. package/dist/lib/init.js +6 -3
  60. package/dist/lib/legacy-managed-scan.js +23 -7
  61. package/dist/lib/legacy-sdlc-migration.js +21 -11
  62. package/dist/lib/long-task-acceptance-reference.d.ts +22 -0
  63. package/dist/lib/long-task-acceptance-reference.js +46 -0
  64. package/dist/lib/long-task-acceptance-shape.d.ts +5 -0
  65. package/dist/lib/long-task-acceptance-shape.js +131 -0
  66. package/dist/lib/long-task-activation-validation.d.ts +21 -0
  67. package/dist/lib/long-task-activation-validation.js +136 -0
  68. package/dist/lib/long-task-artifacts.d.ts +5 -0
  69. package/dist/lib/long-task-artifacts.js +34 -0
  70. package/dist/lib/long-task-assertions-v2.d.ts +22 -0
  71. package/dist/lib/long-task-assertions-v2.js +212 -0
  72. package/dist/lib/long-task-authoring-authority-preview.d.ts +5 -0
  73. package/dist/lib/long-task-authoring-authority-preview.js +85 -0
  74. package/dist/lib/long-task-authoring-preflight-diagnostics.d.ts +8 -0
  75. package/dist/lib/long-task-authoring-preflight-diagnostics.js +179 -0
  76. package/dist/lib/long-task-authoring-preflight-repair-order.d.ts +8 -0
  77. package/dist/lib/long-task-authoring-preflight-repair-order.js +95 -0
  78. package/dist/lib/long-task-authoring-preflight-types.d.ts +46 -0
  79. package/dist/lib/long-task-authoring-preflight-types.js +38 -0
  80. package/dist/lib/long-task-authoring-preflight.d.ts +3 -0
  81. package/dist/lib/long-task-authoring-preflight.js +44 -0
  82. package/dist/lib/long-task-authority-material-diff.d.ts +17 -0
  83. package/dist/lib/long-task-authority-material-diff.js +152 -0
  84. package/dist/lib/long-task-authority-materials.d.ts +7 -0
  85. package/dist/lib/long-task-authority-materials.js +83 -0
  86. package/dist/lib/long-task-authority-policy.d.ts +168 -0
  87. package/dist/lib/long-task-authority-policy.js +181 -0
  88. package/dist/lib/long-task-authority-revision-details.d.ts +19 -0
  89. package/dist/lib/long-task-authority-revision-details.js +192 -0
  90. package/dist/lib/long-task-authority-revision-enforcement.d.ts +3 -0
  91. package/dist/lib/long-task-authority-revision-enforcement.js +41 -0
  92. package/dist/lib/long-task-authority-revision-types.d.ts +50 -0
  93. package/dist/lib/long-task-authority-revision-types.js +1 -0
  94. package/dist/lib/long-task-authority-revision.d.ts +4 -0
  95. package/dist/lib/long-task-authority-revision.js +188 -0
  96. package/dist/lib/long-task-authority-transition-policy.d.ts +62 -0
  97. package/dist/lib/long-task-authority-transition-policy.js +62 -0
  98. package/dist/lib/long-task-authority-types.d.ts +96 -0
  99. package/dist/lib/long-task-authority-types.js +1 -0
  100. package/dist/lib/long-task-authority.d.ts +7 -0
  101. package/dist/lib/long-task-authority.js +142 -0
  102. package/dist/lib/long-task-boundary-check.d.ts +15 -0
  103. package/dist/lib/long-task-boundary-check.js +9 -0
  104. package/dist/lib/long-task-check-evidence-decoder.d.ts +3 -0
  105. package/dist/lib/long-task-check-evidence-decoder.js +74 -0
  106. package/dist/lib/long-task-check-execution-policy.d.ts +18 -0
  107. package/dist/lib/long-task-check-execution-policy.js +36 -0
  108. package/dist/lib/long-task-check-runner.d.ts +2 -0
  109. package/dist/lib/long-task-check-runner.js +181 -0
  110. package/dist/lib/long-task-check-shape.d.ts +2 -0
  111. package/dist/lib/long-task-check-shape.js +136 -0
  112. package/dist/lib/long-task-claim-definitions.d.ts +7 -0
  113. package/dist/lib/long-task-claim-definitions.js +78 -0
  114. package/dist/lib/long-task-claim-proof-policy.d.ts +4 -0
  115. package/dist/lib/long-task-claim-proof-policy.js +57 -0
  116. package/dist/lib/long-task-claims.d.ts +10 -0
  117. package/dist/lib/long-task-claims.js +187 -0
  118. package/dist/lib/long-task-context-authority-topology.d.ts +7 -0
  119. package/dist/lib/long-task-context-authority-topology.js +37 -0
  120. package/dist/lib/long-task-context-authority.d.ts +14 -0
  121. package/dist/lib/long-task-context-authority.js +66 -0
  122. package/dist/lib/long-task-contract-types.d.ts +203 -0
  123. package/dist/lib/long-task-contract-types.js +1 -0
  124. package/dist/lib/long-task-counterfactual-claim-policy.d.ts +10 -0
  125. package/dist/lib/long-task-counterfactual-claim-policy.js +57 -0
  126. package/dist/lib/long-task-counterfactual-sandbox.d.ts +6 -0
  127. package/dist/lib/long-task-counterfactual-sandbox.js +87 -0
  128. package/dist/lib/long-task-counterfactual-types.d.ts +24 -0
  129. package/dist/lib/long-task-counterfactual-types.js +1 -0
  130. package/dist/lib/long-task-delivery-compiler.d.ts +10 -0
  131. package/dist/lib/long-task-delivery-compiler.js +129 -0
  132. package/dist/lib/long-task-delivery-parser.d.ts +13 -0
  133. package/dist/lib/long-task-delivery-parser.js +90 -0
  134. package/dist/lib/long-task-delivery-preflight.d.ts +5 -0
  135. package/dist/lib/long-task-delivery-preflight.js +151 -0
  136. package/dist/lib/long-task-delivery-shape.d.ts +5 -0
  137. package/dist/lib/long-task-delivery-shape.js +5 -0
  138. package/dist/lib/long-task-delivery-types.d.ts +8 -0
  139. package/dist/lib/long-task-delivery-types.js +8 -0
  140. package/dist/lib/long-task-delivery-validation.d.ts +3 -0
  141. package/dist/lib/long-task-delivery-validation.js +175 -0
  142. package/dist/lib/long-task-evidence-adapter-policy.d.ts +4 -0
  143. package/dist/lib/long-task-evidence-adapter-policy.js +15 -0
  144. package/dist/lib/long-task-evidence-adapter-types.d.ts +1 -0
  145. package/dist/lib/long-task-evidence-adapter-types.js +1 -0
  146. package/dist/lib/long-task-evidence-findings.d.ts +5 -0
  147. package/dist/lib/long-task-evidence-findings.js +113 -0
  148. package/dist/lib/long-task-evidence-sensitivity-policy.d.ts +4 -0
  149. package/dist/lib/long-task-evidence-sensitivity-policy.js +98 -0
  150. package/dist/lib/long-task-evidence-v2.d.ts +5 -0
  151. package/dist/lib/long-task-evidence-v2.js +226 -0
  152. package/dist/lib/long-task-explain-acceptance-link.d.ts +46 -0
  153. package/dist/lib/long-task-explain-acceptance-link.js +96 -0
  154. package/dist/lib/long-task-explain-claim-links.d.ts +45 -0
  155. package/dist/lib/long-task-explain-claim-links.js +88 -0
  156. package/dist/lib/long-task-explain-source-links.d.ts +93 -0
  157. package/dist/lib/long-task-explain-source-links.js +51 -0
  158. package/dist/lib/long-task-final-v2.d.ts +2 -0
  159. package/dist/lib/long-task-final-v2.js +124 -0
  160. package/dist/lib/long-task-finding-context.d.ts +3 -0
  161. package/dist/lib/long-task-finding-context.js +63 -0
  162. package/dist/lib/long-task-freshness.d.ts +3 -0
  163. package/dist/lib/long-task-freshness.js +80 -0
  164. package/dist/lib/long-task-hook-install.d.ts +9 -0
  165. package/dist/lib/long-task-hook-install.js +184 -0
  166. package/dist/lib/long-task-hook-preflight.d.ts +7 -0
  167. package/dist/lib/long-task-hook-preflight.js +58 -0
  168. package/dist/lib/long-task-observation-ownership.d.ts +2 -0
  169. package/dist/lib/long-task-observation-ownership.js +22 -0
  170. package/dist/lib/long-task-outcome-parser.d.ts +4 -0
  171. package/dist/lib/long-task-outcome-parser.js +100 -0
  172. package/dist/lib/long-task-paths.d.ts +49 -0
  173. package/dist/lib/long-task-paths.js +418 -0
  174. package/dist/lib/long-task-playwright-counterfactual-policy.d.ts +7 -0
  175. package/dist/lib/long-task-playwright-counterfactual-policy.js +125 -0
  176. package/dist/lib/long-task-playwright-evidence.d.ts +6 -0
  177. package/dist/lib/long-task-playwright-evidence.js +244 -0
  178. package/dist/lib/long-task-product-shape.d.ts +6 -0
  179. package/dist/lib/long-task-product-shape.js +73 -0
  180. package/dist/lib/long-task-progress.d.ts +4 -0
  181. package/dist/lib/long-task-progress.js +114 -0
  182. package/dist/lib/long-task-protected-files.d.ts +1 -0
  183. package/dist/lib/long-task-protected-files.js +30 -0
  184. package/dist/lib/long-task-required-proof-surfaces.d.ts +2 -0
  185. package/dist/lib/long-task-required-proof-surfaces.js +13 -0
  186. package/dist/lib/long-task-requirement-shape.d.ts +2 -0
  187. package/dist/lib/long-task-requirement-shape.js +18 -0
  188. package/dist/lib/long-task-risk-surfaces.d.ts +2 -0
  189. package/dist/lib/long-task-risk-surfaces.js +60 -0
  190. package/dist/lib/long-task-risk-types.d.ts +5 -0
  191. package/dist/lib/long-task-risk-types.js +12 -0
  192. package/dist/lib/long-task-risk.d.ts +9 -0
  193. package/dist/lib/long-task-risk.js +132 -0
  194. package/dist/lib/long-task-root-shape.d.ts +4 -0
  195. package/dist/lib/long-task-root-shape.js +86 -0
  196. package/dist/lib/long-task-runner-environment.d.ts +7 -0
  197. package/dist/lib/long-task-runner-environment.js +57 -0
  198. package/dist/lib/long-task-runner-files.d.ts +3 -0
  199. package/dist/lib/long-task-runner-files.js +39 -0
  200. package/dist/lib/long-task-runner-freeze.d.ts +2 -0
  201. package/dist/lib/long-task-runner-freeze.js +168 -0
  202. package/dist/lib/long-task-runtime-types.d.ts +214 -0
  203. package/dist/lib/long-task-runtime-types.js +1 -0
  204. package/dist/lib/long-task-shape-primitives.d.ts +17 -0
  205. package/dist/lib/long-task-shape-primitives.js +81 -0
  206. package/dist/lib/long-task-source-authority-types.d.ts +13 -0
  207. package/dist/lib/long-task-source-authority-types.js +1 -0
  208. package/dist/lib/long-task-source-claim-validation.d.ts +5 -0
  209. package/dist/lib/long-task-source-claim-validation.js +96 -0
  210. package/dist/lib/long-task-source-continuity.d.ts +4 -0
  211. package/dist/lib/long-task-source-continuity.js +57 -0
  212. package/dist/lib/long-task-source-inventory.d.ts +2 -0
  213. package/dist/lib/long-task-source-inventory.js +21 -0
  214. package/dist/lib/long-task-source-item-parser.d.ts +3 -0
  215. package/dist/lib/long-task-source-item-parser.js +112 -0
  216. package/dist/lib/long-task-source-shape.d.ts +2 -0
  217. package/dist/lib/long-task-source-shape.js +56 -0
  218. package/dist/lib/long-task-source-target-continuity.d.ts +4 -0
  219. package/dist/lib/long-task-source-target-continuity.js +80 -0
  220. package/dist/lib/long-task-source-target-index.d.ts +12 -0
  221. package/dist/lib/long-task-source-target-index.js +76 -0
  222. package/dist/lib/long-task-source-validation.d.ts +3 -0
  223. package/dist/lib/long-task-source-validation.js +46 -0
  224. package/dist/lib/long-task-state.d.ts +91 -0
  225. package/dist/lib/long-task-state.js +611 -0
  226. package/dist/lib/long-task-status-projection.d.ts +21 -0
  227. package/dist/lib/long-task-status-projection.js +116 -0
  228. package/dist/lib/long-task-status-v2.d.ts +37 -0
  229. package/dist/lib/long-task-status-v2.js +306 -0
  230. package/dist/lib/long-task-technical-shape.d.ts +3 -0
  231. package/dist/lib/long-task-technical-shape.js +44 -0
  232. package/dist/lib/long-task-verifier-authority.d.ts +12 -0
  233. package/dist/lib/long-task-verifier-authority.js +47 -0
  234. package/dist/lib/long-task-verifier-identity.d.ts +2 -0
  235. package/dist/lib/long-task-verifier-identity.js +55 -0
  236. package/dist/lib/long-task-verifier-v2.d.ts +13 -0
  237. package/dist/lib/long-task-verifier-v2.js +266 -0
  238. package/dist/lib/long-task-workspace-runtime-types.d.ts +23 -0
  239. package/dist/lib/long-task-workspace-runtime-types.js +1 -0
  240. package/dist/lib/long-task-workspace.d.ts +25 -0
  241. package/dist/lib/long-task-workspace.js +366 -0
  242. package/dist/lib/managed-file.js +20 -5
  243. package/dist/lib/migrations.js +156 -32
  244. package/dist/lib/modularity.d.ts +27 -1
  245. package/dist/lib/modularity.js +499 -23
  246. package/dist/lib/package-json-config.js +14 -5
  247. package/dist/lib/package-source.js +15 -3
  248. package/dist/lib/profiles.d.ts +13 -0
  249. package/dist/lib/profiles.js +66 -0
  250. package/dist/lib/schema-guard.js +4 -2
  251. package/dist/lib/source-files.js +14 -7
  252. package/dist/lib/source-pack-classify.js +91 -19
  253. package/dist/lib/source-pack-config.js +25 -8
  254. package/dist/lib/source-pack-export.js +84 -25
  255. package/dist/lib/source-pack-manifest.js +18 -6
  256. package/dist/lib/source-pack-records.js +35 -11
  257. package/dist/lib/source-pack-render.js +47 -18
  258. package/dist/lib/stable-json.d.ts +2 -0
  259. package/dist/lib/stable-json.js +21 -0
  260. package/dist/lib/strict-codec.d.ts +4 -0
  261. package/dist/lib/strict-codec.js +65 -0
  262. package/dist/lib/sync-engine.js +76 -24
  263. package/dist/lib/types.d.ts +8 -1
  264. package/dist/lib/upgrade.js +4 -3
  265. package/dist/lib/validators.js +218 -191
  266. package/dist/long-task-hook.d.ts +2 -0
  267. package/dist/long-task-hook.js +55 -0
  268. package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +340 -0
  269. package/dist/schemas/long-task-delivery-v2/long-task-outcomes-v2.schema.json +18 -0
  270. package/migrations/README.md +8 -3
  271. package/package.json +17 -6
  272. package/source-mappings.yaml +0 -3
  273. package/assets/protected-harness-baseline.json +0 -20
  274. package/assets/skills/composite-long-task-workflow/SKILL.md +0 -244
  275. package/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md +0 -57
  276. package/assets/skills/composite-long-task-workflow/assets/goal-objective.template.md +0 -17
  277. package/assets/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md +0 -675
  278. package/dist/commands/superpowers.d.ts +0 -1
  279. package/dist/commands/superpowers.js +0 -8
  280. package/dist/lib/composite-long-task-renderer.d.ts +0 -12
  281. package/dist/lib/composite-long-task-renderer.js +0 -109
  282. package/dist/lib/plan-acceptance-artifacts.d.ts +0 -1
  283. package/dist/lib/plan-acceptance-artifacts.js +0 -220
  284. package/dist/lib/plan-acceptance-evidence.d.ts +0 -2
  285. package/dist/lib/plan-acceptance-evidence.js +0 -108
  286. package/dist/lib/plan-acceptance-json.d.ts +0 -15
  287. package/dist/lib/plan-acceptance-json.js +0 -133
  288. package/dist/lib/plan-acceptance-validator.d.ts +0 -2
  289. package/dist/lib/plan-acceptance-validator.js +0 -209
  290. package/dist/lib/plan-contract-validator.d.ts +0 -2
  291. package/dist/lib/plan-contract-validator.js +0 -127
  292. package/dist/lib/plan-validator-common.d.ts +0 -24
  293. package/dist/lib/plan-validator-common.js +0 -196
  294. package/dist/lib/superpowers-task-ac010.d.ts +0 -6
  295. package/dist/lib/superpowers-task-ac010.js +0 -26
  296. package/dist/lib/superpowers-task-assertion-normalizers.d.ts +0 -3
  297. package/dist/lib/superpowers-task-assertion-normalizers.js +0 -74
  298. package/dist/lib/superpowers-task-assertions.d.ts +0 -20
  299. package/dist/lib/superpowers-task-assertions.js +0 -257
  300. package/dist/lib/superpowers-task-attempt.d.ts +0 -4
  301. package/dist/lib/superpowers-task-attempt.js +0 -102
  302. package/dist/lib/superpowers-task-command-run-correlation.d.ts +0 -8
  303. package/dist/lib/superpowers-task-command-run-correlation.js +0 -103
  304. package/dist/lib/superpowers-task-command-specs.d.ts +0 -3
  305. package/dist/lib/superpowers-task-command-specs.js +0 -52
  306. package/dist/lib/superpowers-task-compile-diagnostics.d.ts +0 -5
  307. package/dist/lib/superpowers-task-compile-diagnostics.js +0 -20
  308. package/dist/lib/superpowers-task-compile-guards.d.ts +0 -2
  309. package/dist/lib/superpowers-task-compile-guards.js +0 -66
  310. package/dist/lib/superpowers-task-compile.d.ts +0 -6
  311. package/dist/lib/superpowers-task-compile.js +0 -133
  312. package/dist/lib/superpowers-task-completion-output.d.ts +0 -52
  313. package/dist/lib/superpowers-task-completion-output.js +0 -228
  314. package/dist/lib/superpowers-task-conformance.d.ts +0 -2
  315. package/dist/lib/superpowers-task-conformance.js +0 -24
  316. package/dist/lib/superpowers-task-contradictions.d.ts +0 -6
  317. package/dist/lib/superpowers-task-contradictions.js +0 -126
  318. package/dist/lib/superpowers-task-current-evidence.d.ts +0 -3
  319. package/dist/lib/superpowers-task-current-evidence.js +0 -176
  320. package/dist/lib/superpowers-task-delivery.d.ts +0 -4
  321. package/dist/lib/superpowers-task-delivery.js +0 -96
  322. package/dist/lib/superpowers-task-derive.d.ts +0 -14
  323. package/dist/lib/superpowers-task-derive.js +0 -382
  324. package/dist/lib/superpowers-task-events.d.ts +0 -1
  325. package/dist/lib/superpowers-task-events.js +0 -13
  326. package/dist/lib/superpowers-task-evidence-kernel.d.ts +0 -19
  327. package/dist/lib/superpowers-task-evidence-kernel.js +0 -347
  328. package/dist/lib/superpowers-task-evidence-records.d.ts +0 -2
  329. package/dist/lib/superpowers-task-evidence-records.js +0 -55
  330. package/dist/lib/superpowers-task-evidence.d.ts +0 -10
  331. package/dist/lib/superpowers-task-evidence.js +0 -147
  332. package/dist/lib/superpowers-task-fields.d.ts +0 -22
  333. package/dist/lib/superpowers-task-fields.js +0 -276
  334. package/dist/lib/superpowers-task-final-card.d.ts +0 -3
  335. package/dist/lib/superpowers-task-final-card.js +0 -24
  336. package/dist/lib/superpowers-task-gates.d.ts +0 -12
  337. package/dist/lib/superpowers-task-gates.js +0 -155
  338. package/dist/lib/superpowers-task-harness-drift.d.ts +0 -11
  339. package/dist/lib/superpowers-task-harness-drift.js +0 -90
  340. package/dist/lib/superpowers-task-next-slices.d.ts +0 -1
  341. package/dist/lib/superpowers-task-next-slices.js +0 -12
  342. package/dist/lib/superpowers-task-protected-baseline.d.ts +0 -10
  343. package/dist/lib/superpowers-task-protected-baseline.js +0 -66
  344. package/dist/lib/superpowers-task-source-compile.d.ts +0 -5
  345. package/dist/lib/superpowers-task-source-compile.js +0 -226
  346. package/dist/lib/superpowers-task-source-parser.d.ts +0 -23
  347. package/dist/lib/superpowers-task-source-parser.js +0 -218
  348. package/dist/lib/superpowers-task-state-schema.d.ts +0 -440
  349. package/dist/lib/superpowers-task-state-schema.js +0 -66
  350. package/dist/lib/superpowers-task-state-shape.d.ts +0 -3
  351. package/dist/lib/superpowers-task-state-shape.js +0 -50
  352. package/dist/lib/superpowers-task-state.d.ts +0 -16
  353. package/dist/lib/superpowers-task-state.js +0 -246
  354. package/dist/lib/superpowers-task-status.d.ts +0 -2
  355. package/dist/lib/superpowers-task-status.js +0 -24
  356. package/dist/lib/superpowers-task-under-specified.d.ts +0 -7
  357. package/dist/lib/superpowers-task-under-specified.js +0 -61
  358. package/dist/lib/superpowers-task-unregistered-evidence.d.ts +0 -11
  359. package/dist/lib/superpowers-task-unregistered-evidence.js +0 -72
  360. package/dist/lib/superpowers-task-validator.d.ts +0 -5
  361. package/dist/lib/superpowers-task-validator.js +0 -290
@@ -1,9 +1,20 @@
1
1
  #!/usr/bin/env python3
2
- from pathlib import Path
2
+ """Standalone Minimal Context validator installed with the portable profile."""
3
+
4
+ from pathlib import Path, PureWindowsPath
5
+ import json
3
6
  import re
7
+ import subprocess
4
8
  import sys
5
9
 
6
- ROOT = Path.cwd()
10
+ try:
11
+ import tomllib
12
+ except ImportError: # Python 3.10 compatibility; Node is already required by ty-context.
13
+ tomllib = None
14
+
15
+
16
+ ROOT = Path.cwd().resolve()
17
+ CONTEXT_ROOT = ROOT / "project_context"
7
18
 
8
19
  GLOBAL_CHECKS = [
9
20
  ("project goal", ["project goal", "项目目标", "目标"]),
@@ -38,13 +49,14 @@ VALID_ROLES = {
38
49
  "implementation-index",
39
50
  "decision-rationale",
40
51
  }
41
-
42
52
  ROLE_ALIASES = {
43
53
  "implementation_index": "implementation-index",
44
54
  "decision_rationale": "decision-rationale",
45
55
  }
46
-
47
56
  READ_POLICIES = {"default", "always", "optional", "on-demand", "never-default"}
57
+ TOP_LEVEL_FIELDS = {"areas", "context"}
58
+ AREA_FIELDS = {"id", "root", "context", "kind", "default", "forbidden_runtime_dependencies"}
59
+ CONTEXT_FIELDS = {"path", "role", "read_when", "read_policy", "triggers", "default_children"}
48
60
  CONFIG_CANDIDATES = [
49
61
  ".agent/config.yaml",
50
62
  ".codex/config.yaml",
@@ -55,6 +67,16 @@ CONFIG_CANDIDATES = [
55
67
  ".roo/config.yaml",
56
68
  ".gemini/config.yaml",
57
69
  ]
70
+ FAKE_VERIFICATION_PATTERNS = [
71
+ re.compile(r"\btests?\s+(?:pass(?:ed|es)?|green)\b", re.I),
72
+ re.compile(r"\bverified\b", re.I),
73
+ re.compile(r"\bdeployed\s+successfully\b", re.I),
74
+ re.compile(r"\bvalidation\s+passed\b", re.I),
75
+ re.compile(r"测试(?:已)?通过"),
76
+ re.compile(r"验证(?:已)?通过"),
77
+ re.compile(r"部署(?:已)?成功"),
78
+ ]
79
+ PLACEHOLDER = re.compile(r"^(?:todo|tbd|placeholder|coming soon|待补充|占位|暂无)[.!。…\s-]*$", re.I)
58
80
 
59
81
 
60
82
  def has_any(text, terms):
@@ -67,6 +89,10 @@ def normalize_role(value):
67
89
  return role if role in VALID_ROLES else None
68
90
 
69
91
 
92
+ def normalize_context_path(value):
93
+ return value.replace("\\", "/").removeprefix("./")
94
+
95
+
70
96
  def parse_front_matter(text):
71
97
  lines = text.splitlines()
72
98
  if not lines or lines[0].strip() != "---":
@@ -81,130 +107,194 @@ def parse_front_matter(text):
81
107
  return result
82
108
 
83
109
 
84
- def strip_comment(line):
85
- quote = None
86
- for index, char in enumerate(line):
87
- if char in {"'", "\""} and (index == 0 or line[index - 1] != "\\"):
88
- quote = None if quote == char else quote or char
89
- if char == "#" and quote is None:
90
- return line[:index]
91
- return line
92
-
93
-
94
- def parse_toml_value(raw):
95
- value = raw.strip()
96
- if value == "true":
97
- return True
98
- if value == "false":
99
- return False
100
- if (value.startswith("\"") and value.endswith("\"")) or (value.startswith("'") and value.endswith("'")):
101
- return value[1:-1]
102
- if value.startswith("[") and value.endswith("]"):
103
- inner = value[1:-1].strip()
104
- if not inner:
105
- return []
106
- items = []
107
- for part in inner.split(","):
108
- item = part.strip()
109
- if not ((item.startswith("\"") and item.endswith("\"")) or (item.startswith("'") and item.endswith("'"))):
110
- return None
111
- items.append(item[1:-1])
112
- return items
113
- return None
110
+ def table_lines(content, table):
111
+ return [
112
+ index
113
+ for index, line in enumerate(content.splitlines(), start=1)
114
+ if line.strip() == f"[[{table}]]"
115
+ ]
114
116
 
115
117
 
116
118
  def parse_manifest(path, errors):
117
- manifest = {"areas": [], "context": []}
118
- current = None
119
- for line_number, raw in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1):
120
- line = strip_comment(raw).strip()
121
- if not line:
122
- continue
123
- match = re.match(r"^\[\[(areas|context)\]\]$", line)
124
- if match:
125
- current = {"line": line_number}
126
- manifest[match.group(1)].append(current)
127
- continue
128
- if current is None:
129
- errors.append(f"project_context/context.toml line {line_number} must appear inside [[areas]] or [[context]]")
130
- continue
131
- assignment = re.match(r"^([A-Za-z0-9_-]+)\s*=\s*(.+)$", line)
132
- if not assignment:
133
- errors.append(f"project_context/context.toml line {line_number} is not a supported assignment")
134
- continue
135
- value = parse_toml_value(assignment.group(2))
136
- if value is None:
137
- errors.append(f"project_context/context.toml line {line_number} has an unsupported value")
138
- continue
139
- current[assignment.group(1)] = value
119
+ content = path.read_text(encoding="utf-8")
120
+ try:
121
+ parsed = parse_toml(content)
122
+ except (ValueError, RuntimeError) as error:
123
+ errors.append(f"project_context/context.toml is not valid TOML: {error}")
124
+ return {"areas": [], "context": []}
125
+ for key in parsed:
126
+ if key not in TOP_LEVEL_FIELDS:
127
+ errors.append(f"project_context/context.toml has unknown top-level field {key}")
128
+ manifest = {}
129
+ for table, allowed in (("areas", AREA_FIELDS), ("context", CONTEXT_FIELDS)):
130
+ entries = parsed.get(table, [])
131
+ if not isinstance(entries, list) or any(not isinstance(entry, dict) for entry in entries):
132
+ errors.append(f"project_context/context.toml field [[{table}]] must be an array of TOML tables")
133
+ entries = []
134
+ lines = table_lines(content, table)
135
+ manifest[table] = []
136
+ for index, raw in enumerate(entries):
137
+ entry = dict(raw)
138
+ entry["line"] = lines[index] if index < len(lines) else 1
139
+ for key in raw:
140
+ if key not in allowed:
141
+ errors.append(f"project_context/context.toml line {entry['line']} has unknown field {key}")
142
+ manifest[table].append(entry)
140
143
  return manifest
141
144
 
142
145
 
143
- def normalize_context_path(value):
144
- return value.replace("\\", "/").removeprefix("./")
146
+ def parse_toml(content):
147
+ if tomllib is not None:
148
+ try:
149
+ return tomllib.loads(content)
150
+ except tomllib.TOMLDecodeError as error:
151
+ raise ValueError(str(error)) from error
152
+ script = "import('smol-toml').then(({parse})=>{let s='';process.stdin.setEncoding('utf8');process.stdin.on('data',c=>s+=c);process.stdin.on('end',()=>process.stdout.write(JSON.stringify(parse(s))))}).catch(e=>{console.error(e.message);process.exit(1)})"
153
+ result = subprocess.run(
154
+ ["node", "--input-type=module", "--eval", script],
155
+ input=content,
156
+ text=True,
157
+ capture_output=True,
158
+ cwd=ROOT,
159
+ check=False,
160
+ )
161
+ if result.returncode != 0:
162
+ raise RuntimeError(f"formal TOML parser unavailable: {result.stderr.strip()}")
163
+ return json.loads(result.stdout)
164
+
165
+
166
+ def required_string(entry, key, errors):
167
+ value = entry.get(key)
168
+ if not isinstance(value, str) or not value.strip():
169
+ errors.append(f"project_context/context.toml line {entry['line']} must include non-empty string field {key}")
170
+ return None
171
+ return value
172
+
173
+
174
+ def optional_string(entry, key, errors):
175
+ value = entry.get(key)
176
+ if value is None:
177
+ return None
178
+ if not isinstance(value, str) or not value.strip():
179
+ errors.append(f"project_context/context.toml line {entry['line']} field {key} must be a non-empty string")
180
+ return None
181
+ return value
182
+
183
+
184
+ def string_array(entry, key, errors):
185
+ value = entry.get(key, [])
186
+ if not isinstance(value, list) or any(not isinstance(item, str) or not item.strip() for item in value):
187
+ errors.append(f"project_context/context.toml line {entry['line']} {key} must be an array of non-empty strings")
188
+ return []
189
+ if len(value) != len(set(value)):
190
+ errors.append(f"project_context/context.toml line {entry['line']} {key} must not contain duplicates")
191
+ return value
192
+
193
+
194
+ def is_relative_to(target, root):
195
+ try:
196
+ target.relative_to(root)
197
+ return True
198
+ except ValueError:
199
+ return False
145
200
 
146
201
 
147
- def add_manifest_role(entry, role, roles, errors):
148
- raw_path = entry.get("context") or entry.get("path")
149
- if not isinstance(raw_path, str):
150
- errors.append(f"project_context/context.toml line {entry['line']} must include a context/path string")
151
- return
152
- rel = normalize_context_path(raw_path)
153
- if not rel.startswith("project_context/") or not rel.endswith(".md"):
154
- errors.append(f"project_context/context.toml line {entry['line']} must reference a markdown file under project_context/")
155
- return
156
- existing = roles.get(rel)
157
- if existing and existing != role:
158
- errors.append(f"project_context/context.toml assigns conflicting roles to {rel}: {existing} and {role}")
159
- return
160
- roles[rel] = role
161
- if not (ROOT / rel).exists():
162
- errors.append(f"project_context/context.toml references missing context file: {rel}")
202
+ def safe_manifest_path(raw, allowed_root, label, expected, errors):
203
+ normalized = raw.replace("\\", "/")
204
+ if Path(raw).is_absolute() or PureWindowsPath(raw).is_absolute() or ".." in normalized.split("/"):
205
+ errors.append(f"project_context/context.toml {label} path must be relative and must not contain '..': {raw}")
206
+ return None
207
+ target = ROOT.joinpath(*normalized.split("/"))
208
+ if not target.exists():
209
+ errors.append(f"project_context/context.toml references missing {expected}: {normalized}")
210
+ return None
211
+ resolved_root = allowed_root.resolve()
212
+ resolved_target = target.resolve()
213
+ if not is_relative_to(resolved_target, resolved_root):
214
+ errors.append(f"project_context/context.toml {label} resolves outside its allowed root: {raw}")
215
+ return None
216
+ if expected == "context file" and not resolved_target.is_file():
217
+ errors.append(f"project_context/context.toml {label} must reference a regular file: {raw}")
218
+ return None
219
+ if expected == "area root" and not resolved_target.is_dir():
220
+ errors.append(f"project_context/context.toml {label} must reference a directory: {raw}")
221
+ return None
222
+ return resolved_target
163
223
 
164
224
 
165
225
  def validate_manifest(errors):
166
- manifest_path = ROOT / "project_context/context.toml"
226
+ manifest_path = CONTEXT_ROOT / "context.toml"
167
227
  roles = {}
228
+ policies = {}
168
229
  if not manifest_path.exists():
169
230
  if schema_requires_manifest():
170
231
  errors.append("missing project_context/context.toml; run ty-context upgrade to create the Schema v4 Context graph manifest")
171
- return roles
232
+ return roles, policies
172
233
  manifest = parse_manifest(manifest_path, errors)
173
- if not manifest["areas"]:
234
+ areas = manifest["areas"]
235
+ contexts = manifest["context"]
236
+ if not areas:
174
237
  errors.append("project_context/context.toml must declare at least one [[areas]] entry")
175
- has_default_area = False
176
- for area in manifest["areas"]:
177
- for key in ["id", "root", "context"]:
178
- if not isinstance(area.get(key), str):
179
- errors.append(f"project_context/context.toml line {area['line']} must include string field {key}")
180
- default_area = area.get("default")
181
- if default_area is not None and not isinstance(default_area, bool):
238
+ defaults = [entry for entry in areas if entry.get("default") is True]
239
+ if areas and len(defaults) != 1:
240
+ errors.append(f"project_context/context.toml must mark exactly one [[areas]] entry with default = true; found {len(defaults)}")
241
+ area_ids = set()
242
+ registered_paths = set()
243
+
244
+ for area in areas:
245
+ area_id = required_string(area, "id", errors)
246
+ area_root = required_string(area, "root", errors)
247
+ context_path = required_string(area, "context", errors)
248
+ optional_string(area, "kind", errors)
249
+ if "default" in area and not isinstance(area["default"], bool):
182
250
  errors.append(f"project_context/context.toml line {area['line']} default must be a boolean")
183
- has_default_area = has_default_area or default_area is True
184
- deps = area.get("forbidden_runtime_dependencies")
185
- if deps is not None and not is_string_array(deps):
186
- errors.append(f"project_context/context.toml line {area['line']} forbidden_runtime_dependencies must be an array of strings")
187
- add_manifest_role(area, "area", roles, errors)
188
- if manifest["areas"] and not has_default_area:
189
- errors.append("project_context/context.toml must mark one [[areas]] entry with default = true")
190
- for context in manifest["context"]:
191
- role = normalize_role(context.get("role", "")) if isinstance(context.get("role"), str) else None
192
- if role is None:
193
- errors.append(f"project_context/context.toml line {context['line']} has unsupported context role")
194
- continue
195
- read_policy = context.get("read_policy")
196
- if read_policy is not None and read_policy not in READ_POLICIES:
197
- errors.append(f"project_context/context.toml line {context['line']} has unsupported read_policy: {read_policy}")
198
- for key in ["triggers", "default_children"]:
199
- value = context.get(key)
200
- if value is not None and not is_string_array(value):
201
- errors.append(f"project_context/context.toml line {context['line']} {key} must be an array of strings")
202
- add_manifest_role(context, role, roles, errors)
203
- return roles
204
-
205
-
206
- def is_string_array(value):
207
- return isinstance(value, list) and all(isinstance(item, str) for item in value)
251
+ string_array(area, "forbidden_runtime_dependencies", errors)
252
+ if area_id:
253
+ if area_id in area_ids:
254
+ errors.append(f"project_context/context.toml has duplicate area id: {area_id}")
255
+ area_ids.add(area_id)
256
+ if area_root:
257
+ safe_manifest_path(area_root, ROOT, f"area {area_id or area['line']} root", "area root", errors)
258
+ if context_path:
259
+ register_context_path(context_path, "area", None, f"area {area_id or area['line']}", registered_paths, roles, policies, errors)
260
+
261
+ for entry in contexts:
262
+ context_path = required_string(entry, "path", errors)
263
+ raw_role = required_string(entry, "role", errors)
264
+ optional_string(entry, "read_when", errors)
265
+ read_policy = optional_string(entry, "read_policy", errors)
266
+ string_array(entry, "triggers", errors)
267
+ string_array(entry, "default_children", errors)
268
+ role = normalize_role(raw_role) if raw_role else None
269
+ if raw_role and role is None:
270
+ errors.append(f"project_context/context.toml line {entry['line']} has unsupported context role: {raw_role}")
271
+ if read_policy and read_policy not in READ_POLICIES:
272
+ errors.append(f"project_context/context.toml line {entry['line']} has unsupported read_policy: {read_policy}")
273
+ if context_path and role:
274
+ register_context_path(context_path, role, read_policy, f"context {context_path}", registered_paths, roles, policies, errors)
275
+
276
+ for entry in contexts:
277
+ for child in entry.get("default_children", []):
278
+ normalized = normalize_context_path(child)
279
+ if normalized not in registered_paths:
280
+ errors.append(f"project_context/context.toml line {entry['line']} default_children references unregistered Context path: {child}")
281
+ return roles, policies
282
+
283
+
284
+ def register_context_path(raw, role, read_policy, label, registered, roles, policies, errors):
285
+ normalized = normalize_context_path(raw)
286
+ if normalized in registered:
287
+ errors.append(f"project_context/context.toml has duplicate Context path: {normalized}")
288
+ return
289
+ registered.add(normalized)
290
+ if not normalized.startswith("project_context/") or not normalized.endswith(".md"):
291
+ errors.append(f"project_context/context.toml {label} must reference a markdown file under project_context/: {raw}")
292
+ return
293
+ if safe_manifest_path(raw, CONTEXT_ROOT, label, "context file", errors) is None:
294
+ return
295
+ roles[normalized] = role
296
+ if read_policy:
297
+ policies[normalized] = read_policy
208
298
 
209
299
 
210
300
  def schema_requires_manifest():
@@ -229,24 +319,90 @@ def validate_checks(rel, text, checks, errors):
229
319
  errors.append(f"{rel} must include {label}")
230
320
 
231
321
 
322
+ def section_bodies(text):
323
+ headings = list(re.finditer(r"^(#{2,6})\s+(.+?)\s*$", text, re.M))
324
+ for index, match in enumerate(headings):
325
+ end = headings[index + 1].start() if index + 1 < len(headings) else len(text)
326
+ yield match.group(2), text[match.end():end].strip()
327
+
328
+
329
+ def validate_no_fake_verification(rel, text, errors):
330
+ for heading, body in section_bodies(text):
331
+ if not re.search(r"verification|tests?|deployment|验证|测试|部署", heading, re.I):
332
+ continue
333
+ if any(pattern.search(body) for pattern in FAKE_VERIFICATION_PATTERNS):
334
+ errors.append(f"{rel} must list verification entry points, not claim tests were already executed")
335
+ return
336
+
337
+
338
+ def validate_minimum_recoverability(rel, text, errors):
339
+ if not re.search(r"^#\s+\S.+$", text, re.M):
340
+ errors.append(f"{rel} must contain a non-empty level-one title")
341
+ body = re.sub(r"^---\s*\n[\s\S]*?\n---\s*\n?", "", text, count=1)
342
+ facts = []
343
+ for line in body.splitlines():
344
+ value = line.strip()
345
+ if not value or value.startswith(("#", "```", "<!--")):
346
+ continue
347
+ value = re.sub(r"^[-*+>]\s*", "", value).strip()
348
+ if len(value) >= 8 and not PLACEHOLDER.fullmatch(value):
349
+ facts.append(value)
350
+ if not facts:
351
+ errors.append(f"{rel} must contain at least one concrete fact paragraph and cannot be only TODO or placeholder text")
352
+
353
+
354
+ def validate_role_recoverability(rel, text, role, errors):
355
+ if role == "verification" and not re.search(r"`[^`\r\n]+`|(?:npm|pnpm|yarn|make|node|python|pytest|cargo|go)\s+\S+|(?:^|\s)(?:\./|[\w.-]+/)[\w./-]+", text, re.I | re.M):
356
+ errors.append(f"{rel} verification Context must contain a command, repository path or repeatable entry point")
357
+ if role == "implementation-index":
358
+ candidates = [match.group(1) for match in re.finditer(r"`([^`]+)`", text) if ("/" in match.group(1) or "\\" in match.group(1)) and " " not in match.group(1)]
359
+ if not any(repository_path_exists(candidate) for candidate in candidates):
360
+ errors.append(f"{rel} implementation-index Context must contain at least one existing repository path")
361
+ if role == "contract" and not re.search(r"producer|consumer|input|output|schema|compatibility|constraint|must|shall|禁止|必须|输入|输出|兼容", text, re.I):
362
+ errors.append(f"{rel} contract Context must state at least one constraint or input/output semantic")
363
+ if role == "decision-rationale":
364
+ has_decision = re.search(r"^##\s+(?:(?:Decision|Current Design Choice|Design Choice)(?:\b|\s|$)|决定(?:\s|$)|当前设计选择(?:\s|$))", text, re.I | re.M)
365
+ has_reason = re.search(r"^##\s+(?:(?:Reason|Rationale|Why)(?:\b|\s|$)|原因(?:\s|$)|理由(?:\s|$)|为什么(?:\s|$))", text, re.I | re.M)
366
+ if not has_decision or not has_reason:
367
+ errors.append(f"{rel} decision-rationale Context must contain both a Decision and a Reason")
368
+
369
+
370
+ def repository_path_exists(candidate):
371
+ clean = candidate.rstrip(".:,;").replace("\\", "/")
372
+ if Path(clean).is_absolute() or PureWindowsPath(clean).is_absolute() or ".." in clean.split("/"):
373
+ return False
374
+ target = ROOT.joinpath(*clean.split("/"))
375
+ return target.exists() and is_relative_to(target.resolve(), ROOT)
376
+
377
+
378
+ def validate_context_file(rel, text, role, errors):
379
+ validate_no_fake_verification(rel, text, errors)
380
+ validate_minimum_recoverability(rel, text, errors)
381
+ if role:
382
+ validate_role_recoverability(rel, text, role, errors)
383
+
384
+
232
385
  def main():
233
386
  errors = []
234
- global_path = ROOT / "project_context/global.md"
387
+ warnings = []
388
+ global_path = CONTEXT_ROOT / "global.md"
389
+ architecture_path = CONTEXT_ROOT / "architecture.md"
235
390
  if not global_path.exists():
236
391
  errors.append("missing project_context/global.md")
237
392
  else:
238
- validate_checks("project_context/global.md", global_path.read_text(encoding="utf-8"), GLOBAL_CHECKS, errors)
239
-
240
- architecture_path = ROOT / "project_context/architecture.md"
393
+ text = global_path.read_text(encoding="utf-8")
394
+ validate_checks("project_context/global.md", text, GLOBAL_CHECKS, errors)
395
+ validate_context_file("project_context/global.md", text, "global", errors)
241
396
  if not architecture_path.exists():
242
397
  errors.append("missing project_context/architecture.md")
243
398
  else:
244
- validate_checks("project_context/architecture.md", architecture_path.read_text(encoding="utf-8"), ARCHITECTURE_CHECKS, errors)
399
+ text = architecture_path.read_text(encoding="utf-8")
400
+ validate_checks("project_context/architecture.md", text, ARCHITECTURE_CHECKS, errors)
401
+ validate_context_file("project_context/architecture.md", text, "architecture", errors)
245
402
 
246
- manifest_roles = validate_manifest(errors)
247
- context_root = ROOT / "project_context"
248
- context_files = sorted(context_root.rglob("*.md")) if context_root.exists() else []
403
+ manifest_roles, manifest_policies = validate_manifest(errors)
249
404
  checked = 0
405
+ context_files = sorted(CONTEXT_ROOT.rglob("*.md")) if CONTEXT_ROOT.exists() else []
250
406
  for path in context_files:
251
407
  rel = path.relative_to(ROOT).as_posix()
252
408
  if rel in {"project_context/global.md", "project_context/architecture.md"}:
@@ -254,16 +410,26 @@ def main():
254
410
  text = path.read_text(encoding="utf-8")
255
411
  front_matter = parse_front_matter(text)
256
412
  declared_role = front_matter.get("context_role")
257
- front_matter_role = normalize_role(declared_role) if declared_role else None
258
- if declared_role and front_matter_role is None:
413
+ front_role = normalize_role(declared_role) if declared_role else None
414
+ if declared_role and front_role is None:
259
415
  errors.append(f"{rel} has unsupported context_role: {declared_role}")
260
- read_policy = front_matter.get("read_policy")
261
- if read_policy and read_policy not in READ_POLICIES:
262
- errors.append(f"{rel} has unsupported read_policy: {read_policy}")
263
- role = manifest_roles.get(rel) or front_matter_role
264
- if role is not None and role not in {"global", "architecture"}:
265
- checked += 1
266
-
416
+ front_policy = front_matter.get("read_policy")
417
+ if front_policy and front_policy not in READ_POLICIES:
418
+ errors.append(f"{rel} has unsupported read_policy: {front_policy}")
419
+ manifest_role = manifest_roles.get(rel)
420
+ manifest_policy = manifest_policies.get(rel)
421
+ if manifest_role and front_role and manifest_role != front_role:
422
+ errors.append(f"{rel} front matter context_role {front_role} does not match manifest role {manifest_role}")
423
+ if manifest_policy and front_policy and manifest_policy != front_policy:
424
+ errors.append(f"{rel} front matter read_policy {front_policy} does not match manifest read_policy {manifest_policy}")
425
+ if not manifest_role:
426
+ warnings.append(f"{rel} is an unregistered Context Markdown file; add it to project_context/context.toml or move it out of project_context/**")
427
+ role = manifest_role or front_role
428
+ validate_context_file(rel, text, role, errors)
429
+ checked += 1
430
+
431
+ for warning in warnings:
432
+ print(f"warning: {warning}", file=sys.stderr)
267
433
  if errors:
268
434
  for error in errors:
269
435
  print(f"error: {error}", file=sys.stderr)
package/dist/cli.js CHANGED
@@ -2,7 +2,9 @@
2
2
  import { commands } from "./commands/index.js";
3
3
  const [command = "help", ...args] = process.argv.slice(2);
4
4
  async function main() {
5
- const handler = commands[command] ?? commands.help;
5
+ const handler = commands[command];
6
+ if (!handler)
7
+ throw new Error(`Unknown command: ${command}`);
6
8
  await handler(args);
7
9
  }
8
10
  main().catch((error) => {
@@ -9,7 +9,11 @@ export async function checkModularity(args) {
9
9
  process.exitCode = 1;
10
10
  return;
11
11
  }
12
- if (parsed.help || (!parsed.touched && !parsed.base && parsed.files.length === 0)) {
12
+ if (parsed.help ||
13
+ (!parsed.configOnly &&
14
+ !parsed.touched &&
15
+ !parsed.base &&
16
+ parsed.files.length === 0)) {
13
17
  console.log(helpText());
14
18
  if (!parsed.help) {
15
19
  process.exitCode = 1;
@@ -21,15 +25,21 @@ export async function checkModularity(args) {
21
25
  touched: parsed.touched,
22
26
  base: parsed.base,
23
27
  files: parsed.files,
24
- limit: parsed.limit
28
+ limit: parsed.limit,
25
29
  });
26
30
  console.log(`check-modularity audited=${report.files.length} warning=${report.warnings.length} limit=${report.limit} waived=${report.waivedWarnings.length}`);
27
31
  if (report.files.length === 0) {
28
32
  console.log("No handwritten source files matched the selected scope.");
29
33
  }
30
34
  for (const file of report.files) {
31
- const prefix = file.overLimit && file.waived ? "waived" : file.overLimit ? "over-limit" : "ok";
32
- console.log(`${prefix}: ${file.relativePath} ${file.lines} lines`);
35
+ const prefix = file.regressed && file.waived
36
+ ? "waived"
37
+ : file.regressed
38
+ ? "over-limit"
39
+ : file.overLimit
40
+ ? "observed-risk"
41
+ : "ok";
42
+ console.log(`${prefix}: ${file.relativePath} ${file.lines} lines statements=${file.metrics.maxFunctionStatements} branches=${file.metrics.maxBranchComplexity} exports=${file.metrics.exports} transitions=${file.metrics.stateTransitions} responsibilities=${file.metrics.responsibilities.join(",") || "none"} statement_at=${formatLocation(file.metrics.maxFunctionStatementsLocation)} branch_at=${formatLocation(file.metrics.maxBranchComplexityLocation)}`);
33
43
  }
34
44
  for (const error of report.errors) {
35
45
  console.error(`error: ${error}`);
@@ -41,9 +51,10 @@ export async function checkModularity(args) {
41
51
  console.warn(`waived: ${waiver}`);
42
52
  }
43
53
  if (report.warnings.length > 0) {
44
- console.warn("warning: over-limit touched files need a split or, when modularity.policy is scoped_waivers, a valid <harnessRoot>/config.yaml modularity waiver with file, reason and future split boundary.");
54
+ console.warn("warning: modularity risks need a split or, when modularity.policy is scoped_waivers, a valid <harnessRoot>/config.yaml waiver with path, category, owner, introduced_at, reason, tracking_issue and expiry_condition.");
45
55
  }
46
- if (report.errors.length > 0 || (report.warnings.length > 0 && parsed.failOnWarning)) {
56
+ if (report.errors.length > 0 ||
57
+ (report.warnings.length > 0 && parsed.failOnWarning)) {
47
58
  process.exitCode = 1;
48
59
  }
49
60
  }
@@ -52,13 +63,17 @@ export async function checkModularity(args) {
52
63
  process.exitCode = 1;
53
64
  }
54
65
  }
66
+ function formatLocation(location) {
67
+ return location ? `${location.symbol}:${location.line}` : "none";
68
+ }
55
69
  function parseArgs(args) {
56
70
  const parsed = {
57
71
  touched: false,
58
72
  files: [],
59
73
  limit: 300,
60
74
  failOnWarning: false,
61
- help: false
75
+ configOnly: false,
76
+ help: false,
62
77
  };
63
78
  for (let index = 0; index < args.length; index += 1) {
64
79
  const arg = args[index];
@@ -70,6 +85,10 @@ function parseArgs(args) {
70
85
  parsed.failOnWarning = true;
71
86
  continue;
72
87
  }
88
+ if (arg === "--config-only") {
89
+ parsed.configOnly = true;
90
+ continue;
91
+ }
73
92
  if (arg === "--help" || arg === "-h") {
74
93
  parsed.help = true;
75
94
  continue;
@@ -127,7 +146,9 @@ function parseArgs(args) {
127
146
  }
128
147
  function parseLimit(value) {
129
148
  const limit = Number.parseInt(value, 10);
130
- if (!Number.isInteger(limit) || limit <= 0 || String(limit) !== value.trim()) {
149
+ if (!Number.isInteger(limit) ||
150
+ limit <= 0 ||
151
+ String(limit) !== value.trim()) {
131
152
  throw new Error("check-modularity --limit requires a positive integer");
132
153
  }
133
154
  return limit;
@@ -137,9 +158,11 @@ function helpText() {
137
158
  check-modularity --touched [--limit 300] [--fail-on-warning]
138
159
  check-modularity --file <path> [--file <path> ...] [--limit 300] [--fail-on-warning]
139
160
  check-modularity --base <ref> [--limit 300] [--fail-on-warning]
161
+ check-modularity --config-only
140
162
 
141
- Audits selected handwritten source files for physical line-count risk.
163
+ Audits physical lines, per-function statements and branch complexity, exports, state transitions and module responsibilities.
164
+ For --touched and --base, existing findings are reported but only new or worsened non-line complexity is a warning; physical lines remain a risk signal and new files are audited in full.
142
165
  The default is warning-only; --fail-on-warning lets projects opt into CI enforcement.
143
166
  Generated configs default to modularity.policy: strict_except_generated; omitted policy is treated as scoped_waivers for compatibility.
144
- Over-limit files can be waived only through <harnessRoot>/config.yaml modularity.waivers when policy is scoped_waivers.`;
167
+ Risks can be waived only through lifecycle-complete <harnessRoot>/config.yaml modularity.waivers when policy is scoped_waivers.`;
145
168
  }
@@ -0,0 +1 @@
1
+ export declare function compositeCampaign(_args: string[]): void;
@@ -0,0 +1,8 @@
1
+ export function compositeCampaign(_args) {
2
+ console.log(JSON.stringify({
3
+ status: "retired",
4
+ command: "composite-campaign",
5
+ next_command: "ty-context long-task",
6
+ historical_state_executed: false,
7
+ }));
8
+ }
@@ -1,6 +1 @@
1
- export declare function compositeLongTask(args: string[]): Promise<void>;
2
- export declare function runCompositeLongTaskCommand(args: string[], options: {
3
- commandName: string;
4
- label: string;
5
- showHelp: boolean;
6
- }): Promise<void>;
1
+ export declare function compositeLongTask(_args: string[]): void;