@riddledc/riddle-proof 0.7.223 → 0.7.224

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.
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-UYB7ABWU.js";
7
7
  import {
8
8
  noImplementationModeFor
9
- } from "./chunk-SGXB5S4B.js";
9
+ } from "./chunk-WWYZBWKZ.js";
10
10
  import {
11
11
  createRunResult
12
12
  } from "./chunk-VY4Y5U57.js";
@@ -14,7 +14,7 @@ import {
14
14
  visualDeltaForState,
15
15
  visualDeltaRequiredForState,
16
16
  visualDeltaShipGateReason
17
- } from "./chunk-SGXB5S4B.js";
17
+ } from "./chunk-WWYZBWKZ.js";
18
18
  import {
19
19
  authorPacketPayloadFromCheckpointResponse,
20
20
  buildCheckpointPacketForEngineResult,
@@ -23,6 +23,9 @@ function noImplementationModeFor(params, state) {
23
23
  const allowCodeChanges = input.allow_code_changes ?? stateInput.allow_code_changes;
24
24
  return mode === "audit" || mode === "profile" || implementationMode === "none" || requireDiff === false || allowCodeChanges === false;
25
25
  }
26
+ function canRunSetupWithoutRepo(params) {
27
+ return noImplementationModeFor(params) && Boolean((params.prod_url || "").trim());
28
+ }
26
29
  var CHECKPOINT_CONTRACT_VERSION = "riddle-proof-run.checkpoint.v1";
27
30
  function currentDistDir() {
28
31
  const meta = typeof import.meta === "object" ? import.meta : {};
@@ -88,7 +91,7 @@ function asJsonString(value) {
88
91
  return JSON.stringify(value);
89
92
  }
90
93
  function buildSetupArgs(params, config) {
91
- if (!params.repo) throw new Error("repo is required for setup/run");
94
+ if (!params.repo && !canRunSetupWithoutRepo(params)) throw new Error("repo is required for setup/run");
92
95
  if (!params.change_request) throw new Error("change_request is required for setup/run");
93
96
  const commitMessage = (params.commit_message || params.change_request || "").trim();
94
97
  const captureScript = (params.capture_script || "").trim();
package/dist/cli.cjs CHANGED
@@ -50,6 +50,9 @@ function noImplementationModeFor(params, state) {
50
50
  const allowCodeChanges = input.allow_code_changes ?? stateInput.allow_code_changes;
51
51
  return mode === "audit" || mode === "profile" || implementationMode === "none" || requireDiff === false || allowCodeChanges === false;
52
52
  }
53
+ function canRunSetupWithoutRepo(params) {
54
+ return noImplementationModeFor(params) && Boolean((params.prod_url || "").trim());
55
+ }
53
56
  function currentDistDir() {
54
57
  const meta = typeof import_meta === "object" ? import_meta : {};
55
58
  if (typeof meta.url === "string" && meta.url) {
@@ -106,7 +109,7 @@ function asJsonString(value) {
106
109
  return JSON.stringify(value);
107
110
  }
108
111
  function buildSetupArgs(params, config) {
109
- if (!params.repo) throw new Error("repo is required for setup/run");
112
+ if (!params.repo && !canRunSetupWithoutRepo(params)) throw new Error("repo is required for setup/run");
110
113
  if (!params.change_request) throw new Error("change_request is required for setup/run");
111
114
  const commitMessage = (params.commit_message || params.change_request || "").trim();
112
115
  const captureScript = (params.capture_script || "").trim();
package/dist/cli.js CHANGED
@@ -23,10 +23,10 @@ import {
23
23
  createDisabledRiddleProofAgentAdapter,
24
24
  readRiddleProofRunStatus,
25
25
  runRiddleProofEngineHarness
26
- } from "./chunk-MPPLZ35C.js";
26
+ } from "./chunk-VJLHXZAA.js";
27
27
  import "./chunk-UYB7ABWU.js";
28
28
  import "./chunk-ZHEFYLII.js";
29
- import "./chunk-SGXB5S4B.js";
29
+ import "./chunk-WWYZBWKZ.js";
30
30
  import {
31
31
  createCheckpointResponseTemplate
32
32
  } from "./chunk-ABQQLRTS.js";
@@ -50,6 +50,9 @@ function noImplementationModeFor(params, state) {
50
50
  const allowCodeChanges = input.allow_code_changes ?? stateInput.allow_code_changes;
51
51
  return mode === "audit" || mode === "profile" || implementationMode === "none" || requireDiff === false || allowCodeChanges === false;
52
52
  }
53
+ function canRunSetupWithoutRepo(params) {
54
+ return noImplementationModeFor(params) && Boolean((params.prod_url || "").trim());
55
+ }
53
56
  function currentDistDir() {
54
57
  const meta = typeof import_meta === "object" ? import_meta : {};
55
58
  if (typeof meta.url === "string" && meta.url) {
@@ -106,7 +109,7 @@ function asJsonString(value) {
106
109
  return JSON.stringify(value);
107
110
  }
108
111
  function buildSetupArgs(params, config) {
109
- if (!params.repo) throw new Error("repo is required for setup/run");
112
+ if (!params.repo && !canRunSetupWithoutRepo(params)) throw new Error("repo is required for setup/run");
110
113
  if (!params.change_request) throw new Error("change_request is required for setup/run");
111
114
  const commitMessage = (params.commit_message || params.change_request || "").trim();
112
115
  const captureScript = (params.capture_script || "").trim();
@@ -2,10 +2,10 @@ import {
2
2
  createDisabledRiddleProofAgentAdapter,
3
3
  readRiddleProofRunStatus,
4
4
  runRiddleProofEngineHarness
5
- } from "./chunk-MPPLZ35C.js";
5
+ } from "./chunk-VJLHXZAA.js";
6
6
  import "./chunk-UYB7ABWU.js";
7
7
  import "./chunk-ZHEFYLII.js";
8
- import "./chunk-SGXB5S4B.js";
8
+ import "./chunk-WWYZBWKZ.js";
9
9
  import "./chunk-ABQQLRTS.js";
10
10
  import "./chunk-VY4Y5U57.js";
11
11
  export {
package/dist/index.cjs CHANGED
@@ -50,6 +50,9 @@ function noImplementationModeFor(params, state) {
50
50
  const allowCodeChanges = input.allow_code_changes ?? stateInput.allow_code_changes;
51
51
  return mode === "audit" || mode === "profile" || implementationMode === "none" || requireDiff === false || allowCodeChanges === false;
52
52
  }
53
+ function canRunSetupWithoutRepo(params) {
54
+ return noImplementationModeFor(params) && Boolean((params.prod_url || "").trim());
55
+ }
53
56
  function currentDistDir() {
54
57
  const meta = typeof import_meta === "object" ? import_meta : {};
55
58
  if (typeof meta.url === "string" && meta.url) {
@@ -106,7 +109,7 @@ function asJsonString(value) {
106
109
  return JSON.stringify(value);
107
110
  }
108
111
  function buildSetupArgs(params, config) {
109
- if (!params.repo) throw new Error("repo is required for setup/run");
112
+ if (!params.repo && !canRunSetupWithoutRepo(params)) throw new Error("repo is required for setup/run");
110
113
  if (!params.change_request) throw new Error("change_request is required for setup/run");
111
114
  const commitMessage = (params.commit_message || params.change_request || "").trim();
112
115
  const captureScript = (params.capture_script || "").trim();
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runRiddleProof
3
- } from "./chunk-ENWYM4L7.js";
3
+ } from "./chunk-MA6ZR5XY.js";
4
4
  import "./chunk-6F4PWJZI.js";
5
5
  import {
6
6
  RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION,
@@ -95,7 +95,7 @@ import {
95
95
  createDisabledRiddleProofAgentAdapter,
96
96
  readRiddleProofRunStatus,
97
97
  runRiddleProofEngineHarness
98
- } from "./chunk-MPPLZ35C.js";
98
+ } from "./chunk-VJLHXZAA.js";
99
99
  import {
100
100
  RIDDLE_PROOF_RUN_STATE_VERSION,
101
101
  appendRunEvent,
@@ -112,7 +112,7 @@ import {
112
112
  RIDDLE_PROOF_RUN_CARD_VERSION,
113
113
  createRiddleProofRunCard
114
114
  } from "./chunk-ZHEFYLII.js";
115
- import "./chunk-SGXB5S4B.js";
115
+ import "./chunk-WWYZBWKZ.js";
116
116
  import {
117
117
  RIDDLE_PROOF_CHECKPOINT_PACKET_VERSION,
118
118
  RIDDLE_PROOF_CHECKPOINT_RESPONSE_VERSION,
@@ -84,6 +84,9 @@ function noImplementationModeFor(params, state) {
84
84
  const allowCodeChanges = input.allow_code_changes ?? stateInput.allow_code_changes;
85
85
  return mode === "audit" || mode === "profile" || implementationMode === "none" || requireDiff === false || allowCodeChanges === false;
86
86
  }
87
+ function canRunSetupWithoutRepo(params) {
88
+ return noImplementationModeFor(params) && Boolean((params.prod_url || "").trim());
89
+ }
87
90
  var CHECKPOINT_CONTRACT_VERSION = "riddle-proof-run.checkpoint.v1";
88
91
  function currentDistDir() {
89
92
  const meta = typeof import_meta === "object" ? import_meta : {};
@@ -149,7 +152,7 @@ function asJsonString(value) {
149
152
  return JSON.stringify(value);
150
153
  }
151
154
  function buildSetupArgs(params, config) {
152
- if (!params.repo) throw new Error("repo is required for setup/run");
155
+ if (!params.repo && !canRunSetupWithoutRepo(params)) throw new Error("repo is required for setup/run");
153
156
  if (!params.change_request) throw new Error("change_request is required for setup/run");
154
157
  const commitMessage = (params.commit_message || params.change_request || "").trim();
155
158
  const captureScript = (params.capture_script || "").trim();
@@ -106,7 +106,7 @@ declare function resolveConfig(config?: PluginConfig, params?: Partial<Pick<Work
106
106
  declare function ensureAction(action: string): WorkflowAction;
107
107
  declare function workflowFile(riddleProofDir: string, action: WorkflowStage): string;
108
108
  declare function buildSetupArgs(params: WorkflowParams, config: ReturnType<typeof resolveConfig>): {
109
- repo: string;
109
+ repo: string | undefined;
110
110
  branch: string;
111
111
  change_request: string;
112
112
  commit_message: string;
@@ -106,7 +106,7 @@ declare function resolveConfig(config?: PluginConfig, params?: Partial<Pick<Work
106
106
  declare function ensureAction(action: string): WorkflowAction;
107
107
  declare function workflowFile(riddleProofDir: string, action: WorkflowStage): string;
108
108
  declare function buildSetupArgs(params: WorkflowParams, config: ReturnType<typeof resolveConfig>): {
109
- repo: string;
109
+ repo: string | undefined;
110
110
  branch: string;
111
111
  change_request: string;
112
112
  commit_message: string;
@@ -26,7 +26,7 @@ import {
26
26
  visualDeltaShipGateReason,
27
27
  workflowFile,
28
28
  writeState
29
- } from "./chunk-SGXB5S4B.js";
29
+ } from "./chunk-WWYZBWKZ.js";
30
30
  export {
31
31
  BUNDLED_RIDDLE_PROOF_DIR,
32
32
  CHECKPOINT_CONTRACT_VERSION,
@@ -64,6 +64,9 @@ function noImplementationModeFor(params, state) {
64
64
  const allowCodeChanges = input.allow_code_changes ?? stateInput.allow_code_changes;
65
65
  return mode === "audit" || mode === "profile" || implementationMode === "none" || requireDiff === false || allowCodeChanges === false;
66
66
  }
67
+ function canRunSetupWithoutRepo(params) {
68
+ return noImplementationModeFor(params) && Boolean((params.prod_url || "").trim());
69
+ }
67
70
  var CHECKPOINT_CONTRACT_VERSION = "riddle-proof-run.checkpoint.v1";
68
71
  function currentDistDir() {
69
72
  const meta = typeof import_meta === "object" ? import_meta : {};
@@ -129,7 +132,7 @@ function asJsonString(value) {
129
132
  return JSON.stringify(value);
130
133
  }
131
134
  function buildSetupArgs(params, config) {
132
- if (!params.repo) throw new Error("repo is required for setup/run");
135
+ if (!params.repo && !canRunSetupWithoutRepo(params)) throw new Error("repo is required for setup/run");
133
136
  if (!params.change_request) throw new Error("change_request is required for setup/run");
134
137
  const commitMessage = (params.commit_message || params.change_request || "").trim();
135
138
  const captureScript = (params.capture_script || "").trim();
@@ -15,7 +15,7 @@ import {
15
15
  validateShipGate,
16
16
  workflowFile,
17
17
  writeState
18
- } from "./chunk-SGXB5S4B.js";
18
+ } from "./chunk-WWYZBWKZ.js";
19
19
 
20
20
  // src/proof-run-engine.ts
21
21
  import { execFileSync } from "child_process";
package/dist/runner.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  runRiddleProof
3
- } from "./chunk-ENWYM4L7.js";
3
+ } from "./chunk-MA6ZR5XY.js";
4
4
  import "./chunk-UYB7ABWU.js";
5
5
  import "./chunk-ZHEFYLII.js";
6
- import "./chunk-SGXB5S4B.js";
6
+ import "./chunk-WWYZBWKZ.js";
7
7
  import "./chunk-ABQQLRTS.js";
8
8
  import "./chunk-VY4Y5U57.js";
9
9
  export {
@@ -96,6 +96,25 @@ export function workspaceRoots({ workspaceRoot = "", currentRepoDir = "" } = {})
96
96
  return roots;
97
97
  }
98
98
 
99
+ function expandHome(candidate) {
100
+ const text = String(candidate || "").trim();
101
+ if (!text.startsWith("~")) return text;
102
+ const home = process.env.HOME || "/root";
103
+ return path.join(home, text.slice(1));
104
+ }
105
+
106
+ function looksLikeLocalRepoRef(repo) {
107
+ const text = String(repo || "").trim();
108
+ if (!text) return false;
109
+ if (path.isAbsolute(text) || text.startsWith("./") || text.startsWith("../") || text.startsWith("~/")) return true;
110
+ return existsSync(expandHome(text));
111
+ }
112
+
113
+ function localRepoDirFromRef(repo) {
114
+ if (!looksLikeLocalRepoRef(repo)) return "";
115
+ return path.resolve(expandHome(repo));
116
+ }
117
+
99
118
  export function resolveRepoDir({ repoName, repoDir = "", workspaceRoot = "", currentRepoDir = "" }) {
100
119
  const candidates = [];
101
120
  if (repoDir) candidates.push(repoDir);
@@ -136,22 +155,27 @@ export function prepareRepo({
136
155
  if (!repo) throw new Error("repo is required");
137
156
  if (!branch) throw new Error("branch is required");
138
157
 
139
- const repoName = repo.split("/").pop() || repo;
140
- const resolvedRepoDir = resolveRepoDir({ repoName, repoDir, workspaceRoot });
158
+ const localRepoDir = localRepoDirFromRef(repo);
159
+ const repoName = localRepoDir ? path.basename(localRepoDir) : repo.split("/").pop() || repo;
160
+ const resolvedRepoDir = localRepoDir || resolveRepoDir({ repoName, repoDir, workspaceRoot });
161
+ const localRepo = Boolean(localRepoDir);
141
162
  const hadExistingRepo = existsSync(path.join(resolvedRepoDir, ".git"));
142
163
  mkdirSync(path.dirname(resolvedRepoDir), { recursive: true });
143
164
 
144
165
  if (hadExistingRepo) {
145
- if (ensureHttpsRemote) {
166
+ if (ensureHttpsRemote && !localRepo) {
146
167
  runSafe(`git remote set-url origin https://github.com/${repo}.git`, resolvedRepoDir);
147
168
  }
148
- if (fetch) {
169
+ if (fetch && !localRepo) {
149
170
  const fetchResult = runSafe("git fetch --prune origin", resolvedRepoDir, 60000);
150
171
  if (!fetchResult.ok) {
151
172
  throw new Error(`git fetch failed for ${resolvedRepoDir}: ${fetchResult.output.slice(0, 300)}`);
152
173
  }
153
174
  }
154
175
  } else {
176
+ if (localRepo) {
177
+ throw new Error(`local repo path does not contain a .git directory: ${resolvedRepoDir}`);
178
+ }
155
179
  run(`git clone https://github.com/${repo}.git ${shellQuote(resolvedRepoDir)}`, undefined, 120000);
156
180
  }
157
181
 
@@ -165,10 +189,15 @@ export function prepareRepo({
165
189
  branchResult = runSafe(`git branch ${shellQuote(branch)} ${shellQuote(`origin/${branch}`)}`, resolvedRepoDir);
166
190
  } else {
167
191
  const remoteBase = `origin/${baseBranch}`;
168
- branchResult = runSafe(`git branch ${shellQuote(branch)} ${shellQuote(remoteBase)}`, resolvedRepoDir);
169
- if (!branchResult.ok) {
192
+ branchResult = localRepo
193
+ ? runSafe(`git branch ${shellQuote(branch)} ${shellQuote(baseBranch)}`, resolvedRepoDir)
194
+ : runSafe(`git branch ${shellQuote(branch)} ${shellQuote(remoteBase)}`, resolvedRepoDir);
195
+ if (!branchResult.ok && !localRepo) {
170
196
  branchResult = runSafe(`git branch ${shellQuote(branch)} ${shellQuote(baseBranch)}`, resolvedRepoDir);
171
197
  }
198
+ if (!branchResult.ok && localRepo) {
199
+ branchResult = runSafe(`git branch ${shellQuote(branch)} HEAD`, resolvedRepoDir);
200
+ }
172
201
  }
173
202
  if (!branchResult.ok) {
174
203
  throw new Error(`Failed to prepare workspace branch ${branch}: ${branchResult.output.slice(0, 300)}`);
@@ -180,7 +209,8 @@ export function prepareRepo({
180
209
  repoName,
181
210
  repoDir: resolvedRepoDir,
182
211
  branch,
183
- source: hadExistingRepo ? "existing_repo" : "cloned_repo",
212
+ localRepo,
213
+ source: hadExistingRepo ? (localRepo ? "existing_local_repo" : "existing_repo") : "cloned_repo",
184
214
  };
185
215
  }
186
216
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.223",
3
+ "version": "0.7.224",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",
@@ -24,6 +24,10 @@ def truthy(value):
24
24
  return str(value or '').strip().lower() in ('1', 'true', 'yes', 'y', 'on')
25
25
 
26
26
 
27
+ def explicitly_false(value):
28
+ return value is False or str(value or '').strip().lower() in ('false', '0', 'no', 'off')
29
+
30
+
27
31
  def parse_json_arg(key, expected, default):
28
32
  raw = s.get(key)
29
33
  if raw in (None, ''):
@@ -70,6 +74,12 @@ reference_note = ''
70
74
  verification_mode = (s.get('verification_mode') or 'proof').strip() or 'proof'
71
75
  s['verification_mode'] = verification_mode
72
76
  s['success_criteria'] = (s.get('success_criteria') or '').strip()
77
+ implementation_mode = (s.get('implementation_mode') or '').strip().lower()
78
+ s['implementation_mode'] = implementation_mode or s.get('implementation_mode') or ''
79
+ if explicitly_false(s.get('require_diff')):
80
+ s['require_diff'] = False
81
+ if explicitly_false(s.get('allow_code_changes')):
82
+ s['allow_code_changes'] = False
73
83
  raw_assertions = (s.get('assertions_json') or '').strip()
74
84
  allow_static_preview_fallback = str(s.get('allow_static_preview_fallback') or '').strip().lower() in ('1', 'true', 'yes', 'y', 'on')
75
85
  s['allow_static_preview_fallback'] = allow_static_preview_fallback
@@ -101,6 +111,28 @@ s['reference_resolution'] = {
101
111
  'prod_reference_skipped': False,
102
112
  'prod_reference_skip_reason': '',
103
113
  }
114
+ no_implementation_mode = (
115
+ implementation_mode in ('none', 'audit', 'no_implementation', 'no-implementation')
116
+ or explicitly_false(s.get('require_diff'))
117
+ or explicitly_false(s.get('allow_code_changes'))
118
+ )
119
+ remote_audit = (not (s.get('repo') or '').strip()) and prod_url_present and no_implementation_mode
120
+ if remote_audit:
121
+ s['remote_audit'] = True
122
+ s['implementation_mode'] = s.get('implementation_mode') or 'none'
123
+ s['require_diff'] = False
124
+ s['allow_code_changes'] = False
125
+ if reference != 'prod':
126
+ s['reference_input_ignored'] = reference
127
+ reference = 'prod'
128
+ s['reference'] = reference
129
+ s['reference_resolution'].update({
130
+ 'effective_reference': reference,
131
+ 'prod_reference_requested': True,
132
+ 'prod_reference_skipped': False,
133
+ 'prod_reference_skip_reason': '',
134
+ 'note': 'repo not provided for audit/no-diff prod_url run; using reference=prod and skipping repo worktrees.',
135
+ })
104
136
 
105
137
  # Infer a reasonable commit title during setup instead of forcing the caller to
106
138
  # fill boilerplate that can be derived from the requested change.
@@ -159,9 +191,11 @@ if s['target_branch'].startswith('riddle-proof/'):
159
191
 
160
192
  # Validate required fields (common)
161
193
  missing = []
162
- for k in ('repo', 'change_request', 'commit_message'):
194
+ for k in ('change_request', 'commit_message'):
163
195
  if not s.get(k):
164
196
  missing.append(k)
197
+ if not s.get('repo') and not remote_audit:
198
+ missing.append('repo')
165
199
 
166
200
  # prod_url required only once prod is actively part of the comparison
167
201
  if reference in ('prod', 'both') and not s.get('prod_url', '').strip():
@@ -175,6 +209,8 @@ if mode == 'server':
175
209
 
176
210
  # Derived fields
177
211
  repo_short = s['repo'].split('/')[-1] if s.get('repo') else ''
212
+ if repo_short and (repo_short == s.get('repo') or s.get('repo', '').startswith(('/', './', '../', '~'))):
213
+ repo_short = os.path.basename(os.path.abspath(os.path.expanduser(s.get('repo', ''))))
178
214
  s['repo_short'] = repo_short
179
215
  base_branch = (s.get('base_branch') or 'main').strip() or 'main'
180
216
  s['base_branch'] = base_branch
@@ -46,6 +46,23 @@ s = load_state()
46
46
  after_dir = (s.get('after_worktree') or '').strip()
47
47
  before_dir = (s.get('before_worktree') or '').strip()
48
48
  if not after_dir or not os.path.exists(after_dir):
49
+ if s.get('remote_audit'):
50
+ s['workspace_ready'] = True
51
+ s['recon_status'] = 'ready_for_proof_plan'
52
+ s['recon_summary'] = s.get('recon_summary') or 'Remote audit/no-diff run skips repo-backed recon.'
53
+ s['recon_results'] = s.get('recon_results') or {
54
+ 'status': 'ready_for_proof_plan',
55
+ 'mode': 'remote_audit',
56
+ 'hypothesis': s.get('recon_hypothesis') or {},
57
+ 'baselines': {},
58
+ 'attempt_history': [],
59
+ 'route_hints': [],
60
+ 'keyword_hits': [],
61
+ 'max_attempts': 0,
62
+ }
63
+ save_state(s)
64
+ print('Remote audit/no-diff recon: repo worktree not required.')
65
+ sys.exit(0)
49
66
  raise SystemExit('after_worktree not found. Run setup first.')
50
67
 
51
68
 
@@ -7,14 +7,15 @@ scratch storage by default:
7
7
  """
8
8
 
9
9
  import json, subprocess as sp, os, sys, shutil, time, tempfile
10
+ from urllib.parse import urlparse
10
11
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
11
12
  from util import load_state, save_state, git, shell_quote
12
13
  from util import apply_capture_hint
13
14
 
14
15
  s = load_state()
15
- repo = s['repo']
16
+ repo = (s.get('repo') or '').strip()
16
17
  branch = (s.get('target_branch') or s['branch']).strip()
17
- repo_dir = s['repo_dir']
18
+ repo_dir = s.get('repo_dir', '')
18
19
  base_branch = s.get('base_branch', 'main')
19
20
  before_ref_arg = (s.get('before_ref') or s.get('base_ref') or '').strip()
20
21
  mode = s.get('mode', 'server')
@@ -199,6 +200,36 @@ def env_int(name, default):
199
200
  return value if value > 0 else default
200
201
 
201
202
 
203
+ def explicitly_false(value):
204
+ return value is False or str(value or '').strip().lower() in ('false', '0', 'no', 'off')
205
+
206
+
207
+ def audit_no_diff_mode():
208
+ implementation_mode = str(s.get('implementation_mode') or '').strip().lower()
209
+ return (
210
+ implementation_mode in ('none', 'audit', 'no_implementation', 'no-implementation')
211
+ or explicitly_false(s.get('require_diff'))
212
+ or explicitly_false(s.get('allow_code_changes'))
213
+ )
214
+
215
+
216
+ def remote_audit_mode():
217
+ return bool(s.get('remote_audit')) or (
218
+ not repo
219
+ and bool((s.get('prod_url') or '').strip())
220
+ and audit_no_diff_mode()
221
+ )
222
+
223
+
224
+ def remote_audit_target_path():
225
+ explicit = (s.get('server_path') or '').strip()
226
+ if explicit:
227
+ return explicit
228
+ parsed = urlparse((s.get('prod_url') or '').strip())
229
+ path = parsed.path or '/'
230
+ return path + (('?' + parsed.query) if parsed.query else '')
231
+
232
+
202
233
  def disk_free_bytes(path):
203
234
  probe = path
204
235
  while probe and not os.path.exists(probe):
@@ -489,6 +520,61 @@ def apply_repo_profile(project_dir):
489
520
  }
490
521
  print('Applied Riddle Proof repo profile: ' + ', '.join(s['proof_profile']['applied_fields']))
491
522
 
523
+ if remote_audit_mode():
524
+ target_path = remote_audit_target_path()
525
+ s['remote_audit'] = True
526
+ s['workspace_kind'] = 'remote_audit'
527
+ s['repo_dir'] = ''
528
+ s['worktree_root'] = ''
529
+ s['scratch_root'] = DEFAULT_SCRATCH_ROOT
530
+ s['before_worktree'] = ''
531
+ s['after_worktree'] = ''
532
+ s['after_worktree_branch'] = ''
533
+ s['before_ref'] = ''
534
+ s['before_ref_source'] = ''
535
+ s['workspace_ready'] = True
536
+ s['stage'] = 'author'
537
+ s['implementation_status'] = 'not_required'
538
+ s['implementation_mode'] = s.get('implementation_mode') or 'none'
539
+ s['require_diff'] = False
540
+ s['allow_code_changes'] = False
541
+ s['server_path'] = s.get('server_path') or target_path
542
+ s['server_path_source'] = s.get('server_path_source') or 'prod_url'
543
+ s['recon_status'] = 'ready_for_proof_plan'
544
+ s['recon_summary'] = 'Remote audit/no-diff run uses prod_url as the current target and skips repo worktrees.'
545
+ s['recon_hypothesis'] = {
546
+ 'target_path': target_path,
547
+ 'path_source': 'prod_url',
548
+ 'reference': s.get('reference') or 'prod',
549
+ 'mode': s.get('mode') or 'server',
550
+ 'wait_for_selector': (s.get('wait_for_selector') or '').strip(),
551
+ 'notes': ['Remote audit/no-diff setup skipped repository checkout and dependency staging.'],
552
+ }
553
+ s['recon_results'] = {
554
+ 'status': 'ready_for_proof_plan',
555
+ 'mode': 'remote_audit',
556
+ 'hypothesis': s['recon_hypothesis'],
557
+ 'baselines': {},
558
+ 'attempt_history': [],
559
+ 'route_hints': [],
560
+ 'keyword_hits': [],
561
+ 'max_attempts': 0,
562
+ }
563
+ s['author_status'] = 'ready'
564
+ s['proof_plan_status'] = 'ready'
565
+ s['proof_plan'] = (s.get('proof_plan') or 'Audit the current prod_url target and capture current evidence without requiring a repo diff.').strip()
566
+ s['dependency_install'] = {
567
+ 'shared': 'skipped:remote_audit',
568
+ 'before': 'skipped:remote_audit',
569
+ 'after': 'skipped:remote_audit',
570
+ }
571
+ s['scratch_disk_after_setup'] = disk_snapshot(DEFAULT_SCRATCH_ROOT)
572
+ save_state(s)
573
+ print('Remote audit/no-diff setup: repo worktrees and dependency staging skipped.')
574
+ print('Current target: ' + (s.get('prod_url') or ''))
575
+ print(json.dumps({'ok': True, 'remote_audit': True}))
576
+ sys.exit(0)
577
+
492
578
  # Ensure the repo is cloned and up to date via the shared workspace core.
493
579
  setup = workspace_core('prepare-repo', {
494
580
  'repo': repo,
@@ -561,7 +647,7 @@ if reference in ('before', 'both'):
561
647
  'ref': before_ref,
562
648
  'detach': True,
563
649
  'cleanupPaths': worktree_cleanup_dirs,
564
- 'verifyPackageJson': True,
650
+ 'verifyPackageJson': False,
565
651
  }, timeout=300)
566
652
  print('Before worktree: ' + BEFORE_DIR + ' (' + before_ref + ', source=' + before_ref_source + ')')
567
653
 
@@ -587,7 +673,7 @@ workspace_core('ensure-worktree', {
587
673
  'resetBranch': True,
588
674
  'cleanupPaths': after_cleanup_dirs,
589
675
  'cleanupBranches': cleanup_branches,
590
- 'verifyPackageJson': True,
676
+ 'verifyPackageJson': False,
591
677
  }, timeout=300)
592
678
  print('After worktree: ' + AFTER_DIR + ' (' + AFTER_WORKTREE_BRANCH + ' -> ' + branch + ')')
593
679
  apply_repo_profile(AFTER_DIR)
@@ -597,20 +683,20 @@ target_dependency_dirs = [AFTER_DIR]
597
683
  if reference in ('before', 'both'):
598
684
  target_dependency_dirs.append(BEFORE_DIR)
599
685
 
600
- reuse_source = repo_dir if os.path.exists(os.path.join(repo_dir, 'package.json')) else ''
686
+ reuse_source = repo_dir if env_flag('RIDDLE_PROOF_USE_ACTIVE_WORKSPACE_DEPS', False) and os.path.exists(os.path.join(repo_dir, 'package.json')) else ''
601
687
  shared_reuse_source = compatible_reuse_source(reuse_source, target_dependency_dirs)
602
688
  shared_status = ''
603
689
  if shared_reuse_source:
604
690
  shared_status = ensure_deps_phase('shared_deps', shared_reuse_source, summary='Ensuring shared repository dependencies.')
605
691
  if shared_status:
606
692
  print('Shared deps status: ' + shared_status)
607
- elif reuse_source:
693
+ elif os.path.exists(os.path.join(repo_dir, 'package.json')):
608
694
  record_setup_phase(
609
695
  'shared_deps',
610
696
  'completed',
611
- 'skipped: active workspace dependencies differ from proof worktrees',
697
+ 'skipped: active workspace dependency reuse disabled; proof worktrees use scratch cache',
612
698
  )
613
- print('Shared deps skipped: active workspace dependencies differ from proof worktrees')
699
+ print('Shared deps skipped: active workspace dependency reuse disabled; proof worktrees use scratch cache')
614
700
 
615
701
  before_dep_status = ''
616
702
  if reference in ('before', 'both'):
@@ -2553,9 +2553,10 @@ if s.get('use_auth', '').lower() in ('true', '1', 'yes'):
2553
2553
 
2554
2554
  existing_before = (s.get('before_cdn') or '').strip()
2555
2555
  existing_prod = (s.get('prod_cdn') or '').strip()
2556
- if reference in ('before', 'both') and not existing_before:
2556
+ remote_audit = bool(s.get('remote_audit')) and no_implementation_mode
2557
+ if reference in ('before', 'both') and not existing_before and not remote_audit:
2557
2558
  raise SystemExit('Recon baseline missing: before_cdn is empty. Run recon again and confirm the before baseline succeeds before verify.')
2558
- if reference in ('prod', 'both') and prod_url and not existing_prod:
2559
+ if reference in ('prod', 'both') and prod_url and not existing_prod and not remote_audit:
2559
2560
  raise SystemExit('Recon baseline missing: prod_cdn is empty. Run recon again and confirm the prod baseline succeeds before verify.')
2560
2561
  if reference == 'prod' and not prod_url:
2561
2562
  raise SystemExit('reference is "prod" but no prod_url provided.')
@@ -2796,6 +2797,8 @@ if reference in ('before', 'both'):
2796
2797
  required_baseline_present = required_baseline_present and bool(existing_before)
2797
2798
  if reference in ('prod', 'both') and prod_url:
2798
2799
  required_baseline_present = required_baseline_present and bool(existing_prod)
2800
+ if remote_audit:
2801
+ required_baseline_present = True
2799
2802
 
2800
2803
  evidence_bundle = build_evidence_bundle(s, results, after_payload, after_observation, required_baseline_present, expected_path)
2801
2804
  s['evidence_bundle'] = evidence_bundle
@@ -46,6 +46,15 @@ args:
46
46
  mode:
47
47
  default: "server"
48
48
  description: "static or server"
49
+ implementation_mode:
50
+ default: ""
51
+ description: "change or none/audit"
52
+ require_diff:
53
+ default: ""
54
+ description: "Set false for audit/no-diff proof runs"
55
+ allow_code_changes:
56
+ default: ""
57
+ description: "Set false for audit/no-diff proof runs"
49
58
  build_command:
50
59
  default: "npm run build"
51
60
  build_output:
@@ -110,6 +119,9 @@ steps:
110
119
  'base_branch': """${base_branch}""".strip() or 'main',
111
120
  'before_ref': """${before_ref}""".strip(),
112
121
  'allow_static_preview_fallback': """${allow_static_preview_fallback}""".strip(),
122
+ 'implementation_mode': """${implementation_mode}""".strip(),
123
+ 'require_diff': """${require_diff}""".strip(),
124
+ 'allow_code_changes': """${allow_code_changes}""".strip(),
113
125
  'build_command': """${build_command}""".strip() or 'npm run build',
114
126
  'build_output': """${build_output}""".strip() or 'build',
115
127
  'server_image': """${server_image}""".strip() or 'node:20-slim',
@@ -14,6 +14,7 @@ ROOT = Path(__file__).resolve().parents[1]
14
14
  LIB = ROOT / 'lib'
15
15
  UTIL_PATH = LIB / 'util.py'
16
16
  PREFLIGHT_PATH = LIB / 'preflight.py'
17
+ SETUP_PATH = LIB / 'setup.py'
17
18
  RECON_PATH = LIB / 'recon.py'
18
19
  VERIFY_PATH = LIB / 'verify.py'
19
20
  AUTHOR_PATH = LIB / 'author.py'
@@ -992,6 +993,54 @@ def run_preflight_records_prod_reference_skip_reason():
992
993
  shutil.rmtree(tempdir, ignore_errors=True)
993
994
 
994
995
 
996
+ def run_remote_audit_setup_without_repo():
997
+ tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-remote-audit-'))
998
+ args_path = tempdir / 'args.json'
999
+ state_path = tempdir / 'state.json'
1000
+ try:
1001
+ args_path.write_text(json.dumps({
1002
+ 'repo': '',
1003
+ 'mode': 'server',
1004
+ 'reference': 'both',
1005
+ 'prod_url': 'https://prod.example.com/pricing?plan=pro',
1006
+ 'change_request': 'Audit the current pricing page without a repo checkout.',
1007
+ 'commit_message': '',
1008
+ 'success_criteria': 'Current target is captured.',
1009
+ 'verification_mode': 'visual',
1010
+ 'implementation_mode': 'none',
1011
+ 'require_diff': False,
1012
+ 'allow_code_changes': False,
1013
+ 'server_image': 'node:20-slim',
1014
+ 'server_command': 'npm start',
1015
+ 'server_port': '3000',
1016
+ }, indent=2))
1017
+ with temporary_env(
1018
+ RIDDLE_PROOF_ARGS_FILE=str(args_path),
1019
+ RIDDLE_PROOF_STATE_FILE=str(state_path),
1020
+ ):
1021
+ sys.modules.pop('util', None)
1022
+ load_module('util_remote_audit_preflight', UTIL_PATH)
1023
+ load_module('preflight_remote_audit', PREFLIGHT_PATH)
1024
+ sys.modules.pop('util', None)
1025
+ try:
1026
+ load_module('setup_remote_audit', SETUP_PATH)
1027
+ except SystemExit as exc:
1028
+ assert exc.code in (0, None), exc
1029
+ state = json.loads(state_path.read_text())
1030
+ assert state['remote_audit'] is True
1031
+ assert state['workspace_ready'] is True
1032
+ assert state['reference'] == 'prod'
1033
+ assert state['implementation_status'] == 'not_required'
1034
+ assert state['dependency_install']['after'] == 'skipped:remote_audit'
1035
+ assert state['server_path'] == '/pricing?plan=pro'
1036
+ assert state['recon_status'] == 'ready_for_proof_plan'
1037
+ assert state['proof_plan_status'] == 'ready'
1038
+ return {'ok': True, 'server_path': state['server_path']}
1039
+ finally:
1040
+ sys.modules.pop('util', None)
1041
+ shutil.rmtree(tempdir, ignore_errors=True)
1042
+
1043
+
995
1044
  def run_preflight_resumes_visual_proof_session():
996
1045
  tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-preflight-session-'))
997
1046
  args_path = tempdir / 'args.json'
@@ -2388,6 +2437,7 @@ def run_ship_resolves_real_pr_branch():
2388
2437
  if __name__ == '__main__':
2389
2438
  payload = {
2390
2439
  'preflight_reference_skip_reason': run_preflight_records_prod_reference_skip_reason(),
2440
+ 'remote_audit_setup_without_repo': run_remote_audit_setup_without_repo(),
2391
2441
  'preflight_resume_visual_proof_session': run_preflight_resumes_visual_proof_session(),
2392
2442
  'capture_artifact_enrichment': run_capture_artifact_enrichment(),
2393
2443
  'capture_diagnostics_redaction': run_capture_diagnostics_redact_sensitive_values(),