@a5c-ai/kradle 5.0.1-staging.3abdf9534c25

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 (295) hide show
  1. package/Dockerfile +31 -0
  2. package/README.md +187 -0
  3. package/bin/kradle-demo.mjs +23 -0
  4. package/bin/kradle-server.mjs +14 -0
  5. package/dist/kradle-controller-ui.json +3482 -0
  6. package/dist/kradle-lifecycle.json +201 -0
  7. package/dist/kradle-runtime-snapshot.json +3125 -0
  8. package/dist/kradle-summary.json +724 -0
  9. package/docs/README.md +61 -0
  10. package/docs/agents/README.md +83 -0
  11. package/docs/agents/acceptance-test-matrix.md +193 -0
  12. package/docs/agents/agent-mux-adapter-contract.md +167 -0
  13. package/docs/agents/agent-mux-source-map.md +310 -0
  14. package/docs/agents/agent-run-memory-import-spec.md +256 -0
  15. package/docs/agents/agent-stack-management-spec.md +421 -0
  16. package/docs/agents/api-contract-spec.md +309 -0
  17. package/docs/agents/artifacts-writeback-spec.md +145 -0
  18. package/docs/agents/chart-packaging-spec.md +128 -0
  19. package/docs/agents/ci-orchestration-spec.md +140 -0
  20. package/docs/agents/context-assembly-spec.md +219 -0
  21. package/docs/agents/controller-reconciliation-spec.md +255 -0
  22. package/docs/agents/crd-schema-spec.md +315 -0
  23. package/docs/agents/decision-log-open-questions.md +169 -0
  24. package/docs/agents/developer-implementation-checklist.md +329 -0
  25. package/docs/agents/dispatching-design.md +262 -0
  26. package/docs/agents/gaps-agent-mux-to-kradle-crds.md +298 -0
  27. package/docs/agents/glossary.md +66 -0
  28. package/docs/agents/implementation-blueprint.md +324 -0
  29. package/docs/agents/implementation-rollout-slices.md +251 -0
  30. package/docs/agents/memory-context-integration-spec.md +194 -0
  31. package/docs/agents/memory-ontology-schema-spec.md +253 -0
  32. package/docs/agents/memory-operations-runbook.md +121 -0
  33. package/docs/agents/mvp-vertical-slice-spec.md +146 -0
  34. package/docs/agents/observability-audit-spec.md +265 -0
  35. package/docs/agents/operator-runbook.md +174 -0
  36. package/docs/agents/org-memory-api-payload-examples.md +333 -0
  37. package/docs/agents/org-memory-controller-sequence-spec.md +181 -0
  38. package/docs/agents/org-memory-e2e-fixture-plan.md +161 -0
  39. package/docs/agents/org-memory-ui-implementation-map.md +114 -0
  40. package/docs/agents/org-memory-vertical-slice-spec.md +168 -0
  41. package/docs/agents/org-resource-model-delta-spec.md +111 -0
  42. package/docs/agents/org-route-resource-model-spec.md +183 -0
  43. package/docs/agents/org-scoping-namespace-spec.md +114 -0
  44. package/docs/agents/rbac-secrets-management-spec.md +406 -0
  45. package/docs/agents/repository-page-integration-spec.md +255 -0
  46. package/docs/agents/resource-contract-examples.md +808 -0
  47. package/docs/agents/resource-relationship-map.md +190 -0
  48. package/docs/agents/security-threat-model.md +188 -0
  49. package/docs/agents/shared-memory-company-brain-spec.md +358 -0
  50. package/docs/agents/storage-migration-spec.md +168 -0
  51. package/docs/agents/subagent-orchestration-spec.md +152 -0
  52. package/docs/agents/system-overview.md +88 -0
  53. package/docs/agents/tools-mcp-skills-spec.md +189 -0
  54. package/docs/agents/traceability-matrix.md +79 -0
  55. package/docs/agents/ui-flow-spec.md +211 -0
  56. package/docs/agents/ui-ux-system-spec.md +426 -0
  57. package/docs/agents/workspace-lifecycle-spec.md +166 -0
  58. package/docs/architecture-spec.md +78 -0
  59. package/docs/architecture-v2.md +2759 -0
  60. package/docs/components/control-plane.md +78 -0
  61. package/docs/components/data-plane.md +69 -0
  62. package/docs/components/hooks-events.md +67 -0
  63. package/docs/components/identity-rbac-policy.md +73 -0
  64. package/docs/components/kubevela-oam.md +70 -0
  65. package/docs/components/operations-publishing.md +81 -0
  66. package/docs/components/runners-ci.md +66 -0
  67. package/docs/components/web-ui.md +94 -0
  68. package/docs/crd-behaviors-and-relationships.md +3926 -0
  69. package/docs/external/README.md +47 -0
  70. package/docs/external/bidirectional-sync-design.md +134 -0
  71. package/docs/external/cicd-interface.md +64 -0
  72. package/docs/external/external-backend-controllers.md +170 -0
  73. package/docs/external/external-backend-crds.md +234 -0
  74. package/docs/external/external-backend-ui-spec.md +151 -0
  75. package/docs/external/external-backend-ux-flows.md +115 -0
  76. package/docs/external/external-object-mapping.md +125 -0
  77. package/docs/external/git-forge-interface.md +68 -0
  78. package/docs/external/github-integration-design.md +151 -0
  79. package/docs/external/issue-tracking-interface.md +66 -0
  80. package/docs/external/provider-capability-manifests.md +204 -0
  81. package/docs/external/provider-catalog.md +139 -0
  82. package/docs/external/provider-rollout-testing.md +78 -0
  83. package/docs/external/research-results.md +48 -0
  84. package/docs/external/security-auth-permissions.md +81 -0
  85. package/docs/external/sync-state-machines.md +108 -0
  86. package/docs/external/unified-external-backend-model.md +107 -0
  87. package/docs/external/user-facing-changes.md +67 -0
  88. package/docs/gaps.md +161 -0
  89. package/docs/install.md +94 -0
  90. package/docs/integration-and-design-decisions.md +1530 -0
  91. package/docs/kradle-design.md +334 -0
  92. package/docs/local-minikube.md +55 -0
  93. package/docs/ontology/README.md +32 -0
  94. package/docs/ontology/bounded-contexts.md +29 -0
  95. package/docs/ontology/events-and-hooks.md +32 -0
  96. package/docs/ontology/oam-kubevela.md +32 -0
  97. package/docs/ontology/operations-and-release.md +25 -0
  98. package/docs/ontology/personas-and-actors.md +32 -0
  99. package/docs/ontology/policies-and-invariants.md +33 -0
  100. package/docs/ontology/problem-space.md +30 -0
  101. package/docs/ontology/resource-contracts.md +40 -0
  102. package/docs/ontology/resource-taxonomy.md +42 -0
  103. package/docs/ontology/runners-and-ci.md +29 -0
  104. package/docs/ontology/solution-space.md +24 -0
  105. package/docs/ontology/storage-and-data-boundaries.md +29 -0
  106. package/docs/ontology/validation-matrix.md +24 -0
  107. package/docs/ontology/web-ui-excellent-flows.md +32 -0
  108. package/docs/ontology/workflows.md +39 -0
  109. package/docs/ontology/world.md +35 -0
  110. package/docs/openapi.yaml +1291 -0
  111. package/docs/product-requirements.md +62 -0
  112. package/docs/requirements-v2.md +235 -0
  113. package/docs/roadmap-mvp.md +87 -0
  114. package/docs/sdk-api-reference.md +1108 -0
  115. package/docs/system-requirements.md +90 -0
  116. package/docs/system-spec-v2.md +1230 -0
  117. package/docs/tests/README.md +53 -0
  118. package/docs/tests/agent-qa-plan.md +63 -0
  119. package/docs/tests/browser-ui-tests.md +62 -0
  120. package/docs/tests/ci-quality-gates.md +48 -0
  121. package/docs/tests/coverage-model.md +64 -0
  122. package/docs/tests/e2e-scenario-tests.md +53 -0
  123. package/docs/tests/fixtures-test-data.md +63 -0
  124. package/docs/tests/observability-reliability-tests.md +54 -0
  125. package/docs/tests/product-test-matrix.md +145 -0
  126. package/docs/tests/qa-adoption-roadmap.md +130 -0
  127. package/docs/tests/qa-automation-plan.md +101 -0
  128. package/docs/tests/security-compliance-tests.md +57 -0
  129. package/docs/tests/test-framework-tools.md +88 -0
  130. package/docs/tests/test-suite-layout.md +121 -0
  131. package/docs/tests/unit-integration-tests.md +48 -0
  132. package/docs/todo-kyverno +714 -0
  133. package/docs/todos.md +4 -0
  134. package/docs/user-stories.md +78 -0
  135. package/docs/web-console-spec.md +533 -0
  136. package/examples/minikube-demo.yaml +190 -0
  137. package/examples/oam-application.yaml +23 -0
  138. package/examples/policy-kyverno-pr-title.yaml +18 -0
  139. package/package.json +66 -0
  140. package/scripts/build.mjs +29 -0
  141. package/scripts/setup-minikube.mjs +65 -0
  142. package/scripts/smoke.mjs +37 -0
  143. package/scripts/validate-doc-coverage.mjs +152 -0
  144. package/scripts/validate-package.mjs +95 -0
  145. package/scripts/validate-ui.mjs +305 -0
  146. package/src/agent-adapter-controller.js +169 -0
  147. package/src/agent-approval-controller.js +170 -0
  148. package/src/agent-context-bundles.js +242 -0
  149. package/src/agent-dispatch-controller.js +549 -0
  150. package/src/agent-gateway-config-controller.js +147 -0
  151. package/src/agent-identity-migration.js +115 -0
  152. package/src/agent-memory-controller.js +357 -0
  153. package/src/agent-memory-import.js +327 -0
  154. package/src/agent-memory-query.js +292 -0
  155. package/src/agent-memory-repository-source-controller.js +255 -0
  156. package/src/agent-mux-client.js +589 -0
  157. package/src/agent-permission-review.js +250 -0
  158. package/src/agent-persona-controller.js +135 -0
  159. package/src/agent-project-controller.js +117 -0
  160. package/src/agent-prompt-composition.js +55 -0
  161. package/src/agent-provider-config-controller.js +151 -0
  162. package/src/agent-secret-config-grant-controller.js +282 -0
  163. package/src/agent-session-transcript-controller.js +189 -0
  164. package/src/agent-stack-controller.js +421 -0
  165. package/src/agent-subagent-controller.js +160 -0
  166. package/src/agent-transport-binding-controller.js +121 -0
  167. package/src/agent-trigger-controller.js +387 -0
  168. package/src/agent-workspace-controller.js +702 -0
  169. package/src/agent-writeback-controller.js +302 -0
  170. package/src/api-controller.js +621 -0
  171. package/src/argocd-gitops.js +43 -0
  172. package/src/artifact-registry-controller.js +542 -0
  173. package/src/assistant-runtime.js +284 -0
  174. package/src/async-controller.js +207 -0
  175. package/src/audit-controller.js +191 -0
  176. package/src/auth.js +310 -0
  177. package/src/component-catalog.js +41 -0
  178. package/src/control-plane.js +136 -0
  179. package/src/controller-client.js +112 -0
  180. package/src/controller-ui.js +620 -0
  181. package/src/data-plane.js +179 -0
  182. package/src/event-bus.js +397 -0
  183. package/src/external/conflict-controller.js +225 -0
  184. package/src/external/github/auth.js +96 -0
  185. package/src/external/github/cicd.js +180 -0
  186. package/src/external/github/git-forge.js +240 -0
  187. package/src/external/github/index.js +144 -0
  188. package/src/external/github/issue-tracking.js +163 -0
  189. package/src/external/provider-adapter.js +161 -0
  190. package/src/external/provider-resource-factory.js +221 -0
  191. package/src/external/sync-controller.js +235 -0
  192. package/src/external/webhook-controller.js +144 -0
  193. package/src/external/write-controller.js +283 -0
  194. package/src/gitea-backend.js +131 -0
  195. package/src/gitea-service.js +173 -0
  196. package/src/handoff.js +98 -0
  197. package/src/health-probes.js +134 -0
  198. package/src/hooks-events.js +63 -0
  199. package/src/hooks-lifecycle.js +117 -0
  200. package/src/http-server.js +409 -0
  201. package/src/identity-policy.js +86 -0
  202. package/src/index.js +71 -0
  203. package/src/jitsi-agent-bridge.js +141 -0
  204. package/src/jitsi-meeting-controller.js +291 -0
  205. package/src/jitsi-sync-controller.js +198 -0
  206. package/src/kradle-inference-service-controller.js +246 -0
  207. package/src/kubernetes-controller-async.js +531 -0
  208. package/src/kubernetes-controller.js +904 -0
  209. package/src/kubernetes-resource-gateway.js +48 -0
  210. package/src/model-route-controller.js +364 -0
  211. package/src/notification-controller.js +178 -0
  212. package/src/operations.js +112 -0
  213. package/src/org-scoping.js +5 -0
  214. package/src/resource-model.js +282 -0
  215. package/src/runner-controller.js +272 -0
  216. package/src/runners-ci.js +48 -0
  217. package/src/runtime.js +196 -0
  218. package/src/snapshot-cache.js +157 -0
  219. package/src/virtual-model-controller.js +538 -0
  220. package/src/virtual-model-hook-bridge.js +200 -0
  221. package/src/web-ui.js +40 -0
  222. package/tests/agent-adapter-controller.test.js +361 -0
  223. package/tests/agent-approval-controller.test.js +173 -0
  224. package/tests/agent-context-bundles.test.js +278 -0
  225. package/tests/agent-dispatch-controller.test.js +679 -0
  226. package/tests/agent-gateway-config-controller.test.js +386 -0
  227. package/tests/agent-identity-migration.test.js +87 -0
  228. package/tests/agent-memory-controller.test.js +461 -0
  229. package/tests/agent-memory-import-snapshot.test.js +477 -0
  230. package/tests/agent-memory-query.test.js +404 -0
  231. package/tests/agent-memory-repository-source.test.js +514 -0
  232. package/tests/agent-mux-client.test.js +389 -0
  233. package/tests/agent-mux-integration.test.js +971 -0
  234. package/tests/agent-permission-review-v2.test.js +317 -0
  235. package/tests/agent-permission-review.test.js +209 -0
  236. package/tests/agent-persona-controller.test.js +127 -0
  237. package/tests/agent-project-controller.test.js +302 -0
  238. package/tests/agent-prompt-composition.test.js +76 -0
  239. package/tests/agent-provider-config-controller.test.js +376 -0
  240. package/tests/agent-resources.test.js +303 -0
  241. package/tests/agent-secret-config-grant.test.js +231 -0
  242. package/tests/agent-session-transcript-controller.test.js +499 -0
  243. package/tests/agent-stack-controller.test.js +283 -0
  244. package/tests/agent-subagent-controller.test.js +201 -0
  245. package/tests/agent-transport-binding-controller.test.js +294 -0
  246. package/tests/agent-trigger-controller.test.js +271 -0
  247. package/tests/agent-trigger-routes.test.js +190 -0
  248. package/tests/agent-trigger-sources.test.js +245 -0
  249. package/tests/agent-workspace-controller.test.js +181 -0
  250. package/tests/agent-writeback.test.js +292 -0
  251. package/tests/approval-persistence.test.js +171 -0
  252. package/tests/artifact-registry.test.js +511 -0
  253. package/tests/assistant-runtime.test.js +506 -0
  254. package/tests/async-controller.test.js +252 -0
  255. package/tests/audit-controller.test.js +227 -0
  256. package/tests/codespace-controller.test.js +318 -0
  257. package/tests/controller-client.test.js +133 -0
  258. package/tests/deployment.test.js +527 -0
  259. package/tests/e2e/lifecycle.test.js +120 -0
  260. package/tests/event-bus-integration.test.js +355 -0
  261. package/tests/external-github-forge.test.js +560 -0
  262. package/tests/external-github-issues-cicd.test.js +520 -0
  263. package/tests/external-integration.test.js +470 -0
  264. package/tests/external-persistence.test.js +415 -0
  265. package/tests/external-provider-adapter.test.js +365 -0
  266. package/tests/external-resource-model.test.js +223 -0
  267. package/tests/external-webhook-sync.test.js +287 -0
  268. package/tests/external-write-conflict.test.js +353 -0
  269. package/tests/gitea-service.test.js +253 -0
  270. package/tests/health-check-real.test.js +165 -0
  271. package/tests/health-probes.test.js +90 -0
  272. package/tests/hooks-lifecycle.test.js +364 -0
  273. package/tests/integration/full-flow.test.js +266 -0
  274. package/tests/jitsi-agent-bridge.test.js +119 -0
  275. package/tests/jitsi-helm-integration.test.js +77 -0
  276. package/tests/jitsi-meeting-controller.test.js +170 -0
  277. package/tests/jitsi-resource-model.test.js +73 -0
  278. package/tests/jitsi-sync-controller.test.js +112 -0
  279. package/tests/kradle-inference-service.test.js +689 -0
  280. package/tests/kradle.test.js +779 -0
  281. package/tests/memory-search-wiring.test.js +270 -0
  282. package/tests/model-route-controller.test.js +733 -0
  283. package/tests/notification-controller.test.js +196 -0
  284. package/tests/notification-integration.test.js +179 -0
  285. package/tests/org-scoping.test.js +687 -0
  286. package/tests/runner-controller.test.js +327 -0
  287. package/tests/runner-integration.test.js +231 -0
  288. package/tests/session-cookie-hmac.test.js +151 -0
  289. package/tests/snapshot-performance.test.js +315 -0
  290. package/tests/sse-events.test.js +107 -0
  291. package/tests/virtual-model-controller.test.js +877 -0
  292. package/tests/virtual-model-hook-bridge.test.js +384 -0
  293. package/tests/webhook-trigger.test.js +198 -0
  294. package/tests/workspace-volumes.test.js +312 -0
  295. package/tests/writeback-persistence.test.js +207 -0
