@riddledc/riddle-proof 0.5.36 → 0.5.38

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
@@ -82,21 +82,25 @@ updates Discord, OpenClaw, GitHub, or another integration.
82
82
 
83
83
  The packaged proof-run setup uses isolated git worktrees for before and after
84
84
  states. By default those worktrees now live under
85
- `/tmp/.riddle-proof-worktrees`, with dependency caches under the matching local
86
- temp root. This keeps repeated `node_modules` cache materialization on local
87
- scratch storage instead of walking large dependency trees on EFS or other shared
88
- workspace filesystems.
85
+ `/var/tmp/riddle-proof/.riddle-proof-worktrees`, with dependency caches under
86
+ the matching disk-backed scratch root. This keeps repeated `node_modules` cache
87
+ materialization off tmpfs `/tmp` and away from EFS or other shared workspace
88
+ filesystems.
89
89
 
90
90
  Set `RIDDLE_PROOF_WORKTREE_ROOT` to choose an explicit location. Set
91
91
  `RIDDLE_PROOF_USE_WORKSPACE_WORKTREE_ROOT=1` to keep the previous behavior of
92
- placing proof worktrees next to the active repository.
92
+ placing proof worktrees next to the active repository. Set
93
+ `RIDDLE_PROOF_SCRATCH_ROOT` to choose the scratch parent, or
94
+ `RIDDLE_PROOF_USE_TMP_SCRATCH=1` to force tmp-backed scratch for a short-lived
95
+ test.
93
96
 
94
97
  When local scratch storage is low, setup prunes stale
95
98
  `riddle-proof-*` worktrees from the scratch root before creating the next run.
96
99
  This preserves the dependency cache for speed while avoiding old failed runs
97
- filling `/tmp`. Set `RIDDLE_PROOF_KEEP_SCRATCH_WORKTREES=1` to disable that
98
- cleanup for debugging, or tune the low-space threshold with
99
- `RIDDLE_PROOF_MIN_SCRATCH_FREE_MB`.
100
+ filling the scratch disk. Setup also records scratch disk snapshots in the run
101
+ state so disk blockers are visible during proof inspection. Set
102
+ `RIDDLE_PROOF_KEEP_SCRATCH_WORKTREES=1` to disable that cleanup for debugging,
103
+ or tune the low-space threshold with `RIDDLE_PROOF_MIN_SCRATCH_FREE_MB`.
100
104
 
101
105
  ## Capture Diagnostics
102
106
 
@@ -158,6 +162,22 @@ transport state, current playhead time, instrument lane readiness, measured
158
162
  frame drift, console errors, and screenshots of the intended view. The reviewer
159
163
  still decides whether it sounds good.
160
164
 
165
+ ### Playable / Game Proof
166
+
167
+ For games, canvas scenes, and interactive toys, use `verification_mode:
168
+ "playable"` or `"gameplay"` when a static screenshot is not enough. The capture
169
+ must prove the experience responds over time:
170
+
171
+ - accepted keyboard, pointer, or touch input
172
+ - game state or HUD values changed after input
173
+ - elapsed play/animation time progressed
174
+ - non-HUD playfield/canvas pixels changed by a measured threshold
175
+
176
+ Expose that as `window.__riddleProofEvidence.playability` or
177
+ `playability_evidence` with `version: "riddle-proof.playability.v1"`. A nice
178
+ still frame, including a generated image plate, is supporting evidence only and
179
+ does not satisfy playable proof by itself.
180
+
161
181
  ### Server Preview Usage
162
182
 
163
183
  Server preview proof is most useful when the request, capture, and PR comment
