@riddledc/riddle-proof 0.1.1 → 0.3.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.
package/README.md CHANGED
@@ -7,9 +7,11 @@ Riddle Proof is agent-agnostic. Bring a coding agent through an adapter; Riddle
7
7
  Proof standardizes evidence, proof assessment, ship gates, terminal results,
8
8
  and integration metadata.
9
9
 
10
- This package is intentionally small at first. The current OpenClaw
11
- `proofed_change_run` implementation remains the reference workflow while
12
- reusable contracts and low-risk helpers are extracted here.
10
+ This package includes the reusable runner harness that drives a request through
11
+ preflight, setup, implementation, proof capture, judgment, shipping, and notification
12
+ adapters. The current OpenClaw `proofed_change_run` implementation remains the
13
+ reference workflow while adapter implementations are extracted behind parity
14
+ tests.
13
15
 
14
16
  ## Initial Scope
15
17
 
@@ -17,6 +19,9 @@ reusable contracts and low-risk helpers are extracted here.
17
19
  - Evidence bundle and proof assessment types
18
20
  - Adapter interfaces
19
21
  - State/event helpers for wrappers that need a stable run envelope
22
+ - Runner harness for preflight -> setup -> implement -> prove -> judge -> ship -> notify
23
+ - Stage heartbeat and run status snapshot helpers
24
+ - Worktree metadata and proof artifact role contracts
20
25
  - Terminal ship metadata normalization
21
26
  - Stable result helpers
22
27
  - OpenClaw parameter normalization via `@riddledc/riddle-proof/openclaw`
@@ -40,6 +45,7 @@ npm install @riddledc/riddle-proof
40
45
 
41
46
  ```ts
42
47
  import { createRunResult, createRunState } from "@riddledc/riddle-proof";
48
+ import { runRiddleProof } from "@riddledc/riddle-proof/runner";
43
49
  import { toRiddleProofRunParams } from "@riddledc/riddle-proof/openclaw";
44
50
  ```
45
51
 
@@ -48,6 +54,26 @@ adapters are exposed through subpaths such as
48
54
  `@riddledc/riddle-proof/openclaw`, so wrappers can reuse the mapping logic
49
55
  without depending on another plugin runtime.
50
56
 
57
+ ## Runner Harness
58
+
59
+ `runRiddleProof` is the reusable idea-to-PR workflow driver. It does not ship
60
+ credentials or a coding agent. It calls adapters supplied by the host
61
+ integration:
62
+
63
+ ```text
64
+ preflight -> setup -> implement -> prove -> judge -> ship -> notify
65
+ ```
66
+
67
+ The preflight adapter checks model/tool availability before proof work starts.
68
+ The setup adapter should report the isolated worktree path, branch, and cleanup
69
+ policy it chose. During the run, wrappers can emit `appendStageHeartbeat`
70
+ events and return `createRunStatusSnapshot` for cheap observer status.
71
+
72
+ The proof adapter is where a host wires Riddle server-backed capture. The ship
73
+ adapter is where a host commits, pushes, opens or updates a PR, and waits for CI
74
+ when configured. The notification adapter is where a host updates Discord,
75
+ OpenClaw, GitHub, or another integration.
76
+
51
77
  ## OpenClaw Adapter Boundary
52
78
 
53
79
  `@riddledc/riddle-proof/openclaw` translates the current
