create-yss-spec 2.2.10 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1721) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/cli.js +0 -1
  4. package/template/.agents/skills/.strategic-design-skills-manifest.json +35 -0
  5. package/template/.agents/skills/.yss-skills-manifest.json +0 -2
  6. package/template/.agents/skills/ask-matt/SKILL.md +1 -1
  7. package/template/.agents/skills/code-review/SKILL.md +1 -1
  8. package/template/.agents/skills/code-review/references/yss-review-standards.md +1 -1
  9. package/template/.agents/skills/lombok/SKILL.md +1 -1
  10. package/template/.agents/skills/mapstruct/SKILL.md +1 -1
  11. package/template/.agents/skills/prototype-review/SKILL.md +7 -6
  12. package/template/.agents/skills/wayfinder/SKILL.md +2 -2
  13. package/template/.agents/skills/yss-antd-design/SKILL.md +8 -7
  14. package/template/.agents/skills/yss-antd-design/references/evidence.md +11 -6
  15. package/template/.agents/skills/yss-api-integration/SKILL.md +2 -2
  16. package/template/.agents/skills/yss-application/SKILL.md +2 -2
  17. package/template/.agents/skills/yss-audit-log/SKILL.md +2 -2
  18. package/template/.agents/skills/yss-audit-log/references/source-index.md +1 -1
  19. package/template/.agents/skills/yss-cache/SKILL.md +2 -2
  20. package/template/.agents/skills/yss-cache/references/source-index.md +1 -1
  21. package/template/.agents/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  22. package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  23. package/template/.agents/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  24. package/template/.agents/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  25. package/template/.agents/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  26. package/template/.agents/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  27. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  28. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  29. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  30. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  31. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  32. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  33. package/template/.agents/skills/yss-design-system/SKILL.md +6 -6
  34. package/template/.agents/skills/yss-distributed-id/SKILL.md +2 -2
  35. package/template/.agents/skills/yss-distributed-id/references/source-index.md +1 -1
  36. package/template/.agents/skills/yss-domain/SKILL.md +3 -2
  37. package/template/.agents/skills/yss-domain/agents/openai.yaml +2 -2
  38. package/template/.agents/skills/yss-domain/references/domain-layer-guide.md +2 -0
  39. package/template/.agents/skills/yss-dto/SKILL.md +8 -2
  40. package/template/.agents/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  41. package/template/.agents/skills/yss-dto/references/source-index.md +1 -1
  42. package/template/.agents/skills/yss-excel-mvc/SKILL.md +2 -2
  43. package/template/.agents/skills/yss-excel-mvc/references/source-index.md +1 -1
  44. package/template/.agents/skills/yss-exception/SKILL.md +1 -1
  45. package/template/.agents/skills/yss-exception/references/source-index.md +1 -1
  46. package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  47. package/template/.agents/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  48. package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  49. package/template/.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  50. package/template/.agents/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  51. package/template/.agents/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  52. package/template/.agents/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  53. package/template/.agents/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  54. package/template/.agents/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  55. package/template/.agents/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  56. package/template/.agents/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  57. package/template/.agents/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  58. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  59. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  60. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  61. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  62. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  63. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  64. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  65. package/template/.agents/skills/yss-mybatis/SKILL.md +2 -2
  66. package/template/.agents/skills/yss-mybatis/references/source-index.md +1 -1
  67. package/template/.agents/skills/yss-page-module-development/SKILL.md +1 -1
  68. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +7 -7
  69. package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  70. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  71. package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  72. package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +1 -1
  73. package/template/.agents/skills/yss-prototype-stage/SKILL.md +34 -24
  74. package/template/.agents/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  75. package/template/.agents/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  76. package/template/.agents/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  77. package/template/.agents/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  78. package/template/.agents/skills/yss-repository/SKILL.md +16 -14
  79. package/template/.agents/skills/yss-repository/agents/openai.yaml +2 -2
  80. package/template/.agents/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  81. package/template/.agents/skills/yss-research/SKILL.md +73 -0
  82. package/template/.agents/skills/yss-research/agents/openai.yaml +3 -0
  83. package/template/.agents/skills/yss-research/assets/evidence-template.yaml +68 -0
  84. package/template/.agents/skills/yss-research/assets/research-brief-template.md +36 -0
  85. package/template/.agents/skills/yss-research/references/evidence-contract.md +42 -0
  86. package/template/.agents/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  87. package/template/.agents/skills/yss-research/tests/run-scenarios.mjs +81 -0
  88. package/template/.agents/skills/yss-resilience4j/SKILL.md +1 -1
  89. package/template/.agents/skills/yss-resilience4j/references/source-index.md +1 -1
  90. package/template/.agents/skills/yss-security-algorithm/SKILL.md +1 -1
  91. package/template/.agents/skills/yss-security-algorithm/references/source-index.md +1 -1
  92. package/template/.agents/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  93. package/template/.agents/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  94. package/template/.agents/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  95. package/template/.agents/skills/yss-stage-decision/SKILL.md +2 -1
  96. package/template/.agents/skills/yss-ui/SKILL.md +2 -1
  97. package/template/.agents/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  98. package/template/.agents/skills/yss-up-springboot3/SKILL.md +1 -1
  99. package/template/.agents/skills/yss-userinfo/SKILL.md +1 -1
  100. package/template/.agents/skills/yss-userinfo/references/source-index.md +1 -1
  101. package/template/.agents/skills/yss-validation/SKILL.md +1 -1
  102. package/template/.agents/skills/yss-validation/references/source-index.md +1 -1
  103. package/template/.agents/skills/yss-web-controller/SKILL.md +17 -11
  104. package/template/.agents/skills/yss-web-controller/agents/openai.yaml +2 -2
  105. package/template/.agents/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  106. package/template/.agents/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  107. package/template/.agents/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  108. package/template/.agents/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  109. package/template/.agents/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  110. package/template/.agents/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  111. package/template/.agents/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  112. package/template/.agents/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  113. package/template/.agents/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  114. package/template/.claude/skills/ask-matt/SKILL.md +1 -1
  115. package/template/.claude/skills/code-review/SKILL.md +1 -1
  116. package/template/.claude/skills/code-review/references/yss-review-standards.md +1 -1
  117. package/template/.claude/skills/lombok/SKILL.md +1 -1
  118. package/template/.claude/skills/mapstruct/SKILL.md +1 -1
  119. package/template/.claude/skills/prototype-review/SKILL.md +7 -6
  120. package/template/.claude/skills/wayfinder/SKILL.md +2 -2
  121. package/template/.claude/skills/yss-antd-design/SKILL.md +8 -7
  122. package/template/.claude/skills/yss-antd-design/references/evidence.md +11 -6
  123. package/template/.claude/skills/yss-api-integration/SKILL.md +2 -2
  124. package/template/.claude/skills/yss-application/SKILL.md +2 -2
  125. package/template/.claude/skills/yss-audit-log/SKILL.md +2 -2
  126. package/template/.claude/skills/yss-audit-log/references/source-index.md +1 -1
  127. package/template/.claude/skills/yss-cache/SKILL.md +2 -2
  128. package/template/.claude/skills/yss-cache/references/source-index.md +1 -1
  129. package/template/.claude/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  130. package/template/.claude/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  131. package/template/.claude/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  132. package/template/.claude/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  133. package/template/.claude/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  134. package/template/.claude/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  135. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  136. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  137. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  138. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  139. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  140. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  141. package/template/.claude/skills/yss-design-system/SKILL.md +6 -6
  142. package/template/.claude/skills/yss-distributed-id/SKILL.md +2 -2
  143. package/template/.claude/skills/yss-distributed-id/references/source-index.md +1 -1
  144. package/template/.claude/skills/yss-domain/SKILL.md +3 -2
  145. package/template/.claude/skills/yss-domain/agents/openai.yaml +2 -2
  146. package/template/.claude/skills/yss-domain/references/domain-layer-guide.md +2 -0
  147. package/template/.claude/skills/yss-dto/SKILL.md +8 -2
  148. package/template/.claude/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  149. package/template/.claude/skills/yss-dto/references/source-index.md +1 -1
  150. package/template/.claude/skills/yss-excel-mvc/SKILL.md +2 -2
  151. package/template/.claude/skills/yss-excel-mvc/references/source-index.md +1 -1
  152. package/template/.claude/skills/yss-exception/SKILL.md +1 -1
  153. package/template/.claude/skills/yss-exception/references/source-index.md +1 -1
  154. package/template/.claude/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  155. package/template/.claude/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  156. package/template/.claude/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  157. package/template/.claude/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  158. package/template/.claude/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  159. package/template/.claude/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  160. package/template/.claude/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  161. package/template/.claude/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  162. package/template/.claude/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  163. package/template/.claude/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  164. package/template/.claude/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  165. package/template/.claude/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  166. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  167. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  168. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  169. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  170. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  171. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  172. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  173. package/template/.claude/skills/yss-mybatis/SKILL.md +2 -2
  174. package/template/.claude/skills/yss-mybatis/references/source-index.md +1 -1
  175. package/template/.claude/skills/yss-page-module-development/SKILL.md +1 -1
  176. package/template/.claude/skills/yss-product-lifecycle/SKILL.md +7 -7
  177. package/template/.claude/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  178. package/template/.claude/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  179. package/template/.claude/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  180. package/template/.claude/skills/yss-product-lifecycle/references/state-model.md +1 -1
  181. package/template/.claude/skills/yss-prototype-stage/SKILL.md +34 -24
  182. package/template/.claude/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  183. package/template/.claude/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  184. package/template/.claude/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  185. package/template/.claude/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  186. package/template/.claude/skills/yss-repository/SKILL.md +16 -14
  187. package/template/.claude/skills/yss-repository/agents/openai.yaml +2 -2
  188. package/template/.claude/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  189. package/template/.claude/skills/yss-research/SKILL.md +73 -0
  190. package/template/.claude/skills/yss-research/agents/openai.yaml +3 -0
  191. package/template/.claude/skills/yss-research/assets/evidence-template.yaml +68 -0
  192. package/template/.claude/skills/yss-research/assets/research-brief-template.md +36 -0
  193. package/template/.claude/skills/yss-research/references/evidence-contract.md +42 -0
  194. package/template/.claude/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  195. package/template/.claude/skills/yss-research/tests/run-scenarios.mjs +81 -0
  196. package/template/.claude/skills/yss-resilience4j/SKILL.md +1 -1
  197. package/template/.claude/skills/yss-resilience4j/references/source-index.md +1 -1
  198. package/template/.claude/skills/yss-security-algorithm/SKILL.md +1 -1
  199. package/template/.claude/skills/yss-security-algorithm/references/source-index.md +1 -1
  200. package/template/.claude/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  201. package/template/.claude/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  202. package/template/.claude/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  203. package/template/.claude/skills/yss-stage-decision/SKILL.md +2 -1
  204. package/template/.claude/skills/yss-ui/SKILL.md +2 -1
  205. package/template/.claude/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  206. package/template/.claude/skills/yss-up-springboot3/SKILL.md +1 -1
  207. package/template/.claude/skills/yss-userinfo/SKILL.md +1 -1
  208. package/template/.claude/skills/yss-userinfo/references/source-index.md +1 -1
  209. package/template/.claude/skills/yss-validation/SKILL.md +1 -1
  210. package/template/.claude/skills/yss-validation/references/source-index.md +1 -1
  211. package/template/.claude/skills/yss-web-controller/SKILL.md +17 -11
  212. package/template/.claude/skills/yss-web-controller/agents/openai.yaml +2 -2
  213. package/template/.claude/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  214. package/template/.claude/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  215. package/template/.claude/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  216. package/template/.claude/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  217. package/template/.claude/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  218. package/template/.claude/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  219. package/template/.claude/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  220. package/template/.claude/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  221. package/template/.claude/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  222. package/template/.codex/skills/ask-matt/SKILL.md +1 -1
  223. package/template/.codex/skills/code-review/SKILL.md +1 -1
  224. package/template/.codex/skills/code-review/references/yss-review-standards.md +1 -1
  225. package/template/.codex/skills/lombok/SKILL.md +1 -1
  226. package/template/.codex/skills/mapstruct/SKILL.md +1 -1
  227. package/template/.codex/skills/product-design/package.json +4 -2
  228. package/template/.codex/skills/product-design/skills/research/SKILL.md +22 -0
  229. package/template/.codex/skills/product-design/skills/research/assets/evidence-template.yaml +65 -0
  230. package/template/.codex/skills/product-design/skills/research/assets/research-brief-template.md +33 -0
  231. package/template/.codex/skills/product-design/skills/research/references/evidence-audited.md +80 -0
  232. package/template/.codex/skills/product-design/skills/research/scripts/validate-research-package.mjs +149 -0
  233. package/template/.codex/skills/product-design/skills/research/tests/run-scenarios.mjs +72 -0
  234. package/template/.codex/skills/prototype-review/SKILL.md +7 -6
  235. package/template/.codex/skills/wayfinder/SKILL.md +2 -2
  236. package/template/.codex/skills/yss-antd-design/SKILL.md +8 -7
  237. package/template/.codex/skills/yss-antd-design/references/evidence.md +11 -6
  238. package/template/.codex/skills/yss-api-integration/SKILL.md +2 -2
  239. package/template/.codex/skills/yss-application/SKILL.md +2 -2
  240. package/template/.codex/skills/yss-audit-log/SKILL.md +2 -2
  241. package/template/.codex/skills/yss-audit-log/references/source-index.md +1 -1
  242. package/template/.codex/skills/yss-cache/SKILL.md +2 -2
  243. package/template/.codex/skills/yss-cache/references/source-index.md +1 -1
  244. package/template/.codex/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  245. package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  246. package/template/.codex/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  247. package/template/.codex/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  248. package/template/.codex/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  249. package/template/.codex/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  250. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  251. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  252. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  253. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  254. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  255. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  256. package/template/.codex/skills/yss-design-system/SKILL.md +6 -6
  257. package/template/.codex/skills/yss-distributed-id/SKILL.md +2 -2
  258. package/template/.codex/skills/yss-distributed-id/references/source-index.md +1 -1
  259. package/template/.codex/skills/yss-domain/SKILL.md +3 -2
  260. package/template/.codex/skills/yss-domain/agents/openai.yaml +2 -2
  261. package/template/.codex/skills/yss-domain/references/domain-layer-guide.md +2 -0
  262. package/template/.codex/skills/yss-dto/SKILL.md +8 -2
  263. package/template/.codex/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  264. package/template/.codex/skills/yss-dto/references/source-index.md +1 -1
  265. package/template/.codex/skills/yss-excel-mvc/SKILL.md +2 -2
  266. package/template/.codex/skills/yss-excel-mvc/references/source-index.md +1 -1
  267. package/template/.codex/skills/yss-exception/SKILL.md +1 -1
  268. package/template/.codex/skills/yss-exception/references/source-index.md +1 -1
  269. package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  270. package/template/.codex/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  271. package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  272. package/template/.codex/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  273. package/template/.codex/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  274. package/template/.codex/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  275. package/template/.codex/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  276. package/template/.codex/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  277. package/template/.codex/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  278. package/template/.codex/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  279. package/template/.codex/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  280. package/template/.codex/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  281. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  282. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  283. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  284. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  285. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  286. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  287. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  288. package/template/.codex/skills/yss-mybatis/SKILL.md +2 -2
  289. package/template/.codex/skills/yss-mybatis/references/source-index.md +1 -1
  290. package/template/.codex/skills/yss-page-module-development/SKILL.md +1 -1
  291. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +7 -7
  292. package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  293. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  294. package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  295. package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +1 -1
  296. package/template/.codex/skills/yss-prototype-stage/SKILL.md +34 -24
  297. package/template/.codex/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  298. package/template/.codex/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  299. package/template/.codex/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  300. package/template/.codex/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  301. package/template/.codex/skills/yss-repository/SKILL.md +16 -14
  302. package/template/.codex/skills/yss-repository/agents/openai.yaml +2 -2
  303. package/template/.codex/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  304. package/template/.codex/skills/yss-research/SKILL.md +73 -0
  305. package/template/.codex/skills/yss-research/agents/openai.yaml +3 -0
  306. package/template/.codex/skills/yss-research/assets/evidence-template.yaml +68 -0
  307. package/template/.codex/skills/yss-research/assets/research-brief-template.md +36 -0
  308. package/template/.codex/skills/yss-research/references/evidence-contract.md +42 -0
  309. package/template/.codex/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  310. package/template/.codex/skills/yss-research/tests/run-scenarios.mjs +81 -0
  311. package/template/.codex/skills/yss-resilience4j/SKILL.md +1 -1
  312. package/template/.codex/skills/yss-resilience4j/references/source-index.md +1 -1
  313. package/template/.codex/skills/yss-security-algorithm/SKILL.md +1 -1
  314. package/template/.codex/skills/yss-security-algorithm/references/source-index.md +1 -1
  315. package/template/.codex/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  316. package/template/.codex/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  317. package/template/.codex/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  318. package/template/.codex/skills/yss-stage-decision/SKILL.md +2 -1
  319. package/template/.codex/skills/yss-ui/SKILL.md +2 -1
  320. package/template/.codex/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  321. package/template/.codex/skills/yss-up-springboot3/SKILL.md +1 -1
  322. package/template/.codex/skills/yss-userinfo/SKILL.md +1 -1
  323. package/template/.codex/skills/yss-userinfo/references/source-index.md +1 -1
  324. package/template/.codex/skills/yss-validation/SKILL.md +1 -1
  325. package/template/.codex/skills/yss-validation/references/source-index.md +1 -1
  326. package/template/.codex/skills/yss-web-controller/SKILL.md +17 -11
  327. package/template/.codex/skills/yss-web-controller/agents/openai.yaml +2 -2
  328. package/template/.codex/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  329. package/template/.codex/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  330. package/template/.codex/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  331. package/template/.codex/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  332. package/template/.codex/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  333. package/template/.codex/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  334. package/template/.codex/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  335. package/template/.codex/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  336. package/template/.codex/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  337. package/template/.cursor/skills/ask-matt/SKILL.md +1 -1
  338. package/template/.cursor/skills/code-review/SKILL.md +1 -1
  339. package/template/.cursor/skills/code-review/references/yss-review-standards.md +1 -1
  340. package/template/.cursor/skills/lombok/SKILL.md +1 -1
  341. package/template/.cursor/skills/mapstruct/SKILL.md +1 -1
  342. package/template/.cursor/skills/prototype-review/SKILL.md +7 -6
  343. package/template/.cursor/skills/wayfinder/SKILL.md +2 -2
  344. package/template/.cursor/skills/yss-antd-design/SKILL.md +8 -7
  345. package/template/.cursor/skills/yss-antd-design/references/evidence.md +11 -6
  346. package/template/.cursor/skills/yss-api-integration/SKILL.md +2 -2
  347. package/template/.cursor/skills/yss-application/SKILL.md +2 -2
  348. package/template/.cursor/skills/yss-audit-log/SKILL.md +2 -2
  349. package/template/.cursor/skills/yss-audit-log/references/source-index.md +1 -1
  350. package/template/.cursor/skills/yss-cache/SKILL.md +2 -2
  351. package/template/.cursor/skills/yss-cache/references/source-index.md +1 -1
  352. package/template/.cursor/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  353. package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  354. package/template/.cursor/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  355. package/template/.cursor/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  356. package/template/.cursor/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  357. package/template/.cursor/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  358. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  359. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  360. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  361. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  362. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  363. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  364. package/template/.cursor/skills/yss-design-system/SKILL.md +6 -6
  365. package/template/.cursor/skills/yss-distributed-id/SKILL.md +2 -2
  366. package/template/.cursor/skills/yss-distributed-id/references/source-index.md +1 -1
  367. package/template/.cursor/skills/yss-domain/SKILL.md +3 -2
  368. package/template/.cursor/skills/yss-domain/agents/openai.yaml +2 -2
  369. package/template/.cursor/skills/yss-domain/references/domain-layer-guide.md +2 -0
  370. package/template/.cursor/skills/yss-dto/SKILL.md +8 -2
  371. package/template/.cursor/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  372. package/template/.cursor/skills/yss-dto/references/source-index.md +1 -1
  373. package/template/.cursor/skills/yss-excel-mvc/SKILL.md +2 -2
  374. package/template/.cursor/skills/yss-excel-mvc/references/source-index.md +1 -1
  375. package/template/.cursor/skills/yss-exception/SKILL.md +1 -1
  376. package/template/.cursor/skills/yss-exception/references/source-index.md +1 -1
  377. package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  378. package/template/.cursor/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  379. package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  380. package/template/.cursor/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  381. package/template/.cursor/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  382. package/template/.cursor/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  383. package/template/.cursor/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  384. package/template/.cursor/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  385. package/template/.cursor/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  386. package/template/.cursor/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  387. package/template/.cursor/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  388. package/template/.cursor/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  389. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  390. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  391. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  392. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  393. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  394. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  395. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  396. package/template/.cursor/skills/yss-mybatis/SKILL.md +2 -2
  397. package/template/.cursor/skills/yss-mybatis/references/source-index.md +1 -1
  398. package/template/.cursor/skills/yss-page-module-development/SKILL.md +1 -1
  399. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +7 -7
  400. package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  401. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  402. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  403. package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +1 -1
  404. package/template/.cursor/skills/yss-prototype-stage/SKILL.md +34 -24
  405. package/template/.cursor/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  406. package/template/.cursor/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  407. package/template/.cursor/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  408. package/template/.cursor/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  409. package/template/.cursor/skills/yss-repository/SKILL.md +16 -14
  410. package/template/.cursor/skills/yss-repository/agents/openai.yaml +2 -2
  411. package/template/.cursor/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  412. package/template/.cursor/skills/yss-research/SKILL.md +73 -0
  413. package/template/.cursor/skills/yss-research/agents/openai.yaml +3 -0
  414. package/template/.cursor/skills/yss-research/assets/evidence-template.yaml +68 -0
  415. package/template/.cursor/skills/yss-research/assets/research-brief-template.md +36 -0
  416. package/template/.cursor/skills/yss-research/references/evidence-contract.md +42 -0
  417. package/template/.cursor/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  418. package/template/.cursor/skills/yss-research/tests/run-scenarios.mjs +81 -0
  419. package/template/.cursor/skills/yss-resilience4j/SKILL.md +1 -1
  420. package/template/.cursor/skills/yss-resilience4j/references/source-index.md +1 -1
  421. package/template/.cursor/skills/yss-security-algorithm/SKILL.md +1 -1
  422. package/template/.cursor/skills/yss-security-algorithm/references/source-index.md +1 -1
  423. package/template/.cursor/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  424. package/template/.cursor/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  425. package/template/.cursor/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  426. package/template/.cursor/skills/yss-stage-decision/SKILL.md +2 -1
  427. package/template/.cursor/skills/yss-ui/SKILL.md +2 -1
  428. package/template/.cursor/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  429. package/template/.cursor/skills/yss-up-springboot3/SKILL.md +1 -1
  430. package/template/.cursor/skills/yss-userinfo/SKILL.md +1 -1
  431. package/template/.cursor/skills/yss-userinfo/references/source-index.md +1 -1
  432. package/template/.cursor/skills/yss-validation/SKILL.md +1 -1
  433. package/template/.cursor/skills/yss-validation/references/source-index.md +1 -1
  434. package/template/.cursor/skills/yss-web-controller/SKILL.md +17 -11
  435. package/template/.cursor/skills/yss-web-controller/agents/openai.yaml +2 -2
  436. package/template/.cursor/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  437. package/template/.cursor/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  438. package/template/.cursor/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  439. package/template/.cursor/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  440. package/template/.cursor/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  441. package/template/.cursor/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  442. package/template/.cursor/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  443. package/template/.cursor/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  444. package/template/.cursor/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  445. package/template/.pi/skills/ask-matt/SKILL.md +1 -1
  446. package/template/.pi/skills/code-review/SKILL.md +1 -1
  447. package/template/.pi/skills/code-review/references/yss-review-standards.md +1 -1
  448. package/template/.pi/skills/lombok/SKILL.md +1 -1
  449. package/template/.pi/skills/mapstruct/SKILL.md +1 -1
  450. package/template/.pi/skills/prototype-review/SKILL.md +7 -6
  451. package/template/.pi/skills/wayfinder/SKILL.md +2 -2
  452. package/template/.pi/skills/yss-antd-design/SKILL.md +8 -7
  453. package/template/.pi/skills/yss-antd-design/references/evidence.md +11 -6
  454. package/template/.pi/skills/yss-api-integration/SKILL.md +2 -2
  455. package/template/.pi/skills/yss-application/SKILL.md +2 -2
  456. package/template/.pi/skills/yss-audit-log/SKILL.md +2 -2
  457. package/template/.pi/skills/yss-audit-log/references/source-index.md +1 -1
  458. package/template/.pi/skills/yss-cache/SKILL.md +2 -2
  459. package/template/.pi/skills/yss-cache/references/source-index.md +1 -1
  460. package/template/.pi/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  461. package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  462. package/template/.pi/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  463. package/template/.pi/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  464. package/template/.pi/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  465. package/template/.pi/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  466. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  467. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  468. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  469. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  470. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  471. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  472. package/template/.pi/skills/yss-design-system/SKILL.md +6 -6
  473. package/template/.pi/skills/yss-distributed-id/SKILL.md +2 -2
  474. package/template/.pi/skills/yss-distributed-id/references/source-index.md +1 -1
  475. package/template/.pi/skills/yss-domain/SKILL.md +3 -2
  476. package/template/.pi/skills/yss-domain/agents/openai.yaml +2 -2
  477. package/template/.pi/skills/yss-domain/references/domain-layer-guide.md +2 -0
  478. package/template/.pi/skills/yss-dto/SKILL.md +8 -2
  479. package/template/.pi/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  480. package/template/.pi/skills/yss-dto/references/source-index.md +1 -1
  481. package/template/.pi/skills/yss-excel-mvc/SKILL.md +2 -2
  482. package/template/.pi/skills/yss-excel-mvc/references/source-index.md +1 -1
  483. package/template/.pi/skills/yss-exception/SKILL.md +1 -1
  484. package/template/.pi/skills/yss-exception/references/source-index.md +1 -1
  485. package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  486. package/template/.pi/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  487. package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  488. package/template/.pi/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  489. package/template/.pi/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  490. package/template/.pi/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  491. package/template/.pi/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  492. package/template/.pi/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  493. package/template/.pi/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  494. package/template/.pi/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  495. package/template/.pi/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  496. package/template/.pi/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  497. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  498. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  499. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  500. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  501. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  502. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  503. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  504. package/template/.pi/skills/yss-mybatis/SKILL.md +2 -2
  505. package/template/.pi/skills/yss-mybatis/references/source-index.md +1 -1
  506. package/template/.pi/skills/yss-page-module-development/SKILL.md +1 -1
  507. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +7 -7
  508. package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  509. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  510. package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  511. package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +1 -1
  512. package/template/.pi/skills/yss-prototype-stage/SKILL.md +34 -24
  513. package/template/.pi/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  514. package/template/.pi/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  515. package/template/.pi/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  516. package/template/.pi/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  517. package/template/.pi/skills/yss-repository/SKILL.md +16 -14
  518. package/template/.pi/skills/yss-repository/agents/openai.yaml +2 -2
  519. package/template/.pi/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  520. package/template/.pi/skills/yss-research/SKILL.md +73 -0
  521. package/template/.pi/skills/yss-research/agents/openai.yaml +3 -0
  522. package/template/.pi/skills/yss-research/assets/evidence-template.yaml +68 -0
  523. package/template/.pi/skills/yss-research/assets/research-brief-template.md +36 -0
  524. package/template/.pi/skills/yss-research/references/evidence-contract.md +42 -0
  525. package/template/.pi/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  526. package/template/.pi/skills/yss-research/tests/run-scenarios.mjs +81 -0
  527. package/template/.pi/skills/yss-resilience4j/SKILL.md +1 -1
  528. package/template/.pi/skills/yss-resilience4j/references/source-index.md +1 -1
  529. package/template/.pi/skills/yss-security-algorithm/SKILL.md +1 -1
  530. package/template/.pi/skills/yss-security-algorithm/references/source-index.md +1 -1
  531. package/template/.pi/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  532. package/template/.pi/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  533. package/template/.pi/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  534. package/template/.pi/skills/yss-stage-decision/SKILL.md +2 -1
  535. package/template/.pi/skills/yss-ui/SKILL.md +2 -1
  536. package/template/.pi/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  537. package/template/.pi/skills/yss-up-springboot3/SKILL.md +1 -1
  538. package/template/.pi/skills/yss-userinfo/SKILL.md +1 -1
  539. package/template/.pi/skills/yss-userinfo/references/source-index.md +1 -1
  540. package/template/.pi/skills/yss-validation/SKILL.md +1 -1
  541. package/template/.pi/skills/yss-validation/references/source-index.md +1 -1
  542. package/template/.pi/skills/yss-web-controller/SKILL.md +17 -11
  543. package/template/.pi/skills/yss-web-controller/agents/openai.yaml +2 -2
  544. package/template/.pi/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  545. package/template/.pi/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  546. package/template/.pi/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  547. package/template/.pi/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  548. package/template/.pi/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  549. package/template/.pi/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  550. package/template/.pi/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  551. package/template/.pi/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  552. package/template/.pi/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  553. package/template/.qoder/skills/ask-matt/SKILL.md +1 -1
  554. package/template/.qoder/skills/code-review/SKILL.md +1 -1
  555. package/template/.qoder/skills/code-review/references/yss-review-standards.md +1 -1
  556. package/template/.qoder/skills/lombok/SKILL.md +1 -1
  557. package/template/.qoder/skills/mapstruct/SKILL.md +1 -1
  558. package/template/.qoder/skills/prototype-review/SKILL.md +7 -6
  559. package/template/.qoder/skills/wayfinder/SKILL.md +2 -2
  560. package/template/.qoder/skills/yss-antd-design/SKILL.md +8 -7
  561. package/template/.qoder/skills/yss-antd-design/references/evidence.md +11 -6
  562. package/template/.qoder/skills/yss-api-integration/SKILL.md +2 -2
  563. package/template/.qoder/skills/yss-application/SKILL.md +2 -2
  564. package/template/.qoder/skills/yss-audit-log/SKILL.md +2 -2
  565. package/template/.qoder/skills/yss-audit-log/references/source-index.md +1 -1
  566. package/template/.qoder/skills/yss-cache/SKILL.md +2 -2
  567. package/template/.qoder/skills/yss-cache/references/source-index.md +1 -1
  568. package/template/.qoder/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  569. package/template/.qoder/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  570. package/template/.qoder/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  571. package/template/.qoder/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  572. package/template/.qoder/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  573. package/template/.qoder/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  574. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  575. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  576. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  577. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  578. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  579. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  580. package/template/.qoder/skills/yss-design-system/SKILL.md +6 -6
  581. package/template/.qoder/skills/yss-distributed-id/SKILL.md +2 -2
  582. package/template/.qoder/skills/yss-distributed-id/references/source-index.md +1 -1
  583. package/template/.qoder/skills/yss-domain/SKILL.md +3 -2
  584. package/template/.qoder/skills/yss-domain/agents/openai.yaml +2 -2
  585. package/template/.qoder/skills/yss-domain/references/domain-layer-guide.md +2 -0
  586. package/template/.qoder/skills/yss-dto/SKILL.md +8 -2
  587. package/template/.qoder/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  588. package/template/.qoder/skills/yss-dto/references/source-index.md +1 -1
  589. package/template/.qoder/skills/yss-excel-mvc/SKILL.md +2 -2
  590. package/template/.qoder/skills/yss-excel-mvc/references/source-index.md +1 -1
  591. package/template/.qoder/skills/yss-exception/SKILL.md +1 -1
  592. package/template/.qoder/skills/yss-exception/references/source-index.md +1 -1
  593. package/template/.qoder/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  594. package/template/.qoder/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  595. package/template/.qoder/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  596. package/template/.qoder/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  597. package/template/.qoder/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  598. package/template/.qoder/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  599. package/template/.qoder/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  600. package/template/.qoder/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  601. package/template/.qoder/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  602. package/template/.qoder/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  603. package/template/.qoder/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  604. package/template/.qoder/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  605. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  606. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  607. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  608. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  609. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  610. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  611. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  612. package/template/.qoder/skills/yss-mybatis/SKILL.md +2 -2
  613. package/template/.qoder/skills/yss-mybatis/references/source-index.md +1 -1
  614. package/template/.qoder/skills/yss-page-module-development/SKILL.md +1 -1
  615. package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +7 -7
  616. package/template/.qoder/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  617. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  618. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  619. package/template/.qoder/skills/yss-product-lifecycle/references/state-model.md +1 -1
  620. package/template/.qoder/skills/yss-prototype-stage/SKILL.md +34 -24
  621. package/template/.qoder/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  622. package/template/.qoder/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  623. package/template/.qoder/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  624. package/template/.qoder/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  625. package/template/.qoder/skills/yss-repository/SKILL.md +16 -14
  626. package/template/.qoder/skills/yss-repository/agents/openai.yaml +2 -2
  627. package/template/.qoder/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  628. package/template/.qoder/skills/yss-research/SKILL.md +73 -0
  629. package/template/.qoder/skills/yss-research/agents/openai.yaml +3 -0
  630. package/template/.qoder/skills/yss-research/assets/evidence-template.yaml +68 -0
  631. package/template/.qoder/skills/yss-research/assets/research-brief-template.md +36 -0
  632. package/template/.qoder/skills/yss-research/references/evidence-contract.md +42 -0
  633. package/template/.qoder/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  634. package/template/.qoder/skills/yss-research/tests/run-scenarios.mjs +81 -0
  635. package/template/.qoder/skills/yss-resilience4j/SKILL.md +1 -1
  636. package/template/.qoder/skills/yss-resilience4j/references/source-index.md +1 -1
  637. package/template/.qoder/skills/yss-security-algorithm/SKILL.md +1 -1
  638. package/template/.qoder/skills/yss-security-algorithm/references/source-index.md +1 -1
  639. package/template/.qoder/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  640. package/template/.qoder/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  641. package/template/.qoder/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  642. package/template/.qoder/skills/yss-stage-decision/SKILL.md +2 -1
  643. package/template/.qoder/skills/yss-ui/SKILL.md +2 -1
  644. package/template/.qoder/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  645. package/template/.qoder/skills/yss-up-springboot3/SKILL.md +1 -1
  646. package/template/.qoder/skills/yss-userinfo/SKILL.md +1 -1
  647. package/template/.qoder/skills/yss-userinfo/references/source-index.md +1 -1
  648. package/template/.qoder/skills/yss-validation/SKILL.md +1 -1
  649. package/template/.qoder/skills/yss-validation/references/source-index.md +1 -1
  650. package/template/.qoder/skills/yss-web-controller/SKILL.md +17 -11
  651. package/template/.qoder/skills/yss-web-controller/agents/openai.yaml +2 -2
  652. package/template/.qoder/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  653. package/template/.qoder/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  654. package/template/.qoder/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  655. package/template/.qoder/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  656. package/template/.qoder/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  657. package/template/.qoder/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  658. package/template/.qoder/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  659. package/template/.qoder/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  660. package/template/.qoder/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  661. package/template/.trae/skills/ask-matt/SKILL.md +1 -1
  662. package/template/.trae/skills/code-review/SKILL.md +1 -1
  663. package/template/.trae/skills/code-review/references/yss-review-standards.md +1 -1
  664. package/template/.trae/skills/lombok/SKILL.md +1 -1
  665. package/template/.trae/skills/mapstruct/SKILL.md +1 -1
  666. package/template/.trae/skills/prototype-review/SKILL.md +7 -6
  667. package/template/.trae/skills/wayfinder/SKILL.md +2 -2
  668. package/template/.trae/skills/yss-antd-design/SKILL.md +8 -7
  669. package/template/.trae/skills/yss-antd-design/references/evidence.md +11 -6
  670. package/template/.trae/skills/yss-api-integration/SKILL.md +2 -2
  671. package/template/.trae/skills/yss-application/SKILL.md +2 -2
  672. package/template/.trae/skills/yss-audit-log/SKILL.md +2 -2
  673. package/template/.trae/skills/yss-audit-log/references/source-index.md +1 -1
  674. package/template/.trae/skills/yss-cache/SKILL.md +2 -2
  675. package/template/.trae/skills/yss-cache/references/source-index.md +1 -1
  676. package/template/.trae/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  677. package/template/.trae/skills/yss-ddd-scaffold-generator/SKILL.md +18 -7
  678. package/template/.trae/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  679. package/template/.trae/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
  680. package/template/.trae/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +14 -3
  681. package/template/.trae/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -2
  682. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +48 -14
  683. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
  684. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +24 -10
  685. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
  686. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  687. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +21 -8
  688. package/template/.trae/skills/yss-design-system/SKILL.md +6 -6
  689. package/template/.trae/skills/yss-distributed-id/SKILL.md +2 -2
  690. package/template/.trae/skills/yss-distributed-id/references/source-index.md +1 -1
  691. package/template/.trae/skills/yss-domain/SKILL.md +3 -2
  692. package/template/.trae/skills/yss-domain/agents/openai.yaml +2 -2
  693. package/template/.trae/skills/yss-domain/references/domain-layer-guide.md +2 -0
  694. package/template/.trae/skills/yss-dto/SKILL.md +8 -2
  695. package/template/.trae/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
  696. package/template/.trae/skills/yss-dto/references/source-index.md +1 -1
  697. package/template/.trae/skills/yss-excel-mvc/SKILL.md +2 -2
  698. package/template/.trae/skills/yss-excel-mvc/references/source-index.md +1 -1
  699. package/template/.trae/skills/yss-exception/SKILL.md +1 -1
  700. package/template/.trae/skills/yss-exception/references/source-index.md +1 -1
  701. package/template/.trae/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  702. package/template/.trae/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  703. package/template/.trae/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  704. package/template/.trae/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  705. package/template/.trae/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  706. package/template/.trae/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  707. package/template/.trae/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  708. package/template/.trae/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  709. package/template/.trae/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  710. package/template/.trae/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  711. package/template/.trae/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  712. package/template/.trae/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  713. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  714. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  715. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  716. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  717. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  718. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  719. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  720. package/template/.trae/skills/yss-mybatis/SKILL.md +2 -2
  721. package/template/.trae/skills/yss-mybatis/references/source-index.md +1 -1
  722. package/template/.trae/skills/yss-page-module-development/SKILL.md +1 -1
  723. package/template/.trae/skills/yss-product-lifecycle/SKILL.md +7 -7
  724. package/template/.trae/skills/yss-product-lifecycle/references/matt-yss-adapter.md +7 -7
  725. package/template/.trae/skills/yss-product-lifecycle/references/orchestration-contract.yaml +37 -27
  726. package/template/.trae/skills/yss-product-lifecycle/references/orchestration.md +7 -7
  727. package/template/.trae/skills/yss-product-lifecycle/references/state-model.md +1 -1
  728. package/template/.trae/skills/yss-prototype-stage/SKILL.md +34 -24
  729. package/template/.trae/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  730. package/template/.trae/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  731. package/template/.trae/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  732. package/template/.trae/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  733. package/template/.trae/skills/yss-repository/SKILL.md +16 -14
  734. package/template/.trae/skills/yss-repository/agents/openai.yaml +2 -2
  735. package/template/.trae/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
  736. package/template/.trae/skills/yss-research/SKILL.md +73 -0
  737. package/template/.trae/skills/yss-research/agents/openai.yaml +3 -0
  738. package/template/.trae/skills/yss-research/assets/evidence-template.yaml +68 -0
  739. package/template/.trae/skills/yss-research/assets/research-brief-template.md +36 -0
  740. package/template/.trae/skills/yss-research/references/evidence-contract.md +42 -0
  741. package/template/.trae/skills/yss-research/scripts/validate-research-package.mjs +128 -0
  742. package/template/.trae/skills/yss-research/tests/run-scenarios.mjs +81 -0
  743. package/template/.trae/skills/yss-resilience4j/SKILL.md +1 -1
  744. package/template/.trae/skills/yss-resilience4j/references/source-index.md +1 -1
  745. package/template/.trae/skills/yss-security-algorithm/SKILL.md +1 -1
  746. package/template/.trae/skills/yss-security-algorithm/references/source-index.md +1 -1
  747. package/template/.trae/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  748. package/template/.trae/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  749. package/template/.trae/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  750. package/template/.trae/skills/yss-stage-decision/SKILL.md +2 -1
  751. package/template/.trae/skills/yss-ui/SKILL.md +2 -1
  752. package/template/.trae/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  753. package/template/.trae/skills/yss-up-springboot3/SKILL.md +1 -1
  754. package/template/.trae/skills/yss-userinfo/SKILL.md +1 -1
  755. package/template/.trae/skills/yss-userinfo/references/source-index.md +1 -1
  756. package/template/.trae/skills/yss-validation/SKILL.md +1 -1
  757. package/template/.trae/skills/yss-validation/references/source-index.md +1 -1
  758. package/template/.trae/skills/yss-web-controller/SKILL.md +17 -11
  759. package/template/.trae/skills/yss-web-controller/agents/openai.yaml +2 -2
  760. package/template/.trae/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
  761. package/template/.trae/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
  762. package/template/.trae/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
  763. package/template/.trae/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
  764. package/template/.trae/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
  765. package/template/.trae/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
  766. package/template/.trae/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
  767. package/template/.trae/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
  768. package/template/.trae/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
  769. package/template/AGENTS.md +13 -13
  770. package/template/CONTEXT.md +9 -8
  771. package/template/README.md +0 -1
  772. package/template/docs/agents/digital-human-roles.md +3 -3
  773. package/template/docs/agents/digital-human-roles.yaml +5 -5
  774. package/template/docs/agents/skill-migrations.md +18 -5
  775. package/template/docs/agents/skills-maintenance.md +3 -3
  776. package/template/docs/agents/strategic-design-skills-integration.md +41 -0
  777. package/template/docs/agents/yss-skill-registry.yaml +137 -16
  778. package/template/docs/api/templates/openapi-draft-review-checklist.md +3 -3
  779. package/template/docs/architecture/templates/engineering-baseline-review-template.md +3 -3
  780. package/template/docs/design/README.md +7 -7
  781. package/template/docs/design/design.md +25 -17
  782. package/template/docs/design/templates/interaction-spec-template.md +4 -4
  783. package/template/docs/design/templates/prototype-confirmation-template.md +13 -15
  784. package/template/docs/design/templates/prototype-evidence-template.yaml +99 -75
  785. package/template/docs/design/templates/prototype-review-checklist.md +5 -4
  786. package/template/docs/design/templates/state-matrix-template.md +11 -0
  787. package/template/docs/discovery/reports/agent-governance-options.md +8 -8
  788. package/template/docs/process/MATT-POCOCK-ENGINEERING-SKILLS.md +2 -1
  789. package/template/docs/process/implementation-repo-integration.md +1 -1
  790. package/template/docs/process/lifecycle-artifact-map.md +4 -2
  791. package/template/docs/process/lifecycle-registry-baseline.json +4 -1
  792. package/template/docs/process/lifecycle-registry.yaml +15 -9
  793. package/template/docs/process/template-engineering-overview.md +2 -2
  794. package/template/docs/process/template-verification-profiles.yaml +27 -6
  795. package/template/docs/templates/build-architecture-checklist-template.md +6 -6
  796. package/template/docs/templates/implementation-routing-template.md +8 -8
  797. package/template/docs/templates/requirement-freeze-template.md +2 -2
  798. package/template/docs/templates/review-report-template.md +4 -4
  799. package/template/docs/templates/spec-template.md +1 -1
  800. package/template/docs/templates/vertical-slice-ticket-template.md +4 -4
  801. package/template/docs/user-guide/yss-ui-mcp.md +0 -2
  802. package/template/docs/user-guide//346/210/230/346/234/257/350/256/276/350/256/241/345/255/220/351/241/271/347/233/256/347/224/250/346/210/267/346/211/213/345/206/214.md +2 -2
  803. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +5 -5
  804. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214/347/264/242/345/274/225.md +4 -4
  805. package/template/scripts/audit-yss-backend-skills +56 -0
  806. package/template/scripts/generate-yss-skill-boundaries +67 -0
  807. package/template/scripts/lib/export-skills.mjs +4 -4
  808. package/template/scripts/lib/implementation-contract-compiler.mjs +194 -0
  809. package/template/scripts/lib/scenario-checks.mjs +17 -10
  810. package/template/scripts/lib/skill-governance.mjs +27 -5
  811. package/template/scripts/lib/skill-registry.mjs +129 -41
  812. package/template/scripts/lib/skill-supply-chain.mjs +47 -6
  813. package/template/scripts/verify-repository-scope-scenarios +2 -2
  814. package/template/scripts/verify-retired-skill-ids +37 -0
  815. package/template/scripts/verify-scaffold-generator-scenarios +18 -18
  816. package/template/scripts/verify-template-verification-scenarios +4 -1
  817. package/template/scripts/verify-yss-dto-openapi-profile +8 -0
  818. package/template/scripts/verify-yss-implementation-contract-compiler-scenarios +14 -0
  819. package/template/skills-lock.json +101 -171
  820. package/template/yss-public-skills.json +4 -4
  821. package/template.manifest.json +0 -3
  822. package/template.snapshot.json +5 -5
  823. package/template/.agents/skills/research/SKILL.md +0 -12
  824. package/template/.agents/skills/research/agents/openai.yaml +0 -3
  825. package/template/.agents/skills/yss-router/SKILL.md +0 -51
  826. package/template/.agents/skills/yss-router/agents/openai.yaml +0 -4
  827. package/template/.agents/skills/yss-router/references/boundaries.md +0 -50
  828. package/template/.agents/skills/yss-router/references/router-contract.yaml +0 -345
  829. package/template/.agents/skills/yss-router/references/slice-implementation-contract.md +0 -185
  830. package/template/.agents/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  831. package/template/.agents/skills/yss-source-index/SKILL.md +0 -63
  832. package/template/.agents/skills/yss-source-index/agents/openai.yaml +0 -4
  833. package/template/.agents/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  834. package/template/.claude/skills/research/SKILL.md +0 -12
  835. package/template/.claude/skills/research/agents/openai.yaml +0 -3
  836. package/template/.claude/skills/yss-router/SKILL.md +0 -51
  837. package/template/.claude/skills/yss-router/agents/openai.yaml +0 -4
  838. package/template/.claude/skills/yss-router/references/boundaries.md +0 -50
  839. package/template/.claude/skills/yss-router/references/router-contract.yaml +0 -345
  840. package/template/.claude/skills/yss-router/references/slice-implementation-contract.md +0 -185
  841. package/template/.claude/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  842. package/template/.claude/skills/yss-source-index/SKILL.md +0 -63
  843. package/template/.claude/skills/yss-source-index/agents/openai.yaml +0 -4
  844. package/template/.claude/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  845. package/template/.codex/skills/research/SKILL.md +0 -12
  846. package/template/.codex/skills/research/agents/openai.yaml +0 -3
  847. package/template/.codex/skills/yss-router/SKILL.md +0 -51
  848. package/template/.codex/skills/yss-router/agents/openai.yaml +0 -4
  849. package/template/.codex/skills/yss-router/references/boundaries.md +0 -50
  850. package/template/.codex/skills/yss-router/references/router-contract.yaml +0 -345
  851. package/template/.codex/skills/yss-router/references/slice-implementation-contract.md +0 -185
  852. package/template/.codex/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  853. package/template/.codex/skills/yss-source-index/SKILL.md +0 -63
  854. package/template/.codex/skills/yss-source-index/agents/openai.yaml +0 -4
  855. package/template/.codex/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  856. package/template/.cursor/skills/research/SKILL.md +0 -12
  857. package/template/.cursor/skills/research/agents/openai.yaml +0 -3
  858. package/template/.cursor/skills/yss-router/SKILL.md +0 -51
  859. package/template/.cursor/skills/yss-router/agents/openai.yaml +0 -4
  860. package/template/.cursor/skills/yss-router/references/boundaries.md +0 -50
  861. package/template/.cursor/skills/yss-router/references/router-contract.yaml +0 -345
  862. package/template/.cursor/skills/yss-router/references/slice-implementation-contract.md +0 -185
  863. package/template/.cursor/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  864. package/template/.cursor/skills/yss-source-index/SKILL.md +0 -63
  865. package/template/.cursor/skills/yss-source-index/agents/openai.yaml +0 -4
  866. package/template/.cursor/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  867. package/template/.gemini/settings.json +0 -8
  868. package/template/.hermes/skills/alibaba-java-code-style/SKILL.md +0 -51
  869. package/template/.hermes/skills/alibaba-java-code-style/agents/openai.yaml +0 -4
  870. package/template/.hermes/skills/alibaba-java-code-style/references/database-engineering-design.md +0 -147
  871. package/template/.hermes/skills/alibaba-java-code-style/references/java-programming.md +0 -178
  872. package/template/.hermes/skills/alibaba-java-code-style/references/reliability-test-security.md +0 -89
  873. package/template/.hermes/skills/archify/LICENSE +0 -22
  874. package/template/.hermes/skills/archify/SKILL.md +0 -156
  875. package/template/.hermes/skills/archify/assets/template.html +0 -14787
  876. package/template/.hermes/skills/archify/bin/archify.mjs +0 -1990
  877. package/template/.hermes/skills/archify/bin/open-artifact.mjs +0 -86
  878. package/template/.hermes/skills/archify/bin/preview.mjs +0 -648
  879. package/template/.hermes/skills/archify/bin/visual-check.mjs +0 -829
  880. package/template/.hermes/skills/archify/brand-marks/README.md +0 -31
  881. package/template/.hermes/skills/archify/brand-marks/catalog.json +0 -131
  882. package/template/.hermes/skills/archify/delta/architecture-delta.mjs +0 -1214
  883. package/template/.hermes/skills/archify/examples/agent-run.lifecycle.json +0 -71
  884. package/template/.hermes/skills/archify/examples/agent-tool-call.workflow.json +0 -94
  885. package/template/.hermes/skills/archify/examples/async-job-roundtrip.sequence.json +0 -61
  886. package/template/.hermes/skills/archify/examples/brand-aware-delivery.architecture.json +0 -47
  887. package/template/.hermes/skills/archify/examples/cache-miss-request.sequence.json +0 -81
  888. package/template/.hermes/skills/archify/examples/checkout-platform.base.architecture.json +0 -31
  889. package/template/.hermes/skills/archify/examples/checkout-platform.head.architecture.json +0 -31
  890. package/template/.hermes/skills/archify/examples/dataflow-product-analytics.html +0 -14898
  891. package/template/.hermes/skills/archify/examples/deployment-release.lifecycle.json +0 -49
  892. package/template/.hermes/skills/archify/examples/event-stream.dataflow.json +0 -57
  893. package/template/.hermes/skills/archify/examples/incident-response.workflow.json +0 -64
  894. package/template/.hermes/skills/archify/examples/lifecycle-agent-run.html +0 -14847
  895. package/template/.hermes/skills/archify/examples/product-analytics.dataflow.json +0 -76
  896. package/template/.hermes/skills/archify/examples/production-deployment.architecture.json +0 -71
  897. package/template/.hermes/skills/archify/examples/release-delivery.workflow.json +0 -62
  898. package/template/.hermes/skills/archify/examples/sequence-cache-miss-request.html +0 -14913
  899. package/template/.hermes/skills/archify/examples/web-app-rendered.html +0 -14862
  900. package/template/.hermes/skills/archify/examples/web-app.architecture.json +0 -46
  901. package/template/.hermes/skills/archify/examples/workflow-agent-tool-call-rendered.html +0 -14904
  902. package/template/.hermes/skills/archify/migrations/workflow-v2.mjs +0 -279
  903. package/template/.hermes/skills/archify/package-lock.json +0 -149
  904. package/template/.hermes/skills/archify/package.json +0 -37
  905. package/template/.hermes/skills/archify/recipes/scenarios.mjs +0 -391
  906. package/template/.hermes/skills/archify/references/authoring-contract.md +0 -191
  907. package/template/.hermes/skills/archify/references/brand-marks.md +0 -65
  908. package/template/.hermes/skills/archify/references/delivery-contract.md +0 -112
  909. package/template/.hermes/skills/archify/references/viewer-runtime.md +0 -45
  910. package/template/.hermes/skills/archify/renderers/architecture/grid.mjs +0 -62
  911. package/template/.hermes/skills/archify/renderers/architecture/render-architecture.mjs +0 -1078
  912. package/template/.hermes/skills/archify/renderers/dataflow/README.md +0 -104
  913. package/template/.hermes/skills/archify/renderers/dataflow/render-dataflow.mjs +0 -483
  914. package/template/.hermes/skills/archify/renderers/lifecycle/README.md +0 -115
  915. package/template/.hermes/skills/archify/renderers/lifecycle/render-lifecycle.mjs +0 -561
  916. package/template/.hermes/skills/archify/renderers/sequence/README.md +0 -114
  917. package/template/.hermes/skills/archify/renderers/sequence/render-sequence.mjs +0 -453
  918. package/template/.hermes/skills/archify/renderers/shared/brand-marks.mjs +0 -563
  919. package/template/.hermes/skills/archify/renderers/shared/cli.mjs +0 -218
  920. package/template/.hermes/skills/archify/renderers/shared/desktop-readability.mjs +0 -26
  921. package/template/.hermes/skills/archify/renderers/shared/diagnostics.mjs +0 -127
  922. package/template/.hermes/skills/archify/renderers/shared/engineering-profiles.mjs +0 -157
  923. package/template/.hermes/skills/archify/renderers/shared/generated-brand-marks.mjs +0 -2003
  924. package/template/.hermes/skills/archify/renderers/shared/generated-validators.mjs +0 -13
  925. package/template/.hermes/skills/archify/renderers/shared/geometry.mjs +0 -1423
  926. package/template/.hermes/skills/archify/renderers/shared/i18n.mjs +0 -594
  927. package/template/.hermes/skills/archify/renderers/shared/layout-report.mjs +0 -40
  928. package/template/.hermes/skills/archify/renderers/shared/legend.mjs +0 -217
  929. package/template/.hermes/skills/archify/renderers/shared/output-path.mjs +0 -321
  930. package/template/.hermes/skills/archify/renderers/shared/repository-evidence.mjs +0 -235
  931. package/template/.hermes/skills/archify/renderers/shared/text-fit.mjs +0 -49
  932. package/template/.hermes/skills/archify/renderers/shared/utils.mjs +0 -232
  933. package/template/.hermes/skills/archify/renderers/shared/validator.mjs +0 -86
  934. package/template/.hermes/skills/archify/renderers/workflow/README.md +0 -223
  935. package/template/.hermes/skills/archify/renderers/workflow/render-workflow.mjs +0 -35
  936. package/template/.hermes/skills/archify/renderers/workflow/workflow-compiler.mjs +0 -4400
  937. package/template/.hermes/skills/archify/renderers/workflow/workflow-migration-geometry.mjs +0 -144
  938. package/template/.hermes/skills/archify/schemas/README.md +0 -211
  939. package/template/.hermes/skills/archify/schemas/architecture.schema.json +0 -176
  940. package/template/.hermes/skills/archify/schemas/common.schema.json +0 -115
  941. package/template/.hermes/skills/archify/schemas/dataflow.schema.json +0 -243
  942. package/template/.hermes/skills/archify/schemas/lifecycle.schema.json +0 -266
  943. package/template/.hermes/skills/archify/schemas/sequence.schema.json +0 -223
  944. package/template/.hermes/skills/archify/schemas/workflow.schema.json +0 -428
  945. package/template/.hermes/skills/archify/scripts/check-render-output.mjs +0 -835
  946. package/template/.hermes/skills/archify/scripts/check-update.mjs +0 -1667
  947. package/template/.hermes/skills/archify/scripts/generate-brand-marks.mjs +0 -141
  948. package/template/.hermes/skills/archify/scripts/generate-validators.mjs +0 -66
  949. package/template/.hermes/skills/archify/scripts/render-examples.mjs +0 -26
  950. package/template/.hermes/skills/archify/scripts/update-contract.mjs +0 -182
  951. package/template/.hermes/skills/archify/scripts/yss-safe-deliver.mjs +0 -214
  952. package/template/.hermes/skills/archify/skill-release.json +0 -10
  953. package/template/.hermes/skills/archify/test/adaptive-reader-layout.test.mjs +0 -86
  954. package/template/.hermes/skills/archify/test/animation.test.mjs +0 -179
  955. package/template/.hermes/skills/archify/test/architecture-delta.test.mjs +0 -518
  956. package/template/.hermes/skills/archify/test/authored-reachability.test.mjs +0 -95
  957. package/template/.hermes/skills/archify/test/authoring-safety-contract.test.mjs +0 -54
  958. package/template/.hermes/skills/archify/test/automatic-port-spread.test.mjs +0 -376
  959. package/template/.hermes/skills/archify/test/base-input-compatibility.test.mjs +0 -42
  960. package/template/.hermes/skills/archify/test/brand-marks.test.mjs +0 -654
  961. package/template/.hermes/skills/archify/test/chapter-delta-preview.test.mjs +0 -157
  962. package/template/.hermes/skills/archify/test/chapter-handoff.test.mjs +0 -97
  963. package/template/.hermes/skills/archify/test/chapter-rail.test.mjs +0 -86
  964. package/template/.hermes/skills/archify/test/checkout-line-endings.test.mjs +0 -72
  965. package/template/.hermes/skills/archify/test/clean-skill-staging.test.mjs +0 -244
  966. package/template/.hermes/skills/archify/test/cli.test.mjs +0 -809
  967. package/template/.hermes/skills/archify/test/community-proof-intake.test.mjs +0 -104
  968. package/template/.hermes/skills/archify/test/cursor-onboarding.test.mjs +0 -74
  969. package/template/.hermes/skills/archify/test/degraded.test.mjs +0 -161
  970. package/template/.hermes/skills/archify/test/delivery-contract.test.mjs +0 -52
  971. package/template/.hermes/skills/archify/test/desktop-reader-browser.test.mjs +0 -58
  972. package/template/.hermes/skills/archify/test/diagram-guide.test.mjs +0 -92
  973. package/template/.hermes/skills/archify/test/engineering-profile.test.mjs +0 -209
  974. package/template/.hermes/skills/archify/test/finder.test.mjs +0 -104
  975. package/template/.hermes/skills/archify/test/fixtures/automatic-routing-node-border-clearance.workflow.json +0 -40
  976. package/template/.hermes/skills/archify/test/fixtures/fail-migration-cleanup.mjs +0 -17
  977. package/template/.hermes/skills/archify/test/fixtures/issue-126/custom-widths.workflow.json +0 -18
  978. package/template/.hermes/skills/archify/test/fixtures/issue-126/explicit-route.workflow.json +0 -28
  979. package/template/.hermes/skills/archify/test/fixtures/issue-126/explicit-viewbox.workflow.json +0 -19
  980. package/template/.hermes/skills/archify/test/fixtures/issue-126/labels.workflow.json +0 -18
  981. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/agent-run.lifecycle.json +0 -64
  982. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/agent-tool-call.workflow.json +0 -88
  983. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/cache-miss-request.sequence.json +0 -75
  984. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/event-stream.dataflow.json +0 -57
  985. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/product-analytics.dataflow.json +0 -70
  986. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/production-deployment.architecture.json +0 -71
  987. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/web-app.architecture.json +0 -41
  988. package/template/.hermes/skills/archify/test/fixtures/v1-workflow-700x400.workflow.json +0 -22
  989. package/template/.hermes/skills/archify/test/fixtures/v1-workflow-explicit-coordinates.workflow.json +0 -47
  990. package/template/.hermes/skills/archify/test/gallery.test.mjs +0 -130
  991. package/template/.hermes/skills/archify/test/generate-validators.test.mjs +0 -59
  992. package/template/.hermes/skills/archify/test/generated-artifact-xml.test.mjs +0 -88
  993. package/template/.hermes/skills/archify/test/geometry.test.mjs +0 -739
  994. package/template/.hermes/skills/archify/test/golden.mjs +0 -207
  995. package/template/.hermes/skills/archify/test/grid.test.mjs +0 -47
  996. package/template/.hermes/skills/archify/test/guide-page.test.mjs +0 -59
  997. package/template/.hermes/skills/archify/test/guide.test.mjs +0 -81
  998. package/template/.hermes/skills/archify/test/guided-views.test.mjs +0 -144
  999. package/template/.hermes/skills/archify/test/helpers/xml.mjs +0 -37
  1000. package/template/.hermes/skills/archify/test/i18n.test.mjs +0 -422
  1001. package/template/.hermes/skills/archify/test/intent-trace.test.mjs +0 -111
  1002. package/template/.hermes/skills/archify/test/landing.test.mjs +0 -97
  1003. package/template/.hermes/skills/archify/test/layout-rules.test.mjs +0 -1449
  1004. package/template/.hermes/skills/archify/test/legend-contract.test.mjs +0 -404
  1005. package/template/.hermes/skills/archify/test/motion-governor.test.mjs +0 -111
  1006. package/template/.hermes/skills/archify/test/open-artifact.test.mjs +0 -118
  1007. package/template/.hermes/skills/archify/test/ordinary-model-floor.test.mjs +0 -1024
  1008. package/template/.hermes/skills/archify/test/output-path.test.mjs +0 -587
  1009. package/template/.hermes/skills/archify/test/presentation.test.mjs +0 -71
  1010. package/template/.hermes/skills/archify/test/preset-tryon.test.mjs +0 -113
  1011. package/template/.hermes/skills/archify/test/preview-contract.test.mjs +0 -40
  1012. package/template/.hermes/skills/archify/test/preview.test.mjs +0 -499
  1013. package/template/.hermes/skills/archify/test/proof-aperture.test.mjs +0 -78
  1014. package/template/.hermes/skills/archify/test/reach-share-card.test.mjs +0 -149
  1015. package/template/.hermes/skills/archify/test/readme-showcase.test.mjs +0 -318
  1016. package/template/.hermes/skills/archify/test/real-repository-proof.test.mjs +0 -209
  1017. package/template/.hermes/skills/archify/test/relationship-direct-explorer.test.mjs +0 -115
  1018. package/template/.hermes/skills/archify/test/relationship-lens.test.mjs +0 -128
  1019. package/template/.hermes/skills/archify/test/relationship-permalink.test.mjs +0 -130
  1020. package/template/.hermes/skills/archify/test/relationship-pulse.test.mjs +0 -122
  1021. package/template/.hermes/skills/archify/test/release-identity.test.mjs +0 -504
  1022. package/template/.hermes/skills/archify/test/release-package-gates.test.mjs +0 -508
  1023. package/template/.hermes/skills/archify/test/render-output-checks.test.mjs +0 -435
  1024. package/template/.hermes/skills/archify/test/repair-receipt.test.mjs +0 -181
  1025. package/template/.hermes/skills/archify/test/repository-evidence.test.mjs +0 -217
  1026. package/template/.hermes/skills/archify/test/repository-language-metadata.test.mjs +0 -52
  1027. package/template/.hermes/skills/archify/test/route-journey.test.mjs +0 -123
  1028. package/template/.hermes/skills/archify/test/route-probe.test.mjs +0 -114
  1029. package/template/.hermes/skills/archify/test/route-share-card.test.mjs +0 -163
  1030. package/template/.hermes/skills/archify/test/semantic-camera.test.mjs +0 -88
  1031. package/template/.hermes/skills/archify/test/semantic-flow.test.mjs +0 -90
  1032. package/template/.hermes/skills/archify/test/semantic-legend-gateway.test.mjs +0 -156
  1033. package/template/.hermes/skills/archify/test/semantic-lens.test.mjs +0 -103
  1034. package/template/.hermes/skills/archify/test/semantic-passport.test.mjs +0 -99
  1035. package/template/.hermes/skills/archify/test/semantic-radar.test.mjs +0 -575
  1036. package/template/.hermes/skills/archify/test/semantic-zoom.test.mjs +0 -86
  1037. package/template/.hermes/skills/archify/test/sequence-column-fit.test.mjs +0 -137
  1038. package/template/.hermes/skills/archify/test/settled-flow.test.mjs +0 -97
  1039. package/template/.hermes/skills/archify/test/share-card-export.test.mjs +0 -132
  1040. package/template/.hermes/skills/archify/test/site-language-continuity.test.mjs +0 -562
  1041. package/template/.hermes/skills/archify/test/site-language-integration.mjs +0 -9
  1042. package/template/.hermes/skills/archify/test/skill-metadata.test.mjs +0 -84
  1043. package/template/.hermes/skills/archify/test/stable-update-manifest.test.mjs +0 -167
  1044. package/template/.hermes/skills/archify/test/start-page.test.mjs +0 -312
  1045. package/template/.hermes/skills/archify/test/story-beat-navigator.test.mjs +0 -103
  1046. package/template/.hermes/skills/archify/test/story-carrier.test.mjs +0 -90
  1047. package/template/.hermes/skills/archify/test/story-director-strip.test.mjs +0 -91
  1048. package/template/.hermes/skills/archify/test/story-follow-camera.test.mjs +0 -103
  1049. package/template/.hermes/skills/archify/test/story-horizon.test.mjs +0 -89
  1050. package/template/.hermes/skills/archify/test/story-moment-link.test.mjs +0 -93
  1051. package/template/.hermes/skills/archify/test/story-shelf.test.mjs +0 -84
  1052. package/template/.hermes/skills/archify/test/story-trail.test.mjs +0 -81
  1053. package/template/.hermes/skills/archify/test/toolbar-polish.test.mjs +0 -57
  1054. package/template/.hermes/skills/archify/test/update-contract.test.mjs +0 -133
  1055. package/template/.hermes/skills/archify/test/update-notifier.test.mjs +0 -3317
  1056. package/template/.hermes/skills/archify/test/v1-compatibility.test.mjs +0 -329
  1057. package/template/.hermes/skills/archify/test/vertical-edge.test.mjs +0 -68
  1058. package/template/.hermes/skills/archify/test/viewer-chrome-layout.test.mjs +0 -872
  1059. package/template/.hermes/skills/archify/test/visual-check.test.mjs +0 -400
  1060. package/template/.hermes/skills/archify/test/webm-artifact.smoke.mjs +0 -1840
  1061. package/template/.hermes/skills/archify/test/workflow-compiler-hard-contract.test.mjs +0 -2135
  1062. package/template/.hermes/skills/archify/test/workflow-compiler.test.mjs +0 -1163
  1063. package/template/.hermes/skills/archify/test/workflow-migration.test.mjs +0 -620
  1064. package/template/.hermes/skills/archify/test/workflow-semantic-contract.test.mjs +0 -131
  1065. package/template/.hermes/skills/archify/test/yss-safe-deliver.test.mjs +0 -153
  1066. package/template/.hermes/skills/ask-matt/PHASE-BOUNDARIES.md +0 -55
  1067. package/template/.hermes/skills/ask-matt/SKILL.md +0 -95
  1068. package/template/.hermes/skills/ask-matt/agents/openai.yaml +0 -5
  1069. package/template/.hermes/skills/code-review/SKILL.md +0 -122
  1070. package/template/.hermes/skills/code-review/agents/openai.yaml +0 -3
  1071. package/template/.hermes/skills/code-review/references/yss-review-standards.md +0 -54
  1072. package/template/.hermes/skills/codebase-design/DEEPENING.md +0 -37
  1073. package/template/.hermes/skills/codebase-design/DESIGN-IT-TWICE.md +0 -44
  1074. package/template/.hermes/skills/codebase-design/SKILL.md +0 -118
  1075. package/template/.hermes/skills/codebase-design/agents/openai.yaml +0 -3
  1076. package/template/.hermes/skills/competitive-intelligence/SKILL.md +0 -94
  1077. package/template/.hermes/skills/component-selection-imports/SKILL.md +0 -64
  1078. package/template/.hermes/skills/cross-repo-implementation-routing/SKILL.md +0 -51
  1079. package/template/.hermes/skills/diagnosing-bugs/SKILL.md +0 -138
  1080. package/template/.hermes/skills/diagnosing-bugs/agents/openai.yaml +0 -3
  1081. package/template/.hermes/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +0 -44
  1082. package/template/.hermes/skills/dispatching-parallel-agents/SKILL.md +0 -185
  1083. package/template/.hermes/skills/domain-modeling/ADR-FORMAT.md +0 -47
  1084. package/template/.hermes/skills/domain-modeling/CONTEXT-FORMAT.md +0 -60
  1085. package/template/.hermes/skills/domain-modeling/SKILL.md +0 -74
  1086. package/template/.hermes/skills/domain-modeling/agents/openai.yaml +0 -3
  1087. package/template/.hermes/skills/file-export-download/SKILL.md +0 -160
  1088. package/template/.hermes/skills/file-export-download/agents/openai.yaml +0 -4
  1089. package/template/.hermes/skills/formily-foundation/SKILL.md +0 -176
  1090. package/template/.hermes/skills/formily-foundation/references/examples.md +0 -486
  1091. package/template/.hermes/skills/formily-linkage-effects/SKILL.md +0 -167
  1092. package/template/.hermes/skills/formily-mode-slot-detail/SKILL.md +0 -137
  1093. package/template/.hermes/skills/formily-step-flow/SKILL.md +0 -194
  1094. package/template/.hermes/skills/frontend-commit/SKILL.md +0 -118
  1095. package/template/.hermes/skills/frontend-commit/agents/openai.yaml +0 -7
  1096. package/template/.hermes/skills/frontend-commit/references/scope-and-examples.md +0 -111
  1097. package/template/.hermes/skills/grill-me/SKILL.md +0 -7
  1098. package/template/.hermes/skills/grill-me/agents/openai.yaml +0 -5
  1099. package/template/.hermes/skills/grill-with-docs/SKILL.md +0 -19
  1100. package/template/.hermes/skills/grill-with-docs/agents/openai.yaml +0 -5
  1101. package/template/.hermes/skills/grilling/SKILL.md +0 -28
  1102. package/template/.hermes/skills/grilling/agents/openai.yaml +0 -3
  1103. package/template/.hermes/skills/handoff/SKILL.md +0 -16
  1104. package/template/.hermes/skills/handoff/agents/openai.yaml +0 -5
  1105. package/template/.hermes/skills/implement/SKILL.md +0 -15
  1106. package/template/.hermes/skills/implement/agents/openai.yaml +0 -5
  1107. package/template/.hermes/skills/implementation-repo-onboarding/SKILL.md +0 -50
  1108. package/template/.hermes/skills/improve-codebase-architecture/HTML-REPORT.md +0 -123
  1109. package/template/.hermes/skills/improve-codebase-architecture/SKILL.md +0 -71
  1110. package/template/.hermes/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
  1111. package/template/.hermes/skills/java-backend-commit/SKILL.md +0 -121
  1112. package/template/.hermes/skills/java-backend-commit/agents/openai.yaml +0 -7
  1113. package/template/.hermes/skills/java-backend-commit/references/scope-and-examples.md +0 -118
  1114. package/template/.hermes/skills/llm-wiki/SKILL.md +0 -60
  1115. package/template/.hermes/skills/llm-wiki/assets/CLAUDE.md.template +0 -26
  1116. package/template/.hermes/skills/llm-wiki/assets/fixtures/broken-wiki/wiki/CLAUDE.md +0 -3
  1117. package/template/.hermes/skills/llm-wiki/assets/fixtures/broken-wiki/wiki/index.md +0 -5
  1118. package/template/.hermes/skills/llm-wiki/assets/fixtures/broken-wiki/wiki/log.md +0 -1
  1119. package/template/.hermes/skills/llm-wiki/assets/fixtures/broken-wiki/wiki//345/255/230/345/234/250/347/232/204/351/241/265.md +0 -7
  1120. package/template/.hermes/skills/llm-wiki/references/compile.md +0 -126
  1121. package/template/.hermes/skills/llm-wiki/references/discover.md +0 -42
  1122. package/template/.hermes/skills/llm-wiki/references/ingest.md +0 -36
  1123. package/template/.hermes/skills/llm-wiki/references/lint.md +0 -54
  1124. package/template/.hermes/skills/llm-wiki/references/query.md +0 -21
  1125. package/template/.hermes/skills/llm-wiki/references/schema.md +0 -45
  1126. package/template/.hermes/skills/llm-wiki/references/writing.md +0 -35
  1127. package/template/.hermes/skills/llm-wiki/scripts/advise.mjs +0 -196
  1128. package/template/.hermes/skills/llm-wiki/scripts/advise.test.mjs +0 -91
  1129. package/template/.hermes/skills/llm-wiki/scripts/extract.mjs +0 -55
  1130. package/template/.hermes/skills/llm-wiki/scripts/extract.test.mjs +0 -34
  1131. package/template/.hermes/skills/llm-wiki/scripts/inventory.mjs +0 -174
  1132. package/template/.hermes/skills/llm-wiki/scripts/inventory.test.mjs +0 -98
  1133. package/template/.hermes/skills/llm-wiki/scripts/lint-wikilinks.mjs +0 -177
  1134. package/template/.hermes/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +0 -212
  1135. package/template/.hermes/skills/lombok/SKILL.md +0 -351
  1136. package/template/.hermes/skills/loop-me/SKILL.md +0 -32
  1137. package/template/.hermes/skills/loop-me/agents/openai.yaml +0 -5
  1138. package/template/.hermes/skills/maintaining-skills/SKILL.md +0 -27
  1139. package/template/.hermes/skills/mapstruct/SKILL.md +0 -334
  1140. package/template/.hermes/skills/migrate-to-shoehorn/SKILL.md +0 -118
  1141. package/template/.hermes/skills/migrate-to-shoehorn/agents/openai.yaml +0 -3
  1142. package/template/.hermes/skills/page-form-module/SKILL.md +0 -168
  1143. package/template/.hermes/skills/page-list-module/SKILL.md +0 -137
  1144. package/template/.hermes/skills/page-skeleton/SKILL.md +0 -76
  1145. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/constant.ts +0 -34
  1146. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/hooks/useRuleConfig.ts +0 -75
  1147. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/index.vue +0 -107
  1148. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/schemas/ruleConfigSchema.ts +0 -256
  1149. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/style.less +0 -30
  1150. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/constant.ts +0 -28
  1151. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/hooks/useQualityRuleList.ts +0 -58
  1152. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/hooks/useRuleActions.ts +0 -65
  1153. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/index.vue +0 -91
  1154. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/style.less +0 -49
  1155. package/template/.hermes/skills/prototype/LOGIC.md +0 -67
  1156. package/template/.hermes/skills/prototype/SKILL.md +0 -26
  1157. package/template/.hermes/skills/prototype/UI.md +0 -112
  1158. package/template/.hermes/skills/prototype/agents/openai.yaml +0 -3
  1159. package/template/.hermes/skills/prototype-page-acceptance/SKILL.md +0 -62
  1160. package/template/.hermes/skills/prototype-review/SKILL.md +0 -74
  1161. package/template/.hermes/skills/prototype-review/agents/openai.yaml +0 -4
  1162. package/template/.hermes/skills/research/SKILL.md +0 -12
  1163. package/template/.hermes/skills/research/agents/openai.yaml +0 -3
  1164. package/template/.hermes/skills/resolving-merge-conflicts/SKILL.md +0 -14
  1165. package/template/.hermes/skills/resolving-merge-conflicts/agents/openai.yaml +0 -3
  1166. package/template/.hermes/skills/scaffold-exercises/SKILL.md +0 -106
  1167. package/template/.hermes/skills/scaffold-exercises/agents/openai.yaml +0 -3
  1168. package/template/.hermes/skills/setup-matt-pocock-skills/SKILL.md +0 -125
  1169. package/template/.hermes/skills/setup-matt-pocock-skills/agents/openai.yaml +0 -5
  1170. package/template/.hermes/skills/setup-matt-pocock-skills/domain.md +0 -51
  1171. package/template/.hermes/skills/setup-matt-pocock-skills/issue-tracker-github.md +0 -45
  1172. package/template/.hermes/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +0 -46
  1173. package/template/.hermes/skills/setup-matt-pocock-skills/issue-tracker-local.md +0 -66
  1174. package/template/.hermes/skills/setup-matt-pocock-skills/triage-labels.md +0 -15
  1175. package/template/.hermes/skills/setup-pre-commit/SKILL.md +0 -91
  1176. package/template/.hermes/skills/setup-pre-commit/agents/openai.yaml +0 -3
  1177. package/template/.hermes/skills/setup-ts-deep-modules/SKILL.md +0 -102
  1178. package/template/.hermes/skills/setup-ts-deep-modules/agents/openai.yaml +0 -5
  1179. package/template/.hermes/skills/setup-ts-deep-modules/dependency-cruiser.config.cjs +0 -95
  1180. package/template/.hermes/skills/tdd/SKILL.md +0 -38
  1181. package/template/.hermes/skills/tdd/agents/openai.yaml +0 -3
  1182. package/template/.hermes/skills/tdd/mocking.md +0 -59
  1183. package/template/.hermes/skills/tdd/tests.md +0 -77
  1184. package/template/.hermes/skills/teach/GLOSSARY-FORMAT.md +0 -35
  1185. package/template/.hermes/skills/teach/LEARNING-RECORD-FORMAT.md +0 -46
  1186. package/template/.hermes/skills/teach/MISSION-FORMAT.md +0 -31
  1187. package/template/.hermes/skills/teach/RESOURCES-FORMAT.md +0 -32
  1188. package/template/.hermes/skills/teach/SKILL.md +0 -140
  1189. package/template/.hermes/skills/teach/agents/openai.yaml +0 -5
  1190. package/template/.hermes/skills/theme-token-usage/SKILL.md +0 -123
  1191. package/template/.hermes/skills/theme-token-usage/agents/openai.yaml +0 -4
  1192. package/template/.hermes/skills/to-questionnaire/SKILL.md +0 -53
  1193. package/template/.hermes/skills/to-questionnaire/agents/openai.yaml +0 -5
  1194. package/template/.hermes/skills/to-spec/SKILL.md +0 -75
  1195. package/template/.hermes/skills/to-spec/agents/openai.yaml +0 -5
  1196. package/template/.hermes/skills/to-tickets/SKILL.md +0 -105
  1197. package/template/.hermes/skills/to-tickets/agents/openai.yaml +0 -5
  1198. package/template/.hermes/skills/triage/AGENT-BRIEF.md +0 -207
  1199. package/template/.hermes/skills/triage/OUT-OF-SCOPE.md +0 -105
  1200. package/template/.hermes/skills/triage/SKILL.md +0 -112
  1201. package/template/.hermes/skills/triage/agents/openai.yaml +0 -5
  1202. package/template/.hermes/skills/using-git-worktrees/SKILL.md +0 -202
  1203. package/template/.hermes/skills/vue3-best-practices/SKILL.md +0 -68
  1204. package/template/.hermes/skills/wait-what/SKILL.md +0 -7
  1205. package/template/.hermes/skills/wait-what/agents/openai.yaml +0 -5
  1206. package/template/.hermes/skills/wayfinder/SKILL.md +0 -128
  1207. package/template/.hermes/skills/wayfinder/agents/openai.yaml +0 -5
  1208. package/template/.hermes/skills/writing-beats/SKILL.md +0 -67
  1209. package/template/.hermes/skills/writing-beats/agents/openai.yaml +0 -5
  1210. package/template/.hermes/skills/writing-for-agents/SKILL-MECHANICS.md +0 -22
  1211. package/template/.hermes/skills/writing-for-agents/SKILL.md +0 -81
  1212. package/template/.hermes/skills/writing-for-agents/agents/openai.yaml +0 -3
  1213. package/template/.hermes/skills/writing-fragments/SKILL.md +0 -79
  1214. package/template/.hermes/skills/writing-fragments/agents/openai.yaml +0 -5
  1215. package/template/.hermes/skills/writing-shape/SKILL.md +0 -79
  1216. package/template/.hermes/skills/writing-shape/agents/openai.yaml +0 -5
  1217. package/template/.hermes/skills/yedit-table-usage/SKILL.md +0 -227
  1218. package/template/.hermes/skills/yss-antd-design/SKILL.md +0 -34
  1219. package/template/.hermes/skills/yss-antd-design/references/boundaries.md +0 -30
  1220. package/template/.hermes/skills/yss-antd-design/references/cli-matrix.md +0 -38
  1221. package/template/.hermes/skills/yss-antd-design/references/evidence.md +0 -30
  1222. package/template/.hermes/skills/yss-antd-design/scripts/validate-frontmatter.mjs +0 -46
  1223. package/template/.hermes/skills/yss-antd-design/scripts/validate-frontmatter.test.mjs +0 -27
  1224. package/template/.hermes/skills/yss-api-integration/SKILL.md +0 -185
  1225. package/template/.hermes/skills/yss-api-integration/agents/openai.yaml +0 -5
  1226. package/template/.hermes/skills/yss-application/SKILL.md +0 -54
  1227. package/template/.hermes/skills/yss-application/agents/openai.yaml +0 -5
  1228. package/template/.hermes/skills/yss-application/references/application-layer-guide.md +0 -87
  1229. package/template/.hermes/skills/yss-audit-log/SKILL.md +0 -67
  1230. package/template/.hermes/skills/yss-audit-log/agents/openai.yaml +0 -5
  1231. package/template/.hermes/skills/yss-audit-log/assets/AuditLogAspect.java +0 -262
  1232. package/template/.hermes/skills/yss-audit-log/assets/YssAuditLogPrintSubscriberImpl.java +0 -37
  1233. package/template/.hermes/skills/yss-audit-log/assets/YssAuditLogSysManagerSubscriberImpl.java +0 -18
  1234. package/template/.hermes/skills/yss-audit-log/assets/YssAuditPublishService.java +0 -69
  1235. package/template/.hermes/skills/yss-audit-log/references/README.md +0 -69
  1236. package/template/.hermes/skills/yss-audit-log/references/source-index.md +0 -43
  1237. package/template/.hermes/skills/yss-cache/SKILL.md +0 -44
  1238. package/template/.hermes/skills/yss-cache/agents/openai.yaml +0 -5
  1239. package/template/.hermes/skills/yss-cache/references/annotations.md +0 -39
  1240. package/template/.hermes/skills/yss-cache/references/architecture.md +0 -25
  1241. package/template/.hermes/skills/yss-cache/references/configuration.md +0 -34
  1242. package/template/.hermes/skills/yss-cache/references/redis-fallback.md +0 -23
  1243. package/template/.hermes/skills/yss-cache/references/redis-topology.md +0 -62
  1244. package/template/.hermes/skills/yss-cache/references/serialization.md +0 -17
  1245. package/template/.hermes/skills/yss-cache/references/source-index.md +0 -67
  1246. package/template/.hermes/skills/yss-cache/references/troubleshooting.md +0 -33
  1247. package/template/.hermes/skills/yss-cache/references/verification.md +0 -34
  1248. package/template/.hermes/skills/yss-cache/scripts/check-skill-freshness.sh +0 -84
  1249. package/template/.hermes/skills/yss-cache/scripts/inspect-cache-usage.sh +0 -96
  1250. package/template/.hermes/skills/yss-cache/scripts/verify-cache-component.sh +0 -101
  1251. package/template/.hermes/skills/yss-components/SKILL.md +0 -386
  1252. package/template/.hermes/skills/yss-components/agents/openai.yaml +0 -5
  1253. package/template/.hermes/skills/yss-components/references/frontend-docs.md +0 -9
  1254. package/template/.hermes/skills/yss-ddd-scaffold-generator/SKILL.md +0 -110
  1255. package/template/.hermes/skills/yss-ddd-scaffold-generator/agents/openai.yaml +0 -5
  1256. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/config/application-local.yml.template +0 -8
  1257. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/config/application.yml.template +0 -36
  1258. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/config/logback-spring.xml.template +0 -15
  1259. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/java/architecture-rules-test.java.template +0 -36
  1260. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/java/bootstrap-application.java.template +0 -15
  1261. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/adapter-pom.xml.template +0 -19
  1262. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/application-pom.xml.template +0 -52
  1263. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/bootstrap-pom.xml.template +0 -70
  1264. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/domain-pom.xml.template +0 -22
  1265. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/infrastructure-pom.xml.template +0 -54
  1266. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/parent-pom.xml.template +0 -155
  1267. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/web-pom.xml.template +0 -58
  1268. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/jvm.config +0 -1
  1269. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/maven.config +0 -2
  1270. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/settings.xml +0 -41
  1271. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/wrapper/MavenWrapperDownloader.java +0 -117
  1272. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/wrapper/maven-wrapper.jar +0 -0
  1273. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/wrapper/maven-wrapper.properties +0 -20
  1274. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/mvnw +0 -316
  1275. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/mvnw.cmd +0 -188
  1276. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +0 -116
  1277. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +0 -114
  1278. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-adapter/SKILL.md +0 -31
  1279. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-adapter/agents/openai.yaml +0 -5
  1280. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-application/SKILL.md +0 -8
  1281. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-application/agents/openai.yaml +0 -5
  1282. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-domain/SKILL.md +0 -8
  1283. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-domain/agents/openai.yaml +0 -5
  1284. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-infrastructure/SKILL.md +0 -8
  1285. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-infrastructure/agents/openai.yaml +0 -5
  1286. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +0 -57
  1287. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/agents/openai.yaml +0 -5
  1288. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-web/SKILL.md +0 -8
  1289. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-web/agents/openai.yaml +0 -5
  1290. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +0 -191
  1291. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/generate_and_verify_scaffold.mjs +0 -126
  1292. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +0 -349
  1293. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +0 -112
  1294. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +0 -388
  1295. package/template/.hermes/skills/yss-design-system/SKILL.md +0 -105
  1296. package/template/.hermes/skills/yss-design-system/agents/openai.yaml +0 -5
  1297. package/template/.hermes/skills/yss-design-system/references/design-qa-theme.md +0 -64
  1298. package/template/.hermes/skills/yss-design-system/references/design-system.md +0 -245
  1299. package/template/.hermes/skills/yss-design-system/scripts/validate-frontmatter.mjs +0 -36
  1300. package/template/.hermes/skills/yss-design-system/scripts/validate-frontmatter.test.mjs +0 -12
  1301. package/template/.hermes/skills/yss-distributed-id/SKILL.md +0 -69
  1302. package/template/.hermes/skills/yss-distributed-id/agents/openai.yaml +0 -5
  1303. package/template/.hermes/skills/yss-distributed-id/assets/AutoIdInterceptor.java +0 -246
  1304. package/template/.hermes/skills/yss-distributed-id/assets/EnableDistributedId.java +0 -25
  1305. package/template/.hermes/skills/yss-distributed-id/assets/LeafConf.java +0 -36
  1306. package/template/.hermes/skills/yss-distributed-id/references/README.md +0 -51
  1307. package/template/.hermes/skills/yss-distributed-id/references/source-index.md +0 -86
  1308. package/template/.hermes/skills/yss-domain/SKILL.md +0 -69
  1309. package/template/.hermes/skills/yss-domain/agents/openai.yaml +0 -5
  1310. package/template/.hermes/skills/yss-domain/references/domain-layer-guide.md +0 -99
  1311. package/template/.hermes/skills/yss-dto/SKILL.md +0 -86
  1312. package/template/.hermes/skills/yss-dto/agents/openai.yaml +0 -5
  1313. package/template/.hermes/skills/yss-dto/assets/CommandDTO.java +0 -8
  1314. package/template/.hermes/skills/yss-dto/assets/PageQuery.java +0 -133
  1315. package/template/.hermes/skills/yss-dto/assets/QueryDTO.java +0 -6
  1316. package/template/.hermes/skills/yss-dto/assets/Result.java +0 -105
  1317. package/template/.hermes/skills/yss-dto/references/README.md +0 -67
  1318. package/template/.hermes/skills/yss-dto/references/openapi-wire-profile.yaml +0 -181
  1319. package/template/.hermes/skills/yss-dto/references/source-index.md +0 -55
  1320. package/template/.hermes/skills/yss-excel-mvc/SKILL.md +0 -66
  1321. package/template/.hermes/skills/yss-excel-mvc/agents/openai.yaml +0 -5
  1322. package/template/.hermes/skills/yss-excel-mvc/assets/ExcelDynamicData.java +0 -66
  1323. package/template/.hermes/skills/yss-excel-mvc/assets/RequestExcel.java +0 -33
  1324. package/template/.hermes/skills/yss-excel-mvc/assets/RequestFastExcelArgumentResolver.java +0 -272
  1325. package/template/.hermes/skills/yss-excel-mvc/assets/ResponseExcel.java +0 -46
  1326. package/template/.hermes/skills/yss-excel-mvc/assets/ResponseFastExcelReturnValueHandler.java +0 -154
  1327. package/template/.hermes/skills/yss-excel-mvc/references/README.md +0 -5
  1328. package/template/.hermes/skills/yss-excel-mvc/references/source-index.md +0 -74
  1329. package/template/.hermes/skills/yss-exception/SKILL.md +0 -57
  1330. package/template/.hermes/skills/yss-exception/agents/openai.yaml +0 -4
  1331. package/template/.hermes/skills/yss-exception/references/source-index.md +0 -35
  1332. package/template/.hermes/skills/yss-formily/SKILL.md +0 -272
  1333. package/template/.hermes/skills/yss-formily-schema-generator/SKILL.md +0 -189
  1334. package/template/.hermes/skills/yss-formily-schema-generator/agents/openai.yaml +0 -5
  1335. package/template/.hermes/skills/yss-formily-schema-generator/assets/java-sdk/ExampleUsage.java +0 -111
  1336. package/template/.hermes/skills/yss-formily-schema-generator/assets/java-sdk/YssFormilyDsl.java +0 -1219
  1337. package/template/.hermes/skills/yss-formily-schema-generator/references/workflow.md +0 -240
  1338. package/template/.hermes/skills/yss-frontend-scaffold-generator/SKILL.md +0 -76
  1339. package/template/.hermes/skills/yss-hook/SKILL.md +0 -200
  1340. package/template/.hermes/skills/yss-hook/agents/openai.yaml +0 -5
  1341. package/template/.hermes/skills/yss-hook/references/frontend-docs.md +0 -10
  1342. package/template/.hermes/skills/yss-microapp-commit/SKILL.md +0 -201
  1343. package/template/.hermes/skills/yss-microapp-commit/agents/openai.yaml +0 -5
  1344. package/template/.hermes/skills/yss-microapp-commit/references/scope-mapping.md +0 -204
  1345. package/template/.hermes/skills/yss-mybatis/SKILL.md +0 -77
  1346. package/template/.hermes/skills/yss-mybatis/agents/openai.yaml +0 -5
  1347. package/template/.hermes/skills/yss-mybatis/assets/BaseRepository.java +0 -112
  1348. package/template/.hermes/skills/yss-mybatis/assets/EntityQueryAspect.java +0 -37
  1349. package/template/.hermes/skills/yss-mybatis/assets/MybatisBaseConfiguration.java +0 -18
  1350. package/template/.hermes/skills/yss-mybatis/assets/PageQueryEntityConfigration.java +0 -18
  1351. package/template/.hermes/skills/yss-mybatis/references/README.md +0 -57
  1352. package/template/.hermes/skills/yss-mybatis/references/source-index.md +0 -93
  1353. package/template/.hermes/skills/yss-openapi-draft-review/SKILL.md +0 -82
  1354. package/template/.hermes/skills/yss-openapi-governance/SKILL.md +0 -130
  1355. package/template/.hermes/skills/yss-openapi-governance/agents/openai.yaml +0 -4
  1356. package/template/.hermes/skills/yss-page-module-development/SKILL.md +0 -165
  1357. package/template/.hermes/skills/yss-page-module-development/agents/openai.yaml +0 -5
  1358. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/constant.ts +0 -32
  1359. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/hooks/useRuleConfig.ts +0 -158
  1360. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/index.vue +0 -188
  1361. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/schemas/ruleConfigSchema.ts +0 -256
  1362. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/style.less +0 -76
  1363. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/constant.ts +0 -128
  1364. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/hooks/useQualityRuleList.ts +0 -144
  1365. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/hooks/useRuleActions.ts +0 -141
  1366. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/index.vue +0 -175
  1367. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/style.less +0 -83
  1368. package/template/.hermes/skills/yss-page-module-development/references/component-map.md +0 -34
  1369. package/template/.hermes/skills/yss-page-module-development/references/frontend-docs.md +0 -11
  1370. package/template/.hermes/skills/yss-page-module-development/references/hooks-map.md +0 -11
  1371. package/template/.hermes/skills/yss-page-module-development/references/utils-map.md +0 -14
  1372. package/template/.hermes/skills/yss-product-lifecycle/SKILL.md +0 -52
  1373. package/template/.hermes/skills/yss-product-lifecycle/agents/openai.yaml +0 -4
  1374. package/template/.hermes/skills/yss-product-lifecycle/references/artifact-dependencies.md +0 -36
  1375. package/template/.hermes/skills/yss-product-lifecycle/references/matt-yss-adapter.md +0 -122
  1376. package/template/.hermes/skills/yss-product-lifecycle/references/orchestration-contract.yaml +0 -863
  1377. package/template/.hermes/skills/yss-product-lifecycle/references/orchestration.md +0 -116
  1378. package/template/.hermes/skills/yss-product-lifecycle/references/state-model.md +0 -112
  1379. package/template/.hermes/skills/yss-prototype-stage/SKILL.md +0 -47
  1380. package/template/.hermes/skills/yss-prototype-stage/references/product-design-adapter.md +0 -54
  1381. package/template/.hermes/skills/yss-prototype-stage/scripts/prototype-contract.mjs +0 -207
  1382. package/template/.hermes/skills/yss-prototype-stage/tests/run-scenarios.mjs +0 -154
  1383. package/template/.hermes/skills/yss-repository/SKILL.md +0 -78
  1384. package/template/.hermes/skills/yss-repository/agents/openai.yaml +0 -5
  1385. package/template/.hermes/skills/yss-repository/references/infrastructure-layer-guide.md +0 -107
  1386. package/template/.hermes/skills/yss-resilience4j/SKILL.md +0 -46
  1387. package/template/.hermes/skills/yss-resilience4j/agents/openai.yaml +0 -4
  1388. package/template/.hermes/skills/yss-resilience4j/references/source-index.md +0 -33
  1389. package/template/.hermes/skills/yss-router/SKILL.md +0 -51
  1390. package/template/.hermes/skills/yss-router/agents/openai.yaml +0 -4
  1391. package/template/.hermes/skills/yss-router/references/boundaries.md +0 -50
  1392. package/template/.hermes/skills/yss-router/references/router-contract.yaml +0 -345
  1393. package/template/.hermes/skills/yss-router/references/slice-implementation-contract.md +0 -185
  1394. package/template/.hermes/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  1395. package/template/.hermes/skills/yss-security-algorithm/SKILL.md +0 -52
  1396. package/template/.hermes/skills/yss-security-algorithm/agents/openai.yaml +0 -4
  1397. package/template/.hermes/skills/yss-security-algorithm/references/source-index.md +0 -35
  1398. package/template/.hermes/skills/yss-source-index/SKILL.md +0 -63
  1399. package/template/.hermes/skills/yss-source-index/agents/openai.yaml +0 -4
  1400. package/template/.hermes/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  1401. package/template/.hermes/skills/yss-stage-decision/SKILL.md +0 -50
  1402. package/template/.hermes/skills/yss-stage-decision/references/domain-strategy-contract.md +0 -26
  1403. package/template/.hermes/skills/yss-stage-decision/references/domain-strategy.schema.json +0 -128
  1404. package/template/.hermes/skills/yss-stage-decision/references/stage-decision-package-contract.md +0 -19
  1405. package/template/.hermes/skills/yss-stage-decision/references/stage-decision-package.schema.json +0 -86
  1406. package/template/.hermes/skills/yss-stage-decision/references/validation-rules.md +0 -18
  1407. package/template/.hermes/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +0 -169
  1408. package/template/.hermes/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +0 -100
  1409. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +0 -15
  1410. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +0 -58
  1411. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +0 -44
  1412. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +0 -15
  1413. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +0 -49
  1414. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +0 -108
  1415. package/template/.hermes/skills/yss-stage-decision/tests/run-scenarios.mjs +0 -68
  1416. package/template/.hermes/skills/yss-tactical-design/SKILL.md +0 -35
  1417. package/template/.hermes/skills/yss-tactical-design/agents/openai.yaml +0 -4
  1418. package/template/.hermes/skills/yss-tactical-design/references/output-template.md +0 -28
  1419. package/template/.hermes/skills/yss-tactical-design/references/tactical-design-contract.md +0 -41
  1420. package/template/.hermes/skills/yss-tactical-design/references/tactical-design.schema.json +0 -31
  1421. package/template/.hermes/skills/yss-tactical-design/references/validation-rules.md +0 -13
  1422. package/template/.hermes/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +0 -213
  1423. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +0 -21
  1424. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +0 -21
  1425. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +0 -21
  1426. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +0 -21
  1427. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +0 -21
  1428. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +0 -80
  1429. package/template/.hermes/skills/yss-tactical-design/tests/run-scenarios.mjs +0 -47
  1430. package/template/.hermes/skills/yss-ui/SKILL.md +0 -170
  1431. package/template/.hermes/skills/yss-ui/agents/openai.yaml +0 -5
  1432. package/template/.hermes/skills/yss-ui/assets/demos/button/auth-basic.vue +0 -24
  1433. package/template/.hermes/skills/yss-ui/assets/demos/button/auth-dropdown.vue +0 -29
  1434. package/template/.hermes/skills/yss-ui/assets/demos/button/permission.vue +0 -10
  1435. package/template/.hermes/skills/yss-ui/assets/demos/button/size.vue +0 -12
  1436. package/template/.hermes/skills/yss-ui/assets/demos/button/states.vue +0 -12
  1437. package/template/.hermes/skills/yss-ui/assets/demos/card/basic/index.vue +0 -54
  1438. package/template/.hermes/skills/yss-ui/assets/demos/card/basic/style.less +0 -41
  1439. package/template/.hermes/skills/yss-ui/assets/demos/card/cover/index.vue +0 -25
  1440. package/template/.hermes/skills/yss-ui/assets/demos/card/flexible-layout/index.vue +0 -122
  1441. package/template/.hermes/skills/yss-ui/assets/demos/card/flexible-layout/style.less +0 -191
  1442. package/template/.hermes/skills/yss-ui/assets/demos/card/header-extra/index.vue +0 -260
  1443. package/template/.hermes/skills/yss-ui/assets/demos/card/header-extra/style.less +0 -124
  1444. package/template/.hermes/skills/yss-ui/assets/demos/card/layout-metrics/constants.ts +0 -61
  1445. package/template/.hermes/skills/yss-ui/assets/demos/card/layout-metrics/index.vue +0 -72
  1446. package/template/.hermes/skills/yss-ui/assets/demos/card/layout-metrics/metrics-cards.vue +0 -70
  1447. package/template/.hermes/skills/yss-ui/assets/demos/card/layout-metrics/style.less +0 -232
  1448. package/template/.hermes/skills/yss-ui/assets/demos/card/loading/index.vue +0 -19
  1449. package/template/.hermes/skills/yss-ui/assets/demos/card/meta/index.vue +0 -19
  1450. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/basic/constants.ts +0 -44
  1451. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/basic/hooks/useConditionBasic.ts +0 -18
  1452. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/basic/index.vue +0 -100
  1453. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/linked/constants.ts +0 -51
  1454. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/linked/hooks/useConditionLinked.ts +0 -29
  1455. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/linked/index.vue +0 -23
  1456. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/nesting-between/constants.ts +0 -35
  1457. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/nesting-between/hooks/useConditionNesting.ts +0 -25
  1458. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/nesting-between/index.vue +0 -24
  1459. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/remote/constants.ts +0 -7
  1460. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/remote/hooks/useConditionRemote.ts +0 -75
  1461. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/remote/index.vue +0 -23
  1462. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation/constants.ts +0 -42
  1463. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation/hooks/useConditionBasic.ts +0 -22
  1464. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation/index.vue +0 -138
  1465. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation-custom/constants.ts +0 -42
  1466. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation-custom/hooks/useConditionBasic.ts +0 -22
  1467. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation-custom/index.vue +0 -134
  1468. package/template/.hermes/skills/yss-ui/assets/demos/cron/basic/index.vue +0 -54
  1469. package/template/.hermes/skills/yss-ui/assets/demos/cron/select/index.vue +0 -145
  1470. package/template/.hermes/skills/yss-ui/assets/demos/echarts/basic.vue +0 -30
  1471. package/template/.hermes/skills/yss-ui/assets/demos/echarts/calendar-heatmap.vue +0 -49
  1472. package/template/.hermes/skills/yss-ui/assets/demos/echarts/candlestick.vue +0 -30
  1473. package/template/.hermes/skills/yss-ui/assets/demos/echarts/dark.vue +0 -40
  1474. package/template/.hermes/skills/yss-ui/assets/demos/echarts/expose-methods.vue +0 -97
  1475. package/template/.hermes/skills/yss-ui/assets/demos/echarts/funnel.vue +0 -42
  1476. package/template/.hermes/skills/yss-ui/assets/demos/echarts/gauge.vue +0 -25
  1477. package/template/.hermes/skills/yss-ui/assets/demos/echarts/line.vue +0 -24
  1478. package/template/.hermes/skills/yss-ui/assets/demos/echarts/mix.vue +0 -33
  1479. package/template/.hermes/skills/yss-ui/assets/demos/echarts/pie.vue +0 -34
  1480. package/template/.hermes/skills/yss-ui/assets/demos/echarts/radar.vue +0 -38
  1481. package/template/.hermes/skills/yss-ui/assets/demos/echarts/ring-segment.vue +0 -77
  1482. package/template/.hermes/skills/yss-ui/assets/demos/echarts/scatter.vue +0 -23
  1483. package/template/.hermes/skills/yss-ui/assets/demos/echarts/stacked-area.vue +0 -55
  1484. package/template/.hermes/skills/yss-ui/assets/demos/echarts/stacked-bar.vue +0 -52
  1485. package/template/.hermes/skills/yss-ui/assets/demos/echarts/treemap.vue +0 -30
  1486. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/all-types/hooks/useAllTypes.ts +0 -259
  1487. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/all-types/index.vue +0 -33
  1488. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/async-remote/hooks/useAsyncRemote.ts +0 -201
  1489. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/async-remote/index.vue +0 -45
  1490. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/basic/index.vue +0 -104
  1491. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/big-data/index.vue +0 -82
  1492. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/custom-rule/index.vue +0 -114
  1493. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/direct-editable/index.vue +0 -94
  1494. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/drag/index.vue +0 -125
  1495. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/filter/index.vue +0 -107
  1496. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/linkage/index.vue +0 -181
  1497. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/pagination-action/index.vue +0 -131
  1498. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/required/index.vue +0 -95
  1499. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/row-edit/index.vue +0 -220
  1500. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/row-options/index.vue +0 -165
  1501. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/slot-linkage/index.vue +0 -184
  1502. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/submit-view/index.vue +0 -107
  1503. package/template/.hermes/skills/yss-ui/assets/demos/file-import/basic.vue +0 -54
  1504. package/template/.hermes/skills/yss-ui/assets/demos/file-import/formily.vue +0 -42
  1505. package/template/.hermes/skills/yss-ui/assets/demos/file-import/multiple.vue +0 -33
  1506. package/template/.hermes/skills/yss-ui/assets/demos/file-import/result-slot.vue +0 -30
  1507. package/template/.hermes/skills/yss-ui/assets/demos/formily/actions.vue +0 -61
  1508. package/template/.hermes/skills/yss-ui/assets/demos/formily/align.vue +0 -56
  1509. package/template/.hermes/skills/yss-ui/assets/demos/formily/async-validate.vue +0 -69
  1510. package/template/.hermes/skills/yss-ui/assets/demos/formily/basic.vue +0 -317
  1511. package/template/.hermes/skills/yss-ui/assets/demos/formily/collapse.vue +0 -104
  1512. package/template/.hermes/skills/yss-ui/assets/demos/formily/custom-component.vue +0 -75
  1513. package/template/.hermes/skills/yss-ui/assets/demos/formily/disabled.vue +0 -71
  1514. package/template/.hermes/skills/yss-ui/assets/demos/formily/dynamic-array.vue +0 -74
  1515. package/template/.hermes/skills/yss-ui/assets/demos/formily/dynamic.vue +0 -59
  1516. package/template/.hermes/skills/yss-ui/assets/demos/formily/effects.vue +0 -117
  1517. package/template/.hermes/skills/yss-ui/assets/demos/formily/events-inline.vue +0 -82
  1518. package/template/.hermes/skills/yss-ui/assets/demos/formily/events-scope.vue +0 -101
  1519. package/template/.hermes/skills/yss-ui/assets/demos/formily/grid.vue +0 -71
  1520. package/template/.hermes/skills/yss-ui/assets/demos/formily/group.vue +0 -102
  1521. package/template/.hermes/skills/yss-ui/assets/demos/formily/helper-slot.vue +0 -50
  1522. package/template/.hermes/skills/yss-ui/assets/demos/formily/label-tip.vue +0 -42
  1523. package/template/.hermes/skills/yss-ui/assets/demos/formily/linkage-multi.vue +0 -127
  1524. package/template/.hermes/skills/yss-ui/assets/demos/formily/linkage.vue +0 -78
  1525. package/template/.hermes/skills/yss-ui/assets/demos/formily/modes.vue +0 -72
  1526. package/template/.hermes/skills/yss-ui/assets/demos/formily/remote-enum.vue +0 -88
  1527. package/template/.hermes/skills/yss-ui/assets/demos/formily/slot.vue +0 -54
  1528. package/template/.hermes/skills/yss-ui/assets/demos/formily/steps.vue +0 -135
  1529. package/template/.hermes/skills/yss-ui/assets/demos/formily/submit-failed.vue +0 -72
  1530. package/template/.hermes/skills/yss-ui/assets/demos/monaco/basic.vue +0 -40
  1531. package/template/.hermes/skills/yss-ui/assets/demos/monaco/diff-basic.vue +0 -143
  1532. package/template/.hermes/skills/yss-ui/assets/demos/monaco/fullscreen.vue +0 -84
  1533. package/template/.hermes/skills/yss-ui/assets/demos/monaco/history-log-viewer.vue +0 -586
  1534. package/template/.hermes/skills/yss-ui/assets/demos/monaco/log-viewer.vue +0 -260
  1535. package/template/.hermes/skills/yss-ui/assets/demos/monaco/mock/mockLogApi.ts +0 -199
  1536. package/template/.hermes/skills/yss-ui/assets/demos/monaco/monaco.vue +0 -66
  1537. package/template/.hermes/skills/yss-ui/assets/demos/monaco/readonly.vue +0 -20
  1538. package/template/.hermes/skills/yss-ui/assets/demos/monaco/resizable.vue +0 -22
  1539. package/template/.hermes/skills/yss-ui/assets/demos/monaco/sql-schema.vue +0 -20
  1540. package/template/.hermes/skills/yss-ui/assets/demos/monaco/switch-lang-theme.vue +0 -48
  1541. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table/constant.ts +0 -18
  1542. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table/index.vue +0 -60
  1543. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table/style.less +0 -39
  1544. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table-layout/hooks/useTreeTable.ts +0 -158
  1545. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table-layout/index.vue +0 -88
  1546. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table-layout/style.less +0 -73
  1547. package/template/.hermes/skills/yss-ui/assets/demos/table/action/index.vue +0 -83
  1548. package/template/.hermes/skills/yss-ui/assets/demos/table/action/style.less +0 -3
  1549. package/template/.hermes/skills/yss-ui/assets/demos/table/action-auth/index.vue +0 -41
  1550. package/template/.hermes/skills/yss-ui/assets/demos/table/action-slot/index.vue +0 -62
  1551. package/template/.hermes/skills/yss-ui/assets/demos/table/action-slot/style.less +0 -36
  1552. package/template/.hermes/skills/yss-ui/assets/demos/table/basic/hooks/useBasicTable.ts +0 -205
  1553. package/template/.hermes/skills/yss-ui/assets/demos/table/basic/index.vue +0 -89
  1554. package/template/.hermes/skills/yss-ui/assets/demos/table/basic/style.less +0 -28
  1555. package/template/.hermes/skills/yss-ui/assets/demos/table/column-custom-search/hooks/useColumnCustomSearch.ts +0 -236
  1556. package/template/.hermes/skills/yss-ui/assets/demos/table/column-custom-search/index.vue +0 -141
  1557. package/template/.hermes/skills/yss-ui/assets/demos/table/column-custom-search/style.less +0 -48
  1558. package/template/.hermes/skills/yss-ui/assets/demos/table/drag/index.vue +0 -42
  1559. package/template/.hermes/skills/yss-ui/assets/demos/table/expand/action-expand.vue +0 -142
  1560. package/template/.hermes/skills/yss-ui/assets/demos/table/expand/index.vue +0 -35
  1561. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-async/index.vue +0 -93
  1562. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-auto/index.vue +0 -99
  1563. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-custom/index.vue +0 -72
  1564. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-local/index.vue +0 -112
  1565. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-mixed/index.vue +0 -142
  1566. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-remote/constant.ts +0 -129
  1567. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-remote/hooks/useTableData.ts +0 -186
  1568. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-remote/index.vue +0 -73
  1569. package/template/.hermes/skills/yss-ui/assets/demos/table/group-header/index.vue +0 -86
  1570. package/template/.hermes/skills/yss-ui/assets/demos/table/group-header/style.less +0 -3
  1571. package/template/.hermes/skills/yss-ui/assets/demos/table/header-height/index.vue +0 -29
  1572. package/template/.hermes/skills/yss-ui/assets/demos/table/header-height/style.less +0 -9
  1573. package/template/.hermes/skills/yss-ui/assets/demos/table/merge-cells/hooks/useMergeCells.ts +0 -183
  1574. package/template/.hermes/skills/yss-ui/assets/demos/table/merge-cells/index.vue +0 -17
  1575. package/template/.hermes/skills/yss-ui/assets/demos/table/merge-cells/style.less +0 -3
  1576. package/template/.hermes/skills/yss-ui/assets/demos/table/pagination-remote/hooks/usePaginationRemote.ts +0 -106
  1577. package/template/.hermes/skills/yss-ui/assets/demos/table/pagination-remote/index.vue +0 -24
  1578. package/template/.hermes/skills/yss-ui/assets/demos/table/renderers/index.vue +0 -64
  1579. package/template/.hermes/skills/yss-ui/assets/demos/table/selection-checkbox/index.vue +0 -88
  1580. package/template/.hermes/skills/yss-ui/assets/demos/table/selection-checkbox/style.less +0 -9
  1581. package/template/.hermes/skills/yss-ui/assets/demos/table/selection-radio/index.vue +0 -57
  1582. package/template/.hermes/skills/yss-ui/assets/demos/table/selection-radio/style.less +0 -9
  1583. package/template/.hermes/skills/yss-ui/assets/demos/table/transform/index.vue +0 -32
  1584. package/template/.hermes/skills/yss-ui/assets/demos/tree/basic-left/index.vue +0 -124
  1585. package/template/.hermes/skills/yss-ui/assets/demos/tree/datasource-group/index.vue +0 -31
  1586. package/template/.hermes/skills/yss-ui/assets/demos/tree/multi-checkable/index.vue +0 -86
  1587. package/template/.hermes/skills/yss-ui/assets/demos/tree/multiple-select/index.vue +0 -76
  1588. package/template/.hermes/skills/yss-ui/assets/demos/tree/resource-list/index.vue +0 -41
  1589. package/template/.hermes/skills/yss-ui/assets/demos/tree/search-toolbar/index.vue +0 -156
  1590. package/template/.hermes/skills/yss-ui/assets/demos/tree/slot-icon/index.vue +0 -36
  1591. package/template/.hermes/skills/yss-ui/assets/demos/tree/slot-switcher-icon/index.vue +0 -34
  1592. package/template/.hermes/skills/yss-ui/assets/docs/components/button.md +0 -79
  1593. package/template/.hermes/skills/yss-ui/assets/docs/components/card.md +0 -162
  1594. package/template/.hermes/skills/yss-ui/assets/docs/components/conditionBuilder.md +0 -114
  1595. package/template/.hermes/skills/yss-ui/assets/docs/components/cron.md +0 -77
  1596. package/template/.hermes/skills/yss-ui/assets/docs/components/echarts.md +0 -110
  1597. package/template/.hermes/skills/yss-ui/assets/docs/components/editTable.md +0 -212
  1598. package/template/.hermes/skills/yss-ui/assets/docs/components/fileImport.md +0 -85
  1599. package/template/.hermes/skills/yss-ui/assets/docs/components/formily.md +0 -370
  1600. package/template/.hermes/skills/yss-ui/assets/docs/components/monaco.md +0 -369
  1601. package/template/.hermes/skills/yss-ui/assets/docs/components/sheet.md +0 -169
  1602. package/template/.hermes/skills/yss-ui/assets/docs/components/split-pane.md +0 -45
  1603. package/template/.hermes/skills/yss-ui/assets/docs/components/table.md +0 -398
  1604. package/template/.hermes/skills/yss-ui/assets/docs/components/tree.md +0 -102
  1605. package/template/.hermes/skills/yss-ui/assets/docs/guide/gitlab-ci-integration.md +0 -166
  1606. package/template/.hermes/skills/yss-ui/assets/docs/guide/index.md +0 -132
  1607. package/template/.hermes/skills/yss-ui/assets/docs/guide/installation.md +0 -97
  1608. package/template/.hermes/skills/yss-ui/assets/docs/guide/jsp.md +0 -227
  1609. package/template/.hermes/skills/yss-ui/assets/docs/guide/llms.md +0 -295
  1610. package/template/.hermes/skills/yss-ui/assets/docs/guide/monorepo-versioning.md +0 -149
  1611. package/template/.hermes/skills/yss-ui/assets/docs/guide/release-workflow.md +0 -251
  1612. package/template/.hermes/skills/yss-ui/assets/docs/hooks/use-table-height.md +0 -101
  1613. package/template/.hermes/skills/yss-ui/assets/docs/hooks/use-tree-height.md +0 -115
  1614. package/template/.hermes/skills/yss-ui/assets/docs/hooks/useFullscreen.md +0 -130
  1615. package/template/.hermes/skills/yss-ui/assets/docs/hooks/useLoading.md +0 -97
  1616. package/template/.hermes/skills/yss-ui/assets/docs/root/resources.md +0 -18
  1617. package/template/.hermes/skills/yss-ui/assets/keyword-index.json +0 -53
  1618. package/template/.hermes/skills/yss-ui/assets/keyword-index.md +0 -47
  1619. package/template/.hermes/skills/yss-ui/assets/reference-index.json +0 -55
  1620. package/template/.hermes/skills/yss-ui/assets/reference-index.md +0 -74
  1621. package/template/.hermes/skills/yss-ui/assets/scenario-index.json +0 -50
  1622. package/template/.hermes/skills/yss-ui/assets/scenario-index.md +0 -41
  1623. package/template/.hermes/skills/yss-ui/references/accessibility.md +0 -23
  1624. package/template/.hermes/skills/yss-ui/references/antdv-compatibility.md +0 -39
  1625. package/template/.hermes/skills/yss-ui/references/checklist.md +0 -110
  1626. package/template/.hermes/skills/yss-ui/references/component-routing.md +0 -31
  1627. package/template/.hermes/skills/yss-ui/references/frontend-docs.md +0 -11
  1628. package/template/.hermes/skills/yss-ui/references/migration-antdv-to-yss.md +0 -20
  1629. package/template/.hermes/skills/yss-ui/references/quick-recipes.md +0 -280
  1630. package/template/.hermes/skills/yss-ui/references/theme-locale-overlay.md +0 -25
  1631. package/template/.hermes/skills/yss-ui/references/verification.md +0 -20
  1632. package/template/.hermes/skills/yss-ui-business-page-generation/SKILL.md +0 -159
  1633. package/template/.hermes/skills/yss-ui-business-page-generation/references/component-map.md +0 -39
  1634. package/template/.hermes/skills/yss-ui-business-page-generation/references/hooks-map.md +0 -70
  1635. package/template/.hermes/skills/yss-ui-business-page-generation/references/utils-map.md +0 -51
  1636. package/template/.hermes/skills/yss-up-springboot3/SKILL.md +0 -40
  1637. package/template/.hermes/skills/yss-up-springboot3/agents/openai.yaml +0 -4
  1638. package/template/.hermes/skills/yss-use-table-height/SKILL.md +0 -130
  1639. package/template/.hermes/skills/yss-use-table-height/agents/openai.yaml +0 -5
  1640. package/template/.hermes/skills/yss-use-table-height/references/frontend-docs.md +0 -9
  1641. package/template/.hermes/skills/yss-use-tree-height/SKILL.md +0 -98
  1642. package/template/.hermes/skills/yss-use-tree-height/agents/openai.yaml +0 -5
  1643. package/template/.hermes/skills/yss-use-tree-height/references/frontend-docs.md +0 -9
  1644. package/template/.hermes/skills/yss-userinfo/SKILL.md +0 -49
  1645. package/template/.hermes/skills/yss-userinfo/agents/openai.yaml +0 -4
  1646. package/template/.hermes/skills/yss-userinfo/references/source-index.md +0 -34
  1647. package/template/.hermes/skills/yss-validation/SKILL.md +0 -56
  1648. package/template/.hermes/skills/yss-validation/agents/openai.yaml +0 -4
  1649. package/template/.hermes/skills/yss-validation/references/source-index.md +0 -33
  1650. package/template/.hermes/skills/yss-web-controller/SKILL.md +0 -79
  1651. package/template/.hermes/skills/yss-web-controller/agents/openai.yaml +0 -5
  1652. package/template/.hermes/skills/yss-web-controller/assets/templates/Controller.java.template +0 -72
  1653. package/template/.hermes/skills/yss-web-controller/assets/templates/WebConvertor.java.template +0 -33
  1654. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +0 -19
  1655. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +0 -24
  1656. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/Response.java.template +0 -18
  1657. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +0 -19
  1658. package/template/.hermes/skills/yss-web-controller/references/web-adapter-layer-guide.md +0 -84
  1659. package/template/.hermes/skills/yss-web-controller/scripts/generate_controller.mjs +0 -105
  1660. package/template/.hermes/skills/yss-web-controller/scripts/generate_controller.test.mjs +0 -60
  1661. package/template/.hermes/skills/ytable-usage/SKILL.md +0 -251
  1662. package/template/.hermes/skills/ytree-usage/SKILL.md +0 -132
  1663. package/template/.kiro/settings/mcp.json +0 -8
  1664. package/template/.pi/skills/research/SKILL.md +0 -12
  1665. package/template/.pi/skills/research/agents/openai.yaml +0 -3
  1666. package/template/.pi/skills/yss-router/SKILL.md +0 -51
  1667. package/template/.pi/skills/yss-router/agents/openai.yaml +0 -4
  1668. package/template/.pi/skills/yss-router/references/boundaries.md +0 -50
  1669. package/template/.pi/skills/yss-router/references/router-contract.yaml +0 -345
  1670. package/template/.pi/skills/yss-router/references/slice-implementation-contract.md +0 -185
  1671. package/template/.pi/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  1672. package/template/.pi/skills/yss-source-index/SKILL.md +0 -63
  1673. package/template/.pi/skills/yss-source-index/agents/openai.yaml +0 -4
  1674. package/template/.pi/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  1675. package/template/.qoder/skills/research/SKILL.md +0 -12
  1676. package/template/.qoder/skills/research/agents/openai.yaml +0 -3
  1677. package/template/.qoder/skills/yss-router/SKILL.md +0 -51
  1678. package/template/.qoder/skills/yss-router/agents/openai.yaml +0 -4
  1679. package/template/.qoder/skills/yss-router/references/boundaries.md +0 -50
  1680. package/template/.qoder/skills/yss-router/references/router-contract.yaml +0 -345
  1681. package/template/.qoder/skills/yss-router/references/slice-implementation-contract.md +0 -185
  1682. package/template/.qoder/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  1683. package/template/.qoder/skills/yss-source-index/SKILL.md +0 -63
  1684. package/template/.qoder/skills/yss-source-index/agents/openai.yaml +0 -4
  1685. package/template/.qoder/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  1686. package/template/.trae/skills/research/SKILL.md +0 -12
  1687. package/template/.trae/skills/research/agents/openai.yaml +0 -3
  1688. package/template/.trae/skills/yss-router/SKILL.md +0 -51
  1689. package/template/.trae/skills/yss-router/agents/openai.yaml +0 -4
  1690. package/template/.trae/skills/yss-router/references/boundaries.md +0 -50
  1691. package/template/.trae/skills/yss-router/references/router-contract.yaml +0 -345
  1692. package/template/.trae/skills/yss-router/references/slice-implementation-contract.md +0 -185
  1693. package/template/.trae/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  1694. package/template/.trae/skills/yss-source-index/SKILL.md +0 -63
  1695. package/template/.trae/skills/yss-source-index/agents/openai.yaml +0 -4
  1696. package/template/.trae/skills/yss-source-index/references/source-location.md +0 -18
  1697. package/template/.trae/skills/yss-source-index/references/source-map-config.md +0 -30
  1698. package/template/.trae/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  1699. package/template/.trae/skills/yss-source-index/scripts/refresh-yss-skill-index.test.mjs +0 -15
  1700. package/template/scripts/verify-yss-router-scenarios +0 -3
  1701. /package/template/.agents/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1702. /package/template/.agents/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1703. /package/template/.agents/skills/{yss-source-index → yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1704. /package/template/.claude/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1705. /package/template/.claude/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1706. /package/template/.claude/skills/{yss-source-index → yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1707. /package/template/.codex/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1708. /package/template/.codex/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1709. /package/template/.codex/skills/{yss-source-index → yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1710. /package/template/.cursor/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1711. /package/template/.cursor/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1712. /package/template/.cursor/skills/{yss-source-index → yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1713. /package/template/{.hermes/skills/yss-source-index → .pi/skills/yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1714. /package/template/{.hermes/skills/yss-source-index → .pi/skills/yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1715. /package/template/{.hermes/skills/yss-source-index → .pi/skills/yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1716. /package/template/{.pi/skills/yss-source-index → .qoder/skills/yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1717. /package/template/{.pi/skills/yss-source-index → .qoder/skills/yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1718. /package/template/{.pi/skills/yss-source-index → .qoder/skills/yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1719. /package/template/{.qoder/skills/yss-source-index → .trae/skills/yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1720. /package/template/{.qoder/skills/yss-source-index → .trae/skills/yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1721. /package/template/{.qoder/skills/yss-source-index → .trae/skills/yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
@@ -1,3317 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import crypto from 'node:crypto';
3
- import fs from 'node:fs';
4
- import fsPromises from 'node:fs/promises';
5
- import os from 'node:os';
6
- import path from 'node:path';
7
- import { spawnSync } from 'node:child_process';
8
- import test from 'node:test';
9
- import { fileURLToPath, pathToFileURL } from 'node:url';
10
-
11
- import {
12
- acknowledgeUpdate,
13
- checkForUpdate,
14
- } from '../scripts/check-update.mjs';
15
- import { DEFAULT_MANIFEST_URL, compareSemver, parseSemver } from '../scripts/update-contract.mjs';
16
-
17
- const here = path.dirname(fileURLToPath(import.meta.url));
18
- const skillRoot = path.resolve(here, '..');
19
- const checkerPath = path.join(skillRoot, 'scripts', 'check-update.mjs');
20
- const contractPath = path.join(skillRoot, 'scripts', 'update-contract.mjs');
21
- const expectedRepository = 'https://github.com/tt-a1i/archify';
22
- const expectedManifestUrl = 'https://tt-a1i.github.io/archify/skill-updates/archify/stable.json';
23
- const baseTime = Date.parse('2026-08-28T08:00:00Z');
24
- const childCheckTimeoutMs = 2_000;
25
- const parentCheckTimeoutMs = 5_000;
26
- const maxCacheStateBytes = 64 * 1_024;
27
-
28
- function writeJson(target, value) {
29
- fs.mkdirSync(path.dirname(target), { recursive: true });
30
- fs.writeFileSync(target, `${JSON.stringify(value, null, 2)}\n`);
31
- }
32
-
33
- function compactStateSource(state) {
34
- return `${JSON.stringify(state)}\n`;
35
- }
36
-
37
- function metadataWithOverrides(metadata, overrides) {
38
- return new Proxy(metadata, {
39
- get(target, property) {
40
- if (Object.hasOwn(overrides, property)) return overrides[property];
41
- const value = Reflect.get(target, property, target);
42
- return typeof value === 'function' ? value.bind(target) : value;
43
- },
44
- });
45
- }
46
-
47
- function historyDigest(index) {
48
- return `sha256:${crypto.createHash('sha256').update(`history-${index}`).digest('hex')}`;
49
- }
50
-
51
- function cachedStateWithHistory({ offeredDigests = [], acknowledgedDigests = [] } = {}) {
52
- return {
53
- schemaVersion: 1,
54
- skillId: 'archify',
55
- installedVersion: '2.15.0',
56
- check: {
57
- nextCheckAt: '2020-01-01T00:00:00.000Z',
58
- consecutiveFailures: 0,
59
- },
60
- notification: {
61
- offeredDigests: [...offeredDigests],
62
- acknowledgedDigests: [...acknowledgedDigests],
63
- },
64
- };
65
- }
66
-
67
- function candidateStateForDigest(state, digest) {
68
- return {
69
- ...state,
70
- check: {
71
- nextCheckAt: new Date(baseTime + (72 * 60 * 60 * 1_000)).toISOString(),
72
- consecutiveFailures: 0,
73
- },
74
- notification: {
75
- offeredDigests: [...state.notification.offeredDigests, digest],
76
- acknowledgedDigests: [...state.notification.acknowledgedDigests],
77
- },
78
- candidate: {
79
- version: '2.16.0',
80
- targetDigest: digest,
81
- severity: 'normal',
82
- releaseNotes: 'https://github.com/tt-a1i/archify/releases/tag/v2.16.0',
83
- },
84
- };
85
- }
86
-
87
- function writeCompactCommittedState(testFixture, state, generation = 1n) {
88
- const target = path.join(operationPath(testFixture, 'committed', generation), 'state.json');
89
- fs.mkdirSync(path.dirname(target), { recursive: true });
90
- fs.writeFileSync(target, compactStateSource(state));
91
- return target;
92
- }
93
-
94
- function committedStateFiles(testFixture) {
95
- return fs.readdirSync(stateDirectory(testFixture), { withFileTypes: true })
96
- .filter((entry) => entry.isDirectory() && /^committed-\d+$/.test(entry.name))
97
- .map((entry) => path.join(stateDirectory(testFixture), entry.name, 'state.json'));
98
- }
99
-
100
- function localRelease(version = '2.15.0') {
101
- return {
102
- schemaVersion: 1,
103
- skillId: 'archify',
104
- channel: version.includes('-') ? 'development' : 'stable',
105
- version,
106
- source: { repository: expectedRepository },
107
- updateManifestUrl: expectedManifestUrl,
108
- };
109
- }
110
-
111
- function remoteRelease(overrides = {}) {
112
- return {
113
- schemaVersion: 1,
114
- skillId: 'archify',
115
- channel: 'stable',
116
- version: '2.16.0',
117
- publishedAt: '2026-08-28T07:00:00Z',
118
- source: {
119
- repository: expectedRepository,
120
- ref: 'v2.16.0',
121
- treeSha: 'a'.repeat(40),
122
- },
123
- artifact: {
124
- sha256: 'b'.repeat(64),
125
- },
126
- summary: 'Improve large-repository scanning and diagram layout.',
127
- releaseNotes: 'https://github.com/tt-a1i/archify/releases/tag/v2.16.0',
128
- severity: 'normal',
129
- ...overrides,
130
- };
131
- }
132
-
133
- function remoteReleaseForVersion(version, digest = 'b'.repeat(64)) {
134
- const release = remoteRelease();
135
- return {
136
- ...release,
137
- version,
138
- source: { ...release.source, ref: `v${version}` },
139
- artifact: { sha256: digest },
140
- releaseNotes: `https://github.com/tt-a1i/archify/releases/tag/v${version}`,
141
- };
142
- }
143
-
144
- function response(body, { status = 200, etag = '"archify-2.16.0"' } = {}) {
145
- return new Response(typeof body === 'string' ? body : JSON.stringify(body), {
146
- status,
147
- headers: {
148
- 'content-type': 'application/json',
149
- etag,
150
- },
151
- });
152
- }
153
-
154
- function fixture(version = '2.15.0') {
155
- const root = fs.realpathSync(fs.mkdtempSync(
156
- path.join(os.tmpdir(), 'archify-update-notifier-'),
157
- ));
158
- const releasePath = path.join(root, 'skill-release.json');
159
- const cacheDirectory = path.join(root, 'cache');
160
- writeJson(releasePath, localRelease(version));
161
- return { root, releasePath, cacheDirectory };
162
- }
163
-
164
- function stateDirectory(testFixture, version = '2.15.0') {
165
- const partition = crypto.createHash('sha256').update(version).digest('hex').slice(0, 24);
166
- return path.join(testFixture.cacheDirectory, `version-${partition}`);
167
- }
168
-
169
- function statePath(testFixture, version = '2.15.0') {
170
- const directory = stateDirectory(testFixture, version);
171
- let committed = [];
172
- try {
173
- committed = fs.readdirSync(directory)
174
- .filter((name) => /^committed-\d+$/.test(name))
175
- .sort((left, right) => {
176
- const leftGeneration = BigInt(left.slice('committed-'.length));
177
- const rightGeneration = BigInt(right.slice('committed-'.length));
178
- return leftGeneration > rightGeneration ? -1 : 1;
179
- });
180
- } catch {
181
- // A seed may be written before the cache partition exists.
182
- }
183
- return committed[0]
184
- ? path.join(directory, committed[0], 'state.json')
185
- : path.join(operationPath(testFixture, 'committed', 1n, version), 'state.json');
186
- }
187
-
188
- function operationPath(testFixture, kind, generation, version = '2.15.0') {
189
- return path.join(
190
- stateDirectory(testFixture, version),
191
- `${kind}-${BigInt(generation).toString().padStart(20, '0')}`,
192
- );
193
- }
194
-
195
- function writePendingOperation(testFixture, {
196
- generation = 1n,
197
- owner = { pid: process.pid, token: 'e'.repeat(32) },
198
- modifiedAt = new Date(),
199
- } = {}) {
200
- const directory = operationPath(testFixture, 'pending', generation);
201
- fs.mkdirSync(directory, { recursive: true });
202
- const ownerPath = path.join(directory, 'owner.json');
203
- fs.writeFileSync(ownerPath, typeof owner === 'string' ? owner : `${JSON.stringify(owner)}\n`);
204
- fs.utimesSync(ownerPath, modifiedAt, modifiedAt);
205
- return directory;
206
- }
207
-
208
- function pauseMkdirOnce(targetPath) {
209
- const originalMkdir = fsPromises.mkdir;
210
- let release;
211
- let markReached;
212
- let intercepted = false;
213
- const reached = new Promise((resolve) => { markReached = resolve; });
214
- const gate = new Promise((resolve) => { release = resolve; });
215
- fsPromises.mkdir = async (target, ...args) => {
216
- if (!intercepted && path.resolve(target) === path.resolve(targetPath)) {
217
- intercepted = true;
218
- markReached();
219
- await gate;
220
- }
221
- return originalMkdir(target, ...args);
222
- };
223
- return {
224
- reached,
225
- release,
226
- restore() {
227
- release();
228
- fsPromises.mkdir = originalMkdir;
229
- },
230
- };
231
- }
232
-
233
- function pauseReaddirSnapshot(targetDirectory, callNumber) {
234
- const originalReaddir = fsPromises.readdir;
235
- let release;
236
- let markReached;
237
- let calls = 0;
238
- const reached = new Promise((resolve) => { markReached = resolve; });
239
- const gate = new Promise((resolve) => { release = resolve; });
240
- fsPromises.readdir = async (target, ...args) => {
241
- const result = await originalReaddir(target, ...args);
242
- if (path.resolve(target) === path.resolve(targetDirectory) && ++calls === callNumber) {
243
- markReached();
244
- await gate;
245
- }
246
- return result;
247
- };
248
- return {
249
- reached,
250
- release,
251
- restore() {
252
- release();
253
- fsPromises.readdir = originalReaddir;
254
- },
255
- };
256
- }
257
-
258
- function pauseReaddirSnapshotOnce(targetDirectory, predicate) {
259
- const originalReaddir = fsPromises.readdir;
260
- let release;
261
- let markReached;
262
- let intercepted = false;
263
- const reached = new Promise((resolve) => { markReached = resolve; });
264
- const gate = new Promise((resolve) => { release = resolve; });
265
- fsPromises.readdir = async (target, ...args) => {
266
- const result = await originalReaddir(target, ...args);
267
- if (!intercepted
268
- && path.resolve(target) === path.resolve(targetDirectory)
269
- && predicate(result)) {
270
- intercepted = true;
271
- markReached();
272
- await gate;
273
- }
274
- return result;
275
- };
276
- return {
277
- reached,
278
- release,
279
- restore() {
280
- release();
281
- fsPromises.readdir = originalReaddir;
282
- },
283
- };
284
- }
285
-
286
- function pauseOpenOnce(predicate) {
287
- const originalOpen = fsPromises.open;
288
- let release;
289
- let markReached;
290
- let intercepted = false;
291
- const reached = new Promise((resolve) => { markReached = resolve; });
292
- const gate = new Promise((resolve) => { release = resolve; });
293
- fsPromises.open = async (target, ...args) => {
294
- if (!intercepted && predicate(target)) {
295
- intercepted = true;
296
- markReached();
297
- await gate;
298
- }
299
- return originalOpen(target, ...args);
300
- };
301
- return {
302
- reached,
303
- release,
304
- restore() {
305
- release();
306
- fsPromises.open = originalOpen;
307
- },
308
- };
309
- }
310
-
311
- function pauseWriteOnce(basename) {
312
- return pauseOpenOnce((target) => path.basename(target) === basename);
313
- }
314
-
315
- function pauseRenameMatchingOnce(predicate) {
316
- const originalRename = fsPromises.rename;
317
- let release;
318
- let markReached;
319
- let intercepted = false;
320
- const reached = new Promise((resolve) => { markReached = resolve; });
321
- const gate = new Promise((resolve) => { release = resolve; });
322
- fsPromises.rename = async (source, destination, ...args) => {
323
- if (!intercepted && predicate(source, destination)) {
324
- intercepted = true;
325
- markReached();
326
- await gate;
327
- }
328
- return originalRename(source, destination, ...args);
329
- };
330
- return {
331
- reached,
332
- release,
333
- restore() {
334
- release();
335
- fsPromises.rename = originalRename;
336
- },
337
- };
338
- }
339
-
340
- function pauseRenameOnce(sourcePath, destinationPath) {
341
- return pauseRenameMatchingOnce((source, destination) => (
342
- path.resolve(source) === path.resolve(sourcePath)
343
- && path.resolve(destination) === path.resolve(destinationPath)
344
- ));
345
- }
346
-
347
- function options(testFixture, fetchImpl, overrides = {}) {
348
- return {
349
- releasePath: testFixture.releasePath,
350
- cacheDirectory: testFixture.cacheDirectory,
351
- fetchImpl,
352
- now: () => baseTime,
353
- random: () => 0.5,
354
- timeoutMs: 50,
355
- ...overrides,
356
- };
357
- }
358
-
359
- function cachedUpdateState() {
360
- return {
361
- schemaVersion: 1,
362
- skillId: 'archify',
363
- installedVersion: '2.15.0',
364
- check: {
365
- nextCheckAt: new Date(baseTime + (24 * 60 * 60 * 1_000)).toISOString(),
366
- consecutiveFailures: 0,
367
- },
368
- notification: {
369
- offeredDigests: [`sha256:${'b'.repeat(64)}`],
370
- acknowledgedDigests: [],
371
- },
372
- candidate: {
373
- version: '2.16.0',
374
- targetDigest: `sha256:${'b'.repeat(64)}`,
375
- severity: 'normal',
376
- releaseNotes: 'https://github.com/tt-a1i/archify/releases/tag/v2.16.0',
377
- },
378
- };
379
- }
380
-
381
- function createFifo(target) {
382
- const created = spawnSync('mkfifo', [target], { encoding: 'utf8' });
383
- if (created.error?.code === 'ENOENT') return false;
384
- assert.equal(created.status, 0, created.stderr || created.error?.message);
385
- return true;
386
- }
387
-
388
- function runCheckInChild(testFixture) {
389
- const manifest = remoteReleaseForVersion('2.15.0', 'd'.repeat(64));
390
- const childSource = `
391
- import { checkForUpdate } from ${JSON.stringify(pathToFileURL(checkerPath).href)};
392
- const watchdog = setTimeout(() => {
393
- process.stderr.write('child update check timed out\\n');
394
- process.exit(124);
395
- }, ${childCheckTimeoutMs});
396
- let requests = 0;
397
- try {
398
- const result = await checkForUpdate({
399
- releasePath: ${JSON.stringify(testFixture.releasePath)},
400
- cacheDirectory: ${JSON.stringify(testFixture.cacheDirectory)},
401
- fetchImpl: async () => {
402
- requests += 1;
403
- return new Response(${JSON.stringify(JSON.stringify(manifest))}, {
404
- status: 200,
405
- headers: { 'content-type': 'application/json' },
406
- });
407
- },
408
- now: () => ${baseTime},
409
- random: () => 0.5,
410
- timeoutMs: 50,
411
- });
412
- process.stdout.write(JSON.stringify({ result, requests }));
413
- } finally {
414
- clearTimeout(watchdog);
415
- }
416
- `;
417
- const child = spawnSync(process.execPath, ['--input-type=module', '--eval', childSource], {
418
- cwd: skillRoot,
419
- encoding: 'utf8',
420
- timeout: parentCheckTimeoutMs,
421
- });
422
- const diagnostics = [child.error?.stack, child.stderr, child.stdout].filter(Boolean).join('\n');
423
- assert.equal(child.error, undefined, diagnostics);
424
- assert.equal(child.status, 0, diagnostics);
425
- return JSON.parse(child.stdout);
426
- }
427
-
428
- function assertUnsafeCacheStateIsIgnored(testFixture) {
429
- assert.deepEqual(runCheckInChild(testFixture), {
430
- result: { status: 'silent', reason: 'current' },
431
- requests: 1,
432
- });
433
- }
434
-
435
- test('production manifest URL is a fixed trusted GitHub Pages resource', () => {
436
- assert.equal(DEFAULT_MANIFEST_URL, expectedManifestUrl);
437
- const local = JSON.parse(fs.readFileSync(path.join(skillRoot, 'skill-release.json'), 'utf8'));
438
- assert.equal(local.updateManifestUrl, expectedManifestUrl);
439
- assert.equal(local.source.repository, expectedRepository);
440
- });
441
-
442
- test('SemVer comparison handles stable, prerelease, and downgrade ordering', () => {
443
- assert.equal(compareSemver('2.16.0', '2.15.0'), 1);
444
- assert.equal(compareSemver('2.16.0-dev.0', '2.15.0'), 1);
445
- assert.equal(compareSemver('2.16.0', '2.16.0-dev.9'), 1);
446
- assert.equal(compareSemver('2.16.0-dev.2', '2.16.0-dev.10'), -1);
447
- assert.equal(compareSemver('2.16.0', '2.16.0'), 0);
448
- assert.equal(compareSemver('2.16.0+build.9', '2.16.0+build.1'), 0);
449
- assert.equal(compareSemver('9007199254740993.0.0', '9007199254740992.0.0'), 1);
450
- assert.equal(compareSemver('2.16.0-dev.9007199254740993', '2.16.0-dev.9007199254740992'), 1);
451
- assert.throws(() => compareSemver('2.16.0-dev.01', '2.16.0'));
452
- });
453
-
454
- test('development installs never treat the older stable release as an update', async (t) => {
455
- const testFixture = fixture('2.16.0-dev.0');
456
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
457
-
458
- const result = await checkForUpdate(options(
459
- testFixture,
460
- async () => response(remoteRelease({
461
- version: '2.15.0',
462
- source: {
463
- repository: expectedRepository,
464
- ref: 'v2.15.0',
465
- treeSha: 'c'.repeat(40),
466
- },
467
- artifact: { sha256: 'd'.repeat(64) },
468
- releaseNotes: 'https://github.com/tt-a1i/archify/releases/tag/v2.15.0',
469
- })),
470
- ));
471
-
472
- assert.deepEqual(result, { status: 'silent', reason: 'current' });
473
- });
474
-
475
- test('a changed digest never bypasses same-version or downgrade protection', async () => {
476
- for (const [version, digest] of [
477
- ['2.15.0', 'c'.repeat(64)],
478
- ['2.14.9', 'd'.repeat(64)],
479
- ]) {
480
- const testFixture = fixture();
481
- try {
482
- const result = await checkForUpdate(options(
483
- testFixture,
484
- async () => response(remoteReleaseForVersion(version, digest)),
485
- ));
486
- assert.deepEqual(result, { status: 'silent', reason: 'current' });
487
- } finally {
488
- fs.rmSync(testFixture.root, { recursive: true, force: true });
489
- }
490
- }
491
- });
492
-
493
- test('a successful refresh withdraws a previously offered higher candidate', async (t) => {
494
- const testFixture = fixture();
495
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
496
- let requests = 0;
497
- const fetchImpl = async () => {
498
- requests += 1;
499
- return response(requests === 1
500
- ? remoteReleaseForVersion('3.0.0', 'c'.repeat(64))
501
- : remoteReleaseForVersion('2.15.0', 'd'.repeat(64)));
502
- };
503
-
504
- const offered = await checkForUpdate(options(testFixture, fetchImpl));
505
- assert.equal(offered.status, 'update_available');
506
- assert.equal(offered.latestVersion, '3.0.0');
507
-
508
- const withdrawn = await checkForUpdate(options(testFixture, fetchImpl, {
509
- now: () => baseTime + (73 * 60 * 60 * 1_000),
510
- }));
511
- assert.deepEqual(withdrawn, { status: 'silent', reason: 'current' });
512
- assert.equal(JSON.parse(fs.readFileSync(statePath(testFixture), 'utf8')).candidate.version, '2.15.0');
513
- assert.equal(requests, 2);
514
- });
515
-
516
- test('a newer immutable candidate is re-offered until the visible notice is acknowledged', async (t) => {
517
- const testFixture = fixture();
518
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
519
- let requests = 0;
520
- const fetchImpl = async () => {
521
- requests += 1;
522
- return response(remoteRelease());
523
- };
524
-
525
- const first = await checkForUpdate(options(testFixture, fetchImpl));
526
- assert.equal(first.status, 'update_available');
527
- assert.equal(first.installedVersion, '2.15.0');
528
- assert.equal(first.latestVersion, '2.16.0');
529
- assert.equal(first.eventKey, `archify@sha256:${'b'.repeat(64)}`);
530
- assert.equal(first.targetDigest, `sha256:${'b'.repeat(64)}`);
531
- assert.equal(Object.hasOwn(first, 'updateCommand'), false);
532
- assert.equal(first.summary, 'Archify 2.16.0 is available; see the official release notes for details.');
533
- const persisted = JSON.parse(fs.readFileSync(statePath(testFixture), 'utf8'));
534
- assert.deepEqual(Object.keys(persisted.check).sort(), [
535
- 'consecutiveFailures', 'nextCheckAt',
536
- ]);
537
- assert.deepEqual(Object.keys(persisted.notification).sort(), [
538
- 'acknowledgedDigests', 'offeredDigests',
539
- ]);
540
- assert.deepEqual(Object.keys(persisted.candidate).sort(), [
541
- 'releaseNotes', 'severity', 'targetDigest', 'version',
542
- ]);
543
-
544
- const second = await checkForUpdate(options(testFixture, fetchImpl));
545
- assert.equal(second.status, 'update_available');
546
- assert.equal(second.eventKey, first.eventKey);
547
- assert.equal(requests, 1, 'fresh cached candidates must not make another request');
548
-
549
- const acknowledgement = await acknowledgeUpdate({
550
- releasePath: testFixture.releasePath,
551
- cacheDirectory: testFixture.cacheDirectory,
552
- eventKey: first.eventKey,
553
- now: () => baseTime + 1_000,
554
- });
555
- assert.deepEqual(acknowledgement, { status: 'acknowledged', eventKey: first.eventKey });
556
- assert.deepEqual(
557
- JSON.parse(fs.readFileSync(statePath(testFixture), 'utf8')).notification.acknowledgedDigests,
558
- [first.targetDigest],
559
- );
560
-
561
- const third = await checkForUpdate(options(testFixture, fetchImpl));
562
- assert.deepEqual(third, { status: 'silent', reason: 'already-notified' });
563
- assert.equal(requests, 1);
564
- });
565
-
566
- test('an acknowledged candidate stays suppressed after a later candidate and manifest rollback', async (t) => {
567
- const testFixture = fixture();
568
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
569
- const releases = [
570
- remoteReleaseForVersion('2.16.0', 'b'.repeat(64)),
571
- remoteReleaseForVersion('2.17.0', 'c'.repeat(64)),
572
- remoteReleaseForVersion('2.16.0', 'b'.repeat(64)),
573
- ];
574
- let requests = 0;
575
- const fetchImpl = async () => response(releases[requests++]);
576
-
577
- const first = await checkForUpdate(options(testFixture, fetchImpl));
578
- assert.equal(first.status, 'update_available');
579
- assert.deepEqual(await acknowledgeUpdate({
580
- releasePath: testFixture.releasePath,
581
- cacheDirectory: testFixture.cacheDirectory,
582
- eventKey: first.eventKey,
583
- }), { status: 'acknowledged', eventKey: first.eventKey });
584
-
585
- const second = await checkForUpdate(options(testFixture, fetchImpl, {
586
- now: () => baseTime + (73 * 60 * 60 * 1_000),
587
- }));
588
- assert.equal(second.status, 'update_available');
589
- assert.deepEqual(await acknowledgeUpdate({
590
- releasePath: testFixture.releasePath,
591
- cacheDirectory: testFixture.cacheDirectory,
592
- eventKey: second.eventKey,
593
- }), { status: 'acknowledged', eventKey: second.eventKey });
594
-
595
- assert.deepEqual(await checkForUpdate(options(testFixture, fetchImpl, {
596
- now: () => baseTime + (146 * 60 * 60 * 1_000),
597
- })), { status: 'silent', reason: 'already-notified' });
598
- assert.equal(requests, 3);
599
- assert.deepEqual(
600
- JSON.parse(fs.readFileSync(statePath(testFixture), 'utf8')).notification.acknowledgedDigests,
601
- [first.targetDigest, second.targetDigest],
602
- );
603
- });
604
-
605
- test('a 64 KiB multi-offer cache only returns an event whose acknowledgement closure can commit', async (t) => {
606
- const testFixture = fixture();
607
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
608
- const earlierDigest = `sha256:${'a'.repeat(64)}`;
609
- const currentDigest = `sha256:${'b'.repeat(64)}`;
610
- const replacementDigest = `sha256:${'c'.repeat(64)}`;
611
- const cachedVersion = `3.16.${'9'.repeat(35)}`;
612
- const state = cachedStateWithHistory({
613
- offeredDigests: [earlierDigest, currentDigest],
614
- acknowledgedDigests: Array.from({ length: 877 }, (_, index) => historyDigest(index)),
615
- });
616
- state.check.nextCheckAt = 'Mon, 31 Aug 2026 08:00:00 GMT';
617
- state.candidate = {
618
- version: cachedVersion,
619
- targetDigest: currentDigest,
620
- severity: 'normal',
621
- releaseNotes: `https://github.com/tt-a1i/archify/releases/tag/v${cachedVersion}`,
622
- };
623
- assert.equal(Buffer.byteLength(compactStateSource(state)), maxCacheStateBytes);
624
- writeCompactCommittedState(testFixture, state);
625
- let requests = 0;
626
-
627
- const offered = await checkForUpdate(options(testFixture, async () => {
628
- requests += 1;
629
- return response(remoteReleaseForVersion('2.16.0', 'c'.repeat(64)));
630
- }));
631
- assert.equal(offered.status, 'update_available');
632
- const acknowledgement = await acknowledgeUpdate({
633
- releasePath: testFixture.releasePath,
634
- cacheDirectory: testFixture.cacheDirectory,
635
- eventKey: offered.eventKey,
636
- });
637
-
638
- assert.deepEqual(acknowledgement, {
639
- status: 'acknowledged',
640
- eventKey: offered.eventKey,
641
- });
642
- assert.equal(requests, 1, 'an unrecoverable cached offer must be rebuilt before exposure');
643
- assert.equal(offered.targetDigest, replacementDigest);
644
- });
645
-
646
- test('a recoverable multi-offer boundary acknowledges every event without pruning history', async (t) => {
647
- const testFixture = fixture();
648
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
649
- const earlierDigest = `sha256:${'a'.repeat(64)}`;
650
- const currentDigest = `sha256:${'b'.repeat(64)}`;
651
- const cachedVersion = `3.16.${'9'.repeat(35)}`;
652
- const exactHistory = Array.from({ length: 877 }, (_, index) => historyDigest(index));
653
- const state = cachedStateWithHistory({
654
- offeredDigests: [earlierDigest, currentDigest],
655
- acknowledgedDigests: exactHistory,
656
- });
657
- state.check.nextCheckAt = 'Mon, 31 Aug 2026 8:00:00 GMT';
658
- state.candidate = {
659
- version: cachedVersion,
660
- targetDigest: currentDigest,
661
- severity: 'normal',
662
- releaseNotes: `https://github.com/tt-a1i/archify/releases/tag/v${cachedVersion}`,
663
- };
664
- assert.equal(Buffer.byteLength(compactStateSource(state)), maxCacheStateBytes - 1);
665
- writeCompactCommittedState(testFixture, state);
666
-
667
- const current = await checkForUpdate(options(testFixture, async () => {
668
- throw new Error('a fresh recoverable cache must not use the network');
669
- }));
670
- assert.equal(current.targetDigest, currentDigest);
671
- assert.deepEqual(await acknowledgeUpdate({
672
- releasePath: testFixture.releasePath,
673
- cacheDirectory: testFixture.cacheDirectory,
674
- eventKey: current.eventKey,
675
- }), { status: 'acknowledged', eventKey: current.eventKey });
676
- const earlierEventKey = `archify@${earlierDigest}`;
677
- assert.deepEqual(await acknowledgeUpdate({
678
- releasePath: testFixture.releasePath,
679
- cacheDirectory: testFixture.cacheDirectory,
680
- eventKey: earlierEventKey,
681
- }), { status: 'acknowledged', eventKey: earlierEventKey });
682
-
683
- const persisted = JSON.parse(fs.readFileSync(statePath(testFixture), 'utf8'));
684
- assert.deepEqual(persisted.notification, {
685
- offeredDigests: [],
686
- acknowledgedDigests: [...exactHistory, currentDigest, earlierDigest],
687
- });
688
- assert.equal(fs.statSync(statePath(testFixture)).size, maxCacheStateBytes);
689
- });
690
-
691
- test('a near-capacity null schedule does not retry the network on every activation', async (t) => {
692
- const testFixture = fixture();
693
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
694
- const state = cachedStateWithHistory({
695
- acknowledgedDigests: Array.from({ length: 883 }, (_, index) => historyDigest(index)),
696
- });
697
- state.check.nextCheckAt = null;
698
- assert.equal(Buffer.byteLength(compactStateSource(state)), 65_524);
699
- writeCompactCommittedState(testFixture, state);
700
- let requests = 0;
701
- const fetchImpl = async () => {
702
- requests += 1;
703
- return response(remoteRelease());
704
- };
705
-
706
- const first = await checkForUpdate(options(testFixture, fetchImpl));
707
- const second = await checkForUpdate(options(testFixture, fetchImpl));
708
-
709
- assert.equal(first.status, 'update_available');
710
- assert.deepEqual(second, first);
711
- assert.equal(requests, 1, 'the first check must commit a bounded retry or reusable candidate');
712
- });
713
-
714
- test('a saturated exact acknowledgement history never returns an unacknowledgeable offer', async (t) => {
715
- const testFixture = fixture();
716
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
717
- const targetDigest = `sha256:${'f'.repeat(64)}`;
718
- const state = cachedStateWithHistory();
719
- let projected = candidateStateForDigest(state, targetDigest);
720
- let index = 0;
721
- while (Buffer.byteLength(compactStateSource(projected)) <= maxCacheStateBytes) {
722
- state.notification.acknowledgedDigests.push(historyDigest(index));
723
- index += 1;
724
- projected = candidateStateForDigest(state, targetDigest);
725
- }
726
- assert.ok(Buffer.byteLength(compactStateSource(state)) <= maxCacheStateBytes);
727
- assert.ok(Buffer.byteLength(compactStateSource(projected)) > maxCacheStateBytes);
728
- const exactHistory = [...state.notification.acknowledgedDigests];
729
- writeCompactCommittedState(testFixture, state);
730
- let requests = 0;
731
-
732
- const result = await checkForUpdate(options(testFixture, async () => {
733
- requests += 1;
734
- return response(remoteReleaseForVersion('2.16.0', 'f'.repeat(64)));
735
- }));
736
- const acknowledgement = result.status === 'update_available'
737
- ? await acknowledgeUpdate({
738
- releasePath: testFixture.releasePath,
739
- cacheDirectory: testFixture.cacheDirectory,
740
- eventKey: result.eventKey,
741
- })
742
- : null;
743
-
744
- assert.deepEqual({ result, acknowledgement }, {
745
- result: { status: 'silent', reason: 'cache-unavailable' },
746
- acknowledgement: null,
747
- });
748
- assert.equal(requests, 1);
749
- assert.ok(committedStateFiles(testFixture).every(
750
- (target) => fs.statSync(target).size <= maxCacheStateBytes,
751
- ));
752
- assert.deepEqual(
753
- JSON.parse(fs.readFileSync(statePath(testFixture), 'utf8')).notification.acknowledgedDigests,
754
- exactHistory,
755
- );
756
-
757
- assert.deepEqual(await checkForUpdate(options(testFixture, async () => {
758
- requests += 1;
759
- return response(remoteReleaseForVersion('2.16.0', 'f'.repeat(64)));
760
- })), { status: 'silent', reason: 'cache-valid' });
761
- assert.equal(requests, 1, 'capacity rejection should commit a bounded retry delay');
762
- });
763
-
764
- test('capacity backoff withdraws a stale candidate while preserving its late acknowledgement', async (t) => {
765
- const testFixture = fixture();
766
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
767
- const staleDigest = `sha256:${'b'.repeat(64)}`;
768
- const replacementDigest = `sha256:${'c'.repeat(64)}`;
769
- const state = cachedStateWithHistory({ offeredDigests: [staleDigest] });
770
- state.candidate = {
771
- version: '2.16.0',
772
- targetDigest: staleDigest,
773
- severity: 'normal',
774
- releaseNotes: 'https://github.com/tt-a1i/archify/releases/tag/v2.16.0',
775
- };
776
- let replacement = candidateStateForDigest(state, replacementDigest);
777
- let index = 0;
778
- while (Buffer.byteLength(compactStateSource(replacement)) <= maxCacheStateBytes) {
779
- state.notification.acknowledgedDigests.push(historyDigest(index));
780
- index += 1;
781
- replacement = candidateStateForDigest(state, replacementDigest);
782
- }
783
- assert.ok(Buffer.byteLength(compactStateSource(state)) <= maxCacheStateBytes);
784
- assert.ok(Buffer.byteLength(compactStateSource(replacement)) > maxCacheStateBytes);
785
- const exactHistory = [...state.notification.acknowledgedDigests];
786
- writeCompactCommittedState(testFixture, state);
787
- let requests = 0;
788
- const fetchImpl = async () => {
789
- requests += 1;
790
- return response(remoteReleaseForVersion('2.16.0', 'c'.repeat(64)));
791
- };
792
-
793
- const refresh = await checkForUpdate(options(testFixture, fetchImpl));
794
- const cached = await checkForUpdate(options(testFixture, fetchImpl));
795
- const staleEventKey = `archify@${staleDigest}`;
796
- const lateAcknowledgement = await acknowledgeUpdate({
797
- releasePath: testFixture.releasePath,
798
- cacheDirectory: testFixture.cacheDirectory,
799
- eventKey: staleEventKey,
800
- });
801
-
802
- assert.deepEqual({ refresh, cached, lateAcknowledgement }, {
803
- refresh: { status: 'silent', reason: 'cache-unavailable' },
804
- cached: { status: 'silent', reason: 'cache-valid' },
805
- lateAcknowledgement: { status: 'acknowledged', eventKey: staleEventKey },
806
- });
807
- assert.equal(requests, 1);
808
- assert.ok(committedStateFiles(testFixture).every(
809
- (target) => fs.statSync(target).size <= maxCacheStateBytes,
810
- ));
811
- const persisted = JSON.parse(fs.readFileSync(statePath(testFixture), 'utf8'));
812
- assert.equal(Object.hasOwn(persisted, 'candidate'), false);
813
- assert.deepEqual(persisted.notification, {
814
- offeredDigests: [],
815
- acknowledgedDigests: [...exactHistory, staleDigest],
816
- });
817
- });
818
-
819
- test('a 64 KiB state can be acknowledged but a 64 KiB plus one state is ignored', async () => {
820
- const targetDigest = `sha256:${'e'.repeat(64)}`;
821
- const exactState = cachedStateWithHistory({ offeredDigests: [targetDigest] });
822
- exactState.candidate = {
823
- version: '2.16.0',
824
- targetDigest,
825
- severity: 'normal',
826
- releaseNotes: 'https://github.com/tt-a1i/archify/releases/tag/v2.16.0',
827
- };
828
- const initialBytes = Buffer.byteLength(compactStateSource(exactState));
829
- exactState.check.nextCheckAt += 'x'.repeat(maxCacheStateBytes - initialBytes);
830
- assert.equal(Buffer.byteLength(compactStateSource(exactState)), maxCacheStateBytes);
831
-
832
- const exactFixture = fixture();
833
- try {
834
- writeCompactCommittedState(exactFixture, exactState);
835
- const eventKey = `archify@${targetDigest}`;
836
- assert.deepEqual(await acknowledgeUpdate({
837
- releasePath: exactFixture.releasePath,
838
- cacheDirectory: exactFixture.cacheDirectory,
839
- eventKey,
840
- }), { status: 'acknowledged', eventKey });
841
- assert.equal(fs.statSync(statePath(exactFixture)).size, maxCacheStateBytes);
842
- assert.deepEqual(
843
- JSON.parse(fs.readFileSync(statePath(exactFixture), 'utf8')).notification,
844
- { offeredDigests: [], acknowledgedDigests: [targetDigest] },
845
- );
846
- } finally {
847
- fs.rmSync(exactFixture.root, { recursive: true, force: true });
848
- }
849
-
850
- const oversizedFixture = fixture();
851
- try {
852
- const target = writeCompactCommittedState(oversizedFixture, exactState);
853
- fs.appendFileSync(target, ' ');
854
- assert.equal(fs.statSync(target).size, maxCacheStateBytes + 1);
855
- assert.deepEqual(await acknowledgeUpdate({
856
- releasePath: oversizedFixture.releasePath,
857
- cacheDirectory: oversizedFixture.cacheDirectory,
858
- eventKey: `archify@${targetDigest}`,
859
- }), { status: 'silent', reason: 'invalid-acknowledgement' });
860
- } finally {
861
- fs.rmSync(oversizedFixture.root, { recursive: true, force: true });
862
- }
863
- });
864
-
865
- test('a boundary offer remains acknowledgeable without pruning exact history', async (t) => {
866
- const testFixture = fixture();
867
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
868
- const targetDigest = `sha256:${'d'.repeat(64)}`;
869
- const state = cachedStateWithHistory();
870
- let index = 0;
871
- while (true) {
872
- state.notification.acknowledgedDigests.push(historyDigest(index));
873
- if (Buffer.byteLength(compactStateSource(
874
- candidateStateForDigest(state, targetDigest),
875
- )) > maxCacheStateBytes) {
876
- state.notification.acknowledgedDigests.pop();
877
- break;
878
- }
879
- index += 1;
880
- }
881
- const projected = candidateStateForDigest(state, targetDigest);
882
- assert.ok(Buffer.byteLength(compactStateSource(projected)) <= maxCacheStateBytes);
883
- const oneMoreAcknowledgement = cachedStateWithHistory({
884
- acknowledgedDigests: [...state.notification.acknowledgedDigests, historyDigest(index)],
885
- });
886
- assert.ok(Buffer.byteLength(compactStateSource(
887
- candidateStateForDigest(oneMoreAcknowledgement, targetDigest),
888
- )) > maxCacheStateBytes);
889
- const exactHistory = [...state.notification.acknowledgedDigests];
890
- writeCompactCommittedState(testFixture, state);
891
- const fetchImpl = async () => response(remoteReleaseForVersion('2.16.0', 'd'.repeat(64)));
892
-
893
- const offered = await checkForUpdate(options(testFixture, fetchImpl));
894
- assert.equal(offered.status, 'update_available');
895
- assert.deepEqual(await acknowledgeUpdate({
896
- releasePath: testFixture.releasePath,
897
- cacheDirectory: testFixture.cacheDirectory,
898
- eventKey: offered.eventKey,
899
- }), { status: 'acknowledged', eventKey: offered.eventKey });
900
-
901
- assert.ok(committedStateFiles(testFixture).every(
902
- (target) => fs.statSync(target).size <= maxCacheStateBytes,
903
- ));
904
- assert.deepEqual(
905
- JSON.parse(fs.readFileSync(statePath(testFixture), 'utf8')).notification,
906
- {
907
- offeredDigests: [],
908
- acknowledgedDigests: [...exactHistory, targetDigest],
909
- },
910
- );
911
- assert.deepEqual(await checkForUpdate(options(testFixture, fetchImpl)), {
912
- status: 'silent',
913
- reason: 'already-notified',
914
- });
915
- });
916
-
917
- test('opaque response validators are neither persisted nor replayed after the check TTL', async (t) => {
918
- const testFixture = fixture();
919
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
920
- let requests = 0;
921
- const fetchImpl = async (_url, init) => {
922
- requests += 1;
923
- assert.equal(init.headers['if-none-match'], undefined);
924
- return response(remoteRelease(), { etag: `"per-client-${requests}"` });
925
- };
926
-
927
- const first = await checkForUpdate(options(testFixture, fetchImpl));
928
- assert.equal(first.status, 'update_available');
929
-
930
- const second = await checkForUpdate(options(testFixture, fetchImpl, {
931
- now: () => baseTime + (73 * 60 * 60 * 1_000),
932
- }));
933
- assert.equal(second.status, 'update_available');
934
- assert.equal(second.eventKey, first.eventKey);
935
- assert.equal(requests, 2);
936
- assert.equal(Object.hasOwn(JSON.parse(fs.readFileSync(statePath(testFixture), 'utf8')).check, 'etag'), false);
937
- });
938
-
939
- test('an HTTP 304 is always a failed unconditional refresh', async (t) => {
940
- const testFixture = fixture();
941
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
942
- let requests = 0;
943
- const fetchImpl = async () => {
944
- requests += 1;
945
- if (requests === 1) {
946
- return new Response(JSON.stringify(remoteRelease()), {
947
- status: 200,
948
- headers: { 'content-type': 'application/json' },
949
- });
950
- }
951
- return new Response(null, { status: 304 });
952
- };
953
-
954
- assert.equal((await checkForUpdate(options(testFixture, fetchImpl))).status, 'update_available');
955
- const refresh = await checkForUpdate(options(testFixture, fetchImpl, {
956
- now: () => baseTime + (73 * 60 * 60 * 1_000),
957
- }));
958
-
959
- assert.deepEqual(refresh, { status: 'silent', reason: 'check-failed' });
960
- assert.equal(requests, 2);
961
- });
962
-
963
- test('a failed refresh preserves the last-good unacknowledged candidate', async (t) => {
964
- const testFixture = fixture();
965
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
966
- let requests = 0;
967
- const fetchImpl = async () => {
968
- requests += 1;
969
- if (requests === 1) return response(remoteRelease());
970
- throw new Error('offline');
971
- };
972
-
973
- const first = await checkForUpdate(options(testFixture, fetchImpl));
974
- assert.equal(first.status, 'update_available');
975
- const failed = await checkForUpdate(options(testFixture, fetchImpl, {
976
- now: () => baseTime + (73 * 60 * 60 * 1_000),
977
- }));
978
- assert.deepEqual(failed, { status: 'silent', reason: 'check-failed' });
979
-
980
- const cached = await checkForUpdate(options(testFixture, fetchImpl, {
981
- now: () => baseTime + (74 * 60 * 60 * 1_000),
982
- }));
983
- assert.equal(cached.status, 'update_available');
984
- assert.equal(cached.eventKey, first.eventKey);
985
- assert.equal(requests, 2);
986
- });
987
-
988
- test('failure backoff saturates safely instead of overflowing the cache counter', async (t) => {
989
- const testFixture = fixture();
990
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
991
- writeJson(statePath(testFixture), {
992
- schemaVersion: 1,
993
- skillId: 'archify',
994
- installedVersion: '2.15.0',
995
- check: {
996
- nextCheckAt: new Date(baseTime - 1_000).toISOString(),
997
- consecutiveFailures: Number.MAX_SAFE_INTEGER,
998
- },
999
- notification: {
1000
- offeredDigests: [],
1001
- acknowledgedDigests: [],
1002
- },
1003
- });
1004
- let requests = 0;
1005
- const fetchImpl = async () => {
1006
- requests += 1;
1007
- throw new Error('offline');
1008
- };
1009
-
1010
- assert.deepEqual(await checkForUpdate(options(testFixture, fetchImpl)), {
1011
- status: 'silent',
1012
- reason: 'check-failed',
1013
- });
1014
- assert.deepEqual(await checkForUpdate(options(testFixture, fetchImpl, {
1015
- now: () => baseTime + (60 * 60 * 1_000),
1016
- })), { status: 'silent', reason: 'cache-valid' });
1017
- assert.equal(requests, 1);
1018
- assert.equal(JSON.parse(fs.readFileSync(statePath(testFixture), 'utf8')).check.consecutiveFailures, 2);
1019
- });
1020
-
1021
- test('a committed cache FIFO is ignored without blocking the update check', (t) => {
1022
- if (process.platform === 'win32') {
1023
- t.skip('FIFO files are unavailable on Windows');
1024
- return;
1025
- }
1026
- const testFixture = fixture();
1027
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
1028
- const cacheStatePath = statePath(testFixture);
1029
- fs.mkdirSync(path.dirname(cacheStatePath), { recursive: true });
1030
- if (!createFifo(cacheStatePath)) {
1031
- t.skip('mkfifo is unavailable');
1032
- return;
1033
- }
1034
-
1035
- assertUnsafeCacheStateIsIgnored(testFixture);
1036
- });
1037
-
1038
- test('a committed cache state symlink is ignored without following its target', (t) => {
1039
- const testFixture = fixture();
1040
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
1041
- const cacheStatePath = statePath(testFixture);
1042
- const linkedTarget = path.join(testFixture.root, 'linked-state-target');
1043
- fs.mkdirSync(path.dirname(cacheStatePath), { recursive: true });
1044
- const targetIsFifo = process.platform !== 'win32' && createFifo(linkedTarget);
1045
- if (!targetIsFifo) writeJson(linkedTarget, cachedUpdateState());
1046
- fs.symlinkSync(linkedTarget, cacheStatePath, process.platform === 'win32' ? 'file' : undefined);
1047
-
1048
- assertUnsafeCacheStateIsIgnored(testFixture);
1049
- });
1050
-
1051
- test('an oversized committed cache state is ignored without an unbounded read', (t) => {
1052
- const testFixture = fixture();
1053
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
1054
- const oversizedState = `${JSON.stringify(cachedUpdateState())}${' '.repeat(256 * 1_024)}`;
1055
- const cacheStatePath = statePath(testFixture);
1056
- fs.mkdirSync(path.dirname(cacheStatePath), { recursive: true });
1057
- fs.writeFileSync(cacheStatePath, oversizedState);
1058
-
1059
- assertUnsafeCacheStateIsIgnored(testFixture);
1060
- });
1061
-
1062
- test('a cache ancestor replaced before a cached read cannot inject a reminder', async (t) => {
1063
- const testFixture = fixture();
1064
- const originalReaddir = fsPromises.readdir;
1065
- t.after(() => {
1066
- fsPromises.readdir = originalReaddir;
1067
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1068
- });
1069
- writeCompactCommittedState(testFixture, cachedStateWithHistory());
1070
- const replacementRoot = path.join(testFixture.root, 'replacement-cache');
1071
- writeJson(
1072
- path.join(replacementRoot, path.basename(stateDirectory(testFixture)),
1073
- path.basename(operationPath(testFixture, 'committed', 1n)), 'state.json'),
1074
- cachedUpdateState(),
1075
- );
1076
- const detachedCache = path.join(testFixture.root, 'detached-cache');
1077
- const probe = path.join(testFixture.root, 'symlink-probe');
1078
- try {
1079
- fs.symlinkSync(
1080
- replacementRoot,
1081
- probe,
1082
- process.platform === 'win32' ? 'junction' : 'dir',
1083
- );
1084
- fs.unlinkSync(probe);
1085
- } catch (error) {
1086
- if (['EPERM', 'EACCES', 'ENOTSUP'].includes(error?.code)) {
1087
- t.skip(`directory symlinks are unavailable: ${error.code}`);
1088
- return;
1089
- }
1090
- throw error;
1091
- }
1092
- let replaced = false;
1093
- fsPromises.readdir = async (target, ...args) => {
1094
- if (!replaced && path.resolve(target) === path.resolve(stateDirectory(testFixture))) {
1095
- replaced = true;
1096
- fs.renameSync(testFixture.cacheDirectory, detachedCache);
1097
- fs.symlinkSync(
1098
- replacementRoot,
1099
- testFixture.cacheDirectory,
1100
- process.platform === 'win32' ? 'junction' : 'dir',
1101
- );
1102
- }
1103
- return originalReaddir(target, ...args);
1104
- };
1105
- let requests = 0;
1106
-
1107
- const result = await checkForUpdate(options(testFixture, async () => {
1108
- requests += 1;
1109
- return response(remoteRelease());
1110
- }));
1111
-
1112
- assert.equal(replaced, true);
1113
- assert.deepEqual(result, { status: 'silent', reason: 'cache-unavailable' });
1114
- assert.equal(requests, 0);
1115
- });
1116
-
1117
- test('a committed directory replaced by a symlink cannot inject a reminder', async (t) => {
1118
- const testFixture = fixture();
1119
- const originalReaddir = fsPromises.readdir;
1120
- t.after(() => {
1121
- fsPromises.readdir = originalReaddir;
1122
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1123
- });
1124
- writeCompactCommittedState(testFixture, cachedStateWithHistory());
1125
- const committed = operationPath(testFixture, 'committed', 1n);
1126
- const replacement = path.join(testFixture.root, 'replacement-committed');
1127
- writeJson(path.join(replacement, 'state.json'), cachedUpdateState());
1128
- const probe = path.join(testFixture.root, 'nested-symlink-probe');
1129
- try {
1130
- fs.symlinkSync(
1131
- replacement,
1132
- probe,
1133
- process.platform === 'win32' ? 'junction' : 'dir',
1134
- );
1135
- fs.unlinkSync(probe);
1136
- } catch (error) {
1137
- if (['EPERM', 'EACCES', 'ENOTSUP'].includes(error?.code)) {
1138
- t.skip(`directory symlinks are unavailable: ${error.code}`);
1139
- return;
1140
- }
1141
- throw error;
1142
- }
1143
- const detached = path.join(stateDirectory(testFixture), 'detached-committed');
1144
- let replaced = false;
1145
- fsPromises.readdir = async (target, ...args) => {
1146
- const entries = await originalReaddir(target, ...args);
1147
- if (!replaced && path.resolve(target) === path.resolve(stateDirectory(testFixture))) {
1148
- replaced = true;
1149
- fs.renameSync(committed, detached);
1150
- fs.symlinkSync(
1151
- replacement,
1152
- committed,
1153
- process.platform === 'win32' ? 'junction' : 'dir',
1154
- );
1155
- }
1156
- return entries;
1157
- };
1158
- let requests = 0;
1159
-
1160
- const result = await checkForUpdate(options(testFixture, async () => {
1161
- requests += 1;
1162
- return response(remoteRelease());
1163
- }));
1164
-
1165
- assert.equal(replaced, true);
1166
- assert.deepEqual(result, { status: 'silent', reason: 'cache-unavailable' });
1167
- assert.equal(requests, 0);
1168
- });
1169
-
1170
- test('a pending directory replaced by a symlink cannot be ignored as inactive', async (t) => {
1171
- const testFixture = fixture();
1172
- const originalReaddir = fsPromises.readdir;
1173
- t.after(() => {
1174
- fsPromises.readdir = originalReaddir;
1175
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1176
- });
1177
- const pending = writePendingOperation(testFixture);
1178
- const replacement = path.join(testFixture.root, 'replacement-pending');
1179
- writePendingOperation({
1180
- ...testFixture,
1181
- cacheDirectory: path.join(testFixture.root, 'replacement-cache'),
1182
- }, { modifiedAt: new Date(Date.now() - 60_000) });
1183
- const replacementPending = operationPath({
1184
- ...testFixture,
1185
- cacheDirectory: path.join(testFixture.root, 'replacement-cache'),
1186
- }, 'pending', 1n);
1187
- fs.renameSync(replacementPending, replacement);
1188
- const probe = path.join(testFixture.root, 'pending-symlink-probe');
1189
- try {
1190
- fs.symlinkSync(
1191
- replacement,
1192
- probe,
1193
- process.platform === 'win32' ? 'junction' : 'dir',
1194
- );
1195
- fs.unlinkSync(probe);
1196
- } catch (error) {
1197
- if (['EPERM', 'EACCES', 'ENOTSUP'].includes(error?.code)) {
1198
- t.skip(`directory symlinks are unavailable: ${error.code}`);
1199
- return;
1200
- }
1201
- throw error;
1202
- }
1203
- const detached = path.join(stateDirectory(testFixture), 'detached-pending');
1204
- let replaced = false;
1205
- fsPromises.readdir = async (target, ...args) => {
1206
- const entries = await originalReaddir(target, ...args);
1207
- if (!replaced && path.resolve(target) === path.resolve(stateDirectory(testFixture))) {
1208
- replaced = true;
1209
- fs.renameSync(pending, detached);
1210
- fs.symlinkSync(
1211
- replacement,
1212
- pending,
1213
- process.platform === 'win32' ? 'junction' : 'dir',
1214
- );
1215
- }
1216
- return entries;
1217
- };
1218
- let requests = 0;
1219
-
1220
- const result = await checkForUpdate(options(testFixture, async () => {
1221
- requests += 1;
1222
- return response(remoteRelease());
1223
- }));
1224
-
1225
- assert.equal(replaced, true);
1226
- assert.deepEqual(result, { status: 'silent', reason: 'cache-unavailable' });
1227
- assert.equal(requests, 0);
1228
- });
1229
-
1230
- test('zero-inode cache metadata uses timestamp fallback to reject a replaced ancestor', async (t) => {
1231
- const testFixture = fixture();
1232
- const originalLstat = fsPromises.lstat;
1233
- const originalReaddir = fsPromises.readdir;
1234
- t.after(() => {
1235
- fsPromises.lstat = originalLstat;
1236
- fsPromises.readdir = originalReaddir;
1237
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1238
- });
1239
- writeCompactCommittedState(testFixture, cachedStateWithHistory());
1240
- const replacementRoot = path.join(testFixture.root, 'replacement-cache');
1241
- writeJson(
1242
- path.join(replacementRoot, path.basename(stateDirectory(testFixture)),
1243
- path.basename(operationPath(testFixture, 'committed', 1n)), 'state.json'),
1244
- cachedUpdateState(),
1245
- );
1246
- const detachedCache = path.join(testFixture.root, 'detached-cache');
1247
- fsPromises.lstat = async (target, ...args) => {
1248
- const metadata = await originalLstat(target, ...args);
1249
- const resolved = path.resolve(target);
1250
- const isCachePath = resolved === path.resolve(testFixture.cacheDirectory)
1251
- || resolved.startsWith(`${path.resolve(testFixture.cacheDirectory)}${path.sep}`);
1252
- if (args[0]?.bigint && isCachePath && metadata.isDirectory()) {
1253
- return metadataWithOverrides(metadata, {
1254
- ino: 0n,
1255
- birthtimeNs: replaced ? 2_000n : 1_000n,
1256
- });
1257
- }
1258
- return metadata;
1259
- };
1260
- let replaced = false;
1261
- fsPromises.readdir = async (target, ...args) => {
1262
- if (!replaced && path.resolve(target) === path.resolve(stateDirectory(testFixture))) {
1263
- replaced = true;
1264
- fs.renameSync(testFixture.cacheDirectory, detachedCache);
1265
- fs.renameSync(replacementRoot, testFixture.cacheDirectory);
1266
- }
1267
- return originalReaddir(target, ...args);
1268
- };
1269
- let requests = 0;
1270
-
1271
- const result = await checkForUpdate(options(testFixture, async () => {
1272
- requests += 1;
1273
- return response(remoteRelease());
1274
- }));
1275
-
1276
- assert.equal(replaced, true);
1277
- assert.deepEqual(result, { status: 'silent', reason: 'cache-unavailable' });
1278
- assert.equal(requests, 0);
1279
- });
1280
-
1281
- test('zero-inode cache metadata still supports a normal check and acknowledgement', async (t) => {
1282
- const testFixture = fixture();
1283
- const originalLstat = fsPromises.lstat;
1284
- const originalOpen = fsPromises.open;
1285
- t.after(() => {
1286
- fsPromises.lstat = originalLstat;
1287
- fsPromises.open = originalOpen;
1288
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1289
- });
1290
- const cacheRoot = path.resolve(testFixture.cacheDirectory);
1291
- const isCachePath = (target) => {
1292
- const resolved = path.resolve(target);
1293
- return resolved === cacheRoot || resolved.startsWith(`${cacheRoot}${path.sep}`);
1294
- };
1295
- fsPromises.lstat = async (target, ...args) => {
1296
- const metadata = await originalLstat(target, ...args);
1297
- if (args[0]?.bigint && isCachePath(target)) {
1298
- return metadataWithOverrides(metadata, { ino: 0n, birthtimeNs: 1_000n });
1299
- }
1300
- return metadata;
1301
- };
1302
- fsPromises.open = async (target, ...args) => {
1303
- const handle = await originalOpen(target, ...args);
1304
- if (!isCachePath(target)) return handle;
1305
- return new Proxy(handle, {
1306
- get(fileHandle, property) {
1307
- if (property === 'stat') {
1308
- return async (...statArguments) => {
1309
- const metadata = await fileHandle.stat(...statArguments);
1310
- return statArguments[0]?.bigint
1311
- ? metadataWithOverrides(metadata, { ino: 0n, birthtimeNs: 1_000n })
1312
- : metadataWithOverrides(metadata, { ino: 0 });
1313
- };
1314
- }
1315
- const value = Reflect.get(fileHandle, property, fileHandle);
1316
- return typeof value === 'function' ? value.bind(fileHandle) : value;
1317
- },
1318
- });
1319
- };
1320
- let requests = 0;
1321
-
1322
- const offered = await checkForUpdate(options(testFixture, async () => {
1323
- requests += 1;
1324
- return response(remoteRelease());
1325
- }));
1326
- const acknowledged = await acknowledgeUpdate({
1327
- releasePath: testFixture.releasePath,
1328
- cacheDirectory: testFixture.cacheDirectory,
1329
- eventKey: offered.eventKey,
1330
- });
1331
-
1332
- assert.equal(offered.status, 'update_available');
1333
- assert.deepEqual(acknowledged, { status: 'acknowledged', eventKey: offered.eventKey });
1334
- assert.equal(requests, 1);
1335
- });
1336
-
1337
- test('zero-inode cache metadata without birthtime fails closed', async (t) => {
1338
- const testFixture = fixture();
1339
- const originalLstat = fsPromises.lstat;
1340
- t.after(() => {
1341
- fsPromises.lstat = originalLstat;
1342
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1343
- });
1344
- const cacheRoot = path.resolve(testFixture.cacheDirectory);
1345
- fsPromises.lstat = async (target, ...args) => {
1346
- const metadata = await originalLstat(target, ...args);
1347
- const resolved = path.resolve(target);
1348
- if (args[0]?.bigint
1349
- && (resolved === cacheRoot || resolved.startsWith(`${cacheRoot}${path.sep}`))) {
1350
- return metadataWithOverrides(metadata, { ino: 0n, birthtimeNs: 0n });
1351
- }
1352
- return metadata;
1353
- };
1354
- let requests = 0;
1355
-
1356
- const result = await checkForUpdate(options(testFixture, async () => {
1357
- requests += 1;
1358
- return response(remoteRelease());
1359
- }));
1360
-
1361
- assert.deepEqual(result, { status: 'silent', reason: 'cache-unavailable' });
1362
- assert.equal(requests, 0);
1363
- });
1364
-
1365
- test('a state write replaced before post-verification is never reported as committed', async (t) => {
1366
- const testFixture = fixture();
1367
- const originalOpen = fsPromises.open;
1368
- const originalWriteFile = fsPromises.writeFile;
1369
- t.after(() => {
1370
- fsPromises.open = originalOpen;
1371
- fsPromises.writeFile = originalWriteFile;
1372
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1373
- });
1374
- let replaced = false;
1375
- const replaceStateFile = (target) => {
1376
- if (replaced) return;
1377
- replaced = true;
1378
- fs.renameSync(target, `${target}.written-by-checker`);
1379
- fs.writeFileSync(target, '{}\n', { flag: 'wx', mode: 0o600 });
1380
- };
1381
- fsPromises.writeFile = async (target, ...args) => {
1382
- const result = await originalWriteFile(target, ...args);
1383
- if (path.basename(target).startsWith('.state.')) replaceStateFile(target);
1384
- return result;
1385
- };
1386
- fsPromises.open = async (target, ...args) => {
1387
- const handle = await originalOpen(target, ...args);
1388
- if (!path.basename(target).startsWith('.state.')) return handle;
1389
- return {
1390
- stat: handle.stat.bind(handle),
1391
- writeFile: handle.writeFile.bind(handle),
1392
- close: async () => {
1393
- const result = await handle.close();
1394
- replaceStateFile(target);
1395
- return result;
1396
- },
1397
- };
1398
- };
1399
-
1400
- const result = await checkForUpdate(options(testFixture, async () => response(remoteRelease())));
1401
-
1402
- assert.equal(replaced, true);
1403
- assert.deepEqual(result, { status: 'silent', reason: 'cache-unavailable' });
1404
- for (const committed of committedStateFiles(testFixture)) {
1405
- assert.notDeepEqual(JSON.parse(fs.readFileSync(committed, 'utf8')), {});
1406
- }
1407
- });
1408
-
1409
- test('a pending directory replaced by a symlink cannot be reported as committed', async (t) => {
1410
- const testFixture = fixture();
1411
- const originalRename = fsPromises.rename;
1412
- t.after(() => {
1413
- fsPromises.rename = originalRename;
1414
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1415
- });
1416
- const probe = path.join(testFixture.root, 'symlink-probe');
1417
- try {
1418
- fs.mkdirSync(probe);
1419
- const probeLink = `${probe}-link`;
1420
- fs.symlinkSync(probe, probeLink, process.platform === 'win32' ? 'junction' : 'dir');
1421
- fs.unlinkSync(probeLink);
1422
- fs.rmdirSync(probe);
1423
- } catch (error) {
1424
- if (['EPERM', 'EACCES', 'ENOTSUP'].includes(error?.code)) {
1425
- t.skip(`directory symlinks are unavailable: ${error.code}`);
1426
- return;
1427
- }
1428
- throw error;
1429
- }
1430
- const detachedPending = path.join(testFixture.root, 'detached-pending');
1431
- let pending = null;
1432
- let replaced = false;
1433
- fsPromises.rename = async (source, destination, ...args) => {
1434
- if (!pending
1435
- && path.basename(source).startsWith('.state.')
1436
- && path.basename(destination) === 'state.json') {
1437
- const result = await originalRename(source, destination, ...args);
1438
- pending = path.dirname(destination);
1439
- return result;
1440
- }
1441
- if (!replaced
1442
- && pending
1443
- && path.resolve(source) === path.resolve(pending)
1444
- && path.basename(destination).startsWith('committed-')) {
1445
- replaced = true;
1446
- fs.renameSync(pending, detachedPending);
1447
- fs.symlinkSync(
1448
- detachedPending,
1449
- pending,
1450
- process.platform === 'win32' ? 'junction' : 'dir',
1451
- );
1452
- }
1453
- return originalRename(source, destination, ...args);
1454
- };
1455
- let requests = 0;
1456
-
1457
- const result = await checkForUpdate(options(testFixture, async () => {
1458
- requests += 1;
1459
- return response(remoteRelease());
1460
- }));
1461
-
1462
- assert.equal(replaced, true);
1463
- assert.deepEqual(result, { status: 'silent', reason: 'cache-unavailable' });
1464
- assert.equal(requests, 1);
1465
- assert.equal(fs.existsSync(path.join(detachedPending, 'state.json')), true);
1466
- });
1467
-
1468
- test('a cache trust failure while cancelling cannot return a cached reminder', async (t) => {
1469
- const testFixture = fixture();
1470
- const originalRename = fsPromises.rename;
1471
- t.after(() => {
1472
- fsPromises.rename = originalRename;
1473
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1474
- });
1475
- const stale = cachedUpdateState();
1476
- stale.check.nextCheckAt = new Date(baseTime - 1_000).toISOString();
1477
- const committed = writeCompactCommittedState(testFixture, stale);
1478
- const detachedCache = path.join(testFixture.root, 'detached-cache');
1479
- let refreshed = false;
1480
- let replaced = false;
1481
- fsPromises.rename = async (source, destination, ...args) => {
1482
- const sourceName = path.basename(source);
1483
- const destinationName = path.basename(destination);
1484
- if (!refreshed && sourceName.startsWith('claim-') && destinationName === 'active-claim') {
1485
- const result = await originalRename(source, destination, ...args);
1486
- const fresh = cachedUpdateState();
1487
- writeJson(committed, fresh);
1488
- refreshed = true;
1489
- return result;
1490
- }
1491
- if (!replaced && sourceName.startsWith('pending-') && destinationName.startsWith('cancelled-')) {
1492
- replaced = true;
1493
- fs.renameSync(testFixture.cacheDirectory, detachedCache);
1494
- fs.symlinkSync(
1495
- detachedCache,
1496
- testFixture.cacheDirectory,
1497
- process.platform === 'win32' ? 'junction' : 'dir',
1498
- );
1499
- }
1500
- return originalRename(source, destination, ...args);
1501
- };
1502
- let requests = 0;
1503
-
1504
- const result = await checkForUpdate(options(testFixture, async () => {
1505
- requests += 1;
1506
- return response(remoteRelease());
1507
- }));
1508
-
1509
- assert.equal(refreshed, true);
1510
- assert.equal(replaced, true);
1511
- assert.deepEqual(result, { status: 'silent', reason: 'cache-unavailable' });
1512
- assert.equal(requests, 0);
1513
- });
1514
-
1515
- test('prepared claim cleanup never recursively deletes through a replaced cache ancestor', async (t) => {
1516
- const testFixture = fixture();
1517
- const originalOpen = fsPromises.open;
1518
- const originalRm = fsPromises.rm;
1519
- const originalWriteFile = fsPromises.writeFile;
1520
- t.after(() => {
1521
- fsPromises.open = originalOpen;
1522
- fsPromises.rm = originalRm;
1523
- fsPromises.writeFile = originalWriteFile;
1524
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1525
- });
1526
- const activeClaim = path.join(stateDirectory(testFixture), 'active-claim');
1527
- let injectedActiveClaim = false;
1528
- const injectActiveClaim = () => {
1529
- if (injectedActiveClaim) return;
1530
- injectedActiveClaim = true;
1531
- fs.mkdirSync(activeClaim);
1532
- };
1533
- fsPromises.writeFile = async (target, ...args) => {
1534
- const result = await originalWriteFile(target, ...args);
1535
- if (path.basename(path.dirname(target)).startsWith('claim-')
1536
- && path.basename(target) === 'owner.json') injectActiveClaim();
1537
- return result;
1538
- };
1539
- fsPromises.open = async (target, ...args) => {
1540
- const handle = await originalOpen(target, ...args);
1541
- if (path.basename(path.dirname(target)).startsWith('claim-')
1542
- && path.basename(target) === 'owner.json') {
1543
- return {
1544
- stat: handle.stat.bind(handle),
1545
- writeFile: handle.writeFile.bind(handle),
1546
- close: async () => {
1547
- const result = await handle.close();
1548
- injectActiveClaim();
1549
- return result;
1550
- },
1551
- };
1552
- }
1553
- return handle;
1554
- };
1555
- const victimRoot = path.join(testFixture.root, 'victim-cache');
1556
- const victimClaim = path.join(
1557
- victimRoot,
1558
- path.basename(stateDirectory(testFixture)),
1559
- path.basename(operationPath(testFixture, 'claim', 1n)),
1560
- );
1561
- const victimFile = path.join(victimClaim, 'important.txt');
1562
- fs.mkdirSync(victimClaim, { recursive: true });
1563
- fs.writeFileSync(victimFile, 'must survive\n');
1564
- const detachedCache = path.join(testFixture.root, 'detached-cache');
1565
- let interceptedRemoval = false;
1566
- fsPromises.rm = async (target, ...args) => {
1567
- if (!interceptedRemoval && path.basename(target).startsWith('claim-')) {
1568
- interceptedRemoval = true;
1569
- fs.renameSync(testFixture.cacheDirectory, detachedCache);
1570
- fs.symlinkSync(
1571
- victimRoot,
1572
- testFixture.cacheDirectory,
1573
- process.platform === 'win32' ? 'junction' : 'dir',
1574
- );
1575
- }
1576
- return originalRm(target, ...args);
1577
- };
1578
- let requests = 0;
1579
-
1580
- const result = await checkForUpdate(options(testFixture, async () => {
1581
- requests += 1;
1582
- return response(remoteRelease());
1583
- }));
1584
-
1585
- assert.equal(injectedActiveClaim, true);
1586
- assert.equal(requests, 0);
1587
- assert.equal(result.status, 'silent');
1588
- assert.equal(interceptedRemoval, false);
1589
- assert.equal(fs.readFileSync(victimFile, 'utf8'), 'must survive\n');
1590
- const coordinationEntries = fs.readdirSync(stateDirectory(testFixture));
1591
- assert.deepEqual(
1592
- coordinationEntries.filter((name) => name.startsWith('claim-')),
1593
- [],
1594
- );
1595
- const discardedClaims = coordinationEntries
1596
- .filter((name) => name.startsWith('discarded-claim-'));
1597
- assert.equal(discardedClaims.length, 1);
1598
- const discardedOwnerPath = path.join(
1599
- stateDirectory(testFixture),
1600
- discardedClaims[0],
1601
- 'owner.json',
1602
- );
1603
- const discardedOwner = JSON.parse(fs.readFileSync(discardedOwnerPath, 'utf8'));
1604
- assert.deepEqual(Object.keys(discardedOwner).sort(), ['generation', 'pid', 'token']);
1605
- assert.equal(discardedOwner.generation, '1');
1606
- assert.match(discardedOwner.token, /^[a-f0-9]{32}$/);
1607
- assert.equal(discardedClaims[0].endsWith(`-${discardedOwner.token}`), true);
1608
- });
1609
-
1610
- test('a symlink cache root cannot write into its target', async (t) => {
1611
- const testFixture = fixture();
1612
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
1613
- const protectedTarget = path.join(testFixture.root, 'simulated-project-config');
1614
- fs.mkdirSync(protectedTarget);
1615
- fs.writeFileSync(path.join(protectedTarget, 'settings.json'), '{"protected":true}\n');
1616
- try {
1617
- fs.symlinkSync(
1618
- protectedTarget,
1619
- testFixture.cacheDirectory,
1620
- process.platform === 'win32' ? 'junction' : 'dir',
1621
- );
1622
- } catch (error) {
1623
- if (['EPERM', 'EACCES', 'ENOTSUP'].includes(error?.code)) {
1624
- t.skip(`directory symlinks are unavailable: ${error.code}`);
1625
- return;
1626
- }
1627
- throw error;
1628
- }
1629
- let requests = 0;
1630
-
1631
- const result = await checkForUpdate(options(testFixture, async () => {
1632
- requests += 1;
1633
- return response(remoteRelease());
1634
- }));
1635
-
1636
- assert.deepEqual(result, { status: 'silent', reason: 'cache-unavailable' });
1637
- assert.equal(requests, 0);
1638
- assert.deepEqual(fs.readdirSync(protectedTarget), ['settings.json']);
1639
- });
1640
-
1641
- test('a symlink cache ancestor cannot redirect cache writes', async (t) => {
1642
- const testFixture = fixture();
1643
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
1644
- const protectedTarget = path.join(testFixture.root, 'simulated-project-config');
1645
- const linkedAncestor = path.join(testFixture.root, 'cache-parent');
1646
- const cacheDirectory = path.join(linkedAncestor, 'nested', 'cache');
1647
- fs.mkdirSync(protectedTarget);
1648
- fs.writeFileSync(path.join(protectedTarget, 'settings.json'), '{"protected":true}\n');
1649
- try {
1650
- fs.symlinkSync(
1651
- protectedTarget,
1652
- linkedAncestor,
1653
- process.platform === 'win32' ? 'junction' : 'dir',
1654
- );
1655
- } catch (error) {
1656
- if (['EPERM', 'EACCES', 'ENOTSUP'].includes(error?.code)) {
1657
- t.skip(`directory symlinks are unavailable: ${error.code}`);
1658
- return;
1659
- }
1660
- throw error;
1661
- }
1662
- let requests = 0;
1663
-
1664
- const result = await checkForUpdate(options(testFixture, async () => {
1665
- requests += 1;
1666
- return response(remoteRelease());
1667
- }, { cacheDirectory }));
1668
-
1669
- assert.deepEqual(result, { status: 'silent', reason: 'cache-unavailable' });
1670
- assert.equal(requests, 0);
1671
- assert.deepEqual(fs.readdirSync(protectedTarget), ['settings.json']);
1672
- });
1673
-
1674
- test('a cache ancestor replaced during preparation cannot redirect later writes', async (t) => {
1675
- const testFixture = fixture();
1676
- const originalLstat = fsPromises.lstat;
1677
- t.after(() => {
1678
- fsPromises.lstat = originalLstat;
1679
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1680
- });
1681
- const protectedTarget = path.join(testFixture.root, 'simulated-project-config');
1682
- const protectedVersion = path.join(protectedTarget, path.basename(stateDirectory(testFixture)));
1683
- const detachedCache = path.join(testFixture.root, 'detached-cache');
1684
- const probe = path.join(testFixture.root, 'symlink-probe');
1685
- fs.mkdirSync(testFixture.cacheDirectory);
1686
- fs.mkdirSync(protectedVersion, { recursive: true });
1687
- fs.writeFileSync(path.join(protectedVersion, 'settings.json'), '{"protected":true}\n');
1688
- try {
1689
- fs.symlinkSync(
1690
- protectedTarget,
1691
- probe,
1692
- process.platform === 'win32' ? 'junction' : 'dir',
1693
- );
1694
- fs.unlinkSync(probe);
1695
- } catch (error) {
1696
- if (['EPERM', 'EACCES', 'ENOTSUP'].includes(error?.code)) {
1697
- t.skip(`directory symlinks are unavailable: ${error.code}`);
1698
- return;
1699
- }
1700
- throw error;
1701
- }
1702
- let replaced = false;
1703
- const versionDirectoryName = path.basename(stateDirectory(testFixture));
1704
- fsPromises.lstat = async (target, ...args) => {
1705
- if (!replaced && path.basename(target) === versionDirectoryName) {
1706
- replaced = true;
1707
- fs.renameSync(testFixture.cacheDirectory, detachedCache);
1708
- fs.symlinkSync(
1709
- protectedTarget,
1710
- testFixture.cacheDirectory,
1711
- process.platform === 'win32' ? 'junction' : 'dir',
1712
- );
1713
- }
1714
- return originalLstat(target, ...args);
1715
- };
1716
- let requests = 0;
1717
-
1718
- const result = await checkForUpdate(options(testFixture, async () => {
1719
- requests += 1;
1720
- return response(remoteRelease());
1721
- }));
1722
-
1723
- assert.equal(replaced, true);
1724
- assert.deepEqual(result, { status: 'silent', reason: 'cache-unavailable' });
1725
- assert.equal(requests, 0);
1726
- assert.deepEqual(fs.readdirSync(protectedVersion), ['settings.json']);
1727
- });
1728
-
1729
- test('a cache ancestor replaced before reservation creation fails closed', async (t) => {
1730
- const testFixture = fixture();
1731
- const originalMkdir = fsPromises.mkdir;
1732
- t.after(() => {
1733
- fsPromises.mkdir = originalMkdir;
1734
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1735
- });
1736
- const replacementRoot = path.join(testFixture.root, 'replacement-cache');
1737
- const replacementVersion = path.join(
1738
- replacementRoot,
1739
- path.basename(stateDirectory(testFixture)),
1740
- );
1741
- const detachedCache = path.join(testFixture.root, 'detached-cache');
1742
- const probe = path.join(testFixture.root, 'symlink-probe');
1743
- fs.mkdirSync(replacementVersion, { recursive: true });
1744
- fs.writeFileSync(path.join(replacementVersion, 'settings.json'), '{"protected":true}\n');
1745
- try {
1746
- fs.symlinkSync(
1747
- replacementRoot,
1748
- probe,
1749
- process.platform === 'win32' ? 'junction' : 'dir',
1750
- );
1751
- fs.unlinkSync(probe);
1752
- } catch (error) {
1753
- if (['EPERM', 'EACCES', 'ENOTSUP'].includes(error?.code)) {
1754
- t.skip(`directory symlinks are unavailable: ${error.code}`);
1755
- return;
1756
- }
1757
- throw error;
1758
- }
1759
- let replaced = false;
1760
- fsPromises.mkdir = async (target, ...args) => {
1761
- if (!replaced && path.basename(target).startsWith('reserved-')) {
1762
- replaced = true;
1763
- fs.renameSync(testFixture.cacheDirectory, detachedCache);
1764
- fs.symlinkSync(
1765
- replacementRoot,
1766
- testFixture.cacheDirectory,
1767
- process.platform === 'win32' ? 'junction' : 'dir',
1768
- );
1769
- }
1770
- return originalMkdir(target, ...args);
1771
- };
1772
- let requests = 0;
1773
-
1774
- const result = await checkForUpdate(options(testFixture, async () => {
1775
- requests += 1;
1776
- return response(remoteRelease());
1777
- }));
1778
-
1779
- assert.equal(replaced, true);
1780
- assert.deepEqual(result, { status: 'silent', reason: 'cache-unavailable' });
1781
- assert.equal(requests, 0);
1782
- const replacementEntries = fs.readdirSync(replacementVersion).sort();
1783
- const reservations = replacementEntries.filter((entry) => entry.startsWith('reserved-'));
1784
- assert.equal(reservations.length, 1);
1785
- assert.deepEqual(
1786
- replacementEntries.filter((entry) => !entry.startsWith('reserved-')),
1787
- ['settings.json'],
1788
- );
1789
- assert.deepEqual(fs.readdirSync(path.join(replacementVersion, reservations[0])), []);
1790
- });
1791
-
1792
- test('a cache ancestor replacement restored after reservation creation fails closed', async (t) => {
1793
- const testFixture = fixture();
1794
- const originalMkdir = fsPromises.mkdir;
1795
- t.after(() => {
1796
- fsPromises.mkdir = originalMkdir;
1797
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1798
- });
1799
- const replacementRoot = path.join(testFixture.root, 'replacement-cache');
1800
- const replacementVersion = path.join(
1801
- replacementRoot,
1802
- path.basename(stateDirectory(testFixture)),
1803
- );
1804
- const detachedCache = path.join(testFixture.root, 'detached-cache');
1805
- const probe = path.join(testFixture.root, 'symlink-probe');
1806
- fs.mkdirSync(replacementVersion, { recursive: true });
1807
- fs.writeFileSync(path.join(replacementVersion, 'settings.json'), '{"protected":true}\n');
1808
- try {
1809
- fs.symlinkSync(
1810
- replacementRoot,
1811
- probe,
1812
- process.platform === 'win32' ? 'junction' : 'dir',
1813
- );
1814
- fs.unlinkSync(probe);
1815
- } catch (error) {
1816
- if (['EPERM', 'EACCES', 'ENOTSUP'].includes(error?.code)) {
1817
- t.skip(`directory symlinks are unavailable: ${error.code}`);
1818
- return;
1819
- }
1820
- throw error;
1821
- }
1822
- let restored = false;
1823
- fsPromises.mkdir = async (target, ...args) => {
1824
- if (!restored && path.basename(target).startsWith('reserved-')) {
1825
- fs.renameSync(testFixture.cacheDirectory, detachedCache);
1826
- fs.symlinkSync(
1827
- replacementRoot,
1828
- testFixture.cacheDirectory,
1829
- process.platform === 'win32' ? 'junction' : 'dir',
1830
- );
1831
- try {
1832
- return await originalMkdir(target, ...args);
1833
- } finally {
1834
- fs.unlinkSync(testFixture.cacheDirectory);
1835
- fs.renameSync(detachedCache, testFixture.cacheDirectory);
1836
- restored = true;
1837
- }
1838
- }
1839
- return originalMkdir(target, ...args);
1840
- };
1841
- let requests = 0;
1842
-
1843
- const result = await checkForUpdate(options(testFixture, async () => {
1844
- requests += 1;
1845
- return response(remoteRelease());
1846
- }));
1847
-
1848
- assert.equal(restored, true);
1849
- assert.deepEqual(result, { status: 'silent', reason: 'cache-unavailable' });
1850
- assert.equal(requests, 0);
1851
- const replacementEntries = fs.readdirSync(replacementVersion).sort();
1852
- const reservations = replacementEntries.filter((entry) => entry.startsWith('reserved-'));
1853
- assert.equal(reservations.length, 1);
1854
- assert.deepEqual(
1855
- replacementEntries.filter((entry) => !entry.startsWith('reserved-')),
1856
- ['settings.json'],
1857
- );
1858
- assert.deepEqual(fs.readdirSync(path.join(replacementVersion, reservations[0])), []);
1859
- });
1860
-
1861
- test('a trusted cache prefix switched after validation cannot redirect later writes', async (t) => {
1862
- const testFixture = fixture();
1863
- const originalHomedir = os.homedir;
1864
- const originalLstat = fsPromises.lstat;
1865
- t.after(() => {
1866
- os.homedir = originalHomedir;
1867
- fsPromises.lstat = originalLstat;
1868
- fs.rmSync(testFixture.root, { recursive: true, force: true });
1869
- });
1870
- const trustedAlias = path.join(testFixture.root, 'trusted-home');
1871
- const canonicalHome = path.join(testFixture.root, 'canonical-home');
1872
- const protectedTarget = path.join(testFixture.root, 'simulated-project-config');
1873
- const cacheDirectory = path.join(trustedAlias, 'cache');
1874
- fs.mkdirSync(canonicalHome);
1875
- fs.mkdirSync(protectedTarget);
1876
- fs.writeFileSync(path.join(protectedTarget, 'settings.json'), '{"protected":true}\n');
1877
- try {
1878
- fs.symlinkSync(
1879
- canonicalHome,
1880
- trustedAlias,
1881
- process.platform === 'win32' ? 'junction' : 'dir',
1882
- );
1883
- } catch (error) {
1884
- if (['EPERM', 'EACCES', 'ENOTSUP'].includes(error?.code)) {
1885
- t.skip(`directory symlinks are unavailable: ${error.code}`);
1886
- return;
1887
- }
1888
- throw error;
1889
- }
1890
- os.homedir = () => trustedAlias;
1891
- const canonicalVersionDirectory = path.join(
1892
- fs.realpathSync(canonicalHome),
1893
- 'cache',
1894
- path.basename(stateDirectory(testFixture)),
1895
- );
1896
- let targetVisits = 0;
1897
- let switched = false;
1898
- fsPromises.lstat = async (target, ...args) => {
1899
- const metadata = await originalLstat(target, ...args);
1900
- if (!switched && path.resolve(target) === canonicalVersionDirectory) {
1901
- targetVisits += 1;
1902
- if (targetVisits === 2) {
1903
- switched = true;
1904
- fs.unlinkSync(trustedAlias);
1905
- fs.symlinkSync(
1906
- protectedTarget,
1907
- trustedAlias,
1908
- process.platform === 'win32' ? 'junction' : 'dir',
1909
- );
1910
- }
1911
- }
1912
- return metadata;
1913
- };
1914
-
1915
- const result = await checkForUpdate(options(testFixture, async () => response(remoteRelease()), {
1916
- cacheDirectory,
1917
- }));
1918
-
1919
- assert.equal(switched, true);
1920
- assert.equal(result.status, 'update_available');
1921
- assert.deepEqual(fs.readdirSync(protectedTarget), ['settings.json']);
1922
- assert.ok(fs.readdirSync(canonicalVersionDirectory).some(
1923
- (entry) => entry.startsWith('committed-'),
1924
- ));
1925
- });
1926
-
1927
- test('concurrent checks use one writer and leave a valid cache', async (t) => {
1928
- const testFixture = fixture();
1929
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
1930
- let releaseFetch;
1931
- let markStarted;
1932
- const started = new Promise((resolve) => { markStarted = resolve; });
1933
- const fetchImpl = async () => {
1934
- markStarted();
1935
- return new Promise((resolve) => { releaseFetch = resolve; });
1936
- };
1937
-
1938
- const firstCheck = checkForUpdate(options(testFixture, fetchImpl));
1939
- await started;
1940
- const overlapping = await checkForUpdate(options(testFixture, fetchImpl));
1941
- assert.deepEqual(overlapping, { status: 'silent', reason: 'check-in-progress' });
1942
-
1943
- releaseFetch(response(remoteRelease()));
1944
- const first = await firstCheck;
1945
- assert.equal(first.status, 'update_available');
1946
- assert.doesNotThrow(() => JSON.parse(
1947
- fs.readFileSync(statePath(testFixture), 'utf8'),
1948
- ));
1949
- });
1950
-
1951
- test('an empty precheck snapshot cannot start a second concurrent network request', async (t) => {
1952
- const testFixture = fixture();
1953
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
1954
- fs.mkdirSync(stateDirectory(testFixture), { recursive: true });
1955
- const pause = pauseReaddirSnapshot(stateDirectory(testFixture), 2);
1956
- t.after(() => pause.restore());
1957
- let releaseFetch;
1958
- let markFetchStarted;
1959
- let requests = 0;
1960
- const fetchStarted = new Promise((resolve) => { markFetchStarted = resolve; });
1961
- const fetchImpl = async () => {
1962
- requests += 1;
1963
- markFetchStarted();
1964
- return new Promise((resolve) => { releaseFetch = resolve; });
1965
- };
1966
-
1967
- const delayedPrecheck = checkForUpdate(options(testFixture, fetchImpl));
1968
- await pause.reached;
1969
- const claimedCheck = checkForUpdate(options(testFixture, fetchImpl));
1970
- await fetchStarted;
1971
- pause.release();
1972
- assert.deepEqual(await delayedPrecheck, { status: 'silent', reason: 'check-in-progress' });
1973
- assert.equal(requests, 1);
1974
-
1975
- releaseFetch(response(remoteRelease()));
1976
- assert.equal((await claimedCheck).status, 'update_available');
1977
- pause.restore();
1978
- assert.equal(requests, 1);
1979
- });
1980
-
1981
- test('two promoters cannot replace and then steal a stale empty active claim', async (t) => {
1982
- const testFixture = fixture();
1983
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
1984
- const activeClaim = path.join(stateDirectory(testFixture), 'active-claim');
1985
- fs.mkdirSync(activeClaim, { recursive: true });
1986
- const staleTime = new Date(Date.now() - 60_000);
1987
- fs.utimesSync(activeClaim, staleTime, staleTime);
1988
- const pause = pauseWriteOnce('.retirement-guard');
1989
- t.after(() => pause.restore());
1990
- let releaseFetch;
1991
- let markFetchStarted;
1992
- let requests = 0;
1993
- const fetchStarted = new Promise((resolve) => { markFetchStarted = resolve; });
1994
- const fetchImpl = async () => {
1995
- requests += 1;
1996
- markFetchStarted();
1997
- return new Promise((resolve) => { releaseFetch = resolve; });
1998
- };
1999
-
2000
- const delayedRetirement = checkForUpdate(options(testFixture, fetchImpl));
2001
- await pause.reached;
2002
- fs.utimesSync(
2003
- path.join(operationPath(testFixture, 'pending', 1n), 'owner.json'),
2004
- staleTime,
2005
- staleTime,
2006
- );
2007
- const claimedCheck = checkForUpdate(options(testFixture, fetchImpl));
2008
- await fetchStarted;
2009
- pause.release();
2010
- assert.deepEqual(await delayedRetirement, { status: 'silent', reason: 'check-in-progress' });
2011
- assert.equal(requests, 1);
2012
-
2013
- releaseFetch(response(remoteRelease()));
2014
- assert.equal((await claimedCheck).status, 'update_available');
2015
- pause.restore();
2016
- assert.equal(requests, 1);
2017
- });
2018
-
2019
- test('a delayed lower-generation promoter cannot retire a completed higher claim', async (t) => {
2020
- const testFixture = fixture();
2021
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2022
- const pause = pauseRenameOnce(
2023
- operationPath(testFixture, 'claim', 1n),
2024
- path.join(stateDirectory(testFixture), 'active-claim'),
2025
- );
2026
- t.after(() => pause.restore());
2027
- let requests = 0;
2028
- const fetchImpl = async () => {
2029
- requests += 1;
2030
- return response(remoteRelease());
2031
- };
2032
-
2033
- const delayedLower = checkForUpdate(options(testFixture, fetchImpl));
2034
- await pause.reached;
2035
- const staleTime = new Date(Date.now() - 60_000);
2036
- fs.utimesSync(
2037
- path.join(operationPath(testFixture, 'pending', 1n), 'owner.json'),
2038
- staleTime,
2039
- staleTime,
2040
- );
2041
- assert.equal((await checkForUpdate(options(testFixture, fetchImpl))).status, 'update_available');
2042
- assert.equal(requests, 1);
2043
-
2044
- pause.release();
2045
- assert.equal((await delayedLower).status, 'update_available');
2046
- pause.restore();
2047
- assert.equal(requests, 1);
2048
- assert.equal(
2049
- JSON.parse(fs.readFileSync(
2050
- path.join(stateDirectory(testFixture), 'active-claim', 'owner.json'),
2051
- 'utf8',
2052
- )).generation,
2053
- '2',
2054
- );
2055
- });
2056
-
2057
- test('an expired contender cannot fetch after its delayed claim promotion succeeds', async (t) => {
2058
- const testFixture = fixture();
2059
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2060
- const pause = pauseRenameOnce(
2061
- operationPath(testFixture, 'claim', 1n),
2062
- path.join(stateDirectory(testFixture), 'active-claim'),
2063
- );
2064
- t.after(() => pause.restore());
2065
- let requests = 0;
2066
- const fetchImpl = async () => {
2067
- requests += 1;
2068
- return response(remoteRelease());
2069
- };
2070
-
2071
- const delayedExpired = checkForUpdate(options(testFixture, fetchImpl));
2072
- await pause.reached;
2073
- const staleTime = new Date(Date.now() - 60_000);
2074
- fs.utimesSync(
2075
- path.join(operationPath(testFixture, 'pending', 1n), 'owner.json'),
2076
- staleTime,
2077
- staleTime,
2078
- );
2079
- pause.release();
2080
- assert.deepEqual(await delayedExpired, { status: 'silent', reason: 'check-in-progress' });
2081
- assert.equal(requests, 0);
2082
-
2083
- assert.equal((await checkForUpdate(options(testFixture, fetchImpl))).status, 'update_available');
2084
- pause.restore();
2085
- assert.equal(requests, 1);
2086
- assert.equal(
2087
- JSON.parse(fs.readFileSync(
2088
- path.join(stateDirectory(testFixture), 'active-claim', 'owner.json'),
2089
- 'utf8',
2090
- )).generation,
2091
- '2',
2092
- );
2093
- });
2094
-
2095
- test('a stale supersession snapshot cannot let a lower promoter retire a higher claim', async (t) => {
2096
- const testFixture = fixture();
2097
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2098
- const generationOneClaim = path.basename(operationPath(testFixture, 'claim', 1n));
2099
- const pause = pauseReaddirSnapshotOnce(
2100
- stateDirectory(testFixture),
2101
- (entries) => entries.some((entry) => entry.name === generationOneClaim)
2102
- && !entries.some((entry) => /-(?:0{19}2)$/.test(entry.name)),
2103
- );
2104
- t.after(() => pause.restore());
2105
- let requests = 0;
2106
- const fetchImpl = async () => {
2107
- requests += 1;
2108
- return response(remoteRelease());
2109
- };
2110
-
2111
- const delayedLower = checkForUpdate(options(testFixture, fetchImpl));
2112
- await pause.reached;
2113
- const staleTime = new Date(Date.now() - 60_000);
2114
- fs.utimesSync(
2115
- path.join(operationPath(testFixture, 'pending', 1n), 'owner.json'),
2116
- staleTime,
2117
- staleTime,
2118
- );
2119
- assert.equal((await checkForUpdate(options(testFixture, fetchImpl))).status, 'update_available');
2120
- assert.equal(requests, 1);
2121
-
2122
- pause.release();
2123
- assert.equal((await delayedLower).status, 'update_available');
2124
- pause.restore();
2125
- assert.equal(requests, 1);
2126
- assert.equal(
2127
- JSON.parse(fs.readFileSync(
2128
- path.join(stateDirectory(testFixture), 'active-claim', 'owner.json'),
2129
- 'utf8',
2130
- )).generation,
2131
- '2',
2132
- );
2133
- });
2134
-
2135
- test('an owner fenced while active ownership is read cannot issue a second GET', {
2136
- timeout: parentCheckTimeoutMs,
2137
- }, async (t) => {
2138
- const testFixture = fixture();
2139
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2140
- const directory = stateDirectory(testFixture);
2141
- const originalOpen = fsPromises.open;
2142
- let releaseVerification;
2143
- let markVerification;
2144
- let intercepted = false;
2145
- const verificationReached = new Promise((resolve) => { markVerification = resolve; });
2146
- const verificationGate = new Promise((resolve) => { releaseVerification = resolve; });
2147
- fsPromises.open = async (target, ...args) => {
2148
- const handle = await originalOpen(target, ...args);
2149
- if (intercepted
2150
- || path.resolve(target) !== path.join(directory, 'active-claim', 'owner.json')) {
2151
- return handle;
2152
- }
2153
- intercepted = true;
2154
- return new Proxy(handle, {
2155
- get(fileHandle, property) {
2156
- if (property === 'close') {
2157
- return async () => {
2158
- const result = await fileHandle.close();
2159
- markVerification();
2160
- await verificationGate;
2161
- return result;
2162
- };
2163
- }
2164
- const value = Reflect.get(fileHandle, property, fileHandle);
2165
- return typeof value === 'function' ? value.bind(fileHandle) : value;
2166
- },
2167
- });
2168
- };
2169
- t.after(() => {
2170
- releaseVerification();
2171
- fsPromises.open = originalOpen;
2172
- });
2173
- let requests = 0;
2174
- const checkOptions = options(testFixture, async () => {
2175
- requests += 1;
2176
- return response(remoteRelease());
2177
- });
2178
-
2179
- const suspendedOwner = checkForUpdate(checkOptions);
2180
- await verificationReached;
2181
- const staleTime = new Date(Date.now() - 60_000);
2182
- fs.utimesSync(
2183
- path.join(operationPath(testFixture, 'pending', 1n), 'owner.json'),
2184
- staleTime,
2185
- staleTime,
2186
- );
2187
- assert.equal((await checkForUpdate(checkOptions)).status, 'update_available');
2188
- assert.equal(requests, 1);
2189
-
2190
- releaseVerification();
2191
- assert.deepEqual(await suspendedOwner, { status: 'silent', reason: 'cache-unavailable' });
2192
- assert.equal(requests, 1);
2193
- });
2194
-
2195
- test('an expired active owner cannot start a network request', async (t) => {
2196
- const testFixture = fixture();
2197
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2198
- const pendingOne = path.basename(operationPath(testFixture, 'pending', 1n));
2199
- const claimOne = path.basename(operationPath(testFixture, 'claim', 1n));
2200
- const pause = pauseReaddirSnapshotOnce(
2201
- stateDirectory(testFixture),
2202
- (entries) => entries.some((entry) => entry.name === 'active-claim')
2203
- && entries.some((entry) => entry.name === pendingOne)
2204
- && !entries.some((entry) => entry.name === claimOne),
2205
- );
2206
- t.after(() => pause.restore());
2207
- let requests = 0;
2208
-
2209
- const check = checkForUpdate(options(testFixture, async () => {
2210
- requests += 1;
2211
- return response(remoteRelease());
2212
- }));
2213
- await pause.reached;
2214
- const staleTime = new Date(Date.now() - 60_000);
2215
- fs.utimesSync(
2216
- path.join(operationPath(testFixture, 'pending', 1n), 'owner.json'),
2217
- staleTime,
2218
- staleTime,
2219
- );
2220
- pause.release();
2221
-
2222
- assert.deepEqual(await check, { status: 'silent', reason: 'check-in-progress' });
2223
- pause.restore();
2224
- assert.equal(requests, 0);
2225
- });
2226
-
2227
- test('a fenced owner rechecks its active claim immediately before network access', async (t) => {
2228
- const testFixture = fixture();
2229
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2230
- const pendingOne = path.basename(operationPath(testFixture, 'pending', 1n));
2231
- const claimOne = path.basename(operationPath(testFixture, 'claim', 1n));
2232
- const pause = pauseReaddirSnapshotOnce(
2233
- stateDirectory(testFixture),
2234
- (entries) => entries.some((entry) => entry.name === 'active-claim')
2235
- && entries.some((entry) => entry.name === pendingOne)
2236
- && !entries.some((entry) => entry.name === claimOne),
2237
- );
2238
- t.after(() => pause.restore());
2239
- let requests = 0;
2240
- let releaseFetch;
2241
- let markFetchStarted;
2242
- const fetchStarted = new Promise((resolve) => { markFetchStarted = resolve; });
2243
- const fetchImpl = async () => {
2244
- requests += 1;
2245
- markFetchStarted();
2246
- return new Promise((resolve) => { releaseFetch = resolve; });
2247
- };
2248
-
2249
- const delayedOwner = checkForUpdate(options(testFixture, fetchImpl));
2250
- await pause.reached;
2251
- const staleTime = new Date(Date.now() - 60_000);
2252
- fs.utimesSync(
2253
- path.join(operationPath(testFixture, 'pending', 1n), 'owner.json'),
2254
- staleTime,
2255
- staleTime,
2256
- );
2257
- const successor = checkForUpdate(options(testFixture, fetchImpl));
2258
- await fetchStarted;
2259
- assert.equal(requests, 1);
2260
-
2261
- pause.release();
2262
- assert.deepEqual(await delayedOwner, { status: 'silent', reason: 'check-in-progress' });
2263
- pause.restore();
2264
- assert.equal(requests, 1);
2265
-
2266
- releaseFetch(response(remoteRelease()));
2267
- assert.equal((await successor).status, 'update_available');
2268
- assert.equal(requests, 1);
2269
- });
2270
-
2271
- test('an overlapping check reads the last-good candidate while another process refreshes it', async (t) => {
2272
- const testFixture = fixture();
2273
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2274
- let requests = 0;
2275
- let releaseRefresh;
2276
- let markRefreshStarted;
2277
- const refreshStarted = new Promise((resolve) => { markRefreshStarted = resolve; });
2278
- const fetchImpl = async () => {
2279
- requests += 1;
2280
- if (requests === 1) return response(remoteRelease());
2281
- markRefreshStarted();
2282
- return new Promise((resolve) => { releaseRefresh = resolve; });
2283
- };
2284
-
2285
- const first = await checkForUpdate(options(testFixture, fetchImpl));
2286
- const refreshOptions = options(testFixture, fetchImpl, {
2287
- now: () => baseTime + (73 * 60 * 60 * 1_000),
2288
- });
2289
- const refresh = checkForUpdate(refreshOptions);
2290
- await refreshStarted;
2291
-
2292
- const overlapping = await checkForUpdate(refreshOptions);
2293
- assert.equal(overlapping.status, 'update_available');
2294
- assert.equal(overlapping.eventKey, first.eventKey);
2295
- assert.equal(requests, 2);
2296
-
2297
- releaseRefresh(response(remoteRelease()));
2298
- assert.equal((await refresh).status, 'update_available');
2299
- });
2300
-
2301
- test('acknowledgement waits briefly for an in-flight refresh instead of losing the notice', async (t) => {
2302
- const testFixture = fixture();
2303
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2304
- const offered = await checkForUpdate(options(
2305
- testFixture,
2306
- async () => response(remoteRelease()),
2307
- ));
2308
- let releaseRefresh;
2309
- let markRefreshStarted;
2310
- const refreshStarted = new Promise((resolve) => { markRefreshStarted = resolve; });
2311
- const refresh = checkForUpdate(options(testFixture, async () => {
2312
- markRefreshStarted();
2313
- return new Promise((resolve) => { releaseRefresh = resolve; });
2314
- }, {
2315
- now: () => baseTime + (73 * 60 * 60 * 1_000),
2316
- }));
2317
- await refreshStarted;
2318
-
2319
- const acknowledgement = acknowledgeUpdate({
2320
- releasePath: testFixture.releasePath,
2321
- cacheDirectory: testFixture.cacheDirectory,
2322
- eventKey: offered.eventKey,
2323
- now: () => baseTime + (73 * 60 * 60 * 1_000) + 1,
2324
- });
2325
- await new Promise((resolve) => setTimeout(resolve, 25));
2326
- releaseRefresh(response(remoteRelease()));
2327
- await refresh;
2328
-
2329
- assert.deepEqual(await acknowledgement, {
2330
- status: 'acknowledged',
2331
- eventKey: offered.eventKey,
2332
- });
2333
- assert.deepEqual(await checkForUpdate(options(testFixture, async () => response(remoteRelease()), {
2334
- now: () => baseTime + (74 * 60 * 60 * 1_000),
2335
- })), { status: 'silent', reason: 'already-notified' });
2336
- });
2337
-
2338
- test('a last-good notice remains acknowledgeable after the refresh commits a new candidate', async (t) => {
2339
- const testFixture = fixture();
2340
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2341
- const firstRelease = remoteReleaseForVersion('2.16.0', 'b'.repeat(64));
2342
- const secondRelease = remoteReleaseForVersion('2.17.0', 'c'.repeat(64));
2343
- const offered = await checkForUpdate(options(
2344
- testFixture,
2345
- async () => response(firstRelease),
2346
- ));
2347
- let releaseRefresh;
2348
- let markRefreshStarted;
2349
- const refreshStarted = new Promise((resolve) => { markRefreshStarted = resolve; });
2350
- const refresh = checkForUpdate(options(testFixture, async () => {
2351
- markRefreshStarted();
2352
- return new Promise((resolve) => { releaseRefresh = resolve; });
2353
- }, {
2354
- now: () => baseTime + (73 * 60 * 60 * 1_000),
2355
- }));
2356
- await refreshStarted;
2357
-
2358
- const visibleDuringRefresh = await checkForUpdate(options(testFixture, async () => {
2359
- assert.fail('an overlapping reader must not start another network request');
2360
- }, {
2361
- now: () => baseTime + (73 * 60 * 60 * 1_000),
2362
- }));
2363
- assert.equal(visibleDuringRefresh.eventKey, offered.eventKey);
2364
- const acknowledgement = acknowledgeUpdate({
2365
- releasePath: testFixture.releasePath,
2366
- cacheDirectory: testFixture.cacheDirectory,
2367
- eventKey: visibleDuringRefresh.eventKey,
2368
- });
2369
- await new Promise((resolve) => setTimeout(resolve, 25));
2370
- releaseRefresh(response(secondRelease));
2371
- assert.equal((await refresh).latestVersion, '2.17.0');
2372
- assert.deepEqual(await acknowledgement, {
2373
- status: 'acknowledged',
2374
- eventKey: offered.eventKey,
2375
- });
2376
-
2377
- assert.deepEqual(await checkForUpdate(options(testFixture, async () => response(firstRelease), {
2378
- now: () => baseTime + (146 * 60 * 60 * 1_000),
2379
- })), { status: 'silent', reason: 'already-notified' });
2380
- });
2381
-
2382
- test('acknowledgement retry budget uses a monotonic clock', async (t) => {
2383
- const testFixture = fixture();
2384
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2385
- const offered = await checkForUpdate(options(
2386
- testFixture,
2387
- async () => response(remoteRelease()),
2388
- ));
2389
- writePendingOperation(testFixture, { generation: 2n });
2390
- const ticks = [100, 1_301];
2391
- const started = Date.now();
2392
-
2393
- assert.deepEqual(await acknowledgeUpdate({
2394
- releasePath: testFixture.releasePath,
2395
- cacheDirectory: testFixture.cacheDirectory,
2396
- eventKey: offered.eventKey,
2397
- monotonicNow: () => ticks.shift() ?? 1_301,
2398
- }), { status: 'silent', reason: 'check-in-progress' });
2399
- assert.ok(Date.now() - started < 200, 'an exhausted monotonic budget should not sleep on wall time');
2400
- });
2401
-
2402
- test('a delayed allocator cannot reuse a generation after its reservation name was taken', async (t) => {
2403
- const testFixture = fixture();
2404
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2405
- const firstRelease = remoteReleaseForVersion('2.16.0', 'b'.repeat(64));
2406
- const secondRelease = remoteReleaseForVersion('2.17.0', 'c'.repeat(64));
2407
- const offered = await checkForUpdate(options(
2408
- testFixture,
2409
- async () => response(firstRelease),
2410
- ));
2411
- const pause = pauseMkdirOnce(operationPath(testFixture, 'reserved', 2n));
2412
- t.after(() => pause.restore());
2413
-
2414
- const acknowledgement = acknowledgeUpdate({
2415
- releasePath: testFixture.releasePath,
2416
- cacheDirectory: testFixture.cacheDirectory,
2417
- eventKey: offered.eventKey,
2418
- });
2419
- await pause.reached;
2420
- assert.equal((await checkForUpdate(options(testFixture, async () => response(secondRelease), {
2421
- now: () => baseTime + (73 * 60 * 60 * 1_000),
2422
- }))).latestVersion, '2.17.0');
2423
-
2424
- pause.release();
2425
- assert.deepEqual(await acknowledgement, { status: 'acknowledged', eventKey: offered.eventKey });
2426
- pause.restore();
2427
- assert.equal(fs.existsSync(operationPath(testFixture, 'reserved', 2n)), true);
2428
- assert.equal(fs.existsSync(operationPath(testFixture, 'reserved', 3n)), true);
2429
- assert.deepEqual(await checkForUpdate(options(testFixture, async () => response(firstRelease), {
2430
- now: () => baseTime + (146 * 60 * 60 * 1_000),
2431
- })), { status: 'silent', reason: 'already-notified' });
2432
- });
2433
-
2434
- test('an oversized generation name cannot poison future allocation', async (t) => {
2435
- const testFixture = fixture();
2436
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2437
- fs.mkdirSync(stateDirectory(testFixture), { recursive: true });
2438
- fs.mkdirSync(path.join(stateDirectory(testFixture), `reserved-${'9'.repeat(246)}`));
2439
- let requests = 0;
2440
- const fetchImpl = async () => {
2441
- requests += 1;
2442
- return response(remoteRelease());
2443
- };
2444
-
2445
- assert.equal((await checkForUpdate(options(testFixture, fetchImpl))).status, 'update_available');
2446
- assert.equal((await checkForUpdate(options(testFixture, fetchImpl))).status, 'update_available');
2447
- assert.equal(requests, 1);
2448
- assert.ok(fs.existsSync(operationPath(testFixture, 'reserved', 1n)));
2449
- });
2450
-
2451
- test('a lower generation stalled after reservation cannot commit behind a newer generation', async (t) => {
2452
- const testFixture = fixture();
2453
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2454
- const firstRelease = remoteReleaseForVersion('2.16.0', 'b'.repeat(64));
2455
- const secondRelease = remoteReleaseForVersion('2.17.0', 'c'.repeat(64));
2456
- const offered = await checkForUpdate(options(
2457
- testFixture,
2458
- async () => response(firstRelease),
2459
- ));
2460
- const pause = pauseMkdirOnce(operationPath(testFixture, 'pending', 2n));
2461
- t.after(() => pause.restore());
2462
-
2463
- const acknowledgement = acknowledgeUpdate({
2464
- releasePath: testFixture.releasePath,
2465
- cacheDirectory: testFixture.cacheDirectory,
2466
- eventKey: offered.eventKey,
2467
- });
2468
- await pause.reached;
2469
- assert.equal(fs.existsSync(operationPath(testFixture, 'reserved', 2n)), true);
2470
- assert.equal((await checkForUpdate(options(testFixture, async () => response(secondRelease), {
2471
- now: () => baseTime + (73 * 60 * 60 * 1_000),
2472
- }))).latestVersion, '2.17.0');
2473
-
2474
- pause.release();
2475
- assert.deepEqual(await acknowledgement, { status: 'acknowledged', eventKey: offered.eventKey });
2476
- pause.restore();
2477
- assert.equal(fs.existsSync(operationPath(testFixture, 'committed', 2n)), false);
2478
- assert.equal(fs.existsSync(operationPath(testFixture, 'cancelled', 2n)), true);
2479
- assert.equal(fs.existsSync(operationPath(testFixture, 'reserved', 3n)), true);
2480
- assert.equal(fs.existsSync(operationPath(testFixture, 'reserved', 4n)), true);
2481
- assert.deepEqual(await checkForUpdate(options(testFixture, async () => response(firstRelease), {
2482
- now: () => baseTime + (146 * 60 * 60 * 1_000),
2483
- })), { status: 'silent', reason: 'already-notified' });
2484
- });
2485
-
2486
- test('acknowledgement fences an expired refresh and the resumed owner cannot erase it', async (t) => {
2487
- const testFixture = fixture();
2488
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2489
- const offered = await checkForUpdate(options(
2490
- testFixture,
2491
- async () => response(remoteRelease()),
2492
- ));
2493
-
2494
- let resumeRefresh;
2495
- let markRefreshStarted;
2496
- const refreshStarted = new Promise((resolve) => { markRefreshStarted = resolve; });
2497
- const refresh = checkForUpdate(options(testFixture, async () => {
2498
- markRefreshStarted();
2499
- return new Promise((resolve) => { resumeRefresh = resolve; });
2500
- }, {
2501
- now: () => baseTime + (73 * 60 * 60 * 1_000),
2502
- timeoutMs: 2_000,
2503
- }));
2504
- await refreshStarted;
2505
-
2506
- const refreshPending = operationPath(testFixture, 'pending', 2n);
2507
- const staleTime = new Date(Date.now() - 60_000);
2508
- fs.utimesSync(path.join(refreshPending, 'owner.json'), staleTime, staleTime);
2509
- assert.deepEqual(await acknowledgeUpdate({
2510
- releasePath: testFixture.releasePath,
2511
- cacheDirectory: testFixture.cacheDirectory,
2512
- eventKey: offered.eventKey,
2513
- now: () => baseTime + (73 * 60 * 60 * 1_000) + 1,
2514
- }), { status: 'acknowledged', eventKey: offered.eventKey });
2515
-
2516
- resumeRefresh(response(remoteRelease()));
2517
- assert.deepEqual(await refresh, { status: 'silent', reason: 'check-in-progress' });
2518
- assert.deepEqual(await checkForUpdate(options(testFixture, async () => response(remoteRelease()), {
2519
- now: () => baseTime + (71 * 60 * 60 * 1_000),
2520
- })), { status: 'silent', reason: 'already-notified' });
2521
- });
2522
-
2523
- for (const pausePoint of ['pending owner creation', 'state temp creation']) {
2524
- test(`acknowledgement retries when fenced during ${pausePoint}`, async (t) => {
2525
- const testFixture = fixture();
2526
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2527
- const offered = await checkForUpdate(options(
2528
- testFixture,
2529
- async () => response(remoteRelease()),
2530
- ));
2531
- const pending = operationPath(testFixture, 'pending', 2n);
2532
- const pause = pauseOpenOnce((target) => {
2533
- if (pausePoint === 'pending owner creation') {
2534
- return path.resolve(target) === path.join(pending, 'owner.json');
2535
- }
2536
- return path.dirname(path.resolve(target)) === pending
2537
- && /^\.state\..+\.tmp$/.test(path.basename(target));
2538
- });
2539
- t.after(() => pause.restore());
2540
-
2541
- const acknowledgement = acknowledgeUpdate({
2542
- releasePath: testFixture.releasePath,
2543
- cacheDirectory: testFixture.cacheDirectory,
2544
- eventKey: offered.eventKey,
2545
- });
2546
- await pause.reached;
2547
- const staleTime = new Date(Date.now() - 60_000);
2548
- fs.utimesSync(
2549
- pausePoint === 'pending owner creation' ? pending : path.join(pending, 'owner.json'),
2550
- staleTime,
2551
- staleTime,
2552
- );
2553
-
2554
- assert.equal((await checkForUpdate(options(
2555
- testFixture,
2556
- async () => response(remoteRelease()),
2557
- { now: () => baseTime + (73 * 60 * 60 * 1_000) },
2558
- ))).status, 'update_available');
2559
- assert.equal(fs.existsSync(operationPath(testFixture, 'fenced', 2n)), true);
2560
-
2561
- pause.release();
2562
- assert.deepEqual(await acknowledgement, {
2563
- status: 'acknowledged',
2564
- eventKey: offered.eventKey,
2565
- });
2566
- pause.restore();
2567
- });
2568
- }
2569
-
2570
- test('a stale corrupt operation is fenced without exposing a failure', async (t) => {
2571
- const testFixture = fixture();
2572
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2573
- const staleTime = new Date(Date.now() - 60_000);
2574
- const pending = writePendingOperation(testFixture, {
2575
- owner: 'corrupt-owner',
2576
- modifiedAt: staleTime,
2577
- });
2578
-
2579
- const result = await checkForUpdate(options(
2580
- testFixture,
2581
- async () => response(remoteRelease()),
2582
- ));
2583
-
2584
- assert.equal(result.status, 'update_available');
2585
- assert.equal(fs.existsSync(pending), false);
2586
- assert.equal(fs.existsSync(operationPath(testFixture, 'fenced', 1n)), true);
2587
- });
2588
-
2589
- test('malformed active-claim shapes recover after the hard lease', async () => {
2590
- for (const shape of ['file', 'symlink', 'owner-directory']) {
2591
- const testFixture = fixture();
2592
- try {
2593
- const directory = stateDirectory(testFixture);
2594
- const activeClaim = path.join(directory, 'active-claim');
2595
- fs.mkdirSync(directory, { recursive: true });
2596
- if (shape === 'file') {
2597
- fs.writeFileSync(activeClaim, 'corrupt claim');
2598
- } else if (shape === 'symlink') {
2599
- fs.symlinkSync(path.join(testFixture.root, 'missing-claim-target'), activeClaim);
2600
- } else {
2601
- fs.mkdirSync(path.join(activeClaim, 'owner.json'), { recursive: true });
2602
- }
2603
- let requests = 0;
2604
- const fetchImpl = async () => {
2605
- requests += 1;
2606
- return response(remoteRelease());
2607
- };
2608
-
2609
- assert.deepEqual(await checkForUpdate(options(testFixture, fetchImpl)), {
2610
- status: 'silent',
2611
- reason: 'check-in-progress',
2612
- }, `${shape} should retain a fresh hard lease`);
2613
- assert.equal(requests, 0);
2614
-
2615
- const staleTime = new Date(Date.now() - 60_000);
2616
- if (shape === 'symlink') fs.lutimesSync(activeClaim, staleTime, staleTime);
2617
- else if (shape === 'owner-directory') {
2618
- fs.utimesSync(path.join(activeClaim, 'owner.json'), staleTime, staleTime);
2619
- } else fs.utimesSync(activeClaim, staleTime, staleTime);
2620
-
2621
- assert.equal((await checkForUpdate(options(testFixture, fetchImpl))).status, 'update_available');
2622
- assert.equal(requests, 1, `${shape} should be retired after its hard lease`);
2623
- } finally {
2624
- fs.rmSync(testFixture.root, { recursive: true, force: true });
2625
- }
2626
- }
2627
- });
2628
-
2629
- test('a delayed corrupt-claim retirement cannot move a successor active claim', async (t) => {
2630
- const testFixture = fixture();
2631
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2632
- const directory = stateDirectory(testFixture);
2633
- const activeClaim = path.join(directory, 'active-claim');
2634
- fs.mkdirSync(activeClaim, { recursive: true });
2635
- const staleTime = new Date(Date.now() - 60_000);
2636
- fs.utimesSync(activeClaim, staleTime, staleTime);
2637
- let firstRetirement = null;
2638
- const pause = pauseRenameMatchingOnce((source, destination) => {
2639
- if (path.resolve(source) !== activeClaim
2640
- || !path.basename(path.dirname(destination)).startsWith('retired-claim-corrupt-')) {
2641
- return false;
2642
- }
2643
- firstRetirement = destination;
2644
- return true;
2645
- });
2646
- t.after(() => pause.restore());
2647
-
2648
- const delayed = checkForUpdate(options(
2649
- testFixture,
2650
- async () => response(remoteRelease()),
2651
- ));
2652
- await pause.reached;
2653
- fs.utimesSync(activeClaim, staleTime, staleTime);
2654
- fs.utimesSync(
2655
- path.join(operationPath(testFixture, 'pending', 1n), 'owner.json'),
2656
- staleTime,
2657
- staleTime,
2658
- );
2659
-
2660
- assert.equal((await checkForUpdate(options(
2661
- testFixture,
2662
- async () => response(remoteRelease()),
2663
- ))).status, 'update_available');
2664
- assert.equal(
2665
- JSON.parse(fs.readFileSync(path.join(activeClaim, 'owner.json'), 'utf8')).generation,
2666
- '2',
2667
- );
2668
-
2669
- pause.release();
2670
- assert.equal((await delayed).status, 'update_available');
2671
- pause.restore();
2672
- assert.equal(firstRetirement !== null, true);
2673
- assert.equal(fs.existsSync(activeClaim), true);
2674
- assert.equal(
2675
- JSON.parse(fs.readFileSync(path.join(activeClaim, 'owner.json'), 'utf8')).generation,
2676
- '2',
2677
- );
2678
- });
2679
-
2680
- test('repeated corrupt claim contents receive distinct retirement identities', async (t) => {
2681
- const testFixture = fixture();
2682
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2683
- const directory = stateDirectory(testFixture);
2684
- const activeClaim = path.join(directory, 'active-claim');
2685
- fs.mkdirSync(directory, { recursive: true });
2686
- fs.writeFileSync(activeClaim, 'same corrupt claim');
2687
- const staleTime = new Date(Date.now() - 60_000);
2688
- fs.utimesSync(activeClaim, staleTime, staleTime);
2689
- let requests = 0;
2690
- const fetchImpl = async () => {
2691
- requests += 1;
2692
- return response(remoteRelease());
2693
- };
2694
-
2695
- assert.equal((await checkForUpdate(options(testFixture, fetchImpl))).status, 'update_available');
2696
- fs.renameSync(activeClaim, path.join(directory, 'test-completed-active-claim'));
2697
- fs.writeFileSync(activeClaim, 'same corrupt claim');
2698
- fs.utimesSync(activeClaim, staleTime, staleTime);
2699
- assert.equal((await checkForUpdate(options(testFixture, fetchImpl, {
2700
- now: () => baseTime + (73 * 60 * 60 * 1_000),
2701
- }))).status, 'update_available');
2702
-
2703
- assert.equal(requests, 2);
2704
- assert.equal(
2705
- fs.readdirSync(directory).filter((name) => name.startsWith('retired-claim-corrupt-')).length,
2706
- 2,
2707
- );
2708
- });
2709
-
2710
- test('corrupt claim retirement preserves distinct 64-bit inode identities', async (t) => {
2711
- const testFixture = fixture();
2712
- const originalLstat = fsPromises.lstat;
2713
- t.after(() => {
2714
- fsPromises.lstat = originalLstat;
2715
- fs.rmSync(testFixture.root, { recursive: true, force: true });
2716
- });
2717
- const directory = stateDirectory(testFixture);
2718
- const activeClaim = path.join(directory, 'active-claim');
2719
- fs.mkdirSync(directory, { recursive: true });
2720
- fs.writeFileSync(activeClaim, 'corrupt claim');
2721
- const staleTime = new Date(Date.now() - 60_000);
2722
- fs.utimesSync(activeClaim, staleTime, staleTime);
2723
- let instance = 1n;
2724
- fsPromises.lstat = async (target, ...args) => {
2725
- const metadata = await originalLstat(target, ...args);
2726
- const resolved = path.resolve(target);
2727
- const isCorruptClaimInstance = resolved === path.resolve(activeClaim)
2728
- || (path.basename(resolved) === 'active'
2729
- && path.basename(path.dirname(resolved)).startsWith('retired-claim-corrupt-'));
2730
- if (!isCorruptClaimInstance || !metadata.isFile()) return metadata;
2731
- const inode = 9_007_199_254_740_992n + (instance - 1n);
2732
- return args[0]?.bigint
2733
- ? metadataWithOverrides(metadata, {
2734
- ino: inode,
2735
- birthtimeNs: 1_000n,
2736
- ctimeNs: 2_000n,
2737
- })
2738
- : metadataWithOverrides(metadata, {
2739
- ino: Number(inode),
2740
- birthtimeMs: 1,
2741
- ctimeMs: 2,
2742
- });
2743
- };
2744
- let requests = 0;
2745
- const fetchImpl = async () => {
2746
- requests += 1;
2747
- return response(remoteRelease());
2748
- };
2749
-
2750
- assert.equal((await checkForUpdate(options(testFixture, fetchImpl))).status, 'update_available');
2751
- fs.renameSync(activeClaim, path.join(directory, 'test-completed-active-claim'));
2752
- fs.writeFileSync(activeClaim, 'corrupt claim');
2753
- fs.utimesSync(activeClaim, staleTime, staleTime);
2754
- instance = 2n;
2755
- assert.equal((await checkForUpdate(options(testFixture, fetchImpl, {
2756
- now: () => baseTime + (73 * 60 * 60 * 1_000),
2757
- }))).status, 'update_available');
2758
-
2759
- assert.equal(requests, 2);
2760
- assert.equal(
2761
- fs.readdirSync(directory).filter((name) => name.startsWith('retired-claim-corrupt-')).length,
2762
- 2,
2763
- );
2764
- });
2765
-
2766
- test('a forged retirement symlink cannot move an active claim outside its cache partition', async (t) => {
2767
- const testFixture = fixture();
2768
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2769
- let requests = 0;
2770
- const fetchImpl = async () => {
2771
- requests += 1;
2772
- return response(remoteRelease());
2773
- };
2774
- assert.equal((await checkForUpdate(options(testFixture, fetchImpl))).status, 'update_available');
2775
-
2776
- const outside = path.join(testFixture.root, 'outside');
2777
- fs.mkdirSync(outside);
2778
- fs.symlinkSync(outside, path.join(stateDirectory(testFixture), 'retired-claim-1'));
2779
- const refreshOptions = options(testFixture, fetchImpl, {
2780
- now: () => baseTime + (73 * 60 * 60 * 1_000),
2781
- });
2782
- assert.equal((await checkForUpdate(refreshOptions)).status, 'update_available');
2783
- assert.deepEqual(fs.readdirSync(outside), []);
2784
- assert.equal(requests, 1);
2785
-
2786
- fs.unlinkSync(path.join(stateDirectory(testFixture), 'retired-claim-1'));
2787
- assert.equal((await checkForUpdate(refreshOptions)).status, 'update_available');
2788
- assert.deepEqual(fs.readdirSync(outside), []);
2789
- assert.equal(requests, 2);
2790
- });
2791
-
2792
- test('an expired operation is fenced even when its PID was reused by a live process', async (t) => {
2793
- const testFixture = fixture();
2794
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2795
- const staleTime = new Date(Date.now() - 60_000);
2796
- const pending = writePendingOperation(testFixture, { modifiedAt: staleTime });
2797
- let requests = 0;
2798
-
2799
- const result = await checkForUpdate(options(testFixture, async () => {
2800
- requests += 1;
2801
- return response(remoteRelease());
2802
- }));
2803
-
2804
- assert.equal(result.status, 'update_available');
2805
- assert.equal(requests, 1);
2806
- assert.equal(fs.existsSync(pending), false);
2807
- assert.equal(fs.existsSync(operationPath(testFixture, 'fenced', 1n)), true);
2808
- });
2809
-
2810
- test('a corrupt future-dated operation cannot suppress checks indefinitely', async (t) => {
2811
- const testFixture = fixture();
2812
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2813
- const futureTime = new Date(Date.now() + (365 * 24 * 60 * 60 * 1_000));
2814
- const pending = writePendingOperation(testFixture, {
2815
- owner: 'corrupt-owner',
2816
- modifiedAt: futureTime,
2817
- });
2818
-
2819
- const result = await checkForUpdate(options(
2820
- testFixture,
2821
- async () => response(remoteRelease()),
2822
- ));
2823
-
2824
- assert.equal(result.status, 'update_available');
2825
- assert.equal(fs.existsSync(pending), false);
2826
- assert.equal(fs.existsSync(operationPath(testFixture, 'fenced', 1n)), true);
2827
- });
2828
-
2829
- test('a newly-created operation with slight clock skew is never fenced as stale', async (t) => {
2830
- const testFixture = fixture();
2831
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2832
- const nearFuture = new Date(Date.now() + 1_000);
2833
- const pending = writePendingOperation(testFixture, {
2834
- owner: 'writer-has-not-finished-the-record',
2835
- modifiedAt: nearFuture,
2836
- });
2837
- let requests = 0;
2838
-
2839
- const result = await checkForUpdate(options(testFixture, async () => {
2840
- requests += 1;
2841
- return response(remoteRelease());
2842
- }));
2843
-
2844
- assert.deepEqual(result, { status: 'silent', reason: 'check-in-progress' });
2845
- assert.equal(requests, 0);
2846
- assert.equal(fs.existsSync(pending), true);
2847
- });
2848
-
2849
- test('a fenced stale owner cannot overwrite a newer committed candidate after resuming', async (t) => {
2850
- const testFixture = fixture();
2851
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2852
- let resumeOldOwner;
2853
- let markOldStarted;
2854
- const oldStarted = new Promise((resolve) => { markOldStarted = resolve; });
2855
- const oldCheck = checkForUpdate(options(testFixture, async () => {
2856
- markOldStarted();
2857
- return new Promise((resolve) => { resumeOldOwner = resolve; });
2858
- }, { timeoutMs: 2_000 }));
2859
- await oldStarted;
2860
-
2861
- const oldPending = operationPath(testFixture, 'pending', 1n);
2862
- const staleTime = new Date(Date.now() - 60_000);
2863
- fs.utimesSync(path.join(oldPending, 'owner.json'), staleTime, staleTime);
2864
-
2865
- const newer = await checkForUpdate(options(
2866
- testFixture,
2867
- async () => response(remoteReleaseForVersion('3.0.0', 'c'.repeat(64))),
2868
- ));
2869
- assert.equal(newer.status, 'update_available');
2870
- assert.equal(newer.latestVersion, '3.0.0');
2871
-
2872
- resumeOldOwner(response(remoteReleaseForVersion('2.16.0', 'b'.repeat(64))));
2873
- assert.deepEqual(await oldCheck, { status: 'silent', reason: 'check-in-progress' });
2874
- const persisted = JSON.parse(fs.readFileSync(statePath(testFixture), 'utf8'));
2875
- assert.equal(persisted.candidate.version, '3.0.0');
2876
- assert.equal(fs.existsSync(operationPath(testFixture, 'fenced', 1n)), true);
2877
- });
2878
-
2879
- test('different installed versions share no acknowledgement or cache-reset state', async (t) => {
2880
- const testFixture = fixture('2.15.0');
2881
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2882
- const olderReleasePath = path.join(testFixture.root, 'older-skill-release.json');
2883
- writeJson(olderReleasePath, localRelease('2.14.0'));
2884
- let requests = 0;
2885
- const fetchImpl = async () => {
2886
- requests += 1;
2887
- return response(remoteRelease());
2888
- };
2889
-
2890
- const newerInstall = await checkForUpdate(options(testFixture, fetchImpl));
2891
- assert.equal(newerInstall.status, 'update_available');
2892
- assert.deepEqual(await acknowledgeUpdate({
2893
- releasePath: testFixture.releasePath,
2894
- cacheDirectory: testFixture.cacheDirectory,
2895
- eventKey: newerInstall.eventKey,
2896
- now: () => baseTime + 1_000,
2897
- }), { status: 'acknowledged', eventKey: newerInstall.eventKey });
2898
-
2899
- const olderInstallOptions = options(testFixture, fetchImpl, { releasePath: olderReleasePath });
2900
- const olderInstall = await checkForUpdate(olderInstallOptions);
2901
- assert.equal(olderInstall.status, 'update_available');
2902
- assert.equal(olderInstall.installedVersion, '2.14.0');
2903
-
2904
- assert.deepEqual(await checkForUpdate(options(testFixture, fetchImpl)), {
2905
- status: 'silent',
2906
- reason: 'already-notified',
2907
- });
2908
- const olderAgain = await checkForUpdate(olderInstallOptions);
2909
- assert.equal(olderAgain.status, 'update_available');
2910
- assert.equal(requests, 2, 'each installed version should retain its own fresh cache');
2911
- assert.equal(fs.existsSync(statePath(testFixture, '2.15.0')), true);
2912
- assert.equal(fs.existsSync(statePath(testFixture, '2.14.0')), true);
2913
- });
2914
-
2915
- test('an identity mismatch is rejected silently and never cached as a candidate', async (t) => {
2916
- const testFixture = fixture();
2917
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2918
-
2919
- const result = await checkForUpdate(options(
2920
- testFixture,
2921
- async () => response(remoteRelease({ skillId: 'different-skill' })),
2922
- ));
2923
-
2924
- assert.deepEqual(result, { status: 'silent', reason: 'invalid-manifest' });
2925
- const state = JSON.parse(fs.readFileSync(statePath(testFixture), 'utf8'));
2926
- assert.equal(Object.hasOwn(state, 'candidate'), false);
2927
- });
2928
-
2929
- test('release notes must byte-match the exact trusted GitHub URL', async () => {
2930
- const base = 'https://github.com/tt-a1i/archify/releases/tag/v2.16.0';
2931
- for (const releaseNotes of [
2932
- 'https://github.com:443/tt-a1i/archify/releases/tag/v2.16.0',
2933
- 'https://github.com:444/tt-a1i/archify/releases/tag/v2.16.0',
2934
- `${base}?`,
2935
- `${base}#`,
2936
- `${base}?source=manifest`,
2937
- 'https://GITHUB.COM/tt-a1i/archify/releases/tag/v2.16.0',
2938
- ]) {
2939
- const testFixture = fixture();
2940
- try {
2941
- const result = await checkForUpdate(options(
2942
- testFixture,
2943
- async () => response(remoteRelease({ releaseNotes })),
2944
- ));
2945
- assert.deepEqual(result, { status: 'silent', reason: 'invalid-manifest' }, releaseNotes);
2946
- } finally {
2947
- fs.rmSync(testFixture.root, { recursive: true, force: true });
2948
- }
2949
- }
2950
- });
2951
-
2952
- test('malformed release notes are classified as an invalid manifest', async (t) => {
2953
- const testFixture = fixture();
2954
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
2955
-
2956
- const result = await checkForUpdate(options(
2957
- testFixture,
2958
- async () => response(remoteRelease({ releaseNotes: 'not-a-url' })),
2959
- ));
2960
-
2961
- assert.deepEqual(result, { status: 'silent', reason: 'invalid-manifest' });
2962
- });
2963
-
2964
- test('publishedAt must use canonical UTC seconds and a real calendar date', async () => {
2965
- for (const publishedAt of [
2966
- '2026-08-28T15:00:00+08:00',
2967
- '2026-08-28',
2968
- '2026-02-30T00:00:00Z',
2969
- ]) {
2970
- const testFixture = fixture();
2971
- try {
2972
- const result = await checkForUpdate(options(
2973
- testFixture,
2974
- async () => response(remoteRelease({ publishedAt })),
2975
- ));
2976
- assert.deepEqual(result, { status: 'silent', reason: 'invalid-manifest' }, publishedAt);
2977
- } finally {
2978
- fs.rmSync(testFixture.root, { recursive: true, force: true });
2979
- }
2980
- }
2981
- });
2982
-
2983
- test('stable candidates cannot use prerelease or build metadata', async () => {
2984
- for (const version of ['2.16.0-dev.1', '2.16.0+build.1']) {
2985
- const testFixture = fixture();
2986
- try {
2987
- const result = await checkForUpdate(options(
2988
- testFixture,
2989
- async () => response(remoteReleaseForVersion(version)),
2990
- ));
2991
- assert.deepEqual(result, { status: 'silent', reason: 'invalid-manifest' }, version);
2992
- } finally {
2993
- fs.rmSync(testFixture.root, { recursive: true, force: true });
2994
- }
2995
- }
2996
- });
2997
-
2998
- test('an invalid local identity fails before any network disclosure', async (t) => {
2999
- const testFixture = fixture();
3000
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
3001
- writeJson(testFixture.releasePath, {
3002
- ...localRelease(),
3003
- source: { repository: 'https://example.com/untrusted/archify' },
3004
- });
3005
- let requests = 0;
3006
-
3007
- const result = await checkForUpdate(options(testFixture, async () => {
3008
- requests += 1;
3009
- return response(remoteRelease());
3010
- }));
3011
-
3012
- assert.deepEqual(result, { status: 'silent', reason: 'invalid-local-release' });
3013
- assert.equal(requests, 0);
3014
- });
3015
-
3016
- test('a bounded timeout fails silently without retrying', async (t) => {
3017
- const testFixture = fixture();
3018
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
3019
- let requests = 0;
3020
- const fetchImpl = (_url, init) => new Promise((_resolve, reject) => {
3021
- requests += 1;
3022
- init.signal.addEventListener('abort', () => {
3023
- const error = new Error('aborted');
3024
- error.name = 'AbortError';
3025
- reject(error);
3026
- }, { once: true });
3027
- });
3028
-
3029
- const result = await checkForUpdate(options(testFixture, fetchImpl, { timeoutMs: 10 }));
3030
- assert.deepEqual(result, { status: 'silent', reason: 'check-failed' });
3031
- assert.equal(requests, 1);
3032
- });
3033
-
3034
- test('non-success responses cancel their unread body before failing silently', async (t) => {
3035
- const testFixture = fixture();
3036
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
3037
- let cancelled = false;
3038
-
3039
- const result = await checkForUpdate(options(testFixture, async () => ({
3040
- status: 503,
3041
- headers: new Headers(),
3042
- body: {
3043
- async cancel() {
3044
- cancelled = true;
3045
- },
3046
- },
3047
- })));
3048
-
3049
- assert.deepEqual(result, { status: 'silent', reason: 'check-failed' });
3050
- assert.equal(cancelled, true);
3051
- });
3052
-
3053
- test('media-type and declared-size rejection cancel unread response bodies', async () => {
3054
- for (const headers of [
3055
- { 'content-type': 'text/html' },
3056
- { 'content-type': 'application/json', 'content-length': String((32 * 1024) + 1) },
3057
- ]) {
3058
- const testFixture = fixture();
3059
- let cancelled = false;
3060
- try {
3061
- const result = await checkForUpdate(options(testFixture, async () => ({
3062
- status: 200,
3063
- headers: new Headers(headers),
3064
- body: {
3065
- async cancel() {
3066
- cancelled = true;
3067
- },
3068
- },
3069
- })));
3070
- assert.deepEqual(result, { status: 'silent', reason: 'invalid-manifest' });
3071
- assert.equal(cancelled, true);
3072
- } finally {
3073
- fs.rmSync(testFixture.root, { recursive: true, force: true });
3074
- }
3075
- }
3076
- });
3077
-
3078
- test('response stream failures are classified as network failures', async (t) => {
3079
- const testFixture = fixture();
3080
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
3081
-
3082
- const result = await checkForUpdate(options(testFixture, async () => ({
3083
- status: 200,
3084
- headers: new Headers({ 'content-type': 'application/json' }),
3085
- body: {
3086
- getReader() {
3087
- return {
3088
- async read() {
3089
- throw new TypeError('socket closed while reading');
3090
- },
3091
- releaseLock() {},
3092
- };
3093
- },
3094
- },
3095
- })));
3096
-
3097
- assert.deepEqual(result, { status: 'silent', reason: 'check-failed' });
3098
- });
3099
-
3100
- test('invalid UTF-8 is classified as an invalid manifest rather than a network failure', async (t) => {
3101
- const testFixture = fixture();
3102
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
3103
- const result = await checkForUpdate(options(
3104
- testFixture,
3105
- async () => new Response(new Uint8Array([0xff]), {
3106
- status: 200,
3107
- headers: { 'content-type': 'application/json' },
3108
- }),
3109
- ));
3110
- assert.deepEqual(result, { status: 'silent', reason: 'invalid-manifest' });
3111
- });
3112
-
3113
- test('chunked responses above 32 KiB are rejected instead of fully trusted', async (t) => {
3114
- const testFixture = fixture();
3115
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
3116
- const oversized = `${JSON.stringify(remoteRelease())}${' '.repeat(33 * 1024)}`;
3117
-
3118
- const result = await checkForUpdate(options(
3119
- testFixture,
3120
- async () => response(oversized),
3121
- ));
3122
-
3123
- assert.deepEqual(result, { status: 'silent', reason: 'invalid-manifest' });
3124
- });
3125
-
3126
- test('a poisoned far-future cache timestamp cannot suppress checks indefinitely', async (t) => {
3127
- const testFixture = fixture();
3128
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
3129
- await checkForUpdate(options(testFixture, async () => response(remoteRelease())));
3130
- const cacheStatePath = statePath(testFixture);
3131
- const state = JSON.parse(fs.readFileSync(cacheStatePath, 'utf8'));
3132
- state.check.nextCheckAt = '9999-12-31T23:59:59.000Z';
3133
- writeJson(cacheStatePath, state);
3134
- let requests = 0;
3135
-
3136
- const result = await checkForUpdate(options(testFixture, async () => {
3137
- requests += 1;
3138
- return response(remoteRelease());
3139
- }));
3140
-
3141
- assert.equal(result.status, 'update_available');
3142
- assert.equal(requests, 1);
3143
- });
3144
-
3145
- test('a semantically corrupt cached candidate discards legacy validators and rebuilds unconditionally', async (t) => {
3146
- const testFixture = fixture();
3147
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
3148
- await checkForUpdate(options(testFixture, async () => response(remoteRelease())));
3149
- const cacheStatePath = statePath(testFixture);
3150
- const state = JSON.parse(fs.readFileSync(cacheStatePath, 'utf8'));
3151
- state.candidate.targetDigest = 'sha256:corrupt';
3152
- state.check.etag = '"legacy-tracker"';
3153
- state.check.nextCheckAt = '9999-12-31T23:59:59.000Z';
3154
- writeJson(cacheStatePath, state);
3155
- let conditionalHeader = 'not-observed';
3156
-
3157
- const result = await checkForUpdate(options(testFixture, async (_url, init) => {
3158
- conditionalHeader = init.headers['if-none-match'];
3159
- return response(remoteRelease());
3160
- }));
3161
-
3162
- assert.equal(result.status, 'update_available');
3163
- assert.equal(conditionalHeader, undefined);
3164
- assert.equal(
3165
- JSON.parse(fs.readFileSync(statePath(testFixture), 'utf8')).candidate.targetDigest,
3166
- result.targetDigest,
3167
- );
3168
- });
3169
-
3170
- test('a newer cached candidate without offered or acknowledged provenance is rebuilt', async (t) => {
3171
- const testFixture = fixture();
3172
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
3173
- writeJson(statePath(testFixture), {
3174
- schemaVersion: 1,
3175
- skillId: 'archify',
3176
- installedVersion: '2.15.0',
3177
- check: {
3178
- nextCheckAt: new Date(baseTime + (24 * 60 * 60 * 1_000)).toISOString(),
3179
- consecutiveFailures: 0,
3180
- },
3181
- notification: {
3182
- offeredDigests: [],
3183
- acknowledgedDigests: [],
3184
- },
3185
- candidate: {
3186
- version: '2.16.0',
3187
- targetDigest: `sha256:${'b'.repeat(64)}`,
3188
- severity: 'normal',
3189
- releaseNotes: 'https://github.com/tt-a1i/archify/releases/tag/v2.16.0',
3190
- },
3191
- });
3192
- let requests = 0;
3193
-
3194
- const result = await checkForUpdate(options(testFixture, async () => {
3195
- requests += 1;
3196
- return response(remoteReleaseForVersion('2.15.0', 'd'.repeat(64)));
3197
- }));
3198
-
3199
- assert.deepEqual(result, { status: 'silent', reason: 'current' });
3200
- assert.equal(requests, 1, 'semantic corruption must not produce an unacknowledgeable cached notice');
3201
- });
3202
-
3203
- test('corrupt cache is rebuilt without exposing an error to the user', async (t) => {
3204
- const testFixture = fixture();
3205
- t.after(() => fs.rmSync(testFixture.root, { recursive: true, force: true }));
3206
- fs.mkdirSync(stateDirectory(testFixture), { recursive: true });
3207
- fs.mkdirSync(path.dirname(statePath(testFixture)), { recursive: true });
3208
- fs.writeFileSync(statePath(testFixture), '{not-json');
3209
-
3210
- const result = await checkForUpdate(options(
3211
- testFixture,
3212
- async () => response(remoteRelease({ version: '2.15.0' })),
3213
- ));
3214
-
3215
- assert.deepEqual(result, { status: 'silent', reason: 'invalid-manifest' });
3216
- assert.doesNotThrow(() => JSON.parse(
3217
- fs.readFileSync(statePath(testFixture), 'utf8'),
3218
- ));
3219
- });
3220
-
3221
- test('disabled CLI returns one silent JSON line and never needs the network', () => {
3222
- const result = spawnSync(process.execPath, [checkerPath], {
3223
- cwd: skillRoot,
3224
- encoding: 'utf8',
3225
- env: { ...process.env, ARCHIFY_UPDATE_CHECK_DISABLED: '1' },
3226
- });
3227
-
3228
- assert.equal(result.status, 0, result.stderr);
3229
- assert.deepEqual(JSON.parse(result.stdout), { status: 'silent', reason: 'disabled' });
3230
- assert.equal(result.stdout.trim().split('\n').length, 1);
3231
- });
3232
-
3233
- test('CLI acknowledgement emits the documented one-line success schema', async (t) => {
3234
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'archify-update-cli-ack-'));
3235
- t.after(() => fs.rmSync(root, { recursive: true, force: true }));
3236
- const home = path.join(root, 'home');
3237
- const xdg = path.join(root, 'xdg-cache');
3238
- const localData = path.join(root, 'local-data');
3239
- for (const directory of [home, xdg, localData]) fs.mkdirSync(directory, { recursive: true });
3240
- const cacheDirectory = process.platform === 'win32'
3241
- ? path.join(localData, 'archify-skill')
3242
- : process.platform === 'darwin'
3243
- ? path.join(home, 'Library', 'Caches', 'archify-skill')
3244
- : path.join(xdg, 'archify-skill');
3245
- const releasePath = path.join(skillRoot, 'skill-release.json');
3246
- const installedRelease = JSON.parse(fs.readFileSync(releasePath, 'utf8'));
3247
- const [major, minor, patch] = parseSemver(installedRelease.version).core;
3248
- const candidateVersion = `${major}.${minor}.${BigInt(patch) + 1n}`;
3249
- const offered = await checkForUpdate({
3250
- releasePath,
3251
- cacheDirectory,
3252
- fetchImpl: async () => response(remoteReleaseForVersion(candidateVersion)),
3253
- now: () => baseTime,
3254
- random: () => 0.5,
3255
- timeoutMs: 50,
3256
- });
3257
- assert.equal(offered.status, 'update_available');
3258
-
3259
- const acknowledgement = spawnSync(process.execPath, [checkerPath, '--ack', offered.eventKey], {
3260
- cwd: skillRoot,
3261
- encoding: 'utf8',
3262
- env: {
3263
- ...process.env,
3264
- HOME: home,
3265
- USERPROFILE: home,
3266
- XDG_CACHE_HOME: xdg,
3267
- LOCALAPPDATA: localData,
3268
- },
3269
- });
3270
- assert.equal(acknowledgement.status, 0, acknowledgement.stderr);
3271
- assert.deepEqual(JSON.parse(acknowledgement.stdout), {
3272
- status: 'acknowledged',
3273
- eventKey: offered.eventKey,
3274
- });
3275
- assert.equal(acknowledgement.stdout.trim().split('\n').length, 1);
3276
- });
3277
-
3278
- test('CLI entry detection survives a realpath or symlink alias', (t) => {
3279
- const aliasRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'archify-update-cli-alias-'));
3280
- t.after(() => fs.rmSync(aliasRoot, { recursive: true, force: true }));
3281
- const aliasPath = path.join(aliasRoot, 'check-update-alias.mjs');
3282
- try {
3283
- fs.symlinkSync(checkerPath, aliasPath);
3284
- } catch (error) {
3285
- if (['EPERM', 'EACCES', 'ENOTSUP'].includes(error?.code)) {
3286
- t.skip(`symlinks unavailable: ${error.code}`);
3287
- return;
3288
- }
3289
- throw error;
3290
- }
3291
-
3292
- const result = spawnSync(process.execPath, [aliasPath], {
3293
- cwd: aliasRoot,
3294
- encoding: 'utf8',
3295
- env: { ...process.env, ARCHIFY_UPDATE_CHECK_DISABLED: '1' },
3296
- });
3297
- assert.equal(result.status, 0, result.stderr);
3298
- assert.deepEqual(JSON.parse(result.stdout), { status: 'silent', reason: 'disabled' });
3299
- });
3300
-
3301
- test('notifier source has no process execution or remote-origin override surface', () => {
3302
- const checkerSource = fs.readFileSync(checkerPath, 'utf8');
3303
- const contractSource = fs.readFileSync(contractPath, 'utf8');
3304
- const combinedSource = `${checkerSource}\n${contractSource}`;
3305
- assert.doesNotMatch(combinedSource, /(?:node:)?child_process/);
3306
- assert.doesNotMatch(combinedSource, /(?:^|[^\w.])(?:spawn|exec|execFile|fork)(?:Sync)?\s*\(/m);
3307
- assert.doesNotMatch(contractSource, /\bfetch\s*\(/);
3308
- const environmentReads = [...combinedSource.matchAll(/process\.env\.([A-Z0-9_]+)/g)]
3309
- .map((match) => match[1])
3310
- .sort();
3311
- assert.deepEqual(environmentReads, [
3312
- 'ARCHIFY_UPDATE_CHECK_DISABLED',
3313
- 'LOCALAPPDATA',
3314
- 'XDG_CACHE_HOME',
3315
- ]);
3316
- assert.doesNotMatch(combinedSource, /updateCommand/);
3317
- });