@@ -0,0 +1,361 @@
1
+ // src/playability.ts
2
+ var RIDDLE_PROOF_PLAYABILITY_VERSION = "riddle-proof.playability.v1";
3
+ var RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION = "riddle-proof.playability.assessment.v1";
4
+ var PLAYABILITY_MODES = /* @__PURE__ */ new Set(["playable", "gameplay", "game"]);
5
+ var PLAYABILITY_CONTAINER_KEYS = [
6
+ "playability",
7
+ "playability_evidence",
8
+ "playabilityEvidence",
9
+ "playable",
10
+ "gameplay",
11
+ "gameplay_evidence",
12
+ "gameplayEvidence"
13
+ ];
14
+ var INPUT_ASSERTION_KEYS = [
15
+ "inputAccepted",
16
+ "inputObserved",
17
+ "inputReceived",
18
+ "controlsWorked",
19
+ "keyboardWorked",
20
+ "pointerWorked",
21
+ "touchWorked",
22
+ "steeringInputAccepted",
23
+ "userInputObserved"
24
+ ];
25
+ var STATE_ASSERTION_KEYS = [
26
+ "stateChanged",
27
+ "gameStateChanged",
28
+ "playStarted",
29
+ "simulationAdvanced",
30
+ "distanceAdvanced",
31
+ "scoreChanged",
32
+ "hudChanged",
33
+ "positionChanged",
34
+ "speedChanged"
35
+ ];
36
+ var MOTION_ASSERTION_KEYS = [
37
+ "motionObserved",
38
+ "visualMotion",
39
+ "canvasChanged",
40
+ "pixelChanged",
41
+ "playfieldMoved",
42
+ "playfieldPixelsChanged",
43
+ "nonHudPixelsChanged",
44
+ "animationAdvanced",
45
+ "frameChanged",
46
+ "framesChanged"
47
+ ];
48
+ var TIME_ASSERTION_KEYS = [
49
+ "timeProgressed",
50
+ "clockAdvanced",
51
+ "animationAdvanced",
52
+ "simulationAdvanced",
53
+ "playStarted"
54
+ ];
55
+ var PERCENT_KEYS = [
56
+ "changed_percent",
57
+ "change_percent",
58
+ "percent_changed",
59
+ "diff_percent",
60
+ "motion_percent",
61
+ "pixel_change_percent"
62
+ ];
63
+ var RATIO_KEYS = [
64
+ "changed_ratio",
65
+ "change_ratio",
66
+ "diff_ratio",
67
+ "motion_ratio",
68
+ "pixel_change_ratio"
69
+ ];
70
+ var PIXEL_KEYS = [
71
+ "changed_pixels",
72
+ "changed_pixel_count",
73
+ "diff_pixels",
74
+ "motion_pixels",
75
+ "pixel_delta",
76
+ "changedPixels"
77
+ ];
78
+ var AVERAGE_DELTA_KEYS = [
79
+ "average_delta",
80
+ "avg_delta",
81
+ "mean_delta",
82
+ "avg_abs_delta",
83
+ "mean_abs_delta"
84
+ ];
85
+ var TIME_DELTA_KEYS = [
86
+ "time_delta_ms",
87
+ "elapsed_ms",
88
+ "duration_ms",
89
+ "sample_duration_ms",
90
+ "animation_delta_ms"
91
+ ];
92
+ function isRiddleProofPlayabilityMode(mode) {
93
+ return PLAYABILITY_MODES.has(stringValue(mode).toLowerCase());
94
+ }
95
+ function extractPlayabilityEvidence(...sources) {
96
+ const seen = /* @__PURE__ */ new Set();
97
+ for (const source of sources) {
98
+ const found = findPlayabilityEvidence(source, seen);
99
+ if (found) return found;
100
+ }
101
+ return null;
102
+ }
103
+ function assessPlayabilityEvidence(evidence, options = {}) {
104
+ const thresholds = {
105
+ minChangedPercent: options.minChangedPercent ?? 0.5,
106
+ minChangedPixels: options.minChangedPixels ?? 1e3,
107
+ minAverageDelta: options.minAverageDelta ?? 1,
108
+ minTimeDeltaMs: options.minTimeDeltaMs ?? 250
109
+ };
110
+ const required = {
111
+ requireInput: options.requireInput ?? true,
112
+ requireStateChange: options.requireStateChange ?? true,
113
+ requireMotion: options.requireMotion ?? true,
114
+ requireTimeProgression: options.requireTimeProgression ?? true
115
+ };
116
+ const record = extractPlayabilityEvidence(evidence);
117
+ const metrics = {};
118
+ const concerns = [];
119
+ if (!record) {
120
+ return {
121
+ version: RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION,
122
+ evidence_present: false,
123
+ passed: false,
124
+ input_observed: false,
125
+ state_changed: false,
126
+ motion_observed: false,
127
+ time_progressed: false,
128
+ concerns: ["playability evidence is missing"],
129
+ metrics,
130
+ thresholds,
131
+ required,
132
+ evidence_keys: []
133
+ };
134
+ }
135
+ const assertions = recordValue(record.assertions) || record;
136
+ const inputObserved = inputObservedFrom(record, assertions);
137
+ const stateChanged = stateChangedFrom(record, assertions, metrics);
138
+ const motionObserved = motionObservedFrom(record, assertions, thresholds, metrics);
139
+ const timeProgressed = timeProgressedFrom(record, assertions, thresholds, metrics);
140
+ const explicitFailure = hasExplicitPlayabilityFailure(record, assertions);
141
+ if (required.requireInput && !inputObserved) {
142
+ concerns.push("no accepted player input was observed");
143
+ }
144
+ if (required.requireStateChange && !stateChanged) {
145
+ concerns.push("game state did not measurably change");
146
+ }
147
+ if (required.requireMotion && !motionObserved) {
148
+ concerns.push("playfield/canvas pixels did not measurably change");
149
+ }
150
+ if (required.requireTimeProgression && !timeProgressed) {
151
+ concerns.push("play time or animation time did not measurably progress");
152
+ }
153
+ if (explicitFailure) {
154
+ concerns.push("playability evidence includes an explicit failed assertion");
155
+ }
156
+ const passed = Boolean(
157
+ (!required.requireInput || inputObserved) && (!required.requireStateChange || stateChanged) && (!required.requireMotion || motionObserved) && (!required.requireTimeProgression || timeProgressed) && !explicitFailure
158
+ );
159
+ return {
160
+ version: RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION,
161
+ evidence_present: true,
162
+ passed,
163
+ input_observed: inputObserved,
164
+ state_changed: stateChanged,
165
+ motion_observed: motionObserved,
166
+ time_progressed: timeProgressed,
167
+ concerns,
168
+ metrics,
169
+ thresholds,
170
+ required,
171
+ evidence_keys: Object.keys(record)
172
+ };
173
+ }
174
+ function findPlayabilityEvidence(value, seen, depth = 0) {
175
+ if (depth > 6 || value === null || value === void 0) return null;
176
+ if (typeof value === "string") {
177
+ const parsed = parseJson(value);
178
+ return parsed === null ? null : findPlayabilityEvidence(parsed, seen, depth + 1);
179
+ }
180
+ if (Array.isArray(value)) {
181
+ if (seen.has(value)) return null;
182
+ seen.add(value);
183
+ for (const item of value) {
184
+ const found = findPlayabilityEvidence(item, seen, depth + 1);
185
+ if (found) return found;
186
+ }
187
+ return null;
188
+ }
189
+ const record = recordValue(value);
190
+ if (!record || seen.has(record)) return null;
191
+ seen.add(record);
192
+ if (record.version === RIDDLE_PROOF_PLAYABILITY_VERSION || hasPlayabilityShape(record)) {
193
+ return record;
194
+ }
195
+ for (const key of PLAYABILITY_CONTAINER_KEYS) {
196
+ if (Object.prototype.hasOwnProperty.call(record, key)) {
197
+ const nested = findPlayabilityEvidence(record[key], seen, depth + 1);
198
+ if (nested) return nested;
199
+ if (typeof record[key] === "boolean") {
200
+ return { assertions: { [key]: record[key] } };
201
+ }
202
+ }
203
+ }
204
+ for (const item of Object.values(record)) {
205
+ const found = findPlayabilityEvidence(item, seen, depth + 1);
206
+ if (found) return found;
207
+ }
208
+ return null;
209
+ }
210
+ function hasPlayabilityShape(record) {
211
+ return Boolean(
212
+ record.input_events || record.state_delta || record.pixel_delta || record.canvas_delta || record.motion_delta || record.playfield_delta || record.time_delta_ms !== void 0 || hasAnyKey(recordValue(record.assertions) || record, [
213
+ ...INPUT_ASSERTION_KEYS,
214
+ ...STATE_ASSERTION_KEYS,
215
+ ...MOTION_ASSERTION_KEYS,
216
+ ...TIME_ASSERTION_KEYS
217
+ ])
218
+ );
219
+ }
220
+ function inputObservedFrom(record, assertions) {
221
+ if (trueForAnyKey(assertions, INPUT_ASSERTION_KEYS)) return true;
222
+ if (listValue(record.input_events).length > 0) return true;
223
+ if (listValue(record.inputs).length > 0) return true;
224
+ if (listValue(record.interactions).length > 0) return true;
225
+ const eventCount = numericFromAnyKey(record, ["input_event_count", "input_count", "interaction_count"]);
226
+ return eventCount !== null && eventCount > 0;
227
+ }
228
+ function stateChangedFrom(record, assertions, metrics) {
229
+ if (trueForAnyKey(assertions, STATE_ASSERTION_KEYS)) return true;
230
+ const stateDelta = recordValue(record.state_delta) || recordValue(record.stateDelta);
231
+ if (stateDelta) {
232
+ const changedKeys = listValue(stateDelta.changed_keys || stateDelta.changedKeys);
233
+ metrics.state_changed_keys = changedKeys;
234
+ if (stateDelta.changed === true || changedKeys.length > 0) return true;
235
+ }
236
+ const delta = numericFromAnyKey(record, [
237
+ "distance_delta",
238
+ "score_delta",
239
+ "position_delta",
240
+ "speed_delta",
241
+ "hud_delta"
242
+ ]);
243
+ if (delta !== null) {
244
+ metrics.state_numeric_delta = delta;
245
+ return Math.abs(delta) > 0;
246
+ }
247
+ return false;
248
+ }
249
+ function motionObservedFrom(record, assertions, thresholds, metrics) {
250
+ if (trueForAnyKey(assertions, MOTION_ASSERTION_KEYS)) return true;
251
+ for (const source of [
252
+ recordValue(record.playfield_delta),
253
+ recordValue(record.playfieldDelta),
254
+ recordValue(record.non_hud_delta),
255
+ recordValue(record.nonHudDelta),
256
+ recordValue(record.pixel_delta),
257
+ recordValue(record.pixelDelta),
258
+ recordValue(record.canvas_delta),
259
+ recordValue(record.canvasDelta),
260
+ recordValue(record.motion_delta),
261
+ recordValue(record.motionDelta),
262
+ recordValue(record.visual_delta),
263
+ recordValue(record.visualDelta),
264
+ record
265
+ ]) {
266
+ if (!source) continue;
267
+ const percent = percentFrom(source);
268
+ const pixels = numericFromAnyKey(source, PIXEL_KEYS);
269
+ const averageDelta = numericFromAnyKey(source, AVERAGE_DELTA_KEYS);
270
+ if (percent !== null) metrics.changed_percent = percent;
271
+ if (pixels !== null) metrics.changed_pixels = pixels;
272
+ if (averageDelta !== null) metrics.average_delta = averageDelta;
273
+ if (percent !== null && percent >= thresholds.minChangedPercent) return true;
274
+ if (pixels !== null && pixels >= thresholds.minChangedPixels) return true;
275
+ if (averageDelta !== null && averageDelta >= thresholds.minAverageDelta) return true;
276
+ }
277
+ return false;
278
+ }
279
+ function timeProgressedFrom(record, assertions, thresholds, metrics) {
280
+ if (trueForAnyKey(assertions, TIME_ASSERTION_KEYS)) return true;
281
+ const timeDelta = numericFromAnyKey(record, TIME_DELTA_KEYS);
282
+ if (timeDelta !== null) {
283
+ metrics.time_delta_ms = timeDelta;
284
+ return timeDelta >= thresholds.minTimeDeltaMs;
285
+ }
286
+ const stateDelta = recordValue(record.state_delta) || recordValue(record.stateDelta);
287
+ const nestedDelta = numericFromAnyKey(stateDelta || {}, TIME_DELTA_KEYS);
288
+ if (nestedDelta !== null) {
289
+ metrics.time_delta_ms = nestedDelta;
290
+ return nestedDelta >= thresholds.minTimeDeltaMs;
291
+ }
292
+ return false;
293
+ }
294
+ function hasExplicitPlayabilityFailure(record, assertions) {
295
+ if (record.passed === false || record.playable === false || assertions.playabilityPassed === false) {
296
+ return true;
297
+ }
298
+ for (const key of [
299
+ ...INPUT_ASSERTION_KEYS,
300
+ ...STATE_ASSERTION_KEYS,
301
+ ...MOTION_ASSERTION_KEYS,
302
+ ...TIME_ASSERTION_KEYS
303
+ ]) {
304
+ if (assertions[key] === false) return true;
305
+ }
306
+ return false;
307
+ }
308
+ function trueForAnyKey(record, keys) {
309
+ return keys.some((key) => record[key] === true);
310
+ }
311
+ function hasAnyKey(record, keys) {
312
+ return keys.some((key) => Object.prototype.hasOwnProperty.call(record, key));
313
+ }
314
+ function percentFrom(record) {
315
+ const percent = numericFromAnyKey(record, PERCENT_KEYS);
316
+ if (percent !== null) return percent;
317
+ const ratio = numericFromAnyKey(record, RATIO_KEYS);
318
+ if (ratio !== null) return ratio <= 1 ? ratio * 100 : ratio;
319
+ return null;
320
+ }
321
+ function numericFromAnyKey(record, keys) {
322
+ for (const key of keys) {
323
+ const value = numericValue(record[key]);
324
+ if (value !== null) return value;
325
+ }
326
+ return null;
327
+ }
328
+ function recordValue(value) {
329
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
330
+ }
331
+ function listValue(value) {
332
+ return Array.isArray(value) ? value : [];
333
+ }
334
+ function numericValue(value) {
335
+ if (typeof value === "number" && Number.isFinite(value)) return value;
336
+ if (typeof value === "string" && value.trim()) {
337
+ const parsed = Number(value);
338
+ return Number.isFinite(parsed) ? parsed : null;
339
+ }
340
+ return null;
341
+ }
342
+ function stringValue(value) {
343
+ return typeof value === "string" && value.trim() ? value.trim() : "";
344
+ }
345
+ function parseJson(value) {
346
+ const trimmed = value.trim();
347
+ if (!trimmed || !/^[{[]/.test(trimmed)) return null;
348
+ try {
349
+ return JSON.parse(trimmed);
350
+ } catch {
351
+ return null;
352
+ }
353
+ }
354
+
355
+ export {
356
+ RIDDLE_PROOF_PLAYABILITY_VERSION,
357
+ RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION,
358
+ isRiddleProofPlayabilityMode,
359
+ extractPlayabilityEvidence,
360
+ assessPlayabilityEvidence
361
+ };
@@ -2,10 +2,10 @@ import {
2
2
  createDisabledRiddleProofAgentAdapter,
3
3
  readRiddleProofRunStatus,
4
4
  runRiddleProofEngineHarness
5
- } from "./chunk-7R6ZQE3X.js";
5
+ } from "./chunk-GN7HSZ6G.js";
6
6
  import "./chunk-OASB3CYU.js";
7
- import "./chunk-4YCWZVBN.js";
8
7
  import "./chunk-J2MERROF.js";
8
+ import "./chunk-4YCWZVBN.js";
9
9
  export {
10
10
  createDisabledRiddleProofAgentAdapter,
11
11
  readRiddleProofRunStatus,