@starci/skills 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. package/INDEX.md +74 -0
  2. package/INDEX.vi.md +75 -0
  3. package/README.md +44 -0
  4. package/README.vi.md +43 -0
  5. package/SKILL.md +135 -0
  6. package/SKILL.vi.md +128 -0
  7. package/alias/INDEX.md +104 -0
  8. package/alias/INDEX.vi.md +104 -0
  9. package/alias/alias.json +297 -0
  10. package/bin/starci-skills.mjs +219 -0
  11. package/knowledge/INDEX.md +22 -0
  12. package/knowledge/INDEX.vi.md +22 -0
  13. package/knowledge/grammars/starci/DNA.md +146 -0
  14. package/knowledge/grammars/starci/DNA.vi.md +146 -0
  15. package/knowledge/grammars/starci/INDEX.md +25 -0
  16. package/knowledge/grammars/starci/INDEX.vi.md +25 -0
  17. package/knowledge/grammars/starci/family.md +50 -0
  18. package/knowledge/grammars/starci/family.vi.md +50 -0
  19. package/knowledge/grammars/starci/idioms.md +120 -0
  20. package/knowledge/grammars/starci/idioms.vi.md +121 -0
  21. package/knowledge/grammars/starci/playbook.md +36 -0
  22. package/knowledge/grammars/starci/playbook.vi.md +35 -0
  23. package/knowledge/patterns/be/INDEX.md +29 -0
  24. package/knowledge/patterns/be/INDEX.vi.md +29 -0
  25. package/knowledge/patterns/be/comment.md +80 -0
  26. package/knowledge/patterns/be/comment.vi.md +80 -0
  27. package/knowledge/patterns/be/error.md +86 -0
  28. package/knowledge/patterns/be/error.vi.md +86 -0
  29. package/knowledge/patterns/be/folder.md +87 -0
  30. package/knowledge/patterns/be/folder.vi.md +86 -0
  31. package/knowledge/patterns/be/function.md +80 -0
  32. package/knowledge/patterns/be/function.vi.md +80 -0
  33. package/knowledge/patterns/be/imports.md +79 -0
  34. package/knowledge/patterns/be/imports.vi.md +79 -0
  35. package/knowledge/patterns/be/naming.md +87 -0
  36. package/knowledge/patterns/be/naming.vi.md +87 -0
  37. package/knowledge/patterns/be/test.md +79 -0
  38. package/knowledge/patterns/be/test.vi.md +79 -0
  39. package/knowledge/patterns/be/typing.md +73 -0
  40. package/knowledge/patterns/be/typing.vi.md +73 -0
  41. package/knowledge/patterns/fe/INDEX.md +29 -0
  42. package/knowledge/patterns/fe/INDEX.vi.md +29 -0
  43. package/knowledge/patterns/fe/comment.md +70 -0
  44. package/knowledge/patterns/fe/comment.vi.md +70 -0
  45. package/knowledge/patterns/fe/error.md +61 -0
  46. package/knowledge/patterns/fe/error.vi.md +61 -0
  47. package/knowledge/patterns/fe/folder.md +98 -0
  48. package/knowledge/patterns/fe/folder.vi.md +95 -0
  49. package/knowledge/patterns/fe/function.md +70 -0
  50. package/knowledge/patterns/fe/function.vi.md +70 -0
  51. package/knowledge/patterns/fe/imports.md +87 -0
  52. package/knowledge/patterns/fe/imports.vi.md +87 -0
  53. package/knowledge/patterns/fe/naming.md +77 -0
  54. package/knowledge/patterns/fe/naming.vi.md +77 -0
  55. package/knowledge/patterns/fe/test.md +73 -0
  56. package/knowledge/patterns/fe/test.vi.md +73 -0
  57. package/knowledge/patterns/fe/typing.md +67 -0
  58. package/knowledge/patterns/fe/typing.vi.md +67 -0
  59. package/knowledge/ui/INDEX.md +101 -0
  60. package/knowledge/ui/INDEX.vi.md +101 -0
  61. package/knowledge/ui/composition/INDEX.md +60 -0
  62. package/knowledge/ui/composition/INDEX.vi.md +63 -0
  63. package/knowledge/ui/composition/accent.md +73 -0
  64. package/knowledge/ui/composition/accent.vi.md +74 -0
  65. package/knowledge/ui/composition/action.md +55 -0
  66. package/knowledge/ui/composition/action.vi.md +56 -0
  67. package/knowledge/ui/composition/coverage.md +37 -0
  68. package/knowledge/ui/composition/coverage.vi.md +37 -0
  69. package/knowledge/ui/composition/cta.md +79 -0
  70. package/knowledge/ui/composition/cta.vi.md +79 -0
  71. package/knowledge/ui/composition/feedback.md +55 -0
  72. package/knowledge/ui/composition/feedback.vi.md +56 -0
  73. package/knowledge/ui/composition/hierarchy.md +83 -0
  74. package/knowledge/ui/composition/hierarchy.vi.md +82 -0
  75. package/knowledge/ui/composition/layout.md +91 -0
  76. package/knowledge/ui/composition/layout.vi.md +93 -0
  77. package/knowledge/ui/composition/responsive.md +67 -0
  78. package/knowledge/ui/composition/responsive.vi.md +67 -0
  79. package/knowledge/ui/composition/state.md +105 -0
  80. package/knowledge/ui/composition/state.vi.md +106 -0
  81. package/knowledge/ui/presentation/INDEX.md +94 -0
  82. package/knowledge/ui/presentation/INDEX.vi.md +69 -0
  83. package/knowledge/ui/presentation/boundary.md +170 -0
  84. package/knowledge/ui/presentation/boundary.vi.md +169 -0
  85. package/knowledge/ui/presentation/font.md +155 -0
  86. package/knowledge/ui/presentation/font.vi.md +156 -0
  87. package/knowledge/ui/presentation/gap.md +181 -0
  88. package/knowledge/ui/presentation/gap.vi.md +181 -0
  89. package/knowledge/ui/presentation/margin.md +168 -0
  90. package/knowledge/ui/presentation/margin.vi.md +171 -0
  91. package/knowledge/ui/presentation/measure.md +174 -0
  92. package/knowledge/ui/presentation/measure.vi.md +178 -0
  93. package/knowledge/ui/presentation/overflow.md +142 -0
  94. package/knowledge/ui/presentation/overflow.vi.md +142 -0
  95. package/knowledge/ui/presentation/padding.md +293 -0
  96. package/knowledge/ui/presentation/padding.vi.md +292 -0
  97. package/knowledge/ui/presentation/surface.md +166 -0
  98. package/knowledge/ui/presentation/surface.vi.md +166 -0
  99. package/knowledge/ui/presentation/text-flow.md +123 -0
  100. package/knowledge/ui/presentation/text-flow.vi.md +123 -0
  101. package/knowledge/ui/presentation/tone.md +114 -0
  102. package/knowledge/ui/presentation/tone.vi.md +113 -0
  103. package/knowledge/ui/proof/INDEX.md +55 -0
  104. package/knowledge/ui/proof/INDEX.vi.md +55 -0
  105. package/knowledge/ui/proof/accessibility.md +67 -0
  106. package/knowledge/ui/proof/accessibility.vi.md +68 -0
  107. package/knowledge/ui/proof/contrast.md +58 -0
  108. package/knowledge/ui/proof/contrast.vi.md +57 -0
  109. package/knowledge/ui/proof/focus.md +76 -0
  110. package/knowledge/ui/proof/focus.vi.md +76 -0
  111. package/knowledge/ui/proof/motion.md +63 -0
  112. package/knowledge/ui/proof/motion.vi.md +64 -0
  113. package/knowledge/ui/proof/render-truth.md +63 -0
  114. package/knowledge/ui/proof/render-truth.vi.md +63 -0
  115. package/operators/INDEX.md +199 -0
  116. package/operators/INDEX.vi.md +199 -0
  117. package/operators/architecture-decide/errors.json +115 -0
  118. package/operators/architecture-decide/operator.json +20 -0
  119. package/operators/architecture-decide/operator.md +133 -0
  120. package/operators/architecture-decide/operator.vi.md +130 -0
  121. package/operators/architecture-decide/self-test.mjs +221 -0
  122. package/operators/architecture-decide/validate.mjs +112 -0
  123. package/operators/backend-source-apply/errors.json +66 -0
  124. package/operators/backend-source-apply/operator.json +20 -0
  125. package/operators/backend-source-apply/operator.md +189 -0
  126. package/operators/backend-source-apply/operator.vi.md +185 -0
  127. package/operators/backend-source-apply/self-test.mjs +257 -0
  128. package/operators/backend-source-apply/validate.mjs +240 -0
  129. package/operators/business-decide/errors.json +78 -0
  130. package/operators/business-decide/operator.json +20 -0
  131. package/operators/business-decide/operator.md +176 -0
  132. package/operators/business-decide/operator.vi.md +174 -0
  133. package/operators/business-decide/self-test.mjs +225 -0
  134. package/operators/business-decide/validate.mjs +277 -0
  135. package/operators/content-generate/errors.json +106 -0
  136. package/operators/content-generate/operator.json +21 -0
  137. package/operators/content-generate/operator.md +155 -0
  138. package/operators/content-generate/operator.vi.md +155 -0
  139. package/operators/content-generate/self-test.mjs +288 -0
  140. package/operators/content-generate/validate.mjs +196 -0
  141. package/operators/errors.json +178 -0
  142. package/operators/frontend-direction-decide/errors.json +135 -0
  143. package/operators/frontend-direction-decide/operator.json +21 -0
  144. package/operators/frontend-direction-decide/operator.md +167 -0
  145. package/operators/frontend-direction-decide/operator.vi.md +167 -0
  146. package/operators/frontend-direction-decide/self-test.mjs +226 -0
  147. package/operators/frontend-direction-decide/validate.mjs +146 -0
  148. package/operators/frontend-presentation-resolve/errors.json +42 -0
  149. package/operators/frontend-presentation-resolve/operator.json +19 -0
  150. package/operators/frontend-presentation-resolve/operator.md +142 -0
  151. package/operators/frontend-presentation-resolve/operator.vi.md +140 -0
  152. package/operators/frontend-presentation-resolve/self-test.mjs +163 -0
  153. package/operators/frontend-presentation-resolve/validate.mjs +143 -0
  154. package/operators/frontend-source-apply/errors.json +30 -0
  155. package/operators/frontend-source-apply/operator.json +20 -0
  156. package/operators/frontend-source-apply/operator.md +127 -0
  157. package/operators/frontend-source-apply/operator.vi.md +126 -0
  158. package/operators/frontend-source-apply/self-test.mjs +214 -0
  159. package/operators/frontend-source-apply/validate.mjs +133 -0
  160. package/operators/frontend-surface-audit/errors.json +5 -0
  161. package/operators/frontend-surface-audit/operator.json +22 -0
  162. package/operators/frontend-surface-audit/operator.md +121 -0
  163. package/operators/frontend-surface-audit/operator.vi.md +121 -0
  164. package/operators/frontend-surface-audit/self-test.mjs +185 -0
  165. package/operators/frontend-surface-audit/validate.mjs +127 -0
  166. package/operators/git-publish/errors.json +54 -0
  167. package/operators/git-publish/operator.json +20 -0
  168. package/operators/git-publish/operator.md +180 -0
  169. package/operators/git-publish/operator.vi.md +178 -0
  170. package/operators/git-publish/self-test.mjs +177 -0
  171. package/operators/git-publish/validate.mjs +106 -0
  172. package/operators/platform-operate/errors.json +90 -0
  173. package/operators/platform-operate/operator.json +22 -0
  174. package/operators/platform-operate/operator.md +162 -0
  175. package/operators/platform-operate/operator.vi.md +160 -0
  176. package/operators/platform-operate/self-test.mjs +202 -0
  177. package/operators/platform-operate/validate.mjs +193 -0
  178. package/operators/quality-verify/errors.json +54 -0
  179. package/operators/quality-verify/operator.json +20 -0
  180. package/operators/quality-verify/operator.md +185 -0
  181. package/operators/quality-verify/operator.vi.md +177 -0
  182. package/operators/quality-verify/self-test.mjs +210 -0
  183. package/operators/quality-verify/validate.mjs +197 -0
  184. package/operators/release-deploy/errors.json +158 -0
  185. package/operators/release-deploy/operator.json +23 -0
  186. package/operators/release-deploy/operator.md +183 -0
  187. package/operators/release-deploy/operator.vi.md +181 -0
  188. package/operators/release-deploy/self-test.mjs +235 -0
  189. package/operators/release-deploy/validate.mjs +129 -0
  190. package/operators/uat-verify/errors.json +78 -0
  191. package/operators/uat-verify/operator.json +25 -0
  192. package/operators/uat-verify/operator.md +162 -0
  193. package/operators/uat-verify/operator.vi.md +161 -0
  194. package/operators/uat-verify/self-test.mjs +270 -0
  195. package/operators/uat-verify/validate.mjs +202 -0
  196. package/operators/workspace-bind/errors.json +90 -0
  197. package/operators/workspace-bind/operator.json +21 -0
  198. package/operators/workspace-bind/operator.md +148 -0
  199. package/operators/workspace-bind/operator.vi.md +148 -0
  200. package/operators/workspace-bind/self-test.mjs +205 -0
  201. package/operators/workspace-bind/validate.mjs +152 -0
  202. package/package.json +50 -0
  203. package/readiness/initialization/workspaces/commit-policy.json +91 -0
  204. package/readiness/initialization/workspaces/config.schema.json +35 -0
  205. package/readiness/initialization/workspaces/device-state.schema.json +54 -0
  206. package/readiness/initialization/workspaces/local-route.schema.json +206 -0
  207. package/readiness/initialization/workspaces/portable-route.schema.json +200 -0
  208. package/resources/INDEX.md +96 -0
  209. package/resources/INDEX.vi.md +99 -0
  210. package/resources/agents/profiles/claude.json +131 -0
  211. package/resources/agents/profiles/openai.json +131 -0
  212. package/resources/orchestrator.json +71 -0
  213. package/resources/tools.json +85 -0
  214. package/routing.json +272 -0
  215. package/scripts/alias-registry.mjs +31 -0
  216. package/scripts/device-state.mjs +497 -0
  217. package/scripts/device-state.spec.mjs +18 -0
  218. package/scripts/errors-registry.mjs +68 -0
  219. package/scripts/generate-alias-doc.mjs +66 -0
  220. package/scripts/generate-grammar-dna.mjs +387 -0
  221. package/scripts/generate-operators-index.mjs +95 -0
  222. package/scripts/generate-presentation-owned.mjs +681 -0
  223. package/scripts/install-cli.spec.mjs +74 -0
  224. package/scripts/json-schema.mjs +94 -0
  225. package/scripts/operator-md.mjs +96 -0
  226. package/scripts/run-operator-self-tests.mjs +36 -0
  227. package/scripts/validate-alias.mjs +165 -0
  228. package/scripts/validate-defaults.mjs +72 -0
  229. package/scripts/validate-knowledge-citations.mjs +90 -0
  230. package/scripts/validate-operator.mjs +125 -0
  231. package/scripts/validate-request.mjs +80 -0
  232. package/scripts/validate-resources.mjs +117 -0
  233. package/scripts/validate-response.mjs +141 -0
  234. package/scripts/validate-routing.mjs +91 -0
  235. package/scripts/validate-step.mjs +50 -0
  236. package/scripts/validate-templates.mjs +226 -0
  237. package/scripts/validate-templates.spec.mjs +144 -0
  238. package/scripts/validate-workflows.mjs +106 -0
  239. package/scripts/workspace-portable.mjs +389 -0
  240. package/scripts/workspace-portable.spec.mjs +246 -0
  241. package/templates/README.md +47 -0
  242. package/templates/README.vi.md +45 -0
  243. package/templates/changes.example.md +27 -0
  244. package/templates/grammars.template.md +34 -0
  245. package/templates/kinds/architecture-decision.contract.json +14 -0
  246. package/templates/kinds/architecture-decision.skeleton.md +52 -0
  247. package/templates/kinds/backend-source-application.contract.json +10 -0
  248. package/templates/kinds/backend-source-application.skeleton.md +34 -0
  249. package/templates/kinds/business-promise-authority.contract.json +13 -0
  250. package/templates/kinds/business-promise-authority.skeleton.md +67 -0
  251. package/templates/kinds/capture.schema.json +33 -0
  252. package/templates/kinds/changes.contract.json +10 -0
  253. package/templates/kinds/changes.skeleton.md +26 -0
  254. package/templates/kinds/checks.schema.json +44 -0
  255. package/templates/kinds/claims.schema.json +165 -0
  256. package/templates/kinds/conformance.schema.json +15 -0
  257. package/templates/kinds/content-brief.contract.json +12 -0
  258. package/templates/kinds/content-brief.skeleton.md +45 -0
  259. package/templates/kinds/content-generation-receipt.contract.json +11 -0
  260. package/templates/kinds/content-generation-receipt.skeleton.md +40 -0
  261. package/templates/kinds/content-review.contract.json +11 -0
  262. package/templates/kinds/content-review.skeleton.md +45 -0
  263. package/templates/kinds/contract.schema.json +28 -0
  264. package/templates/kinds/coverage-matrix.schema.json +86 -0
  265. package/templates/kinds/coverage.schema.json +62 -0
  266. package/templates/kinds/current-state.schema.json +40 -0
  267. package/templates/kinds/delta.schema.json +95 -0
  268. package/templates/kinds/e2e.schema.json +63 -0
  269. package/templates/kinds/frontend-direction-decision.contract.json +93 -0
  270. package/templates/kinds/frontend-direction-decision.skeleton.md +68 -0
  271. package/templates/kinds/frontend-presentation-resolution.contract.json +11 -0
  272. package/templates/kinds/frontend-presentation-resolution.skeleton.md +32 -0
  273. package/templates/kinds/frontend-source-application.contract.json +10 -0
  274. package/templates/kinds/frontend-source-application.skeleton.md +30 -0
  275. package/templates/kinds/frontend-surface-audit.contract.json +52 -0
  276. package/templates/kinds/frontend-surface-audit.skeleton.md +32 -0
  277. package/templates/kinds/gate-result.schema.json +64 -0
  278. package/templates/kinds/git-publication.contract.json +82 -0
  279. package/templates/kinds/git-publication.skeleton.md +61 -0
  280. package/templates/kinds/independent-critique.contract.json +9 -0
  281. package/templates/kinds/independent-critique.skeleton.md +28 -0
  282. package/templates/kinds/inventory.schema.json +35 -0
  283. package/templates/kinds/model.schema.json +99 -0
  284. package/templates/kinds/mutations.schema.json +65 -0
  285. package/templates/kinds/platform-operation-receipt.contract.json +76 -0
  286. package/templates/kinds/platform-operation-receipt.skeleton.md +54 -0
  287. package/templates/kinds/probes.schema.json +130 -0
  288. package/templates/kinds/proof.schema.json +17 -0
  289. package/templates/kinds/quality-verification.contract.json +87 -0
  290. package/templates/kinds/quality-verification.skeleton.md +58 -0
  291. package/templates/kinds/release-deployment.contract.json +85 -0
  292. package/templates/kinds/release-deployment.skeleton.md +67 -0
  293. package/templates/kinds/route.schema.json +293 -0
  294. package/templates/kinds/stack-model.schema.json +90 -0
  295. package/templates/kinds/uat-capture.schema.json +35 -0
  296. package/templates/kinds/uat-flow-verification.contract.json +11 -0
  297. package/templates/kinds/uat-flow-verification.skeleton.md +47 -0
  298. package/templates/kinds/uat-snapshot.schema.json +96 -0
  299. package/templates/kinds/uat-verdicts.schema.json +41 -0
  300. package/templates/kinds/ui-coverage.schema.json +77 -0
  301. package/templates/kinds/verdicts.schema.json +39 -0
  302. package/templates/kinds/workspace-route-binding.contract.json +12 -0
  303. package/templates/kinds/workspace-route-binding.skeleton.md +60 -0
  304. package/templates/kinds/writes.schema.json +37 -0
  305. package/templates/operator.template.md +73 -0
  306. package/templates/patterns.template.md +31 -0
  307. package/templates/step/request.schema.json +42 -0
  308. package/templates/step/response.schema.json +144 -0
  309. package/templates/ui-composition.template.md +36 -0
  310. package/templates/ui-presentation.template.md +57 -0
  311. package/templates/ui-proof.template.md +34 -0
  312. package/workflows/README.md +37 -0
  313. package/workflows/README.vi.md +37 -0
  314. package/workflows/backend-feature.json +59 -0
  315. package/workflows/content-unit.json +19 -0
  316. package/workflows/frontend-new-surface.json +81 -0
  317. package/workflows/frontend-reconstruct.json +67 -0
  318. package/workflows/frontend-refine.json +67 -0
  319. package/workflows/frontend-with-uat.json +78 -0
  320. package/workflows/full-feature.json +104 -0
  321. package/workflows/release.json +29 -0
