@kontextmind/kxm 0.6.0

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 (227) hide show
  1. package/.claude-plugin/marketplace.json +19 -0
  2. package/.kxm/README.md +14 -0
  3. package/.kxm/assets/README.md +5 -0
  4. package/.kxm/assets/retrospectives/README.md +5 -0
  5. package/.kxm/config/README.md +5 -0
  6. package/.kxm/config/agents.json +43 -0
  7. package/.kxm/config/env.example +56 -0
  8. package/.kxm/config/update.example.yaml +9 -0
  9. package/.kxm/config/workflows/fix.json +160 -0
  10. package/.kxm/config/workflows/jira-development.json +116 -0
  11. package/.kxm/config/workflows/provenance-quorum.json +150 -0
  12. package/.kxm/config/workflows/v04-dogfood.json +72 -0
  13. package/CHANGELOG.md +465 -0
  14. package/LICENSE +21 -0
  15. package/README.md +306 -0
  16. package/SECURITY.md +72 -0
  17. package/docs/README.md +48 -0
  18. package/docs/agent-communication-envelopes-and-gates.md +553 -0
  19. package/docs/architecture.md +242 -0
  20. package/docs/assignment-runner.md +241 -0
  21. package/docs/configuration.md +361 -0
  22. package/docs/continuous-improvement.md +114 -0
  23. package/docs/getting-started.md +253 -0
  24. package/docs/kxm-handbook.md +1090 -0
  25. package/docs/operations.md +205 -0
  26. package/docs/provenance-gates.md +291 -0
  27. package/docs/skills.md +45 -0
  28. package/docs/templates/README.md +95 -0
  29. package/docs/templates/adr.md +88 -0
  30. package/docs/templates/architecture.md +120 -0
  31. package/docs/templates/bug-fix.md +109 -0
  32. package/docs/templates/feature.md +108 -0
  33. package/docs/templates/handoff.md +72 -0
  34. package/docs/templates/postmortem.md +77 -0
  35. package/docs/templates/research.md +100 -0
  36. package/docs/templates/review.md +85 -0
  37. package/docs/templates/runbook.md +73 -0
  38. package/docs/templates/test-plan.md +87 -0
  39. package/docs/templates/test-report.md +72 -0
  40. package/docs/test-matrix.md +121 -0
  41. package/docs/troubleshooting.md +249 -0
  42. package/docs/vnext/README.md +62 -0
  43. package/docs/vnext/architecture.md +185 -0
  44. package/docs/vnext/effects-and-recovery.md +172 -0
  45. package/docs/vnext/lifecycles.md +235 -0
  46. package/docs/vnext/migration.md +220 -0
  47. package/docs/vnext/routing.md +184 -0
  48. package/docs/vnext/synchronization.md +172 -0
  49. package/docs/vnext/terminology.md +240 -0
  50. package/docs/vnext/validation.md +335 -0
  51. package/docs/webhook-workflows.md +240 -0
  52. package/docs/workflow-guide.md +1150 -0
  53. package/examples/README.md +102 -0
  54. package/examples/provenance-workflow.json +40 -0
  55. package/examples/requester.ts +30 -0
  56. package/examples/reviewer-agent.ts +29 -0
  57. package/examples/roundtrip.ts +46 -0
  58. package/examples/vnext/.kxm/agents/coordinator.yaml +16 -0
  59. package/examples/vnext/.kxm/agents/critic-1.yaml +16 -0
  60. package/examples/vnext/.kxm/agents/critic-2.yaml +15 -0
  61. package/examples/vnext/.kxm/agents/critic-3.yaml +15 -0
  62. package/examples/vnext/.kxm/agents/implementer.yaml +15 -0
  63. package/examples/vnext/.kxm/agents/planner.yaml +13 -0
  64. package/examples/vnext/.kxm/agents/reproducer.yaml +15 -0
  65. package/examples/vnext/.kxm/agents/reviewer.yaml +15 -0
  66. package/examples/vnext/.kxm/gates.yaml +8 -0
  67. package/examples/vnext/.kxm/models/critic-claude.yaml +11 -0
  68. package/examples/vnext/.kxm/models/critic-gemini.yaml +11 -0
  69. package/examples/vnext/.kxm/models/critic-grok.yaml +12 -0
  70. package/examples/vnext/.kxm/models/implementation.yaml +14 -0
  71. package/examples/vnext/.kxm/models/primary.yaml +17 -0
  72. package/examples/vnext/.kxm/prices.yaml +111 -0
  73. package/examples/vnext/.kxm/project/env.yaml +7 -0
  74. package/examples/vnext/.kxm/project.yaml +32 -0
  75. package/examples/vnext/.kxm/repo/repo.yaml +8 -0
  76. package/examples/vnext/.kxm/workflows/default.yaml +92 -0
  77. package/examples/vnext/.kxm/workflows/fix.yaml +376 -0
  78. package/examples/vnext/.kxm/workflows/improve.yaml +57 -0
  79. package/examples/vnext/README.md +53 -0
  80. package/examples/vnext/records/assignment-result-recorded.json +63 -0
  81. package/examples/vnext/records/assignment-result.json +46 -0
  82. package/examples/vnext/records/context-candidate.json +42 -0
  83. package/examples/vnext/records/delivery-manifest.json +66 -0
  84. package/examples/vnext/records/effect-uncertainty-resolved-sync.json +67 -0
  85. package/examples/vnext/records/effect-uncertainty-resolved.json +62 -0
  86. package/examples/vnext/records/run-created.json +54 -0
  87. package/examples/vnext/records/sync-event.json +65 -0
  88. package/examples/vnext/repositories/api/.kxm/repo/env.yaml +7 -0
  89. package/examples/vnext/repositories/api/.kxm/repo/repo.yaml +8 -0
  90. package/examples/vnext/repositories/web/.kxm/repo/repo.yaml +8 -0
  91. package/examples/workflow-signal.ts +63 -0
  92. package/package.json +129 -0
  93. package/plugins/kxm/.claude-plugin/plugin.json +73 -0
  94. package/plugins/kxm/.mcp.json +19 -0
  95. package/plugins/kxm/README.md +93 -0
  96. package/plugins/kxm/dist/cli.js +42853 -0
  97. package/plugins/kxm/dist/client.js +416 -0
  98. package/plugins/kxm/dist/core.js +1823 -0
  99. package/plugins/kxm/dist/extension.js +3797 -0
  100. package/plugins/kxm/dist/mcp-server.js +17104 -0
  101. package/plugins/kxm/dist/runtime.js +23361 -0
  102. package/plugins/kxm/dist/server.js +13640 -0
  103. package/plugins/kxm/dist/vnext-runtime-supervisor.js +21109 -0
  104. package/plugins/kxm/package.json +12 -0
  105. package/plugins/kxm/skills/kxm/SKILL.md +97 -0
  106. package/plugins/kxm/skills/kxm/references/protocol.md +103 -0
  107. package/plugins/kxm/skills/kxm-session/SKILL.md +53 -0
  108. package/plugins/kxm/src/arbiter.ts +355 -0
  109. package/plugins/kxm/src/artifacts-exist.ts +62 -0
  110. package/plugins/kxm/src/autocomplete.ts +236 -0
  111. package/plugins/kxm/src/cli.ts +3707 -0
  112. package/plugins/kxm/src/client.ts +614 -0
  113. package/plugins/kxm/src/commands.ts +1063 -0
  114. package/plugins/kxm/src/config.ts +290 -0
  115. package/plugins/kxm/src/context/providers.ts +101 -0
  116. package/plugins/kxm/src/context-packet.ts +332 -0
  117. package/plugins/kxm/src/context.ts +499 -0
  118. package/plugins/kxm/src/core.ts +6 -0
  119. package/plugins/kxm/src/database.ts +563 -0
  120. package/plugins/kxm/src/diagnostics.ts +184 -0
  121. package/plugins/kxm/src/envelope.ts +118 -0
  122. package/plugins/kxm/src/extension.ts +895 -0
  123. package/plugins/kxm/src/external-effects.ts +299 -0
  124. package/plugins/kxm/src/github-watch.ts +255 -0
  125. package/plugins/kxm/src/hub-binding.ts +160 -0
  126. package/plugins/kxm/src/hub.ts +2502 -0
  127. package/plugins/kxm/src/improve.ts +383 -0
  128. package/plugins/kxm/src/inbox.ts +10 -0
  129. package/plugins/kxm/src/kxm-install-kind.ts +113 -0
  130. package/plugins/kxm/src/kxm-update-config.ts +39 -0
  131. package/plugins/kxm/src/kxm-update.ts +238 -0
  132. package/plugins/kxm/src/local-snapshot.ts +406 -0
  133. package/plugins/kxm/src/logger.ts +198 -0
  134. package/plugins/kxm/src/mcp-server.ts +143 -0
  135. package/plugins/kxm/src/memory.ts +385 -0
  136. package/plugins/kxm/src/nous-pi.ts +287 -0
  137. package/plugins/kxm/src/nous-provider.ts +729 -0
  138. package/plugins/kxm/src/price-calc.ts +87 -0
  139. package/plugins/kxm/src/prices.ts +121 -0
  140. package/plugins/kxm/src/protocol.ts +172 -0
  141. package/plugins/kxm/src/recovery.ts +211 -0
  142. package/plugins/kxm/src/redact.ts +26 -0
  143. package/plugins/kxm/src/retrospective.ts +400 -0
  144. package/plugins/kxm/src/routing.ts +830 -0
  145. package/plugins/kxm/src/runtime.ts +9 -0
  146. package/plugins/kxm/src/server.ts +117 -0
  147. package/plugins/kxm/src/session-work.ts +571 -0
  148. package/plugins/kxm/src/session.ts +184 -0
  149. package/plugins/kxm/src/skills.ts +535 -0
  150. package/plugins/kxm/src/state.ts +326 -0
  151. package/plugins/kxm/src/store.ts +637 -0
  152. package/plugins/kxm/src/studio-layout.ts +268 -0
  153. package/plugins/kxm/src/suggest.ts +162 -0
  154. package/plugins/kxm/src/task-manager.ts +244 -0
  155. package/plugins/kxm/src/telemetry.ts +116 -0
  156. package/plugins/kxm/src/tui.ts +1046 -0
  157. package/plugins/kxm/src/vnext-bindings.ts +403 -0
  158. package/plugins/kxm/src/vnext-config.ts +1646 -0
  159. package/plugins/kxm/src/vnext-engine-artifacts.ts +86 -0
  160. package/plugins/kxm/src/vnext-engine-command.ts +533 -0
  161. package/plugins/kxm/src/vnext-engine-compile.ts +722 -0
  162. package/plugins/kxm/src/vnext-engine-evidence.ts +273 -0
  163. package/plugins/kxm/src/vnext-engine-fold.ts +1400 -0
  164. package/plugins/kxm/src/vnext-engine-gate-records.ts +583 -0
  165. package/plugins/kxm/src/vnext-engine-plan.ts +717 -0
  166. package/plugins/kxm/src/vnext-engine.ts +2458 -0
  167. package/plugins/kxm/src/vnext-gate-hash.ts +10 -0
  168. package/plugins/kxm/src/vnext-harness.ts +1142 -0
  169. package/plugins/kxm/src/vnext-init.ts +430 -0
  170. package/plugins/kxm/src/vnext-migrate.ts +1848 -0
  171. package/plugins/kxm/src/vnext-oneshot-producer.ts +424 -0
  172. package/plugins/kxm/src/vnext-permission.ts +936 -0
  173. package/plugins/kxm/src/vnext-pi-producer.ts +628 -0
  174. package/plugins/kxm/src/vnext-repair.ts +1094 -0
  175. package/plugins/kxm/src/vnext-runtime-owner.ts +320 -0
  176. package/plugins/kxm/src/vnext-runtime-store.ts +1560 -0
  177. package/plugins/kxm/src/vnext-runtime-supervisor.ts +586 -0
  178. package/plugins/kxm/src/vnext-runtime.ts +663 -0
  179. package/plugins/kxm/src/vnext-template.ts +247 -0
  180. package/plugins/kxm/src/wiki.ts +313 -0
  181. package/plugins/kxm/src/workflow.ts +1548 -0
  182. package/schemas/vnext/README.md +46 -0
  183. package/schemas/vnext/agent.schema.json +40 -0
  184. package/schemas/vnext/assignment-result.schema.json +66 -0
  185. package/schemas/vnext/backup-manifest.schema.json +89 -0
  186. package/schemas/vnext/candidate.schema.json +109 -0
  187. package/schemas/vnext/common.schema.json +422 -0
  188. package/schemas/vnext/context-candidate.schema.json +76 -0
  189. package/schemas/vnext/context-packet.schema.json +192 -0
  190. package/schemas/vnext/delivery-manifest.schema.json +159 -0
  191. package/schemas/vnext/environment.schema.json +66 -0
  192. package/schemas/vnext/gate-registry.schema.json +109 -0
  193. package/schemas/vnext/handoff-manifest.schema.json +146 -0
  194. package/schemas/vnext/init-operation.schema.json +61 -0
  195. package/schemas/vnext/local-repository-bindings.schema.json +30 -0
  196. package/schemas/vnext/memory-record.schema.json +45 -0
  197. package/schemas/vnext/migration-decision.schema.json +26 -0
  198. package/schemas/vnext/migration-plan.schema.json +123 -0
  199. package/schemas/vnext/migration-receipt.schema.json +52 -0
  200. package/schemas/vnext/model.schema.json +42 -0
  201. package/schemas/vnext/permission-diff.schema.json +57 -0
  202. package/schemas/vnext/prices.schema.json +115 -0
  203. package/schemas/vnext/project.schema.json +85 -0
  204. package/schemas/vnext/repository.schema.json +24 -0
  205. package/schemas/vnext/run-event.schema.json +460 -0
  206. package/schemas/vnext/session-brief.schema.json +153 -0
  207. package/schemas/vnext/sync-event.schema.json +234 -0
  208. package/schemas/vnext/template-provenance.schema.json +38 -0
  209. package/schemas/vnext/workflow.schema.json +248 -0
  210. package/scripts/assignment-run.d.mts +354 -0
  211. package/scripts/assignment-run.mjs +4451 -0
  212. package/scripts/build-runtime.mjs +56 -0
  213. package/scripts/check-generated.mjs +77 -0
  214. package/scripts/check-versions.mjs +34 -0
  215. package/scripts/emit-codex-artifacts.d.mts +9 -0
  216. package/scripts/emit-codex-artifacts.mjs +91 -0
  217. package/scripts/harness-run.d.mts +83 -0
  218. package/scripts/harness-run.mjs +2095 -0
  219. package/scripts/kxm-hub.mjs +105 -0
  220. package/scripts/kxm-publish-npm.mjs +327 -0
  221. package/scripts/kxm-release-github.mjs +472 -0
  222. package/scripts/kxm-runtime-supervisor.mjs +7 -0
  223. package/scripts/kxm-worker.mjs +1127 -0
  224. package/scripts/kxm.mjs +27 -0
  225. package/scripts/roster-policy.d.mts +20 -0
  226. package/scripts/roster-policy.mjs +161 -0
  227. package/scripts/smoke-multi-pi.mjs +479 -0