@@ -0,0 +1,302 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { createAgentProjectController, validateAgentProject, createResource, AGENT_PROJECT_CONTROLLER_BOUNDARY } from '../src/index.js';
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Acceptance criteria: Slice 1.2d — Agent Project Controller
7
+ //
8
+ // A KradleProject groups issues and tasks into a kanban board with workflow
9
+ // columns, default column assignment, issue tracking, and board state management.
10
+ //
11
+ // All tests in this file are expected to FAIL until the controller is
12
+ // implemented and exported from src/index.js.
13
+ // ---------------------------------------------------------------------------
14
+
15
+ function makeProject(name, overrides = {}) {
16
+ return createResource('KradleProject', { name, namespace: 'kradle-org-default' }, {
17
+ organizationRef: 'default',
18
+ workflowColumns: [
19
+ { id: 'todo', displayName: 'To Do', color: '#888888', default: true },
20
+ { id: 'in-progress', displayName: 'In Progress', color: '#0075ca' },
21
+ { id: 'review', displayName: 'Review', color: '#e4e669' },
22
+ { id: 'done', displayName: 'Done', color: '#0e8a16' }
23
+ ],
24
+ ...overrides
25
+ });
26
+ }
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // 1. Factory and shape
30
+ // ---------------------------------------------------------------------------
31
+
32
+ test('createAgentProjectController returns a controller with validate, getWorkflowColumns, getBoardState', () => {
33
+ const controller = createAgentProjectController();
34
+ assert.ok(controller, 'controller must be truthy');
35
+ assert.equal(typeof controller.validate, 'function', 'controller must expose a validate method');
36
+ assert.equal(typeof controller.getWorkflowColumns, 'function', 'controller must expose a getWorkflowColumns method');
37
+ assert.equal(typeof controller.getBoardState, 'function', 'controller must expose a getBoardState method');
38
+ assert.equal(controller.role, 'agent-project-controller', 'controller must declare its role');
39
+ });
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // 2. validate — happy path
43
+ // ---------------------------------------------------------------------------
44
+
45
+ test('validate accepts valid project with name, organizationRef, workflowColumns', () => {
46
+ const controller = createAgentProjectController();
47
+ const project = makeProject('my-sprint');
48
+ const result = controller.validate(project);
49
+
50
+ assert.equal(result.valid, true, 'valid project must pass validation');
51
+ assert.ok(Array.isArray(result.errors), 'result must contain an errors array');
52
+ assert.equal(result.errors.length, 0, 'errors array must be empty for a valid project');
53
+ });
54
+
55
+ // ---------------------------------------------------------------------------
56
+ // 3. validate — missing name
57
+ // ---------------------------------------------------------------------------
58
+
59
+ test('validate rejects project with missing name', () => {
60
+ const controller = createAgentProjectController();
61
+ const project = {
62
+ apiVersion: 'kradle.a5c.ai/v1alpha1',
63
+ kind: 'KradleProject',
64
+ metadata: { namespace: 'kradle-org-default', labels: {}, annotations: {} },
65
+ spec: {
66
+ organizationRef: 'default',
67
+ workflowColumns: [{ id: 'todo', displayName: 'To Do', color: '#888888' }]
68
+ },
69
+ status: {}
70
+ };
71
+ const result = controller.validate(project);
72
+
73
+ assert.equal(result.valid, false, 'project without name must fail validation');
74
+ assert.ok(result.errors.length > 0, 'errors array must not be empty');
75
+ assert.ok(
76
+ result.errors.some((e) => /name/i.test(e)),
77
+ 'at least one error must mention "name"'
78
+ );
79
+ });
80
+
81
+ // ---------------------------------------------------------------------------
82
+ // 4. validate — missing organizationRef
83
+ // ---------------------------------------------------------------------------
84
+
85
+ test('validate rejects project with missing organizationRef', () => {
86
+ const controller = createAgentProjectController();
87
+ const project = makeProject('no-org-project', { organizationRef: undefined });
88
+ delete project.spec.organizationRef;
89
+ const result = controller.validate(project);
90
+
91
+ assert.equal(result.valid, false, 'project without organizationRef must fail validation');
92
+ assert.ok(result.errors.length > 0, 'errors array must not be empty');
93
+ assert.ok(
94
+ result.errors.some((e) => /organizationRef/i.test(e)),
95
+ 'at least one error must mention "organizationRef"'
96
+ );
97
+ });
98
+
99
+ // ---------------------------------------------------------------------------
100
+ // 5. validate — empty workflowColumns array
101
+ // ---------------------------------------------------------------------------
102
+
103
+ test('validate rejects project with empty workflowColumns array', () => {
104
+ const controller = createAgentProjectController();
105
+ const project = makeProject('no-columns-project', { workflowColumns: [] });
106
+ const result = controller.validate(project);
107
+
108
+ assert.equal(result.valid, false, 'project with empty workflowColumns must fail validation');
109
+ assert.ok(result.errors.length > 0, 'errors array must not be empty');
110
+ assert.ok(
111
+ result.errors.some((e) => /workflowColumns/i.test(e)),
112
+ 'at least one error must mention "workflowColumns"'
113
+ );
114
+ });
115
+
116
+ // ---------------------------------------------------------------------------
117
+ // 6. validate — duplicate column IDs
118
+ // ---------------------------------------------------------------------------
119
+
120
+ test('validate rejects project with duplicate column IDs', () => {
121
+ const controller = createAgentProjectController();
122
+ const project = makeProject('dup-columns-project', {
123
+ workflowColumns: [
124
+ { id: 'todo', displayName: 'To Do', color: '#888888' },
125
+ { id: 'todo', displayName: 'Also To Do', color: '#999999' },
126
+ { id: 'done', displayName: 'Done', color: '#0e8a16' }
127
+ ]
128
+ });
129
+ const result = controller.validate(project);
130
+
131
+ assert.equal(result.valid, false, 'project with duplicate column IDs must fail validation');
132
+ assert.ok(result.errors.length > 0, 'errors array must not be empty');
133
+ assert.ok(
134
+ result.errors.some((e) => /duplicate|column/i.test(e)),
135
+ 'at least one error must mention duplicate column IDs'
136
+ );
137
+ });
138
+
139
+ // ---------------------------------------------------------------------------
140
+ // 7. getWorkflowColumns — returns columns in order
141
+ // ---------------------------------------------------------------------------
142
+
143
+ test('getWorkflowColumns returns columns from spec with order preserved', () => {
144
+ const controller = createAgentProjectController();
145
+ const columns = [
146
+ { id: 'backlog', displayName: 'Backlog', color: '#cccccc' },
147
+ { id: 'todo', displayName: 'To Do', color: '#888888', default: true },
148
+ { id: 'doing', displayName: 'Doing', color: '#0075ca' },
149
+ { id: 'done', displayName: 'Done', color: '#0e8a16' }
150
+ ];
151
+ const project = makeProject('ordered-project', { workflowColumns: columns });
152
+ const result = controller.getWorkflowColumns(project);
153
+
154
+ assert.ok(Array.isArray(result), 'getWorkflowColumns must return an array');
155
+ assert.equal(result.length, 4, 'must return all four columns');
156
+ assert.equal(result[0].id, 'backlog', 'first column must be backlog');
157
+ assert.equal(result[1].id, 'todo', 'second column must be todo');
158
+ assert.equal(result[2].id, 'doing', 'third column must be doing');
159
+ assert.equal(result[3].id, 'done', 'fourth column must be done');
160
+ });
161
+
162
+ // ---------------------------------------------------------------------------
163
+ // 8. getDefaultColumn — returns marked default or first column
164
+ // ---------------------------------------------------------------------------
165
+
166
+ test('getDefaultColumn returns the column marked as default', () => {
167
+ const controller = createAgentProjectController();
168
+ const project = makeProject('default-col-project', {
169
+ workflowColumns: [
170
+ { id: 'todo', displayName: 'To Do', color: '#888888' },
171
+ { id: 'in-progress', displayName: 'In Progress', color: '#0075ca', default: true },
172
+ { id: 'done', displayName: 'Done', color: '#0e8a16' }
173
+ ]
174
+ });
175
+ const col = controller.getDefaultColumn(project);
176
+
177
+ assert.ok(col, 'getDefaultColumn must return a column');
178
+ assert.equal(col.id, 'in-progress', 'must return the column marked as default');
179
+ });
180
+
181
+ test('getDefaultColumn returns first column when none is marked as default', () => {
182
+ const controller = createAgentProjectController();
183
+ const project = makeProject('no-default-col-project', {
184
+ workflowColumns: [
185
+ { id: 'backlog', displayName: 'Backlog', color: '#cccccc' },
186
+ { id: 'todo', displayName: 'To Do', color: '#888888' },
187
+ { id: 'done', displayName: 'Done', color: '#0e8a16' }
188
+ ]
189
+ });
190
+ const col = controller.getDefaultColumn(project);
191
+
192
+ assert.ok(col, 'getDefaultColumn must return a column');
193
+ assert.equal(col.id, 'backlog', 'must return first column when no default is marked');
194
+ });
195
+
196
+ // ---------------------------------------------------------------------------
197
+ // 9. getBoardState — default is 'active'
198
+ // ---------------------------------------------------------------------------
199
+
200
+ test('getBoardState returns "active" by default when boardState is not set', () => {
201
+ const controller = createAgentProjectController();
202
+ const project = makeProject('active-project');
203
+ const state = controller.getBoardState(project);
204
+
205
+ assert.equal(state, 'active', 'getBoardState must return "active" by default');
206
+ });
207
+
208
+ // ---------------------------------------------------------------------------
209
+ // 10. getBoardState — returns spec.boardState when set
210
+ // ---------------------------------------------------------------------------
211
+
212
+ test('getBoardState returns spec.boardState when explicitly set', () => {
213
+ const controller = createAgentProjectController();
214
+ const project = makeProject('archived-project', { boardState: 'archived' });
215
+ const state = controller.getBoardState(project);
216
+
217
+ assert.equal(state, 'archived', 'getBoardState must return the spec boardState value');
218
+ });
219
+
220
+ // ---------------------------------------------------------------------------
221
+ // 11. validate — null resource
222
+ // ---------------------------------------------------------------------------
223
+
224
+ test('validate rejects null resource with a clear error', () => {
225
+ const controller = createAgentProjectController();
226
+ const result = controller.validate(null);
227
+
228
+ assert.equal(result.valid, false, 'null resource must fail validation');
229
+ assert.ok(result.errors.length > 0, 'errors array must not be empty');
230
+ assert.ok(
231
+ result.errors.some((e) => /null|undefined/i.test(e)),
232
+ 'error must mention null or undefined'
233
+ );
234
+ });
235
+
236
+ // ---------------------------------------------------------------------------
237
+ // 12. BOUNDARY exported with correct role
238
+ // ---------------------------------------------------------------------------
239
+
240
+ test('AGENT_PROJECT_CONTROLLER_BOUNDARY is exported and has correct role', () => {
241
+ assert.ok(AGENT_PROJECT_CONTROLLER_BOUNDARY, 'BOUNDARY must be exported');
242
+ assert.equal(
243
+ AGENT_PROJECT_CONTROLLER_BOUNDARY.role,
244
+ 'agent-project-controller',
245
+ 'BOUNDARY role must be "agent-project-controller"'
246
+ );
247
+ assert.ok(
248
+ Array.isArray(AGENT_PROJECT_CONTROLLER_BOUNDARY.owns),
249
+ 'BOUNDARY must declare owned concerns'
250
+ );
251
+ });
252
+
253
+ // ---------------------------------------------------------------------------
254
+ // 13. validateAgentProject — standalone export
255
+ // ---------------------------------------------------------------------------
256
+
257
+ test('validateAgentProject exported as standalone function follows existing pattern', () => {
258
+ assert.equal(typeof validateAgentProject, 'function', 'validateAgentProject must be a named export');
259
+
260
+ const project = makeProject('standalone-validate-project');
261
+ const result = validateAgentProject(project);
262
+
263
+ assert.ok(result, 'validateAgentProject must return a result');
264
+ assert.ok('valid' in result, 'result must have a valid property');
265
+ assert.ok(Array.isArray(result.errors), 'result must have an errors array');
266
+ assert.equal(result.valid, true, 'a fully-specified project must pass standalone validation');
267
+ });
268
+
269
+ // ---------------------------------------------------------------------------
270
+ // 14. validate — missing workflowColumns field entirely
271
+ // ---------------------------------------------------------------------------
272
+
273
+ test('validate rejects project with no workflowColumns field', () => {
274
+ const controller = createAgentProjectController();
275
+ const project = makeProject('missing-cols-project');
276
+ delete project.spec.workflowColumns;
277
+ const result = controller.validate(project);
278
+
279
+ assert.equal(result.valid, false, 'project missing workflowColumns must fail validation');
280
+ assert.ok(
281
+ result.errors.some((e) => /workflowColumns/i.test(e)),
282
+ 'at least one error must mention "workflowColumns"'
283
+ );
284
+ });
285
+
286
+ // ---------------------------------------------------------------------------
287
+ // 15. getWorkflowColumns — returns empty array when no spec
288
+ // ---------------------------------------------------------------------------
289
+
290
+ test('getWorkflowColumns handles resource with no spec gracefully', () => {
291
+ const controller = createAgentProjectController();
292
+ const resource = {
293
+ apiVersion: 'kradle.a5c.ai/v1alpha1',
294
+ kind: 'KradleProject',
295
+ metadata: { name: 'spec-less-project', namespace: 'kradle-org-default', labels: {}, annotations: {} },
296
+ status: {}
297
+ };
298
+ const result = controller.getWorkflowColumns(resource);
299
+
300
+ assert.ok(Array.isArray(result), 'getWorkflowColumns must return an array');
301
+ assert.equal(result.length, 0, 'must return empty array when spec is absent');
302
+ });
@@ -0,0 +1,76 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { composeAgentPrompt, composeAgentSystemPrompt } from '../src/index.js';
4
+
5
+ const soul = { spec: { content: 'Soul layer.' } };
6
+ const persona = {
7
+ spec: {
8
+ displayName: 'Aria',
9
+ tagline: 'Security reviewer',
10
+ personality: {
11
+ traits: ['direct', 'security-conscious'],
12
+ communicationStyle: 'direct',
13
+ explanationDepth: 'thorough',
14
+ tone: 'professional'
15
+ },
16
+ role: {
17
+ title: 'Senior Reviewer',
18
+ domain: 'Security',
19
+ expertise: ['code-review', 'threat-modeling']
20
+ },
21
+ skillRefs: ['security-review'],
22
+ knowledgeRefs: ['owasp-top-10']
23
+ }
24
+ };
25
+ const definition = { spec: { roleContext: 'Definition role context.' } };
26
+ const stack = { spec: { systemPrompt: 'Legacy stack system.', developerPrompt: 'Legacy developer.', taskPrompt: 'Legacy task.' } };
27
+
28
+ test('composeAgentSystemPrompt layers soul persona definition and stack deterministically', () => {
29
+ const prompt = composeAgentSystemPrompt({ soul, persona, definition, stack });
30
+
31
+ const soulIndex = prompt.indexOf('Soul layer.');
32
+ const personaIndex = prompt.indexOf('Aria');
33
+ const definitionIndex = prompt.indexOf('Definition role context.');
34
+ const stackIndex = prompt.indexOf('Legacy stack system.');
35
+
36
+ assert.ok(soulIndex >= 0, 'soul content should be present');
37
+ assert.ok(personaIndex > soulIndex, 'persona layer should follow soul');
38
+ assert.ok(definitionIndex > personaIndex, 'definition layer should follow persona');
39
+ assert.ok(stackIndex > definitionIndex, 'legacy stack system prompt should follow definition');
40
+ assert.ok(prompt.includes('Communication style: direct'));
41
+ assert.ok(prompt.includes('Role: Senior Reviewer'));
42
+ assert.ok(prompt.includes('Skills: security-review'));
43
+ assert.ok(prompt.includes('Knowledge: owasp-top-10'));
44
+ });
45
+
46
+ test('composeAgentPrompt preserves legacy developer and task prompts', () => {
47
+ const prompt = composeAgentPrompt({ soul, persona, definition, stack });
48
+
49
+ assert.ok(prompt.system.includes('Soul layer.'));
50
+ assert.equal(prompt.developer, 'Legacy developer.');
51
+ assert.equal(prompt.task, 'Legacy task.');
52
+ });
53
+
54
+ test('composeAgentPrompt uses migrated persona legacy prompts before stack fallback', () => {
55
+ const migratedPersona = {
56
+ spec: {
57
+ ...persona.spec,
58
+ legacyPrompts: {
59
+ developerPrompt: 'Migrated developer.',
60
+ taskPrompt: 'Migrated task.',
61
+ },
62
+ },
63
+ };
64
+ const prompt = composeAgentPrompt({ soul, persona: migratedPersona, definition, stack });
65
+
66
+ assert.equal(prompt.developer, 'Migrated developer.');
67
+ assert.equal(prompt.task, 'Migrated task.');
68
+ });
69
+
70
+ test('composeAgentPrompt returns legacy stack prompts when no identity resources are supplied', () => {
71
+ const prompt = composeAgentPrompt({ stack });
72
+
73
+ assert.equal(prompt.system, 'Legacy stack system.');
74
+ assert.equal(prompt.developer, 'Legacy developer.');
75
+ assert.equal(prompt.task, 'Legacy task.');
76
+ });