@mnstry/atelier 0.2.0-alpha.12 → 0.2.0-alpha.13

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 (516) hide show
  1. package/CHANGELOG.md +661 -0
  2. package/README.md +142 -6
  3. package/contracts/atelier-adoption-policy.v2.schema.json +55 -0
  4. package/contracts/atelier-architecture.v1.schema.json +258 -0
  5. package/contracts/atelier-boundary-delegations.v1.schema.json +161 -0
  6. package/contracts/atelier-boundary-owner-keys.v1.schema.json +98 -0
  7. package/contracts/atelier-build.v1.schema.json +1076 -0
  8. package/contracts/atelier-capability.v1.schema.json +1252 -0
  9. package/contracts/atelier-coordination.v1.schema.json +1050 -0
  10. package/contracts/atelier-evidence-navigation.v1.schema.json +603 -0
  11. package/contracts/atelier-harness.v1.schema.json +234 -0
  12. package/contracts/atelier-ingestion.v1.schema.json +806 -0
  13. package/contracts/atelier-inquiry.v1.schema.json +1772 -0
  14. package/contracts/atelier-instruction-adoption.v1.schema.json +490 -0
  15. package/contracts/atelier-interaction.v1.schema.json +553 -0
  16. package/contracts/atelier-judgment.v1.schema.json +690 -0
  17. package/contracts/atelier-knowledge.v1.schema.json +1512 -0
  18. package/contracts/atelier-learning.v1.schema.json +1095 -0
  19. package/contracts/atelier-migration.v3.schema.json +64 -0
  20. package/contracts/atelier-personal-workspace-manifest.v1.schema.json +87 -0
  21. package/contracts/atelier-personal-workspace-overlay.v1.schema.json +299 -0
  22. package/contracts/atelier-template-adoption.v1.schema.json +223 -0
  23. package/contracts/atelier-template-profile.v1.schema.json +1168 -0
  24. package/contracts/atelier-trackable.v1.schema.json +781 -0
  25. package/contracts/atelier-upgrade-plan.v3.schema.json +532 -0
  26. package/docs/architecture.md +89 -0
  27. package/docs/assurance-controls.md +2 -2
  28. package/docs/blocks/capability-stewardship.md +17 -0
  29. package/docs/blocks/claims.md +16 -3
  30. package/docs/blocks/inquiry-harnesses.md +19 -0
  31. package/docs/blocks/learning-harnesses.md +17 -0
  32. package/docs/capability-stewardship.md +269 -0
  33. package/docs/coordination.md +51 -0
  34. package/docs/decision-practices.md +111 -0
  35. package/docs/decisions.md +29 -0
  36. package/docs/evidence-navigation.md +237 -0
  37. package/docs/exact-upgrades.md +3 -2
  38. package/docs/ingestion.md +181 -0
  39. package/docs/inquiry-harnesses.md +211 -0
  40. package/docs/install.md +80 -3
  41. package/docs/intake-and-guides.md +5 -0
  42. package/docs/integration-contract-decisions.md +24 -2
  43. package/docs/interaction-and-reflection.md +72 -0
  44. package/docs/knowledge-graph.md +20 -0
  45. package/docs/knowledge-health/api.md +30 -0
  46. package/docs/knowledge-health/assessment/api.md +96 -0
  47. package/docs/knowledge-health/assessment/capabilities.md +40 -0
  48. package/docs/knowledge-health/assessment/evaluation.md +27 -0
  49. package/docs/knowledge-health/assessment/facilitator.md +30 -0
  50. package/docs/knowledge-health/assessment/quickstart.md +61 -0
  51. package/docs/knowledge-health/assessment/troubleshooting.md +23 -0
  52. package/docs/knowledge-health/devday/facilitator.md +16 -0
  53. package/docs/knowledge-health/devday/participant-response.md +22 -0
  54. package/docs/knowledge-health/devday/public-default.md +107 -0
  55. package/docs/knowledge-health/devday/quickstart.md +18 -0
  56. package/docs/knowledge-health/devday/support.md +22 -0
  57. package/docs/knowledge-health/exercise.md +17 -0
  58. package/docs/knowledge-health/facilitator.md +20 -0
  59. package/docs/knowledge-health/quickstart.md +94 -0
  60. package/docs/knowledge-health/readme.md +15 -0
  61. package/docs/knowledge-health/support.md +34 -0
  62. package/docs/knowledge-setup.md +248 -0
  63. package/docs/knowledge-workspace.md +264 -0
  64. package/docs/learning-harnesses.md +238 -0
  65. package/docs/learning.md +215 -0
  66. package/docs/local-services.md +181 -16
  67. package/docs/obsidian-contract.md +223 -15
  68. package/docs/obsidian-plugin.md +38 -4
  69. package/docs/obsidian.md +613 -51
  70. package/docs/optional-ingestion.md +83 -0
  71. package/docs/personal-workspace.md +257 -0
  72. package/docs/practical-judgment.md +92 -0
  73. package/docs/progressive-ingestion-acceptance.md +89 -0
  74. package/docs/progressive-ingestion.md +235 -0
  75. package/docs/project-command-inventory.md +1 -0
  76. package/docs/release-engineering.md +49 -0
  77. package/docs/repo-boundary-guard.md +94 -0
  78. package/docs/research-starter-quickstart.md +66 -0
  79. package/docs/research-starter.md +114 -0
  80. package/docs/semantic-ingestion.md +198 -0
  81. package/docs/semantic-operation.md +317 -0
  82. package/docs/skill-steward.md +6 -0
  83. package/docs/template-profile.md +164 -0
  84. package/docs/template-upgrades.md +114 -0
  85. package/docs/trackables.md +111 -0
  86. package/docs/upgrade.md +47 -2
  87. package/fixtures/atelier-architecture/binding/invalid/document.json +19 -0
  88. package/fixtures/atelier-architecture/binding/valid/document.json +19 -0
  89. package/fixtures/atelier-architecture/catalog/invalid/document.json +290 -0
  90. package/fixtures/atelier-architecture/catalog/valid/document.json +343 -0
  91. package/fixtures/atelier-build-contract/attempt/invalid/document.json +20 -0
  92. package/fixtures/atelier-build-contract/attempt/valid/document.json +19 -0
  93. package/fixtures/atelier-build-contract/candidate/invalid/document.json +24 -0
  94. package/fixtures/atelier-build-contract/candidate/valid/document.json +23 -0
  95. package/fixtures/atelier-build-contract/decision/invalid/document.json +31 -0
  96. package/fixtures/atelier-build-contract/decision/valid/document.json +30 -0
  97. package/fixtures/atelier-build-contract/delivery/invalid/document.json +18 -0
  98. package/fixtures/atelier-build-contract/delivery/valid/document.json +17 -0
  99. package/fixtures/atelier-build-contract/gate/invalid/document.json +22 -0
  100. package/fixtures/atelier-build-contract/gate/valid/document.json +21 -0
  101. package/fixtures/atelier-build-contract/ledger/invalid/document.json +270 -0
  102. package/fixtures/atelier-build-contract/ledger/valid/document.json +269 -0
  103. package/fixtures/atelier-build-contract/objective/invalid/document.json +73 -0
  104. package/fixtures/atelier-build-contract/objective/valid/document.json +72 -0
  105. package/fixtures/atelier-build-contract/progress/invalid/document.json +18 -0
  106. package/fixtures/atelier-build-contract/progress/valid/document.json +17 -0
  107. package/fixtures/atelier-capability-contract/adoption/invalid/document.json +71 -0
  108. package/fixtures/atelier-capability-contract/adoption/valid/document.json +70 -0
  109. package/fixtures/atelier-capability-contract/event/invalid/document.json +18 -0
  110. package/fixtures/atelier-capability-contract/event/valid/document.json +17 -0
  111. package/fixtures/atelier-capability-contract/journal/invalid/document.json +382 -0
  112. package/fixtures/atelier-capability-contract/journal/valid/document.json +381 -0
  113. package/fixtures/atelier-capability-contract/notice/invalid/document.json +8 -0
  114. package/fixtures/atelier-capability-contract/notice/valid/document.json +7 -0
  115. package/fixtures/atelier-capability-contract/package/invalid/document.json +59 -0
  116. package/fixtures/atelier-capability-contract/package/valid/document.json +58 -0
  117. package/fixtures/atelier-capability-contract/plan/invalid/document.json +107 -0
  118. package/fixtures/atelier-capability-contract/plan/valid/document.json +106 -0
  119. package/fixtures/atelier-capability-contract/release/invalid/document.json +85 -0
  120. package/fixtures/atelier-capability-contract/release/valid/document.json +84 -0
  121. package/fixtures/atelier-capability-contract/state/invalid/document.json +316 -0
  122. package/fixtures/atelier-capability-contract/state/valid/document.json +315 -0
  123. package/fixtures/atelier-coordination/dependency/invalid/document.json +37 -0
  124. package/fixtures/atelier-coordination/dependency/valid/document.json +36 -0
  125. package/fixtures/atelier-coordination/directive/invalid/document.json +25 -0
  126. package/fixtures/atelier-coordination/directive/valid/document.json +24 -0
  127. package/fixtures/atelier-coordination/disposition/invalid/document.json +34 -0
  128. package/fixtures/atelier-coordination/disposition/valid/document.json +33 -0
  129. package/fixtures/atelier-coordination/outcome/invalid/document.json +39 -0
  130. package/fixtures/atelier-coordination/outcome/valid/document.json +38 -0
  131. package/fixtures/atelier-coordination/recovery/invalid/document.json +34 -0
  132. package/fixtures/atelier-coordination/recovery/valid/document.json +33 -0
  133. package/fixtures/atelier-harness-contract/handoff/invalid/document.json +25 -0
  134. package/fixtures/atelier-harness-contract/handoff/valid/document.json +24 -0
  135. package/fixtures/atelier-ingestion/invalid/authority.json +19 -0
  136. package/fixtures/atelier-ingestion/semantic-baseline.json +26 -0
  137. package/fixtures/atelier-ingestion/semantic-operation.json +75 -0
  138. package/fixtures/atelier-ingestion/valid/plan-input.json +18 -0
  139. package/fixtures/atelier-inquiry-contract/assessment/invalid/document.json +41 -0
  140. package/fixtures/atelier-inquiry-contract/assessment/valid/document.json +40 -0
  141. package/fixtures/atelier-inquiry-contract/bundle/invalid/document.json +51 -0
  142. package/fixtures/atelier-inquiry-contract/bundle/valid/document.json +50 -0
  143. package/fixtures/atelier-inquiry-contract/campaign/invalid/document.json +17 -0
  144. package/fixtures/atelier-inquiry-contract/campaign/valid/document.json +16 -0
  145. package/fixtures/atelier-inquiry-contract/decision/invalid/document.json +22 -0
  146. package/fixtures/atelier-inquiry-contract/decision/valid/document.json +21 -0
  147. package/fixtures/atelier-inquiry-contract/feedback/invalid/document.json +19 -0
  148. package/fixtures/atelier-inquiry-contract/feedback/valid/document.json +18 -0
  149. package/fixtures/atelier-inquiry-contract/handoff/invalid/document.json +92 -0
  150. package/fixtures/atelier-inquiry-contract/handoff/valid/document.json +91 -0
  151. package/fixtures/atelier-inquiry-contract/hypothesis/invalid/document.json +20 -0
  152. package/fixtures/atelier-inquiry-contract/hypothesis/valid/document.json +19 -0
  153. package/fixtures/atelier-inquiry-contract/ledger/invalid/document.json +680 -0
  154. package/fixtures/atelier-inquiry-contract/ledger/valid/document.json +679 -0
  155. package/fixtures/atelier-inquiry-contract/legacy-assessment/invalid/document.json +19 -0
  156. package/fixtures/atelier-inquiry-contract/legacy-assessment/valid/document.json +18 -0
  157. package/fixtures/atelier-inquiry-contract/request/invalid/document.json +50 -0
  158. package/fixtures/atelier-inquiry-contract/request/valid/document.json +49 -0
  159. package/fixtures/atelier-inquiry-contract/source/invalid/document.json +18 -0
  160. package/fixtures/atelier-inquiry-contract/source/valid/document.json +17 -0
  161. package/fixtures/atelier-inquiry-contract/withdrawal/invalid/document.json +16 -0
  162. package/fixtures/atelier-inquiry-contract/withdrawal/valid/document.json +15 -0
  163. package/fixtures/atelier-instruction-adoption/context-receipt/invalid/document.json +18 -0
  164. package/fixtures/atelier-instruction-adoption/context-receipt/valid/document.json +17 -0
  165. package/fixtures/atelier-instruction-adoption/plan/invalid/document.json +63 -0
  166. package/fixtures/atelier-instruction-adoption/plan/valid/document.json +62 -0
  167. package/fixtures/atelier-instruction-adoption/receipt/invalid/document.json +22 -0
  168. package/fixtures/atelier-instruction-adoption/receipt/valid/document.json +21 -0
  169. package/fixtures/atelier-instruction-adoption/state/invalid/document.json +30 -0
  170. package/fixtures/atelier-instruction-adoption/state/valid/document.json +29 -0
  171. package/fixtures/atelier-interaction/act/invalid/document.json +25 -0
  172. package/fixtures/atelier-interaction/act/valid/document.json +24 -0
  173. package/fixtures/atelier-interaction/assessment/invalid/document.json +32 -0
  174. package/fixtures/atelier-interaction/assessment/valid/document.json +31 -0
  175. package/fixtures/atelier-interaction/observation/invalid/document.json +22 -0
  176. package/fixtures/atelier-interaction/observation/valid/document.json +21 -0
  177. package/fixtures/atelier-interaction/policy/invalid/document.json +20 -0
  178. package/fixtures/atelier-interaction/policy/valid/document.json +19 -0
  179. package/fixtures/atelier-judgment/case/invalid/document.json +76 -0
  180. package/fixtures/atelier-judgment/case/valid/document.json +75 -0
  181. package/fixtures/atelier-judgment/decision/invalid/document.json +38 -0
  182. package/fixtures/atelier-judgment/decision/valid/document.json +37 -0
  183. package/fixtures/atelier-judgment/native-decision.md +3 -0
  184. package/fixtures/atelier-judgment/practice-reconsideration/definition.json +92 -0
  185. package/fixtures/atelier-judgment/practice-reconsideration/scenario.json +435 -0
  186. package/fixtures/atelier-knowledge-contract/activation/invalid/document.json +26 -0
  187. package/fixtures/atelier-knowledge-contract/activation/valid/document.json +25 -0
  188. package/fixtures/atelier-knowledge-contract/contribution/invalid/document.json +34 -0
  189. package/fixtures/atelier-knowledge-contract/contribution/valid/document.json +33 -0
  190. package/fixtures/atelier-knowledge-contract/domain/invalid/document.json +41 -0
  191. package/fixtures/atelier-knowledge-contract/domain/valid/document.json +40 -0
  192. package/fixtures/atelier-knowledge-contract/domain-revision/invalid/document.json +46 -0
  193. package/fixtures/atelier-knowledge-contract/domain-revision/valid/document.json +45 -0
  194. package/fixtures/atelier-knowledge-contract/evaluation/invalid/document.json +21 -0
  195. package/fixtures/atelier-knowledge-contract/evaluation/valid/document.json +20 -0
  196. package/fixtures/atelier-knowledge-contract/ledger/invalid/document.json +318 -0
  197. package/fixtures/atelier-knowledge-contract/ledger/valid/document.json +317 -0
  198. package/fixtures/atelier-knowledge-contract/relation/invalid/document.json +25 -0
  199. package/fixtures/atelier-knowledge-contract/relation/valid/document.json +24 -0
  200. package/fixtures/atelier-knowledge-contract/review/invalid/document.json +18 -0
  201. package/fixtures/atelier-knowledge-contract/review/valid/document.json +17 -0
  202. package/fixtures/atelier-knowledge-contract/withdrawal/invalid/document.json +16 -0
  203. package/fixtures/atelier-knowledge-contract/withdrawal/valid/document.json +15 -0
  204. package/fixtures/atelier-learning/invalid/authority.json +14 -0
  205. package/fixtures/atelier-learning/valid/capture.json +13 -0
  206. package/fixtures/atelier-personal-workspace-contract/manifest/invalid/authority-key.json +17 -0
  207. package/fixtures/atelier-personal-workspace-contract/manifest/invalid/nonempty-ext.json +25 -0
  208. package/fixtures/atelier-personal-workspace-contract/manifest/invalid/wrong-schema.json +22 -0
  209. package/fixtures/atelier-personal-workspace-contract/manifest/valid/manifest.json +22 -0
  210. package/fixtures/atelier-personal-workspace-contract/overlay/invalid/authority-key.json +25 -0
  211. package/fixtures/atelier-personal-workspace-contract/overlay/invalid/unknown-top-level-field.json +27 -0
  212. package/fixtures/atelier-personal-workspace-contract/overlay/invalid/wrong-schema.json +24 -0
  213. package/fixtures/atelier-trackable/definition/invalid/document.json +15 -0
  214. package/fixtures/atelier-trackable/definition/valid/document.json +14 -0
  215. package/fixtures/atelier-trackable/request/invalid/document.json +23 -0
  216. package/fixtures/atelier-trackable/request/valid/document.json +22 -0
  217. package/fixtures/boundary-delegations/invalid/authority-key.v1.json +30 -0
  218. package/fixtures/boundary-delegations/invalid/push-operation.v1.json +28 -0
  219. package/fixtures/boundary-delegations/invalid/unsigned.v1.json +25 -0
  220. package/fixtures/boundary-delegations/valid/empty.v1.json +4 -0
  221. package/fixtures/boundary-delegations/valid/signed-operator.v1.json +30 -0
  222. package/fixtures/boundary-owner-keys/invalid/wrong-algorithm.v1.json +15 -0
  223. package/fixtures/boundary-owner-keys/invalid/wrong-curve.v1.json +15 -0
  224. package/fixtures/boundary-owner-keys/valid/one-owner.v1.json +17 -0
  225. package/fixtures/capability-packages/adoption.example.json +70 -0
  226. package/fixtures/capability-packages/evidence-review/capability-package.json +58 -0
  227. package/fixtures/capability-packages/evidence-review/capability-release.json +84 -0
  228. package/fixtures/capability-packages/evidence-review/evaluation.md +15 -0
  229. package/fixtures/capability-packages/evidence-review/skills/evidence-review/SKILL.md +22 -0
  230. package/fixtures/capability-packages/evidence-review/skills/evidence-review/references/ledger.md +7 -0
  231. package/fixtures/capability-packages/research/capability-package.json +88 -0
  232. package/fixtures/capability-packages/research/capability-release.json +134 -0
  233. package/fixtures/capability-packages/research/evaluation.md +15 -0
  234. package/fixtures/capability-packages/research/skills/research-integrate/SKILL.md +29 -0
  235. package/fixtures/capability-packages/research/skills/research-integrate/references/integration.md +10 -0
  236. package/fixtures/capability-packages/research/skills/research-plan/SKILL.md +27 -0
  237. package/fixtures/capability-packages/research/skills/research-plan/references/inquiries.md +26 -0
  238. package/fixtures/capability-packages/research/skills/research-synthesize/SKILL.md +23 -0
  239. package/fixtures/capability-packages/research/skills/research-synthesize/references/synthesis.md +12 -0
  240. package/fixtures/evidence-navigation/invalid/duplicate-operation.json +16 -0
  241. package/fixtures/evidence-navigation/invalid/instruction-kind.json +36 -0
  242. package/fixtures/evidence-navigation/invalid/invalid-digest.json +36 -0
  243. package/fixtures/evidence-navigation/invalid/invalid-time.json +36 -0
  244. package/fixtures/evidence-navigation/invalid/missing-attribution.json +35 -0
  245. package/fixtures/evidence-navigation/invalid/reserved-extension.json +19 -0
  246. package/fixtures/evidence-navigation/invalid/unattributed-acceptance.json +36 -0
  247. package/fixtures/evidence-navigation/invalid/uncoerced-version.json +16 -0
  248. package/fixtures/evidence-navigation/invalid/unknown-currency.json +17 -0
  249. package/fixtures/evidence-navigation/invalid/unknown-selector-field.json +37 -0
  250. package/fixtures/evidence-navigation/invalid/unsupported-operation.json +15 -0
  251. package/fixtures/evidence-navigation/valid/claim.json +36 -0
  252. package/fixtures/evidence-navigation/valid/host.json +16 -0
  253. package/fixtures/evidence-navigation/valid/profile.json +16 -0
  254. package/fixtures/evidence-navigation/valid/snapshot.json +17 -0
  255. package/fixtures/harness-packages/build-harness/capability-package.json +61 -0
  256. package/fixtures/harness-packages/build-harness/capability-release.json +82 -0
  257. package/fixtures/harness-packages/build-harness/evaluation.md +3 -0
  258. package/fixtures/harness-packages/build-harness/skills/atelier-build-harness/SKILL.md +59 -0
  259. package/fixtures/harness-packages/knowledge-harness/capability-package.json +61 -0
  260. package/fixtures/harness-packages/knowledge-harness/capability-release.json +82 -0
  261. package/fixtures/harness-packages/knowledge-harness/evaluation.md +3 -0
  262. package/fixtures/harness-packages/knowledge-harness/skills/atelier-knowledge-harness/SKILL.md +60 -0
  263. package/fixtures/harnesses/learning-cycle.json +780 -0
  264. package/fixtures/inquiry/workshop.json +675 -0
  265. package/fixtures/inquiry-packages/discovery-harness/capability-package.json +61 -0
  266. package/fixtures/inquiry-packages/discovery-harness/capability-release.json +82 -0
  267. package/fixtures/inquiry-packages/discovery-harness/evaluation.md +3 -0
  268. package/fixtures/inquiry-packages/discovery-harness/skills/atelier-discovery-harness/SKILL.md +48 -0
  269. package/fixtures/inquiry-packages/research-harness/capability-package.json +61 -0
  270. package/fixtures/inquiry-packages/research-harness/capability-release.json +82 -0
  271. package/fixtures/inquiry-packages/research-harness/evaluation.md +3 -0
  272. package/fixtures/inquiry-packages/research-harness/skills/atelier-research-harness/SKILL.md +43 -0
  273. package/fixtures/knowledge-health/calibration-suite.json +26 -0
  274. package/fixtures/knowledge-health/calibration-trial.json +37 -0
  275. package/fixtures/knowledge-health/corrected/checklist.md +18 -0
  276. package/fixtures/knowledge-health/devday/participant-record.json +19 -0
  277. package/fixtures/knowledge-health/expected-summary.json +27 -0
  278. package/fixtures/knowledge-health/workshop/README.md +20 -0
  279. package/fixtures/knowledge-health/workshop/atelier.project.json +7 -0
  280. package/fixtures/knowledge-health/workshop/knowledge-plan.json +65 -0
  281. package/fixtures/knowledge-health/workshop/records/checklist.md +18 -0
  282. package/fixtures/knowledge-health/workshop/records/workshop.md +18 -0
  283. package/fixtures/knowledge-health/workshop/repo-access.v1.json +5 -0
  284. package/fixtures/knowledge-health/workspace/atelier.project.json +18 -0
  285. package/fixtures/knowledge-health/workspace/knowledge-plan.json +74 -0
  286. package/fixtures/knowledge-health/workspace/records/checklist.md +18 -0
  287. package/fixtures/knowledge-health/workspace/records/pilot.md +16 -0
  288. package/fixtures/knowledge-health/workspace/repo-access.v1.json +9 -0
  289. package/fixtures/personal-workspace/overlay.json +24 -0
  290. package/fixtures/templates/local-library.v1.json +75 -0
  291. package/fixtures/templates/upgrade-contracts/adoption-policy.v2/invalid.json +16 -0
  292. package/fixtures/templates/upgrade-contracts/adoption-policy.v2/valid.json +13 -0
  293. package/fixtures/templates/upgrade-contracts/migration.v3/invalid.json +24 -0
  294. package/fixtures/templates/upgrade-contracts/migration.v3/valid.json +21 -0
  295. package/fixtures/templates/upgrade-contracts/template-adoption.v1/invalid.json +60 -0
  296. package/fixtures/templates/upgrade-contracts/template-adoption.v1/valid.json +57 -0
  297. package/fixtures/templates/upgrade-contracts/upgrade-plan.v3/invalid.json +85 -0
  298. package/fixtures/templates/upgrade-contracts/upgrade-plan.v3/valid.json +82 -0
  299. package/package.json +59 -5
  300. package/skills/claude/atelier-build-harness/SKILL.md +59 -0
  301. package/skills/claude/atelier-coordination/SKILL.md +32 -0
  302. package/skills/claude/atelier-discovery-harness/SKILL.md +48 -0
  303. package/skills/claude/atelier-guided-upgrade/SKILL.md +29 -0
  304. package/skills/claude/atelier-interaction/SKILL.md +34 -0
  305. package/skills/claude/atelier-knowledge-harness/SKILL.md +60 -0
  306. package/skills/claude/atelier-knowledge-setup/SKILL.md +72 -0
  307. package/skills/claude/atelier-practical-judgment/SKILL.md +64 -0
  308. package/skills/claude/atelier-research-harness/SKILL.md +43 -0
  309. package/skills/claude/atelier-skill-steward/SKILL.md +53 -0
  310. package/skills/claude/atelier-trackables/SKILL.md +40 -0
  311. package/skills/codex/atelier-build-harness/SKILL.md +59 -0
  312. package/skills/codex/atelier-coordination/SKILL.md +32 -0
  313. package/skills/codex/atelier-discovery-harness/SKILL.md +48 -0
  314. package/skills/codex/atelier-guided-upgrade/SKILL.md +29 -0
  315. package/skills/codex/atelier-interaction/SKILL.md +34 -0
  316. package/skills/codex/atelier-knowledge-harness/SKILL.md +60 -0
  317. package/skills/codex/atelier-knowledge-setup/SKILL.md +72 -0
  318. package/skills/codex/atelier-practical-judgment/SKILL.md +64 -0
  319. package/skills/codex/atelier-research-harness/SKILL.md +43 -0
  320. package/skills/codex/atelier-skill-steward/SKILL.md +53 -0
  321. package/skills/codex/atelier-trackables/SKILL.md +40 -0
  322. package/src/architecture/catalog.mjs +355 -0
  323. package/src/architecture/index.mjs +93 -0
  324. package/src/boundary/content-rules.mjs +4 -2
  325. package/src/boundary/delegation-command.mjs +158 -0
  326. package/src/boundary/delegation.mjs +396 -0
  327. package/src/boundary/policy.mjs +52 -10
  328. package/src/build/git.mjs +18 -0
  329. package/src/build/index.mjs +3 -0
  330. package/src/build/ledger.mjs +88 -0
  331. package/src/capabilities/adoption.mjs +381 -0
  332. package/src/capabilities/evidence.mjs +140 -0
  333. package/src/capabilities/files.mjs +124 -0
  334. package/src/capabilities/index.mjs +4 -0
  335. package/src/capabilities/instructions.mjs +250 -0
  336. package/src/capabilities/package.mjs +136 -0
  337. package/src/cli/command-failure.mjs +36 -0
  338. package/src/cli/execute-command.mjs +16 -2
  339. package/src/cli/run.mjs +65 -9
  340. package/src/coauthor/store.mjs +43 -2
  341. package/src/collaboration/proposals.mjs +189 -0
  342. package/src/commands/architecture.mjs +27 -0
  343. package/src/commands/boundary.mjs +3 -0
  344. package/src/commands/capability.mjs +73 -0
  345. package/src/commands/enroll.mjs +3 -0
  346. package/src/commands/harness.mjs +79 -0
  347. package/src/commands/ingest.mjs +39 -0
  348. package/src/commands/init.mjs +4 -1
  349. package/src/commands/inquiry.mjs +66 -0
  350. package/src/commands/knowledge-semantic.mjs +128 -0
  351. package/src/commands/knowledge.mjs +119 -0
  352. package/src/commands/learn.mjs +61 -0
  353. package/src/commands/obsidian.mjs +754 -63
  354. package/src/commands/practice.mjs +24 -0
  355. package/src/commands/setup.mjs +22 -2
  356. package/src/commands/trackable.mjs +19 -0
  357. package/src/contracts/corpus.mjs +30 -0
  358. package/src/coordination/index.mjs +110 -0
  359. package/src/evidence-navigation/contracts.mjs +60 -0
  360. package/src/evidence-navigation/evaluate.mjs +69 -0
  361. package/src/evidence-navigation/generation.mjs +22 -0
  362. package/src/evidence-navigation/index.mjs +2 -0
  363. package/src/evidence-navigation/local.mjs +167 -0
  364. package/src/evidence-navigation/schema.generated.mjs +604 -0
  365. package/src/evidence-navigation/types.mjs +12 -0
  366. package/src/export/atelier-export-contract.mjs +35 -8
  367. package/src/graph/enroll.mjs +248 -0
  368. package/src/graph/graph.mjs +17 -1
  369. package/src/graph/knowledge-graph.mjs +58 -8
  370. package/src/harnesses/contracts.mjs +41 -0
  371. package/src/harnesses/exchange.mjs +154 -0
  372. package/src/harnesses/feedback.mjs +16 -0
  373. package/src/harnesses/history.mjs +37 -0
  374. package/src/harnesses/index.mjs +4 -0
  375. package/src/harnesses/store.mjs +29 -0
  376. package/src/ingestion/contracts.mjs +41 -0
  377. package/src/ingestion/evaluation.mjs +119 -0
  378. package/src/ingestion/optional/composition/index.mjs +171 -0
  379. package/src/ingestion/optional/evaluation/index.mjs +219 -0
  380. package/src/ingestion/optional/graphify/composition-binding.mjs +60 -0
  381. package/src/ingestion/optional/graphify/composition_receiving.py +185 -0
  382. package/src/ingestion/optional/graphify/graphify_supplier.py +933 -0
  383. package/src/ingestion/optional/graphify/json_bridge.py +163 -0
  384. package/src/ingestion/optional/graphify/query_receiving.py +209 -0
  385. package/src/ingestion/optional/index.mjs +9 -0
  386. package/src/ingestion/optional/jev/bridge.mjs +166 -0
  387. package/src/ingestion/optional/jev/composition-binding.mjs +116 -0
  388. package/src/ingestion/optional/jev/judgments.mjs +174 -0
  389. package/src/ingestion/optional/jev/native-translation.mjs +32 -0
  390. package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/__init__.py +7 -0
  391. package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/adapter.py +966 -0
  392. package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/artifacts.py +101 -0
  393. package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/projection.py +146 -0
  394. package/src/ingestion/optional/lightrag/composition-binding.mjs +67 -0
  395. package/src/ingestion/optional/vanilla/composition-binding.mjs +102 -0
  396. package/src/ingestion/optional/vanilla/index.mjs +218 -0
  397. package/src/ingestion/optional/vanilla/native-normalization.mjs +187 -0
  398. package/src/ingestion/processors.mjs +218 -0
  399. package/src/ingestion/semantic.mjs +203 -0
  400. package/src/ingestion/store.mjs +372 -0
  401. package/src/inquiry/beliefs.mjs +32 -0
  402. package/src/inquiry/contracts.mjs +23 -0
  403. package/src/inquiry/index.mjs +5 -0
  404. package/src/inquiry/ledger.mjs +135 -0
  405. package/src/inquiry/lenses.mjs +15 -0
  406. package/src/inquiry/projection.mjs +77 -0
  407. package/src/inquiry/store.mjs +35 -0
  408. package/src/intake/read-scope.mjs +12 -0
  409. package/src/intake/store.mjs +69 -6
  410. package/src/interaction/index.mjs +91 -0
  411. package/src/judgment/index.mjs +82 -0
  412. package/src/judgment/practice-evaluation.mjs +70 -0
  413. package/src/judgment/practice.mjs +144 -0
  414. package/src/knowledge/context.mjs +217 -0
  415. package/src/knowledge/index.mjs +5 -0
  416. package/src/knowledge/ingestion.mjs +79 -0
  417. package/src/knowledge/intake.mjs +33 -0
  418. package/src/knowledge/ledger.mjs +110 -0
  419. package/src/knowledge/participatory/guidance.mjs +36 -0
  420. package/src/knowledge/participatory/guided-contribution.mjs +44 -0
  421. package/src/knowledge/participatory/index.mjs +2 -0
  422. package/src/knowledge/participatory/installed-workshop.mjs +108 -0
  423. package/src/knowledge/plan.mjs +67 -0
  424. package/src/knowledge/projection.mjs +43 -0
  425. package/src/knowledge/semantic-operation.mjs +567 -0
  426. package/src/knowledge/sessions.mjs +467 -0
  427. package/src/knowledge/workspace.mjs +295 -0
  428. package/src/knowledge-health/assessment.mjs +165 -0
  429. package/src/knowledge-health/capabilities.mjs +350 -0
  430. package/src/knowledge-health/context.mjs +62 -0
  431. package/src/knowledge-health/example.mjs +47 -0
  432. package/src/knowledge-health/index.mjs +10 -0
  433. package/src/knowledge-health/owner-join.mjs +88 -0
  434. package/src/knowledge-health/proof/common.mjs +24 -0
  435. package/src/knowledge-health/proof/installed-check.mjs +100 -0
  436. package/src/knowledge-health/proof/native-readback.mjs +37 -0
  437. package/src/knowledge-health/proof/rehearse.mjs +68 -0
  438. package/src/knowledge-health/proof/validate-participant.mjs +17 -0
  439. package/src/knowledge-health/proof/verify-native-capture.mjs +14 -0
  440. package/src/knowledge-health/proof/workshop-check.mjs +36 -0
  441. package/src/knowledge-health/rule-identity.mjs +44 -0
  442. package/src/knowledge-health/workshop-composition.mjs +19 -0
  443. package/src/knowledge-health/workshop-profile.mjs +176 -0
  444. package/src/knowledge-health/workshop.mjs +99 -0
  445. package/src/learning/adapters.mjs +68 -0
  446. package/src/learning/assistance.mjs +127 -0
  447. package/src/learning/contracts.mjs +65 -0
  448. package/src/learning/store.mjs +272 -0
  449. package/src/personal-workspace/index.mjs +391 -0
  450. package/src/personal-workspace/selection.mjs +385 -0
  451. package/src/project/config.mjs +8 -0
  452. package/src/project/durable-state.mjs +3 -1
  453. package/src/project/file-class.mjs +1 -1
  454. package/src/projection/obsidian/edits/apply.mjs +55 -16
  455. package/src/projection/obsidian/personal-workspace.mjs +436 -0
  456. package/src/projection/obsidian/proposals/adapter.mjs +47 -8
  457. package/src/projection/obsidian/proposals/observation.mjs +10 -2
  458. package/src/projection/obsidian/publication/publisher.mjs +142 -13
  459. package/src/projection/obsidian/publication/vault-list.mjs +4 -3
  460. package/src/projection/obsidian/recovery/index.mjs +4 -1
  461. package/src/projection/obsidian/recovery/store.mjs +160 -7
  462. package/src/projection/obsidian/selection-ui/contribution.mjs +6 -5
  463. package/src/readiness/readiness.mjs +10 -3
  464. package/src/reflection/index.mjs +56 -0
  465. package/src/runtime/obsidian/app-production-seams.mjs +48 -16
  466. package/src/runtime/obsidian/app-registration.mjs +175 -28
  467. package/src/runtime/obsidian/app-restart.mjs +154 -0
  468. package/src/runtime/obsidian/engine.mjs +163 -20
  469. package/src/runtime/obsidian/extension-points.mjs +1 -1
  470. package/src/runtime/obsidian/index.mjs +10 -7
  471. package/src/runtime/obsidian/launch-plan.mjs +7 -4
  472. package/src/runtime/obsidian/lifecycle.mjs +92 -12
  473. package/src/runtime/obsidian/login-item.mjs +332 -0
  474. package/src/runtime/obsidian/machine-settings.mjs +119 -12
  475. package/src/runtime/obsidian/opening.mjs +171 -37
  476. package/src/runtime/obsidian/pipeline.mjs +71 -3
  477. package/src/runtime/obsidian/plugin-channel.mjs +11 -3
  478. package/src/runtime/obsidian/plugin-choice.mjs +16 -4
  479. package/src/runtime/obsidian/plugin-drift.mjs +59 -11
  480. package/src/runtime/obsidian/plugin-presence.mjs +14 -1
  481. package/src/runtime/obsidian/process-identity.mjs +20 -10
  482. package/src/runtime/obsidian/project-views.mjs +282 -0
  483. package/src/runtime/obsidian/questions.mjs +48 -0
  484. package/src/runtime/obsidian/release-watch.mjs +56 -0
  485. package/src/runtime/obsidian/service-main.mjs +99 -16
  486. package/src/runtime/obsidian/service-manager-production.mjs +61 -0
  487. package/src/runtime/obsidian/service-managers.mjs +275 -0
  488. package/src/runtime/obsidian/service-record.mjs +87 -23
  489. package/src/runtime/obsidian/service.mjs +36 -3
  490. package/src/runtime/obsidian/startup-adapters.mjs +59 -21
  491. package/src/runtime/obsidian/unheld-evidence.mjs +84 -0
  492. package/src/runtime/obsidian/vault-location.mjs +300 -0
  493. package/src/runtime/obsidian/view-counts.mjs +34 -0
  494. package/src/server/local-sidecar.mjs +169 -2
  495. package/src/server/server.mjs +28 -2
  496. package/src/template-bindings/project.mjs +92 -0
  497. package/src/templates/profile.mjs +225 -0
  498. package/src/trackables/domain.mjs +175 -0
  499. package/src/trackables/store.mjs +114 -0
  500. package/src/ui/knowledge-page.mjs +1388 -0
  501. package/src/upgrade/explanation.mjs +1 -1
  502. package/src/upgrade/template-participant.mjs +171 -0
  503. package/src/upgrade/transaction.mjs +72 -25
  504. package/src/upgrade/upgrade.mjs +1 -0
  505. package/templates/external-project-workspace/pack-lifecycle.json +6 -1
  506. package/templates/knowledge-workspace/.gitattributes +2 -0
  507. package/templates/knowledge-workspace/AGENTS.md +27 -0
  508. package/templates/knowledge-workspace/README.md +44 -0
  509. package/templates/knowledge-workspace/atelier.project.json +22 -0
  510. package/templates/knowledge-workspace/gitignore +6 -0
  511. package/templates/knowledge-workspace/knowledge-plan.json +111 -0
  512. package/templates/knowledge-workspace/knowledge-plan.schema.json +281 -0
  513. package/templates/knowledge-workspace/records/forecast.md +22 -0
  514. package/templates/knowledge-workspace/records/inspection.md +25 -0
  515. package/templates/knowledge-workspace/records/telescope.md +24 -0
  516. package/templates/knowledge-workspace/repo-access.v1.json +9 -0
