@a5c-ai/krate 5.0.1-staging.00fa5317c

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 (256) hide show
  1. package/Dockerfile +31 -0
  2. package/README.md +183 -0
  3. package/bin/krate-demo.mjs +23 -0
  4. package/bin/krate-server.mjs +14 -0
  5. package/dist/krate-controller-ui.json +3205 -0
  6. package/dist/krate-lifecycle.json +201 -0
  7. package/dist/krate-runtime-snapshot.json +3125 -0
  8. package/dist/krate-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-krate-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/components/control-plane.md +78 -0
  60. package/docs/components/data-plane.md +69 -0
  61. package/docs/components/hooks-events.md +67 -0
  62. package/docs/components/identity-rbac-policy.md +73 -0
  63. package/docs/components/kubevela-oam.md +70 -0
  64. package/docs/components/operations-publishing.md +81 -0
  65. package/docs/components/runners-ci.md +66 -0
  66. package/docs/components/web-ui.md +94 -0
  67. package/docs/external/README.md +47 -0
  68. package/docs/external/bidirectional-sync-design.md +134 -0
  69. package/docs/external/cicd-interface.md +64 -0
  70. package/docs/external/external-backend-controllers.md +170 -0
  71. package/docs/external/external-backend-crds.md +234 -0
  72. package/docs/external/external-backend-ui-spec.md +151 -0
  73. package/docs/external/external-backend-ux-flows.md +115 -0
  74. package/docs/external/external-object-mapping.md +125 -0
  75. package/docs/external/git-forge-interface.md +68 -0
  76. package/docs/external/github-integration-design.md +151 -0
  77. package/docs/external/issue-tracking-interface.md +66 -0
  78. package/docs/external/provider-capability-manifests.md +204 -0
  79. package/docs/external/provider-catalog.md +139 -0
  80. package/docs/external/provider-rollout-testing.md +78 -0
  81. package/docs/external/research-results.md +48 -0
  82. package/docs/external/security-auth-permissions.md +81 -0
  83. package/docs/external/sync-state-machines.md +108 -0
  84. package/docs/external/unified-external-backend-model.md +107 -0
  85. package/docs/external/user-facing-changes.md +67 -0
  86. package/docs/gaps.md +161 -0
  87. package/docs/install.md +94 -0
  88. package/docs/krate-design.md +334 -0
  89. package/docs/local-minikube.md +55 -0
  90. package/docs/ontology/README.md +32 -0
  91. package/docs/ontology/bounded-contexts.md +29 -0
  92. package/docs/ontology/events-and-hooks.md +32 -0
  93. package/docs/ontology/oam-kubevela.md +32 -0
  94. package/docs/ontology/operations-and-release.md +25 -0
  95. package/docs/ontology/personas-and-actors.md +32 -0
  96. package/docs/ontology/policies-and-invariants.md +33 -0
  97. package/docs/ontology/problem-space.md +30 -0
  98. package/docs/ontology/resource-contracts.md +40 -0
  99. package/docs/ontology/resource-taxonomy.md +42 -0
  100. package/docs/ontology/runners-and-ci.md +29 -0
  101. package/docs/ontology/solution-space.md +24 -0
  102. package/docs/ontology/storage-and-data-boundaries.md +29 -0
  103. package/docs/ontology/validation-matrix.md +24 -0
  104. package/docs/ontology/web-ui-excellent-flows.md +32 -0
  105. package/docs/ontology/workflows.md +39 -0
  106. package/docs/ontology/world.md +35 -0
  107. package/docs/openapi.yaml +1275 -0
  108. package/docs/product-requirements.md +62 -0
  109. package/docs/roadmap-mvp.md +87 -0
  110. package/docs/system-requirements.md +90 -0
  111. package/docs/tests/README.md +53 -0
  112. package/docs/tests/agent-qa-plan.md +63 -0
  113. package/docs/tests/browser-ui-tests.md +62 -0
  114. package/docs/tests/ci-quality-gates.md +48 -0
  115. package/docs/tests/coverage-model.md +64 -0
  116. package/docs/tests/e2e-scenario-tests.md +53 -0
  117. package/docs/tests/fixtures-test-data.md +63 -0
  118. package/docs/tests/observability-reliability-tests.md +54 -0
  119. package/docs/tests/product-test-matrix.md +145 -0
  120. package/docs/tests/qa-adoption-roadmap.md +130 -0
  121. package/docs/tests/qa-automation-plan.md +101 -0
  122. package/docs/tests/security-compliance-tests.md +57 -0
  123. package/docs/tests/test-framework-tools.md +88 -0
  124. package/docs/tests/test-suite-layout.md +121 -0
  125. package/docs/tests/unit-integration-tests.md +48 -0
  126. package/docs/todo-kyverno +714 -0
  127. package/docs/todos.md +4 -0
  128. package/docs/user-stories.md +78 -0
  129. package/examples/minikube-demo.yaml +190 -0
  130. package/examples/oam-application.yaml +23 -0
  131. package/examples/policy-kyverno-pr-title.yaml +18 -0
  132. package/package.json +63 -0
  133. package/scripts/build.mjs +29 -0
  134. package/scripts/setup-minikube.mjs +65 -0
  135. package/scripts/smoke.mjs +37 -0
  136. package/scripts/validate-doc-coverage.mjs +152 -0
  137. package/scripts/validate-package.mjs +93 -0
  138. package/scripts/validate-ui.mjs +278 -0
  139. package/src/agent-adapter-controller.js +169 -0
  140. package/src/agent-approval-controller.js +170 -0
  141. package/src/agent-context-bundles.js +242 -0
  142. package/src/agent-dispatch-controller.js +209 -0
  143. package/src/agent-gateway-config-controller.js +147 -0
  144. package/src/agent-memory-controller.js +357 -0
  145. package/src/agent-memory-import.js +327 -0
  146. package/src/agent-memory-query.js +292 -0
  147. package/src/agent-memory-repository-source-controller.js +255 -0
  148. package/src/agent-mux-client.js +280 -0
  149. package/src/agent-permission-review.js +250 -0
  150. package/src/agent-project-controller.js +117 -0
  151. package/src/agent-provider-config-controller.js +150 -0
  152. package/src/agent-secret-config-grant-controller.js +282 -0
  153. package/src/agent-session-transcript-controller.js +189 -0
  154. package/src/agent-stack-controller.js +347 -0
  155. package/src/agent-subagent-controller.js +160 -0
  156. package/src/agent-transport-binding-controller.js +121 -0
  157. package/src/agent-trigger-controller.js +381 -0
  158. package/src/agent-workspace-controller.js +702 -0
  159. package/src/agent-writeback-controller.js +302 -0
  160. package/src/api-controller.js +541 -0
  161. package/src/argocd-gitops.js +43 -0
  162. package/src/async-controller.js +207 -0
  163. package/src/audit-controller.js +191 -0
  164. package/src/auth.js +307 -0
  165. package/src/component-catalog.js +41 -0
  166. package/src/control-plane.js +136 -0
  167. package/src/controller-client.js +72 -0
  168. package/src/controller-ui.js +617 -0
  169. package/src/data-plane.js +179 -0
  170. package/src/event-bus.js +61 -0
  171. package/src/external/conflict-controller.js +225 -0
  172. package/src/external/github/auth.js +96 -0
  173. package/src/external/github/cicd.js +180 -0
  174. package/src/external/github/git-forge.js +240 -0
  175. package/src/external/github/index.js +144 -0
  176. package/src/external/github/issue-tracking.js +163 -0
  177. package/src/external/provider-adapter.js +161 -0
  178. package/src/external/provider-resource-factory.js +161 -0
  179. package/src/external/sync-controller.js +235 -0
  180. package/src/external/webhook-controller.js +144 -0
  181. package/src/external/write-controller.js +283 -0
  182. package/src/gitea-backend.js +131 -0
  183. package/src/gitea-service.js +173 -0
  184. package/src/handoff.js +98 -0
  185. package/src/hooks-events.js +63 -0
  186. package/src/http-server.js +377 -0
  187. package/src/identity-policy.js +86 -0
  188. package/src/index.js +57 -0
  189. package/src/kubernetes-controller-async.js +511 -0
  190. package/src/kubernetes-controller.js +878 -0
  191. package/src/kubernetes-resource-gateway.js +48 -0
  192. package/src/notification-controller.js +178 -0
  193. package/src/operations.js +112 -0
  194. package/src/org-scoping.js +5 -0
  195. package/src/resource-model.js +221 -0
  196. package/src/runner-controller.js +272 -0
  197. package/src/runners-ci.js +48 -0
  198. package/src/runtime.js +196 -0
  199. package/src/snapshot-cache.js +157 -0
  200. package/src/web-ui.js +40 -0
  201. package/tests/agent-adapter-controller.test.js +361 -0
  202. package/tests/agent-approval-controller.test.js +173 -0
  203. package/tests/agent-context-bundles.test.js +278 -0
  204. package/tests/agent-dispatch-controller.test.js +315 -0
  205. package/tests/agent-gateway-config-controller.test.js +386 -0
  206. package/tests/agent-memory-controller.test.js +308 -0
  207. package/tests/agent-memory-import-snapshot.test.js +477 -0
  208. package/tests/agent-memory-query.test.js +404 -0
  209. package/tests/agent-memory-repository-source.test.js +514 -0
  210. package/tests/agent-mux-client.test.js +204 -0
  211. package/tests/agent-permission-review-v2.test.js +317 -0
  212. package/tests/agent-permission-review.test.js +209 -0
  213. package/tests/agent-project-controller.test.js +302 -0
  214. package/tests/agent-provider-config-controller.test.js +376 -0
  215. package/tests/agent-resources.test.js +228 -0
  216. package/tests/agent-secret-config-grant.test.js +231 -0
  217. package/tests/agent-session-transcript-controller.test.js +499 -0
  218. package/tests/agent-stack-controller.test.js +221 -0
  219. package/tests/agent-subagent-controller.test.js +201 -0
  220. package/tests/agent-transport-binding-controller.test.js +294 -0
  221. package/tests/agent-trigger-controller.test.js +211 -0
  222. package/tests/agent-trigger-routes.test.js +190 -0
  223. package/tests/agent-trigger-sources.test.js +245 -0
  224. package/tests/agent-workspace-controller.test.js +181 -0
  225. package/tests/agent-writeback.test.js +292 -0
  226. package/tests/approval-persistence.test.js +171 -0
  227. package/tests/async-controller.test.js +252 -0
  228. package/tests/audit-controller.test.js +227 -0
  229. package/tests/codespace-controller.test.js +318 -0
  230. package/tests/deployment.test.js +407 -0
  231. package/tests/e2e/lifecycle.test.js +117 -0
  232. package/tests/event-bus-integration.test.js +190 -0
  233. package/tests/external-github-forge.test.js +560 -0
  234. package/tests/external-github-issues-cicd.test.js +520 -0
  235. package/tests/external-integration.test.js +470 -0
  236. package/tests/external-persistence.test.js +340 -0
  237. package/tests/external-provider-adapter.test.js +365 -0
  238. package/tests/external-resource-model.test.js +215 -0
  239. package/tests/external-webhook-sync.test.js +287 -0
  240. package/tests/external-write-conflict.test.js +353 -0
  241. package/tests/gitea-service.test.js +253 -0
  242. package/tests/health-check-real.test.js +165 -0
  243. package/tests/integration/full-flow.test.js +266 -0
  244. package/tests/krate.test.js +756 -0
  245. package/tests/memory-search-wiring.test.js +270 -0
  246. package/tests/notification-controller.test.js +196 -0
  247. package/tests/notification-integration.test.js +179 -0
  248. package/tests/org-scoping.test.js +687 -0
  249. package/tests/runner-controller.test.js +327 -0
  250. package/tests/runner-integration.test.js +231 -0
  251. package/tests/session-cookie-hmac.test.js +151 -0
  252. package/tests/snapshot-performance.test.js +247 -0
  253. package/tests/sse-events.test.js +107 -0
  254. package/tests/webhook-trigger.test.js +198 -0
  255. package/tests/workspace-volumes.test.js +312 -0
  256. package/tests/writeback-persistence.test.js +207 -0
