@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,207 @@
1
+ /**
2
+ * B2: Write-back persistence + execution tests
3
+ * Tests for persistWriteIntent (saves AgentWriteIntent via applyResource),
4
+ * executeWriteIntent (calls gateway for branch push, validates status checks for PR merge).
5
+ */
6
+ import assert from 'node:assert/strict';
7
+ import test from 'node:test';
8
+ import { createAgentWritebackController } from '../src/index.js';
9
+
10
+ function makePushIntent(name, runRef, branch = 'feature/x', approvalStatus = 'pending') {
11
+ return {
12
+ kind: 'AgentWriteIntent',
13
+ metadata: { name, namespace: 'krate-org-default', labels: {}, annotations: {} },
14
+ spec: {
15
+ organizationRef: 'default',
16
+ runRef,
17
+ branch,
18
+ targetRepo: 'org/my-repo',
19
+ writeType: 'branch-push',
20
+ requestedBy: 'agent-stack-1',
21
+ requestedAt: new Date().toISOString()
22
+ },
23
+ status: { approvalStatus, phase: approvalStatus === 'approved' ? 'Approved' : 'Pending', createdAt: new Date().toISOString() }
24
+ };
25
+ }
26
+
27
+ function makeMergeIntent(name, runRef, prRef, approvalStatus = 'pending', statusChecks = []) {
28
+ return {
29
+ kind: 'AgentWriteIntent',
30
+ metadata: { name, namespace: 'krate-org-default', labels: {}, annotations: {} },
31
+ spec: {
32
+ organizationRef: 'default',
33
+ runRef,
34
+ prRef,
35
+ writeType: 'pr-merge',
36
+ statusChecks,
37
+ requestedBy: 'agent-stack-1',
38
+ requestedAt: new Date().toISOString()
39
+ },
40
+ status: { approvalStatus, phase: approvalStatus === 'approved' ? 'Approved' : 'Pending', createdAt: new Date().toISOString() }
41
+ };
42
+ }
43
+
44
+ // --- persistWriteIntent ---
45
+
46
+ test('persistWriteIntent calls applyResource with the intent and returns success', async () => {
47
+ const controller = createAgentWritebackController();
48
+ const intent = makePushIntent('push-persist-1', 'run-persist-1', 'feature/new-thing', 'pending');
49
+
50
+ const applied = [];
51
+ const mockApplyResource = async (resource) => {
52
+ applied.push(resource);
53
+ return { ok: true, resource };
54
+ };
55
+
56
+ const result = await controller.persistWriteIntent({ intent, applyResource: mockApplyResource });
57
+
58
+ assert.equal(result.error, false, 'Should succeed');
59
+ assert.equal(applied.length, 1, 'applyResource should be called exactly once');
60
+ assert.equal(applied[0].kind, 'AgentWriteIntent', 'Applied resource should be AgentWriteIntent');
61
+ assert.equal(applied[0].metadata.name, 'push-persist-1');
62
+ assert.equal(applied[0].spec.runRef, 'run-persist-1');
63
+ });
64
+
65
+ test('persistWriteIntent returns error when applyResource throws', async () => {
66
+ const controller = createAgentWritebackController();
67
+ const intent = makePushIntent('push-persist-2', 'run-persist-2', 'main', 'pending');
68
+
69
+ const mockApplyResource = async () => {
70
+ throw new Error('Storage backend unreachable');
71
+ };
72
+
73
+ const result = await controller.persistWriteIntent({ intent, applyResource: mockApplyResource });
74
+
75
+ assert.equal(result.error, true, 'Should return error when applyResource throws');
76
+ assert.equal(result.reason, 'persist-failed');
77
+ assert.ok(result.message, 'Should include error message');
78
+ });
79
+
80
+ test('persistWriteIntent returns error when intent is missing', async () => {
81
+ const controller = createAgentWritebackController();
82
+
83
+ const mockApplyResource = async () => ({ ok: true });
84
+ const result = await controller.persistWriteIntent({ intent: null, applyResource: mockApplyResource });
85
+
86
+ assert.equal(result.error, true, 'Should fail when intent is null');
87
+ assert.equal(result.reason, 'missing-intent');
88
+ });
89
+
90
+ test('persistWriteIntent returns error when applyResource is not provided', async () => {
91
+ const controller = createAgentWritebackController();
92
+ const intent = makePushIntent('push-persist-3', 'run-persist-3', 'develop', 'pending');
93
+
94
+ const result = await controller.persistWriteIntent({ intent, applyResource: null });
95
+
96
+ assert.equal(result.error, true, 'Should fail without applyResource');
97
+ assert.equal(result.reason, 'missing-apply-resource');
98
+ });
99
+
100
+ test('persistWriteIntent passes applyResource result back in response', async () => {
101
+ const controller = createAgentWritebackController();
102
+ const intent = makePushIntent('push-persist-4', 'run-persist-4', 'hotfix/urgent', 'approved');
103
+
104
+ const mockApplyResult = { ok: true, uid: 'some-uid-abc' };
105
+ const mockApplyResource = async () => mockApplyResult;
106
+
107
+ const result = await controller.persistWriteIntent({ intent, applyResource: mockApplyResource });
108
+
109
+ assert.equal(result.error, false, 'Should succeed');
110
+ assert.deepEqual(result.applyResult, mockApplyResult, 'Should include applyResource result');
111
+ });
112
+
113
+ // --- executeWriteIntent ---
114
+
115
+ test('executeWriteIntent calls gateway for approved branch-push intent', async () => {
116
+ const controller = createAgentWritebackController();
117
+ const intent = makePushIntent('push-exec-1', 'run-exec-1', 'feature/ready', 'approved');
118
+
119
+ const gatewayCalls = [];
120
+ const mockGateway = {
121
+ async pushBranch({ branch, targetRepo, runRef }) {
122
+ gatewayCalls.push({ branch, targetRepo, runRef });
123
+ return { ok: true, sha: 'abc123def456' };
124
+ }
125
+ };
126
+
127
+ const result = await controller.executeWriteIntent({ intent, gateway: mockGateway });
128
+
129
+ assert.equal(result.error, false, 'Should succeed');
130
+ assert.equal(gatewayCalls.length, 1, 'Gateway pushBranch should be called once');
131
+ assert.equal(gatewayCalls[0].branch, 'feature/ready');
132
+ assert.equal(gatewayCalls[0].targetRepo, 'org/my-repo');
133
+ assert.equal(gatewayCalls[0].runRef, 'run-exec-1');
134
+ assert.ok(result.executionResult, 'Should return execution result');
135
+ });
136
+
137
+ test('executeWriteIntent blocks execution when intent approval is still pending', async () => {
138
+ const controller = createAgentWritebackController();
139
+ const intent = makePushIntent('push-exec-2', 'run-exec-2', 'feature/unreviewed', 'pending');
140
+
141
+ const mockGateway = {
142
+ async pushBranch() { return { ok: true }; }
143
+ };
144
+
145
+ const result = await controller.executeWriteIntent({ intent, gateway: mockGateway });
146
+
147
+ assert.equal(result.error, true, 'Should block when not approved');
148
+ assert.equal(result.reason, 'not-approved');
149
+ });
150
+
151
+ test('executeWriteIntent validates status checks are passing for pr-merge intent', async () => {
152
+ const controller = createAgentWritebackController();
153
+ const intent = makeMergeIntent('merge-exec-1', 'run-exec-3', 'pr-55', 'approved', [
154
+ { name: 'ci/build', state: 'success' },
155
+ { name: 'ci/tests', state: 'success' }
156
+ ]);
157
+
158
+ const gatewayCalls = [];
159
+ const mockGateway = {
160
+ async mergePr({ prRef, runRef }) {
161
+ gatewayCalls.push({ prRef, runRef });
162
+ return { ok: true, mergeCommit: 'deadbeef' };
163
+ }
164
+ };
165
+
166
+ const result = await controller.executeWriteIntent({ intent, gateway: mockGateway });
167
+
168
+ assert.equal(result.error, false, 'Should succeed when checks pass and intent is approved');
169
+ assert.equal(gatewayCalls.length, 1, 'Gateway mergePr should be called once');
170
+ assert.equal(gatewayCalls[0].prRef, 'pr-55');
171
+ assert.ok(result.executionResult, 'Should return execution result');
172
+ });
173
+
174
+ test('executeWriteIntent rejects pr-merge when status checks are failing', async () => {
175
+ const controller = createAgentWritebackController();
176
+ const intent = makeMergeIntent('merge-exec-2', 'run-exec-4', 'pr-66', 'approved', [
177
+ { name: 'ci/build', state: 'success' },
178
+ { name: 'security/scan', state: 'failure' }
179
+ ]);
180
+
181
+ const mockGateway = {
182
+ async mergePr() { return { ok: true }; }
183
+ };
184
+
185
+ const result = await controller.executeWriteIntent({ intent, gateway: mockGateway });
186
+
187
+ assert.equal(result.error, true, 'Should block when checks are failing');
188
+ assert.equal(result.reason, 'status-checks-failing');
189
+ assert.ok(result.message.includes('security/scan'), 'Message should name the failing check');
190
+ });
191
+
192
+ test('executeWriteIntent returns error when gateway throws', async () => {
193
+ const controller = createAgentWritebackController();
194
+ const intent = makePushIntent('push-exec-3', 'run-exec-5', 'feature/crash', 'approved');
195
+
196
+ const mockGateway = {
197
+ async pushBranch() {
198
+ throw new Error('Git remote unreachable');
199
+ }
200
+ };
201
+
202
+ const result = await controller.executeWriteIntent({ intent, gateway: mockGateway });
203
+
204
+ assert.equal(result.error, true, 'Should return error when gateway throws');
205
+ assert.equal(result.reason, 'execution-failed');
206
+ assert.ok(result.message, 'Should include error message');
207
+ });