@@ -0,0 +1,155 @@
1
+ # content.generate
2
+
3
+ ## Việc
4
+
5
+ Sinh hoặc viết lại một đơn vị nội dung giáo dục trong một lượt tuyến tính: một brief người dạy ràng
6
+ buộc mọi thứ sau nó, một bản viết cho mỗi ngôn ngữ đã khai, hình ảnh làm theo một tuyên bố đã nêu, mã
7
+ nguồn và các phép kiểm chạy được thật, và một phản biện độc lập nhận các artifact mà không có lý lẽ
8
+ của người sản xuất.
9
+
10
+ ## Brief đi trước và ràng buộc mọi thứ sau nó
11
+
12
+ Brief người dạy được viết trước mọi thứ khác, từ chương trình học và bằng chứng nguồn, trong một lượt
13
+ chạy mới không thừa hưởng lượt nào. Nó công bố đầu vào của người học, các kết quả quan sát được, các
14
+ tuyên bố mà một hình ảnh được phép mã hoá, các ví dụ, và các disposition thêm, đổi, bỏ tường minh,
15
+ rồi được lấy fingerprint để không gì phía sau nới nó ra. Mọi thứ sau đó được đo theo nó: một bản viết
16
+ chỉ được nhận phủ một kết quả mà brief đã công bố, một hình ảnh chỉ được mã hoá một tuyên bố brief đã
17
+ công bố, và mọi bản viết đã khai phải phủ trọn tập kết quả trước khi đơn vị được ship. Một bản viết
18
+ chỉ phủ một phần brief là một bài học thiếu ở ngôn ngữ này và đủ ở ngôn ngữ kia, đúng là khiếm khuyết
19
+ lặng lẽ mà phép kiểm này sinh ra để bắt. Một disposition đổi hay bỏ phải nêu tên thứ nó tác động, vì
20
+ một disposition không có đích chỉ là một mong muốn.
21
+
22
+ ## Mã sinh ra phải chạy được thật
23
+
24
+ Mỗi track cài đặt được build bằng đúng câu lệnh mà `commands` nêu cho nó, và mã thoát được đọc và ghi
25
+ vào `response/data/e2e.json`. Một mã thoát khác không thì không được ship, vì bài học sẽ nói với người
26
+ học rằng đoạn code chạy được mà không có bằng chứng của ai cả. Bước 7 chạy câu lệnh đã khai cho từng
27
+ track trong một vòng lặp chạy, đọc và sửa có trần là `maxE2eIterations`; vòng lặp nằm bên trong bước,
28
+ nên `E2E_FAILED` được nêu một lần, khi hết số vòng, chứ không phải ngay lượt chạy đỏ đầu tiên. Vòng
29
+ lặp được sửa phần cài đặt hoặc bộ khung kiểm. Nó không bao giờ được dời contract mà nó bị đo theo, nên
30
+ fingerprint contract được lấy trước lượt chạy đầu và so lại sau lượt cuối: một test bị xoá, bị bỏ qua,
31
+ bị nới hay bị đặc cách để lượt chạy thành xanh chính là thất bại mà phép so ấy sinh ra để bắt, và nó
32
+ là `CONTRACT_WEAKENED`. Một track mang câu lệnh build và mã thoát của nó, một lượt chạy mang câu lệnh,
33
+ mã thoát và các assertion có tên, và không cái nào mang một câu tuyên bố thành công.
34
+
35
+ ## Hình ảnh làm theo một ý định đã nêu
36
+
37
+ Hình ảnh được dẫn ra từ các tuyên bố của brief, prompt nói ý định ấy được lưu cạnh kết quả, và kết quả
38
+ được soi về độ đọc được, thứ bậc và độ trung thành với tuyên bố. Bước 5 chỉ chạy khi `stageModes` bật
39
+ hình ảnh; khi tắt, bước không sinh gì cả, không hình và không prompt, và biên nhận ghi
40
+ `STAGE_DISABLED`, vì một quyết định không để lại dấu vết sau này đọc thành một sự bỏ sót. Một tuyên bố
41
+ brief chưa công bố thì không được xuất hiện, và một hình ảnh trượt chính phép soi trung thành của nó
42
+ thì không được ship. Hình ảnh không phải đồ trang trí thêm vào lúc cuối; nó là một trong các tuyên bố,
43
+ được vẽ ra.
44
+
45
+ ## Phản biện là một cuộc trao đổi lồng, và sửa lại là fallback có trần
46
+
47
+ Sau phép kiểm chạy được, nhánh tạm ngưng: nó phát `response/response.json` với status `waiting` và
48
+ `awaiting { exchange: review, kind: content-review }`. Orchestrator ghi `review/request/request.json`
49
+ với các artifact đã sản xuất và các tuyên bố chúng đưa ra làm đầu vào, không bao giờ kèm lý lẽ của
50
+ người sản xuất, và chạy một agent mới trên chính profile của operator này, không thừa hưởng lượt nào;
51
+ agent ấy không bao giờ là lượt chạy đã viết brief hay một bản viết, và nó chỉ ghi `review/response/`.
52
+ Nó chấm độ đúng, sư phạm, giá trị phỏng vấn và ngôn ngữ, cùng độ trung thành thị giác, chất lượng mã
53
+ và bằng chứng chạy được ở những giai đoạn đã chạy. Duyệt đòi mọi điểm áp dụng được từ 85 trở lên và
54
+ không còn finding lỗi nào mở. Một yêu cầu sửa trả về không phải một mã dừng: nó là fallback
55
+ `REVIEW_REVISION_REQUIRED`, và nhánh sửa đúng những artifact mà các finding của phản biện nêu tên,
56
+ theo giai đoạn chủ, rồi mở lại cuộc trao đổi cho vòng kế. Trần là `maxReviewRounds`; khi hết số vòng
57
+ ấy mà phản biện vẫn trả về yêu cầu sửa, `REVIEW_ROUNDS_EXHAUSTED` dừng hẳn, vì một đơn vị bị viết lại
58
+ cho vừa lòng người phản biện qua vô số vòng là một đơn vị do người phản biện viết. Một đơn vị tự tác
59
+ giả của nó duyệt thì chưa được phản biện, và đó là lý do lượt chạy chung, lượt thừa hưởng và lý lẽ của
60
+ người sản xuất là ba điều bị từ chối riêng biệt.
61
+
62
+ ## Ranh giới
63
+
64
+ Context chỉ đọc. Operator chỉ ghi `response/` của nhánh mình: brief, biên nhận, bản ghi máy của phần
65
+ build và phép kiểm chạy được, cùng các bản viết, hình, prompt và track dưới `response/artifacts/`;
66
+ agent phản biện chỉ ghi `review/response/`. Nó chỉ chạy các câu lệnh build và kiểm đã khai. Nó không
67
+ bao giờ viết một bài trước khi brief được đóng băng hay vượt ra ngoài những gì brief công bố, không
68
+ nhận một kết quả học tập, ví dụ hay tuyên bố thị giác mà brief không công bố, không báo cáo một lần
69
+ build hay một phép kiểm chưa chạy hoặc chưa đọc mã thoát, không đổi contract chạy được trong vòng lặp
70
+ sửa, không để người sản xuất một artifact tự thực hiện, lái hay ra đề cho phản biện của chính nó, và
71
+ không duyệt một đơn vị khi còn một điểm áp dụng được nằm dưới mức tối thiểu đã công bố. Nó không sửa
72
+ chương trình học, không publish đơn vị lên đối tượng được phục vụ, và không tuyên bố mức sẵn sàng nào
73
+ vượt quá các phép kiểm nó thực sự đã chạy.
74
+
75
+ ## Context
76
+
77
+ | Alias | Bind | Bắt buộc |
78
+ | --- | --- | --- |
79
+ | `@remote/minio/<contentId>/<locale>` | bài học như đang được phục vụ, theo fingerprint của đối tượng đã tải; đơn vị đang được viết hoặc sửa | có |
80
+ | `@worktrees/sessions/central-runtime` | runtime AI chạy, đọc và sửa mã sinh ra, theo generation | có |
81
+
82
+ ## Đầu vào
83
+
84
+ | Kind | Từ đâu | Bắt buộc |
85
+ | --- | --- | --- |
86
+ | `content-generation-receipt` | một lượt chạy trước của `content.generate` trên cùng đơn vị; lịch sử hồi quy, vắng mặt ở lượt đầu | không |
87
+
88
+ ## Yêu cầu
89
+
90
+ | Field | Kiểu | Mặc định | Hỏi |
91
+ | --- | --- | --- | --- |
92
+ | `unit` | id | — | Đơn vị nội dung đang được viết hoặc sửa |
93
+ | `naturalLanguages` | list | vi | Các ngôn ngữ đơn vị được viết bằng; mỗi ngôn ngữ phủ trọn tập kết quả |
94
+ | `implementationLanguages` | list | empty | Các ngôn ngữ mà track cài đặt được viết bằng; rỗng nghĩa là không có mã và không có phép kiểm chạy được |
95
+ | `stageModes` | list `{image}` | image off | Giai đoạn tuỳ chọn nào chạy; `image on` bật bước 5 |
96
+ | `commands` | list `{language, buildCommand, checkCommand}` | the commands the unit declares | Đúng câu lệnh build và kiểm cho mỗi track |
97
+ | `maxE2eIterations` | number 1–20 | 2 | Bước 7 được tiêu bao nhiêu vòng chạy, đọc và sửa trước khi `E2E_FAILED` |
98
+ | `maxReviewRounds` | number 1–20 | 2 | Fallback sửa lại được tiêu bao nhiêu vòng phản biện trước khi `REVIEW_ROUNDS_EXHAUSTED` |
99
+ | `resume` | token | null | Token của nhánh bị chặn khi vào lại sau một mã dừng |
100
+
101
+ ## Các bước
102
+
103
+ | # | Bước | Tham số | Đọc | Ghi | Dừng với |
104
+ | --- | --- | --- | --- | --- | --- |
105
+ | 1 | Kiểm gate và chạy lại | `resume` | `request/request.json`, đầu vào `content-generation-receipt` nếu có, @remote/minio/<contentId>/<locale> ở ràng buộc đơn vị đã đóng băng | — | `INVALID_INPUT`, `SOURCE_DRIFT`, `NO_PROGRESS` |
106
+ | 2 | Ràng đơn vị đang phục vụ và runtime | `unit` | @remote/minio/<contentId>/<locale> cho chương trình học và văn phong như đang phục vụ, @worktrees/sessions/central-runtime cho cấu hình runtime, @tools/objectstorage | — | — |
107
+ | 3 | Viết và đóng băng brief | `naturalLanguages`, `implementationLanguages` | @remote/minio/<contentId>/<locale> cho chương trình học và bằng chứng nguồn, @tools/websearch | `response/brief.md` | `BRIEF_UNBOUND` |
108
+ | 4 | Viết mọi bản đã khai | `naturalLanguages` | `response/brief.md` như brief đã đóng băng | `response/artifacts/article.<language>.md` | `OUTCOME_UNCOVERED` |
109
+ | 5 | Sinh hình theo các tuyên bố của nó, chỉ khi `stageModes` bật | `stageModes` | `response/brief.md` cho các tuyên bố và ý định hình ảnh đã nêu | `response/artifacts/image.<name>`, `response/artifacts/prompt.<name>.txt`, @tools/imagegen | `IMAGE_UNAVAILABLE` |
110
+ | 6 | Cài đặt mọi track đã khai | `implementationLanguages`, `commands` | `response/brief.md`, @worktrees/sessions/central-runtime nơi mỗi câu lệnh build chạy | `response/artifacts/track.<language>.<extension>`, @tools/shell | `CODE_BUILD_FAILED` |
111
+ | 7 | Chạy phép kiểm chạy được, tối đa `maxE2eIterations` vòng | `maxE2eIterations` | @worktrees/sessions/central-runtime nơi mỗi câu lệnh đã khai chạy | `response/data/e2e.json`, @tools/shell | `E2E_FAILED`, `CONTRACT_WEAKENED` |
112
+ | 8 | Chờ phản biện: tạm ngưng, một agent mới đọc các artifact mà không có lý lẽ của người sản xuất, sửa và mở lại khi còn vòng | `maxReviewRounds` | `review/response/review.md` khi cuộc trao đổi done | `response/response.json` (waiting, awaiting review) | `REVIEW_REVISION_REQUIRED`, `REVIEW_ROUNDS_EXHAUSTED` |
113
+ | 9 | Viết biên nhận và phát | — | mọi thứ ở trên | `response/response.md`, `response/response.json` | — |
114
+
115
+ Với mặc định, đơn vị là một bản tiếng Việt, không hình, không track và không phép kiểm chạy được, và
116
+ chất lượng của nó dựa hoàn toàn vào bước 8. Chạy lại thì bắt đầu lại từ bước 1, chỉ dùng lại quan sát
117
+ có fingerprint không đổi, và tiêu thụ đúng phần delta; một brief đã sửa tới dưới dạng một fingerprint
118
+ mới, vì cùng một fingerprint không thể cho một câu trả lời khác. Biên nhận không tuyên bố việc
119
+ publish, không tuyên bố kết quả học tập trong sản phẩm thật, và không tuyên bố mức chấp nhận nào vượt
120
+ quá các phép kiểm đã thực sự chạy.
121
+
122
+ ## Đầu ra
123
+
124
+ | Kind | File | Kiểu | Bắt buộc |
125
+ | --- | --- | --- | --- |
126
+ | `content-generation-receipt` | `response/response.md` | md | có |
127
+ | `content-brief` | `response/brief.md` | md | có |
128
+ | `e2e` | `response/data/e2e.json` | data | không |
129
+ | `content-review` | `review/response/review.md` | md | có |
130
+ | `article` | `response/artifacts/article.<language>.md` | artifact | có |
131
+ | `image` | `response/artifacts/image.<name>` | artifact | không |
132
+ | `image-prompt` | `response/artifacts/prompt.<name>.txt` | artifact | không |
133
+ | `track` | `response/artifacts/track.<language>.<extension>` | artifact | không |
134
+
135
+ ## Dừng
136
+
137
+ | Code | Xử lý |
138
+ | --- | --- |
139
+ | `INVALID_INPUT` | terminate |
140
+ | `SOURCE_DRIFT` | terminate |
141
+ | `NO_PROGRESS` | terminate |
142
+ | `BRIEF_UNBOUND` | terminate |
143
+ | `OUTCOME_UNCOVERED` | terminate |
144
+ | `IMAGE_UNAVAILABLE` | terminate |
145
+ | `CODE_BUILD_FAILED` | terminate |
146
+ | `E2E_FAILED` | terminate |
147
+ | `CONTRACT_WEAKENED` | terminate |
148
+ | `REVIEW_REVISION_REQUIRED` | fallback |
149
+ | `REVIEW_ROUNDS_EXHAUSTED` | terminate |
150
+
151
+ ## Kế tiếp
152
+
153
+ | Khi | Operator |
154
+ | --- | --- |
155
+ | đối tượng nội dung được route hoặc runtime dùng chung không trả lời ở ràng buộc đã đóng băng | `workspace.bind` |
@@ -0,0 +1,288 @@
1
+ // Proves validate.mjs on a synthetic session branch: one conforming unit under the defaults with its
2
+ // review exchange, one unit with an image and two tracks, one unit that spent the revision fallback,
3
+ // one branch blocked on a terminate code, one branch waiting for the review, and one mutation per law,
4
+ // each of which must fail with a line that names the defect.
5
+ import assert from 'node:assert/strict';
6
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
7
+ import { tmpdir } from 'node:os';
8
+ import path from 'node:path';
9
+ import { validateContentStep } from './validate.mjs';
10
+
11
+ const head = 'a'.repeat(40);
12
+ const fp = (c) => `sha256:${c.repeat(64)}`;
13
+ const UNIT = 'idempotent-writes';
14
+ const BRIEF_FP = fp('b');
15
+ const OUTCOMES = ['explain-idempotency', 'apply-request-token'];
16
+ const CLAIMS = ['retry-is-safe'];
17
+ const ARTICLE = (language) => `response/artifacts/article.${language}.md`;
18
+ const TRACK = { typescript: 'response/artifacts/track.typescript.ts', go: 'response/artifacts/track.go.go' };
19
+ const IMAGE = 'response/artifacts/image.retry.png';
20
+ const PROMPT = 'response/artifacts/prompt.retry.txt';
21
+ const COMMANDS = [
22
+ { language: 'typescript', buildCommand: 'build-typescript', checkCommand: 'check-typescript' },
23
+ { language: 'go', buildCommand: 'build-go', checkCommand: 'check-go' },
24
+ ];
25
+
26
+ const e2eJson = (overrides = {}) => ({
27
+ unitId: UNIT, contractFingerprintBefore: fp('c'), contractFingerprintAfter: fp('c'), iterations: 1, maxIterations: 2,
28
+ tracks: COMMANDS.map((c) => ({ language: c.language, sourceRef: TRACK[c.language], buildCommand: c.buildCommand, exitCode: 0 })),
29
+ runs: COMMANDS.map((c) => ({ language: c.language, command: c.checkCommand, exitCode: 0, assertions: [{ name: `${c.language}-retry-is-safe`, held: true }] })),
30
+ ...overrides,
31
+ });
32
+
33
+ const briefMd = ({ fingerprint = BRIEF_FP, outcomes = OUTCOMES, claims = CLAIMS, dispositions = [['add', '—', 'this unit adds the idempotent write lesson']] } = {}) => `# content-brief — ${UNIT}
34
+
35
+ The unit that teaches a learner why a retried write is safe.
36
+
37
+ ## Binding
38
+
39
+ | Field | Value |
40
+ | --- | --- |
41
+ | Unit | ${UNIT} |
42
+ | Objective | teach idempotent writes |
43
+ | Audience | backend learners past the transactions unit |
44
+ | Fingerprint | ${fingerprint} |
45
+
46
+ ## Learner inputs
47
+
48
+ | Input | Statement |
49
+ | --- | --- |
50
+ | transactions | the learner already knows what a transaction is |
51
+
52
+ ## Outcomes
53
+
54
+ | Outcome | Statement |
55
+ | --- | --- |
56
+ ${outcomes.map((id) => `| \`${id}\` | what the learner can do: ${id} |`).join('\n')}
57
+
58
+ ## Claims
59
+
60
+ | Claim | Statement |
61
+ | --- | --- |
62
+ ${claims.map((id) => `| \`${id}\` | the statement the visual may encode: ${id} |`).join('\n')}
63
+
64
+ ## Examples
65
+
66
+ | Example | Statement |
67
+ | --- | --- |
68
+ | retry-example | the same request token applied twice |
69
+
70
+ ## Dispositions
71
+
72
+ | Kind | Target | Statement |
73
+ | --- | --- | --- |
74
+ ${dispositions.map(([kind, target, statement]) => `| ${kind} | ${target} | ${statement} |`).join('\n')}
75
+ `;
76
+
77
+ const reviewMd = ({ received = [ARTICLE('vi')], scores = [90, 88, 86, 92, '—', '—', '—'], findings = [['write', 'improvement', 'one section could open faster', `${ARTICLE('vi')}:20`]], verdict = 'approved', inherited = 'none', rationale = 'withheld', reviewer = 'exec://review-1', round = 1 } = {}) => `# content-review — ${UNIT}
78
+
79
+ The reviewer read the produced artifacts and nothing else.
80
+
81
+ ## Execution
82
+
83
+ | Field | Value |
84
+ | --- | --- |
85
+ | Reviewer execution | ${reviewer} |
86
+ | Inherited turns | ${inherited} |
87
+ | Producer rationale | ${rationale} |
88
+ | Round | ${round} |
89
+
90
+ ## Received
91
+
92
+ | Artifact | Kind |
93
+ | --- | --- |
94
+ ${received.map((ref) => `| ${ref} | ${ref.includes('article') ? 'article' : ref.includes('image') ? 'image' : ref.includes('prompt') ? 'prompt' : 'track'} |`).join('\n')}
95
+
96
+ ## Scores
97
+
98
+ | Dimension | Score |
99
+ | --- | --- |
100
+ ${['correctness', 'pedagogy', 'interview-value', 'language', 'visual-fidelity', 'code-quality', 'e2e-proof'].map((d, i) => `| ${d} | ${scores[i]} |`).join('\n')}
101
+
102
+ ## Findings
103
+
104
+ | Owning stage | Severity | Statement | Evidence |
105
+ | --- | --- | --- | --- |
106
+ ${findings.map(([stage, severity, statement, evidence]) => `| ${stage} | ${severity} | ${statement} | ${evidence} |`).join('\n')}
107
+
108
+ ## Verdict
109
+
110
+ | Field | Value |
111
+ | --- | --- |
112
+ | Verdict | ${verdict} |
113
+ `;
114
+
115
+ const receiptMd = ({ verdict = 'approved', round = 1, editions = [['vi', ARTICLE('vi'), OUTCOMES]], approved = [ARTICLE('vi')], disabled = ['image', 'code', 'e2e'], briefFingerprint = BRIEF_FP, fallbacks = [] } = {}) => `# content-generation-receipt — ${UNIT}
116
+
117
+ The unit was built against a frozen brief and read by an independent review.
118
+
119
+ ## Binding
120
+
121
+ | Field | Value |
122
+ | --- | --- |
123
+ | Unit | ${UNIT} |
124
+ | Brief fingerprint | ${briefFingerprint} |
125
+ | Verdict | ${verdict} |
126
+ | Round | ${round} |
127
+ | Source head | ${head} |
128
+
129
+ ## Editions
130
+
131
+ | Language | Article | Outcomes covered |
132
+ | --- | --- | --- |
133
+ ${editions.map(([language, ref, covered]) => `| ${language} | ${ref} | ${covered.join(', ')} |`).join('\n')}
134
+
135
+ ## Approved artifacts
136
+
137
+ | Artifact | Stage |
138
+ | --- | --- |
139
+ ${approved.map((ref) => `| ${ref} | ${ref.includes('article') ? 'write' : ref.includes('image') || ref.includes('prompt') ? 'image' : 'code'} |`).join('\n')}
140
+
141
+ ## Findings
142
+
143
+ | Code | Stage | Ref | Statement |
144
+ | --- | --- | --- | --- |
145
+ ${disabled.map((stage) => `| \`STAGE_DISABLED\` | ${stage} | — | the ${stage} stage never ran for this unit |`).join('\n')}${disabled.length ? '\n' : ''}
146
+ ## Fallbacks taken
147
+
148
+ | Code | Action |
149
+ | --- | --- |
150
+ ${fallbacks.map(([code, action]) => `| \`${code}\` | ${action} |`).join('\n')}${fallbacks.length ? '\n' : ''}`;
151
+
152
+ const requestJson = ({ extra = {} } = {}) => ({
153
+ schemaVersion: 9, operatorId: 'content.generate', step: 1, parallel: 1, sessionId: 's-test',
154
+ contexts: [{ alias: '@remote/minio/idempotent-writes/vi', head: null }, { alias: '@worktrees/sessions/central-runtime', head: null }],
155
+ requirements: {
156
+ unit: UNIT, naturalLanguages: ['vi'], implementationLanguages: [], stageModes: { image: 'off' },
157
+ commands: [], maxE2eIterations: 2, maxReviewRounds: 2, resume: null, ...extra,
158
+ },
159
+ inputs: {}, resume: null,
160
+ });
161
+ const reviewRequest = (inputs = { article: 'step-1/parallel-1/response/artifacts/article.vi.md' }) => ({ schemaVersion: 9, operatorId: 'content.generate', step: 1, parallel: 1, sessionId: 's-test', exchange: 'review', contexts: [], requirements: {}, inputs, resume: null });
162
+ const reviewResponse = () => ({ schemaVersion: 9, operatorId: 'content.generate', step: 1, parallel: 1, exchange: 'review', status: 'done', fallbacks: [], fields: { 'content-review': 'response/review.md' }, commits: [], next: [] });
163
+ function responseJson({ status = 'done', stop, fallbacks = [], fields = null, next = [] } = {}) {
164
+ return {
165
+ schemaVersion: 9, operatorId: 'content.generate', step: 1, parallel: 1, status, ...(stop ? { stop } : {}), fallbacks,
166
+ fields: fields ?? {
167
+ 'content-generation-receipt': 'response/response.md',
168
+ 'content-brief': 'response/brief.md',
169
+ article: [ARTICLE('vi')],
170
+ },
171
+ commits: [], next,
172
+ };
173
+ }
174
+
175
+ function writeBranch(files) {
176
+ const session = mkdtempSync(path.join(tmpdir(), 'content-session-'));
177
+ const branch = path.join(session, 'step-1', 'parallel-1');
178
+ for (const d of ['request', 'response/data', 'response/artifacts', 'review/request', 'review/response']) mkdirSync(path.join(branch, d), { recursive: true });
179
+ writeFileSync(path.join(session, 'state.json'), JSON.stringify({ id: 's-test', chain: [['1/1']], steps: { '1/1': 'content.generate' }, current: '1/1', status: 'running' }));
180
+ for (const [name, content] of Object.entries(files)) {
181
+ if (content === null) continue;
182
+ writeFileSync(path.join(branch, name), typeof content === 'string' ? content : JSON.stringify(content, null, 2));
183
+ }
184
+ return { branch, session };
185
+ }
186
+ const baseline = () => ({
187
+ 'request/request.json': requestJson(),
188
+ 'response/response.json': responseJson(),
189
+ 'response/response.md': receiptMd(),
190
+ 'response/brief.md': briefMd(),
191
+ [ARTICLE('vi')]: 'the vietnamese edition',
192
+ 'review/request/request.json': reviewRequest(),
193
+ 'review/response/response.json': reviewResponse(),
194
+ 'review/response/review.md': reviewMd(),
195
+ });
196
+
197
+ async function expectValid(files, label) {
198
+ const { branch, session } = writeBranch(files);
199
+ const { errors } = await validateContentStep(branch);
200
+ rmSync(session, { recursive: true, force: true });
201
+ assert.deepEqual(errors, [], `${label} should be valid`);
202
+ }
203
+ async function expectError(files, needle, label) {
204
+ const { branch, session } = writeBranch(files);
205
+ const { errors } = await validateContentStep(branch);
206
+ rmSync(session, { recursive: true, force: true });
207
+ assert.ok(errors.some((e) => e.includes(needle)), `${label}: expected an error containing "${needle}", got:\n${errors.join('\n') || '(none)'}`);
208
+ }
209
+
210
+ const FULL_REFS = [ARTICLE('vi'), TRACK.typescript, TRACK.go, IMAGE, PROMPT];
211
+ const fullFields = () => ({
212
+ 'content-generation-receipt': 'response/response.md',
213
+ 'content-brief': 'response/brief.md',
214
+ e2e: 'response/data/e2e.json',
215
+ article: [ARTICLE('vi')],
216
+ image: [IMAGE],
217
+ 'image-prompt': [PROMPT],
218
+ track: [TRACK.typescript, TRACK.go],
219
+ });
220
+ const fullUnit = (overrides = {}) => {
221
+ const files = {
222
+ ...baseline(),
223
+ 'request/request.json': requestJson({ extra: { implementationLanguages: ['typescript', 'go'], stageModes: { image: 'on' }, commands: COMMANDS } }),
224
+ 'response/data/e2e.json': e2eJson(),
225
+ 'response/response.json': responseJson({ fields: fullFields() }),
226
+ 'response/response.md': receiptMd({ approved: [ARTICLE('vi'), IMAGE], disabled: [] }),
227
+ 'review/response/review.md': reviewMd({ received: FULL_REFS, scores: [90, 88, 86, 92, 91, 89, 90] }),
228
+ };
229
+ for (const ref of FULL_REFS) files[ref] = 'artifact';
230
+ return { ...files, ...overrides };
231
+ };
232
+ const revised = () => ({
233
+ ...baseline(),
234
+ 'response/response.json': responseJson({ fallbacks: ['REVIEW_REVISION_REQUIRED'] }),
235
+ 'response/response.md': receiptMd({ round: 2, fallbacks: [['REVIEW_REVISION_REQUIRED', 'the write stage was repaired and the review exchange was reopened']] }),
236
+ 'review/response/review.md': reviewMd({ round: 2 }),
237
+ });
238
+
239
+ await expectValid(baseline(), 'the defaults: one Vietnamese edition and its review');
240
+ await expectValid(fullUnit(), 'an image, two tracks and their executable check');
241
+ await expectValid(revised(), 'a second round after the revision fallback');
242
+ await expectValid({ ...baseline(), 'response/response.json': responseJson({ status: 'blocked', stop: 'OUTCOME_UNCOVERED', fields: {} }), 'response/response.md': null, 'response/brief.md': null, 'review/request/request.json': null, 'review/response/response.json': null, 'review/response/review.md': null }, 'blocked on an uncovered outcome');
243
+ await expectValid({ ...baseline(), 'response/response.json': { ...responseJson(), status: 'waiting', awaiting: { exchange: 'review', kind: 'content-review' }, fields: { 'content-brief': 'response/brief.md', article: [ARTICLE('vi')] } }, 'response/response.md': null, 'review/request/request.json': null, 'review/response/response.json': null, 'review/response/review.md': null }, 'waiting for the review');
244
+
245
+ await expectError({ ...baseline(), 'response/response.json': { ...responseJson(), stop: 'E2E_FAILED' } }, 'only a blocked response carries a stop', 'done with a stop');
246
+ await expectError({ ...baseline(), 'response/response.json': responseJson({ status: 'blocked', stop: 'MADE_UP_CODE', fields: {} }), 'response/response.md': null, 'response/brief.md': null, 'review/request/request.json': null, 'review/response/response.json': null, 'review/response/review.md': null }, 'not a registered code', 'unknown stop code');
247
+ await expectError({ ...baseline(), 'response/response.json': responseJson({ fallbacks: ['E2E_FAILED'] }) }, 'has disposition terminate under these requirements; it cannot be taken as a fallback', 'fallback on a terminate code');
248
+ await expectError({ ...baseline(), 'response/response.json': responseJson({ status: 'blocked', stop: 'BRIEF_UNBOUND' }) }, 'a blocked branch carries no unit', 'blocked while carrying a unit');
249
+ await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { mystery: 1 } }) }, 'requirements.mystery is not a field', 'undeclared requirement');
250
+ await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { unit: '' } }) }, 'required field unit has no value', 'missing required unit');
251
+ await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { naturalLanguages: [] } }) }, 'a unit is written in at least one natural language', 'a unit in no language');
252
+ await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { commands: COMMANDS } }) }, 'commands must cover exactly the declared implementationLanguages', 'commands for code nobody writes');
253
+ await expectError({ ...baseline(), 'request/request.json': requestJson({ extra: { implementationLanguages: ['typescript'], commands: COMMANDS } }) }, 'commands must cover exactly the declared implementationLanguages', 'a command for an undeclared language');
254
+ await expectError({ ...baseline(), 'response/response.json': responseJson({ fields: { ...responseJson().fields, article: [ARTICLE('vi'), ARTICLE('en')] } }), [ARTICLE('en')]: 'the english edition' }, 'must cover exactly the declared natural languages', 'an edition in an undeclared language');
255
+ await expectError({ ...baseline(), 'response/response.json': responseJson({ fields: { ...responseJson().fields, image: [IMAGE] } }), [IMAGE]: 'artifact' }, 'stageModes turns the image off, so no image and no prompt may be produced', 'an image from a stage that is off');
256
+ await expectError(fullUnit({ 'response/response.json': responseJson({ fields: (() => { const f = fullFields(); delete f.image; delete f['image-prompt']; return f; })() }), 'response/response.md': receiptMd({ approved: [ARTICLE('vi')], disabled: [] }), 'review/response/review.md': reviewMd({ received: [ARTICLE('vi'), TRACK.typescript, TRACK.go], scores: [90, 88, 86, 92, 91, 89, 90] }) }), 'stageModes turns the image on', 'an image stage that produced nothing');
257
+ await expectError({ ...baseline(), 'response/data/e2e.json': e2eJson(), 'response/response.json': responseJson({ fields: { ...responseJson().fields, e2e: 'response/data/e2e.json' } }) }, 'no implementation language is declared, so nothing was built and nothing ran', 'an executable record with no code');
258
+ await expectError(fullUnit({ 'response/response.json': responseJson({ fields: (() => { const f = fullFields(); delete f.e2e; return f; })() }) }), 'a unit that ships code ships the record of building and running it', 'code with no executable record');
259
+ await expectError(fullUnit({ 'response/data/e2e.json': e2eJson({ contractFingerprintAfter: fp('d') }) }), 'the executable contract changed during the repair loop', 'a contract moved during repair');
260
+ await expectError(fullUnit({ 'response/data/e2e.json': e2eJson({ iterations: 3 }) }), 'past the bound of 2', 'a repair loop past its bound');
261
+ await expectError(fullUnit({ 'response/data/e2e.json': e2eJson({ maxIterations: 4 }) }), 'maxIterations 4 differs from the request', 'an iteration bound nobody approved');
262
+ await expectError(fullUnit({ 'response/data/e2e.json': e2eJson({ tracks: e2eJson().tracks.map((t) => (t.language === 'go' ? { ...t, exitCode: 2 } : t)) }) }), 'exits 2 and cannot be shipped as working code', 'a track that does not build');
263
+ await expectError(fullUnit({ 'response/data/e2e.json': e2eJson({ runs: [e2eJson().runs[0]] }) }), 'every implementation track must be exercised by the executable check', 'a track never exercised');
264
+ await expectError(fullUnit({ 'response/data/e2e.json': e2eJson({ runs: e2eJson().runs.map((r) => (r.language === 'go' ? { ...r, exitCode: 1 } : r)) }) }), 'executable check exits 1 and proves nothing', 'a failing executable check');
265
+ await expectError(fullUnit({ 'response/data/e2e.json': e2eJson({ runs: e2eJson().runs.map((r) => (r.language === 'go' ? { ...r, assertions: [{ name: 'go-retry-is-safe', held: false }] } : r)) }) }), 'did not hold, so the check proves nothing', 'an assertion that did not hold');
266
+ await expectError(fullUnit({ 'response/data/e2e.json': e2eJson({ runs: e2eJson().runs.map((r) => (r.language === 'go' ? { ...r, command: 'go test ./...' } : r)) }) }), 'the request declared check-go', 'a check that ran another command');
267
+ await expectError({ ...baseline(), 'review/response/review.md': reviewMd({ inherited: 'producer thread' }) }, 'no inherited turns', 'a review that inherited turns');
268
+ await expectError({ ...baseline(), 'review/response/review.md': reviewMd({ rationale: 'received' }) }, "may not receive the producer's rationale", 'a review given the rationale');
269
+ await expectError({ ...baseline(), 'review/response/review.md': reviewMd({ received: [] }) }, 'the review never received', 'an artifact nobody reviewed');
270
+ await expectError({ ...baseline(), 'review/response/review.md': reviewMd({ scores: [90, 84, 86, 92, '—', '—', '—'] }) }, 'while a score sits at 84, below 85', 'approval below the minimum score');
271
+ await expectError({ ...baseline(), 'review/response/review.md': reviewMd({ findings: [['write', 'error', 'the second outcome is wrong', `${ARTICLE('vi')}:40`]] }) }, 'while an error finding remains open', 'approval with an open error finding');
272
+ await expectError({ ...baseline(), 'review/response/review.md': reviewMd({ verdict: 'revision', findings: [['write', 'improvement', 'nothing serious', `${ARTICLE('vi')}:40`]] }) }, 'a revision verdict must name at least one error finding', 'a revision with no error finding');
273
+ await expectError({ ...baseline(), 'review/response/review.md': reviewMd({ findings: [['code', 'improvement', 'the track could be shorter', 'x']] }) }, 'cannot be assigned to the code stage, which never ran', 'a finding on a stage that never ran');
274
+ await expectError({ ...baseline(), 'review/response/review.md': reviewMd({ verdict: 'revision', findings: [['write', 'error', 'the second outcome is wrong', `${ARTICLE('vi')}:40`]] }), 'response/response.md': receiptMd({ verdict: 'revision' }) }, 'while the independent review demands a revision', 'shipping against a revision verdict');
275
+ await expectError({ ...baseline(), 'review/response/review.md': reviewMd({ round: 3 }), 'response/response.md': receiptMd({ round: 3 }), 'response/response.json': responseJson({ fallbacks: ['REVIEW_REVISION_REQUIRED'] }) }, 'past the approved maxReviewRounds of 2', 'a round past the approved rounds');
276
+ await expectError({ ...revised(), 'response/response.json': responseJson() }, 'the REVIEW_REVISION_REQUIRED fallback that reopened the exchange must be recorded', 'a second round nobody recorded');
277
+ await expectError({ ...baseline(), 'response/response.json': responseJson({ fallbacks: ['REVIEW_REVISION_REQUIRED'] }), 'response/response.md': receiptMd({ fallbacks: [['REVIEW_REVISION_REQUIRED', 'the write stage was repaired']] }) }, 'so the review that answered it is at least the second round', 'a revision fallback with no second round');
278
+ await expectError({ ...baseline(), 'response/brief.md': briefMd({ dispositions: [['change', '—', 'this unit changes something unnamed']] }) }, 'a change disposition must name what it acts on', 'a disposition with no target');
279
+ await expectError({ ...baseline(), 'response/response.md': receiptMd({ briefFingerprint: fp('9') }) }, 'Brief fingerprint differs from the frozen brief', 'a receipt measured against another brief');
280
+ await expectError({ ...baseline(), 'response/response.md': receiptMd({ editions: [['vi', ARTICLE('vi'), [OUTCOMES[0]]]] }) }, 'leaves published outcome apply-request-token uncovered', 'an edition covering half the brief');
281
+ await expectError({ ...baseline(), 'response/response.md': receiptMd({ editions: [['vi', ARTICLE('vi'), [...OUTCOMES, 'invented-outcome']]] }) }, 'which the brief never published', 'an edition claiming an unpublished outcome');
282
+ await expectError({ ...baseline(), 'response/response.md': receiptMd({ disabled: ['image', 'code'] }) }, 'the e2e stage never ran and must be recorded as a STAGE_DISABLED finding', 'a stage that never ran and left no record');
283
+ await expectError({ ...baseline(), 'response/response.md': receiptMd({ approved: [] }) }, 'must name the artifacts the review approved', 'a generated unit approving nothing');
284
+ await expectError({ ...baseline(), 'response/response.md': receiptMd().replace('## Editions', '## Articles') }, 'missing section ^## Editions$', 'response section renamed');
285
+ await expectError({ ...baseline(), 'review/request/request.json': null, 'review/response/response.json': null, 'review/response/review.md': null }, 'the branch is done, but it never ran', 'done without the review exchange');
286
+ await expectError({ ...baseline(), 'response/response.json': (() => { const o = responseJson(); delete o.fields['content-brief']; return o; })() }, 'required output content-brief is not in fields', 'missing required output');
287
+
288
+ process.stdout.write('content.generate self-test: 5 valid branches, 38 rejected mutations\n');