@@ -52,7 +52,11 @@ function createRunResult(input) {
52
52
  return compactRecord({
53
53
  ok,
54
54
  status,
55
+ run_id: state.run_id,
55
56
  state_path: input.state_path ?? state.state_path ?? null,
57
+ worktree_path: state.worktree_path ?? null,
58
+ branch: state.branch ?? null,
59
+ current_stage: state.current_stage ?? null,
56
60
  iterations: state.iterations,
57
61
  last_checkpoint: state.last_checkpoint ?? null,
58
62
  last_summary: input.last_summary ?? null,
@@ -2,13 +2,24 @@ import {
2
2
  compactRecord,
3
3
  nonEmptyString,
4
4
  recordValue
5
- } from "./chunk-2ZQNXVQC.js";
5
+ } from "./chunk-5DC6YXN4.js";
6
6
 
7
7
  // src/state.ts
8
8
  var RIDDLE_PROOF_RUN_STATE_VERSION = "riddle-proof.run-state.v1";
9
9
  function timestamp() {
10
10
  return (/* @__PURE__ */ new Date()).toISOString();
11
11
  }
12
+ function createRunId(createdAt) {
13
+ const stamp = createdAt.replace(/\D/g, "").slice(0, 14) || "unknown";
14
+ const entropy = Math.random().toString(36).slice(2, 8) || "run";
15
+ return `rp_${stamp}_${entropy}`;
16
+ }
17
+ function elapsedMs(start, end) {
18
+ const startMs = start ? Date.parse(start) : NaN;
19
+ const endMs = end ? Date.parse(end) : NaN;
20
+ if (!Number.isFinite(startMs) || !Number.isFinite(endMs)) return void 0;
21
+ return Math.max(0, endMs - startMs);
22
+ }
12
23
  function normalizeIntegrationContext(input, fallbackSource) {
13
24
  const value = recordValue(input);
14
25
  if (!value) {
@@ -59,7 +70,12 @@ function createRunState(input) {
59
70
  const createdAt = input.created_at || timestamp();
60
71
  return compactRecord({
61
72
  version: RIDDLE_PROOF_RUN_STATE_VERSION,
73
+ run_id: input.run_id || createRunId(createdAt),
62
74
  state_path: input.state_path,
75
+ worktree_path: input.worktree_path,
76
+ branch: input.branch || input.request.branch,
77
+ current_stage: input.current_stage ?? null,
78
+ stage_started_at: input.stage_started_at ?? null,
63
79
  status: input.status || "running",
64
80
  created_at: createdAt,
65
81
  updated_at: input.updated_at || createdAt,
@@ -87,9 +103,49 @@ function appendRunEvent(state, input) {
87
103
  details: event.details
88
104
  }));
89
105
  if (input.checkpoint !== void 0) state.last_checkpoint = input.checkpoint;
106
+ if (input.stage !== void 0) {
107
+ if (state.current_stage !== input.stage) state.stage_started_at = event.ts;
108
+ state.current_stage = input.stage;
109
+ }
90
110
  state.updated_at = event.ts;
91
111
  return state;
92
112
  }
113
+ function appendStageHeartbeat(state, input) {
114
+ const at = input.ts || timestamp();
115
+ return appendRunEvent(state, {
116
+ ts: at,
117
+ kind: "stage.heartbeat",
118
+ checkpoint: input.checkpoint || `${input.stage}_heartbeat`,
119
+ stage: input.stage,
120
+ summary: input.summary || `${input.stage} stage is active.`,
121
+ details: compactRecord({
122
+ elapsed_ms: elapsedMs(state.created_at, at),
123
+ stage_elapsed_ms: elapsedMs(state.stage_started_at, at),
124
+ wait_reason: input.wait_reason,
125
+ blocker: input.blocker,
126
+ ...input.details
127
+ })
128
+ });
129
+ }
130
+ function createRunStatusSnapshot(state, at = timestamp()) {
131
+ const latestEvent = state.events[state.events.length - 1];
132
+ const runId = state.run_id || "unknown";
133
+ return compactRecord({
134
+ run_id: runId,
135
+ status: state.status,
136
+ current_stage: state.current_stage ?? null,
137
+ state_path: state.state_path ?? null,
138
+ worktree_path: state.worktree_path ?? null,
139
+ branch: state.branch ?? null,
140
+ iterations: state.iterations,
141
+ last_checkpoint: state.last_checkpoint ?? null,
142
+ updated_at: state.updated_at,
143
+ elapsed_ms: elapsedMs(state.created_at, at),
144
+ stage_elapsed_ms: elapsedMs(state.stage_started_at, at),
145
+ blocker: state.blocker,
146
+ latest_event: latestEvent
147
+ });
148
+ }
93
149
  function setRunStatus(state, status, at = timestamp()) {
94
150
  state.status = status;
95
151
  state.ok = status !== "blocked" && status !== "failed";
@@ -103,5 +159,7 @@ export {
103
159
  normalizeRunParams,
104
160
  createRunState,
105
161
  appendRunEvent,
162
+ appendStageHeartbeat,
163
+ createRunStatusSnapshot,
106
164
  setRunStatus
107
165
  };
@@ -0,0 +1,495 @@
1
+ import {
2
+ appendRunEvent,
3
+ appendStageHeartbeat,
4
+ createRunState,
5
+ setRunStatus
6
+ } from "./chunk-IQIEOQZF.js";
7
+ import {
8
+ createRunResult
9
+ } from "./chunk-5DC6YXN4.js";
10
+
11
+ // src/runner.ts
12
+ function errorDetails(error) {
13
+ if (error instanceof Error) {
14
+ return {
15
+ name: error.name,
16
+ message: error.message
17
+ };
18
+ }
19
+ return { message: String(error) };
20
+ }
21
+ function adapterBlocker(code, message, checkpoint, details) {
22
+ return {
23
+ code,
24
+ message,
25
+ checkpoint,
26
+ details
27
+ };
28
+ }
29
+ function blockRun(input) {
30
+ input.state.blocker = input.blocker;
31
+ appendRunEvent(input.state, {
32
+ kind: "run.blocked",
33
+ checkpoint: input.blocker.checkpoint,
34
+ stage: input.stage,
35
+ summary: input.blocker.message,
36
+ details: {
37
+ code: input.blocker.code,
38
+ ...input.blocker.details
39
+ }
40
+ });
41
+ setRunStatus(input.state, "blocked");
42
+ return createRunResult({
43
+ state: input.state,
44
+ status: "blocked",
45
+ last_summary: input.blocker.message,
46
+ evidence_bundle: input.evidence_bundle,
47
+ raw: input.raw
48
+ });
49
+ }
50
+ function shouldIterate(assessment) {
51
+ const nextStage = assessment.continue_with_stage || assessment.recommended_stage;
52
+ return nextStage === "implement" || nextStage === "author";
53
+ }
54
+ async function notifyIfConfigured(input) {
55
+ if (!input.notification) return input.result;
56
+ try {
57
+ const notification = await input.notification.notify({
58
+ state: input.state,
59
+ result: input.result
60
+ });
61
+ input.state.notification = notification;
62
+ appendRunEvent(input.state, {
63
+ kind: "notification.completed",
64
+ checkpoint: "notification_completed",
65
+ stage: "notify",
66
+ summary: "Integration notification completed."
67
+ });
68
+ } catch (error) {
69
+ appendRunEvent(input.state, {
70
+ kind: "notification.failed",
71
+ checkpoint: "notification_failed",
72
+ stage: "notify",
73
+ summary: "Integration notification failed.",
74
+ details: errorDetails(error)
75
+ });
76
+ }
77
+ return createRunResult({
78
+ state: input.state,
79
+ status: input.state.status,
80
+ last_summary: input.result.last_summary,
81
+ evidence_bundle: input.result.evidence_bundle,
82
+ raw: input.result.raw
83
+ });
84
+ }
85
+ async function runRiddleProof(input) {
86
+ const state = input.state || createRunState({
87
+ request: input.request,
88
+ state_path: input.state_path,
89
+ worktree_path: input.workdir
90
+ });
91
+ const adapters = input.adapters || {};
92
+ const maxIterations = Math.max(1, Math.trunc(input.max_iterations ?? 1));
93
+ appendRunEvent(state, {
94
+ kind: "run.started",
95
+ checkpoint: "run_started",
96
+ stage: "setup",
97
+ summary: "Riddle Proof run started.",
98
+ details: {
99
+ run_id: state.run_id,
100
+ state_path: state.state_path,
101
+ worktree_path: state.worktree_path,
102
+ branch: state.branch,
103
+ max_iterations: maxIterations,
104
+ ship_mode: state.request.ship_mode || "none"
105
+ }
106
+ });
107
+ appendStageHeartbeat(state, {
108
+ stage: "setup",
109
+ summary: "Setup stage is active.",
110
+ details: {
111
+ run_id: state.run_id,
112
+ state_path: state.state_path,
113
+ worktree_path: state.worktree_path,
114
+ branch: state.branch
115
+ }
116
+ });
117
+ let workdir = input.workdir;
118
+ let evidenceContext;
119
+ if (adapters.preflight) {
120
+ appendRunEvent(state, {
121
+ kind: "preflight.started",
122
+ checkpoint: "preflight_started",
123
+ stage: "preflight",
124
+ summary: "Riddle Proof preflight adapter started."
125
+ });
126
+ try {
127
+ const preflight = await adapters.preflight.preflight({ request: state.request, state });
128
+ if (!preflight.ok) {
129
+ return blockRun({
130
+ state,
131
+ stage: "preflight",
132
+ blocker: adapterBlocker(
133
+ "preflight_failed",
134
+ "The preflight adapter found blocking tool or model configuration issues.",
135
+ "preflight_failed",
136
+ {
137
+ blockers: preflight.blockers,
138
+ warnings: preflight.warnings,
139
+ degraded_capabilities: preflight.degraded_capabilities
140
+ }
141
+ ),
142
+ raw: { preflight }
143
+ });
144
+ }
145
+ appendRunEvent(state, {
146
+ kind: "preflight.completed",
147
+ checkpoint: "preflight_completed",
148
+ stage: "preflight",
149
+ summary: "Riddle Proof preflight adapter completed.",
150
+ details: {
151
+ warnings: preflight.warnings,
152
+ degraded_capabilities: preflight.degraded_capabilities
153
+ }
154
+ });
155
+ } catch (error) {
156
+ return blockRun({
157
+ state,
158
+ stage: "preflight",
159
+ blocker: adapterBlocker("preflight_exception", "The preflight adapter threw an exception.", "preflight_failed", errorDetails(error))
160
+ });
161
+ }
162
+ }
163
+ if (adapters.setup) {
164
+ appendRunEvent(state, {
165
+ kind: "setup.started",
166
+ checkpoint: "setup_started",
167
+ stage: "setup",
168
+ summary: "Riddle Proof setup adapter started."
169
+ });
170
+ try {
171
+ const setup = await adapters.setup.setup({ request: state.request, state });
172
+ if (!setup.ok) {
173
+ return blockRun({
174
+ state,
175
+ stage: "setup",
176
+ blocker: adapterBlocker(
177
+ "setup_failed",
178
+ "The setup adapter did not complete successfully.",
179
+ "setup_failed",
180
+ { blockers: setup.blockers }
181
+ ),
182
+ raw: { setup }
183
+ });
184
+ }
185
+ workdir = setup.worktree_path || setup.workdir || workdir;
186
+ state.worktree_path = setup.worktree_path || setup.workdir || state.worktree_path;
187
+ state.branch = setup.branch || state.branch;
188
+ evidenceContext = setup.evidence_context;
189
+ appendRunEvent(state, {
190
+ kind: "setup.completed",
191
+ checkpoint: "setup_completed",
192
+ stage: "setup",
193
+ summary: "Riddle Proof setup adapter completed.",
194
+ details: {
195
+ has_workdir: Boolean(workdir),
196
+ worktree_path: state.worktree_path,
197
+ branch: state.branch,
198
+ cleanup_policy: setup.cleanup_policy,
199
+ has_evidence_context: Boolean(evidenceContext)
200
+ }
201
+ });
202
+ } catch (error) {
203
+ return blockRun({
204
+ state,
205
+ stage: "setup",
206
+ blocker: adapterBlocker("setup_exception", "The setup adapter threw an exception.", "setup_failed", errorDetails(error))
207
+ });
208
+ }
209
+ }
210
+ const changeRequest = state.request.change_request?.trim();
211
+ if (!changeRequest) {
212
+ return blockRun({
213
+ state,
214
+ stage: "setup",
215
+ blocker: adapterBlocker("change_request_required", "A change request is required before implementation.", "request_invalid")
216
+ });
217
+ }
218
+ if (!workdir) {
219
+ return blockRun({
220
+ state,
221
+ stage: "setup",
222
+ blocker: adapterBlocker("workdir_not_configured", "A workdir or setup adapter result is required before implementation.", "setup_required")
223
+ });
224
+ }
225
+ if (!adapters.implementation) {
226
+ return blockRun({
227
+ state,
228
+ stage: "implement",
229
+ blocker: adapterBlocker("implementation_adapter_not_configured", "An implementation adapter is required to change code.", "implementation_required")
230
+ });
231
+ }
232
+ if (!adapters.proof) {
233
+ return blockRun({
234
+ state,
235
+ stage: "prove",
236
+ blocker: adapterBlocker("proof_adapter_not_configured", "A proof adapter is required to capture evidence.", "proof_required")
237
+ });
238
+ }
239
+ if (!adapters.judge) {
240
+ return blockRun({
241
+ state,
242
+ stage: "verify",
243
+ blocker: adapterBlocker("judge_adapter_not_configured", "A judge adapter is required to assess proof.", "judge_required")
244
+ });
245
+ }
246
+ let implementation;
247
+ let evidenceBundle;
248
+ let assessment;
249
+ for (let attempt = 0; attempt < maxIterations; attempt += 1) {
250
+ state.iterations += 1;
251
+ appendStageHeartbeat(state, {
252
+ stage: "implement",
253
+ summary: "Implementation stage is active.",
254
+ details: { iteration: state.iterations }
255
+ });
256
+ appendRunEvent(state, {
257
+ kind: "implementation.started",
258
+ checkpoint: "implementation_started",
259
+ stage: "implement",
260
+ summary: "Implementation adapter started.",
261
+ details: { iteration: state.iterations }
262
+ });
263
+ try {
264
+ implementation = await adapters.implementation.implement({
265
+ workdir,
266
+ change_request: changeRequest,
267
+ evidence_context: evidenceContext,
268
+ state
269
+ });
270
+ } catch (error) {
271
+ return blockRun({
272
+ state,
273
+ stage: "implement",
274
+ blocker: adapterBlocker("implementation_exception", "The implementation adapter threw an exception.", "implementation_failed", errorDetails(error)),
275
+ evidence_bundle: evidenceBundle
276
+ });
277
+ }
278
+ if (!implementation.ok) {
279
+ return blockRun({
280
+ state,
281
+ stage: "implement",
282
+ blocker: adapterBlocker(
283
+ "implementation_failed",
284
+ "The implementation adapter did not complete successfully.",
285
+ "implementation_failed",
286
+ { blockers: implementation.blockers }
287
+ ),
288
+ evidence_bundle: evidenceBundle,
289
+ raw: { implementation }
290
+ });
291
+ }
292
+ appendRunEvent(state, {
293
+ kind: "implementation.completed",
294
+ checkpoint: "implementation_completed",
295
+ stage: "implement",
296
+ summary: "Implementation adapter completed.",
297
+ details: {
298
+ changed_files: implementation.changed_files,
299
+ tests_run: implementation.tests_run
300
+ }
301
+ });
302
+ appendStageHeartbeat(state, {
303
+ stage: "prove",
304
+ summary: "Proof capture stage is active.",
305
+ details: {
306
+ verification_mode: state.request.verification_mode
307
+ }
308
+ });
309
+ appendRunEvent(state, {
310
+ kind: "proof.started",
311
+ checkpoint: "proof_started",
312
+ stage: "prove",
313
+ summary: "Proof adapter started."
314
+ });
315
+ try {
316
+ const proof = await adapters.proof.prove({
317
+ state,
318
+ implementation,
319
+ evidence_context: evidenceContext
320
+ });
321
+ if (!proof.ok || !proof.evidence_bundle) {
322
+ return blockRun({
323
+ state,
324
+ stage: "prove",
325
+ blocker: adapterBlocker(
326
+ "proof_failed",
327
+ "The proof adapter did not produce a usable evidence bundle.",
328
+ "proof_failed",
329
+ { blockers: proof.blockers }
330
+ ),
331
+ raw: { proof }
332
+ });
333
+ }
334
+ evidenceBundle = proof.evidence_bundle;
335
+ appendRunEvent(state, {
336
+ kind: "proof.completed",
337
+ checkpoint: "proof_completed",
338
+ stage: "prove",
339
+ summary: "Proof adapter completed.",
340
+ details: {
341
+ verification_mode: evidenceBundle.verification_mode,
342
+ artifact_count: evidenceBundle.artifacts?.length ?? 0
343
+ }
344
+ });
345
+ } catch (error) {
346
+ return blockRun({
347
+ state,
348
+ stage: "prove",
349
+ blocker: adapterBlocker("proof_exception", "The proof adapter threw an exception.", "proof_failed", errorDetails(error))
350
+ });
351
+ }
352
+ appendRunEvent(state, {
353
+ kind: "judge.started",
354
+ checkpoint: "judge_started",
355
+ stage: "verify",
356
+ summary: "Judge adapter started."
357
+ });
358
+ appendStageHeartbeat(state, {
359
+ stage: "verify",
360
+ summary: "Verification stage is active.",
361
+ details: {
362
+ verification_mode: evidenceBundle.verification_mode
363
+ }
364
+ });
365
+ try {
366
+ assessment = await adapters.judge.assessProof({ state, evidence_bundle: evidenceBundle });
367
+ state.proof_decision = assessment.decision;
368
+ appendRunEvent(state, {
369
+ kind: "judge.completed",
370
+ checkpoint: "judge_completed",
371
+ stage: "verify",
372
+ summary: assessment.summary,
373
+ details: {
374
+ decision: assessment.decision,
375
+ recommended_stage: assessment.recommended_stage,
376
+ continue_with_stage: assessment.continue_with_stage,
377
+ reasons: assessment.reasons
378
+ }
379
+ });
380
+ } catch (error) {
381
+ return blockRun({
382
+ state,
383
+ stage: "verify",
384
+ blocker: adapterBlocker("judge_exception", "The judge adapter threw an exception.", "judge_failed", errorDetails(error)),
385
+ evidence_bundle: evidenceBundle
386
+ });
387
+ }
388
+ if (assessment.decision === "ready_to_ship") break;
389
+ if (attempt + 1 < maxIterations && shouldIterate(assessment)) {
390
+ evidenceContext = evidenceBundle;
391
+ appendRunEvent(state, {
392
+ kind: "run.iterating",
393
+ checkpoint: "iteration_requested",
394
+ stage: "implement",
395
+ summary: "Judge requested another implementation iteration.",
396
+ details: {
397
+ decision: assessment.decision,
398
+ next_iteration: state.iterations + 1
399
+ }
400
+ });
401
+ continue;
402
+ }
403
+ return blockRun({
404
+ state,
405
+ stage: "verify",
406
+ blocker: adapterBlocker(
407
+ "proof_not_ready",
408
+ assessment.summary || "Proof is not ready to ship.",
409
+ "judge_completed",
410
+ {
411
+ decision: assessment.decision,
412
+ recommended_stage: assessment.recommended_stage,
413
+ continue_with_stage: assessment.continue_with_stage,
414
+ reasons: assessment.reasons
415
+ }
416
+ ),
417
+ evidence_bundle: evidenceBundle,
418
+ raw: { assessment }
419
+ });
420
+ }
421
+ if (!assessment || !evidenceBundle) {
422
+ return blockRun({
423
+ state,
424
+ stage: "verify",
425
+ blocker: adapterBlocker("runner_incomplete", "The runner ended without proof assessment.", "runner_incomplete")
426
+ });
427
+ }
428
+ if (state.request.ship_mode !== "ship") {
429
+ setRunStatus(state, "ready_to_ship");
430
+ const result2 = createRunResult({
431
+ state,
432
+ status: "ready_to_ship",
433
+ last_summary: assessment.summary,
434
+ evidence_bundle: evidenceBundle,
435
+ raw: { implementation, assessment }
436
+ });
437
+ return notifyIfConfigured({ state, result: result2, notification: adapters.notification });
438
+ }
439
+ if (!adapters.ship) {
440
+ return blockRun({
441
+ state,
442
+ stage: "ship",
443
+ blocker: adapterBlocker("ship_adapter_not_configured", "A ship adapter is required when ship_mode is ship.", "ship_required"),
444
+ evidence_bundle: evidenceBundle,
445
+ raw: { implementation, assessment }
446
+ });
447
+ }
448
+ appendRunEvent(state, {
449
+ kind: "ship.started",
450
+ checkpoint: "ship_started",
451
+ stage: "ship",
452
+ summary: "Ship adapter started."
453
+ });
454
+ appendStageHeartbeat(state, {
455
+ stage: "ship",
456
+ summary: "Ship stage is active."
457
+ });
458
+ let metadata;
459
+ try {
460
+ metadata = await adapters.ship.ship({ state, assessment });
461
+ } catch (error) {
462
+ return blockRun({
463
+ state,
464
+ stage: "ship",
465
+ blocker: adapterBlocker("ship_exception", "The ship adapter threw an exception.", "ship_failed", errorDetails(error)),
466
+ evidence_bundle: evidenceBundle,
467
+ raw: { implementation, assessment }
468
+ });
469
+ }
470
+ appendRunEvent(state, {
471
+ kind: "ship.completed",
472
+ checkpoint: "ship_completed",
473
+ stage: "ship",
474
+ summary: "Ship adapter completed.",
475
+ details: {
476
+ pr_url: metadata.pr_url,
477
+ marked_ready: metadata.marked_ready,
478
+ finalized: metadata.finalized
479
+ }
480
+ });
481
+ setRunStatus(state, "shipped");
482
+ const result = createRunResult({
483
+ state,
484
+ status: "shipped",
485
+ last_summary: "Riddle Proof shipped.",
486
+ metadata,
487
+ evidence_bundle: evidenceBundle,
488
+ raw: { implementation, assessment }
489
+ });
490
+ return notifyIfConfigured({ state, result, notification: adapters.notification });
491
+ }
492
+
493
+ export {
494
+ runRiddleProof
495
+ };