@@ -0,0 +1,1388 @@
1
+ export function renderKnowledgePage() {
2
+ return `<!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
7
+ <title>Knowledge workspace · Atelier</title>
8
+ <style>
9
+ :root {
10
+ color-scheme: light;
11
+ --ink: #202d37;
12
+ --muted: #52616c;
13
+ --line: #cad2d5;
14
+ --paper: #f6f5f0;
15
+ --accent: #215c52;
16
+ --wash: #e8efeb;
17
+ }
18
+ * {
19
+ box-sizing: border-box;
20
+ }
21
+ body {
22
+ margin: 0;
23
+ background: var(--paper);
24
+ color: var(--ink);
25
+ font: 16px/1.55 system-ui, sans-serif;
26
+ }
27
+ header {
28
+ padding: 24px 4vw;
29
+ border-bottom: 1px solid var(--line);
30
+ display: flex;
31
+ gap: 20px;
32
+ align-items: center;
33
+ justify-content: space-between;
34
+ }
35
+ .eyebrow {
36
+ text-transform: uppercase;
37
+ font-size: 0.75rem;
38
+ letter-spacing: 0.13em;
39
+ font-weight: 750;
40
+ color: var(--muted);
41
+ }
42
+ h1 {
43
+ font-size: 1.5rem;
44
+ margin: 4px 0;
45
+ }
46
+ h2 {
47
+ font-size: 1.7rem;
48
+ line-height: 1.2;
49
+ margin: 0 0 16px;
50
+ }
51
+ h3 {
52
+ font-size: 1.1rem;
53
+ margin: 0 0 10px;
54
+ }
55
+ p {
56
+ margin: 8px 0 16px;
57
+ }
58
+ .muted,
59
+ small {
60
+ color: var(--muted);
61
+ }
62
+ small {
63
+ font-size: 0.85rem;
64
+ }
65
+ .shell {
66
+ max-width: 1500px;
67
+ margin: auto;
68
+ padding: 28px 4vw;
69
+ }
70
+ .steps {
71
+ display: flex;
72
+ gap: 8px;
73
+ flex-wrap: wrap;
74
+ margin-bottom: 24px;
75
+ }
76
+ .steps button {
77
+ flex: 1;
78
+ min-width: 112px;
79
+ text-align: left;
80
+ background: transparent;
81
+ }
82
+ .steps button[aria-pressed='true'] {
83
+ background: var(--ink);
84
+ color: white;
85
+ border-color: var(--ink);
86
+ }
87
+ .steps span {
88
+ display: block;
89
+ font-size: 0.75rem;
90
+ opacity: 0.8;
91
+ }
92
+ .layout {
93
+ display: grid;
94
+ grid-template-columns: minmax(0, 1fr) minmax(300px, 390px);
95
+ gap: 32px;
96
+ align-items: start;
97
+ }
98
+ .card {
99
+ padding: 22px;
100
+ background: white;
101
+ border: 1px solid var(--line);
102
+ border-radius: 10px;
103
+ margin-bottom: 16px;
104
+ overflow-wrap: anywhere;
105
+ }
106
+ .grid {
107
+ display: grid;
108
+ grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
109
+ gap: 14px;
110
+ }
111
+ .grid .card {
112
+ margin: 0;
113
+ }
114
+ .callout {
115
+ border-left: 4px solid var(--accent);
116
+ background: var(--wash);
117
+ }
118
+ .tag {
119
+ display: inline-block;
120
+ padding: 3px 8px;
121
+ border-radius: 4px;
122
+ background: var(--wash);
123
+ font-size: 0.8rem;
124
+ color: var(--accent);
125
+ }
126
+ button,
127
+ input,
128
+ select,
129
+ textarea {
130
+ font: inherit;
131
+ color: inherit;
132
+ min-height: 44px;
133
+ max-width: 100%;
134
+ border: 1px solid #819298;
135
+ border-radius: 5px;
136
+ padding: 9px 12px;
137
+ background: white;
138
+ }
139
+ button {
140
+ cursor: pointer;
141
+ font-weight: 650;
142
+ }
143
+ button.primary {
144
+ background: var(--accent);
145
+ color: white;
146
+ border-color: var(--accent);
147
+ }
148
+ button:disabled {
149
+ opacity: 0.55;
150
+ cursor: not-allowed;
151
+ }
152
+ button:hover:not(:disabled) {
153
+ filter: brightness(0.94);
154
+ }
155
+ button:focus-visible,
156
+ input:focus-visible,
157
+ select:focus-visible,
158
+ textarea:focus-visible,
159
+ summary:focus-visible,
160
+ a:focus-visible {
161
+ outline: 3px solid #477dbe;
162
+ outline-offset: 3px;
163
+ }
164
+ label {
165
+ display: block;
166
+ font-size: 0.9rem;
167
+ font-weight: 650;
168
+ margin: 16px 0 6px;
169
+ }
170
+ input,
171
+ select,
172
+ textarea {
173
+ width: 100%;
174
+ }
175
+ textarea {
176
+ min-height: 155px;
177
+ resize: vertical;
178
+ font-weight: 400;
179
+ }
180
+ fieldset {
181
+ border: 0;
182
+ margin: 0;
183
+ padding: 0;
184
+ min-width: 0;
185
+ }
186
+ fieldset:disabled {
187
+ opacity: 0.75;
188
+ }
189
+ .actions {
190
+ display: flex;
191
+ gap: 8px;
192
+ flex-wrap: wrap;
193
+ margin: 14px 0;
194
+ }
195
+ .notice {
196
+ padding: 12px 16px;
197
+ background: var(--wash);
198
+ border-radius: 6px;
199
+ overflow-wrap: anywhere;
200
+ }
201
+ .error {
202
+ background: #ffeadb;
203
+ color: #6f3114;
204
+ }
205
+ #status {
206
+ margin: 0 0 20px;
207
+ }
208
+ #status:empty {
209
+ display: none;
210
+ }
211
+ #session-status {
212
+ margin: 8px 0;
213
+ }
214
+ pre {
215
+ white-space: pre-wrap;
216
+ overflow-wrap: anywhere;
217
+ font: 0.85rem/1.6 ui-monospace, monospace;
218
+ background: #f1f3f3;
219
+ padding: 14px;
220
+ border-radius: 5px;
221
+ }
222
+ details {
223
+ margin: 10px 0;
224
+ }
225
+ summary {
226
+ cursor: pointer;
227
+ min-height: 44px;
228
+ padding: 10px 0;
229
+ font-weight: 650;
230
+ }
231
+ .scroll {
232
+ overflow: auto;
233
+ }
234
+ table {
235
+ border-collapse: collapse;
236
+ width: 100%;
237
+ font-size: 0.9rem;
238
+ }
239
+ th,
240
+ td {
241
+ padding: 12px 10px;
242
+ border-bottom: 1px solid var(--line);
243
+ text-align: left;
244
+ vertical-align: top;
245
+ }
246
+ ul {
247
+ padding-left: 24px;
248
+ }
249
+ .session-list button {
250
+ width: 100%;
251
+ text-align: left;
252
+ margin-top: 8px;
253
+ }
254
+ .session-list small {
255
+ display: block;
256
+ font-weight: 400;
257
+ }
258
+ .skip {
259
+ position: absolute;
260
+ left: 16px;
261
+ top: -100px;
262
+ background: white;
263
+ padding: 12px;
264
+ z-index: 3;
265
+ }
266
+ .skip:focus {
267
+ top: 12px;
268
+ }
269
+ .split {
270
+ display: flex;
271
+ justify-content: space-between;
272
+ gap: 14px;
273
+ align-items: center;
274
+ }
275
+ .saved {
276
+ border-left: 3px solid var(--accent);
277
+ padding-left: 14px;
278
+ }
279
+ .scope {
280
+ font-size: 0.85rem;
281
+ color: var(--muted);
282
+ margin-top: 20px;
283
+ }
284
+ [hidden] {
285
+ display: none !important;
286
+ }
287
+ @media (max-width: 900px) {
288
+ .layout {
289
+ grid-template-columns: 1fr;
290
+ }
291
+ .shell {
292
+ padding: 20px;
293
+ }
294
+ .steps button {
295
+ min-width: 100px;
296
+ }
297
+ header {
298
+ padding: 20px;
299
+ }
300
+ }
301
+ @media (prefers-reduced-motion: no-preference) {
302
+ button {
303
+ transition: background 0.12s;
304
+ }
305
+ }
306
+ @media print {
307
+ button,
308
+ .steps,
309
+ #start-form,
310
+ #editor {
311
+ display: none;
312
+ }
313
+ .layout {
314
+ display: block;
315
+ }
316
+ .card {
317
+ break-inside: avoid;
318
+ }
319
+ }
320
+ </style>
321
+ </head>
322
+ <body>
323
+ <a class="skip" href="#workspace">Skip to workspace</a>
324
+ <header>
325
+ <div>
326
+ <div class="eyebrow">Atelier / knowledge practice</div>
327
+ <h1>Knowledge workspace</h1>
328
+ </div>
329
+ <button id="refresh">Refresh evidence</button>
330
+ </header>
331
+ <main class="shell" id="workspace">
332
+ <p class="notice" id="status" role="status" aria-live="polite">
333
+ Loading the current plan and graph…
334
+ </p>
335
+ <nav class="steps" aria-label="Knowledge workflow" id="steps"></nav>
336
+ <div class="layout">
337
+ <section aria-label="Current workspace">
338
+ <div class="card callout" id="purpose"></div>
339
+ <div class="card">
340
+ <label for="question">Work on a question</label
341
+ ><select id="question"></select>
342
+ <p class="muted" id="work"></p>
343
+ </div>
344
+ <div id="dashboard"></div>
345
+ </section>
346
+ <aside aria-label="Coauthoring">
347
+ <div class="card">
348
+ <div class="eyebrow">
349
+ Human planning → assisted drafting → review
350
+ </div>
351
+ <h2 style="margin-top: 12px">Coauthor the next step</h2>
352
+ <p class="muted">
353
+ One useful question at a time. Saved wording stays in private
354
+ local history.
355
+ </p>
356
+ <form id="start-form">
357
+ <label for="author">Your name or role</label
358
+ ><input
359
+ id="author"
360
+ maxlength="160"
361
+ required
362
+ autocomplete="name"
363
+ /><label for="flow">Guided flow</label
364
+ ><select id="flow"></select
365
+ ><button class="primary" style="margin-top: 16px" id="start">
366
+ Start a session
367
+ </button>
368
+ </form>
369
+ <p
370
+ id="session-status"
371
+ class="notice"
372
+ role="status"
373
+ aria-live="polite"
374
+ >
375
+ Start here, or resume a session below.
376
+ </p>
377
+ <div id="pending-controls" class="notice" hidden>
378
+ <p>
379
+ The request is unconfirmed. Editing is paused so retry sends the
380
+ exact same wording. Reload and export remain available.
381
+ </p>
382
+ <div class="actions">
383
+ <button id="retry-request">Retry exact request</button>
384
+ <button id="end-retry">End retry and inspect history</button>
385
+ <button id="export-request">Export pending request</button>
386
+ </div>
387
+ </div>
388
+ <div id="session" hidden>
389
+ <p id="session-meta" class="muted"></p>
390
+ <p id="source-state" class="notice"></p>
391
+ <div id="recorded-wording"></div>
392
+ <fieldset id="editor">
393
+ <label for="answer" id="prompt"></label>
394
+ <p id="hint" class="muted"></p>
395
+ <textarea
396
+ id="answer"
397
+ maxlength="24000"
398
+ aria-describedby="hint"
399
+ ></textarea>
400
+ <p id="confirmation" hidden>
401
+ Compare this proposed revision with your original answer.
402
+ Confirm it before saving.
403
+ </p>
404
+ <div class="actions" id="controls"></div>
405
+ </fieldset>
406
+ <div class="actions">
407
+ <button id="reload-session">Reload session</button
408
+ ><button id="discard" hidden>Discard unsaved text</button
409
+ ><button id="snapshot">Export draft snapshot</button>
410
+ </div>
411
+ <p class="muted">
412
+ Unsaved text remains in this tab. Export a snapshot before
413
+ closing it. Reloading a session preserves your unsaved text.
414
+ </p>
415
+ <div id="bound-context"></div>
416
+ <div id="history"></div>
417
+ </div>
418
+ <p class="scope">
419
+ A saved draft is ready for owner review. It does not edit the
420
+ plan, accept evidence, or authorize an action. Names are locally
421
+ asserted.
422
+ </p>
423
+ </div>
424
+ <div class="card session-list">
425
+ <h3>Continue earlier work</h3>
426
+ <form id="open-session-form">
427
+ <label for="session-id">Open an exact session ID</label>
428
+ <input
429
+ id="session-id"
430
+ required
431
+ placeholder="kg-…"
432
+ autocomplete="off"
433
+ />
434
+ <button id="open-session">Open session</button>
435
+ </form>
436
+ <div id="sessions"></div>
437
+ </div>
438
+ </aside>
439
+ </div>
440
+ </main>
441
+ <script type="module">
442
+ const el = (id) => document.getElementById(id)
443
+ let dashboard = null,
444
+ stage = 'onboard',
445
+ active = null,
446
+ busy = false,
447
+ pending = null,
448
+ endedRequests = [],
449
+ dirty = false,
450
+ contextRequest = 0,
451
+ editGeneration = 0,
452
+ composing = false
453
+ const note = (id, text, error = false) => {
454
+ el(id).textContent = text
455
+ el(id).classList.toggle('error', error)
456
+ }
457
+ function node(tag, text, parent, cls) {
458
+ const item = document.createElement(tag)
459
+ if (text !== undefined) item.textContent = text
460
+ if (cls) item.className = cls
461
+ parent?.append(item)
462
+ return item
463
+ }
464
+ function card(parent, title) {
465
+ const box = node('article', undefined, parent, 'card')
466
+ node('h3', title, box)
467
+ return box
468
+ }
469
+ async function request(route, body) {
470
+ if (!/^[-a-z]+$/.test(route.split('?')[0]))
471
+ throw new Error('Invalid local route')
472
+ const options =
473
+ body === undefined
474
+ ? {}
475
+ : {
476
+ method: 'POST',
477
+ headers: { 'Content-Type': 'application/json' },
478
+ body: JSON.stringify(body),
479
+ }
480
+ if (body !== undefined && route !== 'session') {
481
+ const grant = await request('session', {})
482
+ options.headers['X-Atelier-Nonce'] = grant.mutationNonce
483
+ }
484
+ const response = await fetch('/api/knowledge/' + route, options)
485
+ const value = await response.json()
486
+ if (!response.ok || !value.ok)
487
+ throw new Error(value.error || 'Request failed')
488
+ return value
489
+ }
490
+ const selected = () =>
491
+ dashboard?.questions.find((q) => q.id === el('question').value)
492
+ const details = (parent, title, value) => {
493
+ const d = node('details', undefined, parent)
494
+ node('summary', title, d)
495
+ node(
496
+ 'pre',
497
+ typeof value === 'string' ? value : JSON.stringify(value, null, 2),
498
+ d
499
+ )
500
+ return d
501
+ }
502
+ function choose(next) {
503
+ stage = next
504
+ for (const b of el('steps').children)
505
+ b.setAttribute('aria-pressed', String(b.dataset.stage === stage))
506
+ el('flow').value = stage
507
+ render()
508
+ }
509
+ async function refresh() {
510
+ try {
511
+ const data = (await request('dashboard')).dashboard
512
+ const old = el('question').value
513
+ dashboard = data
514
+ el('purpose').replaceChildren()
515
+ node(
516
+ 'div',
517
+ 'THE WORK THIS GRAPH SHOULD IMPROVE',
518
+ el('purpose'),
519
+ 'eyebrow'
520
+ )
521
+ node('h2', data.purpose, el('purpose'))
522
+ node('p', data.next.reason, el('purpose'))
523
+ node('small', data.assurance, el('purpose'))
524
+ el('question').replaceChildren()
525
+ for (const q of data.questions) {
526
+ const o = node('option', q.question, el('question'))
527
+ o.value = q.id
528
+ }
529
+ if (data.questions.some((q) => q.id === old))
530
+ el('question').value = old
531
+ const firstLoad = !el('steps').children.length
532
+ if (firstLoad)
533
+ for (const [i, f] of data.flows.entries()) {
534
+ const b = node('button', undefined, el('steps'))
535
+ b.type = 'button'
536
+ b.dataset.stage = f.id
537
+ node('span', '0' + (i + 1), b)
538
+ node('strong', f.title, b)
539
+ b.onclick = () => choose(f.id)
540
+ const o = node('option', f.title, el('flow'))
541
+ o.value = f.id
542
+ }
543
+ if (firstLoad && !active && data.workshop?.available) stage = 'workshop'
544
+ choose(stage)
545
+ note(
546
+ 'status',
547
+ 'Current plan and graph loaded. Coverage checks do not establish acceptance.'
548
+ )
549
+ await sessions()
550
+ } catch (error) {
551
+ note(
552
+ 'status',
553
+ 'Workspace unavailable: ' +
554
+ error.message +
555
+ '. Check the plan and restart with atelier dev --knowledge if needed. Earlier session history remains available.',
556
+ true
557
+ )
558
+ await sessions().catch(() => {})
559
+ }
560
+ }
561
+ function render() {
562
+ if (!dashboard) return
563
+ contextRequest++
564
+ const out = el('dashboard')
565
+ out.replaceChildren()
566
+ const flow = dashboard.flows.find((f) => f.id === stage)
567
+ node('h2', flow.title, out)
568
+ node('p', flow.description, out, 'muted')
569
+ el('work').textContent = selected()?.work || ''
570
+ if (stage === 'workshop') {
571
+ const box = card(out, 'One missing supporting relationship')
572
+ node('p', 'Read both invented sources. Keep your words, interpretation, response and revision separate.', box)
573
+ node('p', 'A draft or copy-only handoff does not apply a source change or approve readiness.', box, 'notice')
574
+ details(box, 'Current finding and exact source evidence', dashboard.workshop.context)
575
+ }
576
+ if (stage === 'onboard') {
577
+ const box = card(out, 'People and purpose')
578
+ node('p', 'Steward: ' + dashboard.steward, box)
579
+ node('p', 'Reviewer: ' + dashboard.reviewer, box)
580
+ node(
581
+ 'p',
582
+ 'These roles and policies are declarations to review with the people involved.',
583
+ box,
584
+ 'muted'
585
+ )
586
+ const grid = node('div', undefined, out, 'grid')
587
+ for (const [key, value] of Object.entries(dashboard.governance)) {
588
+ const c = card(grid, key[0].toUpperCase() + key.slice(1))
589
+ node('p', value, c)
590
+ }
591
+ const c = card(out, 'Start small')
592
+ node(
593
+ 'p',
594
+ 'Choose one permitted source, one consequential question, and one reviewer. Replace the starter plan with your own definitions and test an end-to-end answer before expanding.',
595
+ c
596
+ )
597
+ node(
598
+ 'p',
599
+ 'Context budget: ' +
600
+ dashboard.budget.maxContextBytes.toLocaleString() +
601
+ ' bytes across at most ' +
602
+ dashboard.budget.maxDocuments +
603
+ ' complete documents.',
604
+ c
605
+ )
606
+ }
607
+ if (stage === 'model') {
608
+ const grid = node('div', undefined, out, 'grid')
609
+ for (const c of dashboard.concepts) {
610
+ const box = card(grid, c.id)
611
+ node('p', c.definition, box)
612
+ node('div', 'Identity rule', box, 'eyebrow')
613
+ node('p', c.identityRule, box)
614
+ node(
615
+ 'small',
616
+ (c.coverage?.eligibleRecords ?? 0) +
617
+ ' eligible / ' + (c.coverage?.records ?? 0) +
618
+ ' total records · questions: ' +
619
+ (c.coverage?.questions.join(', ') || 'none'),
620
+ box
621
+ )
622
+ }
623
+ const c = card(out, 'Relationships with a reason')
624
+ for (const r of dashboard.relations) {
625
+ node('h3', r.from + ' → ' + r.to, c)
626
+ node('span', r.predicate, c, 'tag')
627
+ node('p', r.meaning, c)
628
+ node(
629
+ 'small',
630
+ (r.coverage?.eligibleMatchingEdges ?? 0) + ' eligible / ' +
631
+ (r.coverage?.matchingEdges ?? 0) + ' total declared matching edges',
632
+ c
633
+ )
634
+ }
635
+ node(
636
+ 'p',
637
+ 'Coverage describes recorded structure. Review identity, direction, and meaning against the source.',
638
+ c,
639
+ 'muted'
640
+ )
641
+ }
642
+ if (stage === 'deepen') {
643
+ const c = card(out, 'Evidence and modeling gaps')
644
+ const issues = [
645
+ ...dashboard.inspection.errors,
646
+ ...dashboard.inspection.warnings,
647
+ ]
648
+ if (!issues.length)
649
+ node(
650
+ 'p',
651
+ 'No graph structure warnings. Review the retrieval checks below, then check meaning, contradictions, and an unseen question.',
652
+ c
653
+ )
654
+ else {
655
+ const list = node('ul', undefined, c)
656
+ for (const issue of issues) node('li', issue, list)
657
+ }
658
+ for (const q of dashboard.evaluation?.cases || []) {
659
+ const g = q.runs.graph
660
+ if (!g.expectedEvidencePresent || g.stale.length) {
661
+ const b = card(out, q.question)
662
+ node(
663
+ 'p',
664
+ g.status === 'abstain-unverified'
665
+ ? 'Abstention unverified: matching evidence omitted'
666
+ : q.expect === 'abstain'
667
+ ? 'Abstention needs review: matching evidence retrieved'
668
+ : 'Evidence needs attention',
669
+ b,
670
+ 'notice'
671
+ )
672
+ node(
673
+ 'p',
674
+ 'Candidates: ' + g.candidates + '. Selected: ' + g.sourceIds.length +
675
+ '. Omitted: ' + g.omitted + '. Omission details not listed: ' +
676
+ g.omissionsUnlisted + '.',
677
+ b
678
+ )
679
+ if (q.expect === 'abstain') {
680
+ if (g.sourceIds.length) node('p', 'Selected evidence: ' + g.sourceIds.join(', '), b)
681
+ } else {
682
+ node(
683
+ 'p',
684
+ 'Missing evidence: ' +
685
+ (g.missing.join(', ') || 'none') +
686
+ '. Changed pins: ' +
687
+ (g.stale.join(', ') || 'none') +
688
+ '. Missing relationships: ' +
689
+ (g.missingRelations.join(', ') || 'none') +
690
+ '.',
691
+ b
692
+ )
693
+ }
694
+ if (g.omitted) {
695
+ const list = node('ul', undefined, b)
696
+ for (const omission of g.omissions) node('li', omission.id + ': ' + omission.reason, list)
697
+ node('p', 'Inspect omitted sources and their limits before relying on the result.', b)
698
+ }
699
+ }
700
+ }
701
+ node(
702
+ 'p',
703
+ 'Keep unmodeled observations and disagreement visible. A forecast, a proposal, and an observed outcome have different meanings.',
704
+ c
705
+ )
706
+ }
707
+ if (stage === 'apply') {
708
+ const c = card(out, selected()?.question || 'Choose a question')
709
+ node(
710
+ 'p',
711
+ 'Inspect complete sources before drafting an answer. The context builder selects evidence and makes no model call.',
712
+ c
713
+ )
714
+ const actions = node('div', undefined, c, 'actions')
715
+ for (const mode of ['graph', 'lexical']) {
716
+ const b = node(
717
+ 'button',
718
+ mode === 'graph'
719
+ ? 'Inspect graph context'
720
+ : 'Inspect direct-search baseline',
721
+ actions
722
+ )
723
+ b.onclick = () => loadContext(mode)
724
+ }
725
+ node('div', undefined, out).id = 'evidence'
726
+ }
727
+ if (stage === 'learn') {
728
+ const c = card(out, 'Is the graph helping?')
729
+ node(
730
+ 'p',
731
+ 'The same questions and byte budget are checked with direct metadata search and one hop of declared relationships. These are author-specified retrieval checks.',
732
+ c
733
+ )
734
+ if (dashboard.evaluation) {
735
+ const wrap = node('div', undefined, c, 'scroll'),
736
+ table = node('table', undefined, wrap)
737
+ const tr = node('tr', undefined, node('thead', undefined, table))
738
+ for (const h of ['Question', 'Direct search', 'Graph context'])
739
+ node('th', h, tr)
740
+ const body = node('tbody', undefined, table)
741
+ for (const q of dashboard.evaluation.cases) {
742
+ const row = node('tr', undefined, body)
743
+ node('th', q.question, row)
744
+ for (const mode of ['lexical', 'graph']) {
745
+ const run = q.runs[mode]
746
+ node(
747
+ 'td',
748
+ (run.expectedEvidencePresent && !run.stale.length
749
+ ? q.expect === 'abstain'
750
+ ? 'No evidence retrieved; abstention expected'
751
+ : 'Expected evidence retained'
752
+ : run.status === 'abstain-unverified'
753
+ ? 'Abstention unverified: matching evidence omitted'
754
+ : 'Needs attention') +
755
+ ' · ' +
756
+ run.payloadBytes.toLocaleString() +
757
+ ' bytes',
758
+ row
759
+ )
760
+ if (run.omitted) details(row.lastElementChild, 'Omitted evidence', {
761
+ candidates: run.candidates,
762
+ omitted: run.omitted,
763
+ unreadable: run.unreadable,
764
+ omissions: run.omissions,
765
+ omissionsUnlisted: run.omissionsUnlisted,
766
+ })
767
+ }
768
+ }
769
+ }
770
+ node(
771
+ 'p',
772
+ 'Actual tokens, cost, answer correctness, time saved, and user acceptance: not measured by this dashboard.',
773
+ c,
774
+ 'notice'
775
+ )
776
+ node(
777
+ 'p',
778
+ 'Use the Learn flow to retain observed outcomes and measurement notes. Compare unseen questions, including cases where the right answer is “not enough evidence.”',
779
+ c
780
+ )
781
+ }
782
+ }
783
+ async function loadContext(mode) {
784
+ const generation = ++contextRequest
785
+ const q = selected()
786
+ try {
787
+ const result = await request(
788
+ 'context?' + new URLSearchParams({ id: q.id, mode })
789
+ )
790
+ if (generation !== contextRequest || stage !== 'apply') return
791
+ const out = el('evidence')
792
+ out.replaceChildren()
793
+ const context = result.context
794
+ const c = card(
795
+ out,
796
+ context.sources.length
797
+ ? 'Evidence selected'
798
+ : 'More evidence needed'
799
+ )
800
+ node(
801
+ 'p',
802
+ context.budget.payloadBytes.toLocaleString() +
803
+ ' bytes · approximately ' +
804
+ context.budget.estimatedTokens.toLocaleString() +
805
+ ' tokens · 0 provider calls',
806
+ c
807
+ )
808
+ node(
809
+ 'small',
810
+ 'Token estimate uses bytes / 4. Actual token usage is unknown. ' +
811
+ context.coverage.omitted +
812
+ ' sources omitted; ' +
813
+ context.coverage.unreadable +
814
+ ' unreadable.',
815
+ c
816
+ )
817
+ node('p', context.use, c)
818
+ if (context.coverage.omitted) {
819
+ const list = node('ul', undefined, c)
820
+ for (const omission of context.omissions)
821
+ node('li', omission.id + ': ' + omission.reason, list)
822
+ if (context.coverage.omissionsUnlisted)
823
+ node('p', context.coverage.omissionsUnlisted + ' further omission details exceed the packet allowance. Narrow the source scope or increase the budget, then inspect again.', c, 'notice')
824
+ }
825
+ for (const source of context.sources) {
826
+ const b = card(out, source.id)
827
+ node(
828
+ 'p',
829
+ source.repo +
830
+ '/' +
831
+ source.path +
832
+ ' · lines ' +
833
+ source.lines.start +
834
+ '–' +
835
+ source.lines.end,
836
+ b,
837
+ 'muted'
838
+ )
839
+ details(b, 'Read complete source', source.text)
840
+ details(b, 'Source identity', source.sha256)
841
+ }
842
+ details(out, 'Declared relationships', context.relations)
843
+ details(out, 'Context for your agent', context)
844
+ } catch (error) {
845
+ if (generation === contextRequest)
846
+ note('status', 'Context unavailable: ' + error.message, true)
847
+ }
848
+ }
849
+ async function sessions() {
850
+ try {
851
+ const report = await request('sessions')
852
+ const list = report.sessions
853
+ el('sessions').replaceChildren()
854
+ if (report.truncated)
855
+ node(
856
+ 'p',
857
+ 'Showing the newest ' +
858
+ report.limit +
859
+ ' of ' +
860
+ report.total +
861
+ ' local session files. Open any older session by its exact ID.',
862
+ el('sessions'),
863
+ 'muted'
864
+ )
865
+ if (!list.length)
866
+ node('p', 'No sessions yet.', el('sessions'), 'muted')
867
+ for (const s of list) {
868
+ if (!s.available) {
869
+ const box = node('div', undefined, el('sessions'), 'saved')
870
+ node('p', s.id, box)
871
+ node('p', s.reason, box)
872
+ if (s.recoverable) {
873
+ const resume = node('button', 'Resume incomplete start', box)
874
+ resume.onclick = () => send('recover', { sessionId: s.id })
875
+ }
876
+ continue
877
+ }
878
+ const b = node('button', s.title + ' · ' + s.phase, el('sessions'))
879
+ node(
880
+ 'small',
881
+ s.question +
882
+ ' · ' +
883
+ s.saved +
884
+ '/' +
885
+ s.fields +
886
+ ' saved' +
887
+ (s.current
888
+ ? ''
889
+ : s.currency === 'unavailable'
890
+ ? ' · source check unavailable'
891
+ : ' · earlier source revision'),
892
+ b
893
+ )
894
+ node('small', s.id + (s.createdAt ? ' · ' + s.createdAt : ''), b)
895
+ b.disabled = dirty || busy || Boolean(pending)
896
+ b.onclick = () => readSession(s.id)
897
+ }
898
+ setBusy(busy)
899
+ } catch (error) {
900
+ el('sessions').textContent = 'History unavailable: ' + error.message
901
+ }
902
+ }
903
+ function setBusy(value) {
904
+ busy = value
905
+ el('discard').hidden = !dirty
906
+ el('discard').disabled = value || Boolean(pending) || composing
907
+ el('start').disabled = value || dirty || Boolean(pending) || composing
908
+ el('open-session').disabled = value || dirty || Boolean(pending)
909
+ el('reload-session').disabled = value || composing
910
+ // The native field owns text, IME and undo while a request is pending.
911
+ el('editor').disabled = !active
912
+ if (value || pending || composing) el('answer').readOnly = false
913
+ for (const b of el('controls').querySelectorAll('button'))
914
+ b.disabled = value || Boolean(pending) || composing || !active?.current
915
+ el('pending-controls').hidden = !pending
916
+ el('retry-request').disabled = value
917
+ el('end-retry').disabled = value
918
+ el('export-request').disabled = value
919
+ for (const b of el('sessions').querySelectorAll('button'))
920
+ b.disabled = value || dirty || Boolean(pending)
921
+ }
922
+ function participantText(value, field) {
923
+ if (active?.record.context?.participantResponseProfile === 'four-choice-separate-review-r1'
924
+ && field === 'interpretation') {
925
+ try { return JSON.parse(value).text } catch { return value }
926
+ }
927
+ return value
928
+ }
929
+ function showWorkshopReview() {
930
+ if (!active?.current || busy || pending || dirty || composing) return
931
+ const parentId = active.record.id
932
+ const preview = node('div', undefined, el('controls'), 'saved')
933
+ node('h3', 'Review declaration', preview)
934
+ node('p', 'Current declaration: []', preview)
935
+ node('p', 'Selected draft: ["devday:workshop"]', preview)
936
+ node('p', 'This preview has no saved or source effect. Select the draft to record and save it separately.', preview)
937
+ const select = node('button', 'Select this draft', preview)
938
+ select.type = 'button'
939
+ select.onclick = () => {
940
+ if (active.record.id !== parentId || busy || pending || dirty || composing) return
941
+ send('workshop-review', { requestId: crypto.randomUUID(), sessionId: parentId })
942
+ }
943
+ const back = node('button', 'Return', preview)
944
+ back.type = 'button'
945
+ back.onclick = () => preview.remove()
946
+ }
947
+ function renderSession({ preserve = false } = {}) {
948
+ if (!active) return
949
+ el('session').hidden = false
950
+ const { record, state, current } = active
951
+ el('session-meta').textContent =
952
+ record.title +
953
+ ' · ' +
954
+ record.question.question +
955
+ ' · ' +
956
+ record.author +
957
+ ' (locally asserted) · ' +
958
+ state.saved.length +
959
+ '/' +
960
+ state.fields.length +
961
+ ' saved'
962
+ note(
963
+ 'source-state',
964
+ current
965
+ ? 'Bound sources still match this session.'
966
+ : active.currency === 'unavailable'
967
+ ? 'Bound sources could not be checked. History is retained; repair the workspace before continuing.'
968
+ : 'Earlier source revision. History is retained; refresh evidence and start a new session to continue.',
969
+ !current
970
+ )
971
+ const prompt = record.prompts[state.index]
972
+ el('start-form').hidden = false
973
+ el('prompt').textContent = prompt?.[1] || 'All draft fields saved'
974
+ el('hint').textContent =
975
+ prompt?.[2] ||
976
+ 'Review the retained wording and export it for the source owner.'
977
+ if (!preserve && !dirty) el('answer').value = participantText(state.proposal?.text || '', state.fields[state.index]?.id)
978
+ || (record.flow === 'workshop-review' && state.phase === 'input' ? '["devday:workshop"]' : '')
979
+ const recorded = el('recorded-wording')
980
+ recorded.replaceChildren()
981
+ const field = state.fields[state.index]?.id
982
+ const answers = state.answers.filter((a) => a.fieldId === field)
983
+ if (
984
+ dirty &&
985
+ state.proposal &&
986
+ el('answer').value !== participantText(state.proposal.text, field)
987
+ ) {
988
+ node(
989
+ 'p',
990
+ 'Recorded wording differs from your tab text. Compare it before recording another answer.',
991
+ recorded,
992
+ 'notice'
993
+ )
994
+ node('h3', 'Current recorded wording', recorded)
995
+ node('p', participantText(state.proposal.text, field), recorded, 'saved')
996
+ node(
997
+ 'small',
998
+ 'Session revision ' +
999
+ state.revision +
1000
+ '. Recording a new answer keeps both versions in history and makes your wording the current proposal.',
1001
+ recorded
1002
+ )
1003
+ }
1004
+ if (answers.length) {
1005
+ const history = details(
1006
+ recorded,
1007
+ 'Retained answers for this step',
1008
+ answers.map((a) => ({
1009
+ text: participantText(a.text, field),
1010
+ eventId: a.eventId,
1011
+ revision:
1012
+ (state.events.find((e) => e.id === a.eventId)?.event
1013
+ .expectedRevision ?? -1) + 1,
1014
+ }))
1015
+ )
1016
+ history.open = Boolean(
1017
+ dirty &&
1018
+ state.proposal &&
1019
+ el('answer').value !== participantText(state.proposal.text, field)
1020
+ )
1021
+ }
1022
+ el('answer').hidden = !prompt && !dirty
1023
+ el('answer').readOnly =
1024
+ !['input', 'draft'].includes(state.phase) && !dirty && !composing && !pending && !busy
1025
+ el('confirmation').hidden = state.phase !== 'confirmation'
1026
+ el('controls').replaceChildren()
1027
+ const action = (title, type) => {
1028
+ const b = node(
1029
+ 'button',
1030
+ title,
1031
+ el('controls'),
1032
+ ['save', 'answer'].includes(type) ? 'primary' : undefined
1033
+ )
1034
+ b.type = 'button'
1035
+ b.onclick = () => intent(type)
1036
+ }
1037
+ if (['input', 'draft'].includes(state.phase)) {
1038
+ const participant = record.context?.participantResponseProfile === 'four-choice-separate-review-r1'
1039
+ if (participant && field === 'choice') {
1040
+ for (const [value, label] of [['note', 'Note'], ['perspective-only', 'Perspective'], ['disagreement', 'Disagreement'], ['pause', 'Pause']]) {
1041
+ const choice = node('button', label, el('controls'))
1042
+ choice.type = 'button'
1043
+ choice.onclick = () => {
1044
+ if (busy || pending || composing) return
1045
+ el('answer').value = value
1046
+ editGeneration++
1047
+ dirty = true
1048
+ intent('answer')
1049
+ }
1050
+ }
1051
+ } else action(record.flow === 'workshop-review' ? 'Record selected draft'
1052
+ : participant && field === 'interpretation' ? 'Record optional interpretation' : 'Record my answer', 'answer')
1053
+ if (state.phase === 'draft' && (!participant || field === 'interpretation')) action('Propose revised wording', 'propose')
1054
+ if (state.phase === 'draft') action('Save private draft', 'save')
1055
+ }
1056
+ if (state.phase === 'confirmation') {
1057
+ details(el('controls'), 'Proposed wording', participantText(state.proposal.text, field))
1058
+ details(
1059
+ el('controls'),
1060
+ 'Original wording',
1061
+ participantText(state.answers.find(
1062
+ (a) => a.eventId === state.proposal.originalEventId
1063
+ )?.text || '', field)
1064
+ )
1065
+ action('Confirm revised wording', 'confirm')
1066
+ action('Keep original wording', 'reject')
1067
+ }
1068
+ if (state.phase === 'saved') action('Continue', 'advance')
1069
+ if (state.phase === 'complete' && record.flow === 'workshop'
1070
+ && record.context?.participantResponseProfile === 'four-choice-separate-review-r1'
1071
+ && active.owningReadbacks?.verified === true) {
1072
+ const review = node('button', 'Review', el('controls'))
1073
+ review.type = 'button'
1074
+ review.onclick = showWorkshopReview
1075
+ }
1076
+ if (state.phase === 'complete' && (record.flow === 'workshop-review'
1077
+ || record.flow === 'workshop' && state.saved.find(s => s.fieldId === 'choice')?.text === 'revision')) {
1078
+ const b = node('button', 'Prepare source-owner handoff', el('controls'))
1079
+ b.onclick = async () => {
1080
+ if (busy || pending || dirty || composing) return
1081
+ setBusy(true)
1082
+ try {
1083
+ const value = await request('workshop-handoff', { sessionId: record.id })
1084
+ details(el('bound-context'), 'Copy-only source-owner handoff', value.handoff)
1085
+ note('session-status', 'Handoff prepared from durable readback. Source-owner editing and reassessment remain required.')
1086
+ } catch (error) { note('session-status', error.message, true) }
1087
+ finally { setBusy(false) }
1088
+ }
1089
+ }
1090
+ if (state.phase === 'paused') action('Resume', 'resume')
1091
+ else if (
1092
+ ['input', 'draft', 'confirmation', 'saved', 'recovery'].includes(
1093
+ state.phase
1094
+ )
1095
+ )
1096
+ action('Pause', 'pause')
1097
+ if (state.phase === 'recovery' && state.pending.retries < 1)
1098
+ action('Retry private save', 'retry')
1099
+ if (state.phase === 'saving')
1100
+ action('Reconcile pending save', 'recover')
1101
+ if (state.phase === 'recovery' && state.pending.retries >= 1)
1102
+ node(
1103
+ 'p',
1104
+ 'Retry exhausted. Preserve history for operator reconciliation.',
1105
+ el('controls')
1106
+ )
1107
+ el('bound-context').replaceChildren()
1108
+ details(
1109
+ el('bound-context'),
1110
+ 'Evidence captured for this session',
1111
+ record.context || 'No context available at session start.'
1112
+ )
1113
+ if (active.owningReadbacks) details(el('bound-context'), 'Descriptor, event history and immutable saved values', active.owningReadbacks)
1114
+ if (record.context?.participantResponseProfile === 'four-choice-separate-review-r1' && record.flow === 'workshop')
1115
+ details(el('bound-context'), 'Original contribution and choice IDs', {
1116
+ contributionId: state.answers.filter(a => a.fieldId === 'original-words').at(-1)?.eventId || null,
1117
+ choiceId: state.answers.filter(a => a.fieldId === 'choice').at(-1)?.eventId || null,
1118
+ choiceStatus: state.saved.some(s => s.fieldId === 'choice') && active.owningReadbacks?.verified ? 'confirmed'
1119
+ : state.answers.some(a => a.fieldId === 'choice') ? 'unconfirmed' : 'not-recorded'
1120
+ })
1121
+ el('history').replaceChildren()
1122
+ for (const saved of state.saved) {
1123
+ const box = node('div', undefined, el('history'), 'saved')
1124
+ node(
1125
+ 'h3',
1126
+ record.prompts.find((p) => p[0] === saved.fieldId)?.[1] ||
1127
+ saved.fieldId,
1128
+ box
1129
+ )
1130
+ node('p', participantText(saved.text, saved.fieldId), box)
1131
+ details(box, 'Durable draft receipt', saved.receipt)
1132
+ }
1133
+ setBusy(busy)
1134
+ }
1135
+ async function readSession(id) {
1136
+ try {
1137
+ active = await request('read?' + new URLSearchParams({ id }))
1138
+ renderSession({ preserve: dirty })
1139
+ note(
1140
+ 'session-status',
1141
+ 'Session loaded. ' +
1142
+ (dirty
1143
+ ? 'Your unsaved text is still in the editor.'
1144
+ : 'Saved drafts and retained intents were read from local history.')
1145
+ )
1146
+ await sessions()
1147
+ return true
1148
+ } catch (error) {
1149
+ note('session-status', 'Session unavailable: ' + error.message, true)
1150
+ return false
1151
+ }
1152
+ }
1153
+ async function send(route, input, generation = editGeneration) {
1154
+ pending = { route, input, generation }
1155
+ setBusy(true)
1156
+ try {
1157
+ active = await request(route, input)
1158
+ pending = null
1159
+ if (generation === editGeneration && !composing) dirty = false
1160
+ renderSession({ preserve: dirty || composing })
1161
+ note(
1162
+ 'session-status',
1163
+ active.current
1164
+ ? operationMessage(route, input, active.state)
1165
+ : 'Sources changed during the operation. Retained history is available; start a newly bound session.'
1166
+ )
1167
+ await sessions()
1168
+ } catch (error) {
1169
+ note(
1170
+ 'session-status',
1171
+ 'Not confirmed saved: ' +
1172
+ error.message +
1173
+ '. Your wording remains here. If the service is unavailable, restore atelier dev --knowledge and retry. For a revision, source, or event refusal, end retry and inspect history before another intent.',
1174
+ true
1175
+ )
1176
+ } finally {
1177
+ setBusy(false)
1178
+ }
1179
+ }
1180
+ function operationMessage(route, input, state) {
1181
+ const step = ' Current step: ' + state.phase + '.'
1182
+ if (['saving', 'recovery'].includes(state.phase))
1183
+ return (
1184
+ 'Save not confirmed. Preserve your wording and inspect the recovery controls.' +
1185
+ step
1186
+ )
1187
+ if (route === 'start' || route === 'workshop-review')
1188
+ return 'Session started. Its retained state was read back.' + step
1189
+ const type = input.event?.type
1190
+ if (type === 'answer')
1191
+ return (
1192
+ 'Answer retained in session history; this request did not save a draft.' +
1193
+ step
1194
+ )
1195
+ if (
1196
+ type === 'save' &&
1197
+ state.saved.some((s) => s.receipt.requestId === input.event.id)
1198
+ )
1199
+ return 'Saved draft receipt read back.' + step
1200
+ if (
1201
+ (type === 'retry' || route === 'recover') &&
1202
+ state.phase === 'saved'
1203
+ )
1204
+ return 'Saved draft receipt read back.' + step
1205
+ if (type === 'reject' || type === 'confirm')
1206
+ return (
1207
+ 'Wording choice recorded; save the private draft when ready.' + step
1208
+ )
1209
+ return 'Session state read back.' + step
1210
+ }
1211
+ async function intent(type) {
1212
+ if (pending)
1213
+ return note(
1214
+ 'session-status',
1215
+ 'Resolve the pending request with Retry or End retry before recording another intent.',
1216
+ true
1217
+ )
1218
+ if (!active || busy || composing) return
1219
+ if (dirty && !['answer', 'propose'].includes(type))
1220
+ return note(
1221
+ 'session-status',
1222
+ 'Record or export your changed answer before moving on.',
1223
+ true
1224
+ )
1225
+ if (type === 'recover')
1226
+ return send('recover', { sessionId: active.record.id })
1227
+ const event = {
1228
+ id: crypto.randomUUID(),
1229
+ type,
1230
+ expectedRevision: active.state.revision,
1231
+ }
1232
+ if (['answer', 'propose'].includes(type)) {
1233
+ event.text = el('answer').value
1234
+ if (active.record.context?.participantResponseProfile === 'four-choice-separate-review-r1'
1235
+ && active.state.fields[active.state.index]?.id === 'interpretation') event.text = JSON.stringify({ text: event.text })
1236
+ if (!event.text.trim())
1237
+ return note('session-status', 'Write an answer first.', true)
1238
+ }
1239
+ await send('event', { sessionId: active.record.id, event })
1240
+ }
1241
+ el('start-form').onsubmit = (event) => {
1242
+ event.preventDefault()
1243
+ if (pending)
1244
+ return note(
1245
+ 'session-status',
1246
+ 'Resolve the pending request before starting another session.',
1247
+ true
1248
+ )
1249
+ if (!dashboard || busy || dirty || composing) return
1250
+ send('start', {
1251
+ requestId: crypto.randomUUID(),
1252
+ flow: el('flow').value,
1253
+ questionId: selected().id,
1254
+ snapshot: dashboard.snapshot,
1255
+ author: el('author').value,
1256
+ })
1257
+ }
1258
+ el('discard').onclick = () => {
1259
+ dirty = false
1260
+ renderSession()
1261
+ note(
1262
+ 'session-status',
1263
+ 'Unsaved tab text discarded. Retained session history is unchanged.'
1264
+ )
1265
+ }
1266
+ el('answer').oninput = () => {
1267
+ editGeneration++
1268
+ dirty = true
1269
+ setBusy(busy)
1270
+ note(
1271
+ 'session-status',
1272
+ 'Unsaved wording in this tab. Record it to retain the answer in session history.'
1273
+ )
1274
+ }
1275
+ el('answer').oncompositionstart = () => {
1276
+ composing = true
1277
+ dirty = true
1278
+ editGeneration++
1279
+ setBusy(busy)
1280
+ }
1281
+ el('answer').oncompositionend = () => {
1282
+ composing = false
1283
+ dirty = true
1284
+ editGeneration++
1285
+ renderSession({ preserve: true })
1286
+ }
1287
+ el('question').onchange = render
1288
+ el('refresh').onclick = refresh
1289
+ el('reload-session').onclick = () =>
1290
+ active && readSession(active.record.id)
1291
+ el('retry-request').onclick = async () => {
1292
+ if (!pending || busy || composing) return
1293
+ const original = pending
1294
+ setBusy(true)
1295
+ try {
1296
+ const sessionId = ['start', 'workshop-review'].includes(original.route)
1297
+ ? 'kg-' + original.input.requestId.toLowerCase() : original.input.sessionId
1298
+ const lookup = await request('lookup', { sessionId, eventId: original.input.event?.id || null })
1299
+ if (lookup.outcome === 'unknown') {
1300
+ note('session-status', 'Original outcome unknown. Keep this request and inspect retained history; no retry was sent.', true)
1301
+ } else if (lookup.outcome === 'observed') {
1302
+ // Compare the actual retained event before clearing original custody.
1303
+ const retained = lookup.session.state.events.find(e => e.id === original.input.event?.id)
1304
+ if (original.route === 'event' && (!retained || Object.keys(retained.event).length !== Object.keys(original.input.event).length || Object.keys(original.input.event).some(k => retained.event[k] !== original.input.event[k])))
1305
+ throw new Error('original ID has different retained intent')
1306
+ if (original.route === 'start' && (['flow', 'snapshot'].some(k => lookup.session.record[k] !== original.input[k]) || lookup.session.record.author !== original.input.author.trim()))
1307
+ throw new Error('original start ID has different retained intent')
1308
+ if (original.route === 'workshop-review' && (lookup.session.record.flow !== 'workshop-review'
1309
+ || lookup.session.record.context.participant?.sessionId !== original.input.sessionId))
1310
+ throw new Error('original Review ID has different retained participant')
1311
+ active = lookup.session
1312
+ pending = null
1313
+ if (original.generation === editGeneration && !composing) dirty = false
1314
+ renderSession({ preserve: dirty || composing })
1315
+ note('session-status', 'Original request found in durable history; no repeated effect was sent.')
1316
+ } else if (lookup.outcome === 'not-found') {
1317
+ await send(original.route, original.input, original.generation)
1318
+ } else throw new Error('original outcome not established')
1319
+ } catch (error) { note('session-status', 'Original lookup failed: ' + error.message, true) }
1320
+ finally { setBusy(false) }
1321
+ }
1322
+ el('end-retry').onclick = async () => {
1323
+ if (!pending || busy) return
1324
+ const ended = pending
1325
+ endedRequests.push(ended)
1326
+ pending = null
1327
+ setBusy(false)
1328
+ if (active) {
1329
+ if (!(await readSession(active.record.id))) return
1330
+ } else await sessions()
1331
+ note(
1332
+ 'session-status',
1333
+ 'Retry ended; no server history was deleted or cancelled. Inspect the session before another intent. Your text and the ended request remain in this tab and its exported snapshot. Session: ' +
1334
+ (ended.input.sessionId || 'kg-' + ended.input.requestId)
1335
+ )
1336
+ }
1337
+ el('open-session-form').onsubmit = (event) => {
1338
+ event.preventDefault()
1339
+ if (busy || dirty || pending)
1340
+ return note(
1341
+ 'session-status',
1342
+ 'Resolve your current wording and pending request before opening another session.',
1343
+ true
1344
+ )
1345
+ readSession(el('session-id').value.trim())
1346
+ }
1347
+ function exportSnapshot() {
1348
+ if (!active && !pending && !endedRequests.length) return
1349
+ const value = {
1350
+ status: 'private-draft-for-owner-review',
1351
+ sourceEditsApplied: false,
1352
+ session: active,
1353
+ unsavedText: dirty ? el('answer').value : null,
1354
+ pendingRequest: pending,
1355
+ endedRequests,
1356
+ }
1357
+ const url = URL.createObjectURL(
1358
+ new Blob([JSON.stringify(value, null, 2)], {
1359
+ type: 'application/json',
1360
+ })
1361
+ )
1362
+ const a = document.createElement('a')
1363
+ a.href = url
1364
+ a.download =
1365
+ 'knowledge-draft-' +
1366
+ (active?.record.id || pending?.input.requestId || 'request') +
1367
+ '.json'
1368
+ a.click()
1369
+ setTimeout(() => URL.revokeObjectURL(url), 1000)
1370
+ note(
1371
+ 'session-status',
1372
+ 'Private draft snapshot exported. Review its audience before sharing.'
1373
+ )
1374
+ }
1375
+ el('snapshot').onclick = exportSnapshot
1376
+ el('export-request').onclick = exportSnapshot
1377
+ window.addEventListener('beforeunload', (event) => {
1378
+ if (dirty || pending) {
1379
+ event.preventDefault()
1380
+ event.returnValue = ''
1381
+ }
1382
+ })
1383
+ refresh()
1384
+ </script>
1385
+ </body>
1386
+ </html>
1387
+ `
1388
+ }