@riddledc/riddle-proof 0.8.6 → 0.8.7

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 (49) hide show
  1. package/dist/adapters/codex-exec-agent.cjs +30 -10
  2. package/dist/adapters/codex-exec-agent.js +1 -1
  3. package/dist/adapters/codex.cjs +30 -10
  4. package/dist/adapters/codex.js +1 -1
  5. package/dist/adapters/local-agent.cjs +30 -10
  6. package/dist/adapters/local-agent.js +1 -1
  7. package/dist/advanced/engine-harness.cjs +64 -7
  8. package/dist/advanced/engine-harness.js +2 -2
  9. package/dist/advanced/index.cjs +64 -7
  10. package/dist/advanced/index.d.cts +1 -1
  11. package/dist/advanced/index.d.ts +1 -1
  12. package/dist/advanced/index.js +4 -4
  13. package/dist/advanced/proof-run-core.cjs +63 -6
  14. package/dist/advanced/proof-run-core.js +1 -1
  15. package/dist/advanced/proof-run-engine.cjs +63 -6
  16. package/dist/advanced/proof-run-engine.d.cts +1 -1
  17. package/dist/advanced/proof-run-engine.d.ts +1 -1
  18. package/dist/advanced/proof-run-engine.js +2 -2
  19. package/dist/advanced/runner.js +2 -2
  20. package/dist/{chunk-GMZ57RRY.js → chunk-46DDSZJR.js} +1 -1
  21. package/dist/{chunk-RV6LK7HU.js → chunk-5N5QFI2S.js} +63 -6
  22. package/dist/{chunk-UIJ7X63P.js → chunk-5N6MQCLC.js} +1 -1
  23. package/dist/{chunk-BDFSMWTI.js → chunk-E7ATYSYS.js} +1 -1
  24. package/dist/{chunk-5MILMRQY.js → chunk-PYCQNK66.js} +30 -10
  25. package/dist/{chunk-NAFJ4KSF.js → chunk-V6VZ3CAI.js} +2 -2
  26. package/dist/cli/index.js +4 -4
  27. package/dist/cli.cjs +99 -22
  28. package/dist/cli.js +4 -4
  29. package/dist/codex-exec-agent.cjs +30 -10
  30. package/dist/codex-exec-agent.js +1 -1
  31. package/dist/engine-harness.cjs +64 -7
  32. package/dist/engine-harness.js +2 -2
  33. package/dist/index.cjs +99 -22
  34. package/dist/index.js +4 -4
  35. package/dist/local-agent.cjs +30 -10
  36. package/dist/local-agent.js +1 -1
  37. package/dist/proof-run-core.cjs +63 -6
  38. package/dist/proof-run-core.js +1 -1
  39. package/dist/{proof-run-engine-BO1h0Bmy.d.cts → proof-run-engine-B7DCPzpK.d.cts} +3 -3
  40. package/dist/{proof-run-engine-CIdpWNh6.d.ts → proof-run-engine-BomAcXhA.d.ts} +3 -3
  41. package/dist/proof-run-engine.cjs +63 -6
  42. package/dist/proof-run-engine.d.cts +1 -1
  43. package/dist/proof-run-engine.d.ts +1 -1
  44. package/dist/proof-run-engine.js +2 -2
  45. package/dist/runner.js +2 -2
  46. package/package.json +1 -1
  47. package/runtime/lib/author.py +39 -1
  48. package/runtime/lib/verify.py +37 -1
  49. package/runtime/tests/recon_verify_smoke.py +70 -8
@@ -26,7 +26,7 @@ import {
26
26
  visualDeltaShipGateReason,
27
27
  workflowFile,
28
28
  writeState
29
- } from "./chunk-RV6LK7HU.js";
29
+ } from "./chunk-5N5QFI2S.js";
30
30
  import "./chunk-MLKGABMK.js";
