@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.
@@ -0,0 +1,389 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/playability.ts
21
+ var playability_exports = {};
22
+ __export(playability_exports, {
23
+ RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION: () => RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION,
24
+ RIDDLE_PROOF_PLAYABILITY_VERSION: () => RIDDLE_PROOF_PLAYABILITY_VERSION,
25
+ assessPlayabilityEvidence: () => assessPlayabilityEvidence,
26
+ extractPlayabilityEvidence: () => extractPlayabilityEvidence,
27
+ isRiddleProofPlayabilityMode: () => isRiddleProofPlayabilityMode
28
+ });
29
+ module.exports = __toCommonJS(playability_exports);
30
+ var RIDDLE_PROOF_PLAYABILITY_VERSION = "riddle-proof.playability.v1";
31
+ var RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION = "riddle-proof.playability.assessment.v1";
32
+ var PLAYABILITY_MODES = /* @__PURE__ */ new Set(["playable", "gameplay", "game"]);
33
+ var PLAYABILITY_CONTAINER_KEYS = [
34
+ "playability",
35
+ "playability_evidence",
36
+ "playabilityEvidence",
37
+ "playable",
38
+ "gameplay",
39
+ "gameplay_evidence",
40
+ "gameplayEvidence"
41
+ ];
42
+ var INPUT_ASSERTION_KEYS = [
43
+ "inputAccepted",
44
+ "inputObserved",
45
+ "inputReceived",
46
+ "controlsWorked",
47
+ "keyboardWorked",
48
+ "pointerWorked",
49
+ "touchWorked",
50
+ "steeringInputAccepted",
51
+ "userInputObserved"
52
+ ];
53
+ var STATE_ASSERTION_KEYS = [
54
+ "stateChanged",
55
+ "gameStateChanged",
56
+ "playStarted",
57
+ "simulationAdvanced",
58
+ "distanceAdvanced",
59
+ "scoreChanged",
60
+ "hudChanged",
61
+ "positionChanged",
62
+ "speedChanged"
63
+ ];
64
+ var MOTION_ASSERTION_KEYS = [
65
+ "motionObserved",
66
+ "visualMotion",
67
+ "canvasChanged",
68
+ "pixelChanged",
69
+ "playfieldMoved",
70
+ "playfieldPixelsChanged",
71
+ "nonHudPixelsChanged",
72
+ "animationAdvanced",
73
+ "frameChanged",
74
+ "framesChanged"
75
+ ];
76
+ var TIME_ASSERTION_KEYS = [
77
+ "timeProgressed",
78
+ "clockAdvanced",
79
+ "animationAdvanced",
80
+ "simulationAdvanced",
81
+ "playStarted"
82
+ ];
83
+ var PERCENT_KEYS = [
84
+ "changed_percent",
85
+ "change_percent",
86
+ "percent_changed",
87
+ "diff_percent",
88
+ "motion_percent",
89
+ "pixel_change_percent"
90
+ ];
91
+ var RATIO_KEYS = [
92
+ "changed_ratio",
93
+ "change_ratio",
94
+ "diff_ratio",
95
+ "motion_ratio",
96
+ "pixel_change_ratio"
97
+ ];
98
+ var PIXEL_KEYS = [
99
+ "changed_pixels",
100
+ "changed_pixel_count",
101
+ "diff_pixels",
102
+ "motion_pixels",
103
+ "pixel_delta",
104
+ "changedPixels"
105
+ ];
106
+ var AVERAGE_DELTA_KEYS = [
107
+ "average_delta",
108
+ "avg_delta",
109
+ "mean_delta",
110
+ "avg_abs_delta",
111
+ "mean_abs_delta"
112
+ ];
113
+ var TIME_DELTA_KEYS = [
114
+ "time_delta_ms",
115
+ "elapsed_ms",
116
+ "duration_ms",
117
+ "sample_duration_ms",
118
+ "animation_delta_ms"
119
+ ];
120
+ function isRiddleProofPlayabilityMode(mode) {
121
+ return PLAYABILITY_MODES.has(stringValue(mode).toLowerCase());
122
+ }
123
+ function extractPlayabilityEvidence(...sources) {
124
+ const seen = /* @__PURE__ */ new Set();
125
+ for (const source of sources) {
126
+ const found = findPlayabilityEvidence(source, seen);
127
+ if (found) return found;
128
+ }
129
+ return null;
130
+ }
131
+ function assessPlayabilityEvidence(evidence, options = {}) {
132
+ const thresholds = {
133
+ minChangedPercent: options.minChangedPercent ?? 0.5,
134
+ minChangedPixels: options.minChangedPixels ?? 1e3,
135
+ minAverageDelta: options.minAverageDelta ?? 1,
136
+ minTimeDeltaMs: options.minTimeDeltaMs ?? 250
137
+ };
138
+ const required = {
139
+ requireInput: options.requireInput ?? true,
140
+ requireStateChange: options.requireStateChange ?? true,
141
+ requireMotion: options.requireMotion ?? true,
142
+ requireTimeProgression: options.requireTimeProgression ?? true
143
+ };
144
+ const record = extractPlayabilityEvidence(evidence);
145
+ const metrics = {};
146
+ const concerns = [];
147
+ if (!record) {
148
+ return {
149
+ version: RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION,
150
+ evidence_present: false,
151
+ passed: false,
152
+ input_observed: false,
153
+ state_changed: false,
154
+ motion_observed: false,
155
+ time_progressed: false,
156
+ concerns: ["playability evidence is missing"],
157
+ metrics,
158
+ thresholds,
159
+ required,
160
+ evidence_keys: []
161
+ };
162
+ }
163
+ const assertions = recordValue(record.assertions) || record;
164
+ const inputObserved = inputObservedFrom(record, assertions);
165
+ const stateChanged = stateChangedFrom(record, assertions, metrics);
166
+ const motionObserved = motionObservedFrom(record, assertions, thresholds, metrics);
167
+ const timeProgressed = timeProgressedFrom(record, assertions, thresholds, metrics);
168
+ const explicitFailure = hasExplicitPlayabilityFailure(record, assertions);
169
+ if (required.requireInput && !inputObserved) {
170
+ concerns.push("no accepted player input was observed");
171
+ }
172
+ if (required.requireStateChange && !stateChanged) {
173
+ concerns.push("game state did not measurably change");
174
+ }
175
+ if (required.requireMotion && !motionObserved) {
176
+ concerns.push("playfield/canvas pixels did not measurably change");
177
+ }
178
+ if (required.requireTimeProgression && !timeProgressed) {
179
+ concerns.push("play time or animation time did not measurably progress");
180
+ }
181
+ if (explicitFailure) {
182
+ concerns.push("playability evidence includes an explicit failed assertion");
183
+ }
184
+ const passed = Boolean(
185
+ (!required.requireInput || inputObserved) && (!required.requireStateChange || stateChanged) && (!required.requireMotion || motionObserved) && (!required.requireTimeProgression || timeProgressed) && !explicitFailure
186
+ );
187
+ return {
188
+ version: RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION,
189
+ evidence_present: true,
190
+ passed,
191
+ input_observed: inputObserved,
192
+ state_changed: stateChanged,
193
+ motion_observed: motionObserved,
194
+ time_progressed: timeProgressed,
195
+ concerns,
196
+ metrics,
197
+ thresholds,
198
+ required,
199
+ evidence_keys: Object.keys(record)
200
+ };
201
+ }
202
+ function findPlayabilityEvidence(value, seen, depth = 0) {
203
+ if (depth > 6 || value === null || value === void 0) return null;
204
+ if (typeof value === "string") {
205
+ const parsed = parseJson(value);
206
+ return parsed === null ? null : findPlayabilityEvidence(parsed, seen, depth + 1);
207
+ }
208
+ if (Array.isArray(value)) {
209
+ if (seen.has(value)) return null;
210
+ seen.add(value);
211
+ for (const item of value) {
212
+ const found = findPlayabilityEvidence(item, seen, depth + 1);
213
+ if (found) return found;
214
+ }
215
+ return null;
216
+ }
217
+ const record = recordValue(value);
218
+ if (!record || seen.has(record)) return null;
219
+ seen.add(record);
220
+ if (record.version === RIDDLE_PROOF_PLAYABILITY_VERSION || hasPlayabilityShape(record)) {
221
+ return record;
222
+ }
223
+ for (const key of PLAYABILITY_CONTAINER_KEYS) {
224
+ if (Object.prototype.hasOwnProperty.call(record, key)) {
225
+ const nested = findPlayabilityEvidence(record[key], seen, depth + 1);
226
+ if (nested) return nested;
227
+ if (typeof record[key] === "boolean") {
228
+ return { assertions: { [key]: record[key] } };
229
+ }
230
+ }
231
+ }
232
+ for (const item of Object.values(record)) {
233
+ const found = findPlayabilityEvidence(item, seen, depth + 1);
234
+ if (found) return found;
235
+ }
236
+ return null;
237
+ }
238
+ function hasPlayabilityShape(record) {
239
+ return Boolean(
240
+ 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, [
241
+ ...INPUT_ASSERTION_KEYS,
242
+ ...STATE_ASSERTION_KEYS,
243
+ ...MOTION_ASSERTION_KEYS,
244
+ ...TIME_ASSERTION_KEYS
245
+ ])
246
+ );
247
+ }
248
+ function inputObservedFrom(record, assertions) {
249
+ if (trueForAnyKey(assertions, INPUT_ASSERTION_KEYS)) return true;
250
+ if (listValue(record.input_events).length > 0) return true;
251
+ if (listValue(record.inputs).length > 0) return true;
252
+ if (listValue(record.interactions).length > 0) return true;
253
+ const eventCount = numericFromAnyKey(record, ["input_event_count", "input_count", "interaction_count"]);
254
+ return eventCount !== null && eventCount > 0;
255
+ }
256
+ function stateChangedFrom(record, assertions, metrics) {
257
+ if (trueForAnyKey(assertions, STATE_ASSERTION_KEYS)) return true;
258
+ const stateDelta = recordValue(record.state_delta) || recordValue(record.stateDelta);
259
+ if (stateDelta) {
260
+ const changedKeys = listValue(stateDelta.changed_keys || stateDelta.changedKeys);
261
+ metrics.state_changed_keys = changedKeys;
262
+ if (stateDelta.changed === true || changedKeys.length > 0) return true;
263
+ }
264
+ const delta = numericFromAnyKey(record, [
265
+ "distance_delta",
266
+ "score_delta",
267
+ "position_delta",
268
+ "speed_delta",
269
+ "hud_delta"
270
+ ]);
271
+ if (delta !== null) {
272
+ metrics.state_numeric_delta = delta;
273
+ return Math.abs(delta) > 0;
274
+ }
275
+ return false;
276
+ }
277
+ function motionObservedFrom(record, assertions, thresholds, metrics) {
278
+ if (trueForAnyKey(assertions, MOTION_ASSERTION_KEYS)) return true;
279
+ for (const source of [
280
+ recordValue(record.playfield_delta),
281
+ recordValue(record.playfieldDelta),
282
+ recordValue(record.non_hud_delta),
283
+ recordValue(record.nonHudDelta),
284
+ recordValue(record.pixel_delta),
285
+ recordValue(record.pixelDelta),
286
+ recordValue(record.canvas_delta),
287
+ recordValue(record.canvasDelta),
288
+ recordValue(record.motion_delta),
289
+ recordValue(record.motionDelta),
290
+ recordValue(record.visual_delta),
291
+ recordValue(record.visualDelta),
292
+ record
293
+ ]) {
294
+ if (!source) continue;
295
+ const percent = percentFrom(source);
296
+ const pixels = numericFromAnyKey(source, PIXEL_KEYS);
297
+ const averageDelta = numericFromAnyKey(source, AVERAGE_DELTA_KEYS);
298
+ if (percent !== null) metrics.changed_percent = percent;
299
+ if (pixels !== null) metrics.changed_pixels = pixels;
300
+ if (averageDelta !== null) metrics.average_delta = averageDelta;
301
+ if (percent !== null && percent >= thresholds.minChangedPercent) return true;
302
+ if (pixels !== null && pixels >= thresholds.minChangedPixels) return true;
303
+ if (averageDelta !== null && averageDelta >= thresholds.minAverageDelta) return true;
304
+ }
305
+ return false;
306
+ }
307
+ function timeProgressedFrom(record, assertions, thresholds, metrics) {
308
+ if (trueForAnyKey(assertions, TIME_ASSERTION_KEYS)) return true;
309
+ const timeDelta = numericFromAnyKey(record, TIME_DELTA_KEYS);
310
+ if (timeDelta !== null) {
311
+ metrics.time_delta_ms = timeDelta;
312
+ return timeDelta >= thresholds.minTimeDeltaMs;
313
+ }
314
+ const stateDelta = recordValue(record.state_delta) || recordValue(record.stateDelta);
315
+ const nestedDelta = numericFromAnyKey(stateDelta || {}, TIME_DELTA_KEYS);
316
+ if (nestedDelta !== null) {
317
+ metrics.time_delta_ms = nestedDelta;
318
+ return nestedDelta >= thresholds.minTimeDeltaMs;
319
+ }
320
+ return false;
321
+ }
322
+ function hasExplicitPlayabilityFailure(record, assertions) {
323
+ if (record.passed === false || record.playable === false || assertions.playabilityPassed === false) {
324
+ return true;
325
+ }
326
+ for (const key of [
327
+ ...INPUT_ASSERTION_KEYS,
328
+ ...STATE_ASSERTION_KEYS,
329
+ ...MOTION_ASSERTION_KEYS,
330
+ ...TIME_ASSERTION_KEYS
331
+ ]) {
332
+ if (assertions[key] === false) return true;
333
+ }
334
+ return false;
335
+ }
336
+ function trueForAnyKey(record, keys) {
337
+ return keys.some((key) => record[key] === true);
338
+ }
339
+ function hasAnyKey(record, keys) {
340
+ return keys.some((key) => Object.prototype.hasOwnProperty.call(record, key));
341
+ }
342
+ function percentFrom(record) {
343
+ const percent = numericFromAnyKey(record, PERCENT_KEYS);
344
+ if (percent !== null) return percent;
345
+ const ratio = numericFromAnyKey(record, RATIO_KEYS);
346
+ if (ratio !== null) return ratio <= 1 ? ratio * 100 : ratio;
347
+ return null;
348
+ }
349
+ function numericFromAnyKey(record, keys) {
350
+ for (const key of keys) {
351
+ const value = numericValue(record[key]);
352
+ if (value !== null) return value;
353
+ }
354
+ return null;
355
+ }
356
+ function recordValue(value) {
357
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
358
+ }
359
+ function listValue(value) {
360
+ return Array.isArray(value) ? value : [];
361
+ }
362
+ function numericValue(value) {
363
+ if (typeof value === "number" && Number.isFinite(value)) return value;
364
+ if (typeof value === "string" && value.trim()) {
365
+ const parsed = Number(value);
366
+ return Number.isFinite(parsed) ? parsed : null;
367
+ }
368
+ return null;
369
+ }
370
+ function stringValue(value) {
371
+ return typeof value === "string" && value.trim() ? value.trim() : "";
372
+ }
373
+ function parseJson(value) {
374
+ const trimmed = value.trim();
375
+ if (!trimmed || !/^[{[]/.test(trimmed)) return null;
376
+ try {
377
+ return JSON.parse(trimmed);
378
+ } catch {
379
+ return null;
380
+ }
381
+ }
382
+ // Annotate the CommonJS export names for ESM import in node:
383
+ 0 && (module.exports = {
384
+ RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION,
385
+ RIDDLE_PROOF_PLAYABILITY_VERSION,
386
+ assessPlayabilityEvidence,
387
+ extractPlayabilityEvidence,
388
+ isRiddleProofPlayabilityMode
389
+ });
@@ -0,0 +1,44 @@
1
+ declare const RIDDLE_PROOF_PLAYABILITY_VERSION = "riddle-proof.playability.v1";
2
+ declare const RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION = "riddle-proof.playability.assessment.v1";
3
+ interface RiddleProofPlayabilityEvidence {
4
+ version?: typeof RIDDLE_PROOF_PLAYABILITY_VERSION | string;
5
+ target?: string;
6
+ input_events?: unknown[];
7
+ state_delta?: Record<string, unknown>;
8
+ pixel_delta?: Record<string, unknown>;
9
+ canvas_delta?: Record<string, unknown>;
10
+ motion_delta?: Record<string, unknown>;
11
+ playfield_delta?: Record<string, unknown>;
12
+ time_delta_ms?: number;
13
+ assertions?: Record<string, unknown>;
14
+ [key: string]: unknown;
15
+ }
16
+ interface AssessPlayabilityOptions {
17
+ minChangedPercent?: number;
18
+ minChangedPixels?: number;
19
+ minAverageDelta?: number;
20
+ minTimeDeltaMs?: number;
21
+ requireInput?: boolean;
22
+ requireStateChange?: boolean;
23
+ requireMotion?: boolean;
24
+ requireTimeProgression?: boolean;
25
+ }
26
+ interface RiddleProofPlayabilityAssessment {
27
+ version: typeof RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION;
28
+ evidence_present: boolean;
29
+ passed: boolean;
30
+ input_observed: boolean;
31
+ state_changed: boolean;
32
+ motion_observed: boolean;
33
+ time_progressed: boolean;
34
+ concerns: string[];
35
+ metrics: Record<string, unknown>;
36
+ thresholds: Required<Pick<AssessPlayabilityOptions, "minChangedPercent" | "minChangedPixels" | "minAverageDelta" | "minTimeDeltaMs">>;
37
+ required: Required<Pick<AssessPlayabilityOptions, "requireInput" | "requireStateChange" | "requireMotion" | "requireTimeProgression">>;
38
+ evidence_keys: string[];
39
+ }
40
+ declare function isRiddleProofPlayabilityMode(mode: unknown): boolean;
41
+ declare function extractPlayabilityEvidence(...sources: unknown[]): RiddleProofPlayabilityEvidence | null;
42
+ declare function assessPlayabilityEvidence(evidence: unknown, options?: AssessPlayabilityOptions): RiddleProofPlayabilityAssessment;
43
+
44
+ export { type AssessPlayabilityOptions, RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION, RIDDLE_PROOF_PLAYABILITY_VERSION, type RiddleProofPlayabilityAssessment, type RiddleProofPlayabilityEvidence, assessPlayabilityEvidence, extractPlayabilityEvidence, isRiddleProofPlayabilityMode };
@@ -0,0 +1,44 @@
1
+ declare const RIDDLE_PROOF_PLAYABILITY_VERSION = "riddle-proof.playability.v1";
2
+ declare const RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION = "riddle-proof.playability.assessment.v1";
3
+ interface RiddleProofPlayabilityEvidence {
4
+ version?: typeof RIDDLE_PROOF_PLAYABILITY_VERSION | string;
5
+ target?: string;
6
+ input_events?: unknown[];
7
+ state_delta?: Record<string, unknown>;
8
+ pixel_delta?: Record<string, unknown>;
9
+ canvas_delta?: Record<string, unknown>;
10
+ motion_delta?: Record<string, unknown>;
11
+ playfield_delta?: Record<string, unknown>;
12
+ time_delta_ms?: number;
13
+ assertions?: Record<string, unknown>;
14
+ [key: string]: unknown;
15
+ }
16
+ interface AssessPlayabilityOptions {
17
+ minChangedPercent?: number;
18
+ minChangedPixels?: number;
19
+ minAverageDelta?: number;
20
+ minTimeDeltaMs?: number;
21
+ requireInput?: boolean;
22
+ requireStateChange?: boolean;
23
+ requireMotion?: boolean;
24
+ requireTimeProgression?: boolean;
25
+ }
26
+ interface RiddleProofPlayabilityAssessment {
27
+ version: typeof RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION;
28
+ evidence_present: boolean;
29
+ passed: boolean;
30
+ input_observed: boolean;
31
+ state_changed: boolean;
32
+ motion_observed: boolean;
33
+ time_progressed: boolean;
34
+ concerns: string[];
35
+ metrics: Record<string, unknown>;
36
+ thresholds: Required<Pick<AssessPlayabilityOptions, "minChangedPercent" | "minChangedPixels" | "minAverageDelta" | "minTimeDeltaMs">>;
37
+ required: Required<Pick<AssessPlayabilityOptions, "requireInput" | "requireStateChange" | "requireMotion" | "requireTimeProgression">>;
38
+ evidence_keys: string[];
39
+ }
40
+ declare function isRiddleProofPlayabilityMode(mode: unknown): boolean;
41
+ declare function extractPlayabilityEvidence(...sources: unknown[]): RiddleProofPlayabilityEvidence | null;
42
+ declare function assessPlayabilityEvidence(evidence: unknown, options?: AssessPlayabilityOptions): RiddleProofPlayabilityAssessment;
43
+
44
+ export { type AssessPlayabilityOptions, RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION, RIDDLE_PROOF_PLAYABILITY_VERSION, type RiddleProofPlayabilityAssessment, type RiddleProofPlayabilityEvidence, assessPlayabilityEvidence, extractPlayabilityEvidence, isRiddleProofPlayabilityMode };
@@ -0,0 +1,14 @@
1
+ import {
2
+ RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION,
3
+ RIDDLE_PROOF_PLAYABILITY_VERSION,
4
+ assessPlayabilityEvidence,
5
+ extractPlayabilityEvidence,
6
+ isRiddleProofPlayabilityMode
7
+ } from "./chunk-NSWT3VSV.js";
8
+ export {
9
+ RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION,
10
+ RIDDLE_PROOF_PLAYABILITY_VERSION,
11
+ assessPlayabilityEvidence,
12
+ extractPlayabilityEvidence,
13
+ isRiddleProofPlayabilityMode
14
+ };
package/dist/types.d.cts CHANGED
@@ -5,7 +5,7 @@ type JsonObject = {
5
5
  };
6
6
  type RiddleProofStatus = "running" | "blocked" | "failed" | "ready_to_ship" | "shipped" | "completed";
7
7
  type RiddleProofPrLifecycleStatus = "unknown" | "open" | "merged" | "closed" | "not_found" | "unavailable" | (string & {});
8
- type RiddleProofVerificationMode = "proof" | "visual" | "interaction" | "render" | "data" | "json" | "audio" | "log" | "logs" | "metric" | "metrics" | (string & {});
8
+ type RiddleProofVerificationMode = "proof" | "visual" | "interaction" | "playable" | "gameplay" | "render" | "data" | "json" | "audio" | "log" | "logs" | "metric" | "metrics" | (string & {});
9
9
  type RiddleProofDecision = "ready_to_ship" | "needs_richer_proof" | "revise_capture" | "needs_recon" | "needs_implementation" | (string & {});
10
10
  type RiddleProofStage = "preflight" | "setup" | "recon" | "author" | "implement" | "prove" | "verify" | "ship" | "notify" | (string & {});
11
11
  type RiddleProofArtifactRole = "baseline" | "after_proof" | "incidental" | "diagnostic" | (string & {});
@@ -188,6 +188,7 @@ interface RiddleProofEvidenceBundle {
188
188
  artifacts?: EvidenceArtifact[];
189
189
  proof_session?: RiddleProofVisualSession;
190
190
  proof_evidence?: unknown;
191
+ playability_evidence?: unknown;
191
192
  proof_evidence_sample?: unknown;
192
193
  assertions?: JsonValue;
193
194
  semantic_context?: Record<string, unknown>;
package/dist/types.d.ts CHANGED
@@ -5,7 +5,7 @@ type JsonObject = {
5
5
  };
6
6
  type RiddleProofStatus = "running" | "blocked" | "failed" | "ready_to_ship" | "shipped" | "completed";
7
7
  type RiddleProofPrLifecycleStatus = "unknown" | "open" | "merged" | "closed" | "not_found" | "unavailable" | (string & {});
8
- type RiddleProofVerificationMode = "proof" | "visual" | "interaction" | "render" | "data" | "json" | "audio" | "log" | "logs" | "metric" | "metrics" | (string & {});
8
+ type RiddleProofVerificationMode = "proof" | "visual" | "interaction" | "playable" | "gameplay" | "render" | "data" | "json" | "audio" | "log" | "logs" | "metric" | "metrics" | (string & {});
9
9
  type RiddleProofDecision = "ready_to_ship" | "needs_richer_proof" | "revise_capture" | "needs_recon" | "needs_implementation" | (string & {});
10
10
  type RiddleProofStage = "preflight" | "setup" | "recon" | "author" | "implement" | "prove" | "verify" | "ship" | "notify" | (string & {});
11
11
  type RiddleProofArtifactRole = "baseline" | "after_proof" | "incidental" | "diagnostic" | (string & {});
@@ -188,6 +188,7 @@ interface RiddleProofEvidenceBundle {
188
188
  artifacts?: EvidenceArtifact[];
189
189
  proof_session?: RiddleProofVisualSession;
190
190
  proof_evidence?: unknown;
191
+ playability_evidence?: unknown;
191
192
  proof_evidence_sample?: unknown;
192
193
  assertions?: JsonValue;
193
194
  semantic_context?: Record<string, unknown>;
@@ -15,6 +15,8 @@ import {
15
15
  import path from "node:path";
16
16
  import { fileURLToPath } from "node:url";
17
17
 
18
+ const DEFAULT_RIDDLE_PROOF_SCRATCH_ROOT = "/var/tmp/riddle-proof";
19
+
18
20
  function commandEnv() {
19
21
  return { ...process.env, HOME: "/root" };
20
22
  }
@@ -58,6 +60,26 @@ export function uniqueToken(now = new Date()) {
58
60
  return `${stamp}-${randomUUID().slice(0, 8)}`;
59
61
  }
60
62
 
63
+ function envFlag(name, defaultValue = false) {
64
+ const raw = String(process.env[name] || "").trim().toLowerCase();
65
+ if (["1", "true", "yes", "on"].includes(raw)) return true;
66
+ if (["0", "false", "no", "off"].includes(raw)) return false;
67
+ return defaultValue;
68
+ }
69
+
70
+ export function riddleProofScratchRoot() {
71
+ const configured = (process.env.RIDDLE_PROOF_SCRATCH_ROOT || "").trim();
72
+ if (configured) return path.resolve(configured);
73
+ if (envFlag("RIDDLE_PROOF_USE_TMP_SCRATCH", false)) {
74
+ return path.join("/tmp", "riddle-proof");
75
+ }
76
+ return DEFAULT_RIDDLE_PROOF_SCRATCH_ROOT;
77
+ }
78
+
79
+ export function defaultRiddleProofWorktreeRoot() {
80
+ return path.join(riddleProofScratchRoot(), ".riddle-proof-worktrees");
81
+ }
82
+
61
83
  export function workspaceRoots({ workspaceRoot = "", currentRepoDir = "" } = {}) {
62
84
  const roots = [];
63
85
  for (const candidate of [
@@ -332,7 +354,7 @@ function dependencyCacheRoot(projectDir) {
332
354
  if (worktreeIndex >= 0) {
333
355
  return path.join(resolved.slice(0, worktreeIndex), ".riddle-proof-deps-cache");
334
356
  }
335
- return path.join(path.dirname(resolved), ".riddle-proof-deps-cache");
357
+ return path.join(riddleProofScratchRoot(), ".riddle-proof-deps-cache");
336
358
  }
337
359
 
338
360
  function dependencyCacheKey(fingerprint, installCmd) {
@@ -497,6 +519,15 @@ async function main() {
497
519
  case "dependency-fingerprint":
498
520
  ok({ fingerprint: computeDependencyFingerprint(payload.projectDir) });
499
521
  return;
522
+ case "scratch-root":
523
+ ok({
524
+ scratchRoot: riddleProofScratchRoot(),
525
+ worktreeRoot: defaultRiddleProofWorktreeRoot(),
526
+ });
527
+ return;
528
+ case "dependency-cache-root":
529
+ ok({ cacheRoot: dependencyCacheRoot(payload.projectDir || process.cwd()) });
530
+ return;
500
531
  default:
501
532
  throw new Error(`Unsupported command: ${command}`);
502
533
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.5.36",
3
+ "version": "0.5.38",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",
@@ -54,6 +54,11 @@
54
54
  "import": "./dist/proof-session.js",
55
55
  "require": "./dist/proof-session.cjs"
56
56
  },
57
+ "./playability": {
58
+ "types": "./dist/playability.d.ts",
59
+ "import": "./dist/playability.js",
60
+ "require": "./dist/playability.cjs"
61
+ },
57
62
  "./openclaw": {
58
63
  "types": "./dist/openclaw.d.ts",
59
64
  "import": "./dist/openclaw.js",
@@ -87,7 +92,7 @@
87
92
  "typescript": "^5.4.5"
88
93
  },
89
94
  "scripts": {
90
- "build": "tsup src/index.ts src/types.ts src/result.ts src/state.ts src/runner.ts src/engine-harness.ts src/diagnostics.ts src/proof-session.ts src/openclaw.ts src/proof-run-core.ts src/proof-run-engine.ts --format cjs,esm --dts --out-dir dist --clean",
95
+ "build": "tsup src/index.ts src/types.ts src/result.ts src/state.ts src/runner.ts src/engine-harness.ts src/diagnostics.ts src/proof-session.ts src/playability.ts src/openclaw.ts src/proof-run-core.ts src/proof-run-engine.ts --format cjs,esm --dts --out-dir dist --clean",
91
96
  "clean": "rm -rf dist",
92
97
  "lint": "echo 'lint: (not configured)'",
93
98
  "test": "npm run build && node test.js && node proof-run.test.js"
@@ -105,7 +105,7 @@ def authored_proof_plan(state, reference, target_path, baselines, wait_for_selec
105
105
  if wait_for_selector:
106
106
  lines.append('Stabilize capture on selector: ' + wait_for_selector)
107
107
  lines.append('After evidence should load the recon-confirmed route and collect the evidence type required by verification_mode without rediscovering baseline context.')
108
- lines.append('For visual modes this usually means a stable screenshot; for data, audio, log, metric, or custom modes it may mean structured proofEvidence, JSON artifacts, console observations, or assertions.')
108
+ lines.append('For visual modes this usually means a stable screenshot; for playable/gameplay modes it must also prove accepted input, state/time progression, and playfield/canvas pixel motion; for data, audio, log, metric, or custom modes it may mean structured proofEvidence, JSON artifacts, console observations, or assertions.')
109
109
  lines.append('Revise this draft only when the supervising agent concludes the proof needs richer interactions, better sense data, or tighter framing.')
110
110
  return '\n'.join(lines)
111
111
 
@@ -151,6 +151,7 @@ def author_request_payload(state, reference, baselines, current_plan, hypothesis
151
151
  'Return the authored packet via author_packet_json when possible. You may also set proof_plan, capture_script, server_path, and wait_for_selector directly.',
152
152
  'Keep capture_script concise Playwright statements.',
153
153
  'For visual/UI proof, include saveScreenshot(\'after-proof\') exactly once.',
154
+ 'For playable/gameplay proof, start the experience, send keyboard or pointer input, sample state before/after, measure non-HUD playfield/canvas pixel deltas across time, and set window.__riddleProofEvidence.playability or playability_evidence with version riddle-proof.playability.v1.',
154
155
  'For data/audio/log/metric/custom proof, screenshots are optional; set window.__riddleProofEvidence inside page.evaluate to a JSON-serializable object with the measured observations the verifier should judge.',
155
156
  'Do not assign globalThis.__riddleProofEvidence, window.__riddleProofEvidence, or self.__riddleProofEvidence outside page.evaluate; the Riddle worker context may not expose those globals safely.',
156
157
  'Do not begin capture_script with page.goto unless an in-app navigation is genuinely required after the preview opens the target route.',