@@ -0,0 +1,583 @@
1
+ import { gateDefinitionHash } from "./vnext-gate-hash.ts";
2
+ import { mintVnextCapabilitySecret, vnextTransitionBudgetFailure } from "./vnext-engine.ts";
3
+ import type { VnextRunPlanEnvelope } from "./vnext-engine-plan.ts";
4
+ import { vnextCanonicalJson, type JsonValue } from "./vnext-config.ts";
5
+ import {
6
+ foldStoredVnextRun,
7
+ persistVnextRunState,
8
+ vnextEventBase,
9
+ vnextIncrementMonotonicNs,
10
+ vnextMonotonicNs,
11
+ type VnextRuntimeContext,
12
+ } from "./vnext-runtime.ts";
13
+ import {
14
+ assertClosedGateObservation,
15
+ computeGateEvidenceOutcome,
16
+ gateRowContentHash,
17
+ newVnextAssignmentId,
18
+ newVnextAttemptId,
19
+ newVnextEffectId,
20
+ newVnextEventId,
21
+ newVnextEvidenceId,
22
+ newVnextObservationId,
23
+ runtimeError,
24
+ type VnextGateErrorClass,
25
+ type VnextGateEvidenceOutcome,
26
+ type VnextGateEvidenceRow,
27
+ type VnextGateObservationRow,
28
+ type VnextGateStopCause,
29
+ type VnextRunEvent,
30
+ type VnextRunRecord,
31
+ } from "./vnext-runtime-store.ts";
32
+
33
+ export interface VnextGateObservationInput {
34
+ completeness: VnextGateObservationRow["completeness"];
35
+ spawned: 0 | 1;
36
+ pid: number | null;
37
+ exitCode: number | null;
38
+ signal: string | null;
39
+ exitObserved: 0 | 1;
40
+ closeObserved: 0 | 1;
41
+ stopCause: VnextGateStopCause;
42
+ signalsAttempted: VnextGateObservationRow["signalsAttempted"];
43
+ errorClass: VnextGateErrorClass | null;
44
+ stdoutSha256: string | null;
45
+ stdoutBytes: number | null;
46
+ stdoutComplete: 0 | 1 | null;
47
+ stderrSha256: string | null;
48
+ stderrBytes: number | null;
49
+ stderrComplete: 0 | 1 | null;
50
+ checkedCount: number | null;
51
+ failedCount: number | null;
52
+ elapsedMs: number;
53
+ startedAt: string;
54
+ finishedAt: string | null;
55
+ }
56
+
57
+ export function recordGateIntent(context: VnextRuntimeContext, run: VnextRunRecord, envelope: VnextRunPlanEnvelope, stepId: string) {
58
+ return context.eventStore.transaction(() => recordGateIntentInTransaction(context, run, envelope, stepId));
59
+ }
60
+
61
+ export function recordGateIntentInTransaction(context: VnextRuntimeContext, run: VnextRunRecord, envelope: VnextRunPlanEnvelope, stepId: string) {
62
+ const step = requireGateStep(envelope, stepId, run.runId);
63
+ const definition = envelope.gates.definitions[step.gate];
64
+ if (!definition || definition.kind === "reserved") {
65
+ throw runtimeError("gate_row_invalid", run.runId, "reserved gates cannot record intent in this slice");
66
+ }
67
+ const folded = foldStoredVnextRun(context, run);
68
+ const used = folded.stepAttempts[stepId] ?? 0;
69
+ const stepAttempt = used + 1;
70
+ const assignmentId = newVnextAssignmentId();
71
+ const attemptId = newVnextAttemptId();
72
+ const effectId = newVnextEffectId();
73
+ const minted = mintVnextCapabilitySecret();
74
+ const now = new Date().toISOString();
75
+ const seq = sequencer(context, run, now);
76
+ seq.push("step.entered", { stepId, stepAttempt, status: "pending" });
77
+ seq.push("step.status_changed", { stepId, status: "preparing", previousStatus: "pending" });
78
+ seq.push("assignment.created", { assignmentId, stepId, stepAttempt, agentId: "coordinator", status: "created" });
79
+ seq.push("assignment.accepted", { assignmentId, stepId, stepAttempt, attemptId, status: "accepted" });
80
+ seq.push("attempt.created", { attemptId, assignmentId, stepId, stepAttempt, status: "created" });
81
+ seq.push("assignment.dispatched", { assignmentId, stepId, stepAttempt, attemptId, capabilityHash: minted.hash, status: "dispatched" });
82
+ seq.push("attempt.status_changed", { attemptId, assignmentId, stepId, stepAttempt, status: "starting" });
83
+ seq.push("step.status_changed", { stepId, status: "running", previousStatus: "preparing" });
84
+ const policyClass = definition.kind === "artifacts-exist" ? "read-only" : "unknown";
85
+ const intent = seq.push("effect.intent_recorded", {
86
+ effect: { id: effectId, policy: { class: policyClass, sharedMutable: false } },
87
+ stepId,
88
+ stepAttempt,
89
+ assignmentId,
90
+ attemptId,
91
+ });
92
+ for (const event of seq.events) context.eventStore.appendEvent(event);
93
+ context.eventStore.insertCapability({
94
+ attemptId,
95
+ runId: run.runId,
96
+ assignmentId,
97
+ stepId,
98
+ stepAttempt,
99
+ producerId: "kxm-gate",
100
+ capabilityHash: minted.hash,
101
+ state: "issued",
102
+ });
103
+ const attemptRow = {
104
+ attemptId,
105
+ runId: run.runId,
106
+ projectId: run.projectId,
107
+ homeRuntimeId: run.homeRuntimeId,
108
+ stepId,
109
+ stepAttempt,
110
+ assignmentId,
111
+ effectId,
112
+ gateId: step.gate,
113
+ gateKind: definition.kind,
114
+ expect: step.expect,
115
+ gateDefinitionHash: gateDefinitionHash(step.gate, definition as unknown as JsonValue),
116
+ registryHash: envelope.gates.registry!.hash,
117
+ runPlanHash: envelope && folded.runPlanHash ? folded.runPlanHash : hashFromStore(context, run.runId),
118
+ controlProjectKey: envelope.gates.controlRoot.projectKey,
119
+ producerId: "kxm-gate" as const,
120
+ intentEventId: intent.eventId,
121
+ contentHash: "",
122
+ };
123
+ attemptRow.runPlanHash = context.eventStore.runPlan(run.runId)!.runPlanHash;
124
+ const withHash = { ...attemptRow, contentHash: gateRowContentHash("gate_attempts", { ...attemptRow, contentHash: "" }) };
125
+ context.eventStore.insertGateAttempt(withHash);
126
+ return persist(context, run, now, seq.events.at(-1)!.sequence);
127
+ }
128
+
129
+ export function recordGateSpawned(context: VnextRuntimeContext, run: VnextRunRecord, envelope: VnextRunPlanEnvelope, stepId: string) {
130
+ return context.eventStore.transaction(() => recordGateSpawnedInTransaction(context, run, envelope, stepId));
131
+ }
132
+
133
+ export function recordGateSpawnedInTransaction(context: VnextRuntimeContext, run: VnextRunRecord, envelope: VnextRunPlanEnvelope, stepId: string) {
134
+ const ids = requireIntent(context, run, envelope, stepId);
135
+ if (ids.gateKind !== "command") throw runtimeError("gate_row_invalid", run.runId, "effect.dispatched is command-only");
136
+ const now = new Date().toISOString();
137
+ const seq = sequencer(context, run, now);
138
+ seq.push("assignment.executing", { assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, attemptId: ids.attemptId, status: "executing" });
139
+ seq.push("attempt.status_changed", { attemptId: ids.attemptId, assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, status: "executing" });
140
+ seq.push("effect.dispatched", {
141
+ effect: { id: ids.effectId, policy: { class: "unknown", sharedMutable: false } },
142
+ stepId,
143
+ stepAttempt: ids.stepAttempt,
144
+ assignmentId: ids.assignmentId,
145
+ attemptId: ids.attemptId,
146
+ });
147
+ for (const event of seq.events) context.eventStore.appendEvent(event);
148
+ return persist(context, run, now, seq.events.at(-1)!.sequence);
149
+ }
150
+
151
+ export function recordGateSettlement(
152
+ context: VnextRuntimeContext,
153
+ run: VnextRunRecord,
154
+ envelope: VnextRunPlanEnvelope,
155
+ stepId: string,
156
+ observation: VnextGateObservationInput,
157
+ ) {
158
+ return context.eventStore.transaction(() => recordGateSettlementInTransaction(context, run, envelope, stepId, observation));
159
+ }
160
+
161
+ export function recordGateSettlementInTransaction(
162
+ context: VnextRuntimeContext,
163
+ run: VnextRunRecord,
164
+ envelope: VnextRunPlanEnvelope,
165
+ stepId: string,
166
+ observation: VnextGateObservationInput,
167
+ ) {
168
+ const ids = requireIntent(context, run, envelope, stepId);
169
+ assertClosedGateObservation(ids.gateKind, observation);
170
+ const outcome = computeGateEvidenceOutcome(ids.gateKind, ids.expect, observation);
171
+ const now = new Date().toISOString();
172
+ const seq = sequencer(context, run, now);
173
+ if (ids.gateKind === "artifacts-exist") {
174
+ seq.push("assignment.executing", { assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, attemptId: ids.attemptId, status: "executing" });
175
+ seq.push("attempt.status_changed", { attemptId: ids.attemptId, assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, status: "executing" });
176
+ seq.push("effect.dispatched", {
177
+ effect: { id: ids.effectId, policy: { class: "read-only", sharedMutable: false } },
178
+ stepId,
179
+ stepAttempt: ids.stepAttempt,
180
+ assignmentId: ids.assignmentId,
181
+ attemptId: ids.attemptId,
182
+ });
183
+ }
184
+ seq.push("attempt.status_changed", { attemptId: ids.attemptId, assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, status: "settling" });
185
+ const observationId = newVnextObservationId();
186
+ const observed = seq.push("effect.observed", {
187
+ effect: { id: ids.effectId, policy: { class: ids.gateKind === "artifacts-exist" ? "read-only" : "unknown", sharedMutable: false } },
188
+ stepId,
189
+ stepAttempt: ids.stepAttempt,
190
+ assignmentId: ids.assignmentId,
191
+ attemptId: ids.attemptId,
192
+ receipt: placeholderReceipt(observationId, ids.gateKind, now),
193
+ });
194
+ const observationRow = buildObservation(ids, observation, observationId, observed.eventId);
195
+ patchReceipt(observed, observationRow);
196
+ const evidenceId = newVnextEvidenceId();
197
+ const evidenceDraft: VnextGateEvidenceRow = {
198
+ evidenceId,
199
+ attemptId: ids.attemptId,
200
+ observationId,
201
+ runId: run.runId,
202
+ projectId: run.projectId,
203
+ homeRuntimeId: run.homeRuntimeId,
204
+ stepId,
205
+ stepAttempt: ids.stepAttempt,
206
+ assignmentId: ids.assignmentId,
207
+ effectId: ids.effectId,
208
+ evidenceKey: evidenceKeyFor(envelope, stepId),
209
+ kind: "gate",
210
+ expect: ids.expect,
211
+ outcome,
212
+ settledEventId: "evt_pending00000000000000000000000",
213
+ contentHash: "",
214
+ };
215
+ const settled = seq.push("effect.settled", {
216
+ effect: { id: ids.effectId, policy: { class: ids.gateKind === "artifacts-exist" ? "read-only" : "unknown", sharedMutable: false } },
217
+ stepId,
218
+ stepAttempt: ids.stepAttempt,
219
+ assignmentId: ids.assignmentId,
220
+ attemptId: ids.attemptId,
221
+ outcome,
222
+ evidenceRefs: [{ id: evidenceId, kind: "gate", hash: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", status: "settled" }],
223
+ });
224
+ evidenceDraft.settledEventId = settled.eventId;
225
+ evidenceDraft.contentHash = gateRowContentHash("gate_evidence", { ...evidenceDraft, contentHash: "" });
226
+ (settled.payload.evidenceRefs as Array<{ hash: string }>)[0]!.hash = evidenceDraft.contentHash;
227
+ appendEvaluatedTail(seq, envelope, ids, stepId, outcome);
228
+ for (const event of seq.events) context.eventStore.appendEvent(event);
229
+ context.eventStore.insertGateObservation(observationRow);
230
+ context.eventStore.insertGateEvidence(evidenceDraft);
231
+ context.eventStore.settleCapability(ids.attemptId, "settled");
232
+ return persist(context, run, now, seq.events.at(-1)!.sequence);
233
+ }
234
+
235
+ export function recordGateNoStart(
236
+ context: VnextRuntimeContext,
237
+ run: VnextRunRecord,
238
+ envelope: VnextRunPlanEnvelope,
239
+ stepId: string,
240
+ observation: VnextGateObservationInput,
241
+ ) {
242
+ return context.eventStore.transaction(() => recordGateNoStartInTransaction(context, run, envelope, stepId, observation));
243
+ }
244
+
245
+ export function recordGateNoStartInTransaction(
246
+ context: VnextRuntimeContext,
247
+ run: VnextRunRecord,
248
+ envelope: VnextRunPlanEnvelope,
249
+ stepId: string,
250
+ observation: VnextGateObservationInput,
251
+ ) {
252
+ const ids = requireIntent(context, run, envelope, stepId);
253
+ assertClosedGateObservation(ids.gateKind, observation);
254
+ if (observation.completeness !== "no-start") {
255
+ throw runtimeError("gate_row_invalid", run.runId, "no-start proof requires spawned 0 and completeness no-start");
256
+ }
257
+ const cancelled = observation.stopCause === "cancel";
258
+ const now = new Date().toISOString();
259
+ const seq = sequencer(context, run, now);
260
+ if (cancelled) ensureOperatorCancel(seq, ids.state, run);
261
+ const observationId = newVnextObservationId();
262
+ const observed = seq.push("effect.observed", {
263
+ effect: { id: ids.effectId, policy: { class: ids.gateKind === "artifacts-exist" ? "read-only" : "unknown", sharedMutable: false } },
264
+ stepId,
265
+ stepAttempt: ids.stepAttempt,
266
+ assignmentId: ids.assignmentId,
267
+ attemptId: ids.attemptId,
268
+ receipt: placeholderReceipt(observationId, ids.gateKind, now),
269
+ });
270
+ const observationRow = buildObservation(ids, observation, observationId, observed.eventId);
271
+ patchReceipt(observed, observationRow);
272
+ seq.push("attempt.status_changed", { attemptId: ids.attemptId, assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, status: "settling" });
273
+ seq.push("effect.settled", {
274
+ effect: { id: ids.effectId, policy: { class: ids.gateKind === "artifacts-exist" ? "read-only" : "unknown", sharedMutable: false } },
275
+ stepId,
276
+ stepAttempt: ids.stepAttempt,
277
+ assignmentId: ids.assignmentId,
278
+ attemptId: ids.attemptId,
279
+ receipt: {
280
+ provider: "kxm-gate",
281
+ kind: ids.gateKind,
282
+ id: observationId,
283
+ hash: observationRow.contentHash,
284
+ observedAt: now,
285
+ },
286
+ });
287
+ if (cancelled) {
288
+ seq.push("assignment.result_recorded", { assignmentId: ids.assignmentId, resultClass: "cancelled", status: "result_recorded" });
289
+ seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
290
+ seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome: "cancelled", status: "terminal" });
291
+ seq.push("step.status_changed", { stepId, status: "cancelled", previousStatus: "running" });
292
+ seq.push("run.status_changed", { status: "cancelled", reason: "operator_cancel", stepId });
293
+ } else {
294
+ seq.push("assignment.result_recorded", { assignmentId: ids.assignmentId, resultClass: "producer_rejected", status: "result_recorded" });
295
+ seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
296
+ seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome: "failed", status: "terminal" });
297
+ seq.push("step.status_changed", { stepId, status: "failed", previousStatus: "running" });
298
+ seq.push("run.status_changed", { status: "failed", reason: "gate_start_failed", stepId });
299
+ }
300
+ for (const event of seq.events) context.eventStore.appendEvent(event);
301
+ context.eventStore.insertGateObservation(observationRow);
302
+ context.eventStore.settleCapability(ids.attemptId, "settled");
303
+ return persist(context, run, now, seq.events.at(-1)!.sequence);
304
+ }
305
+
306
+ export function recordGateUncertain(
307
+ context: VnextRuntimeContext,
308
+ run: VnextRunRecord,
309
+ envelope: VnextRunPlanEnvelope,
310
+ stepId: string,
311
+ reason: string,
312
+ observation: VnextGateObservationInput,
313
+ ) {
314
+ return context.eventStore.transaction(() => recordGateUncertainInTransaction(context, run, envelope, stepId, reason, observation));
315
+ }
316
+
317
+ export function recordGateUncertainInTransaction(
318
+ context: VnextRuntimeContext,
319
+ run: VnextRunRecord,
320
+ envelope: VnextRunPlanEnvelope,
321
+ stepId: string,
322
+ reason: string,
323
+ observation: VnextGateObservationInput,
324
+ ) {
325
+ const ids = requireIntent(context, run, envelope, stepId);
326
+ if (observation.completeness !== "incomplete") {
327
+ throw runtimeError("gate_row_invalid", run.runId, "uncertainty observation must be incomplete");
328
+ }
329
+ const effectState = ids.state.currentStep?.effectState;
330
+ if (
331
+ effectState === "observed-complete"
332
+ || effectState === "observed-no-start"
333
+ || effectState === "observed-unknown"
334
+ || effectState === "settled"
335
+ || effectState === "settled-proof"
336
+ ) {
337
+ throw runtimeError("run_events_illegal", run.runId, "blocked_uncertain after an observation is illegal");
338
+ }
339
+ if (context.eventStore.gateObservationForAttempt(ids.attemptId)) {
340
+ throw runtimeError("run_events_illegal", run.runId, "a completed observation cannot be rewritten");
341
+ }
342
+ const now = new Date().toISOString();
343
+ const seq = sequencer(context, run, now);
344
+ const observationId = newVnextObservationId();
345
+ const blocked = seq.push("effect.blocked_uncertain", {
346
+ effect: { id: ids.effectId, policy: { class: ids.gateKind === "artifacts-exist" ? "read-only" : "unknown", sharedMutable: false } },
347
+ stepId,
348
+ stepAttempt: ids.stepAttempt,
349
+ assignmentId: ids.assignmentId,
350
+ attemptId: ids.attemptId,
351
+ reason,
352
+ receipt: placeholderReceipt(observationId, ids.gateKind, now),
353
+ });
354
+ const observationRow = buildObservation(ids, observation, observationId, blocked.eventId);
355
+ patchReceipt(blocked, observationRow);
356
+ for (const event of seq.events) context.eventStore.appendEvent(event);
357
+ context.eventStore.insertGateObservation(observationRow);
358
+ return persist(context, run, now, seq.events.at(-1)!.sequence);
359
+ }
360
+
361
+ export function recordGateCancelObserved(
362
+ context: VnextRuntimeContext,
363
+ run: VnextRunRecord,
364
+ envelope: VnextRunPlanEnvelope,
365
+ stepId: string,
366
+ observation: VnextGateObservationInput,
367
+ ) {
368
+ return context.eventStore.transaction(() => recordGateCancelObservedInTransaction(context, run, envelope, stepId, observation));
369
+ }
370
+
371
+ export function recordGateCancelObservedInTransaction(
372
+ context: VnextRuntimeContext,
373
+ run: VnextRunRecord,
374
+ envelope: VnextRunPlanEnvelope,
375
+ stepId: string,
376
+ observation: VnextGateObservationInput,
377
+ ) {
378
+ const ids = requireIntent(context, run, envelope, stepId);
379
+ assertClosedGateObservation(ids.gateKind, observation);
380
+ if (observation.completeness !== "complete") {
381
+ throw runtimeError("gate_row_invalid", run.runId, "cancel-after-observation requires a complete observation");
382
+ }
383
+ if (ids.gateKind === "command" && ids.state.currentStep?.effectState !== "dispatched") {
384
+ throw runtimeError("run_events_illegal", run.runId, "complete command observations require effect.dispatched");
385
+ }
386
+ const now = new Date().toISOString();
387
+ const seq = sequencer(context, run, now);
388
+ ensureOperatorCancel(seq, ids.state, run);
389
+ if (ids.gateKind === "artifacts-exist") {
390
+ seq.push("assignment.executing", { assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, attemptId: ids.attemptId, status: "executing" });
391
+ seq.push("attempt.status_changed", { attemptId: ids.attemptId, assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, status: "executing" });
392
+ seq.push("effect.dispatched", {
393
+ effect: { id: ids.effectId, policy: { class: "read-only", sharedMutable: false } },
394
+ stepId,
395
+ stepAttempt: ids.stepAttempt,
396
+ assignmentId: ids.assignmentId,
397
+ attemptId: ids.attemptId,
398
+ });
399
+ }
400
+ seq.push("attempt.status_changed", { attemptId: ids.attemptId, assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, status: "settling" });
401
+ const observationId = newVnextObservationId();
402
+ const policyClass = ids.gateKind === "artifacts-exist" ? "read-only" : "unknown";
403
+ const observed = seq.push("effect.observed", {
404
+ effect: { id: ids.effectId, policy: { class: policyClass, sharedMutable: false } },
405
+ stepId,
406
+ stepAttempt: ids.stepAttempt,
407
+ assignmentId: ids.assignmentId,
408
+ attemptId: ids.attemptId,
409
+ receipt: placeholderReceipt(observationId, ids.gateKind, now),
410
+ });
411
+ const observationRow = buildObservation(ids, observation, observationId, observed.eventId);
412
+ patchReceipt(observed, observationRow);
413
+ seq.push("effect.settled", {
414
+ effect: { id: ids.effectId, policy: { class: policyClass, sharedMutable: false } },
415
+ stepId,
416
+ stepAttempt: ids.stepAttempt,
417
+ assignmentId: ids.assignmentId,
418
+ attemptId: ids.attemptId,
419
+ receipt: {
420
+ provider: "kxm-gate",
421
+ kind: ids.gateKind,
422
+ id: observationId,
423
+ hash: observationRow.contentHash,
424
+ observedAt: now,
425
+ },
426
+ });
427
+ seq.push("assignment.result_recorded", { assignmentId: ids.assignmentId, resultClass: "cancelled", status: "result_recorded" });
428
+ seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
429
+ seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome: "cancelled", status: "terminal" });
430
+ seq.push("step.status_changed", { stepId, status: "cancelled", previousStatus: "running" });
431
+ seq.push("run.status_changed", { status: "cancelled", reason: "operator_cancel", stepId });
432
+ for (const event of seq.events) context.eventStore.appendEvent(event);
433
+ context.eventStore.insertGateObservation(observationRow);
434
+ context.eventStore.settleCapability(ids.attemptId, "settled");
435
+ return persist(context, run, now, seq.events.at(-1)!.sequence);
436
+ }
437
+
438
+ function appendEvaluatedTail(
439
+ seq: ReturnType<typeof sequencer>,
440
+ envelope: VnextRunPlanEnvelope,
441
+ ids: ReturnType<typeof requireIntent>,
442
+ stepId: string,
443
+ outcome: VnextGateEvidenceOutcome,
444
+ ) {
445
+ const mapped = outcome === "passed" ? "passed" : outcome === "repro-missing" ? "failed" : "failed";
446
+ seq.push("assignment.result_recorded", { assignmentId: ids.assignmentId, resultClass: "outcome", outcome, status: "result_recorded" });
447
+ seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
448
+ seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome, status: "terminal" });
449
+ seq.push("step.outcome_recorded", { stepId, stepAttempt: ids.stepAttempt, outcome });
450
+ seq.push("step.status_changed", { stepId, status: mapped, previousStatus: "running" });
451
+ const selected = envelope.plan.steps[stepId]!.transitions[outcome];
452
+ if (!selected) {
453
+ seq.push("run.status_changed", { status: "failed", reason: "outcome_unknown", stepId, outcome });
454
+ return;
455
+ }
456
+ const budget = vnextTransitionBudgetFailure(envelope.plan, ids.state, stepId, outcome);
457
+ if (budget) {
458
+ seq.push("run.status_changed", { status: "failed", reason: budget, stepId, outcome });
459
+ return;
460
+ }
461
+ if (selected.to === "step") {
462
+ seq.push("step.transitioned", { fromStepId: stepId, toStepId: selected.target, outcome });
463
+ } else {
464
+ seq.push("step.transitioned", { fromStepId: stepId, status: selected.terminalStatus, outcome });
465
+ seq.push("run.status_changed", { status: selected.terminalStatus });
466
+ }
467
+ }
468
+
469
+ function evidenceKeyFor(envelope: VnextRunPlanEnvelope, stepId: string): string | null {
470
+ const required = envelope.plan.steps[stepId]?.requiredEvidence.filter((item) => item.kind === "gate") ?? [];
471
+ return required.length === 1 ? required[0]!.key : null;
472
+ }
473
+
474
+ function placeholderReceipt(observationId: string, kind: string, now: string) {
475
+ return {
476
+ provider: "kxm-gate",
477
+ kind,
478
+ id: observationId,
479
+ hash: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
480
+ observedAt: now,
481
+ };
482
+ }
483
+
484
+ function patchReceipt(event: VnextRunEvent, row: VnextGateObservationRow): void {
485
+ const receipt = event.payload.receipt as { hash: string };
486
+ receipt.hash = row.contentHash;
487
+ }
488
+
489
+ function buildObservation(
490
+ ids: ReturnType<typeof requireIntent>,
491
+ observation: VnextGateObservationInput,
492
+ observationId: string,
493
+ recordedEventId: string,
494
+ ): VnextGateObservationRow {
495
+ const row: VnextGateObservationRow = {
496
+ observationId,
497
+ attemptId: ids.attemptId,
498
+ runId: ids.runId,
499
+ projectId: ids.projectId,
500
+ homeRuntimeId: ids.homeRuntimeId,
501
+ stepId: ids.stepId,
502
+ stepAttempt: ids.stepAttempt,
503
+ assignmentId: ids.assignmentId,
504
+ effectId: ids.effectId,
505
+ ...observation,
506
+ recordedEventId,
507
+ contentHash: "",
508
+ };
509
+ return { ...row, contentHash: gateRowContentHash("gate_observations", { ...row, contentHash: "" }) };
510
+ }
511
+
512
+ function requireGateStep(envelope: VnextRunPlanEnvelope, stepId: string, runId: string) {
513
+ const step = envelope.plan.steps[stepId];
514
+ if (!step || step.kind !== "gate") throw runtimeError("run_events_illegal", runId, `step ${stepId} is not a gate`);
515
+ return step;
516
+ }
517
+
518
+ function requireIntent(context: VnextRuntimeContext, run: VnextRunRecord, envelope: VnextRunPlanEnvelope, stepId: string) {
519
+ const step = requireGateStep(envelope, stepId, run.runId);
520
+ const folded = foldStoredVnextRun(context, run);
521
+ const attemptId = folded.currentStep?.attemptId;
522
+ if (!attemptId || folded.currentStep?.stepId !== stepId) {
523
+ throw runtimeError("run_events_illegal", run.runId, "gate intent has not been recorded");
524
+ }
525
+ const row = context.eventStore.gateAttempt(attemptId);
526
+ if (!row) throw runtimeError("gate_row_invalid", run.runId, "missing gate attempt");
527
+ return {
528
+ ...row,
529
+ gateKind: row.gateKind,
530
+ expect: step.expect,
531
+ state: folded,
532
+ };
533
+ }
534
+
535
+ function ensureOperatorCancel(
536
+ seq: ReturnType<typeof sequencer>,
537
+ state: ReturnType<typeof foldStoredVnextRun>,
538
+ run: VnextRunRecord,
539
+ ): void {
540
+ if (state.status === "cancelling") return;
541
+ if (state.status !== "running") {
542
+ throw runtimeError("run_events_illegal", run.runId, "cancelled gate proof requires a running or cancelling run");
543
+ }
544
+ seq.push("run.cancel_requested", {
545
+ actor: { kind: "runtime", id: run.homeRuntimeId },
546
+ reason: "operator_cancel",
547
+ });
548
+ seq.push("run.status_changed", { status: "cancelling", reason: "operator_cancel" });
549
+ }
550
+
551
+ function sequencer(context: VnextRuntimeContext, run: VnextRunRecord, now: string) {
552
+ let sequence = context.eventStore.nextSequence(run.runId);
553
+ let mono = vnextMonotonicNs();
554
+ const events: VnextRunEvent[] = [];
555
+ return {
556
+ events,
557
+ push(eventType: string, payload: Record<string, unknown>): VnextRunEvent {
558
+ const event: VnextRunEvent = {
559
+ ...vnextEventBase(context, run, now, mono),
560
+ eventId: newVnextEventId(),
561
+ eventType,
562
+ sequence: sequence++,
563
+ payload,
564
+ };
565
+ mono = vnextIncrementMonotonicNs(mono);
566
+ events.push(event);
567
+ return event;
568
+ },
569
+ };
570
+ }
571
+
572
+ function persist(context: VnextRuntimeContext, run: VnextRunRecord, now: string, lastSequence: number) {
573
+ const next = foldStoredVnextRun(context, run);
574
+ persistVnextRunState(context, run.runId, next, lastSequence);
575
+ context.eventStore.updateRunStatus(run.runId, next.status, now);
576
+ return { state: next, run: { ...run, status: next.status, updatedAt: now } };
577
+ }
578
+
579
+ function hashFromStore(context: VnextRuntimeContext, runId: string): string {
580
+ return context.eventStore.runPlan(runId)!.runPlanHash;
581
+ }
582
+
583
+ void vnextCanonicalJson;