@@ -0,0 +1,514 @@
1
+ // Slice 2.3a — AgentMemoryRepository & AgentMemorySource Controllers
2
+ // TDD: tests written BEFORE implementation.
3
+ //
4
+ // AgentMemoryRepository: org-level memory storage pointer, git repo ref validation.
5
+ // AgentMemorySource: read policies for memory paths, access control.
6
+
7
+ import assert from 'node:assert/strict';
8
+ import test from 'node:test';
9
+ import {
10
+ createResource,
11
+ validateMemoryRepository,
12
+ validateMemorySource,
13
+ createAgentMemoryRepositoryController,
14
+ createAgentMemorySourceController,
15
+ AGENT_MEMORY_REPOSITORY_CONTROLLER_BOUNDARY,
16
+ AGENT_MEMORY_SOURCE_CONTROLLER_BOUNDARY,
17
+ } from '../src/index.js';
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Helpers
21
+ // ---------------------------------------------------------------------------
22
+
23
+ function makeMemoryRepository(name, overrides = {}) {
24
+ return createResource('AgentMemoryRepository', { name, namespace: 'krate-org-default' }, {
25
+ organizationRef: 'acme',
26
+ repositoryRef: 'company-brain',
27
+ defaultBranch: 'main',
28
+ layoutProfile: 'standard',
29
+ repoUrl: 'https://github.com/acme/company-brain.git',
30
+ ...overrides,
31
+ });
32
+ }
33
+
34
+ function makeMemorySource(name, overrides = {}) {
35
+ return createResource('AgentMemorySource', { name, namespace: 'krate-org-default' }, {
36
+ organizationRef: 'acme',
37
+ repositoryRef: 'company-brain',
38
+ appliesTo: { kind: 'AgentStack', name: 'my-stack' },
39
+ include: { paths: ['docs/**', 'records/**'] },
40
+ paths: ['docs/**', 'records/**'],
41
+ ...overrides,
42
+ });
43
+ }
44
+
45
+ // ===========================================================================
46
+ // AgentMemoryRepository — controller factory
47
+ // ===========================================================================
48
+
49
+ test('createAgentMemoryRepositoryController returns controller with expected methods', () => {
50
+ const controller = createAgentMemoryRepositoryController();
51
+ assert.ok(controller, 'controller must be truthy');
52
+ assert.equal(typeof controller.validateMemoryRepository, 'function', 'must expose validateMemoryRepository');
53
+ assert.equal(typeof controller.getRepositoryUrl, 'function', 'must expose getRepositoryUrl');
54
+ assert.equal(typeof controller.getRetentionPolicy, 'function', 'must expose getRetentionPolicy');
55
+ assert.equal(controller.role, 'agent-memory-repository-controller', 'must declare its role');
56
+ });
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // validateMemoryRepository — happy path
60
+ // ---------------------------------------------------------------------------
61
+
62
+ test('validateMemoryRepository accepts valid config with name, orgRef, and repoUrl', () => {
63
+ const controller = createAgentMemoryRepositoryController();
64
+ const repo = makeMemoryRepository('brain-repo');
65
+
66
+ const result = controller.validateMemoryRepository(repo);
67
+
68
+ assert.equal(result.valid, true, 'valid config must pass validation');
69
+ assert.ok(Array.isArray(result.errors), 'result must contain an errors array');
70
+ assert.equal(result.errors.length, 0, 'errors array must be empty for a valid config');
71
+ });
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // validateMemoryRepository — missing name
75
+ // ---------------------------------------------------------------------------
76
+
77
+ test('validateMemoryRepository rejects missing name', () => {
78
+ const controller = createAgentMemoryRepositoryController();
79
+ const repo = {
80
+ apiVersion: 'krate.a5c.ai/v1alpha1',
81
+ kind: 'AgentMemoryRepository',
82
+ metadata: { namespace: 'krate-org-default', labels: {}, annotations: {} },
83
+ spec: {
84
+ organizationRef: 'acme',
85
+ repositoryRef: 'company-brain',
86
+ defaultBranch: 'main',
87
+ layoutProfile: 'standard',
88
+ repoUrl: 'https://github.com/acme/brain.git',
89
+ },
90
+ status: {},
91
+ };
92
+
93
+ const result = controller.validateMemoryRepository(repo);
94
+
95
+ assert.equal(result.valid, false, 'missing name must fail validation');
96
+ assert.ok(result.errors.length > 0, 'errors must not be empty');
97
+ assert.ok(
98
+ result.errors.some((e) => /name/i.test(e)),
99
+ 'at least one error must mention "name"'
100
+ );
101
+ });
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // validateMemoryRepository — missing repoUrl
105
+ // ---------------------------------------------------------------------------
106
+
107
+ test('validateMemoryRepository rejects missing repoUrl', () => {
108
+ const controller = createAgentMemoryRepositoryController();
109
+ const repo = makeMemoryRepository('no-url-repo');
110
+ delete repo.spec.repoUrl;
111
+
112
+ const result = controller.validateMemoryRepository(repo);
113
+
114
+ assert.equal(result.valid, false, 'missing repoUrl must fail validation');
115
+ assert.ok(result.errors.length > 0, 'errors must not be empty');
116
+ assert.ok(
117
+ result.errors.some((e) => /repoUrl/i.test(e)),
118
+ 'at least one error must mention "repoUrl"'
119
+ );
120
+ });
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // validateMemoryRepository — invalid repoUrl (not git/http/ssh URL)
124
+ // ---------------------------------------------------------------------------
125
+
126
+ test('validateMemoryRepository rejects invalid repoUrl (not git/http/ssh URL)', () => {
127
+ const controller = createAgentMemoryRepositoryController();
128
+ const repo = makeMemoryRepository('bad-url-repo', { repoUrl: 'ftp://not-a-git-url.example.com/repo' });
129
+
130
+ const result = controller.validateMemoryRepository(repo);
131
+
132
+ assert.equal(result.valid, false, 'invalid repoUrl protocol must fail validation');
133
+ assert.ok(result.errors.length > 0, 'errors must not be empty');
134
+ assert.ok(
135
+ result.errors.some((e) => /repoUrl/i.test(e)),
136
+ 'at least one error must mention "repoUrl"'
137
+ );
138
+ });
139
+
140
+ test('validateMemoryRepository accepts git+ssh URL (git@ format)', () => {
141
+ const controller = createAgentMemoryRepositoryController();
142
+ const repo = makeMemoryRepository('ssh-repo', { repoUrl: 'git@github.com:acme/company-brain.git' });
143
+
144
+ const result = controller.validateMemoryRepository(repo);
145
+
146
+ assert.equal(result.valid, true, 'git@host:path.git URL must pass validation');
147
+ assert.equal(result.errors.length, 0, 'errors must be empty for valid git ssh URL');
148
+ });
149
+
150
+ test('validateMemoryRepository accepts ssh:// URL', () => {
151
+ const controller = createAgentMemoryRepositoryController();
152
+ const repo = makeMemoryRepository('ssh-proto-repo', { repoUrl: 'ssh://git@github.com/acme/brain.git' });
153
+
154
+ const result = controller.validateMemoryRepository(repo);
155
+
156
+ assert.equal(result.valid, true, 'ssh:// URL must pass validation');
157
+ assert.equal(result.errors.length, 0, 'errors must be empty for valid ssh:// URL');
158
+ });
159
+
160
+ // ---------------------------------------------------------------------------
161
+ // getRepositoryUrl — returns configured URL
162
+ // ---------------------------------------------------------------------------
163
+
164
+ test('getRepositoryUrl returns the configured repoUrl from spec', () => {
165
+ const controller = createAgentMemoryRepositoryController();
166
+ const repo = makeMemoryRepository('url-repo', { repoUrl: 'https://github.com/acme/brain.git' });
167
+
168
+ const url = controller.getRepositoryUrl(repo);
169
+
170
+ assert.equal(url, 'https://github.com/acme/brain.git', 'must return spec repoUrl');
171
+ });
172
+
173
+ test('getRepositoryUrl throws on null resource', () => {
174
+ const controller = createAgentMemoryRepositoryController();
175
+
176
+ assert.throws(
177
+ () => controller.getRepositoryUrl(null),
178
+ /null|undefined/i,
179
+ 'getRepositoryUrl must throw on null resource'
180
+ );
181
+ });
182
+
183
+ // ---------------------------------------------------------------------------
184
+ // getRetentionPolicy — returns retention config with defaults
185
+ // ---------------------------------------------------------------------------
186
+
187
+ test('getRetentionPolicy returns retention config with defaults (maxAgeDays: 90, maxSizeMb: 500)', () => {
188
+ const controller = createAgentMemoryRepositoryController();
189
+ const repo = makeMemoryRepository('retention-repo');
190
+
191
+ const policy = controller.getRetentionPolicy(repo);
192
+
193
+ assert.ok(policy, 'getRetentionPolicy must return a value');
194
+ assert.equal(policy.maxAgeDays, 90, 'default maxAgeDays must be 90');
195
+ assert.equal(policy.maxSizeMb, 500, 'default maxSizeMb must be 500');
196
+ });
197
+
198
+ test('getRetentionPolicy merges spec retentionPolicy with defaults', () => {
199
+ const controller = createAgentMemoryRepositoryController();
200
+ const repo = makeMemoryRepository('custom-retention-repo', {
201
+ retentionPolicy: { maxAgeDays: 180, maxSizeMb: 1000 },
202
+ });
203
+
204
+ const policy = controller.getRetentionPolicy(repo);
205
+
206
+ assert.equal(policy.maxAgeDays, 180, 'spec maxAgeDays must override default');
207
+ assert.equal(policy.maxSizeMb, 1000, 'spec maxSizeMb must override default');
208
+ });
209
+
210
+ test('getRetentionPolicy throws on null resource', () => {
211
+ const controller = createAgentMemoryRepositoryController();
212
+
213
+ assert.throws(
214
+ () => controller.getRetentionPolicy(null),
215
+ /null|undefined/i,
216
+ 'getRetentionPolicy must throw on null resource'
217
+ );
218
+ });
219
+
220
+ // ---------------------------------------------------------------------------
221
+ // validateMemoryRepository — rejects null resource
222
+ // ---------------------------------------------------------------------------
223
+
224
+ test('validateMemoryRepository rejects null resource', () => {
225
+ const controller = createAgentMemoryRepositoryController();
226
+
227
+ const result = controller.validateMemoryRepository(null);
228
+
229
+ assert.equal(result.valid, false, 'null resource must fail validation');
230
+ assert.ok(result.errors.length > 0, 'errors must not be empty');
231
+ assert.ok(
232
+ result.errors.some((e) => /null|undefined/i.test(e)),
233
+ 'error must mention null or undefined'
234
+ );
235
+ });
236
+
237
+ // ---------------------------------------------------------------------------
238
+ // validateMemoryRepository standalone export
239
+ // ---------------------------------------------------------------------------
240
+
241
+ test('validateMemoryRepository standalone export follows existing pattern', () => {
242
+ assert.equal(typeof validateMemoryRepository, 'function', 'validateMemoryRepository must be a named export');
243
+
244
+ const repo = makeMemoryRepository('standalone-validate-repo');
245
+ const result = validateMemoryRepository(repo);
246
+
247
+ assert.ok(result, 'must return a result');
248
+ assert.ok('valid' in result, 'result must have a valid property');
249
+ assert.ok(Array.isArray(result.errors), 'result must have an errors array');
250
+ assert.equal(result.valid, true, 'a fully-specified repo must pass standalone validation');
251
+ });
252
+
253
+ // ---------------------------------------------------------------------------
254
+ // BOUNDARY — AgentMemoryRepository
255
+ // ---------------------------------------------------------------------------
256
+
257
+ test('AGENT_MEMORY_REPOSITORY_CONTROLLER_BOUNDARY is exported with correct role', () => {
258
+ assert.ok(AGENT_MEMORY_REPOSITORY_CONTROLLER_BOUNDARY, 'BOUNDARY must be exported');
259
+ assert.equal(
260
+ AGENT_MEMORY_REPOSITORY_CONTROLLER_BOUNDARY.role,
261
+ 'agent-memory-repository-controller',
262
+ 'BOUNDARY role must be "agent-memory-repository-controller"'
263
+ );
264
+ assert.ok(
265
+ Array.isArray(AGENT_MEMORY_REPOSITORY_CONTROLLER_BOUNDARY.owns),
266
+ 'BOUNDARY must declare owned concerns'
267
+ );
268
+ });
269
+
270
+ // ===========================================================================
271
+ // AgentMemorySource — controller factory
272
+ // ===========================================================================
273
+
274
+ test('createAgentMemorySourceController returns controller with expected methods', () => {
275
+ const controller = createAgentMemorySourceController();
276
+ assert.ok(controller, 'controller must be truthy');
277
+ assert.equal(typeof controller.validateMemorySource, 'function', 'must expose validateMemorySource');
278
+ assert.equal(typeof controller.getReadPolicy, 'function', 'must expose getReadPolicy');
279
+ assert.equal(typeof controller.getIncludedPaths, 'function', 'must expose getIncludedPaths');
280
+ assert.equal(typeof controller.getExcludedPaths, 'function', 'must expose getExcludedPaths');
281
+ assert.equal(controller.role, 'agent-memory-source-controller', 'must declare its role');
282
+ });
283
+
284
+ // ---------------------------------------------------------------------------
285
+ // validateMemorySource — happy path
286
+ // ---------------------------------------------------------------------------
287
+
288
+ test('validateMemorySource accepts valid config (name, orgRef, repositoryRef, paths)', () => {
289
+ const controller = createAgentMemorySourceController();
290
+ const source = makeMemorySource('good-source');
291
+
292
+ const result = controller.validateMemorySource(source);
293
+
294
+ assert.equal(result.valid, true, 'valid config must pass validation');
295
+ assert.ok(Array.isArray(result.errors), 'result must contain an errors array');
296
+ assert.equal(result.errors.length, 0, 'errors array must be empty for a valid config');
297
+ });
298
+
299
+ // ---------------------------------------------------------------------------
300
+ // validateMemorySource — missing repositoryRef
301
+ // ---------------------------------------------------------------------------
302
+
303
+ test('validateMemorySource rejects missing repositoryRef', () => {
304
+ const controller = createAgentMemorySourceController();
305
+ const source = makeMemorySource('no-repo-source');
306
+ delete source.spec.repositoryRef;
307
+
308
+ const result = controller.validateMemorySource(source);
309
+
310
+ assert.equal(result.valid, false, 'missing repositoryRef must fail validation');
311
+ assert.ok(result.errors.length > 0, 'errors must not be empty');
312
+ assert.ok(
313
+ result.errors.some((e) => /repositoryRef/i.test(e)),
314
+ 'at least one error must mention "repositoryRef"'
315
+ );
316
+ });
317
+
318
+ // ---------------------------------------------------------------------------
319
+ // validateMemorySource — empty paths array
320
+ // ---------------------------------------------------------------------------
321
+
322
+ test('validateMemorySource rejects empty paths array', () => {
323
+ const controller = createAgentMemorySourceController();
324
+ const source = makeMemorySource('empty-paths-source', { paths: [] });
325
+
326
+ const result = controller.validateMemorySource(source);
327
+
328
+ assert.equal(result.valid, false, 'empty paths must fail validation');
329
+ assert.ok(result.errors.length > 0, 'errors must not be empty');
330
+ assert.ok(
331
+ result.errors.some((e) => /paths/i.test(e)),
332
+ 'at least one error must mention "paths"'
333
+ );
334
+ });
335
+
336
+ // ---------------------------------------------------------------------------
337
+ // validateMemorySource — rejects null resource
338
+ // ---------------------------------------------------------------------------
339
+
340
+ test('validateMemorySource rejects null resource', () => {
341
+ const controller = createAgentMemorySourceController();
342
+
343
+ const result = controller.validateMemorySource(null);
344
+
345
+ assert.equal(result.valid, false, 'null resource must fail validation');
346
+ assert.ok(result.errors.length > 0, 'errors must not be empty');
347
+ assert.ok(
348
+ result.errors.some((e) => /null|undefined/i.test(e)),
349
+ 'error must mention null or undefined'
350
+ );
351
+ });
352
+
353
+ // ---------------------------------------------------------------------------
354
+ // getReadPolicy — returns access policy with defaults
355
+ // ---------------------------------------------------------------------------
356
+
357
+ test('getReadPolicy returns access policy from spec with defaults', () => {
358
+ const controller = createAgentMemorySourceController();
359
+ const source = makeMemorySource('policy-source');
360
+
361
+ const policy = controller.getReadPolicy(source);
362
+
363
+ assert.ok(policy, 'getReadPolicy must return a value');
364
+ assert.ok('mode' in policy, 'policy must include a mode');
365
+ assert.ok('maxDepth' in policy, 'policy must include a maxDepth default');
366
+ });
367
+
368
+ test('getReadPolicy merges spec readPolicy with defaults', () => {
369
+ const controller = createAgentMemorySourceController();
370
+ const source = makeMemorySource('custom-policy-source', {
371
+ readPolicy: { mode: 'allow-list', maxDepth: 3 },
372
+ });
373
+
374
+ const policy = controller.getReadPolicy(source);
375
+
376
+ assert.equal(policy.mode, 'allow-list', 'spec mode must override default');
377
+ assert.equal(policy.maxDepth, 3, 'spec maxDepth must override default');
378
+ });
379
+
380
+ test('getReadPolicy throws on null resource', () => {
381
+ const controller = createAgentMemorySourceController();
382
+
383
+ assert.throws(
384
+ () => controller.getReadPolicy(null),
385
+ /null|undefined/i,
386
+ 'getReadPolicy must throw on null resource'
387
+ );
388
+ });
389
+
390
+ // ---------------------------------------------------------------------------
391
+ // getIncludedPaths — returns the paths array
392
+ // ---------------------------------------------------------------------------
393
+
394
+ test('getIncludedPaths returns the paths array from spec', () => {
395
+ const controller = createAgentMemorySourceController();
396
+ const source = makeMemorySource('paths-source', { paths: ['docs/**', 'records/**', 'ontology/**'] });
397
+
398
+ const paths = controller.getIncludedPaths(source);
399
+
400
+ assert.ok(Array.isArray(paths), 'getIncludedPaths must return an array');
401
+ assert.equal(paths.length, 3, 'must return all 3 paths');
402
+ assert.ok(paths.includes('docs/**'), 'must include docs/**');
403
+ assert.ok(paths.includes('records/**'), 'must include records/**');
404
+ assert.ok(paths.includes('ontology/**'), 'must include ontology/**');
405
+ });
406
+
407
+ test('getIncludedPaths throws on null resource', () => {
408
+ const controller = createAgentMemorySourceController();
409
+
410
+ assert.throws(
411
+ () => controller.getIncludedPaths(null),
412
+ /null|undefined/i,
413
+ 'getIncludedPaths must throw on null resource'
414
+ );
415
+ });
416
+
417
+ // ---------------------------------------------------------------------------
418
+ // getExcludedPaths — returns excluded paths or empty array
419
+ // ---------------------------------------------------------------------------
420
+
421
+ test('getExcludedPaths returns empty array when no excludedPaths in spec', () => {
422
+ const controller = createAgentMemorySourceController();
423
+ const source = makeMemorySource('no-exclude-source');
424
+
425
+ const excluded = controller.getExcludedPaths(source);
426
+
427
+ assert.ok(Array.isArray(excluded), 'getExcludedPaths must return an array');
428
+ assert.equal(excluded.length, 0, 'must return empty array when no excludedPaths set');
429
+ });
430
+
431
+ test('getExcludedPaths returns excluded paths from spec', () => {
432
+ const controller = createAgentMemorySourceController();
433
+ const source = makeMemorySource('exclude-source', {
434
+ excludedPaths: ['secrets/**', 'tmp/**'],
435
+ });
436
+
437
+ const excluded = controller.getExcludedPaths(source);
438
+
439
+ assert.ok(Array.isArray(excluded), 'getExcludedPaths must return an array');
440
+ assert.equal(excluded.length, 2, 'must return both excluded paths');
441
+ assert.ok(excluded.includes('secrets/**'), 'must include secrets/**');
442
+ assert.ok(excluded.includes('tmp/**'), 'must include tmp/**');
443
+ });
444
+
445
+ test('getExcludedPaths throws on null resource', () => {
446
+ const controller = createAgentMemorySourceController();
447
+
448
+ assert.throws(
449
+ () => controller.getExcludedPaths(null),
450
+ /null|undefined/i,
451
+ 'getExcludedPaths must throw on null resource'
452
+ );
453
+ });
454
+
455
+ // ---------------------------------------------------------------------------
456
+ // validateMemorySource standalone export
457
+ // ---------------------------------------------------------------------------
458
+
459
+ test('validateMemorySource standalone export follows existing pattern', () => {
460
+ assert.equal(typeof validateMemorySource, 'function', 'validateMemorySource must be a named export');
461
+
462
+ const source = makeMemorySource('standalone-validate-source');
463
+ const result = validateMemorySource(source);
464
+
465
+ assert.ok(result, 'must return a result');
466
+ assert.ok('valid' in result, 'result must have a valid property');
467
+ assert.ok(Array.isArray(result.errors), 'result must have an errors array');
468
+ assert.equal(result.valid, true, 'a fully-specified source must pass standalone validation');
469
+ });
470
+
471
+ // ---------------------------------------------------------------------------
472
+ // BOUNDARY — AgentMemorySource
473
+ // ---------------------------------------------------------------------------
474
+
475
+ test('AGENT_MEMORY_SOURCE_CONTROLLER_BOUNDARY is exported with correct role', () => {
476
+ assert.ok(AGENT_MEMORY_SOURCE_CONTROLLER_BOUNDARY, 'BOUNDARY must be exported');
477
+ assert.equal(
478
+ AGENT_MEMORY_SOURCE_CONTROLLER_BOUNDARY.role,
479
+ 'agent-memory-source-controller',
480
+ 'BOUNDARY role must be "agent-memory-source-controller"'
481
+ );
482
+ assert.ok(
483
+ Array.isArray(AGENT_MEMORY_SOURCE_CONTROLLER_BOUNDARY.owns),
484
+ 'BOUNDARY must declare owned concerns'
485
+ );
486
+ });
487
+
488
+ // ---------------------------------------------------------------------------
489
+ // validate — accumulates multiple errors (source)
490
+ // ---------------------------------------------------------------------------
491
+
492
+ test('validateMemorySource accumulates all errors when multiple fields are invalid', () => {
493
+ const controller = createAgentMemorySourceController();
494
+ const source = {
495
+ apiVersion: 'krate.a5c.ai/v1alpha1',
496
+ kind: 'AgentMemorySource',
497
+ metadata: { namespace: 'krate-org-default', labels: {}, annotations: {} },
498
+ spec: { organizationRef: 'acme', appliesTo: {}, include: {} },
499
+ status: {},
500
+ };
501
+
502
+ const result = controller.validateMemorySource(source);
503
+
504
+ assert.equal(result.valid, false, 'config with multiple missing fields must fail');
505
+ assert.ok(result.errors.length >= 2, 'must accumulate at least two errors');
506
+ assert.ok(
507
+ result.errors.some((e) => /name/i.test(e)),
508
+ 'errors must include a name error'
509
+ );
510
+ assert.ok(
511
+ result.errors.some((e) => /repositoryRef/i.test(e)),
512
+ 'errors must include a repositoryRef error'
513
+ );
514
+ });