@thebackstoryis/engineering-with-ai 0.2.9

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 (334) hide show
  1. package/Docs/README.md +50 -0
  2. package/Docs/adoption/consultancy-and-multi-project-rollout.md +135 -0
  3. package/Docs/adoption/non-technical-team-guide.md +126 -0
  4. package/Docs/archaeology-technology-and-hosting-discovery.md +212 -0
  5. package/Docs/blast-radius-and-impact-routing-guide.md +325 -0
  6. package/Docs/blueprints/internal-blueprint-catalogue.md +146 -0
  7. package/Docs/blueprints/maintaining-organisation-blueprints.md +154 -0
  8. package/Docs/blueprints/validation-and-troubleshooting.md +168 -0
  9. package/Docs/cli-reference.md +113 -0
  10. package/Docs/completed-phase-evidence-amendments.md +74 -0
  11. package/Docs/consultancy-network-rollout-control-plane-guide.md +202 -0
  12. package/Docs/context-aware-delivery-companion-guide.md +198 -0
  13. package/Docs/context-aware-delivery-companion-user-guide.md +184 -0
  14. package/Docs/context-management-and-token-efficiency.md +113 -0
  15. package/Docs/design-systems/design-system-implementation-guide.md +85 -0
  16. package/Docs/design-systems/design-system-pack-authoring-guide.md +95 -0
  17. package/Docs/design-systems/design-system-review-guide.md +51 -0
  18. package/Docs/design-systems/design-system-user-guide.md +96 -0
  19. package/Docs/design-systems/product-owner-guide.md +49 -0
  20. package/Docs/designing-organisation-blueprint-packs.md +384 -0
  21. package/Docs/developer-delivery-guide.md +224 -0
  22. package/Docs/error-reporting-guide.md +110 -0
  23. package/Docs/error-reporting-provider-guide.md +49 -0
  24. package/Docs/examples/error-report-adapter.md +70 -0
  25. package/Docs/examples/meeting-review.md +76 -0
  26. package/Docs/examples/minimal-design-system.md +67 -0
  27. package/Docs/examples/prototype-review-inputs.md +175 -0
  28. package/Docs/examples/reproducible-archaeology-depth-example.md +144 -0
  29. package/Docs/examples/test-scenario-input.md +68 -0
  30. package/Docs/examples/worked-examples.md +147 -0
  31. package/Docs/existing-project-onboarding-guide.md +214 -0
  32. package/Docs/explanation/core-concepts.md +26 -0
  33. package/Docs/explanation/delivery-workflow.md +48 -0
  34. package/Docs/governance/governance-team-guide.md +139 -0
  35. package/Docs/governed-starter-project-materialisation-guide.md +284 -0
  36. package/Docs/guide-catalogue.md +117 -0
  37. package/Docs/guided-discovery-facilitator-guide.md +172 -0
  38. package/Docs/guided-intent-workspace-guide.md +109 -0
  39. package/Docs/guided-phase-evidence-drafting-guide.md +119 -0
  40. package/Docs/human-approval-and-assurance-guide.md +146 -0
  41. package/Docs/knowledge-proposals-implementer-guide.md +106 -0
  42. package/Docs/knowledge-proposals-user-guide.md +247 -0
  43. package/Docs/maintainers/context-benchmarks.md +29 -0
  44. package/Docs/maintainers/contributing.md +58 -0
  45. package/Docs/maintainers/evidence-depth-acceptance.md +72 -0
  46. package/Docs/maintainers/verification-walkthroughs.md +104 -0
  47. package/Docs/meeting-evidence-implementer-guide.md +132 -0
  48. package/Docs/meeting-evidence-user-guide.md +200 -0
  49. package/Docs/operations/dashboard-and-delivery-state.md +153 -0
  50. package/Docs/operations/dashboard-configuration.md +87 -0
  51. package/Docs/operations/installation-updating-and-entitlements.md +135 -0
  52. package/Docs/operations/premium-personas-setup.md +76 -0
  53. package/Docs/operations/troubleshooting-and-recovery.md +205 -0
  54. package/Docs/organisation-rollout-guide.md +142 -0
  55. package/Docs/persona-entitlement-provider-guide.md +199 -0
  56. package/Docs/persona-guided-prototype-iteration.md +129 -0
  57. package/Docs/personas/organisation-specific-personas.md +103 -0
  58. package/Docs/personas/persona-authoring-cookbook.md +176 -0
  59. package/Docs/personas/persona-engagement-ui.md +133 -0
  60. package/Docs/personas/persona-governance.md +118 -0
  61. package/Docs/platform-export-analysis-guide.md +336 -0
  62. package/Docs/policies/governance-owner-guide.md +36 -0
  63. package/Docs/policies/implementation-guide.md +42 -0
  64. package/Docs/policies/organisation-policy-design-gates.md +58 -0
  65. package/Docs/policies/policy-pack-authoring-guide.md +108 -0
  66. package/Docs/policies/product-owner-guide.md +43 -0
  67. package/Docs/policies/technical-owner-guide.md +37 -0
  68. package/Docs/product-owner-guide.md +327 -0
  69. package/Docs/project-portfolio-orchestration-guide.md +199 -0
  70. package/Docs/quality/manual-qa-and-acceptance.md +162 -0
  71. package/Docs/quality/persona-driven-test-scenarios.md +172 -0
  72. package/Docs/quality/reproducible-archaeology-depth-review-checklist.md +89 -0
  73. package/Docs/reference/capabilities-and-project-layout.md +678 -0
  74. package/Docs/reference/cli-and-configuration.md +398 -0
  75. package/Docs/reference/contributions-api.md +23 -0
  76. package/Docs/reference/security-adapter-authoring.md +81 -0
  77. package/Docs/reference/starter-adapter-authoring.md +74 -0
  78. package/Docs/repository-source-map-guide.md +381 -0
  79. package/Docs/reproducible-archaeology-and-discovery-depth.md +292 -0
  80. package/Docs/screen-prototype-creation-guide.md +324 -0
  81. package/Docs/security-validation-guide.md +353 -0
  82. package/Docs/solution-readiness-review-guide.md +123 -0
  83. package/Docs/standards/project-standards-authoring.md +157 -0
  84. package/Docs/team-hub-guide.md +162 -0
  85. package/Docs/team-hub-resource-registry-guide.md +167 -0
  86. package/Docs/tutorials/first-delivery.md +83 -0
  87. package/Docs/tutorials/first-session.md +62 -0
  88. package/Docs/using-lifecycle-hooks.md +381 -0
  89. package/Docs/working-with-personas.md +274 -0
  90. package/LICENSE +165 -0
  91. package/README.md +96 -0
  92. package/agents-src/claude/ewai-security-reviewer.md +15 -0
  93. package/bin/ewai +5 -0
  94. package/config/archaeology-record-families.yaml +59 -0
  95. package/config/delivery-artifacts.yaml +121 -0
  96. package/config/delivery-stages.yaml +77 -0
  97. package/config/design-system.schema.json +46 -0
  98. package/config/error-reporting.schema.json +79 -0
  99. package/config/evidence-depth.schema.json +53 -0
  100. package/config/intent.schema.json +90 -0
  101. package/config/knowledge-proposals-proposal.schema.json +34 -0
  102. package/config/lifecycle-event.schema.json +68 -0
  103. package/config/lifecycle-handler.schema.json +45 -0
  104. package/config/lifecycle-hook-ack.schema.json +19 -0
  105. package/config/meeting-evidence-candidate.schema.json +102 -0
  106. package/config/organisation-policy.schema.json +137 -0
  107. package/config/pack.schema.json +250 -0
  108. package/config/persona-pack.schema.json +21 -0
  109. package/config/persona.schema.json +17 -0
  110. package/config/policy-evaluation.schema.json +77 -0
  111. package/config/policy-facts.schema.json +140 -0
  112. package/config/portfolio.schema.json +68 -0
  113. package/config/project.schema.json +313 -0
  114. package/config/prototype-iteration.schema.json +128 -0
  115. package/config/rollout.schema.json +87 -0
  116. package/config/security-adapter.schema.json +31 -0
  117. package/config/security-scan-request.schema.json +64 -0
  118. package/config/security-scan-response.schema.json +52 -0
  119. package/config/security-validation-policy.schema.json +74 -0
  120. package/config/starter-source-acknowledgement.schema.json +13 -0
  121. package/config/starter-source-adapter.schema.json +38 -0
  122. package/config/starter-source-request.schema.json +61 -0
  123. package/package.json +77 -0
  124. package/packs/core/pack.yaml +7 -0
  125. package/packs/design-systems/default/experience-promise.md +9 -0
  126. package/packs/design-systems/default/intentional-review.md +10 -0
  127. package/packs/design-systems/default/interaction-and-entry.md +9 -0
  128. package/packs/design-systems/default/meaningful-content-and-states.md +9 -0
  129. package/packs/design-systems/default/pack.yaml +44 -0
  130. package/packs/design-systems/default/principles.md +10 -0
  131. package/packs/personas/core/pack.yaml +7 -0
  132. package/packs/personas/core/personas/archaeologist.md +37 -0
  133. package/packs/personas/core/personas/end-user.md +17 -0
  134. package/packs/personas/core/personas/maintainer.md +17 -0
  135. package/packs/personas/core/personas/operator.md +17 -0
  136. package/packs/personas/core/personas/specs-knowledge-curator.md +35 -0
  137. package/packs/technologies/laravel/pack.yaml +30 -0
  138. package/packs/technologies/laravel-nuxt/pack.yaml +30 -0
  139. package/packs/technologies/nuxt/pack.yaml +30 -0
  140. package/packs/technologies/power-platform/pack.yaml +31 -0
  141. package/packs/technologies/salesforce/pack.yaml +25 -0
  142. package/public/app.js +4896 -0
  143. package/public/apple-touch-icon.png +0 -0
  144. package/public/assets/backstory-icon.png +0 -0
  145. package/public/dashboard-navigation.js +98 -0
  146. package/public/favicon-16.png +0 -0
  147. package/public/favicon-32.png +0 -0
  148. package/public/favicon.ico +0 -0
  149. package/public/index.html +789 -0
  150. package/public/styles.css +2693 -0
  151. package/public/team-hub/app.js +202 -0
  152. package/public/team-hub/index.html +79 -0
  153. package/public/team-hub/styles.css +90 -0
  154. package/scripts/publication-check.mjs +140 -0
  155. package/scripts/setup.mjs +21 -0
  156. package/skills-src/ewai-archaeology/SKILL.md +334 -0
  157. package/skills-src/ewai-archaeology/agents/openai.yaml +4 -0
  158. package/skills-src/ewai-archaeology/references/archaeology-contract.md +201 -0
  159. package/skills-src/ewai-archaeology/references/lifecycle-reconstruction.md +177 -0
  160. package/skills-src/ewai-archaeology/references/maximum-detail-reconstruction.md +97 -0
  161. package/skills-src/ewai-archaeology/references/model-routing.md +26 -0
  162. package/skills-src/ewai-architecture/SKILL.md +108 -0
  163. package/skills-src/ewai-architecture/agents/openai.yaml +4 -0
  164. package/skills-src/ewai-architecture/references/architecture-contract.md +176 -0
  165. package/skills-src/ewai-context/SKILL.md +68 -0
  166. package/skills-src/ewai-context/agents/openai.yaml +4 -0
  167. package/skills-src/ewai-context-import/SKILL.md +118 -0
  168. package/skills-src/ewai-context-import/agents/openai.yaml +4 -0
  169. package/skills-src/ewai-context-import/references/context-import-contract.md +106 -0
  170. package/skills-src/ewai-dashboard-configuration/SKILL.md +20 -0
  171. package/skills-src/ewai-deliver/SKILL.md +122 -0
  172. package/skills-src/ewai-deliver/references/delivery-evidence.md +92 -0
  173. package/skills-src/ewai-deliver/references/phase-routing.md +31 -0
  174. package/skills-src/ewai-design-system-apply/SKILL.md +27 -0
  175. package/skills-src/ewai-design-system-apply/agents/openai.yaml +4 -0
  176. package/skills-src/ewai-design-system-apply/references/application-contract.md +36 -0
  177. package/skills-src/ewai-design-system-author/SKILL.md +28 -0
  178. package/skills-src/ewai-design-system-author/agents/openai.yaml +4 -0
  179. package/skills-src/ewai-design-system-author/references/authoring-contract.md +38 -0
  180. package/skills-src/ewai-design-system-review/SKILL.md +26 -0
  181. package/skills-src/ewai-design-system-review/agents/openai.yaml +4 -0
  182. package/skills-src/ewai-design-system-review/references/review-contract.md +40 -0
  183. package/skills-src/ewai-error-reporting/SKILL.md +46 -0
  184. package/skills-src/ewai-error-reporting/agents/openai.yaml +4 -0
  185. package/skills-src/ewai-error-reporting/references/provider-contract.md +74 -0
  186. package/skills-src/ewai-evidence-depth/SKILL.md +72 -0
  187. package/skills-src/ewai-evidence-depth/agents/openai.yaml +4 -0
  188. package/skills-src/ewai-evidence-depth/references/evidence-depth-contract.md +127 -0
  189. package/skills-src/ewai-intent/SKILL.md +68 -0
  190. package/skills-src/ewai-intent/agents/openai.yaml +4 -0
  191. package/skills-src/ewai-intent/references/intent-contract.md +42 -0
  192. package/skills-src/ewai-knowledge-proposals/SKILL.md +105 -0
  193. package/skills-src/ewai-knowledge-proposals/agents/openai.yaml +4 -0
  194. package/skills-src/ewai-knowledge-proposals/references/proposal-contract.md +59 -0
  195. package/skills-src/ewai-meeting-evidence/SKILL.md +106 -0
  196. package/skills-src/ewai-meeting-evidence/agents/openai.yaml +4 -0
  197. package/skills-src/ewai-meeting-evidence/references/candidate-contract.md +64 -0
  198. package/skills-src/ewai-organisation-policy/SKILL.md +62 -0
  199. package/skills-src/ewai-organisation-policy/agents/openai.yaml +4 -0
  200. package/skills-src/ewai-organisation-policy/references/policy-contract.md +94 -0
  201. package/skills-src/ewai-palace-housekeeping/SKILL.md +55 -0
  202. package/skills-src/ewai-palace-housekeeping/agents/openai.yaml +4 -0
  203. package/skills-src/ewai-persona-entitlement/SKILL.md +60 -0
  204. package/skills-src/ewai-persona-entitlement/agents/openai.yaml +4 -0
  205. package/skills-src/ewai-phase-evidence/SKILL.md +79 -0
  206. package/skills-src/ewai-phase-evidence/agents/openai.yaml +4 -0
  207. package/skills-src/ewai-pipeline/SKILL.md +130 -0
  208. package/skills-src/ewai-pipeline/agents/openai.yaml +4 -0
  209. package/skills-src/ewai-pipeline/references/cli.md +86 -0
  210. package/skills-src/ewai-pipeline/references/specs-contract.md +16 -0
  211. package/skills-src/ewai-portfolio/SKILL.md +70 -0
  212. package/skills-src/ewai-portfolio/agents/openai.yaml +4 -0
  213. package/skills-src/ewai-portfolio/references/portfolio-contract.md +67 -0
  214. package/skills-src/ewai-project-discovery/SKILL.md +95 -0
  215. package/skills-src/ewai-project-discovery/agents/openai.yaml +4 -0
  216. package/skills-src/ewai-project-discovery/references/discovery-contract.md +34 -0
  217. package/skills-src/ewai-prototype-iteration/SKILL.md +30 -0
  218. package/skills-src/ewai-prototype-iteration/agents/openai.yaml +4 -0
  219. package/skills-src/ewai-prototype-iteration/references/review-contract.md +49 -0
  220. package/skills-src/ewai-retro/SKILL.md +48 -0
  221. package/skills-src/ewai-retro/agents/openai.yaml +4 -0
  222. package/skills-src/ewai-retro/references/asset-routing.md +14 -0
  223. package/skills-src/ewai-rollout/SKILL.md +74 -0
  224. package/skills-src/ewai-rollout/agents/openai.yaml +4 -0
  225. package/skills-src/ewai-rollout/references/rollout-contract.md +74 -0
  226. package/skills-src/ewai-shape-intents/SKILL.md +84 -0
  227. package/skills-src/ewai-shape-intents/agents/openai.yaml +4 -0
  228. package/skills-src/ewai-shape-intents/references/intent-mapping-contract.md +109 -0
  229. package/skills-src/ewai-solution-readiness/SKILL.md +55 -0
  230. package/skills-src/ewai-solution-readiness/agents/openai.yaml +4 -0
  231. package/skills-src/ewai-standards-check/SKILL.md +93 -0
  232. package/skills-src/ewai-standards-check/agents/openai.yaml +4 -0
  233. package/skills-src/ewai-standards-check/references/report-contract.md +116 -0
  234. package/skills-src/ewai-test-scenarios/SKILL.md +94 -0
  235. package/skills-src/ewai-test-scenarios/agents/openai.yaml +4 -0
  236. package/skills-src/ewai-test-scenarios/references/scenario-contract.md +88 -0
  237. package/src/afk-worker.mjs +16 -0
  238. package/src/archaeology.mjs +1333 -0
  239. package/src/checkin.mjs +261 -0
  240. package/src/cli.mjs +2427 -0
  241. package/src/companion-guidance.mjs +257 -0
  242. package/src/companion-opening.mjs +62 -0
  243. package/src/companion.mjs +256 -0
  244. package/src/context.mjs +210 -0
  245. package/src/dashboard-preferences.mjs +80 -0
  246. package/src/delivery-artifacts.mjs +204 -0
  247. package/src/delivery-documents.mjs +248 -0
  248. package/src/delivery-gates.mjs +317 -0
  249. package/src/delivery.mjs +1433 -0
  250. package/src/design-system-application.mjs +291 -0
  251. package/src/design-system-authoring.mjs +101 -0
  252. package/src/design-systems.mjs +466 -0
  253. package/src/discovery.mjs +1314 -0
  254. package/src/error-reporting.mjs +323 -0
  255. package/src/evidence-depth.mjs +543 -0
  256. package/src/execution-state.mjs +243 -0
  257. package/src/install.mjs +166 -0
  258. package/src/intent-dependencies.mjs +117 -0
  259. package/src/intent-maps.mjs +402 -0
  260. package/src/intents.mjs +747 -0
  261. package/src/knowledge-proposals.mjs +717 -0
  262. package/src/launcher.mjs +51 -0
  263. package/src/meeting-evidence.mjs +703 -0
  264. package/src/network-rollout.mjs +386 -0
  265. package/src/organisation-blueprints.mjs +438 -0
  266. package/src/organisation-policies.mjs +448 -0
  267. package/src/packs.mjs +44 -0
  268. package/src/paths.mjs +62 -0
  269. package/src/persona-entitlements.mjs +438 -0
  270. package/src/persona-licence-config.mjs +98 -0
  271. package/src/persona-website-provider.mjs +134 -0
  272. package/src/persona-zip.mjs +87 -0
  273. package/src/personas.mjs +159 -0
  274. package/src/platform-metadata-analysis.mjs +314 -0
  275. package/src/policy-design-gates.mjs +623 -0
  276. package/src/policy-gate-integration.mjs +318 -0
  277. package/src/portfolio.mjs +509 -0
  278. package/src/power-platform-source-map.mjs +190 -0
  279. package/src/project.mjs +449 -0
  280. package/src/prototype-iterations.mjs +730 -0
  281. package/src/repository-source-map.mjs +603 -0
  282. package/src/runtime/afk-conductor.mjs +973 -0
  283. package/src/runtime/context-assembly.mjs +457 -0
  284. package/src/runtime/context-benchmarks.mjs +115 -0
  285. package/src/runtime/dashboard-actions.mjs +109 -0
  286. package/src/runtime/dashboard-handoffs.mjs +149 -0
  287. package/src/runtime/dashboard-server.mjs +1272 -0
  288. package/src/runtime/dashboard.mjs +197 -0
  289. package/src/runtime/database.mjs +789 -0
  290. package/src/runtime/error-reporting.mjs +581 -0
  291. package/src/runtime/evidence-depth-workspace.mjs +412 -0
  292. package/src/runtime/execution-leases.mjs +299 -0
  293. package/src/runtime/guided-discovery.mjs +350 -0
  294. package/src/runtime/guided-intents.mjs +517 -0
  295. package/src/runtime/impact-analysis.mjs +535 -0
  296. package/src/runtime/intents.mjs +222 -0
  297. package/src/runtime/knowledge.mjs +86 -0
  298. package/src/runtime/lifecycle-hooks.mjs +1239 -0
  299. package/src/runtime/mcp-config.mjs +110 -0
  300. package/src/runtime/mcp-server.mjs +1885 -0
  301. package/src/runtime/palace.mjs +362 -0
  302. package/src/runtime/paths.mjs +58 -0
  303. package/src/runtime/persona-engagement.mjs +255 -0
  304. package/src/runtime/phase-contributions.mjs +594 -0
  305. package/src/runtime/policy-workspace.mjs +170 -0
  306. package/src/runtime/prototype-iterations.mjs +235 -0
  307. package/src/runtime/provider-adapters.mjs +163 -0
  308. package/src/runtime/repository-index.mjs +838 -0
  309. package/src/runtime/runs.mjs +185 -0
  310. package/src/runtime/security-validation.mjs +1230 -0
  311. package/src/runtime/starter-materialisation.mjs +1155 -0
  312. package/src/runtime/team-hub-client.mjs +479 -0
  313. package/src/runtime/team-hub-database.mjs +288 -0
  314. package/src/runtime/team-hub-server.mjs +191 -0
  315. package/src/runtime/team-hub.mjs +110 -0
  316. package/src/runtime/tree-sitter-index.mjs +390 -0
  317. package/src/runtime/version.mjs +1 -0
  318. package/src/runtime/work.mjs +633 -0
  319. package/src/salesforce-source-map.mjs +212 -0
  320. package/src/security-validation-config.mjs +224 -0
  321. package/src/solution-readiness.mjs +620 -0
  322. package/src/starter-materialisation-contract.mjs +407 -0
  323. package/src/task-graph.mjs +544 -0
  324. package/src/team-hub-resources.mjs +239 -0
  325. package/src/team-hub.mjs +242 -0
  326. package/src/test-scenarios.mjs +622 -0
  327. package/src/validation-config.mjs +289 -0
  328. package/templates/SPECS/1.Scope/personas/registry.yaml +12 -0
  329. package/templates/SPECS/5.Strategy/patterns/context-packet.md +119 -0
  330. package/templates/SPECS/6.Build/_tracker-template.md +16 -0
  331. package/templates/SPECS/pipeline.yaml +62 -0
  332. package/templates/discovery-answers.yaml +86 -0
  333. package/templates/intent-body.md +21 -0
  334. package/tests/fixtures/context-benchmarks.json +9 -0
