@riddledc/riddle-proof 0.5.22 → 0.5.24

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.
@@ -89,8 +89,20 @@ function loadRunState(input) {
89
89
  state_path: statePath
90
90
  });
91
91
  }
92
+ function isProtectedFinalStatus(status) {
93
+ return status === "ready_to_ship" || status === "shipped" || status === "completed";
94
+ }
95
+ function shouldPreserveFinalizedRunState(filePath, incoming) {
96
+ const existing = readJson(filePath);
97
+ if (!existing?.finalized || !isProtectedFinalStatus(existing.status)) return false;
98
+ if (!incoming.finalized) return true;
99
+ if (existing.status === incoming.status) return false;
100
+ return !(existing.status === "ready_to_ship" && incoming.status === "shipped");
101
+ }
92
102
  function persist(state) {
93
- if (state.state_path) writeJson(state.state_path, state);
103
+ if (!state.state_path) return;
104
+ if (shouldPreserveFinalizedRunState(state.state_path, state)) return;
105
+ writeJson(state.state_path, state);
94
106
  }
95
107
  function recordEvent(state, event) {
96
108
  appendRunEvent(state, event);
@@ -124,6 +136,16 @@ function fullRiddleState(result, state) {
124
136
  function workdirFromState(state) {
125
137
  return nonEmptyString(state?.after_worktree) || nonEmptyString(state?.worktree_path) || null;
126
138
  }
139
+ function parseGitStatusPaths(status) {
140
+ return status.split(/\r?\n/).map((line) => line.trimEnd()).filter(Boolean).map((line) => {
141
+ let item = line.length > 3 ? line.slice(3) : line;
142
+ if (item.includes(" -> ")) item = item.split(" -> ").pop() || item;
143
+ return item.trim();
144
+ }).filter(Boolean);
145
+ }
146
+ function isToolNoisePath(filePath) {
147
+ return filePath === ".codex" || filePath.startsWith(".codex/") || filePath === ".oc-smoke" || filePath.startsWith(".oc-smoke/");
148
+ }
127
149
  function hasGitDiff(workdir) {
128
150
  if (!workdir || !existsSync(workdir)) return false;
129
151
  try {
@@ -132,7 +154,7 @@ function hasGitDiff(workdir) {
132
154
  encoding: "utf-8",
133
155
  timeout: 1e4
134
156
  });
135
- return status.trim().length > 0;
157
+ return parseGitStatusPaths(status).some((filePath) => !isToolNoisePath(filePath));
136
158
  } catch {
137
159
  return false;
138
160
  }
@@ -315,6 +337,7 @@ function engineFailureBlocker(result, checkpoint) {
315
337
  }
316
338
  function terminalResult(state, status, result, summary, raw = {}) {
317
339
  setRunStatus(state, status);
340
+ if (isProtectedFinalStatus(status)) state.finalized = true;
318
341
  const metadata = normalizeTerminalMetadata({
319
342
  riddleState: result ? fullRiddleState(result, state) : null,
320
343
  engineResult: result
@@ -3091,8 +3091,20 @@ function loadRunState(input) {
3091
3091
  state_path: statePath
3092
3092
  });
3093
3093
  }
3094
+ function isProtectedFinalStatus(status) {
3095
+ return status === "ready_to_ship" || status === "shipped" || status === "completed";
3096
+ }
3097
+ function shouldPreserveFinalizedRunState(filePath, incoming) {
3098
+ const existing = readJson(filePath);
3099
+ if (!existing?.finalized || !isProtectedFinalStatus(existing.status)) return false;
3100
+ if (!incoming.finalized) return true;
3101
+ if (existing.status === incoming.status) return false;
3102
+ return !(existing.status === "ready_to_ship" && incoming.status === "shipped");
3103
+ }
3094
3104
  function persist(state) {
3095
- if (state.state_path) writeJson(state.state_path, state);
3105
+ if (!state.state_path) return;
3106
+ if (shouldPreserveFinalizedRunState(state.state_path, state)) return;
3107
+ writeJson(state.state_path, state);
3096
3108
  }
3097
3109
  function recordEvent(state, event) {
3098
3110
  appendRunEvent(state, event);
@@ -3126,6 +3138,16 @@ function fullRiddleState(result, state) {
3126
3138
  function workdirFromState(state) {
3127
3139
  return nonEmptyString(state?.after_worktree) || nonEmptyString(state?.worktree_path) || null;
3128
3140
  }
3141
+ function parseGitStatusPaths(status) {
3142
+ return status.split(/\r?\n/).map((line) => line.trimEnd()).filter(Boolean).map((line) => {
3143
+ let item = line.length > 3 ? line.slice(3) : line;
3144
+ if (item.includes(" -> ")) item = item.split(" -> ").pop() || item;
3145
+ return item.trim();
3146
+ }).filter(Boolean);
3147
+ }
3148
+ function isToolNoisePath(filePath) {
3149
+ return filePath === ".codex" || filePath.startsWith(".codex/") || filePath === ".oc-smoke" || filePath.startsWith(".oc-smoke/");
3150
+ }
3129
3151
  function hasGitDiff(workdir) {
3130
3152
  if (!workdir || !(0, import_node_fs3.existsSync)(workdir)) return false;
3131
3153
  try {
@@ -3134,7 +3156,7 @@ function hasGitDiff(workdir) {
3134
3156
  encoding: "utf-8",
3135
3157
  timeout: 1e4
3136
3158
  });
3137
- return status.trim().length > 0;
3159
+ return parseGitStatusPaths(status).some((filePath) => !isToolNoisePath(filePath));
3138
3160
  } catch {
3139
3161
  return false;
3140
3162
  }
@@ -3317,6 +3339,7 @@ function engineFailureBlocker(result, checkpoint) {
3317
3339
  }
3318
3340
  function terminalResult(state, status, result, summary, raw = {}) {
3319
3341
  setRunStatus(state, status);
3342
+ if (isProtectedFinalStatus(status)) state.finalized = true;
3320
3343
  const metadata = normalizeTerminalMetadata({
3321
3344
  riddleState: result ? fullRiddleState(result, state) : null,
3322
3345
  engineResult: result
@@ -2,7 +2,7 @@ import {
2
2
  createDisabledRiddleProofAgentAdapter,
3
3
  readRiddleProofRunStatus,
4
4
  runRiddleProofEngineHarness
5
- } from "./chunk-PJ3RTT5D.js";
5
+ } from "./chunk-46355FJD.js";
6
6
  import "./chunk-7ZJAUEUN.js";
7
7
  import "./chunk-TMMKRKY5.js";
8
8
  export {
package/dist/index.cjs CHANGED
@@ -3647,8 +3647,20 @@ function loadRunState(input) {
3647
3647
  state_path: statePath
3648
3648
  });
3649
3649
  }
3650
+ function isProtectedFinalStatus(status) {
3651
+ return status === "ready_to_ship" || status === "shipped" || status === "completed";
3652
+ }
3653
+ function shouldPreserveFinalizedRunState(filePath, incoming) {
3654
+ const existing = readJson(filePath);
3655
+ if (!existing?.finalized || !isProtectedFinalStatus(existing.status)) return false;
3656
+ if (!incoming.finalized) return true;
3657
+ if (existing.status === incoming.status) return false;
3658
+ return !(existing.status === "ready_to_ship" && incoming.status === "shipped");
3659
+ }
3650
3660
  function persist(state) {
3651
- if (state.state_path) writeJson(state.state_path, state);
3661
+ if (!state.state_path) return;
3662
+ if (shouldPreserveFinalizedRunState(state.state_path, state)) return;
3663
+ writeJson(state.state_path, state);
3652
3664
  }
3653
3665
  function recordEvent(state, event) {
3654
3666
  appendRunEvent(state, event);
@@ -3682,6 +3694,16 @@ function fullRiddleState(result, state) {
3682
3694
  function workdirFromState(state) {
3683
3695
  return nonEmptyString(state?.after_worktree) || nonEmptyString(state?.worktree_path) || null;
3684
3696
  }
3697
+ function parseGitStatusPaths(status) {
3698
+ return status.split(/\r?\n/).map((line) => line.trimEnd()).filter(Boolean).map((line) => {
3699
+ let item = line.length > 3 ? line.slice(3) : line;
3700
+ if (item.includes(" -> ")) item = item.split(" -> ").pop() || item;
3701
+ return item.trim();
3702
+ }).filter(Boolean);
3703
+ }
3704
+ function isToolNoisePath(filePath) {
3705
+ return filePath === ".codex" || filePath.startsWith(".codex/") || filePath === ".oc-smoke" || filePath.startsWith(".oc-smoke/");
3706
+ }
3685
3707
  function hasGitDiff(workdir) {
3686
3708
  if (!workdir || !(0, import_node_fs3.existsSync)(workdir)) return false;
3687
3709
  try {
@@ -3690,7 +3712,7 @@ function hasGitDiff(workdir) {
3690
3712
  encoding: "utf-8",
3691
3713
  timeout: 1e4
3692
3714
  });
3693
- return status.trim().length > 0;
3715
+ return parseGitStatusPaths(status).some((filePath) => !isToolNoisePath(filePath));
3694
3716
  } catch {
3695
3717
  return false;
3696
3718
  }
@@ -3873,6 +3895,7 @@ function engineFailureBlocker(result, checkpoint) {
3873
3895
  }
3874
3896
  function terminalResult(state, status, result, summary, raw = {}) {
3875
3897
  setRunStatus(state, status);
3898
+ if (isProtectedFinalStatus(status)) state.finalized = true;
3876
3899
  const metadata = normalizeTerminalMetadata({
3877
3900
  riddleState: result ? fullRiddleState(result, state) : null,
3878
3901
  engineResult: result
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  createDisabledRiddleProofAgentAdapter,
14
14
  readRiddleProofRunStatus,
15
15
  runRiddleProofEngineHarness
16
- } from "./chunk-PJ3RTT5D.js";
16
+ } from "./chunk-46355FJD.js";
17
17
  import {
18
18
  runRiddleProof
19
19
  } from "./chunk-F6VFKS7K.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.5.22",
3
+ "version": "0.5.24",
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,8 @@ import json, os, sys
9
9
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
10
10
  from util import load_state, save_state, git, shell_quote
11
11
 
12
+ TOOL_NOISE_PATHS = ('.codex', '.oc-smoke')
13
+
12
14
 
13
15
  def unique_nonempty(items):
14
16
  seen = set()
@@ -37,6 +39,15 @@ def parse_status_paths(lines):
37
39
  return unique_nonempty(paths)
38
40
 
39
41
 
42
+ def is_tool_noise_path(path):
43
+ text = str(path or '').strip()
44
+ return any(text == prefix or text.startswith(prefix + '/') for prefix in TOOL_NOISE_PATHS)
45
+
46
+
47
+ def material_paths(paths):
48
+ return [path for path in unique_nonempty(paths) if not is_tool_noise_path(path)]
49
+
50
+
40
51
  s = load_state()
41
52
  after_dir = (s.get('after_worktree') or '').strip()
42
53
  if not after_dir or not os.path.exists(after_dir):
@@ -45,7 +56,9 @@ if not after_dir or not os.path.exists(after_dir):
45
56
  base_branch = s.get('base_branch', 'main')
46
57
  base_ref = (s.get('before_ref') or '').strip() or ('origin/' + base_branch)
47
58
  dirty = [ln for ln in git('git status --short', after_dir).stdout.splitlines() if ln.strip()]
48
- dirty_paths = parse_status_paths(dirty)
59
+ dirty_paths_all = parse_status_paths(dirty)
60
+ dirty_paths = material_paths(dirty_paths_all)
61
+ ignored_dirty_paths = [path for path in dirty_paths_all if is_tool_noise_path(path)]
49
62
 
50
63
  diff_probes = []
51
64
 
@@ -70,7 +83,9 @@ if diff_result.returncode != 0:
70
83
  if diff_result.returncode != 0 and not committed:
71
84
  fallback, committed = run_diff_probe('head_parent', 'git diff --name-only HEAD~1 HEAD')
72
85
 
73
- changed = unique_nonempty(dirty_paths + committed)
86
+ committed_material = material_paths(committed)
87
+ ignored_committed_paths = [path for path in committed if is_tool_noise_path(path)]
88
+ changed = unique_nonempty(dirty_paths + committed_material)
74
89
  authored = bool((s.get('capture_script') or '').strip()) and bool((s.get('proof_plan') or '').strip())
75
90
 
76
91
  detection = {
@@ -81,9 +96,13 @@ detection = {
81
96
  'base_ref_requested': base_ref,
82
97
  'dirty_status_lines': dirty[:20],
83
98
  'dirty_paths': dirty_paths[:20],
99
+ 'ignored_dirty_paths': ignored_dirty_paths[:20],
84
100
  'dirty_path_count': len(dirty_paths),
85
- 'committed_paths': committed[:20],
86
- 'committed_path_count': len(committed),
101
+ 'dirty_path_count_including_noise': len(dirty_paths_all),
102
+ 'committed_paths': committed_material[:20],
103
+ 'ignored_committed_paths': ignored_committed_paths[:20],
104
+ 'committed_path_count': len(committed_material),
105
+ 'committed_path_count_including_noise': len(committed),
87
106
  'changed_paths': changed[:20],
88
107
  'changed_path_count': len(changed),
89
108
  'diff_probes': diff_probes[:6],
@@ -637,6 +637,7 @@ def run_implement_records_detection_when_changes_missing():
637
637
  })
638
638
  write_state(state_path, state)
639
639
  os.environ['RIDDLE_PROOF_STATE_FILE'] = str(state_path)
640
+ load_module('util', UTIL_PATH)
640
641
 
641
642
  try:
642
643
  load_module('implement_changes_missing_state', IMPLEMENT_PATH)
@@ -673,6 +674,56 @@ def run_implement_records_detection_when_changes_missing():
673
674
  shutil.rmtree(tempdir, ignore_errors=True)
674
675
 
675
676
 
677
+ def run_implement_ignores_tool_noise_when_detecting_changes():
678
+ tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-implement-noise-'))
679
+ state_path = tempdir / 'state.json'
680
+ previous_state_file = os.environ.get('RIDDLE_PROOF_STATE_FILE')
681
+ try:
682
+ state = base_state(tempdir, reference='before')
683
+ after_dir = Path(state['after_worktree'])
684
+ init_git_repo(after_dir)
685
+ codex_dir = after_dir / '.codex'
686
+ codex_dir.mkdir(parents=True, exist_ok=True)
687
+ (codex_dir / 'session.json').write_text('{}\n')
688
+ state.update({
689
+ 'recon_status': 'ready_for_proof_plan',
690
+ 'author_status': 'ready',
691
+ 'proof_plan_status': 'ready',
692
+ 'proof_plan': 'Capture the pricing CTA after the implementation is applied.',
693
+ 'capture_script': "await page.waitForSelector('[data-testid=pricing-cta]'); await saveScreenshot('after-proof');",
694
+ })
695
+ write_state(state_path, state)
696
+ os.environ['RIDDLE_PROOF_STATE_FILE'] = str(state_path)
697
+ load_module('util', UTIL_PATH)
698
+
699
+ try:
700
+ load_module('implement_ignores_tool_noise', IMPLEMENT_PATH)
701
+ except SystemExit as exc:
702
+ assert 'No implementation detected on the after worktree.' in str(exc), exc
703
+ else:
704
+ raise AssertionError('implement stage should have halted when only tool noise changed')
705
+
706
+ after_state = json.loads(state_path.read_text())
707
+ detection = after_state['implementation_detection']
708
+ assert detection['outcome'] == 'no_changes_detected'
709
+ assert detection['diff_detected'] is False
710
+ assert detection['dirty_path_count'] == 0
711
+ assert detection['dirty_path_count_including_noise'] >= 1
712
+ assert any(str(path).startswith('.codex') for path in detection['ignored_dirty_paths'])
713
+ assert detection['changed_path_count'] == 0
714
+ assert after_state['changed_files'] == []
715
+ return {
716
+ 'ok': True,
717
+ 'ignored_dirty_paths': detection['ignored_dirty_paths'],
718
+ }
719
+ finally:
720
+ if previous_state_file is None:
721
+ os.environ.pop('RIDDLE_PROOF_STATE_FILE', None)
722
+ else:
723
+ os.environ['RIDDLE_PROOF_STATE_FILE'] = previous_state_file
724
+ shutil.rmtree(tempdir, ignore_errors=True)
725
+
726
+
676
727
  def run_recon_then_author_request():
677
728
  tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-supervisor-request-'))
678
729
  state_path = tempdir / 'state.json'
@@ -1481,6 +1532,7 @@ if __name__ == '__main__':
1481
1532
  'apply_auth_context': run_apply_auth_context_passes_supported_auth_payloads(),
1482
1533
  'run_project_build_retries_after_clean_failure': run_project_build_retries_after_clean_failure(),
1483
1534
  'implement_records_detection_when_changes_missing': run_implement_records_detection_when_changes_missing(),
1535
+ 'implement_ignores_tool_noise_when_detecting_changes': run_implement_ignores_tool_noise_when_detecting_changes(),
1484
1536
  'verify_quality_ignores_proof_telemetry_console_text': run_verify_quality_ignores_proof_telemetry_console_text(),
1485
1537
  'recon_then_author_request': run_recon_then_author_request(),
1486
1538
  'recon_preserves_query_route': run_recon_preserves_query_route(),