create-yss-spec 3.0.0 → 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 (1508) 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/code-review/SKILL.md +1 -1
  7. package/template/.agents/skills/code-review/references/yss-review-standards.md +1 -1
  8. package/template/.agents/skills/lombok/SKILL.md +1 -1
  9. package/template/.agents/skills/mapstruct/SKILL.md +1 -1
  10. package/template/.agents/skills/prototype-review/SKILL.md +7 -6
  11. package/template/.agents/skills/yss-antd-design/SKILL.md +8 -7
  12. package/template/.agents/skills/yss-antd-design/references/evidence.md +11 -6
  13. package/template/.agents/skills/yss-api-integration/SKILL.md +2 -2
  14. package/template/.agents/skills/yss-application/SKILL.md +2 -2
  15. package/template/.agents/skills/yss-audit-log/SKILL.md +2 -2
  16. package/template/.agents/skills/yss-audit-log/references/source-index.md +1 -1
  17. package/template/.agents/skills/yss-cache/SKILL.md +2 -2
  18. package/template/.agents/skills/yss-cache/references/source-index.md +1 -1
  19. package/template/.agents/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  20. package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +6 -6
  21. package/template/.agents/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  22. package/template/.agents/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
  23. package/template/.agents/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +3 -3
  24. package/template/.agents/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +1 -1
  25. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +3 -3
  26. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +3 -3
  27. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +8 -8
  28. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +1 -1
  29. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  30. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +8 -8
  31. package/template/.agents/skills/yss-design-system/SKILL.md +6 -6
  32. package/template/.agents/skills/yss-distributed-id/SKILL.md +2 -2
  33. package/template/.agents/skills/yss-distributed-id/references/source-index.md +1 -1
  34. package/template/.agents/skills/yss-domain/SKILL.md +1 -1
  35. package/template/.agents/skills/yss-dto/SKILL.md +2 -2
  36. package/template/.agents/skills/yss-dto/references/source-index.md +1 -1
  37. package/template/.agents/skills/yss-excel-mvc/SKILL.md +2 -2
  38. package/template/.agents/skills/yss-excel-mvc/references/source-index.md +1 -1
  39. package/template/.agents/skills/yss-exception/SKILL.md +1 -1
  40. package/template/.agents/skills/yss-exception/references/source-index.md +1 -1
  41. package/template/.agents/skills/{yss-router → yss-implementation-contract-compiler}/SKILL.md +22 -18
  42. package/template/.agents/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  43. package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  44. package/template/{.codex/skills/yss-router/references/router-contract.yaml → .agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml} +59 -94
  45. package/template/{.codex/skills/yss-router → .agents/skills/yss-implementation-contract-compiler}/references/slice-implementation-contract.md +20 -8
  46. package/template/.agents/skills/{yss-router → yss-implementation-contract-compiler}/references/yss-skill-execution-result.md +5 -3
  47. package/template/.agents/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  48. package/template/.agents/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  49. package/template/.agents/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  50. package/template/.agents/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  51. package/template/.agents/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  52. package/template/.agents/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  53. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  54. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  55. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  56. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  57. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  58. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  59. package/template/.agents/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  60. package/template/.agents/skills/yss-mybatis/SKILL.md +2 -2
  61. package/template/.agents/skills/yss-mybatis/references/source-index.md +1 -1
  62. package/template/.agents/skills/yss-page-module-development/SKILL.md +1 -1
  63. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +6 -6
  64. package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
  65. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +25 -17
  66. package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +5 -5
  67. package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +1 -1
  68. package/template/.agents/skills/yss-prototype-stage/SKILL.md +34 -24
  69. package/template/.agents/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  70. package/template/.agents/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  71. package/template/.agents/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  72. package/template/.agents/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  73. package/template/.agents/skills/yss-repository/SKILL.md +2 -2
  74. package/template/.agents/skills/yss-resilience4j/SKILL.md +1 -1
  75. package/template/.agents/skills/yss-resilience4j/references/source-index.md +1 -1
  76. package/template/.agents/skills/yss-security-algorithm/SKILL.md +1 -1
  77. package/template/.agents/skills/yss-security-algorithm/references/source-index.md +1 -1
  78. package/template/.agents/skills/{yss-source-index → yss-skill-source-index-refresh}/SKILL.md +4 -4
  79. package/template/.agents/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  80. package/template/{.claude/skills/yss-source-index → .agents/skills/yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.mjs +1 -1
  81. package/template/.agents/skills/yss-ui/SKILL.md +2 -1
  82. package/template/.agents/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  83. package/template/.agents/skills/yss-up-springboot3/SKILL.md +1 -1
  84. package/template/.agents/skills/yss-userinfo/SKILL.md +1 -1
  85. package/template/.agents/skills/yss-userinfo/references/source-index.md +1 -1
  86. package/template/.agents/skills/yss-validation/SKILL.md +1 -1
  87. package/template/.agents/skills/yss-validation/references/source-index.md +1 -1
  88. package/template/.agents/skills/yss-web-controller/SKILL.md +2 -2
  89. package/template/.claude/skills/code-review/SKILL.md +1 -1
  90. package/template/.claude/skills/code-review/references/yss-review-standards.md +1 -1
  91. package/template/.claude/skills/lombok/SKILL.md +1 -1
  92. package/template/.claude/skills/mapstruct/SKILL.md +1 -1
  93. package/template/.claude/skills/prototype-review/SKILL.md +7 -6
  94. package/template/.claude/skills/yss-antd-design/SKILL.md +8 -7
  95. package/template/.claude/skills/yss-antd-design/references/evidence.md +11 -6
  96. package/template/.claude/skills/yss-api-integration/SKILL.md +2 -2
  97. package/template/.claude/skills/yss-application/SKILL.md +2 -2
  98. package/template/.claude/skills/yss-audit-log/SKILL.md +2 -2
  99. package/template/.claude/skills/yss-audit-log/references/source-index.md +1 -1
  100. package/template/.claude/skills/yss-cache/SKILL.md +2 -2
  101. package/template/.claude/skills/yss-cache/references/source-index.md +1 -1
  102. package/template/.claude/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  103. package/template/.claude/skills/yss-ddd-scaffold-generator/SKILL.md +6 -6
  104. package/template/.claude/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  105. package/template/.claude/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
  106. package/template/.claude/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +3 -3
  107. package/template/.claude/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +1 -1
  108. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +3 -3
  109. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +3 -3
  110. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +8 -8
  111. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +1 -1
  112. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  113. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +8 -8
  114. package/template/.claude/skills/yss-design-system/SKILL.md +6 -6
  115. package/template/.claude/skills/yss-distributed-id/SKILL.md +2 -2
  116. package/template/.claude/skills/yss-distributed-id/references/source-index.md +1 -1
  117. package/template/.claude/skills/yss-domain/SKILL.md +1 -1
  118. package/template/.claude/skills/yss-dto/SKILL.md +2 -2
  119. package/template/.claude/skills/yss-dto/references/source-index.md +1 -1
  120. package/template/.claude/skills/yss-excel-mvc/SKILL.md +2 -2
  121. package/template/.claude/skills/yss-excel-mvc/references/source-index.md +1 -1
  122. package/template/.claude/skills/yss-exception/SKILL.md +1 -1
  123. package/template/.claude/skills/yss-exception/references/source-index.md +1 -1
  124. package/template/{.codex/skills/yss-router → .claude/skills/yss-implementation-contract-compiler}/SKILL.md +22 -18
  125. package/template/.claude/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  126. package/template/.claude/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  127. package/template/.claude/skills/{yss-router/references/router-contract.yaml → yss-implementation-contract-compiler/references/compiler-contract.yaml} +59 -94
  128. package/template/{.agents/skills/yss-router → .claude/skills/yss-implementation-contract-compiler}/references/slice-implementation-contract.md +20 -8
  129. package/template/{.codex/skills/yss-router → .claude/skills/yss-implementation-contract-compiler}/references/yss-skill-execution-result.md +5 -3
  130. package/template/.claude/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  131. package/template/.claude/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  132. package/template/.claude/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  133. package/template/.claude/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  134. package/template/.claude/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  135. package/template/.claude/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  136. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  137. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  138. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  139. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  140. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  141. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  142. package/template/.claude/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  143. package/template/.claude/skills/yss-mybatis/SKILL.md +2 -2
  144. package/template/.claude/skills/yss-mybatis/references/source-index.md +1 -1
  145. package/template/.claude/skills/yss-page-module-development/SKILL.md +1 -1
  146. package/template/.claude/skills/yss-product-lifecycle/SKILL.md +6 -6
  147. package/template/.claude/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
  148. package/template/.claude/skills/yss-product-lifecycle/references/orchestration-contract.yaml +25 -17
  149. package/template/.claude/skills/yss-product-lifecycle/references/orchestration.md +5 -5
  150. package/template/.claude/skills/yss-product-lifecycle/references/state-model.md +1 -1
  151. package/template/.claude/skills/yss-prototype-stage/SKILL.md +34 -24
  152. package/template/.claude/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  153. package/template/.claude/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  154. package/template/.claude/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  155. package/template/.claude/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  156. package/template/.claude/skills/yss-repository/SKILL.md +2 -2
  157. package/template/.claude/skills/yss-resilience4j/SKILL.md +1 -1
  158. package/template/.claude/skills/yss-resilience4j/references/source-index.md +1 -1
  159. package/template/.claude/skills/yss-security-algorithm/SKILL.md +1 -1
  160. package/template/.claude/skills/yss-security-algorithm/references/source-index.md +1 -1
  161. package/template/.claude/skills/{yss-source-index → yss-skill-source-index-refresh}/SKILL.md +4 -4
  162. package/template/.claude/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  163. package/template/{.codex/skills/yss-source-index → .claude/skills/yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.mjs +1 -1
  164. package/template/.claude/skills/yss-ui/SKILL.md +2 -1
  165. package/template/.claude/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  166. package/template/.claude/skills/yss-up-springboot3/SKILL.md +1 -1
  167. package/template/.claude/skills/yss-userinfo/SKILL.md +1 -1
  168. package/template/.claude/skills/yss-userinfo/references/source-index.md +1 -1
  169. package/template/.claude/skills/yss-validation/SKILL.md +1 -1
  170. package/template/.claude/skills/yss-validation/references/source-index.md +1 -1
  171. package/template/.claude/skills/yss-web-controller/SKILL.md +2 -2
  172. package/template/.codex/skills/code-review/SKILL.md +1 -1
  173. package/template/.codex/skills/code-review/references/yss-review-standards.md +1 -1
  174. package/template/.codex/skills/lombok/SKILL.md +1 -1
  175. package/template/.codex/skills/mapstruct/SKILL.md +1 -1
  176. package/template/.codex/skills/prototype-review/SKILL.md +7 -6
  177. package/template/.codex/skills/yss-antd-design/SKILL.md +8 -7
  178. package/template/.codex/skills/yss-antd-design/references/evidence.md +11 -6
  179. package/template/.codex/skills/yss-api-integration/SKILL.md +2 -2
  180. package/template/.codex/skills/yss-application/SKILL.md +2 -2
  181. package/template/.codex/skills/yss-audit-log/SKILL.md +2 -2
  182. package/template/.codex/skills/yss-audit-log/references/source-index.md +1 -1
  183. package/template/.codex/skills/yss-cache/SKILL.md +2 -2
  184. package/template/.codex/skills/yss-cache/references/source-index.md +1 -1
  185. package/template/.codex/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  186. package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +6 -6
  187. package/template/.codex/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  188. package/template/.codex/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
  189. package/template/.codex/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +3 -3
  190. package/template/.codex/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +1 -1
  191. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +3 -3
  192. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +3 -3
  193. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +8 -8
  194. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +1 -1
  195. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  196. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +8 -8
  197. package/template/.codex/skills/yss-design-system/SKILL.md +6 -6
  198. package/template/.codex/skills/yss-distributed-id/SKILL.md +2 -2
  199. package/template/.codex/skills/yss-distributed-id/references/source-index.md +1 -1
  200. package/template/.codex/skills/yss-domain/SKILL.md +1 -1
  201. package/template/.codex/skills/yss-dto/SKILL.md +2 -2
  202. package/template/.codex/skills/yss-dto/references/source-index.md +1 -1
  203. package/template/.codex/skills/yss-excel-mvc/SKILL.md +2 -2
  204. package/template/.codex/skills/yss-excel-mvc/references/source-index.md +1 -1
  205. package/template/.codex/skills/yss-exception/SKILL.md +1 -1
  206. package/template/.codex/skills/yss-exception/references/source-index.md +1 -1
  207. package/template/{.cursor/skills/yss-router → .codex/skills/yss-implementation-contract-compiler}/SKILL.md +22 -18
  208. package/template/.codex/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  209. package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  210. package/template/{.cursor/skills/yss-router/references/router-contract.yaml → .codex/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml} +59 -94
  211. package/template/{.cursor/skills/yss-router → .codex/skills/yss-implementation-contract-compiler}/references/slice-implementation-contract.md +20 -8
  212. package/template/{.claude/skills/yss-router → .codex/skills/yss-implementation-contract-compiler}/references/yss-skill-execution-result.md +5 -3
  213. package/template/.codex/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  214. package/template/.codex/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  215. package/template/.codex/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  216. package/template/.codex/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  217. package/template/.codex/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  218. package/template/.codex/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  219. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  220. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  221. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  222. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  223. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  224. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  225. package/template/.codex/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  226. package/template/.codex/skills/yss-mybatis/SKILL.md +2 -2
  227. package/template/.codex/skills/yss-mybatis/references/source-index.md +1 -1
  228. package/template/.codex/skills/yss-page-module-development/SKILL.md +1 -1
  229. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +6 -6
  230. package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
  231. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +25 -17
  232. package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +5 -5
  233. package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +1 -1
  234. package/template/.codex/skills/yss-prototype-stage/SKILL.md +34 -24
  235. package/template/.codex/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  236. package/template/.codex/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  237. package/template/.codex/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  238. package/template/.codex/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  239. package/template/.codex/skills/yss-repository/SKILL.md +2 -2
  240. package/template/.codex/skills/yss-resilience4j/SKILL.md +1 -1
  241. package/template/.codex/skills/yss-resilience4j/references/source-index.md +1 -1
  242. package/template/.codex/skills/yss-security-algorithm/SKILL.md +1 -1
  243. package/template/.codex/skills/yss-security-algorithm/references/source-index.md +1 -1
  244. package/template/.codex/skills/{yss-source-index → yss-skill-source-index-refresh}/SKILL.md +4 -4
  245. package/template/.codex/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  246. package/template/{.agents/skills/yss-source-index → .codex/skills/yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.mjs +1 -1
  247. package/template/.codex/skills/yss-ui/SKILL.md +2 -1
  248. package/template/.codex/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  249. package/template/.codex/skills/yss-up-springboot3/SKILL.md +1 -1
  250. package/template/.codex/skills/yss-userinfo/SKILL.md +1 -1
  251. package/template/.codex/skills/yss-userinfo/references/source-index.md +1 -1
  252. package/template/.codex/skills/yss-validation/SKILL.md +1 -1
  253. package/template/.codex/skills/yss-validation/references/source-index.md +1 -1
  254. package/template/.codex/skills/yss-web-controller/SKILL.md +2 -2
  255. package/template/.cursor/skills/code-review/SKILL.md +1 -1
  256. package/template/.cursor/skills/code-review/references/yss-review-standards.md +1 -1
  257. package/template/.cursor/skills/lombok/SKILL.md +1 -1
  258. package/template/.cursor/skills/mapstruct/SKILL.md +1 -1
  259. package/template/.cursor/skills/prototype-review/SKILL.md +7 -6
  260. package/template/.cursor/skills/yss-antd-design/SKILL.md +8 -7
  261. package/template/.cursor/skills/yss-antd-design/references/evidence.md +11 -6
  262. package/template/.cursor/skills/yss-api-integration/SKILL.md +2 -2
  263. package/template/.cursor/skills/yss-application/SKILL.md +2 -2
  264. package/template/.cursor/skills/yss-audit-log/SKILL.md +2 -2
  265. package/template/.cursor/skills/yss-audit-log/references/source-index.md +1 -1
  266. package/template/.cursor/skills/yss-cache/SKILL.md +2 -2
  267. package/template/.cursor/skills/yss-cache/references/source-index.md +1 -1
  268. package/template/.cursor/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  269. package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +6 -6
  270. package/template/.cursor/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  271. package/template/.cursor/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
  272. package/template/.cursor/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +3 -3
  273. package/template/.cursor/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +1 -1
  274. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +3 -3
  275. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +3 -3
  276. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +8 -8
  277. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +1 -1
  278. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  279. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +8 -8
  280. package/template/.cursor/skills/yss-design-system/SKILL.md +6 -6
  281. package/template/.cursor/skills/yss-distributed-id/SKILL.md +2 -2
  282. package/template/.cursor/skills/yss-distributed-id/references/source-index.md +1 -1
  283. package/template/.cursor/skills/yss-domain/SKILL.md +1 -1
  284. package/template/.cursor/skills/yss-dto/SKILL.md +2 -2
  285. package/template/.cursor/skills/yss-dto/references/source-index.md +1 -1
  286. package/template/.cursor/skills/yss-excel-mvc/SKILL.md +2 -2
  287. package/template/.cursor/skills/yss-excel-mvc/references/source-index.md +1 -1
  288. package/template/.cursor/skills/yss-exception/SKILL.md +1 -1
  289. package/template/.cursor/skills/yss-exception/references/source-index.md +1 -1
  290. package/template/{.claude/skills/yss-router → .cursor/skills/yss-implementation-contract-compiler}/SKILL.md +22 -18
  291. package/template/.cursor/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  292. package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  293. package/template/{.agents/skills/yss-router/references/router-contract.yaml → .cursor/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml} +59 -94
  294. package/template/{.claude/skills/yss-router → .cursor/skills/yss-implementation-contract-compiler}/references/slice-implementation-contract.md +20 -8
  295. package/template/.cursor/skills/{yss-router → yss-implementation-contract-compiler}/references/yss-skill-execution-result.md +5 -3
  296. package/template/.cursor/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  297. package/template/.cursor/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  298. package/template/.cursor/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  299. package/template/.cursor/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  300. package/template/.cursor/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  301. package/template/.cursor/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  302. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  303. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  304. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  305. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  306. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  307. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  308. package/template/.cursor/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  309. package/template/.cursor/skills/yss-mybatis/SKILL.md +2 -2
  310. package/template/.cursor/skills/yss-mybatis/references/source-index.md +1 -1
  311. package/template/.cursor/skills/yss-page-module-development/SKILL.md +1 -1
  312. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +6 -6
  313. package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
  314. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +25 -17
  315. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +5 -5
  316. package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +1 -1
  317. package/template/.cursor/skills/yss-prototype-stage/SKILL.md +34 -24
  318. package/template/.cursor/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  319. package/template/.cursor/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  320. package/template/.cursor/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  321. package/template/.cursor/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  322. package/template/.cursor/skills/yss-repository/SKILL.md +2 -2
  323. package/template/.cursor/skills/yss-resilience4j/SKILL.md +1 -1
  324. package/template/.cursor/skills/yss-resilience4j/references/source-index.md +1 -1
  325. package/template/.cursor/skills/yss-security-algorithm/SKILL.md +1 -1
  326. package/template/.cursor/skills/yss-security-algorithm/references/source-index.md +1 -1
  327. package/template/.cursor/skills/{yss-source-index → yss-skill-source-index-refresh}/SKILL.md +4 -4
  328. package/template/.cursor/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  329. package/template/.cursor/skills/{yss-source-index → yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.mjs +1 -1
  330. package/template/.cursor/skills/yss-ui/SKILL.md +2 -1
  331. package/template/.cursor/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  332. package/template/.cursor/skills/yss-up-springboot3/SKILL.md +1 -1
  333. package/template/.cursor/skills/yss-userinfo/SKILL.md +1 -1
  334. package/template/.cursor/skills/yss-userinfo/references/source-index.md +1 -1
  335. package/template/.cursor/skills/yss-validation/SKILL.md +1 -1
  336. package/template/.cursor/skills/yss-validation/references/source-index.md +1 -1
  337. package/template/.cursor/skills/yss-web-controller/SKILL.md +2 -2
  338. package/template/.pi/skills/code-review/SKILL.md +1 -1
  339. package/template/.pi/skills/code-review/references/yss-review-standards.md +1 -1
  340. package/template/.pi/skills/lombok/SKILL.md +1 -1
  341. package/template/.pi/skills/mapstruct/SKILL.md +1 -1
  342. package/template/.pi/skills/prototype-review/SKILL.md +7 -6
  343. package/template/.pi/skills/yss-antd-design/SKILL.md +8 -7
  344. package/template/.pi/skills/yss-antd-design/references/evidence.md +11 -6
  345. package/template/.pi/skills/yss-api-integration/SKILL.md +2 -2
  346. package/template/.pi/skills/yss-application/SKILL.md +2 -2
  347. package/template/.pi/skills/yss-audit-log/SKILL.md +2 -2
  348. package/template/.pi/skills/yss-audit-log/references/source-index.md +1 -1
  349. package/template/.pi/skills/yss-cache/SKILL.md +2 -2
  350. package/template/.pi/skills/yss-cache/references/source-index.md +1 -1
  351. package/template/.pi/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  352. package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +6 -6
  353. package/template/.pi/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  354. package/template/.pi/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
  355. package/template/.pi/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +3 -3
  356. package/template/.pi/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +1 -1
  357. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +3 -3
  358. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +3 -3
  359. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +8 -8
  360. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +1 -1
  361. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  362. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +8 -8
  363. package/template/.pi/skills/yss-design-system/SKILL.md +6 -6
  364. package/template/.pi/skills/yss-distributed-id/SKILL.md +2 -2
  365. package/template/.pi/skills/yss-distributed-id/references/source-index.md +1 -1
  366. package/template/.pi/skills/yss-domain/SKILL.md +1 -1
  367. package/template/.pi/skills/yss-dto/SKILL.md +2 -2
  368. package/template/.pi/skills/yss-dto/references/source-index.md +1 -1
  369. package/template/.pi/skills/yss-excel-mvc/SKILL.md +2 -2
  370. package/template/.pi/skills/yss-excel-mvc/references/source-index.md +1 -1
  371. package/template/.pi/skills/yss-exception/SKILL.md +1 -1
  372. package/template/.pi/skills/yss-exception/references/source-index.md +1 -1
  373. package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  374. package/template/.pi/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  375. package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  376. package/template/.pi/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  377. package/template/.pi/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  378. package/template/.pi/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  379. package/template/.pi/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  380. package/template/.pi/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  381. package/template/.pi/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  382. package/template/.pi/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  383. package/template/.pi/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  384. package/template/.pi/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  385. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  386. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  387. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  388. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  389. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  390. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  391. package/template/.pi/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  392. package/template/.pi/skills/yss-mybatis/SKILL.md +2 -2
  393. package/template/.pi/skills/yss-mybatis/references/source-index.md +1 -1
  394. package/template/.pi/skills/yss-page-module-development/SKILL.md +1 -1
  395. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +6 -6
  396. package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
  397. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +25 -17
  398. package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +5 -5
  399. package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +1 -1
  400. package/template/.pi/skills/yss-prototype-stage/SKILL.md +34 -24
  401. package/template/.pi/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  402. package/template/.pi/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  403. package/template/.pi/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  404. package/template/.pi/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  405. package/template/.pi/skills/yss-repository/SKILL.md +2 -2
  406. package/template/.pi/skills/yss-resilience4j/SKILL.md +1 -1
  407. package/template/.pi/skills/yss-resilience4j/references/source-index.md +1 -1
  408. package/template/.pi/skills/yss-security-algorithm/SKILL.md +1 -1
  409. package/template/.pi/skills/yss-security-algorithm/references/source-index.md +1 -1
  410. package/template/.pi/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  411. package/template/.pi/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  412. package/template/.pi/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  413. package/template/.pi/skills/yss-ui/SKILL.md +2 -1
  414. package/template/.pi/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  415. package/template/.pi/skills/yss-up-springboot3/SKILL.md +1 -1
  416. package/template/.pi/skills/yss-userinfo/SKILL.md +1 -1
  417. package/template/.pi/skills/yss-userinfo/references/source-index.md +1 -1
  418. package/template/.pi/skills/yss-validation/SKILL.md +1 -1
  419. package/template/.pi/skills/yss-validation/references/source-index.md +1 -1
  420. package/template/.pi/skills/yss-web-controller/SKILL.md +2 -2
  421. package/template/.qoder/skills/code-review/SKILL.md +1 -1
  422. package/template/.qoder/skills/code-review/references/yss-review-standards.md +1 -1
  423. package/template/.qoder/skills/lombok/SKILL.md +1 -1
  424. package/template/.qoder/skills/mapstruct/SKILL.md +1 -1
  425. package/template/.qoder/skills/prototype-review/SKILL.md +7 -6
  426. package/template/.qoder/skills/yss-antd-design/SKILL.md +8 -7
  427. package/template/.qoder/skills/yss-antd-design/references/evidence.md +11 -6
  428. package/template/.qoder/skills/yss-api-integration/SKILL.md +2 -2
  429. package/template/.qoder/skills/yss-application/SKILL.md +2 -2
  430. package/template/.qoder/skills/yss-audit-log/SKILL.md +2 -2
  431. package/template/.qoder/skills/yss-audit-log/references/source-index.md +1 -1
  432. package/template/.qoder/skills/yss-cache/SKILL.md +2 -2
  433. package/template/.qoder/skills/yss-cache/references/source-index.md +1 -1
  434. package/template/.qoder/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  435. package/template/.qoder/skills/yss-ddd-scaffold-generator/SKILL.md +6 -6
  436. package/template/.qoder/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  437. package/template/.qoder/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
  438. package/template/.qoder/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +3 -3
  439. package/template/.qoder/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +1 -1
  440. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +3 -3
  441. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +3 -3
  442. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +8 -8
  443. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +1 -1
  444. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  445. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +8 -8
  446. package/template/.qoder/skills/yss-design-system/SKILL.md +6 -6
  447. package/template/.qoder/skills/yss-distributed-id/SKILL.md +2 -2
  448. package/template/.qoder/skills/yss-distributed-id/references/source-index.md +1 -1
  449. package/template/.qoder/skills/yss-domain/SKILL.md +1 -1
  450. package/template/.qoder/skills/yss-dto/SKILL.md +2 -2
  451. package/template/.qoder/skills/yss-dto/references/source-index.md +1 -1
  452. package/template/.qoder/skills/yss-excel-mvc/SKILL.md +2 -2
  453. package/template/.qoder/skills/yss-excel-mvc/references/source-index.md +1 -1
  454. package/template/.qoder/skills/yss-exception/SKILL.md +1 -1
  455. package/template/.qoder/skills/yss-exception/references/source-index.md +1 -1
  456. package/template/.qoder/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  457. package/template/.qoder/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  458. package/template/.qoder/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  459. package/template/.qoder/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  460. package/template/.qoder/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  461. package/template/.qoder/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  462. package/template/.qoder/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  463. package/template/.qoder/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  464. package/template/.qoder/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  465. package/template/.qoder/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  466. package/template/.qoder/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  467. package/template/.qoder/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  468. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  469. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  470. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  471. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  472. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  473. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  474. package/template/.qoder/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  475. package/template/.qoder/skills/yss-mybatis/SKILL.md +2 -2
  476. package/template/.qoder/skills/yss-mybatis/references/source-index.md +1 -1
  477. package/template/.qoder/skills/yss-page-module-development/SKILL.md +1 -1
  478. package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +6 -6
  479. package/template/.qoder/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
  480. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration-contract.yaml +25 -17
  481. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration.md +5 -5
  482. package/template/.qoder/skills/yss-product-lifecycle/references/state-model.md +1 -1
  483. package/template/.qoder/skills/yss-prototype-stage/SKILL.md +34 -24
  484. package/template/.qoder/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  485. package/template/.qoder/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  486. package/template/.qoder/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  487. package/template/.qoder/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  488. package/template/.qoder/skills/yss-repository/SKILL.md +2 -2
  489. package/template/.qoder/skills/yss-resilience4j/SKILL.md +1 -1
  490. package/template/.qoder/skills/yss-resilience4j/references/source-index.md +1 -1
  491. package/template/.qoder/skills/yss-security-algorithm/SKILL.md +1 -1
  492. package/template/.qoder/skills/yss-security-algorithm/references/source-index.md +1 -1
  493. package/template/.qoder/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  494. package/template/.qoder/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  495. package/template/.qoder/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  496. package/template/.qoder/skills/yss-ui/SKILL.md +2 -1
  497. package/template/.qoder/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  498. package/template/.qoder/skills/yss-up-springboot3/SKILL.md +1 -1
  499. package/template/.qoder/skills/yss-userinfo/SKILL.md +1 -1
  500. package/template/.qoder/skills/yss-userinfo/references/source-index.md +1 -1
  501. package/template/.qoder/skills/yss-validation/SKILL.md +1 -1
  502. package/template/.qoder/skills/yss-validation/references/source-index.md +1 -1
  503. package/template/.qoder/skills/yss-web-controller/SKILL.md +2 -2
  504. package/template/.trae/skills/code-review/SKILL.md +1 -1
  505. package/template/.trae/skills/code-review/references/yss-review-standards.md +1 -1
  506. package/template/.trae/skills/lombok/SKILL.md +1 -1
  507. package/template/.trae/skills/mapstruct/SKILL.md +1 -1
  508. package/template/.trae/skills/prototype-review/SKILL.md +7 -6
  509. package/template/.trae/skills/yss-antd-design/SKILL.md +8 -7
  510. package/template/.trae/skills/yss-antd-design/references/evidence.md +11 -6
  511. package/template/.trae/skills/yss-api-integration/SKILL.md +2 -2
  512. package/template/.trae/skills/yss-application/SKILL.md +2 -2
  513. package/template/.trae/skills/yss-audit-log/SKILL.md +2 -2
  514. package/template/.trae/skills/yss-audit-log/references/source-index.md +1 -1
  515. package/template/.trae/skills/yss-cache/SKILL.md +2 -2
  516. package/template/.trae/skills/yss-cache/references/source-index.md +1 -1
  517. package/template/.trae/skills/yss-cache/scripts/check-skill-freshness.sh +1 -1
  518. package/template/.trae/skills/yss-ddd-scaffold-generator/SKILL.md +6 -6
  519. package/template/.trae/skills/yss-ddd-scaffold-generator/agents/openai.yaml +1 -1
  520. package/template/.trae/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
  521. package/template/.trae/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +3 -3
  522. package/template/.trae/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +1 -1
  523. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +3 -3
  524. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +3 -3
  525. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +8 -8
  526. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +1 -1
  527. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +1 -1
  528. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +8 -8
  529. package/template/.trae/skills/yss-design-system/SKILL.md +6 -6
  530. package/template/.trae/skills/yss-distributed-id/SKILL.md +2 -2
  531. package/template/.trae/skills/yss-distributed-id/references/source-index.md +1 -1
  532. package/template/.trae/skills/yss-domain/SKILL.md +1 -1
  533. package/template/.trae/skills/yss-dto/SKILL.md +2 -2
  534. package/template/.trae/skills/yss-dto/references/source-index.md +1 -1
  535. package/template/.trae/skills/yss-excel-mvc/SKILL.md +2 -2
  536. package/template/.trae/skills/yss-excel-mvc/references/source-index.md +1 -1
  537. package/template/.trae/skills/yss-exception/SKILL.md +1 -1
  538. package/template/.trae/skills/yss-exception/references/source-index.md +1 -1
  539. package/template/.trae/skills/yss-implementation-contract-compiler/SKILL.md +55 -0
  540. package/template/.trae/skills/yss-implementation-contract-compiler/agents/openai.yaml +4 -0
  541. package/template/.trae/skills/yss-implementation-contract-compiler/references/boundaries.md +134 -0
  542. package/template/.trae/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +314 -0
  543. package/template/.trae/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +197 -0
  544. package/template/.trae/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +69 -0
  545. package/template/.trae/skills/yss-implementation-contract-compiler/tests/compiler-scenarios.test.mjs +89 -0
  546. package/template/.trae/skills/yss-mvc-scaffold-generator/SKILL.md +87 -0
  547. package/template/.trae/skills/yss-mvc-scaffold-generator/agents/openai.yaml +4 -0
  548. package/template/.trae/skills/yss-mvc-scaffold-generator/assets/logback-spring.xml.template +50 -0
  549. package/template/.trae/skills/yss-mvc-scaffold-generator/assets/smart-doc.json.template +42 -0
  550. package/template/.trae/skills/yss-mvc-scaffold-generator/references/architecture.md +28 -0
  551. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/generate_project.mjs +136 -0
  552. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/generate_project.test.mjs +337 -0
  553. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/lib/envelope.mjs +66 -0
  554. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/lib/runtime.mjs +115 -0
  555. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/lib/storage.mjs +90 -0
  556. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/lib/templates.mjs +54 -0
  557. package/template/.trae/skills/yss-mvc-scaffold-generator/scripts/verify_project.mjs +103 -0
  558. package/template/.trae/skills/yss-mybatis/SKILL.md +2 -2
  559. package/template/.trae/skills/yss-mybatis/references/source-index.md +1 -1
  560. package/template/.trae/skills/yss-page-module-development/SKILL.md +1 -1
  561. package/template/.trae/skills/yss-product-lifecycle/SKILL.md +6 -6
  562. package/template/.trae/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
  563. package/template/.trae/skills/yss-product-lifecycle/references/orchestration-contract.yaml +25 -17
  564. package/template/.trae/skills/yss-product-lifecycle/references/orchestration.md +5 -5
  565. package/template/.trae/skills/yss-product-lifecycle/references/state-model.md +1 -1
  566. package/template/.trae/skills/yss-prototype-stage/SKILL.md +34 -24
  567. package/template/.trae/skills/yss-prototype-stage/references/product-design-adapter.md +43 -38
  568. package/template/.trae/skills/yss-prototype-stage/references/prototype-profile-routing.md +67 -0
  569. package/template/.trae/skills/yss-prototype-stage/scripts/prototype-contract.mjs +192 -103
  570. package/template/.trae/skills/yss-prototype-stage/tests/run-scenarios.mjs +87 -134
  571. package/template/.trae/skills/yss-repository/SKILL.md +2 -2
  572. package/template/.trae/skills/yss-resilience4j/SKILL.md +1 -1
  573. package/template/.trae/skills/yss-resilience4j/references/source-index.md +1 -1
  574. package/template/.trae/skills/yss-security-algorithm/SKILL.md +1 -1
  575. package/template/.trae/skills/yss-security-algorithm/references/source-index.md +1 -1
  576. package/template/.trae/skills/yss-skill-source-index-refresh/SKILL.md +63 -0
  577. package/template/.trae/skills/yss-skill-source-index-refresh/agents/openai.yaml +4 -0
  578. package/template/.trae/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +24 -0
  579. package/template/.trae/skills/yss-ui/SKILL.md +2 -1
  580. package/template/.trae/skills/yss-ui/assets/docs/guide/llms.md +0 -61
  581. package/template/.trae/skills/yss-up-springboot3/SKILL.md +1 -1
  582. package/template/.trae/skills/yss-userinfo/SKILL.md +1 -1
  583. package/template/.trae/skills/yss-userinfo/references/source-index.md +1 -1
  584. package/template/.trae/skills/yss-validation/SKILL.md +1 -1
  585. package/template/.trae/skills/yss-validation/references/source-index.md +1 -1
  586. package/template/.trae/skills/yss-web-controller/SKILL.md +2 -2
  587. package/template/AGENTS.md +11 -11
  588. package/template/CONTEXT.md +8 -7
  589. package/template/README.md +0 -1
  590. package/template/docs/agents/digital-human-roles.md +3 -3
  591. package/template/docs/agents/digital-human-roles.yaml +3 -3
  592. package/template/docs/agents/skill-migrations.md +18 -5
  593. package/template/docs/agents/skills-maintenance.md +3 -3
  594. package/template/docs/agents/strategic-design-skills-integration.md +41 -0
  595. package/template/docs/agents/yss-skill-registry.yaml +134 -28
  596. package/template/docs/api/templates/openapi-draft-review-checklist.md +3 -3
  597. package/template/docs/architecture/templates/engineering-baseline-review-template.md +3 -3
  598. package/template/docs/design/README.md +7 -7
  599. package/template/docs/design/design.md +25 -17
  600. package/template/docs/design/templates/interaction-spec-template.md +4 -4
  601. package/template/docs/design/templates/prototype-confirmation-template.md +13 -15
  602. package/template/docs/design/templates/prototype-evidence-template.yaml +99 -75
  603. package/template/docs/design/templates/prototype-review-checklist.md +5 -4
  604. package/template/docs/design/templates/state-matrix-template.md +11 -0
  605. package/template/docs/discovery/reports/agent-governance-options.md +8 -8
  606. package/template/docs/process/MATT-POCOCK-ENGINEERING-SKILLS.md +1 -1
  607. package/template/docs/process/implementation-repo-integration.md +1 -1
  608. package/template/docs/process/lifecycle-artifact-map.md +4 -2
  609. package/template/docs/process/lifecycle-registry-baseline.json +4 -1
  610. package/template/docs/process/lifecycle-registry.yaml +15 -9
  611. package/template/docs/process/template-engineering-overview.md +2 -2
  612. package/template/docs/process/template-verification-profiles.yaml +27 -6
  613. package/template/docs/templates/build-architecture-checklist-template.md +6 -6
  614. package/template/docs/templates/implementation-routing-template.md +8 -8
  615. package/template/docs/templates/requirement-freeze-template.md +2 -2
  616. package/template/docs/templates/review-report-template.md +4 -4
  617. package/template/docs/templates/spec-template.md +1 -1
  618. package/template/docs/templates/vertical-slice-ticket-template.md +4 -4
  619. package/template/docs/user-guide/yss-ui-mcp.md +0 -2
  620. 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
  621. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +4 -4
  622. 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
  623. package/template/scripts/audit-yss-backend-skills +56 -0
  624. package/template/scripts/generate-yss-skill-boundaries +67 -0
  625. package/template/scripts/lib/export-skills.mjs +4 -4
  626. package/template/scripts/lib/implementation-contract-compiler.mjs +194 -0
  627. package/template/scripts/lib/scenario-checks.mjs +9 -6
  628. package/template/scripts/lib/skill-governance.mjs +27 -5
  629. package/template/scripts/lib/skill-registry.mjs +107 -63
  630. package/template/scripts/lib/skill-supply-chain.mjs +45 -5
  631. package/template/scripts/verify-repository-scope-scenarios +2 -2
  632. package/template/scripts/verify-retired-skill-ids +37 -0
  633. package/template/scripts/verify-scaffold-generator-scenarios +18 -18
  634. package/template/scripts/verify-template-verification-scenarios +4 -1
  635. package/template/scripts/verify-yss-implementation-contract-compiler-scenarios +14 -0
  636. package/template/skills-lock.json +87 -157
  637. package/template/yss-public-skills.json +4 -4
  638. package/template.manifest.json +0 -3
  639. package/template.snapshot.json +5 -5
  640. package/template/.agents/skills/yss-router/agents/openai.yaml +0 -4
  641. package/template/.agents/skills/yss-router/references/boundaries.md +0 -50
  642. package/template/.agents/skills/yss-source-index/agents/openai.yaml +0 -4
  643. package/template/.claude/skills/yss-router/agents/openai.yaml +0 -4
  644. package/template/.claude/skills/yss-router/references/boundaries.md +0 -50
  645. package/template/.claude/skills/yss-source-index/agents/openai.yaml +0 -4
  646. package/template/.codex/skills/yss-router/agents/openai.yaml +0 -4
  647. package/template/.codex/skills/yss-router/references/boundaries.md +0 -50
  648. package/template/.codex/skills/yss-source-index/agents/openai.yaml +0 -4
  649. package/template/.cursor/skills/yss-router/agents/openai.yaml +0 -4
  650. package/template/.cursor/skills/yss-router/references/boundaries.md +0 -50
  651. package/template/.cursor/skills/yss-source-index/agents/openai.yaml +0 -4
  652. package/template/.gemini/settings.json +0 -8
  653. package/template/.hermes/skills/alibaba-java-code-style/SKILL.md +0 -51
  654. package/template/.hermes/skills/alibaba-java-code-style/agents/openai.yaml +0 -4
  655. package/template/.hermes/skills/alibaba-java-code-style/references/database-engineering-design.md +0 -147
  656. package/template/.hermes/skills/alibaba-java-code-style/references/java-programming.md +0 -178
  657. package/template/.hermes/skills/alibaba-java-code-style/references/reliability-test-security.md +0 -89
  658. package/template/.hermes/skills/archify/LICENSE +0 -22
  659. package/template/.hermes/skills/archify/SKILL.md +0 -156
  660. package/template/.hermes/skills/archify/assets/template.html +0 -14787
  661. package/template/.hermes/skills/archify/bin/archify.mjs +0 -1990
  662. package/template/.hermes/skills/archify/bin/open-artifact.mjs +0 -86
  663. package/template/.hermes/skills/archify/bin/preview.mjs +0 -648
  664. package/template/.hermes/skills/archify/bin/visual-check.mjs +0 -829
  665. package/template/.hermes/skills/archify/brand-marks/README.md +0 -31
  666. package/template/.hermes/skills/archify/brand-marks/catalog.json +0 -131
  667. package/template/.hermes/skills/archify/delta/architecture-delta.mjs +0 -1214
  668. package/template/.hermes/skills/archify/examples/agent-run.lifecycle.json +0 -71
  669. package/template/.hermes/skills/archify/examples/agent-tool-call.workflow.json +0 -94
  670. package/template/.hermes/skills/archify/examples/async-job-roundtrip.sequence.json +0 -61
  671. package/template/.hermes/skills/archify/examples/brand-aware-delivery.architecture.json +0 -47
  672. package/template/.hermes/skills/archify/examples/cache-miss-request.sequence.json +0 -81
  673. package/template/.hermes/skills/archify/examples/checkout-platform.base.architecture.json +0 -31
  674. package/template/.hermes/skills/archify/examples/checkout-platform.head.architecture.json +0 -31
  675. package/template/.hermes/skills/archify/examples/dataflow-product-analytics.html +0 -14898
  676. package/template/.hermes/skills/archify/examples/deployment-release.lifecycle.json +0 -49
  677. package/template/.hermes/skills/archify/examples/event-stream.dataflow.json +0 -57
  678. package/template/.hermes/skills/archify/examples/incident-response.workflow.json +0 -64
  679. package/template/.hermes/skills/archify/examples/lifecycle-agent-run.html +0 -14847
  680. package/template/.hermes/skills/archify/examples/product-analytics.dataflow.json +0 -76
  681. package/template/.hermes/skills/archify/examples/production-deployment.architecture.json +0 -71
  682. package/template/.hermes/skills/archify/examples/release-delivery.workflow.json +0 -62
  683. package/template/.hermes/skills/archify/examples/sequence-cache-miss-request.html +0 -14913
  684. package/template/.hermes/skills/archify/examples/web-app-rendered.html +0 -14862
  685. package/template/.hermes/skills/archify/examples/web-app.architecture.json +0 -46
  686. package/template/.hermes/skills/archify/examples/workflow-agent-tool-call-rendered.html +0 -14904
  687. package/template/.hermes/skills/archify/migrations/workflow-v2.mjs +0 -279
  688. package/template/.hermes/skills/archify/package-lock.json +0 -149
  689. package/template/.hermes/skills/archify/package.json +0 -37
  690. package/template/.hermes/skills/archify/recipes/scenarios.mjs +0 -391
  691. package/template/.hermes/skills/archify/references/authoring-contract.md +0 -191
  692. package/template/.hermes/skills/archify/references/brand-marks.md +0 -65
  693. package/template/.hermes/skills/archify/references/delivery-contract.md +0 -112
  694. package/template/.hermes/skills/archify/references/viewer-runtime.md +0 -45
  695. package/template/.hermes/skills/archify/renderers/architecture/grid.mjs +0 -62
  696. package/template/.hermes/skills/archify/renderers/architecture/render-architecture.mjs +0 -1078
  697. package/template/.hermes/skills/archify/renderers/dataflow/README.md +0 -104
  698. package/template/.hermes/skills/archify/renderers/dataflow/render-dataflow.mjs +0 -483
  699. package/template/.hermes/skills/archify/renderers/lifecycle/README.md +0 -115
  700. package/template/.hermes/skills/archify/renderers/lifecycle/render-lifecycle.mjs +0 -561
  701. package/template/.hermes/skills/archify/renderers/sequence/README.md +0 -114
  702. package/template/.hermes/skills/archify/renderers/sequence/render-sequence.mjs +0 -453
  703. package/template/.hermes/skills/archify/renderers/shared/brand-marks.mjs +0 -563
  704. package/template/.hermes/skills/archify/renderers/shared/cli.mjs +0 -218
  705. package/template/.hermes/skills/archify/renderers/shared/desktop-readability.mjs +0 -26
  706. package/template/.hermes/skills/archify/renderers/shared/diagnostics.mjs +0 -127
  707. package/template/.hermes/skills/archify/renderers/shared/engineering-profiles.mjs +0 -157
  708. package/template/.hermes/skills/archify/renderers/shared/generated-brand-marks.mjs +0 -2003
  709. package/template/.hermes/skills/archify/renderers/shared/generated-validators.mjs +0 -13
  710. package/template/.hermes/skills/archify/renderers/shared/geometry.mjs +0 -1423
  711. package/template/.hermes/skills/archify/renderers/shared/i18n.mjs +0 -594
  712. package/template/.hermes/skills/archify/renderers/shared/layout-report.mjs +0 -40
  713. package/template/.hermes/skills/archify/renderers/shared/legend.mjs +0 -217
  714. package/template/.hermes/skills/archify/renderers/shared/output-path.mjs +0 -321
  715. package/template/.hermes/skills/archify/renderers/shared/repository-evidence.mjs +0 -235
  716. package/template/.hermes/skills/archify/renderers/shared/text-fit.mjs +0 -49
  717. package/template/.hermes/skills/archify/renderers/shared/utils.mjs +0 -232
  718. package/template/.hermes/skills/archify/renderers/shared/validator.mjs +0 -86
  719. package/template/.hermes/skills/archify/renderers/workflow/README.md +0 -223
  720. package/template/.hermes/skills/archify/renderers/workflow/render-workflow.mjs +0 -35
  721. package/template/.hermes/skills/archify/renderers/workflow/workflow-compiler.mjs +0 -4400
  722. package/template/.hermes/skills/archify/renderers/workflow/workflow-migration-geometry.mjs +0 -144
  723. package/template/.hermes/skills/archify/schemas/README.md +0 -211
  724. package/template/.hermes/skills/archify/schemas/architecture.schema.json +0 -176
  725. package/template/.hermes/skills/archify/schemas/common.schema.json +0 -115
  726. package/template/.hermes/skills/archify/schemas/dataflow.schema.json +0 -243
  727. package/template/.hermes/skills/archify/schemas/lifecycle.schema.json +0 -266
  728. package/template/.hermes/skills/archify/schemas/sequence.schema.json +0 -223
  729. package/template/.hermes/skills/archify/schemas/workflow.schema.json +0 -428
  730. package/template/.hermes/skills/archify/scripts/check-render-output.mjs +0 -835
  731. package/template/.hermes/skills/archify/scripts/check-update.mjs +0 -1667
  732. package/template/.hermes/skills/archify/scripts/generate-brand-marks.mjs +0 -141
  733. package/template/.hermes/skills/archify/scripts/generate-validators.mjs +0 -66
  734. package/template/.hermes/skills/archify/scripts/render-examples.mjs +0 -26
  735. package/template/.hermes/skills/archify/scripts/update-contract.mjs +0 -182
  736. package/template/.hermes/skills/archify/scripts/yss-safe-deliver.mjs +0 -214
  737. package/template/.hermes/skills/archify/skill-release.json +0 -10
  738. package/template/.hermes/skills/archify/test/adaptive-reader-layout.test.mjs +0 -86
  739. package/template/.hermes/skills/archify/test/animation.test.mjs +0 -179
  740. package/template/.hermes/skills/archify/test/architecture-delta.test.mjs +0 -518
  741. package/template/.hermes/skills/archify/test/authored-reachability.test.mjs +0 -95
  742. package/template/.hermes/skills/archify/test/authoring-safety-contract.test.mjs +0 -54
  743. package/template/.hermes/skills/archify/test/automatic-port-spread.test.mjs +0 -376
  744. package/template/.hermes/skills/archify/test/base-input-compatibility.test.mjs +0 -42
  745. package/template/.hermes/skills/archify/test/brand-marks.test.mjs +0 -654
  746. package/template/.hermes/skills/archify/test/chapter-delta-preview.test.mjs +0 -157
  747. package/template/.hermes/skills/archify/test/chapter-handoff.test.mjs +0 -97
  748. package/template/.hermes/skills/archify/test/chapter-rail.test.mjs +0 -86
  749. package/template/.hermes/skills/archify/test/checkout-line-endings.test.mjs +0 -72
  750. package/template/.hermes/skills/archify/test/clean-skill-staging.test.mjs +0 -244
  751. package/template/.hermes/skills/archify/test/cli.test.mjs +0 -809
  752. package/template/.hermes/skills/archify/test/community-proof-intake.test.mjs +0 -104
  753. package/template/.hermes/skills/archify/test/cursor-onboarding.test.mjs +0 -74
  754. package/template/.hermes/skills/archify/test/degraded.test.mjs +0 -161
  755. package/template/.hermes/skills/archify/test/delivery-contract.test.mjs +0 -52
  756. package/template/.hermes/skills/archify/test/desktop-reader-browser.test.mjs +0 -58
  757. package/template/.hermes/skills/archify/test/diagram-guide.test.mjs +0 -92
  758. package/template/.hermes/skills/archify/test/engineering-profile.test.mjs +0 -209
  759. package/template/.hermes/skills/archify/test/finder.test.mjs +0 -104
  760. package/template/.hermes/skills/archify/test/fixtures/automatic-routing-node-border-clearance.workflow.json +0 -40
  761. package/template/.hermes/skills/archify/test/fixtures/fail-migration-cleanup.mjs +0 -17
  762. package/template/.hermes/skills/archify/test/fixtures/issue-126/custom-widths.workflow.json +0 -18
  763. package/template/.hermes/skills/archify/test/fixtures/issue-126/explicit-route.workflow.json +0 -28
  764. package/template/.hermes/skills/archify/test/fixtures/issue-126/explicit-viewbox.workflow.json +0 -19
  765. package/template/.hermes/skills/archify/test/fixtures/issue-126/labels.workflow.json +0 -18
  766. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/agent-run.lifecycle.json +0 -64
  767. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/agent-tool-call.workflow.json +0 -88
  768. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/cache-miss-request.sequence.json +0 -75
  769. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/event-stream.dataflow.json +0 -57
  770. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/product-analytics.dataflow.json +0 -70
  771. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/production-deployment.architecture.json +0 -71
  772. package/template/.hermes/skills/archify/test/fixtures/v1-baseline/web-app.architecture.json +0 -41
  773. package/template/.hermes/skills/archify/test/fixtures/v1-workflow-700x400.workflow.json +0 -22
  774. package/template/.hermes/skills/archify/test/fixtures/v1-workflow-explicit-coordinates.workflow.json +0 -47
  775. package/template/.hermes/skills/archify/test/gallery.test.mjs +0 -130
  776. package/template/.hermes/skills/archify/test/generate-validators.test.mjs +0 -59
  777. package/template/.hermes/skills/archify/test/generated-artifact-xml.test.mjs +0 -88
  778. package/template/.hermes/skills/archify/test/geometry.test.mjs +0 -739
  779. package/template/.hermes/skills/archify/test/golden.mjs +0 -207
  780. package/template/.hermes/skills/archify/test/grid.test.mjs +0 -47
  781. package/template/.hermes/skills/archify/test/guide-page.test.mjs +0 -59
  782. package/template/.hermes/skills/archify/test/guide.test.mjs +0 -81
  783. package/template/.hermes/skills/archify/test/guided-views.test.mjs +0 -144
  784. package/template/.hermes/skills/archify/test/helpers/xml.mjs +0 -37
  785. package/template/.hermes/skills/archify/test/i18n.test.mjs +0 -422
  786. package/template/.hermes/skills/archify/test/intent-trace.test.mjs +0 -111
  787. package/template/.hermes/skills/archify/test/landing.test.mjs +0 -97
  788. package/template/.hermes/skills/archify/test/layout-rules.test.mjs +0 -1449
  789. package/template/.hermes/skills/archify/test/legend-contract.test.mjs +0 -404
  790. package/template/.hermes/skills/archify/test/motion-governor.test.mjs +0 -111
  791. package/template/.hermes/skills/archify/test/open-artifact.test.mjs +0 -118
  792. package/template/.hermes/skills/archify/test/ordinary-model-floor.test.mjs +0 -1024
  793. package/template/.hermes/skills/archify/test/output-path.test.mjs +0 -587
  794. package/template/.hermes/skills/archify/test/presentation.test.mjs +0 -71
  795. package/template/.hermes/skills/archify/test/preset-tryon.test.mjs +0 -113
  796. package/template/.hermes/skills/archify/test/preview-contract.test.mjs +0 -40
  797. package/template/.hermes/skills/archify/test/preview.test.mjs +0 -499
  798. package/template/.hermes/skills/archify/test/proof-aperture.test.mjs +0 -78
  799. package/template/.hermes/skills/archify/test/reach-share-card.test.mjs +0 -149
  800. package/template/.hermes/skills/archify/test/readme-showcase.test.mjs +0 -318
  801. package/template/.hermes/skills/archify/test/real-repository-proof.test.mjs +0 -209
  802. package/template/.hermes/skills/archify/test/relationship-direct-explorer.test.mjs +0 -115
  803. package/template/.hermes/skills/archify/test/relationship-lens.test.mjs +0 -128
  804. package/template/.hermes/skills/archify/test/relationship-permalink.test.mjs +0 -130
  805. package/template/.hermes/skills/archify/test/relationship-pulse.test.mjs +0 -122
  806. package/template/.hermes/skills/archify/test/release-identity.test.mjs +0 -504
  807. package/template/.hermes/skills/archify/test/release-package-gates.test.mjs +0 -508
  808. package/template/.hermes/skills/archify/test/render-output-checks.test.mjs +0 -435
  809. package/template/.hermes/skills/archify/test/repair-receipt.test.mjs +0 -181
  810. package/template/.hermes/skills/archify/test/repository-evidence.test.mjs +0 -217
  811. package/template/.hermes/skills/archify/test/repository-language-metadata.test.mjs +0 -52
  812. package/template/.hermes/skills/archify/test/route-journey.test.mjs +0 -123
  813. package/template/.hermes/skills/archify/test/route-probe.test.mjs +0 -114
  814. package/template/.hermes/skills/archify/test/route-share-card.test.mjs +0 -163
  815. package/template/.hermes/skills/archify/test/semantic-camera.test.mjs +0 -88
  816. package/template/.hermes/skills/archify/test/semantic-flow.test.mjs +0 -90
  817. package/template/.hermes/skills/archify/test/semantic-legend-gateway.test.mjs +0 -156
  818. package/template/.hermes/skills/archify/test/semantic-lens.test.mjs +0 -103
  819. package/template/.hermes/skills/archify/test/semantic-passport.test.mjs +0 -99
  820. package/template/.hermes/skills/archify/test/semantic-radar.test.mjs +0 -575
  821. package/template/.hermes/skills/archify/test/semantic-zoom.test.mjs +0 -86
  822. package/template/.hermes/skills/archify/test/sequence-column-fit.test.mjs +0 -137
  823. package/template/.hermes/skills/archify/test/settled-flow.test.mjs +0 -97
  824. package/template/.hermes/skills/archify/test/share-card-export.test.mjs +0 -132
  825. package/template/.hermes/skills/archify/test/site-language-continuity.test.mjs +0 -562
  826. package/template/.hermes/skills/archify/test/site-language-integration.mjs +0 -9
  827. package/template/.hermes/skills/archify/test/skill-metadata.test.mjs +0 -84
  828. package/template/.hermes/skills/archify/test/stable-update-manifest.test.mjs +0 -167
  829. package/template/.hermes/skills/archify/test/start-page.test.mjs +0 -312
  830. package/template/.hermes/skills/archify/test/story-beat-navigator.test.mjs +0 -103
  831. package/template/.hermes/skills/archify/test/story-carrier.test.mjs +0 -90
  832. package/template/.hermes/skills/archify/test/story-director-strip.test.mjs +0 -91
  833. package/template/.hermes/skills/archify/test/story-follow-camera.test.mjs +0 -103
  834. package/template/.hermes/skills/archify/test/story-horizon.test.mjs +0 -89
  835. package/template/.hermes/skills/archify/test/story-moment-link.test.mjs +0 -93
  836. package/template/.hermes/skills/archify/test/story-shelf.test.mjs +0 -84
  837. package/template/.hermes/skills/archify/test/story-trail.test.mjs +0 -81
  838. package/template/.hermes/skills/archify/test/toolbar-polish.test.mjs +0 -57
  839. package/template/.hermes/skills/archify/test/update-contract.test.mjs +0 -133
  840. package/template/.hermes/skills/archify/test/update-notifier.test.mjs +0 -3317
  841. package/template/.hermes/skills/archify/test/v1-compatibility.test.mjs +0 -329
  842. package/template/.hermes/skills/archify/test/vertical-edge.test.mjs +0 -68
  843. package/template/.hermes/skills/archify/test/viewer-chrome-layout.test.mjs +0 -872
  844. package/template/.hermes/skills/archify/test/visual-check.test.mjs +0 -400
  845. package/template/.hermes/skills/archify/test/webm-artifact.smoke.mjs +0 -1840
  846. package/template/.hermes/skills/archify/test/workflow-compiler-hard-contract.test.mjs +0 -2135
  847. package/template/.hermes/skills/archify/test/workflow-compiler.test.mjs +0 -1163
  848. package/template/.hermes/skills/archify/test/workflow-migration.test.mjs +0 -620
  849. package/template/.hermes/skills/archify/test/workflow-semantic-contract.test.mjs +0 -131
  850. package/template/.hermes/skills/archify/test/yss-safe-deliver.test.mjs +0 -153
  851. package/template/.hermes/skills/ask-matt/PHASE-BOUNDARIES.md +0 -55
  852. package/template/.hermes/skills/ask-matt/SKILL.md +0 -95
  853. package/template/.hermes/skills/ask-matt/agents/openai.yaml +0 -5
  854. package/template/.hermes/skills/code-review/SKILL.md +0 -122
  855. package/template/.hermes/skills/code-review/agents/openai.yaml +0 -3
  856. package/template/.hermes/skills/code-review/references/yss-review-standards.md +0 -54
  857. package/template/.hermes/skills/codebase-design/DEEPENING.md +0 -37
  858. package/template/.hermes/skills/codebase-design/DESIGN-IT-TWICE.md +0 -44
  859. package/template/.hermes/skills/codebase-design/SKILL.md +0 -118
  860. package/template/.hermes/skills/codebase-design/agents/openai.yaml +0 -3
  861. package/template/.hermes/skills/competitive-intelligence/SKILL.md +0 -94
  862. package/template/.hermes/skills/component-selection-imports/SKILL.md +0 -64
  863. package/template/.hermes/skills/cross-repo-implementation-routing/SKILL.md +0 -51
  864. package/template/.hermes/skills/diagnosing-bugs/SKILL.md +0 -138
  865. package/template/.hermes/skills/diagnosing-bugs/agents/openai.yaml +0 -3
  866. package/template/.hermes/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +0 -44
  867. package/template/.hermes/skills/dispatching-parallel-agents/SKILL.md +0 -185
  868. package/template/.hermes/skills/domain-modeling/ADR-FORMAT.md +0 -47
  869. package/template/.hermes/skills/domain-modeling/CONTEXT-FORMAT.md +0 -60
  870. package/template/.hermes/skills/domain-modeling/SKILL.md +0 -74
  871. package/template/.hermes/skills/domain-modeling/agents/openai.yaml +0 -3
  872. package/template/.hermes/skills/file-export-download/SKILL.md +0 -160
  873. package/template/.hermes/skills/file-export-download/agents/openai.yaml +0 -4
  874. package/template/.hermes/skills/formily-foundation/SKILL.md +0 -176
  875. package/template/.hermes/skills/formily-foundation/references/examples.md +0 -486
  876. package/template/.hermes/skills/formily-linkage-effects/SKILL.md +0 -167
  877. package/template/.hermes/skills/formily-mode-slot-detail/SKILL.md +0 -137
  878. package/template/.hermes/skills/formily-step-flow/SKILL.md +0 -194
  879. package/template/.hermes/skills/frontend-commit/SKILL.md +0 -118
  880. package/template/.hermes/skills/frontend-commit/agents/openai.yaml +0 -7
  881. package/template/.hermes/skills/frontend-commit/references/scope-and-examples.md +0 -111
  882. package/template/.hermes/skills/grill-me/SKILL.md +0 -7
  883. package/template/.hermes/skills/grill-me/agents/openai.yaml +0 -5
  884. package/template/.hermes/skills/grill-with-docs/SKILL.md +0 -19
  885. package/template/.hermes/skills/grill-with-docs/agents/openai.yaml +0 -5
  886. package/template/.hermes/skills/grilling/SKILL.md +0 -28
  887. package/template/.hermes/skills/grilling/agents/openai.yaml +0 -3
  888. package/template/.hermes/skills/handoff/SKILL.md +0 -16
  889. package/template/.hermes/skills/handoff/agents/openai.yaml +0 -5
  890. package/template/.hermes/skills/implement/SKILL.md +0 -15
  891. package/template/.hermes/skills/implement/agents/openai.yaml +0 -5
  892. package/template/.hermes/skills/implementation-repo-onboarding/SKILL.md +0 -50
  893. package/template/.hermes/skills/improve-codebase-architecture/HTML-REPORT.md +0 -123
  894. package/template/.hermes/skills/improve-codebase-architecture/SKILL.md +0 -71
  895. package/template/.hermes/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
  896. package/template/.hermes/skills/java-backend-commit/SKILL.md +0 -121
  897. package/template/.hermes/skills/java-backend-commit/agents/openai.yaml +0 -7
  898. package/template/.hermes/skills/java-backend-commit/references/scope-and-examples.md +0 -118
  899. package/template/.hermes/skills/llm-wiki/SKILL.md +0 -60
  900. package/template/.hermes/skills/llm-wiki/assets/CLAUDE.md.template +0 -26
  901. package/template/.hermes/skills/llm-wiki/assets/fixtures/broken-wiki/wiki/CLAUDE.md +0 -3
  902. package/template/.hermes/skills/llm-wiki/assets/fixtures/broken-wiki/wiki/index.md +0 -5
  903. package/template/.hermes/skills/llm-wiki/assets/fixtures/broken-wiki/wiki/log.md +0 -1
  904. 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
  905. package/template/.hermes/skills/llm-wiki/references/compile.md +0 -126
  906. package/template/.hermes/skills/llm-wiki/references/discover.md +0 -42
  907. package/template/.hermes/skills/llm-wiki/references/ingest.md +0 -36
  908. package/template/.hermes/skills/llm-wiki/references/lint.md +0 -54
  909. package/template/.hermes/skills/llm-wiki/references/query.md +0 -21
  910. package/template/.hermes/skills/llm-wiki/references/schema.md +0 -45
  911. package/template/.hermes/skills/llm-wiki/references/writing.md +0 -35
  912. package/template/.hermes/skills/llm-wiki/scripts/advise.mjs +0 -196
  913. package/template/.hermes/skills/llm-wiki/scripts/advise.test.mjs +0 -91
  914. package/template/.hermes/skills/llm-wiki/scripts/extract.mjs +0 -55
  915. package/template/.hermes/skills/llm-wiki/scripts/extract.test.mjs +0 -34
  916. package/template/.hermes/skills/llm-wiki/scripts/inventory.mjs +0 -174
  917. package/template/.hermes/skills/llm-wiki/scripts/inventory.test.mjs +0 -98
  918. package/template/.hermes/skills/llm-wiki/scripts/lint-wikilinks.mjs +0 -177
  919. package/template/.hermes/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +0 -212
  920. package/template/.hermes/skills/lombok/SKILL.md +0 -351
  921. package/template/.hermes/skills/loop-me/SKILL.md +0 -32
  922. package/template/.hermes/skills/loop-me/agents/openai.yaml +0 -5
  923. package/template/.hermes/skills/maintaining-skills/SKILL.md +0 -27
  924. package/template/.hermes/skills/mapstruct/SKILL.md +0 -334
  925. package/template/.hermes/skills/migrate-to-shoehorn/SKILL.md +0 -118
  926. package/template/.hermes/skills/migrate-to-shoehorn/agents/openai.yaml +0 -3
  927. package/template/.hermes/skills/page-form-module/SKILL.md +0 -168
  928. package/template/.hermes/skills/page-list-module/SKILL.md +0 -137
  929. package/template/.hermes/skills/page-skeleton/SKILL.md +0 -76
  930. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/constant.ts +0 -34
  931. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/hooks/useRuleConfig.ts +0 -75
  932. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/index.vue +0 -107
  933. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/schemas/ruleConfigSchema.ts +0 -256
  934. package/template/.hermes/skills/page-skeleton/examples/AdvancedFormPage/style.less +0 -30
  935. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/constant.ts +0 -28
  936. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/hooks/useQualityRuleList.ts +0 -58
  937. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/hooks/useRuleActions.ts +0 -65
  938. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/index.vue +0 -91
  939. package/template/.hermes/skills/page-skeleton/examples/StandardListPage/style.less +0 -49
  940. package/template/.hermes/skills/prototype/LOGIC.md +0 -67
  941. package/template/.hermes/skills/prototype/SKILL.md +0 -26
  942. package/template/.hermes/skills/prototype/UI.md +0 -112
  943. package/template/.hermes/skills/prototype/agents/openai.yaml +0 -3
  944. package/template/.hermes/skills/prototype-page-acceptance/SKILL.md +0 -62
  945. package/template/.hermes/skills/prototype-review/SKILL.md +0 -74
  946. package/template/.hermes/skills/prototype-review/agents/openai.yaml +0 -4
  947. package/template/.hermes/skills/resolving-merge-conflicts/SKILL.md +0 -14
  948. package/template/.hermes/skills/resolving-merge-conflicts/agents/openai.yaml +0 -3
  949. package/template/.hermes/skills/scaffold-exercises/SKILL.md +0 -106
  950. package/template/.hermes/skills/scaffold-exercises/agents/openai.yaml +0 -3
  951. package/template/.hermes/skills/setup-matt-pocock-skills/SKILL.md +0 -125
  952. package/template/.hermes/skills/setup-matt-pocock-skills/agents/openai.yaml +0 -5
  953. package/template/.hermes/skills/setup-matt-pocock-skills/domain.md +0 -51
  954. package/template/.hermes/skills/setup-matt-pocock-skills/issue-tracker-github.md +0 -45
  955. package/template/.hermes/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +0 -46
  956. package/template/.hermes/skills/setup-matt-pocock-skills/issue-tracker-local.md +0 -66
  957. package/template/.hermes/skills/setup-matt-pocock-skills/triage-labels.md +0 -15
  958. package/template/.hermes/skills/setup-pre-commit/SKILL.md +0 -91
  959. package/template/.hermes/skills/setup-pre-commit/agents/openai.yaml +0 -3
  960. package/template/.hermes/skills/setup-ts-deep-modules/SKILL.md +0 -102
  961. package/template/.hermes/skills/setup-ts-deep-modules/agents/openai.yaml +0 -5
  962. package/template/.hermes/skills/setup-ts-deep-modules/dependency-cruiser.config.cjs +0 -95
  963. package/template/.hermes/skills/tdd/SKILL.md +0 -38
  964. package/template/.hermes/skills/tdd/agents/openai.yaml +0 -3
  965. package/template/.hermes/skills/tdd/mocking.md +0 -59
  966. package/template/.hermes/skills/tdd/tests.md +0 -77
  967. package/template/.hermes/skills/teach/GLOSSARY-FORMAT.md +0 -35
  968. package/template/.hermes/skills/teach/LEARNING-RECORD-FORMAT.md +0 -46
  969. package/template/.hermes/skills/teach/MISSION-FORMAT.md +0 -31
  970. package/template/.hermes/skills/teach/RESOURCES-FORMAT.md +0 -32
  971. package/template/.hermes/skills/teach/SKILL.md +0 -140
  972. package/template/.hermes/skills/teach/agents/openai.yaml +0 -5
  973. package/template/.hermes/skills/theme-token-usage/SKILL.md +0 -123
  974. package/template/.hermes/skills/theme-token-usage/agents/openai.yaml +0 -4
  975. package/template/.hermes/skills/to-questionnaire/SKILL.md +0 -53
  976. package/template/.hermes/skills/to-questionnaire/agents/openai.yaml +0 -5
  977. package/template/.hermes/skills/to-spec/SKILL.md +0 -75
  978. package/template/.hermes/skills/to-spec/agents/openai.yaml +0 -5
  979. package/template/.hermes/skills/to-tickets/SKILL.md +0 -105
  980. package/template/.hermes/skills/to-tickets/agents/openai.yaml +0 -5
  981. package/template/.hermes/skills/triage/AGENT-BRIEF.md +0 -207
  982. package/template/.hermes/skills/triage/OUT-OF-SCOPE.md +0 -105
  983. package/template/.hermes/skills/triage/SKILL.md +0 -112
  984. package/template/.hermes/skills/triage/agents/openai.yaml +0 -5
  985. package/template/.hermes/skills/using-git-worktrees/SKILL.md +0 -202
  986. package/template/.hermes/skills/vue3-best-practices/SKILL.md +0 -68
  987. package/template/.hermes/skills/wait-what/SKILL.md +0 -7
  988. package/template/.hermes/skills/wait-what/agents/openai.yaml +0 -5
  989. package/template/.hermes/skills/wayfinder/SKILL.md +0 -128
  990. package/template/.hermes/skills/wayfinder/agents/openai.yaml +0 -5
  991. package/template/.hermes/skills/writing-beats/SKILL.md +0 -67
  992. package/template/.hermes/skills/writing-beats/agents/openai.yaml +0 -5
  993. package/template/.hermes/skills/writing-for-agents/SKILL-MECHANICS.md +0 -22
  994. package/template/.hermes/skills/writing-for-agents/SKILL.md +0 -81
  995. package/template/.hermes/skills/writing-for-agents/agents/openai.yaml +0 -3
  996. package/template/.hermes/skills/writing-fragments/SKILL.md +0 -79
  997. package/template/.hermes/skills/writing-fragments/agents/openai.yaml +0 -5
  998. package/template/.hermes/skills/writing-shape/SKILL.md +0 -79
  999. package/template/.hermes/skills/writing-shape/agents/openai.yaml +0 -5
  1000. package/template/.hermes/skills/yedit-table-usage/SKILL.md +0 -227
  1001. package/template/.hermes/skills/yss-antd-design/SKILL.md +0 -34
  1002. package/template/.hermes/skills/yss-antd-design/references/boundaries.md +0 -30
  1003. package/template/.hermes/skills/yss-antd-design/references/cli-matrix.md +0 -38
  1004. package/template/.hermes/skills/yss-antd-design/references/evidence.md +0 -30
  1005. package/template/.hermes/skills/yss-antd-design/scripts/validate-frontmatter.mjs +0 -46
  1006. package/template/.hermes/skills/yss-antd-design/scripts/validate-frontmatter.test.mjs +0 -27
  1007. package/template/.hermes/skills/yss-api-integration/SKILL.md +0 -185
  1008. package/template/.hermes/skills/yss-api-integration/agents/openai.yaml +0 -5
  1009. package/template/.hermes/skills/yss-application/SKILL.md +0 -54
  1010. package/template/.hermes/skills/yss-application/agents/openai.yaml +0 -5
  1011. package/template/.hermes/skills/yss-application/references/application-layer-guide.md +0 -87
  1012. package/template/.hermes/skills/yss-audit-log/SKILL.md +0 -67
  1013. package/template/.hermes/skills/yss-audit-log/agents/openai.yaml +0 -5
  1014. package/template/.hermes/skills/yss-audit-log/assets/AuditLogAspect.java +0 -262
  1015. package/template/.hermes/skills/yss-audit-log/assets/YssAuditLogPrintSubscriberImpl.java +0 -37
  1016. package/template/.hermes/skills/yss-audit-log/assets/YssAuditLogSysManagerSubscriberImpl.java +0 -18
  1017. package/template/.hermes/skills/yss-audit-log/assets/YssAuditPublishService.java +0 -69
  1018. package/template/.hermes/skills/yss-audit-log/references/README.md +0 -69
  1019. package/template/.hermes/skills/yss-audit-log/references/source-index.md +0 -43
  1020. package/template/.hermes/skills/yss-cache/SKILL.md +0 -44
  1021. package/template/.hermes/skills/yss-cache/agents/openai.yaml +0 -5
  1022. package/template/.hermes/skills/yss-cache/references/annotations.md +0 -39
  1023. package/template/.hermes/skills/yss-cache/references/architecture.md +0 -25
  1024. package/template/.hermes/skills/yss-cache/references/configuration.md +0 -34
  1025. package/template/.hermes/skills/yss-cache/references/redis-fallback.md +0 -23
  1026. package/template/.hermes/skills/yss-cache/references/redis-topology.md +0 -62
  1027. package/template/.hermes/skills/yss-cache/references/serialization.md +0 -17
  1028. package/template/.hermes/skills/yss-cache/references/source-index.md +0 -67
  1029. package/template/.hermes/skills/yss-cache/references/troubleshooting.md +0 -33
  1030. package/template/.hermes/skills/yss-cache/references/verification.md +0 -34
  1031. package/template/.hermes/skills/yss-cache/scripts/check-skill-freshness.sh +0 -84
  1032. package/template/.hermes/skills/yss-cache/scripts/inspect-cache-usage.sh +0 -96
  1033. package/template/.hermes/skills/yss-cache/scripts/verify-cache-component.sh +0 -101
  1034. package/template/.hermes/skills/yss-components/SKILL.md +0 -386
  1035. package/template/.hermes/skills/yss-components/agents/openai.yaml +0 -5
  1036. package/template/.hermes/skills/yss-components/references/frontend-docs.md +0 -9
  1037. package/template/.hermes/skills/yss-ddd-scaffold-generator/SKILL.md +0 -121
  1038. package/template/.hermes/skills/yss-ddd-scaffold-generator/agents/openai.yaml +0 -5
  1039. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/config/application-local.yml.template +0 -8
  1040. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/config/application.yml.template +0 -36
  1041. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/config/logback-spring.xml.template +0 -15
  1042. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/java/architecture-rules-test.java.template +0 -36
  1043. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/java/bootstrap-application.java.template +0 -15
  1044. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/adapter-pom.xml.template +0 -19
  1045. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/application-pom.xml.template +0 -52
  1046. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/bootstrap-pom.xml.template +0 -70
  1047. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/domain-pom.xml.template +0 -22
  1048. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/infrastructure-pom.xml.template +0 -54
  1049. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/parent-pom.xml.template +0 -155
  1050. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/templates/pom/web-pom.xml.template +0 -58
  1051. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/jvm.config +0 -1
  1052. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/maven.config +0 -2
  1053. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/settings.xml +0 -41
  1054. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/wrapper/MavenWrapperDownloader.java +0 -117
  1055. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/wrapper/maven-wrapper.jar +0 -0
  1056. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/.mvn/wrapper/maven-wrapper.properties +0 -20
  1057. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/mvnw +0 -316
  1058. package/template/.hermes/skills/yss-ddd-scaffold-generator/assets/wrapper/mvnw.cmd +0 -188
  1059. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +0 -116
  1060. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +0 -125
  1061. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-adapter/SKILL.md +0 -31
  1062. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-adapter/agents/openai.yaml +0 -5
  1063. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-application/SKILL.md +0 -8
  1064. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-application/agents/openai.yaml +0 -5
  1065. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-domain/SKILL.md +0 -8
  1066. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-domain/agents/openai.yaml +0 -5
  1067. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-infrastructure/SKILL.md +0 -8
  1068. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-infrastructure/agents/openai.yaml +0 -5
  1069. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +0 -58
  1070. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/agents/openai.yaml +0 -5
  1071. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-web/SKILL.md +0 -8
  1072. package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-web/agents/openai.yaml +0 -5
  1073. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +0 -225
  1074. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +0 -164
  1075. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/generate_and_verify_scaffold.mjs +0 -126
  1076. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +0 -363
  1077. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +0 -172
  1078. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +0 -112
  1079. package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +0 -401
  1080. package/template/.hermes/skills/yss-design-system/SKILL.md +0 -105
  1081. package/template/.hermes/skills/yss-design-system/agents/openai.yaml +0 -5
  1082. package/template/.hermes/skills/yss-design-system/references/design-qa-theme.md +0 -64
  1083. package/template/.hermes/skills/yss-design-system/references/design-system.md +0 -245
  1084. package/template/.hermes/skills/yss-design-system/scripts/validate-frontmatter.mjs +0 -36
  1085. package/template/.hermes/skills/yss-design-system/scripts/validate-frontmatter.test.mjs +0 -12
  1086. package/template/.hermes/skills/yss-distributed-id/SKILL.md +0 -69
  1087. package/template/.hermes/skills/yss-distributed-id/agents/openai.yaml +0 -5
  1088. package/template/.hermes/skills/yss-distributed-id/assets/AutoIdInterceptor.java +0 -246
  1089. package/template/.hermes/skills/yss-distributed-id/assets/EnableDistributedId.java +0 -25
  1090. package/template/.hermes/skills/yss-distributed-id/assets/LeafConf.java +0 -36
  1091. package/template/.hermes/skills/yss-distributed-id/references/README.md +0 -51
  1092. package/template/.hermes/skills/yss-distributed-id/references/source-index.md +0 -86
  1093. package/template/.hermes/skills/yss-domain/SKILL.md +0 -70
  1094. package/template/.hermes/skills/yss-domain/agents/openai.yaml +0 -5
  1095. package/template/.hermes/skills/yss-domain/references/domain-layer-guide.md +0 -101
  1096. package/template/.hermes/skills/yss-dto/SKILL.md +0 -92
  1097. package/template/.hermes/skills/yss-dto/agents/openai.yaml +0 -5
  1098. package/template/.hermes/skills/yss-dto/assets/CommandDTO.java +0 -8
  1099. package/template/.hermes/skills/yss-dto/assets/PageQuery.java +0 -133
  1100. package/template/.hermes/skills/yss-dto/assets/QueryDTO.java +0 -6
  1101. package/template/.hermes/skills/yss-dto/assets/Result.java +0 -105
  1102. package/template/.hermes/skills/yss-dto/references/README.md +0 -67
  1103. package/template/.hermes/skills/yss-dto/references/openapi-wire-profile.yaml +0 -187
  1104. package/template/.hermes/skills/yss-dto/references/source-index.md +0 -55
  1105. package/template/.hermes/skills/yss-excel-mvc/SKILL.md +0 -66
  1106. package/template/.hermes/skills/yss-excel-mvc/agents/openai.yaml +0 -5
  1107. package/template/.hermes/skills/yss-excel-mvc/assets/ExcelDynamicData.java +0 -66
  1108. package/template/.hermes/skills/yss-excel-mvc/assets/RequestExcel.java +0 -33
  1109. package/template/.hermes/skills/yss-excel-mvc/assets/RequestFastExcelArgumentResolver.java +0 -272
  1110. package/template/.hermes/skills/yss-excel-mvc/assets/ResponseExcel.java +0 -46
  1111. package/template/.hermes/skills/yss-excel-mvc/assets/ResponseFastExcelReturnValueHandler.java +0 -154
  1112. package/template/.hermes/skills/yss-excel-mvc/references/README.md +0 -5
  1113. package/template/.hermes/skills/yss-excel-mvc/references/source-index.md +0 -74
  1114. package/template/.hermes/skills/yss-exception/SKILL.md +0 -57
  1115. package/template/.hermes/skills/yss-exception/agents/openai.yaml +0 -4
  1116. package/template/.hermes/skills/yss-exception/references/source-index.md +0 -35
  1117. package/template/.hermes/skills/yss-formily/SKILL.md +0 -272
  1118. package/template/.hermes/skills/yss-formily-schema-generator/SKILL.md +0 -189
  1119. package/template/.hermes/skills/yss-formily-schema-generator/agents/openai.yaml +0 -5
  1120. package/template/.hermes/skills/yss-formily-schema-generator/assets/java-sdk/ExampleUsage.java +0 -111
  1121. package/template/.hermes/skills/yss-formily-schema-generator/assets/java-sdk/YssFormilyDsl.java +0 -1219
  1122. package/template/.hermes/skills/yss-formily-schema-generator/references/workflow.md +0 -240
  1123. package/template/.hermes/skills/yss-frontend-scaffold-generator/SKILL.md +0 -76
  1124. package/template/.hermes/skills/yss-hook/SKILL.md +0 -200
  1125. package/template/.hermes/skills/yss-hook/agents/openai.yaml +0 -5
  1126. package/template/.hermes/skills/yss-hook/references/frontend-docs.md +0 -10
  1127. package/template/.hermes/skills/yss-microapp-commit/SKILL.md +0 -201
  1128. package/template/.hermes/skills/yss-microapp-commit/agents/openai.yaml +0 -5
  1129. package/template/.hermes/skills/yss-microapp-commit/references/scope-mapping.md +0 -204
  1130. package/template/.hermes/skills/yss-mybatis/SKILL.md +0 -77
  1131. package/template/.hermes/skills/yss-mybatis/agents/openai.yaml +0 -5
  1132. package/template/.hermes/skills/yss-mybatis/assets/BaseRepository.java +0 -112
  1133. package/template/.hermes/skills/yss-mybatis/assets/EntityQueryAspect.java +0 -37
  1134. package/template/.hermes/skills/yss-mybatis/assets/MybatisBaseConfiguration.java +0 -18
  1135. package/template/.hermes/skills/yss-mybatis/assets/PageQueryEntityConfigration.java +0 -18
  1136. package/template/.hermes/skills/yss-mybatis/references/README.md +0 -57
  1137. package/template/.hermes/skills/yss-mybatis/references/source-index.md +0 -93
  1138. package/template/.hermes/skills/yss-openapi-draft-review/SKILL.md +0 -82
  1139. package/template/.hermes/skills/yss-openapi-governance/SKILL.md +0 -130
  1140. package/template/.hermes/skills/yss-openapi-governance/agents/openai.yaml +0 -4
  1141. package/template/.hermes/skills/yss-page-module-development/SKILL.md +0 -165
  1142. package/template/.hermes/skills/yss-page-module-development/agents/openai.yaml +0 -5
  1143. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/constant.ts +0 -32
  1144. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/hooks/useRuleConfig.ts +0 -158
  1145. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/index.vue +0 -188
  1146. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/schemas/ruleConfigSchema.ts +0 -256
  1147. package/template/.hermes/skills/yss-page-module-development/examples/AdvancedFormPage/style.less +0 -76
  1148. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/constant.ts +0 -128
  1149. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/hooks/useQualityRuleList.ts +0 -144
  1150. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/hooks/useRuleActions.ts +0 -141
  1151. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/index.vue +0 -175
  1152. package/template/.hermes/skills/yss-page-module-development/examples/StandardListPage/style.less +0 -83
  1153. package/template/.hermes/skills/yss-page-module-development/references/component-map.md +0 -34
  1154. package/template/.hermes/skills/yss-page-module-development/references/frontend-docs.md +0 -11
  1155. package/template/.hermes/skills/yss-page-module-development/references/hooks-map.md +0 -11
  1156. package/template/.hermes/skills/yss-page-module-development/references/utils-map.md +0 -14
  1157. package/template/.hermes/skills/yss-product-lifecycle/SKILL.md +0 -52
  1158. package/template/.hermes/skills/yss-product-lifecycle/agents/openai.yaml +0 -4
  1159. package/template/.hermes/skills/yss-product-lifecycle/references/artifact-dependencies.md +0 -36
  1160. package/template/.hermes/skills/yss-product-lifecycle/references/matt-yss-adapter.md +0 -122
  1161. package/template/.hermes/skills/yss-product-lifecycle/references/orchestration-contract.yaml +0 -865
  1162. package/template/.hermes/skills/yss-product-lifecycle/references/orchestration.md +0 -116
  1163. package/template/.hermes/skills/yss-product-lifecycle/references/state-model.md +0 -112
  1164. package/template/.hermes/skills/yss-prototype-stage/SKILL.md +0 -47
  1165. package/template/.hermes/skills/yss-prototype-stage/references/product-design-adapter.md +0 -54
  1166. package/template/.hermes/skills/yss-prototype-stage/scripts/prototype-contract.mjs +0 -207
  1167. package/template/.hermes/skills/yss-prototype-stage/tests/run-scenarios.mjs +0 -154
  1168. package/template/.hermes/skills/yss-repository/SKILL.md +0 -80
  1169. package/template/.hermes/skills/yss-repository/agents/openai.yaml +0 -5
  1170. package/template/.hermes/skills/yss-repository/references/infrastructure-layer-guide.md +0 -107
  1171. package/template/.hermes/skills/yss-research/SKILL.md +0 -73
  1172. package/template/.hermes/skills/yss-research/agents/openai.yaml +0 -3
  1173. package/template/.hermes/skills/yss-research/assets/evidence-template.yaml +0 -68
  1174. package/template/.hermes/skills/yss-research/assets/research-brief-template.md +0 -36
  1175. package/template/.hermes/skills/yss-research/references/evidence-contract.md +0 -42
  1176. package/template/.hermes/skills/yss-research/scripts/validate-research-package.mjs +0 -128
  1177. package/template/.hermes/skills/yss-research/tests/run-scenarios.mjs +0 -81
  1178. package/template/.hermes/skills/yss-resilience4j/SKILL.md +0 -46
  1179. package/template/.hermes/skills/yss-resilience4j/agents/openai.yaml +0 -4
  1180. package/template/.hermes/skills/yss-resilience4j/references/source-index.md +0 -33
  1181. package/template/.hermes/skills/yss-router/SKILL.md +0 -51
  1182. package/template/.hermes/skills/yss-router/agents/openai.yaml +0 -4
  1183. package/template/.hermes/skills/yss-router/references/boundaries.md +0 -50
  1184. package/template/.hermes/skills/yss-router/references/router-contract.yaml +0 -349
  1185. package/template/.hermes/skills/yss-router/references/slice-implementation-contract.md +0 -185
  1186. package/template/.hermes/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  1187. package/template/.hermes/skills/yss-security-algorithm/SKILL.md +0 -52
  1188. package/template/.hermes/skills/yss-security-algorithm/agents/openai.yaml +0 -4
  1189. package/template/.hermes/skills/yss-security-algorithm/references/source-index.md +0 -35
  1190. package/template/.hermes/skills/yss-source-index/SKILL.md +0 -63
  1191. package/template/.hermes/skills/yss-source-index/agents/openai.yaml +0 -4
  1192. package/template/.hermes/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  1193. package/template/.hermes/skills/yss-stage-decision/SKILL.md +0 -51
  1194. package/template/.hermes/skills/yss-stage-decision/references/domain-strategy-contract.md +0 -26
  1195. package/template/.hermes/skills/yss-stage-decision/references/domain-strategy.schema.json +0 -128
  1196. package/template/.hermes/skills/yss-stage-decision/references/stage-decision-package-contract.md +0 -19
  1197. package/template/.hermes/skills/yss-stage-decision/references/stage-decision-package.schema.json +0 -86
  1198. package/template/.hermes/skills/yss-stage-decision/references/validation-rules.md +0 -18
  1199. package/template/.hermes/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +0 -169
  1200. package/template/.hermes/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +0 -100
  1201. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +0 -15
  1202. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +0 -58
  1203. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +0 -44
  1204. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +0 -15
  1205. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +0 -49
  1206. package/template/.hermes/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +0 -108
  1207. package/template/.hermes/skills/yss-stage-decision/tests/run-scenarios.mjs +0 -68
  1208. package/template/.hermes/skills/yss-tactical-design/SKILL.md +0 -35
  1209. package/template/.hermes/skills/yss-tactical-design/agents/openai.yaml +0 -4
  1210. package/template/.hermes/skills/yss-tactical-design/references/output-template.md +0 -28
  1211. package/template/.hermes/skills/yss-tactical-design/references/tactical-design-contract.md +0 -41
  1212. package/template/.hermes/skills/yss-tactical-design/references/tactical-design.schema.json +0 -31
  1213. package/template/.hermes/skills/yss-tactical-design/references/validation-rules.md +0 -13
  1214. package/template/.hermes/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +0 -213
  1215. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +0 -21
  1216. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +0 -21
  1217. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +0 -21
  1218. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +0 -21
  1219. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +0 -21
  1220. package/template/.hermes/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +0 -80
  1221. package/template/.hermes/skills/yss-tactical-design/tests/run-scenarios.mjs +0 -47
  1222. package/template/.hermes/skills/yss-ui/SKILL.md +0 -170
  1223. package/template/.hermes/skills/yss-ui/agents/openai.yaml +0 -5
  1224. package/template/.hermes/skills/yss-ui/assets/demos/button/auth-basic.vue +0 -24
  1225. package/template/.hermes/skills/yss-ui/assets/demos/button/auth-dropdown.vue +0 -29
  1226. package/template/.hermes/skills/yss-ui/assets/demos/button/permission.vue +0 -10
  1227. package/template/.hermes/skills/yss-ui/assets/demos/button/size.vue +0 -12
  1228. package/template/.hermes/skills/yss-ui/assets/demos/button/states.vue +0 -12
  1229. package/template/.hermes/skills/yss-ui/assets/demos/card/basic/index.vue +0 -54
  1230. package/template/.hermes/skills/yss-ui/assets/demos/card/basic/style.less +0 -41
  1231. package/template/.hermes/skills/yss-ui/assets/demos/card/cover/index.vue +0 -25
  1232. package/template/.hermes/skills/yss-ui/assets/demos/card/flexible-layout/index.vue +0 -122
  1233. package/template/.hermes/skills/yss-ui/assets/demos/card/flexible-layout/style.less +0 -191
  1234. package/template/.hermes/skills/yss-ui/assets/demos/card/header-extra/index.vue +0 -260
  1235. package/template/.hermes/skills/yss-ui/assets/demos/card/header-extra/style.less +0 -124
  1236. package/template/.hermes/skills/yss-ui/assets/demos/card/layout-metrics/constants.ts +0 -61
  1237. package/template/.hermes/skills/yss-ui/assets/demos/card/layout-metrics/index.vue +0 -72
  1238. package/template/.hermes/skills/yss-ui/assets/demos/card/layout-metrics/metrics-cards.vue +0 -70
  1239. package/template/.hermes/skills/yss-ui/assets/demos/card/layout-metrics/style.less +0 -232
  1240. package/template/.hermes/skills/yss-ui/assets/demos/card/loading/index.vue +0 -19
  1241. package/template/.hermes/skills/yss-ui/assets/demos/card/meta/index.vue +0 -19
  1242. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/basic/constants.ts +0 -44
  1243. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/basic/hooks/useConditionBasic.ts +0 -18
  1244. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/basic/index.vue +0 -100
  1245. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/linked/constants.ts +0 -51
  1246. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/linked/hooks/useConditionLinked.ts +0 -29
  1247. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/linked/index.vue +0 -23
  1248. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/nesting-between/constants.ts +0 -35
  1249. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/nesting-between/hooks/useConditionNesting.ts +0 -25
  1250. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/nesting-between/index.vue +0 -24
  1251. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/remote/constants.ts +0 -7
  1252. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/remote/hooks/useConditionRemote.ts +0 -75
  1253. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/remote/index.vue +0 -23
  1254. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation/constants.ts +0 -42
  1255. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation/hooks/useConditionBasic.ts +0 -22
  1256. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation/index.vue +0 -138
  1257. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation-custom/constants.ts +0 -42
  1258. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation-custom/hooks/useConditionBasic.ts +0 -22
  1259. package/template/.hermes/skills/yss-ui/assets/demos/condition-builder/validation-custom/index.vue +0 -134
  1260. package/template/.hermes/skills/yss-ui/assets/demos/cron/basic/index.vue +0 -54
  1261. package/template/.hermes/skills/yss-ui/assets/demos/cron/select/index.vue +0 -145
  1262. package/template/.hermes/skills/yss-ui/assets/demos/echarts/basic.vue +0 -30
  1263. package/template/.hermes/skills/yss-ui/assets/demos/echarts/calendar-heatmap.vue +0 -49
  1264. package/template/.hermes/skills/yss-ui/assets/demos/echarts/candlestick.vue +0 -30
  1265. package/template/.hermes/skills/yss-ui/assets/demos/echarts/dark.vue +0 -40
  1266. package/template/.hermes/skills/yss-ui/assets/demos/echarts/expose-methods.vue +0 -97
  1267. package/template/.hermes/skills/yss-ui/assets/demos/echarts/funnel.vue +0 -42
  1268. package/template/.hermes/skills/yss-ui/assets/demos/echarts/gauge.vue +0 -25
  1269. package/template/.hermes/skills/yss-ui/assets/demos/echarts/line.vue +0 -24
  1270. package/template/.hermes/skills/yss-ui/assets/demos/echarts/mix.vue +0 -33
  1271. package/template/.hermes/skills/yss-ui/assets/demos/echarts/pie.vue +0 -34
  1272. package/template/.hermes/skills/yss-ui/assets/demos/echarts/radar.vue +0 -38
  1273. package/template/.hermes/skills/yss-ui/assets/demos/echarts/ring-segment.vue +0 -77
  1274. package/template/.hermes/skills/yss-ui/assets/demos/echarts/scatter.vue +0 -23
  1275. package/template/.hermes/skills/yss-ui/assets/demos/echarts/stacked-area.vue +0 -55
  1276. package/template/.hermes/skills/yss-ui/assets/demos/echarts/stacked-bar.vue +0 -52
  1277. package/template/.hermes/skills/yss-ui/assets/demos/echarts/treemap.vue +0 -30
  1278. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/all-types/hooks/useAllTypes.ts +0 -259
  1279. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/all-types/index.vue +0 -33
  1280. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/async-remote/hooks/useAsyncRemote.ts +0 -201
  1281. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/async-remote/index.vue +0 -45
  1282. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/basic/index.vue +0 -104
  1283. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/big-data/index.vue +0 -82
  1284. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/custom-rule/index.vue +0 -114
  1285. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/direct-editable/index.vue +0 -94
  1286. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/drag/index.vue +0 -125
  1287. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/filter/index.vue +0 -107
  1288. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/linkage/index.vue +0 -181
  1289. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/pagination-action/index.vue +0 -131
  1290. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/required/index.vue +0 -95
  1291. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/row-edit/index.vue +0 -220
  1292. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/row-options/index.vue +0 -165
  1293. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/slot-linkage/index.vue +0 -184
  1294. package/template/.hermes/skills/yss-ui/assets/demos/edit-table/submit-view/index.vue +0 -107
  1295. package/template/.hermes/skills/yss-ui/assets/demos/file-import/basic.vue +0 -54
  1296. package/template/.hermes/skills/yss-ui/assets/demos/file-import/formily.vue +0 -42
  1297. package/template/.hermes/skills/yss-ui/assets/demos/file-import/multiple.vue +0 -33
  1298. package/template/.hermes/skills/yss-ui/assets/demos/file-import/result-slot.vue +0 -30
  1299. package/template/.hermes/skills/yss-ui/assets/demos/formily/actions.vue +0 -61
  1300. package/template/.hermes/skills/yss-ui/assets/demos/formily/align.vue +0 -56
  1301. package/template/.hermes/skills/yss-ui/assets/demos/formily/async-validate.vue +0 -69
  1302. package/template/.hermes/skills/yss-ui/assets/demos/formily/basic.vue +0 -317
  1303. package/template/.hermes/skills/yss-ui/assets/demos/formily/collapse.vue +0 -104
  1304. package/template/.hermes/skills/yss-ui/assets/demos/formily/custom-component.vue +0 -75
  1305. package/template/.hermes/skills/yss-ui/assets/demos/formily/disabled.vue +0 -71
  1306. package/template/.hermes/skills/yss-ui/assets/demos/formily/dynamic-array.vue +0 -74
  1307. package/template/.hermes/skills/yss-ui/assets/demos/formily/dynamic.vue +0 -59
  1308. package/template/.hermes/skills/yss-ui/assets/demos/formily/effects.vue +0 -117
  1309. package/template/.hermes/skills/yss-ui/assets/demos/formily/events-inline.vue +0 -82
  1310. package/template/.hermes/skills/yss-ui/assets/demos/formily/events-scope.vue +0 -101
  1311. package/template/.hermes/skills/yss-ui/assets/demos/formily/grid.vue +0 -71
  1312. package/template/.hermes/skills/yss-ui/assets/demos/formily/group.vue +0 -102
  1313. package/template/.hermes/skills/yss-ui/assets/demos/formily/helper-slot.vue +0 -50
  1314. package/template/.hermes/skills/yss-ui/assets/demos/formily/label-tip.vue +0 -42
  1315. package/template/.hermes/skills/yss-ui/assets/demos/formily/linkage-multi.vue +0 -127
  1316. package/template/.hermes/skills/yss-ui/assets/demos/formily/linkage.vue +0 -78
  1317. package/template/.hermes/skills/yss-ui/assets/demos/formily/modes.vue +0 -72
  1318. package/template/.hermes/skills/yss-ui/assets/demos/formily/remote-enum.vue +0 -88
  1319. package/template/.hermes/skills/yss-ui/assets/demos/formily/slot.vue +0 -54
  1320. package/template/.hermes/skills/yss-ui/assets/demos/formily/steps.vue +0 -135
  1321. package/template/.hermes/skills/yss-ui/assets/demos/formily/submit-failed.vue +0 -72
  1322. package/template/.hermes/skills/yss-ui/assets/demos/monaco/basic.vue +0 -40
  1323. package/template/.hermes/skills/yss-ui/assets/demos/monaco/diff-basic.vue +0 -143
  1324. package/template/.hermes/skills/yss-ui/assets/demos/monaco/fullscreen.vue +0 -84
  1325. package/template/.hermes/skills/yss-ui/assets/demos/monaco/history-log-viewer.vue +0 -586
  1326. package/template/.hermes/skills/yss-ui/assets/demos/monaco/log-viewer.vue +0 -260
  1327. package/template/.hermes/skills/yss-ui/assets/demos/monaco/mock/mockLogApi.ts +0 -199
  1328. package/template/.hermes/skills/yss-ui/assets/demos/monaco/monaco.vue +0 -66
  1329. package/template/.hermes/skills/yss-ui/assets/demos/monaco/readonly.vue +0 -20
  1330. package/template/.hermes/skills/yss-ui/assets/demos/monaco/resizable.vue +0 -22
  1331. package/template/.hermes/skills/yss-ui/assets/demos/monaco/sql-schema.vue +0 -20
  1332. package/template/.hermes/skills/yss-ui/assets/demos/monaco/switch-lang-theme.vue +0 -48
  1333. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table/constant.ts +0 -18
  1334. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table/index.vue +0 -60
  1335. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table/style.less +0 -39
  1336. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table-layout/hooks/useTreeTable.ts +0 -158
  1337. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table-layout/index.vue +0 -88
  1338. package/template/.hermes/skills/yss-ui/assets/demos/split-pane/tree-table-layout/style.less +0 -73
  1339. package/template/.hermes/skills/yss-ui/assets/demos/table/action/index.vue +0 -83
  1340. package/template/.hermes/skills/yss-ui/assets/demos/table/action/style.less +0 -3
  1341. package/template/.hermes/skills/yss-ui/assets/demos/table/action-auth/index.vue +0 -41
  1342. package/template/.hermes/skills/yss-ui/assets/demos/table/action-slot/index.vue +0 -62
  1343. package/template/.hermes/skills/yss-ui/assets/demos/table/action-slot/style.less +0 -36
  1344. package/template/.hermes/skills/yss-ui/assets/demos/table/basic/hooks/useBasicTable.ts +0 -205
  1345. package/template/.hermes/skills/yss-ui/assets/demos/table/basic/index.vue +0 -89
  1346. package/template/.hermes/skills/yss-ui/assets/demos/table/basic/style.less +0 -28
  1347. package/template/.hermes/skills/yss-ui/assets/demos/table/column-custom-search/hooks/useColumnCustomSearch.ts +0 -236
  1348. package/template/.hermes/skills/yss-ui/assets/demos/table/column-custom-search/index.vue +0 -141
  1349. package/template/.hermes/skills/yss-ui/assets/demos/table/column-custom-search/style.less +0 -48
  1350. package/template/.hermes/skills/yss-ui/assets/demos/table/drag/index.vue +0 -42
  1351. package/template/.hermes/skills/yss-ui/assets/demos/table/expand/action-expand.vue +0 -142
  1352. package/template/.hermes/skills/yss-ui/assets/demos/table/expand/index.vue +0 -35
  1353. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-async/index.vue +0 -93
  1354. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-auto/index.vue +0 -99
  1355. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-custom/index.vue +0 -72
  1356. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-local/index.vue +0 -112
  1357. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-mixed/index.vue +0 -142
  1358. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-remote/constant.ts +0 -129
  1359. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-remote/hooks/useTableData.ts +0 -186
  1360. package/template/.hermes/skills/yss-ui/assets/demos/table/filter-remote/index.vue +0 -73
  1361. package/template/.hermes/skills/yss-ui/assets/demos/table/group-header/index.vue +0 -86
  1362. package/template/.hermes/skills/yss-ui/assets/demos/table/group-header/style.less +0 -3
  1363. package/template/.hermes/skills/yss-ui/assets/demos/table/header-height/index.vue +0 -29
  1364. package/template/.hermes/skills/yss-ui/assets/demos/table/header-height/style.less +0 -9
  1365. package/template/.hermes/skills/yss-ui/assets/demos/table/merge-cells/hooks/useMergeCells.ts +0 -183
  1366. package/template/.hermes/skills/yss-ui/assets/demos/table/merge-cells/index.vue +0 -17
  1367. package/template/.hermes/skills/yss-ui/assets/demos/table/merge-cells/style.less +0 -3
  1368. package/template/.hermes/skills/yss-ui/assets/demos/table/pagination-remote/hooks/usePaginationRemote.ts +0 -106
  1369. package/template/.hermes/skills/yss-ui/assets/demos/table/pagination-remote/index.vue +0 -24
  1370. package/template/.hermes/skills/yss-ui/assets/demos/table/renderers/index.vue +0 -64
  1371. package/template/.hermes/skills/yss-ui/assets/demos/table/selection-checkbox/index.vue +0 -88
  1372. package/template/.hermes/skills/yss-ui/assets/demos/table/selection-checkbox/style.less +0 -9
  1373. package/template/.hermes/skills/yss-ui/assets/demos/table/selection-radio/index.vue +0 -57
  1374. package/template/.hermes/skills/yss-ui/assets/demos/table/selection-radio/style.less +0 -9
  1375. package/template/.hermes/skills/yss-ui/assets/demos/table/transform/index.vue +0 -32
  1376. package/template/.hermes/skills/yss-ui/assets/demos/tree/basic-left/index.vue +0 -124
  1377. package/template/.hermes/skills/yss-ui/assets/demos/tree/datasource-group/index.vue +0 -31
  1378. package/template/.hermes/skills/yss-ui/assets/demos/tree/multi-checkable/index.vue +0 -86
  1379. package/template/.hermes/skills/yss-ui/assets/demos/tree/multiple-select/index.vue +0 -76
  1380. package/template/.hermes/skills/yss-ui/assets/demos/tree/resource-list/index.vue +0 -41
  1381. package/template/.hermes/skills/yss-ui/assets/demos/tree/search-toolbar/index.vue +0 -156
  1382. package/template/.hermes/skills/yss-ui/assets/demos/tree/slot-icon/index.vue +0 -36
  1383. package/template/.hermes/skills/yss-ui/assets/demos/tree/slot-switcher-icon/index.vue +0 -34
  1384. package/template/.hermes/skills/yss-ui/assets/docs/components/button.md +0 -79
  1385. package/template/.hermes/skills/yss-ui/assets/docs/components/card.md +0 -162
  1386. package/template/.hermes/skills/yss-ui/assets/docs/components/conditionBuilder.md +0 -114
  1387. package/template/.hermes/skills/yss-ui/assets/docs/components/cron.md +0 -77
  1388. package/template/.hermes/skills/yss-ui/assets/docs/components/echarts.md +0 -110
  1389. package/template/.hermes/skills/yss-ui/assets/docs/components/editTable.md +0 -212
  1390. package/template/.hermes/skills/yss-ui/assets/docs/components/fileImport.md +0 -85
  1391. package/template/.hermes/skills/yss-ui/assets/docs/components/formily.md +0 -370
  1392. package/template/.hermes/skills/yss-ui/assets/docs/components/monaco.md +0 -369
  1393. package/template/.hermes/skills/yss-ui/assets/docs/components/sheet.md +0 -169
  1394. package/template/.hermes/skills/yss-ui/assets/docs/components/split-pane.md +0 -45
  1395. package/template/.hermes/skills/yss-ui/assets/docs/components/table.md +0 -398
  1396. package/template/.hermes/skills/yss-ui/assets/docs/components/tree.md +0 -102
  1397. package/template/.hermes/skills/yss-ui/assets/docs/guide/gitlab-ci-integration.md +0 -166
  1398. package/template/.hermes/skills/yss-ui/assets/docs/guide/index.md +0 -132
  1399. package/template/.hermes/skills/yss-ui/assets/docs/guide/installation.md +0 -97
  1400. package/template/.hermes/skills/yss-ui/assets/docs/guide/jsp.md +0 -227
  1401. package/template/.hermes/skills/yss-ui/assets/docs/guide/llms.md +0 -295
  1402. package/template/.hermes/skills/yss-ui/assets/docs/guide/monorepo-versioning.md +0 -149
  1403. package/template/.hermes/skills/yss-ui/assets/docs/guide/release-workflow.md +0 -251
  1404. package/template/.hermes/skills/yss-ui/assets/docs/hooks/use-table-height.md +0 -101
  1405. package/template/.hermes/skills/yss-ui/assets/docs/hooks/use-tree-height.md +0 -115
  1406. package/template/.hermes/skills/yss-ui/assets/docs/hooks/useFullscreen.md +0 -130
  1407. package/template/.hermes/skills/yss-ui/assets/docs/hooks/useLoading.md +0 -97
  1408. package/template/.hermes/skills/yss-ui/assets/docs/root/resources.md +0 -18
  1409. package/template/.hermes/skills/yss-ui/assets/keyword-index.json +0 -53
  1410. package/template/.hermes/skills/yss-ui/assets/keyword-index.md +0 -47
  1411. package/template/.hermes/skills/yss-ui/assets/reference-index.json +0 -55
  1412. package/template/.hermes/skills/yss-ui/assets/reference-index.md +0 -74
  1413. package/template/.hermes/skills/yss-ui/assets/scenario-index.json +0 -50
  1414. package/template/.hermes/skills/yss-ui/assets/scenario-index.md +0 -41
  1415. package/template/.hermes/skills/yss-ui/references/accessibility.md +0 -23
  1416. package/template/.hermes/skills/yss-ui/references/antdv-compatibility.md +0 -39
  1417. package/template/.hermes/skills/yss-ui/references/checklist.md +0 -110
  1418. package/template/.hermes/skills/yss-ui/references/component-routing.md +0 -31
  1419. package/template/.hermes/skills/yss-ui/references/frontend-docs.md +0 -11
  1420. package/template/.hermes/skills/yss-ui/references/migration-antdv-to-yss.md +0 -20
  1421. package/template/.hermes/skills/yss-ui/references/quick-recipes.md +0 -280
  1422. package/template/.hermes/skills/yss-ui/references/theme-locale-overlay.md +0 -25
  1423. package/template/.hermes/skills/yss-ui/references/verification.md +0 -20
  1424. package/template/.hermes/skills/yss-ui-business-page-generation/SKILL.md +0 -159
  1425. package/template/.hermes/skills/yss-ui-business-page-generation/references/component-map.md +0 -39
  1426. package/template/.hermes/skills/yss-ui-business-page-generation/references/hooks-map.md +0 -70
  1427. package/template/.hermes/skills/yss-ui-business-page-generation/references/utils-map.md +0 -51
  1428. package/template/.hermes/skills/yss-up-springboot3/SKILL.md +0 -40
  1429. package/template/.hermes/skills/yss-up-springboot3/agents/openai.yaml +0 -4
  1430. package/template/.hermes/skills/yss-use-table-height/SKILL.md +0 -130
  1431. package/template/.hermes/skills/yss-use-table-height/agents/openai.yaml +0 -5
  1432. package/template/.hermes/skills/yss-use-table-height/references/frontend-docs.md +0 -9
  1433. package/template/.hermes/skills/yss-use-tree-height/SKILL.md +0 -98
  1434. package/template/.hermes/skills/yss-use-tree-height/agents/openai.yaml +0 -5
  1435. package/template/.hermes/skills/yss-use-tree-height/references/frontend-docs.md +0 -9
  1436. package/template/.hermes/skills/yss-userinfo/SKILL.md +0 -49
  1437. package/template/.hermes/skills/yss-userinfo/agents/openai.yaml +0 -4
  1438. package/template/.hermes/skills/yss-userinfo/references/source-index.md +0 -34
  1439. package/template/.hermes/skills/yss-validation/SKILL.md +0 -56
  1440. package/template/.hermes/skills/yss-validation/agents/openai.yaml +0 -4
  1441. package/template/.hermes/skills/yss-validation/references/source-index.md +0 -33
  1442. package/template/.hermes/skills/yss-web-controller/SKILL.md +0 -85
  1443. package/template/.hermes/skills/yss-web-controller/agents/openai.yaml +0 -5
  1444. package/template/.hermes/skills/yss-web-controller/assets/templates/Controller.java.template +0 -72
  1445. package/template/.hermes/skills/yss-web-controller/assets/templates/WebConvertor.java.template +0 -30
  1446. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +0 -19
  1447. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +0 -21
  1448. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/Response.java.template +0 -18
  1449. package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +0 -19
  1450. package/template/.hermes/skills/yss-web-controller/references/web-adapter-layer-guide.md +0 -86
  1451. package/template/.hermes/skills/yss-web-controller/references/web-generation-contract.schema.json +0 -97
  1452. package/template/.hermes/skills/yss-web-controller/scripts/generate_controller.mjs +0 -226
  1453. package/template/.hermes/skills/yss-web-controller/scripts/generate_controller.test.mjs +0 -156
  1454. package/template/.hermes/skills/ytable-usage/SKILL.md +0 -251
  1455. package/template/.hermes/skills/ytree-usage/SKILL.md +0 -132
  1456. package/template/.kiro/settings/mcp.json +0 -8
  1457. package/template/.pi/skills/yss-router/SKILL.md +0 -51
  1458. package/template/.pi/skills/yss-router/agents/openai.yaml +0 -4
  1459. package/template/.pi/skills/yss-router/references/boundaries.md +0 -50
  1460. package/template/.pi/skills/yss-router/references/router-contract.yaml +0 -349
  1461. package/template/.pi/skills/yss-router/references/slice-implementation-contract.md +0 -185
  1462. package/template/.pi/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  1463. package/template/.pi/skills/yss-source-index/SKILL.md +0 -63
  1464. package/template/.pi/skills/yss-source-index/agents/openai.yaml +0 -4
  1465. package/template/.pi/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  1466. package/template/.qoder/skills/yss-router/SKILL.md +0 -51
  1467. package/template/.qoder/skills/yss-router/agents/openai.yaml +0 -4
  1468. package/template/.qoder/skills/yss-router/references/boundaries.md +0 -50
  1469. package/template/.qoder/skills/yss-router/references/router-contract.yaml +0 -349
  1470. package/template/.qoder/skills/yss-router/references/slice-implementation-contract.md +0 -185
  1471. package/template/.qoder/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  1472. package/template/.qoder/skills/yss-source-index/SKILL.md +0 -63
  1473. package/template/.qoder/skills/yss-source-index/agents/openai.yaml +0 -4
  1474. package/template/.qoder/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  1475. package/template/.trae/skills/yss-router/SKILL.md +0 -51
  1476. package/template/.trae/skills/yss-router/agents/openai.yaml +0 -4
  1477. package/template/.trae/skills/yss-router/references/boundaries.md +0 -50
  1478. package/template/.trae/skills/yss-router/references/router-contract.yaml +0 -349
  1479. package/template/.trae/skills/yss-router/references/slice-implementation-contract.md +0 -185
  1480. package/template/.trae/skills/yss-router/references/yss-skill-execution-result.md +0 -67
  1481. package/template/.trae/skills/yss-source-index/SKILL.md +0 -63
  1482. package/template/.trae/skills/yss-source-index/agents/openai.yaml +0 -4
  1483. package/template/.trae/skills/yss-source-index/references/source-location.md +0 -18
  1484. package/template/.trae/skills/yss-source-index/references/source-map-config.md +0 -30
  1485. package/template/.trae/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +0 -24
  1486. package/template/.trae/skills/yss-source-index/scripts/refresh-yss-skill-index.test.mjs +0 -15
  1487. package/template/scripts/verify-yss-router-scenarios +0 -3
  1488. /package/template/.agents/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1489. /package/template/.agents/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1490. /package/template/.agents/skills/{yss-source-index → yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1491. /package/template/.claude/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1492. /package/template/.claude/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1493. /package/template/.claude/skills/{yss-source-index → yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1494. /package/template/.codex/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1495. /package/template/.codex/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1496. /package/template/.codex/skills/{yss-source-index → yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1497. /package/template/.cursor/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1498. /package/template/.cursor/skills/{yss-source-index → yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1499. /package/template/.cursor/skills/{yss-source-index → yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1500. /package/template/{.hermes/skills/yss-source-index → .pi/skills/yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1501. /package/template/{.hermes/skills/yss-source-index → .pi/skills/yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1502. /package/template/{.hermes/skills/yss-source-index → .pi/skills/yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1503. /package/template/{.pi/skills/yss-source-index → .qoder/skills/yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1504. /package/template/{.pi/skills/yss-source-index → .qoder/skills/yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1505. /package/template/{.pi/skills/yss-source-index → .qoder/skills/yss-skill-source-index-refresh}/scripts/refresh-yss-skill-index.test.mjs +0 -0
  1506. /package/template/{.qoder/skills/yss-source-index → .trae/skills/yss-skill-source-index-refresh}/references/source-location.md +0 -0
  1507. /package/template/{.qoder/skills/yss-source-index → .trae/skills/yss-skill-source-index-refresh}/references/source-map-config.md +0 -0
  1508. /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
- });