@@ -0,0 +1,291 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
3
+ import { basename, isAbsolute, relative, resolve } from 'node:path';
4
+ import {
5
+ DEFAULT_DESIGN_SYSTEM_ID,
6
+ designSystemStatus,
7
+ listDesignSystems,
8
+ resolveDesignSystem,
9
+ } from './design-systems.mjs';
10
+ import { projectPaths } from './paths.mjs';
11
+ import { loadProjectConfig } from './project.mjs';
12
+ import { prepareContextPack } from './runtime/context-assembly.mjs';
13
+
14
+ const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
15
+ const sha256Pattern = /^sha256:[a-f0-9]{64}$/;
16
+
17
+ function clean(value) {
18
+ return String(value ?? '').trim();
19
+ }
20
+
21
+ function canonical(value) {
22
+ if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`;
23
+ if (value && typeof value === 'object') {
24
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(',')}}`;
25
+ }
26
+ return JSON.stringify(value);
27
+ }
28
+
29
+ function digest(value) {
30
+ return `sha256:${createHash('sha256').update(value).digest('hex')}`;
31
+ }
32
+
33
+ function within(root, candidate) {
34
+ const path = relative(root, candidate);
35
+ return path === '' || (!path.startsWith('..') && !isAbsolute(path));
36
+ }
37
+
38
+ function safeEvidenceDirectory(deliveryRoot, segments, options = {}) {
39
+ const root = resolve(deliveryRoot);
40
+ const realRoot = realpathSync(root);
41
+ let current = root;
42
+ for (const segment of segments) {
43
+ current = resolve(current, segment);
44
+ if (existsSync(current)) {
45
+ const metadata = lstatSync(current);
46
+ if (metadata.isSymbolicLink()) throw new Error(`Design-system evidence directory may not be a symbolic link: ${segment}`);
47
+ if (!metadata.isDirectory()) throw new Error(`Design-system evidence path must be a directory: ${segment}`);
48
+ } else if (options.create) mkdirSync(current, { recursive: false, mode: 0o700 });
49
+ else throw new Error(`Design-system evidence directory does not exist: ${segment}`);
50
+ if (!within(realRoot, realpathSync(current))) throw new Error(`Design-system evidence directory may not escape its delivery: ${segment}`);
51
+ }
52
+ return current;
53
+ }
54
+
55
+ function deliveryWorkspace(projectRoot, slug) {
56
+ const value = clean(slug);
57
+ if (!slugPattern.test(value)) throw new Error('Design-system application requires a lower kebab-case delivery slug');
58
+ const project = projectPaths(projectRoot);
59
+ const root = resolve(project.buildRoot, value);
60
+ const statePath = resolve(root, 'delivery-state.json');
61
+ if (!existsSync(statePath)) throw new Error(`Design-system application requires an existing delivery: ${value}`);
62
+ const state = JSON.parse(readFileSync(statePath, 'utf8'));
63
+ if (state.schema !== 'ewai.delivery-state/v1' || state.slug !== value) throw new Error(`Invalid delivery state for design-system application: ${value}`);
64
+ const ui = (state.adjuncts ?? []).find(({ id }) => id === 'ui-design');
65
+ if (!ui || ui.status === 'skipped') throw new Error('Design-system application is available only for a UI-bearing delivery');
66
+ return { project, root, state };
67
+ }
68
+
69
+ function resolvedSelection(projectRoot, options = {}) {
70
+ const status = designSystemStatus(projectRoot, options);
71
+ if (status.status !== 'ready') throw new Error(`Project design system is ${status.status}; re-resolve and select it before application`);
72
+ const { config } = loadProjectConfig(projectRoot);
73
+ const catalogue = listDesignSystems({ ...options, projectRoot });
74
+ const resolved = resolveDesignSystem(config.design_system?.root?.id ?? DEFAULT_DESIGN_SYSTEM_ID, catalogue);
75
+ return { status, resolved };
76
+ }
77
+
78
+ function focusSignals(focus) {
79
+ return new Set(['ui-design', 'prototype', ...clean(focus).toLowerCase().split(/[^a-z0-9-]+/).filter(Boolean)]);
80
+ }
81
+
82
+ function contributionCandidate(record, focus) {
83
+ const signals = focusSignals(focus);
84
+ const relevant = [record.kind, record.title, ...record.applicability]
85
+ .flatMap((value) => clean(value).toLowerCase().split(/[^a-z0-9-]+/))
86
+ .some((value) => signals.has(value));
87
+ const evidenceClass = record.required ? 'mandatory' : relevant ? 'relevant' : 'optional';
88
+ const requiredMarkers = record.required ? [record.content.slice(0, 48).trim()].filter(Boolean) : [];
89
+ return {
90
+ id: record.qualifiedId.replaceAll('.', '-').replace(':', '--'),
91
+ qualifiedId: record.qualifiedId,
92
+ label: record.title,
93
+ evidenceClass,
94
+ priority: record.required ? 900 : relevant ? 100 : 10,
95
+ sourcePath: `design-system/${record.packId}/${record.source}`,
96
+ sourceDigest: record.contentDigest.replace(/^sha256:/, ''),
97
+ content: record.content,
98
+ requiredMarkers,
99
+ freshness: 'current',
100
+ selectionReason: record.required
101
+ ? `${record.qualifiedId} is mandatory for this design-system application.`
102
+ : relevant
103
+ ? `${record.qualifiedId} matches the UI scope or current focus.`
104
+ : `${record.qualifiedId} is optional background outside the current focus.`,
105
+ };
106
+ }
107
+
108
+ function authorityCandidate() {
109
+ return {
110
+ id: 'design-system-authority', label: 'Design-system authority boundary', evidenceClass: 'mandatory', priority: 1_000,
111
+ sourcePath: 'Docs/human-approval-and-assurance-guide.md',
112
+ content: 'AUTHORITY_NONE Design-system application is advisory and cannot approve Build or Manual QA, certify accessibility, accept a deviation, publish, deploy, or release.',
113
+ requiredMarkers: ['AUTHORITY_NONE', 'cannot approve Build or Manual QA', 'publish, deploy, or release'],
114
+ selectionReason: 'Every application retains explicit human design, assurance, and release authority.',
115
+ };
116
+ }
117
+
118
+ function safePersona(persona) {
119
+ return {
120
+ ref: persona.id,
121
+ name: persona.name,
122
+ tier: persona.tier,
123
+ category: persona.category,
124
+ matchedSignals: [...persona.matchedSignals],
125
+ reason: persona.engagementReason,
126
+ };
127
+ }
128
+
129
+ function receiptDigest(receipt) {
130
+ const safe = structuredClone(receipt);
131
+ delete safe.digest;
132
+ return digest(canonical(safe));
133
+ }
134
+
135
+ function buildReceipt(slug, status, resolved, context, now) {
136
+ const bySegment = new Map(context.segments.map((segment) => [segment.id, segment]));
137
+ const contributions = resolved.contributions.map((record) => {
138
+ const candidate = contributionCandidate(record, context.focus);
139
+ const segment = bySegment.get(candidate.id);
140
+ return {
141
+ id: record.qualifiedId,
142
+ kind: record.kind,
143
+ contentDigest: record.contentDigest,
144
+ disposition: segment?.disposition ?? 'deferred',
145
+ reason: segment?.selectionReason ?? candidate.selectionReason,
146
+ freshness: segment?.freshness ?? 'current',
147
+ };
148
+ });
149
+ const receipt = {
150
+ schema: 'ewai.design-system-receipt/v1',
151
+ status: 'applied',
152
+ delivery: slug,
153
+ createdAt: now,
154
+ designSystem: {
155
+ mode: status.mode,
156
+ approved: status.approved,
157
+ root: { id: resolved.root.id, version: resolved.root.version, digest: resolved.root.digest, sourceClass: resolved.root.sourceClass },
158
+ packs: resolved.packs.map((pack) => ({ id: pack.id, version: pack.version, digest: pack.digest, sourceClass: pack.sourceClass })),
159
+ effectiveDigest: resolved.effectiveDigest,
160
+ notice: status.notice,
161
+ },
162
+ context: {
163
+ digest: context.digest,
164
+ status: context.status,
165
+ profile: context.profile,
166
+ focus: context.focus,
167
+ budget: { ...context.budget },
168
+ selectedContributions: contributions.filter(({ disposition }) => ['selected', 'reused'].includes(disposition)),
169
+ deferredContributions: contributions.filter(({ disposition }) => !['selected', 'reused'].includes(disposition)),
170
+ overflow: context.reason === 'mandatory-overflow',
171
+ recovery: [...context.recovery],
172
+ sourceFreshness: 'current-at-application',
173
+ },
174
+ activePersonas: context.activePersonas.map(safePersona),
175
+ authority: {
176
+ level: 'none',
177
+ notices: [
178
+ 'This receipt proves the context prepared for this delivery; it cannot approve Build or Manual QA.',
179
+ 'Visual quality, accessibility, intentional deviations, publication, deployment, and release require separate accountable evidence.',
180
+ ],
181
+ },
182
+ };
183
+ return { ...receipt, digest: receiptDigest(receipt) };
184
+ }
185
+
186
+ export function prepareDesignSystemApplication(projectRoot, slug, options = {}) {
187
+ deliveryWorkspace(projectRoot, slug);
188
+ const focus = clean(options.focus);
189
+ if (focus.length > 2_000) throw new Error('Design-system application focus must be 2,000 characters or fewer');
190
+ const { status, resolved } = resolvedSelection(projectRoot, options);
191
+ const candidates = [authorityCandidate(), ...resolved.contributions.map((record) => contributionCandidate(record, focus))];
192
+ const context = prepareContextPack({
193
+ profile: 'design-system-apply',
194
+ focus,
195
+ budgetTokens: options.budgetTokens,
196
+ repositoryRevision: options.repositoryRevision ?? resolved.effectiveDigest,
197
+ deliveryRevision: options.deliveryRevision ?? 'workspace',
198
+ candidates,
199
+ personaCatalogue: options.personaCatalogue,
200
+ personaLimit: options.personaLimit ?? 5,
201
+ cacheRoot: resolve(projectPaths(projectRoot).runtimeRoot, 'runtime/context-packs/cache'),
202
+ });
203
+ const designSystem = { mode: status.mode, approved: status.approved, effectiveDigest: resolved.effectiveDigest, notice: status.notice };
204
+ if (context.status !== 'ready') {
205
+ return { status: context.status, reason: context.reason, designSystem, context, receipt: null, modelContext: null, recovery: [...context.recovery] };
206
+ }
207
+ const receipt = buildReceipt(slug, status, resolved, context, options.now ?? new Date().toISOString());
208
+ return { status: 'ready', reason: context.reason, designSystem, context, receipt, modelContext: context.modelContext, recovery: [] };
209
+ }
210
+
211
+ function summary(receipt) {
212
+ const selected = receipt.context.selectedContributions.map(({ id, reason }) => `- \`${id}\` — ${reason}`).join('\n') || '- None';
213
+ const deferred = receipt.context.deferredContributions.map(({ id, reason }) => `- \`${id}\` — ${reason}`).join('\n') || '- None';
214
+ const personas = receipt.activePersonas.map(({ ref, name, tier, reason }) => `- \`${ref}\` — ${name} (${tier}): ${reason}`).join('\n') || '- No contextual personas were engaged.';
215
+ return `# Design-system application\n\n` +
216
+ `**Delivery:** \`${receipt.delivery}\`\n` +
217
+ `**Design-system root:** \`${receipt.designSystem.root.id}\`\n` +
218
+ `**Mode:** ${receipt.designSystem.mode}${receipt.designSystem.approved ? ' (selected)' : ' (unapproved fallback)'}\n` +
219
+ `**Effective digest:** \`${receipt.designSystem.effectiveDigest}\`\n` +
220
+ `**Receipt digest:** \`${receipt.digest}\`\n\n` +
221
+ `## Selected contributions\n\n${selected}\n\n## Deferred contributions\n\n${deferred}\n\n` +
222
+ `## Active personas\n\n${personas}\n\n## Authority\n\n${receipt.authority.notices.map((notice) => `- ${notice}`).join('\n')}\n`;
223
+ }
224
+
225
+ export function writeDesignSystemReceipt(deliveryRoot, receipt) {
226
+ if (receipt?.schema !== 'ewai.design-system-receipt/v1' || receiptDigest(receipt) !== receipt.digest) {
227
+ throw new Error('Design-system receipt is invalid or its digest does not match');
228
+ }
229
+ const root = resolve(deliveryRoot);
230
+ const folder = safeEvidenceDirectory(root, ['ui-design-assets', 'design-system'], { create: true });
231
+ const suffix = receipt.digest.replace('sha256:', '');
232
+ const receiptPath = `ui-design-assets/design-system/receipt-${suffix}.json`;
233
+ const summaryPath = `ui-design-assets/design-system/summary-${suffix}.md`;
234
+ const receiptContent = `${JSON.stringify(receipt, null, 2)}\n`;
235
+ const summaryContent = summary(receipt);
236
+ const writes = [[resolve(root, receiptPath), receiptContent], [resolve(root, summaryPath), summaryContent]];
237
+ for (const [path, content] of writes) {
238
+ if (existsSync(path)) {
239
+ if (readFileSync(path, 'utf8') !== content) throw new Error(`Immutable design-system evidence already exists with different content: ${basename(path)}`);
240
+ }
241
+ }
242
+ const created = [];
243
+ try {
244
+ for (const [path, content] of writes) {
245
+ if (existsSync(path)) continue;
246
+ writeFileSync(path, content, { encoding: 'utf8', flag: 'wx', mode: 0o600 });
247
+ created.push(path);
248
+ }
249
+ } catch (error) {
250
+ for (const path of created.reverse()) unlinkSync(path);
251
+ throw error;
252
+ }
253
+ return { receiptPath, summaryPath, receiptDigest: receipt.digest, effectiveDigest: receipt.designSystem.effectiveDigest };
254
+ }
255
+
256
+ export function validateDesignSystemReceipt(deliveryRoot, receiptPath) {
257
+ const root = resolve(deliveryRoot);
258
+ const folder = safeEvidenceDirectory(root, ['ui-design-assets', 'design-system']);
259
+ const candidate = resolve(root, clean(receiptPath));
260
+ if (!clean(receiptPath) || isAbsolute(clean(receiptPath)) || !within(folder, candidate) || candidate === folder) {
261
+ throw new Error('Design-system receipt path must identify a file inside ui-design-assets/design-system');
262
+ }
263
+ if (!existsSync(candidate) || !statSync(candidate).isFile()) throw new Error(`Design-system receipt does not exist: ${receiptPath}`);
264
+ if (lstatSync(candidate).isSymbolicLink()) throw new Error('Design-system receipt may not be a symbolic link');
265
+ if (!within(realpathSync(folder), realpathSync(candidate))) throw new Error('Design-system receipt may not escape its folder');
266
+ let receipt;
267
+ try {
268
+ receipt = JSON.parse(readFileSync(candidate, 'utf8'));
269
+ } catch (error) {
270
+ throw new Error(`Design-system receipt is not valid JSON: ${error.message}`);
271
+ }
272
+ const errors = [];
273
+ if (receipt.schema !== 'ewai.design-system-receipt/v1') errors.push('schema must be ewai.design-system-receipt/v1');
274
+ if (receipt.status !== 'applied') errors.push('status must be applied');
275
+ if (!slugPattern.test(clean(receipt.delivery))) errors.push('delivery must be lower kebab-case');
276
+ if (!sha256Pattern.test(clean(receipt.digest))) errors.push('digest must be SHA-256');
277
+ if (!sha256Pattern.test(clean(receipt.designSystem?.effectiveDigest))) errors.push('designSystem.effectiveDigest must be SHA-256');
278
+ if (receipt.context?.status !== 'ready' || receipt.context?.overflow) errors.push('context must be ready without overflow');
279
+ if (receipt.context?.sourceFreshness !== 'current-at-application') errors.push('source freshness is not recorded');
280
+ if (receipt.authority?.level !== 'none') errors.push('authority level must be none');
281
+ if (sha256Pattern.test(clean(receipt.digest)) && receiptDigest(receipt) !== receipt.digest) errors.push('receipt digest does not match its content');
282
+ if (errors.length) throw new Error(`Design-system receipt is invalid:\n- ${errors.join('\n- ')}`);
283
+ return receipt;
284
+ }
285
+
286
+ export function applyDesignSystem(projectRoot, slug, options = {}) {
287
+ const prepared = prepareDesignSystemApplication(projectRoot, slug, options);
288
+ if (prepared.status !== 'ready' || !prepared.receipt) return prepared;
289
+ const { root } = deliveryWorkspace(projectRoot, slug);
290
+ return { schema: 'ewai.design-system-application/v1', status: 'applied', ...writeDesignSystemReceipt(root, prepared.receipt), receipt: prepared.receipt };
291
+ }
@@ -0,0 +1,101 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { copyFileSync, existsSync, mkdirSync, realpathSync, renameSync, rmSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { basename, dirname, resolve } from 'node:path';
5
+ import { listDesignSystems, parseDesignSystemPack, projectDesignSystem } from './design-systems.mjs';
6
+
7
+ const scopes = new Set(['project', 'personal']);
8
+
9
+ function candidatePack(folder, options = {}) {
10
+ const candidateRoot = realpathSync(resolve(folder));
11
+ const manifestPath = resolve(candidateRoot, 'pack.yaml');
12
+ if (!existsSync(manifestPath)) throw new Error('Design-system candidate must contain pack.yaml at its root');
13
+ const pack = parseDesignSystemPack(manifestPath, 'candidate', options);
14
+ if (!pack) throw new Error('Candidate pack.yaml is not a design-system pack');
15
+ return { candidateRoot, manifestPath, pack };
16
+ }
17
+
18
+ function safeCandidate(pack) {
19
+ return { schema: 'ewai.design-system-candidate/v1', ...projectDesignSystem(pack), digest: pack.digest };
20
+ }
21
+
22
+ export function validateDesignSystemCandidate(folder, options = {}) {
23
+ return safeCandidate(candidatePack(folder, options).pack);
24
+ }
25
+
26
+ function installationDestination(projectRoot, pack, scope, options = {}) {
27
+ if (!scopes.has(scope)) throw new Error(`Unsupported design-system installation scope: ${scope}`);
28
+ const parent = scope === 'project'
29
+ ? resolve(projectRoot, '.ewai-pipeline/packs')
30
+ : resolve(options.home ?? homedir(), '.ewai/packs');
31
+ return { parent, destination: resolve(parent, pack.id.replaceAll('.', '-')) };
32
+ }
33
+
34
+ export function previewDesignSystemInstallation(projectRoot, folder, options = {}) {
35
+ const scope = String(options.scope ?? '').trim();
36
+ const { candidateRoot, pack } = candidatePack(folder, options);
37
+ const { destination } = installationDestination(projectRoot, pack, scope, options);
38
+ const catalogue = listDesignSystems({ projectRoot, ...(options.home ? { home: options.home } : {}) });
39
+ const identityConflict = catalogue.find(({ id }) => id === pack.id);
40
+ const destinationConflict = existsSync(destination);
41
+ return {
42
+ schema: 'ewai.design-system-installation-preview/v1',
43
+ status: identityConflict || destinationConflict ? 'conflict' : 'ready',
44
+ id: pack.id,
45
+ name: pack.name,
46
+ version: pack.version,
47
+ digest: pack.digest,
48
+ scope,
49
+ source: candidateRoot,
50
+ destination,
51
+ conflict: Boolean(identityConflict || destinationConflict),
52
+ conflicts: [
53
+ ...(identityConflict ? [{ kind: 'installed-id', id: identityConflict.id, version: identityConflict.version, sourceClass: identityConflict.sourceClass }] : []),
54
+ ...(destinationConflict ? [{ kind: 'destination-exists' }] : []),
55
+ ],
56
+ dependencies: [...pack.requires],
57
+ contributions: projectDesignSystem(pack).contributions,
58
+ notice: 'Installation creates a new pack only. It does not select the pack for this project.',
59
+ };
60
+ }
61
+
62
+ function copyCandidateFiles(candidateRoot, pack, stage) {
63
+ mkdirSync(stage, { recursive: false });
64
+ copyFileSync(resolve(candidateRoot, 'pack.yaml'), resolve(stage, 'pack.yaml'));
65
+ for (const source of [...new Set(pack.design_system.contributions.map(({ source: path }) => path))].sort()) {
66
+ const destination = resolve(stage, source);
67
+ mkdirSync(dirname(destination), { recursive: true });
68
+ copyFileSync(resolve(candidateRoot, source), destination);
69
+ }
70
+ }
71
+
72
+ export function installDesignSystemCandidate(projectRoot, folder, options = {}) {
73
+ if (options.confirmed !== true) throw new Error('Design-system installation requires explicit confirmation');
74
+ const expectedDigest = String(options.expectedDigest ?? '').trim();
75
+ if (!/^sha256:[a-f0-9]{64}$/.test(expectedDigest)) throw new Error('Design-system installation requires an exact expected digest');
76
+ const scope = String(options.scope ?? '').trim();
77
+ const { candidateRoot, pack } = candidatePack(folder, options);
78
+ if (pack.digest !== expectedDigest) throw new Error(`Design-system candidate digest drift: expected ${expectedDigest}, resolved ${pack.digest}`);
79
+ const preview = previewDesignSystemInstallation(projectRoot, candidateRoot, { ...options, scope });
80
+ if (preview.conflict) throw new Error(`Design system ${pack.id}@${pack.version} is already installed or its destination already exists`);
81
+ const parent = dirname(preview.destination);
82
+ mkdirSync(parent, { recursive: true });
83
+ const stage = resolve(parent, `.${basename(preview.destination)}.stage-${process.pid}-${randomUUID()}`);
84
+ try {
85
+ copyCandidateFiles(candidateRoot, pack, stage);
86
+ const staged = parseDesignSystemPack(resolve(stage, 'pack.yaml'), scope, options);
87
+ if (staged.digest !== expectedDigest) throw new Error('Staged design-system digest does not match the reviewed candidate');
88
+ options.beforeCommit?.({ stage, destination: preview.destination, pack: staged });
89
+ if (existsSync(preview.destination)) throw new Error('Design-system destination appeared after preview; refusing overwrite');
90
+ renameSync(stage, preview.destination);
91
+ } catch (error) {
92
+ rmSync(stage, { recursive: true, force: true });
93
+ throw error;
94
+ }
95
+ return {
96
+ schema: 'ewai.design-system-installation/v1', status: 'installed', id: pack.id,
97
+ version: pack.version, digest: pack.digest, scope, destination: preview.destination,
98
+ selected: false,
99
+ notice: 'The pack is installed but not selected. Use the separate digest-reviewed selection command when appropriate.',
100
+ };
101
+ }