31
31
  export {
32
32
  BUNDLED_RIDDLE_PROOF_DIR,
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
292
292
  blocking?: boolean;
293
293
  details?: Record<string, unknown>;
294
294
  ok: boolean;
295
- action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
295
+ action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
296
296
  state_path: string;
297
297
  stage: any;
298
298
  summary: string;
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
382
382
  continueWithStage?: WorkflowStage | null;
383
383
  blocking?: boolean;
384
384
  details?: Record<string, unknown>;
385
- action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
385
+ action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
386
386
  state_path: string;
387
387
  stage: any;
388
388
  checkpoint: string;
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
659
659
  error?: undefined;
660
660
  } | {
661
661
  ok: boolean;
662
- action: "setup" | "recon" | "author" | "implement" | "verify" | "ship";
662
+ action: "author" | "recon" | "ship" | "implement" | "verify" | "setup";
663
663
  state_path: string;
664
664
  stage: any;
665
665
  summary: string;
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
292
292
  blocking?: boolean;
293
293
  details?: Record<string, unknown>;
294
294
  ok: boolean;
295
- action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
295
+ action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
296
296
  state_path: string;
297
297
  stage: any;
298
298
  summary: string;
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
382
382
  continueWithStage?: WorkflowStage | null;
383
383
  blocking?: boolean;
384
384
  details?: Record<string, unknown>;
385
- action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
385
+ action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
386
386
  state_path: string;
387
387
  stage: any;
388
388
  checkpoint: string;
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
659
659
  error?: undefined;
660
660
  } | {
661
661
  ok: boolean;
662
- action: "setup" | "recon" | "author" | "implement" | "verify" | "ship";
662
+ action: "author" | "recon" | "ship" | "implement" | "verify" | "setup";
663
663
  state_path: string;
664
664
  stage: any;
665
665
  summary: string;
@@ -195,6 +195,56 @@ function writeState(statePath, state) {
195
195
  function normalizeOptionalString(value) {
196
196
  return typeof value === "string" ? value.trim() : void 0;
197
197
  }
198
+ var INTERACTION_VERIFICATION_MODES = /* @__PURE__ */ new Set(["interaction", "interactive", "user_flow", "user-flow", "workflow"]);
199
+ function normalizeRoutePath(value) {
200
+ const raw = typeof value === "string" ? value.trim() : "";
201
+ if (!raw) return "";
202
+ try {
203
+ const url = /^https?:\/\//i.test(raw) ? new URL(raw) : new URL(raw.startsWith("/") || raw.startsWith("?") || raw.startsWith("#") ? raw : `/${raw}`, "https://riddle-proof.local");
204
+ const pathname = url.pathname.replace(/\/+$/, "") || "/";
205
+ return `${pathname}${url.search}${url.hash}`;
206
+ } catch {
207
+ const hashSplit = raw.split("#");
208
+ const beforeHash = hashSplit.shift() || "";
209
+ const hash = hashSplit.length ? `#${hashSplit.join("#")}` : "";
210
+ const querySplit = beforeHash.split("?");
211
+ const rawPath = querySplit.shift() || "";
212
+ const query = querySplit.length ? `?${querySplit.join("?")}` : "";
213
+ const pathname = `/${rawPath}`.replace(/\/+/g, "/").replace(/\/+$/, "") || "/";
214
+ return `${pathname}${query}${hash}`;
215
+ }
216
+ }
217
+ function isInteractionVerificationMode(value) {
218
+ return INTERACTION_VERIFICATION_MODES.has(typeof value === "string" ? value.trim().toLowerCase() : "");
219
+ }
220
+ function stringRecordValue(record, key) {
221
+ if (!record || typeof record !== "object") return "";
222
+ const value = record[key];
223
+ return typeof value === "string" ? value.trim() : "";
224
+ }
225
+ function appendStateWarning(state, key, warning) {
226
+ const existing = Array.isArray(state[key]) ? state[key].filter((item) => typeof item === "string") : [];
227
+ if (!existing.includes(warning)) state[key] = [...existing, warning];
228
+ }
229
+ function interactionStartPathForAuthorPacket(state, parsed, refined) {
230
+ return normalizeRoutePath(
231
+ stringRecordValue(state, "expected_start_path") || stringRecordValue(refined, "expected_start_path") || stringRecordValue(parsed.interaction_contract, "start_path") || stringRecordValue(parsed.proof_contract, "start_path") || stringRecordValue(state, "server_path") || "/"
232
+ ) || "/";
233
+ }
234
+ function authorPacketServerPath(state, parsed, refined, serverPath, expectedTerminalPath) {
235
+ if (!isInteractionVerificationMode(state.verification_mode)) return serverPath;
236
+ const startPath = interactionStartPathForAuthorPacket(state, parsed, refined);
237
+ state.expected_start_path = startPath;
238
+ if (expectedTerminalPath && normalizeRoutePath(serverPath) === normalizeRoutePath(expectedTerminalPath) && normalizeRoutePath(serverPath) !== startPath) {
239
+ appendStateWarning(
240
+ state,
241
+ "author_warnings",
242
+ "Supervisor packet refined_inputs.server_path matched the terminal interaction route; kept the recon start route for capture."
243
+ );
244
+ return startPath;
245
+ }
246
+ return serverPath;
247
+ }
198
248
  function knownEnvironmentIssuesFromNotes(notes) {
199
249
  const text = notes.toLowerCase();
200
250
  const issues = [];
@@ -853,17 +903,24 @@ function mergeStateFromParams(statePath, params) {
853
903
  state.proof_contract = parsed.proof_contract;
854
904
  }
855
905
  const refined = parsed?.refined_inputs || {};
906
+ const expectedTerminalPath = normalizeOptionalString(
907
+ typeof refined?.expected_terminal_path === "string" ? refined.expected_terminal_path : typeof parsed?.expected_terminal_path === "string" ? parsed.expected_terminal_path : ""
908
+ ) || "";
856
909
  if (typeof refined?.server_path === "string") {
857
- state.server_path = normalizeOptionalString(refined.server_path) || "";
910
+ const refinedServerPath = normalizeOptionalString(refined.server_path) || "";
911
+ state.server_path = authorPacketServerPath(
912
+ state,
913
+ parsed,
914
+ refined,
915
+ refinedServerPath,
916
+ expectedTerminalPath
917
+ );
858
918
  state.server_path_source = "supervising_agent";
859
919
  }
860
920
  if (typeof refined?.wait_for_selector === "string") state.wait_for_selector = normalizeOptionalString(refined.wait_for_selector) || "";
861
921
  if (typeof refined?.reference === "string" && refined.reference.trim()) state.reference = refined.reference.trim();
862
- if (typeof refined?.expected_terminal_path === "string") {
863
- state.expected_terminal_path = normalizeOptionalString(refined.expected_terminal_path) || "";
864
- }
865
- if (typeof parsed?.expected_terminal_path === "string") {
866
- state.expected_terminal_path = normalizeOptionalString(parsed.expected_terminal_path) || "";
922
+ if (expectedTerminalPath) {
923
+ state.expected_terminal_path = expectedTerminalPath;
867
924
  }
868
925
  if (typeof parsed?.confidence === "string") state.supervisor_author_confidence = normalizeOptionalString(parsed.confidence) || null;
869
926
  if (parsed?.rationale !== void 0) state.supervisor_author_rationale = parsed.rationale;
@@ -1,2 +1,2 @@
1
1
  import './proof-run-core-CE0jx7wL.cjs';
2
- export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from './proof-run-engine-BO1h0Bmy.cjs';
2
+ export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from './proof-run-engine-B7DCPzpK.cjs';
@@ -1,2 +1,2 @@
1
1
  import './proof-run-core-CE0jx7wL.js';
2
- export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from './proof-run-engine-CIdpWNh6.js';
2
+ export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from './proof-run-engine-BomAcXhA.js';
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  createRiddleProofEngine,
3
3
  executeWorkflow
4
- } from "./chunk-GMZ57RRY.js";
5
- import "./chunk-RV6LK7HU.js";
4
+ } from "./chunk-46DDSZJR.js";
5
+ import "./chunk-5N5QFI2S.js";
6
6
  import "./chunk-MLKGABMK.js";
7
7
  export {
8
8
  createRiddleProofEngine,
package/dist/runner.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  runRiddleProof
3
- } from "./chunk-UIJ7X63P.js";
3
+ } from "./chunk-5N6MQCLC.js";
4
4
  import "./chunk-YZUVEJ5B.js";
5
5
  import "./chunk-FMOYUYH2.js";
6
- import "./chunk-RV6LK7HU.js";
6
+ import "./chunk-5N5QFI2S.js";
7
7
  import "./chunk-4FOHZ7JG.js";
8
8
  import "./chunk-VY4Y5U57.js";
9
9
  import "./chunk-MLKGABMK.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",
@@ -9,6 +9,7 @@ Instead it does two things:
9
9
  import json
10
10
  import os
11
11
  import sys
12
+ from urllib.parse import urlparse
12
13
 
13
14
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
14
15
  from util import load_state, save_state
@@ -34,6 +35,31 @@ def normalize_path(value):
34
35
  return path
35
36
 
36
37
 
38
+ def normalize_route_path(value):
39
+ raw = (value or '').strip()
40
+ if not raw:
41
+ return ''
42
+ parsed = urlparse(raw)
43
+ path = parsed.path or raw
44
+ query = parsed.query or ''
45
+ fragment = parsed.fragment or ''
46
+ if '?' in path:
47
+ path, query_tail = path.split('?', 1)
48
+ query = query or query_tail.split('#', 1)[0]
49
+ if '#' in path:
50
+ path, fragment_tail = path.split('#', 1)
51
+ fragment = fragment or fragment_tail
52
+ if not path.startswith('/'):
53
+ path = '/' + path.lstrip('/')
54
+ path = path.rstrip('/') or '/'
55
+ return path + (('?' + query) if query else '') + (('#' + fragment) if fragment else '')
56
+
57
+
58
+ def is_interaction_mode(state):
59
+ mode = (state.get('verification_mode') or '').strip().lower()
60
+ return mode in ('interaction', 'interactive', 'user_flow', 'user-flow', 'workflow')
61
+
62
+
37
63
  def first_non_empty(*values):
38
64
  for value in values:
39
65
  if isinstance(value, str) and value.strip():
@@ -284,6 +310,17 @@ expected_terminal_path = normalize_path(first_non_empty(
284
310
  supervisor_packet.get('expected_after_path'),
285
311
  s.get('expected_terminal_path'),
286
312
  ))
313
+ author_warnings = []
314
+ if is_interaction_mode(s):
315
+ interaction_start_path = normalize_route_path(first_non_empty(s.get('expected_start_path'), default_path, s.get('server_path'), '/')) or '/'
316
+ refined_route = normalize_route_path(refined_path)
317
+ terminal_route = normalize_route_path(expected_terminal_path)
318
+ if terminal_route and refined_route == terminal_route and refined_route != interaction_start_path:
319
+ refined_path = interaction_start_path
320
+ author_warnings.append(
321
+ 'Supervisor packet refined_inputs.server_path matched the terminal interaction route; kept the recon start route for capture.'
322
+ )
323
+ s['expected_start_path'] = interaction_start_path
287
324
  confidence = provided_payload['confidence'] if provided_payload['confidence'] in ('high', 'medium', 'low') else 'medium'
288
325
  rationale = sanitize_rationale(provided_payload['rationale'])
289
326
  summary = provided_payload['summary'] or 'Supervising agent supplied the proof packet from recon observations.'
@@ -301,6 +338,7 @@ authored_packet = {
301
338
  'interaction_contract': provided_payload['interaction_contract'],
302
339
  'proof_contract': provided_payload['proof_contract'],
303
340
  'rationale': rationale,
341
+ 'warnings': author_warnings,
304
342
  'confidence': confidence,
305
343
  'mode': 'supervising_agent',
306
344
  'model': ('supervising-agent:' + RUNTIME_MODEL_HINT) if RUNTIME_MODEL_HINT else 'supervising-agent',
@@ -328,7 +366,7 @@ s['author_mode'] = 'supervising_agent'
328
366
  s['author_model'] = authored_packet['model']
329
367
  s['author_confidence'] = confidence
330
368
  s['author_rationale'] = rationale
331
- s['author_warnings'] = []
369
+ s['author_warnings'] = author_warnings
332
370
  s['author_runtime_model_hint'] = RUNTIME_MODEL_HINT
333
371
  s['author_packet'] = authored_packet
334
372
  s['author_summary'] = summary
@@ -1995,16 +1995,22 @@ def route_parts(value):
1995
1995
 
1996
1996
  EXPLICIT_TERMINAL_PATH_KEYS = (
1997
1997
  'expected_terminal_path', 'expectedTerminalPath',
1998
+ 'expected_terminal_url', 'expectedTerminalUrl',
1998
1999
  'expected_terminal_route', 'expectedTerminalRoute',
1999
2000
  'terminal_path', 'terminalPath',
2001
+ 'terminal_url', 'terminalUrl',
2000
2002
  'terminal_route', 'terminalRoute',
2001
2003
  'expected_after_path', 'expectedAfterPath',
2004
+ 'expected_after_url', 'expectedAfterUrl',
2002
2005
  'expected_after_route', 'expectedAfterRoute',
2003
2006
  'after_path', 'afterPath',
2007
+ 'after_url', 'afterUrl',
2004
2008
  'after_route', 'afterRoute',
2005
2009
  'expected_final_path', 'expectedFinalPath',
2010
+ 'expected_final_url', 'expectedFinalUrl',
2006
2011
  'expected_final_route', 'expectedFinalRoute',
2007
2012
  'final_path', 'finalPath',
2013
+ 'final_url', 'finalUrl',
2008
2014
  'final_route', 'finalRoute',
2009
2015
  )
2010
2016
  LOCATION_PATH_KEYS = ('path', 'pathname', 'route', 'url', 'href')
@@ -2016,6 +2022,11 @@ AFTER_STATE_KEYS = (
2016
2022
  'final', 'final_state', 'finalState',
2017
2023
  'expected_final', 'expectedFinal',
2018
2024
  )
2025
+ EVIDENCE_CONTAINER_KEYS = (
2026
+ 'proofEvidence', 'proof_evidence',
2027
+ 'interactionEvidence', 'interaction_evidence',
2028
+ 'evidence',
2029
+ )
2019
2030
  CONTRACT_STATE_KEYS = (
2020
2031
  'interaction_contract', 'interactionContract',
2021
2032
  'proof_contract', 'proofContract',
@@ -2067,6 +2078,17 @@ def terminal_path_from_record(record, depth=0):
2067
2078
  candidate = terminal_path_from_record(item, depth + 1)
2068
2079
  if candidate:
2069
2080
  return candidate
2081
+ for key in EVIDENCE_CONTAINER_KEYS:
2082
+ value = record.get(key)
2083
+ if isinstance(value, dict):
2084
+ candidate = terminal_path_from_record(value, depth + 1)
2085
+ if candidate:
2086
+ return candidate
2087
+ elif isinstance(value, list):
2088
+ for item in value:
2089
+ candidate = terminal_path_from_record(item, depth + 1)
2090
+ if candidate:
2091
+ return candidate
2070
2092
  for key in CONTRACT_STATE_KEYS:
2071
2093
  value = record.get(key)
2072
2094
  if isinstance(value, dict):
@@ -2081,11 +2103,25 @@ def terminal_path_from_record(record, depth=0):
2081
2103
  return ''
2082
2104
 
2083
2105
 
2106
+ def text_path_candidate(value):
2107
+ if not isinstance(value, str):
2108
+ return ''
2109
+ raw = value.strip().rstrip('.,;:)]}')
2110
+ return path_candidate(raw)
2111
+
2112
+
2084
2113
  def terminal_path_from_text(value):
2085
2114
  if not isinstance(value, str):
2086
2115
  return ''
2087
2116
  for match in re.findall(r"""['"`](/[^'"`\s]+[?#][^'"`\s]*)['"`]""", value):
2088
- candidate = path_candidate(match)
2117
+ candidate = text_path_candidate(match)
2118
+ if candidate:
2119
+ return candidate
2120
+ context_pattern = re.compile(
2121
+ r"""(?is)\b(?:expected\s+(?:terminal|after|final)|terminal|after|final)\b[^/\r\n]{0,120}['"`]?(/[^'"`\s,;)]*)"""
2122
+ )
2123
+ for match in context_pattern.findall(value):
2124
+ candidate = text_path_candidate(match)
2089
2125
  if candidate:
2090
2126
  return candidate
2091
2127
  return ''
@@ -340,14 +340,18 @@ class FakeRiddle:
340
340
  'largeVisibleElements': [{'tag': 'h1', 'text': 'Proof'}],
341
341
  }
342
342
  proof_evidence = {
343
- 'before': {'path': '/'},
344
- 'action': 'clicked Proof',
345
- 'after': {'path': '/proof/'},
346
- 'assertions': {
347
- 'startedOnHome': True,
348
- 'clickedProofNavigation': True,
349
- 'terminalPathIsProof': True,
350
- 'proofContentVisible': True,
343
+ 'proofEvidence': {
344
+ 'version': 'riddle-proof.interaction.v1',
345
+ 'start': {'href': 'https://riddledc.com/'},
346
+ 'action': {'type': 'click', 'target': 'Proof'},
347
+ 'terminal': {'href': 'https://riddledc.com/proof/'},
348
+ 'afterUrl': 'https://riddledc.com/proof/',
349
+ 'assertions': {
350
+ 'startedOnHome': True,
351
+ 'clickedProofNavigation': True,
352
+ 'terminalPathIsProof': True,
353
+ 'proofContentVisible': True,
354
+ },
351
355
  },
352
356
  }
353
357
  return {
@@ -1921,6 +1925,63 @@ def run_author_applies_supervisor_packet():
1921
1925
  shutil.rmtree(tempdir, ignore_errors=True)
1922
1926
 
1923
1927
 
1928
+ def run_author_keeps_interaction_start_route():
1929
+ tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-supervisor-interaction-start-'))
1930
+ state_path = tempdir / 'state.json'
1931
+ try:
1932
+ state = base_state(tempdir, reference='before')
1933
+ state.update({
1934
+ 'recon_status': 'ready_for_proof_plan',
1935
+ 'verification_mode': 'interaction',
1936
+ 'server_path': '/',
1937
+ 'expected_start_path': '/',
1938
+ 'before_cdn': 'https://cdn.example.com/before-home.png',
1939
+ 'recon_results': {
1940
+ 'baselines': {'before': {'path': '/', 'url': 'https://cdn.example.com/before-home.png'}},
1941
+ 'current_plan': {'target_path': '/'},
1942
+ },
1943
+ 'author_request': {
1944
+ 'current_plan': {'target_path': '/'},
1945
+ 'observed_baselines': {'before': {'path': '/', 'url': 'https://cdn.example.com/before-home.png'}},
1946
+ },
1947
+ 'supervisor_author_packet': {
1948
+ 'proof_plan': 'Start at /, click Proof, and verify the terminal /proof/ route.',
1949
+ 'capture_script': "clickedProofNavigation(); await saveScreenshot('after-proof');",
1950
+ 'refined_inputs': {
1951
+ 'server_path': '/proof/',
1952
+ 'expected_terminal_path': '/proof/',
1953
+ 'wait_for_selector': '',
1954
+ 'reference': 'before',
1955
+ },
1956
+ 'rationale': ['The interaction starts on home and terminates on Proof.'],
1957
+ 'confidence': 'high',
1958
+ 'summary': 'Supervisor supplied the interaction proof packet.',
1959
+ },
1960
+ })
1961
+ write_state(state_path, state)
1962
+ os.environ['RIDDLE_PROOF_STATE_FILE'] = str(state_path)
1963
+
1964
+ fake = FakeRiddle()
1965
+ load_util_with_fake(fake)
1966
+ load_module('author_supervisor_interaction_start', AUTHOR_PATH)
1967
+ after_author = json.loads(state_path.read_text())
1968
+
1969
+ assert after_author['author_status'] == 'ready'
1970
+ assert after_author['server_path'] == '/'
1971
+ assert after_author['expected_start_path'] == '/'
1972
+ assert after_author['expected_terminal_path'] == '/proof/'
1973
+ assert after_author['author_packet']['refined_inputs']['server_path'] == '/'
1974
+ assert after_author['author_warnings']
1975
+ assert 'terminal interaction route' in after_author['author_warnings'][0]
1976
+ return {
1977
+ 'ok': True,
1978
+ 'server_path': after_author['server_path'],
1979
+ 'expected_terminal_path': after_author['expected_terminal_path'],
1980
+ }
1981
+ finally:
1982
+ shutil.rmtree(tempdir, ignore_errors=True)
1983
+
1984
+
1924
1985
  def run_verify_requests_supervisor_assessment():
1925
1986
  tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-verify-supervisor-'))
1926
1987
  state_path = tempdir / 'state.json'
@@ -3009,6 +3070,7 @@ if __name__ == '__main__':
3009
3070
  'recon_hint_root_preference': run_recon_prefers_hint_root_over_single_route_literal(),
3010
3071
  'capture_hint_rejects_route_specific_mode_only_match': run_capture_hint_rejects_route_specific_mode_only_match(),
3011
3072
  'author_applies_supervisor_packet': run_author_applies_supervisor_packet(),
3073
+ 'author_keeps_interaction_start_route': run_author_keeps_interaction_start_route(),
3012
3074
  'verify_requests_supervisor_assessment': run_verify_requests_supervisor_assessment(),
3013
3075
  'verify_routes_unmeasured_visual_delta_to_recovery': run_verify_routes_unmeasured_visual_delta_to_recovery(),
3014
3076
  'verify_structured_evidence_without_screenshot': run_verify_structured_evidence_without_screenshot(),