@cyanautomation/kaseki-agent 1.64.1 → 1.64.2

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 (2) hide show
  1. package/kaseki-agent.sh +587 -498
  2. package/package.json +1 -1
package/kaseki-agent.sh CHANGED
@@ -3,6 +3,61 @@
3
3
  # record status/timing artifacts before deciding whether to stop.
4
4
  set -uo pipefail
5
5
 
6
+ # Early exit for helper resolution check - must happen before variable initialization
7
+ if [ "${KASEKI_AGENT_HELPER_RESOLUTION_CHECK:-0}" = "1" ]; then
8
+ # Define helper resolution function locally
9
+ resolve_allowlist_helper() {
10
+ local script_dir="$1"
11
+ local script_relative_helper="$script_dir/scripts/allowlist-helper.sh"
12
+ local fallback_helper="${KASEKI_ALLOWLIST_HELPER_FALLBACK:-/app/scripts/allowlist-helper.sh}"
13
+
14
+ if [ -r "$script_relative_helper" ]; then
15
+ printf '%s\n' "$script_relative_helper"
16
+ return 0
17
+ fi
18
+
19
+ if [ -r "$fallback_helper" ]; then
20
+ printf '%s\n' "$fallback_helper"
21
+ return 0
22
+ fi
23
+
24
+ printf 'ERROR: Allowlist helper is not readable. Expected packaged helper at %s or fallback helper at %s. This worker image or mounted template is incomplete; rebuild the image or restore scripts/allowlist-helper.sh.\n' \
25
+ "$script_relative_helper" \
26
+ "$fallback_helper" >&2
27
+ return 66
28
+ }
29
+
30
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
31
+ ALLOWLIST_HELPER="$(resolve_allowlist_helper "$SCRIPT_DIR")"
32
+ allowlist_helper_status=$?
33
+ if [ "$allowlist_helper_status" -ne 0 ]; then
34
+ exit "$allowlist_helper_status"
35
+ fi
36
+
37
+ # Source the helper
38
+ # shellcheck source=/dev/null
39
+ . "$ALLOWLIST_HELPER" || {
40
+ printf 'ERROR: Failed to source %s (exit code: %d)\n' "$ALLOWLIST_HELPER" $? >&2
41
+ exit 1
42
+ }
43
+
44
+ # Verify the helper was sourced successfully
45
+ if ! declare -f build_allowlist_regex >/dev/null 2>&1; then
46
+ printf 'ERROR: build_allowlist_regex function not found after sourcing %s\n' "$ALLOWLIST_HELPER" >&2
47
+ exit 1
48
+ fi
49
+
50
+ # Call the function to verify it works
51
+ build_allowlist_regex "${KASEKI_CHANGED_FILES_ALLOWLIST:-}" >/dev/null 2>&1 || {
52
+ printf 'ERROR: build_allowlist_regex exited with status %d\n' $? >&2
53
+ exit 1
54
+ }
55
+
56
+ # Output and exit
57
+ printf 'allowlist_helper=%s\n' "$ALLOWLIST_HELPER"
58
+ exit 0
59
+ fi
60
+
6
61
  INSTANCE_NAME="${KASEKI_INSTANCE:-kaseki-unknown}"
7
62
  REPO_URL="${REPO_URL:-https://github.com/CyanAutomation/crudmapper}"
8
63
  GIT_REF="${GIT_REF:-main}"
@@ -19,12 +74,21 @@ KASEKI_AUTO_LINT_CLEANUP_COMMANDS="${KASEKI_AUTO_LINT_CLEANUP_COMMANDS-npm run l
19
74
  KASEKI_SKIP_MISSING_NPM_SCRIPTS="${KASEKI_SKIP_MISSING_NPM_SCRIPTS:-1}"
20
75
  KASEKI_DEBUG_RAW_EVENTS="${KASEKI_DEBUG_RAW_EVENTS:-0}"
21
76
  KASEKI_STREAM_PROGRESS="${KASEKI_STREAM_PROGRESS:-1}"
22
- KASEKI_RESULTS_DIR="${KASEKI_RESULTS_DIR:-/results}"
77
+ KASEKI_RESULTS_DIR="${KASEKI_RESULTS_DIR:-${KASEKI_RESULTS_DIR}}"
78
+ export KASEKI_RESULTS_DIR
79
+ KASEKI_WORKSPACE_DIR="${KASEKI_WORKSPACE_DIR:-/workspace}"
80
+ export KASEKI_WORKSPACE_DIR
81
+ KASEKI_WORKSPACE_BASELINE_DIR="${KASEKI_WORKSPACE_BASELINE_DIR:-${KASEKI_WORKSPACE_BASELINE_DIR}}"
82
+ export KASEKI_WORKSPACE_BASELINE_DIR
83
+ KASEKI_APP_LIB_DIR="${KASEKI_APP_LIB_DIR:-/app/lib}"
84
+ export KASEKI_APP_LIB_DIR
85
+ KASEKI_CACHE_DIR="${KASEKI_CACHE_DIR:-/cache}"
86
+ export KASEKI_CACHE_DIR
23
87
  KASEKI_VALIDATE_AFTER_AGENT_FAILURE="${KASEKI_VALIDATE_AFTER_AGENT_FAILURE:-0}"
24
88
  KASEKI_PRE_AGENT_VALIDATION="${KASEKI_PRE_AGENT_VALIDATION:-1}"
25
89
  KASEKI_PRE_AGENT_VALIDATION_COMMANDS="${KASEKI_PRE_AGENT_VALIDATION_COMMANDS-$KASEKI_VALIDATION_COMMANDS}"
26
90
  KASEKI_BASELINE_VALIDATION_ENABLED="${KASEKI_BASELINE_VALIDATION_ENABLED:-1}"
27
- KASEKI_BASELINE_CACHE_ROOT="${KASEKI_BASELINE_CACHE_ROOT:-/cache/kaseki-baseline}"
91
+ KASEKI_BASELINE_CACHE_ROOT="${KASEKI_BASELINE_CACHE_ROOT:-${KASEKI_CACHE_DIR}/kaseki-baseline}"
28
92
  KASEKI_BASELINE_CACHE_MAX_AGE_HOURS="${KASEKI_BASELINE_CACHE_MAX_AGE_HOURS:-24}"
29
93
  KASEKI_BASELINE_CACHE_DISABLED="${KASEKI_BASELINE_CACHE_DISABLED:-0}"
30
94
  KASEKI_TS_PRE_CHECK="${KASEKI_TS_PRE_CHECK:-1}"
@@ -58,7 +122,7 @@ KASEKI_MAX_DIFF_BYTES="${KASEKI_MAX_DIFF_BYTES:-400000}"
58
122
  KASEKI_REPO_MEMORY_MODE="${KASEKI_REPO_MEMORY_MODE:-off}"
59
123
  KASEKI_REPO_MEMORY_TTL_DAYS="${KASEKI_REPO_MEMORY_TTL_DAYS:-30}"
60
124
  KASEKI_REPO_MEMORY_MAX_BYTES="${KASEKI_REPO_MEMORY_MAX_BYTES:-8000}"
61
- KASEKI_REPO_MEMORY_ROOT="${KASEKI_REPO_MEMORY_ROOT:-/cache/repo-memory}"
125
+ KASEKI_REPO_MEMORY_ROOT="${KASEKI_REPO_MEMORY_ROOT:-${KASEKI_CACHE_DIR}/repo-memory}"
62
126
  TASK_PROMPT="${TASK_PROMPT:-Make normalizeRole treat a non-string Name fallback safely when FriendlyName is empty or missing. It should fall back to \"Unnamed Role\" instead of preserving arbitrary truthy non-string values. Add or update exactly one compact table-driven Vitest case in tests/parser.validation.ts, with a neutral static test title and no per-case assertion messages or explanatory comments. Do not add broad repeated test blocks. Do not print, inspect, or expose environment variables, secrets, credentials, or API keys. Keep changes limited to the source and test files needed for this fix.}"
63
127
  KASEKI_AGENT_GUARDRAILS="${KASEKI_AGENT_GUARDRAILS:-1}"
64
128
  KASEKI_RESTORE_DISALLOWED_CHANGES="${KASEKI_RESTORE_DISALLOWED_CHANGES:-1}"
@@ -134,13 +198,13 @@ TS_PRE_CHECK_DURATION_SECONDS=0
134
198
  TS_PRE_CHECK_TIMESTAMP=""
135
199
  FILTER_STDERR_TAIL=""
136
200
  FILTER_STDERR_FILE="/tmp/kaseki-filter-stderr.log"
137
- VALIDATION_RAW_LOG="/results/validation-raw.log"
138
- PRE_VALIDATION_RAW_LOG="/results/pre-validation-raw.log"
139
- AUTO_LINT_CLEANUP_LOG="/results/auto-lint-cleanup.log"
140
- AUTO_LINT_CLEANUP_TIMINGS_FILE="/results/auto-lint-cleanup-timings.tsv"
141
- FILTER_DIAGNOSTICS_LOG="/results/filter-diagnostics.log"
142
- VALIDATION_ENV_LOG="/results/validation-env.log"
143
- PRE_VALIDATION_ENV_LOG="/results/pre-validation-env.log"
201
+ VALIDATION_RAW_LOG="${KASEKI_RESULTS_DIR}/validation-raw.log"
202
+ PRE_VALIDATION_RAW_LOG="${KASEKI_RESULTS_DIR}/pre-validation-raw.log"
203
+ AUTO_LINT_CLEANUP_LOG="${KASEKI_RESULTS_DIR}/auto-lint-cleanup.log"
204
+ AUTO_LINT_CLEANUP_TIMINGS_FILE="${KASEKI_RESULTS_DIR}/auto-lint-cleanup-timings.tsv"
205
+ FILTER_DIAGNOSTICS_LOG="${KASEKI_RESULTS_DIR}/filter-diagnostics.log"
206
+ VALIDATION_ENV_LOG="${KASEKI_RESULTS_DIR}/validation-env.log"
207
+ PRE_VALIDATION_ENV_LOG="${KASEKI_RESULTS_DIR}/pre-validation-env.log"
144
208
  DIFF_NONEMPTY=false
145
209
  QUALITY_EXIT=0
146
210
  QUALITY_FAILURE_REASON=""
@@ -154,25 +218,25 @@ GITHUB_OPERATION_PHASE=""
154
218
  ACTUAL_MODEL="unknown"
155
219
  GITHUB_PR_URL=""
156
220
  GITHUB_SKIP_REASONS=()
157
- VALIDATION_TIMINGS_FILE="/results/validation-timings.tsv"
158
- PRE_VALIDATION_TIMINGS_FILE="/results/pre-validation-timings.tsv"
159
- STAGE_TIMINGS_FILE="/results/stage-timings.tsv"
160
- DEPENDENCY_CACHE_LOG="/results/dependency-cache.log"
221
+ VALIDATION_TIMINGS_FILE="${KASEKI_RESULTS_DIR}/validation-timings.tsv"
222
+ PRE_VALIDATION_TIMINGS_FILE="${KASEKI_RESULTS_DIR}/pre-validation-timings.tsv"
223
+ STAGE_TIMINGS_FILE="${KASEKI_RESULTS_DIR}/stage-timings.tsv"
224
+ DEPENDENCY_CACHE_LOG="${KASEKI_RESULTS_DIR}/dependency-cache.log"
161
225
  RAW_EVENTS="/tmp/pi-events.raw.jsonl"
162
226
  SCOUTING_RAW_EVENTS="/tmp/pi-scouting-events.raw.jsonl"
163
227
  GOAL_SETTING_RAW_EVENTS="/tmp/pi-goal-setting-events.raw.jsonl"
164
228
  GOAL_CHECK_RAW_EVENTS="/tmp/pi-goal-check-events.raw.jsonl"
165
229
  RUN_EVALUATION_RAW_EVENTS="/tmp/pi-run-evaluation-events.raw.jsonl"
166
- SCOUTING_ARTIFACT="/results/scouting.json"
167
- SCOUTING_CANDIDATE_ARTIFACT="/results/scouting-candidate.json"
168
- GOAL_SETTING_ARTIFACT="/results/goal-setting.json"
169
- GOAL_SETTING_CANDIDATE_ARTIFACT="/results/goal-setting-candidate.json"
170
- GOAL_CHECK_CANDIDATE_ARTIFACT="/results/goal-check-candidate.json"
171
- RUN_EVALUATION_ARTIFACT="/results/run-evaluation.json"
172
- RUN_EVALUATION_CANDIDATE_ARTIFACT="/results/run-evaluation-candidate.json"
173
- TEST_IMPACT_WARNINGS_ARTIFACT="/results/test-impact-warnings.log"
174
- EXPECTATION_MISMATCH_WARNINGS_ARTIFACT="/results/expectation-mismatch-warnings.jsonl"
175
- KASEKI_DEPENDENCY_CACHE_DIR="${KASEKI_DEPENDENCY_CACHE_DIR:-/workspace/.kaseki-cache}"
230
+ SCOUTING_ARTIFACT="${KASEKI_RESULTS_DIR}/scouting.json"
231
+ SCOUTING_CANDIDATE_ARTIFACT="${KASEKI_RESULTS_DIR}/scouting-candidate.json"
232
+ GOAL_SETTING_ARTIFACT="${KASEKI_RESULTS_DIR}/goal-setting.json"
233
+ GOAL_SETTING_CANDIDATE_ARTIFACT="${KASEKI_RESULTS_DIR}/goal-setting-candidate.json"
234
+ GOAL_CHECK_CANDIDATE_ARTIFACT="${KASEKI_RESULTS_DIR}/goal-check-candidate.json"
235
+ RUN_EVALUATION_ARTIFACT="${KASEKI_RESULTS_DIR}/run-evaluation.json"
236
+ RUN_EVALUATION_CANDIDATE_ARTIFACT="${KASEKI_RESULTS_DIR}/run-evaluation-candidate.json"
237
+ TEST_IMPACT_WARNINGS_ARTIFACT="${KASEKI_RESULTS_DIR}/test-impact-warnings.log"
238
+ EXPECTATION_MISMATCH_WARNINGS_ARTIFACT="${KASEKI_RESULTS_DIR}/expectation-mismatch-warnings.jsonl"
239
+ KASEKI_DEPENDENCY_CACHE_DIR="${KASEKI_DEPENDENCY_CACHE_DIR:-${KASEKI_WORKSPACE_DIR}/.kaseki-cache}"
176
240
  KASEKI_DEPENDENCY_RESTORE_MODE="${KASEKI_DEPENDENCY_RESTORE_MODE:-auto}"
177
241
  KASEKI_DEPENDENCY_CACHE_MAX_BYTES="${KASEKI_DEPENDENCY_CACHE_MAX_BYTES:-5368709120}"
178
242
  KASEKI_DEPENDENCY_CACHE_MAX_AGE_DAYS="${KASEKI_DEPENDENCY_CACHE_MAX_AGE_DAYS:-30}"
@@ -184,7 +248,7 @@ KASEKI_IMAGE_DEPENDENCY_CACHE_DIR="${KASEKI_IMAGE_DEPENDENCY_CACHE_DIR:-/opt/kas
184
248
  KASEKI_LOG_DIR="${KASEKI_LOG_DIR:-/var/log/kaseki}"
185
249
  KASEKI_STRICT_HOST_LOGGING="${KASEKI_STRICT_HOST_LOGGING:-0}"
186
250
  KASEKI_GIT_CACHE_MODE="${KASEKI_GIT_CACHE_MODE:-mirror}"
187
- KASEKI_GIT_CACHE_ROOT="${KASEKI_GIT_CACHE_ROOT:-/cache/git}"
251
+ KASEKI_GIT_CACHE_ROOT="${KASEKI_GIT_CACHE_ROOT:-${KASEKI_CACHE_DIR}/git}"
188
252
  KASEKI_GIT_CACHE_FETCH_TIMEOUT_SECONDS="${KASEKI_GIT_CACHE_FETCH_TIMEOUT_SECONDS:-120}"
189
253
  GIT_CACHE_KEY=""
190
254
  GIT_CACHE_MIRROR=""
@@ -201,12 +265,12 @@ REPO_MEMORY_COMMIT_SHA="unknown"
201
265
 
202
266
  # Track last executed command for better error reporting
203
267
  LAST_COMMAND=""
204
- LAST_COMMAND_LOG="/results/last-command.log"
268
+ LAST_COMMAND_LOG="${KASEKI_RESULTS_DIR}/last-command.log"
205
269
 
206
270
  # Signal handler for graceful termination
207
271
  handle_termination() {
208
272
  local signal="$1"
209
- printf '\nReceived %s; terminating kaseki-agent...\n' "$signal" | tee -a /results/progress.log
273
+ printf '\nReceived %s; terminating kaseki-agent...\n' "$signal" | tee -a ${KASEKI_RESULTS_DIR}/progress.log
210
274
  # Exit with standard code for signal (128 + signal_number)
211
275
  # SIGINT = 130, SIGTERM = 143
212
276
  if [ "$signal" = "SIGINT" ]; then
@@ -243,8 +307,8 @@ setup_host_logging_mirror() {
243
307
  if mkdir -p "$KASEKI_LOG_DIR" 2>/dev/null && [ -w "$KASEKI_LOG_DIR" ]; then
244
308
  stamp="$(date -u +%Y%m%dT%H%M%SZ)"
245
309
  host_log_file="$KASEKI_LOG_DIR/${base_name}-${stamp}.log"
246
- exec > >(tee -a /results/stdout.log | tee -a "$host_log_file") \
247
- 2> >(tee -a /results/stderr.log | tee -a "$host_log_file" >&2)
310
+ exec > >(tee -a ${KASEKI_RESULTS_DIR}/stdout.log | tee -a "$host_log_file") \
311
+ 2> >(tee -a ${KASEKI_RESULTS_DIR}/stderr.log | tee -a "$host_log_file" >&2)
248
312
  printf 'Host log mirror: %s\n' "$host_log_file"
249
313
  return 0
250
314
  fi
@@ -252,11 +316,11 @@ setup_host_logging_mirror() {
252
316
  printf 'Error: strict host logging enabled, but KASEKI_LOG_DIR is not writable: %s\n' "$KASEKI_LOG_DIR" >&2
253
317
  exit 1
254
318
  fi
255
- exec > >(tee -a /results/stdout.log) 2> >(tee -a /results/stderr.log >&2)
319
+ exec > >(tee -a ${KASEKI_RESULTS_DIR}/stdout.log) 2> >(tee -a ${KASEKI_RESULTS_DIR}/stderr.log >&2)
256
320
  printf 'Warning: host log mirror disabled; KASEKI_LOG_DIR is unavailable: %s (set writable KASEKI_LOG_DIR to enable mirror, or set KASEKI_STRICT_HOST_LOGGING=1 to fail fast)\n' "$KASEKI_LOG_DIR" >&2
257
321
  }
258
322
 
259
- mkdir_paths=(/results)
323
+ mkdir_paths=("${KASEKI_RESULTS_DIR}")
260
324
  if [ -n "${HOME:-}" ]; then
261
325
  mkdir_paths+=("${HOME}")
262
326
  fi
@@ -273,41 +337,41 @@ if ! mkdir -p "${mkdir_paths[@]}"; then
273
337
  printf 'Error: Failed to create required runtime directories.\n' >&2
274
338
  exit 1
275
339
  fi
276
- : > /results/stdout.log
277
- : > /results/stderr.log
278
- : > /results/pi-events.jsonl
279
- : > /results/pi-summary.json
280
- : > /results/scouting-events.jsonl
281
- : > /results/scouting-summary.json
282
- : > /results/scouting-validation-errors.jsonl
283
- : > /results/scouting-validation-summary.txt
284
- : > /results/goal-check-events.jsonl
285
- : > /results/goal-check-summary.json
286
- : > /results/goal-check-stderr.log
287
- : > /results/goal-check-validation-errors.jsonl
288
- : > /results/goal-check-validation-summary.txt
289
- : > /results/goal-check-attempts.jsonl
290
- : > /results/goal-check.json
291
- : > /results/run-evaluation-events.jsonl
292
- : > /results/run-evaluation-summary.json
293
- : > /results/run-evaluation-stderr.log
294
- : > /results/run-evaluation.json
340
+ : > ${KASEKI_RESULTS_DIR}/stdout.log
341
+ : > ${KASEKI_RESULTS_DIR}/stderr.log
342
+ : > ${KASEKI_RESULTS_DIR}/pi-events.jsonl
343
+ : > ${KASEKI_RESULTS_DIR}/pi-summary.json
344
+ : > ${KASEKI_RESULTS_DIR}/scouting-events.jsonl
345
+ : > ${KASEKI_RESULTS_DIR}/scouting-summary.json
346
+ : > ${KASEKI_RESULTS_DIR}/scouting-validation-errors.jsonl
347
+ : > ${KASEKI_RESULTS_DIR}/scouting-validation-summary.txt
348
+ : > ${KASEKI_RESULTS_DIR}/goal-check-events.jsonl
349
+ : > ${KASEKI_RESULTS_DIR}/goal-check-summary.json
350
+ : > ${KASEKI_RESULTS_DIR}/goal-check-stderr.log
351
+ : > ${KASEKI_RESULTS_DIR}/goal-check-validation-errors.jsonl
352
+ : > ${KASEKI_RESULTS_DIR}/goal-check-validation-summary.txt
353
+ : > ${KASEKI_RESULTS_DIR}/goal-check-attempts.jsonl
354
+ : > ${KASEKI_RESULTS_DIR}/goal-check.json
355
+ : > ${KASEKI_RESULTS_DIR}/run-evaluation-events.jsonl
356
+ : > ${KASEKI_RESULTS_DIR}/run-evaluation-summary.json
357
+ : > ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log
358
+ : > ${KASEKI_RESULTS_DIR}/run-evaluation.json
295
359
  : > "$TEST_IMPACT_WARNINGS_ARTIFACT"
296
360
  : > "$EXPECTATION_MISMATCH_WARNINGS_ARTIFACT"
297
- : > /results/validation.log
298
- : > /results/pre-validation.log
361
+ : > ${KASEKI_RESULTS_DIR}/validation.log
362
+ : > ${KASEKI_RESULTS_DIR}/pre-validation.log
299
363
  : > "$PRE_VALIDATION_RAW_LOG"
300
364
  : > "$PRE_VALIDATION_ENV_LOG"
301
365
  : > "$AUTO_LINT_CLEANUP_LOG"
302
366
  : > "$AUTO_LINT_CLEANUP_TIMINGS_FILE"
303
- : > /results/quality.log
304
- : > /results/secret-scan.log
305
- : > /results/git-push.log
306
- : > /results/progress.log
307
- : > /results/progress.jsonl
308
- : > /results/format-check-command.txt
309
- : > /results/failure.json
310
- : > /results/result-summary.md
367
+ : > ${KASEKI_RESULTS_DIR}/quality.log
368
+ : > ${KASEKI_RESULTS_DIR}/secret-scan.log
369
+ : > ${KASEKI_RESULTS_DIR}/git-push.log
370
+ : > ${KASEKI_RESULTS_DIR}/progress.log
371
+ : > ${KASEKI_RESULTS_DIR}/progress.jsonl
372
+ : > ${KASEKI_RESULTS_DIR}/format-check-command.txt
373
+ : > ${KASEKI_RESULTS_DIR}/failure.json
374
+ : > ${KASEKI_RESULTS_DIR}/result-summary.md
311
375
  : > "$VALIDATION_TIMINGS_FILE"
312
376
  : > "$PRE_VALIDATION_TIMINGS_FILE"
313
377
  : >> "$STAGE_TIMINGS_FILE"
@@ -474,9 +538,9 @@ validate_scouting_artifact_with_node() {
474
538
  "$candidate_artifact" \
475
539
  "$final_artifact" \
476
540
  "$validation_error_file" \
477
- "/results/scouting-validation-errors.jsonl" \
541
+ "${KASEKI_RESULTS_DIR}/scouting-validation-errors.jsonl" \
478
542
  >/dev/null \
479
- 2>> /results/scouting-stderr.log
543
+ 2>> ${KASEKI_RESULTS_DIR}/scouting-stderr.log
480
544
  }
481
545
 
482
546
  # Validate scouting artifact and emit structured reason code
@@ -490,7 +554,7 @@ validate_scouting_artifact() {
490
554
 
491
555
  : > "$validation_error_file"
492
556
  if [ ! -f "$candidate_artifact" ]; then
493
- if [ -f /results/filesystem-readonly-reason.txt ]; then
557
+ if [ -f ${KASEKI_RESULTS_DIR}/filesystem-readonly-reason.txt ]; then
494
558
  reason_code="readonly_filesystem"
495
559
  reason_details="1 critical scouting validation error: scouting-candidate.json missing due to read-only filesystem"
496
560
  else
@@ -498,15 +562,15 @@ validate_scouting_artifact() {
498
562
  reason_details="1 critical scouting validation error: scouting-candidate.json"
499
563
  fi
500
564
  # shellcheck disable=SC2016
501
- node -e 'const fs=require("node:fs"); const candidate=process.argv[1]; const reason=process.argv[2]; const error={timestamp:new Date().toISOString(),reason_code:reason,field:"scouting-candidate.json",expected:"file at /results/scouting-candidate.json",actual:`missing: ${candidate}`,severity:"critical",suggestion:reason==="readonly_filesystem" ? "remount /results as read-write (docker run -v /path:/results:rw)" : "ensure the scouting Pi writes exactly one valid JSON object to /results/scouting-candidate.json"}; fs.appendFileSync("/results/scouting-validation-errors.jsonl", JSON.stringify(error)+"\n");' "$candidate_artifact" "$reason_code" 2>> /results/scouting-stderr.log || true
565
+ node -e 'const fs=require("node:fs"); const candidate=process.argv[1]; const reason=process.argv[2]; const error={timestamp:new Date().toISOString(),reason_code:reason,field:"scouting-candidate.json",expected:"file at " + process.env.KASEKI_RESULTS_DIR + "/scouting-candidate.json",actual:`missing: ${candidate}`,severity:"critical",suggestion:reason==="readonly_filesystem" ? "remount " + process.env.KASEKI_RESULTS_DIR + " as read-write (docker run -v /path:" + process.env.KASEKI_RESULTS_DIR + ":rw)" : "ensure the scouting Pi writes exactly one valid JSON object to " + process.env.KASEKI_RESULTS_DIR + "/scouting-candidate.json"}; fs.appendFileSync(process.env.KASEKI_RESULTS_DIR + "/scouting-validation-errors.jsonl", JSON.stringify(error)+"\n");' "$candidate_artifact" "$reason_code" 2>> "${KASEKI_RESULTS_DIR}/scouting-stderr.log" || true
502
566
  elif ! validate_scouting_artifact_with_node "$candidate_artifact" "$final_artifact" "$validation_error_file"; then
503
567
  reason_code="$(node -e 'try{const v=JSON.parse(require("node:fs").readFileSync(process.argv[1],"utf8")); process.stdout.write(String(v.reason_code||"schema_mismatch"));}catch{process.stdout.write("schema_mismatch");}' "$validation_error_file" 2>/dev/null || printf 'schema_mismatch')"
504
568
  reason_details="$(node -e 'try{const v=JSON.parse(require("node:fs").readFileSync(process.argv[1],"utf8")); process.stdout.write(String(v.details||"scouting artifact validation failed"));}catch{process.stdout.write("scouting artifact validation failed");}' "$validation_error_file" 2>/dev/null || printf 'scouting artifact validation failed')"
505
569
  fi
506
570
 
507
571
  printf '%s\n' "$reason_code" > "$reason_file"
508
- printf '%s\n' "$reason_details" > /results/scouting-validation-summary.txt
509
- printf '[scouting-validation] reason=%s details=%s\n' "$reason_code" "$reason_details" | tee -a /results/scouting-stderr.log
572
+ printf '%s\n' "$reason_details" > ${KASEKI_RESULTS_DIR}/scouting-validation-summary.txt
573
+ printf '[scouting-validation] reason=%s details=%s\n' "$reason_code" "$reason_details" | tee -a ${KASEKI_RESULTS_DIR}/scouting-stderr.log
510
574
  rm -f "$validation_error_file" 2>/dev/null || true
511
575
  [ "$reason_code" = "valid" ]
512
576
  }
@@ -521,12 +585,14 @@ validate_goal_check_artifact_with_node() {
521
585
  # shellcheck disable=SC2016
522
586
  node -e '
523
587
  const fs = require("node:fs");
588
+ const path = require("node:path");
524
589
  const input = process.argv[1];
525
590
  const output = process.argv[2];
526
591
  const attempt = Number(process.argv[3]);
527
592
  const errorLog = process.argv[4];
528
- const jsonlLog = "/results/goal-check-validation-errors.jsonl";
529
- const attemptsLog = "/results/goal-check-attempts.jsonl";
593
+ const resultsDir = process.env.KASEKI_RESULTS_DIR || "/results";
594
+ const jsonlLog = path.join(resultsDir, "goal-check-validation-errors.jsonl");
595
+ const attemptsLog = path.join(resultsDir, "goal-check-attempts.jsonl");
530
596
 
531
597
  function actualType(value) {
532
598
  if (value === null) return "null";
@@ -535,11 +601,16 @@ function actualType(value) {
535
601
  }
536
602
 
537
603
  function appendValidationFailure(error) {
538
- fs.appendFileSync(jsonlLog, JSON.stringify({
539
- timestamp: new Date().toISOString(),
540
- attempt,
541
- ...error,
542
- }) + "\n");
604
+ try {
605
+ fs.mkdirSync(path.dirname(jsonlLog), { recursive: true });
606
+ fs.appendFileSync(jsonlLog, JSON.stringify({
607
+ timestamp: new Date().toISOString(),
608
+ attempt,
609
+ ...error,
610
+ }) + "\n");
611
+ } catch (e) {
612
+ // Non-blocking log failure
613
+ }
543
614
  }
544
615
 
545
616
  function summarize(errors) {
@@ -572,7 +643,7 @@ try {
572
643
  expected: "valid JSON object",
573
644
  actual: error && error.message ? String(error.message) : String(error),
574
645
  severity: "critical",
575
- suggestion: "ensure exactly one valid JSON object is written to /results/goal-check-candidate.json",
646
+ suggestion: "ensure exactly one valid JSON object is written to ${KASEKI_RESULTS_DIR}/goal-check-candidate.json",
576
647
  }]);
577
648
  }
578
649
 
@@ -625,8 +696,13 @@ if (errors.length) {
625
696
  artifact.attempt = attempt;
626
697
  artifact.timestamp = new Date().toISOString();
627
698
  fs.writeFileSync(output, JSON.stringify(artifact, null, 2) + "\n");
628
- fs.appendFileSync(attemptsLog, JSON.stringify(artifact) + "\n");
629
- ' "$candidate_artifact" "$final_artifact" "$attempt" "$validation_error_file" 2>> /results/goal-check-stderr.log
699
+ try {
700
+ fs.mkdirSync(path.dirname(attemptsLog), { recursive: true });
701
+ fs.appendFileSync(attemptsLog, JSON.stringify(artifact) + "\n");
702
+ } catch (e) {
703
+ // Non-blocking log failure
704
+ }
705
+ ' "$candidate_artifact" "$final_artifact" "$attempt" "$validation_error_file" 2>> "${KASEKI_RESULTS_DIR}/goal-check-stderr.log"
630
706
  }
631
707
 
632
708
  validate_goal_check_artifact() {
@@ -634,7 +710,7 @@ validate_goal_check_artifact() {
634
710
  local final_artifact="$2"
635
711
  local attempt="$3"
636
712
  local reason_file="$4"
637
- local summary_file="${5:-/results/goal-check-validation-summary.txt}"
713
+ local summary_file="${5:-${KASEKI_RESULTS_DIR}/goal-check-validation-summary.txt}"
638
714
  local validation_error_file="/tmp/goal-check-validation-errors.json"
639
715
  local reason_code="valid"
640
716
  local reason_details="artifact validation passed"
@@ -644,7 +720,7 @@ validate_goal_check_artifact() {
644
720
  reason_code="missing_file"
645
721
  reason_details="1 critical goal-check validation error: goal-check-candidate.json"
646
722
  # shellcheck disable=SC2016
647
- node -e 'const fs=require("node:fs"); const candidate=process.argv[1]; const attempt=Number(process.argv[2]); const error={timestamp:new Date().toISOString(),attempt,field:"goal-check-candidate.json",expected:"file at /results/goal-check-candidate.json",actual:`missing: ${candidate}`,severity:"critical",suggestion:"ensure the goal-check Pi writes exactly one valid JSON object to /results/goal-check-candidate.json before exiting successfully"}; fs.appendFileSync("/results/goal-check-validation-errors.jsonl", JSON.stringify(error)+"\n");' "$candidate_artifact" "$attempt" 2>> /results/goal-check-stderr.log || true
723
+ node -e 'const fs=require("node:fs"); const candidate=process.argv[1]; const attempt=Number(process.argv[2]); const error={timestamp:new Date().toISOString(),attempt,field:"goal-check-candidate.json",expected:"file at ${KASEKI_RESULTS_DIR}/goal-check-candidate.json",actual:`missing: ${candidate}`,severity:"critical",suggestion:"ensure the goal-check Pi writes exactly one valid JSON object to ${KASEKI_RESULTS_DIR}/goal-check-candidate.json before exiting successfully"}; fs.appendFileSync(process.env.KASEKI_RESULTS_DIR + "/goal-check-validation-errors.jsonl", JSON.stringify(error)+"\n");' "$candidate_artifact" "$attempt" 2>> ${KASEKI_RESULTS_DIR}/goal-check-stderr.log || true
648
724
  elif ! validate_goal_check_artifact_with_node "$candidate_artifact" "$final_artifact" "$attempt" "$validation_error_file"; then
649
725
  reason_code="$(node -e 'try{const v=JSON.parse(require("node:fs").readFileSync(process.argv[1],"utf8")); const hint=String(v.reason_hint||""); process.stdout.write(hint === "malformed_json" ? "malformed_json" : "schema_mismatch");}catch{process.stdout.write("schema_mismatch");}' "$validation_error_file" 2>/dev/null || printf 'schema_mismatch')"
650
726
  reason_details="$(node -e 'try{const v=JSON.parse(require("node:fs").readFileSync(process.argv[1],"utf8")); process.stdout.write(String(v.details||"goal-check artifact validation failed"));}catch{process.stdout.write("goal-check artifact validation failed");}' "$validation_error_file" 2>/dev/null || printf 'goal-check artifact validation failed')"
@@ -652,7 +728,7 @@ validate_goal_check_artifact() {
652
728
 
653
729
  printf '%s\n' "$reason_code" > "$reason_file"
654
730
  printf '%s\n' "$reason_details" > "$summary_file"
655
- printf '[goal-check-validation] reason=%s details=%s\n' "$reason_code" "$reason_details" | tee -a /results/goal-check-stderr.log
731
+ printf '[goal-check-validation] reason=%s details=%s\n' "$reason_code" "$reason_details" | tee -a ${KASEKI_RESULTS_DIR}/goal-check-stderr.log
656
732
  rm -f "$validation_error_file" 2>/dev/null || true
657
733
  [ "$reason_code" = "valid" ]
658
734
  }
@@ -661,20 +737,20 @@ emit_progress() {
661
737
  local stage="$1"
662
738
  local detail="$2"
663
739
  local status="${3:-info}"
664
- append_jsonl_object /results/progress.jsonl \
740
+ append_jsonl_object ${KASEKI_RESULTS_DIR}/progress.jsonl \
665
741
  "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
666
742
  "component=kaseki-agent" \
667
743
  "stage=$stage" \
668
744
  "status=$status" \
669
745
  "instance=$INSTANCE_NAME" \
670
746
  "detail=$detail"
671
- printf '[progress] %s %s: %s\n' "$stage" "$status" "$detail" | tee -a /results/progress.log
747
+ printf '[progress] %s %s: %s\n' "$stage" "$status" "$detail" | tee -a ${KASEKI_RESULTS_DIR}/progress.log
672
748
  }
673
749
 
674
750
  emit_event() {
675
751
  local event_type="$1"
676
752
  shift
677
- append_jsonl_object /results/progress.jsonl \
753
+ append_jsonl_object ${KASEKI_RESULTS_DIR}/progress.jsonl \
678
754
  "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
679
755
  "component=kaseki-agent" \
680
756
  "event_type=$event_type" \
@@ -687,7 +763,7 @@ emit_error_event() {
687
763
  local detail="$2"
688
764
  local recovery="${3:-continue}"
689
765
  emit_event "error" "error_type=$error_type" "detail=$detail" "recovery_action=$recovery"
690
- printf '[error] %s: %s (recovery: %s)\n' "$error_type" "$detail" "$recovery" | tee -a /results/progress.log
766
+ printf '[error] %s: %s (recovery: %s)\n' "$error_type" "$detail" "$recovery" | tee -a ${KASEKI_RESULTS_DIR}/progress.log
691
767
  }
692
768
 
693
769
  write_metadata() {
@@ -705,7 +781,7 @@ write_metadata() {
705
781
  stages_json="[\"unknown\"]"
706
782
  fi
707
783
 
708
- cat > /results/metadata.json <<META
784
+ cat > ${KASEKI_RESULTS_DIR}/metadata.json <<META
709
785
  {
710
786
  "instance": $(printf '%s' "$INSTANCE_NAME" | json_encode),
711
787
  "repo_url": $(printf '%s' "$REPO_URL" | json_encode),
@@ -826,7 +902,7 @@ write_metadata() {
826
902
  "stages": $stages_json
827
903
  }
828
904
  META
829
- printf '%s\n' "$exit_code" > /results/exit_code
905
+ printf '%s\n' "$exit_code" > ${KASEKI_RESULTS_DIR}/exit_code
830
906
  }
831
907
 
832
908
  set_current_stage() {
@@ -881,7 +957,7 @@ build_stages_array() {
881
957
 
882
958
  write_result_summary() {
883
959
  local changed_files changed_files_markdown validation_status pr_status github_skip_reasons_summary goal_check_status
884
- changed_files="$(cat /results/changed-files.txt 2>/dev/null || true)"
960
+ changed_files="$(cat ${KASEKI_RESULTS_DIR}/changed-files.txt 2>/dev/null || true)"
885
961
  if [ -n "$changed_files" ]; then
886
962
  changed_files_markdown="$(printf '%s\n' "$changed_files" | sed 's/^/ - /')"
887
963
  else
@@ -934,7 +1010,7 @@ write_result_summary() {
934
1010
  goal_check_status="not reached"
935
1011
  fi
936
1012
 
937
- cat > /results/result-summary.md <<SUMMARY
1013
+ cat > ${KASEKI_RESULTS_DIR}/result-summary.md <<SUMMARY
938
1014
  # Kaseki Result: $INSTANCE_NAME
939
1015
 
940
1016
  - Status: $(if [ "$STATUS" -eq 0 ]; then printf 'passed'; else printf 'failed'; fi)
@@ -959,7 +1035,7 @@ $(if [ -n "$VALIDATION_ALLOWLIST_FAILURE_REASON" ]; then printf ' - Allowlist r
959
1035
  $(if [ "$KASEKI_VALIDATION_RUN_ALL_COMMANDS" -eq 1 ]; then printf -- '- **ℹ️ Validation mode: Comprehensive** (KASEKI_VALIDATION_RUN_ALL_COMMANDS=1) - all %d commands executed\n' "$(echo "$KASEKI_VALIDATION_COMMANDS" | tr ';' '\n' | grep -c .)"; elif [ "$VALIDATION_STOPPED_EARLY" = "true" ]; then printf -- '- **⚠️ Validation stopped early** (fail-fast mode): %s of %s commands ran\n' "$VALIDATION_COMMANDS_ATTEMPTED" "$(echo "$KASEKI_VALIDATION_COMMANDS" | tr ';' '\n' | grep -c .)"; fi)
960
1036
  - Test failure analysis: $TEST_FAILURE_CLASSIFICATION_STATUS
961
1037
  $(if [ "$TEST_FAILURE_CLASSIFICATION_STATUS" = "completed" ] && [ "$NEWLY_INTRODUCED_FAILURES_COUNT" -gt 0 ]; then printf ' - ⚠️ **Newly introduced failures: %d**\n' "$NEWLY_INTRODUCED_FAILURES_COUNT"; fi)
962
- $(if [ "$TEST_FAILURE_CLASSIFICATION_STATUS" = "completed" ] && [ -f /results/test-baseline-comparison.json ]; then printf ' - See test-baseline-comparison.json for full breakdown\n'; fi)
1038
+ $(if [ "$TEST_FAILURE_CLASSIFICATION_STATUS" = "completed" ] && [ -f ${KASEKI_RESULTS_DIR}/test-baseline-comparison.json ]; then printf ' - See test-baseline-comparison.json for full breakdown\n'; fi)
963
1039
  - Quality checks: $QUALITY_EXIT
964
1040
  - Secret scan: $SECRET_SCAN_EXIT
965
1041
  - GitHub PR: $pr_status
@@ -1001,12 +1077,12 @@ SUMMARY
1001
1077
  write_failure_json() {
1002
1078
  local exit_code="$1"
1003
1079
  local stderr_tail
1004
- stderr_tail="$(tail -20 /results/stderr.log 2>/dev/null || true)"
1080
+ stderr_tail="$(tail -20 ${KASEKI_RESULTS_DIR}/stderr.log 2>/dev/null || true)"
1005
1081
  if [ "$exit_code" -eq 0 ]; then
1006
- : > /results/failure.json
1082
+ : > ${KASEKI_RESULTS_DIR}/failure.json
1007
1083
  return 0
1008
1084
  fi
1009
- cat > /results/failure.json <<FAILURE
1085
+ cat > ${KASEKI_RESULTS_DIR}/failure.json <<FAILURE
1010
1086
  {
1011
1087
  "instance": $(printf '%s' "$INSTANCE_NAME" | json_encode),
1012
1088
  "exit_code": $exit_code,
@@ -1024,7 +1100,7 @@ write_failure_json() {
1024
1100
  "goal_check_met": $GOAL_CHECK_MET,
1025
1101
  "stage": $(printf '%s' "$CURRENT_STAGE" | json_encode),
1026
1102
  "stderr_tail": $(printf '%s' "$stderr_tail" | json_encode),
1027
- "artifacts_dir": "/results",
1103
+ "artifacts_dir": "${KASEKI_RESULTS_DIR}",
1028
1104
  "metadata": "metadata.json",
1029
1105
  "stderr": "stderr.log",
1030
1106
  "stdout": "stdout.log",
@@ -1036,28 +1112,28 @@ FAILURE
1036
1112
 
1037
1113
  collect_git_artifacts() {
1038
1114
  DIFF_NONEMPTY=false
1039
- if [ -d /workspace/repo/.git ]; then
1115
+ if [ -d ${KASEKI_WORKSPACE_DIR}/repo/.git ]; then
1040
1116
  while IFS= read -r untracked_file || [ -n "$untracked_file" ]; do
1041
1117
  [ -z "$untracked_file" ] && continue
1042
- git -C /workspace/repo add -N -- "$untracked_file" 2>/dev/null || true
1043
- done < <(git -C /workspace/repo ls-files --others --exclude-standard 2>/dev/null || true)
1044
- git -C /workspace/repo status --short > /results/git.status 2>/dev/null || true
1045
- git -C /workspace/repo diff -- . > /results/git.diff 2>/dev/null || true
1046
- git -C /workspace/repo diff --name-only -- . > /results/changed-files.txt 2>/dev/null || true
1047
- if [ -s /results/git.diff ]; then
1118
+ git -C ${KASEKI_WORKSPACE_DIR}/repo add -N -- "$untracked_file" 2>/dev/null || true
1119
+ done < <(git -C ${KASEKI_WORKSPACE_DIR}/repo ls-files --others --exclude-standard 2>/dev/null || true)
1120
+ git -C ${KASEKI_WORKSPACE_DIR}/repo status --short > ${KASEKI_RESULTS_DIR}/git.status 2>/dev/null || true
1121
+ git -C ${KASEKI_WORKSPACE_DIR}/repo diff -- . > ${KASEKI_RESULTS_DIR}/git.diff 2>/dev/null || true
1122
+ git -C ${KASEKI_WORKSPACE_DIR}/repo diff --name-only -- . > ${KASEKI_RESULTS_DIR}/changed-files.txt 2>/dev/null || true
1123
+ if [ -s ${KASEKI_RESULTS_DIR}/git.diff ]; then
1048
1124
  DIFF_NONEMPTY=true
1049
1125
  fi
1050
1126
  else
1051
- : > /results/git.status
1052
- : > /results/git.diff
1053
- : > /results/changed-files.txt
1127
+ : > ${KASEKI_RESULTS_DIR}/git.status
1128
+ : > ${KASEKI_RESULTS_DIR}/git.diff
1129
+ : > ${KASEKI_RESULTS_DIR}/changed-files.txt
1054
1130
  fi
1055
1131
  }
1056
1132
 
1057
1133
  run_static_test_impact_check() {
1058
- local changed_files_file="/results/changed-files.txt"
1059
- local diff_file="/results/git.diff"
1060
- local artifact="${TEST_IMPACT_WARNINGS_ARTIFACT:-/results/test-impact-warnings.log}"
1134
+ local changed_files_file="${KASEKI_RESULTS_DIR}/changed-files.txt"
1135
+ local diff_file="${KASEKI_RESULTS_DIR}/git.diff"
1136
+ local artifact="${TEST_IMPACT_WARNINGS_ARTIFACT:-${KASEKI_RESULTS_DIR}/test-impact-warnings.log}"
1061
1137
  local indicator_regex='(parse|parser|regex|regexp|stage|event|format|serialize|name)'
1062
1138
  local production_matches diff_matches warning_detail
1063
1139
 
@@ -1099,7 +1175,7 @@ run_static_test_impact_check() {
1099
1175
  "warning_type=test_impact_without_tests" \
1100
1176
  "artifact=$artifact" \
1101
1177
  "detail=$warning_detail"
1102
- printf '[warning] test-impact: %s (artifact: %s)\n' "$warning_detail" "$artifact" | tee -a /results/progress.log
1178
+ printf '[warning] test-impact: %s (artifact: %s)\n' "$warning_detail" "$artifact" | tee -a ${KASEKI_RESULTS_DIR}/progress.log
1103
1179
  return 0
1104
1180
  }
1105
1181
 
@@ -1112,21 +1188,21 @@ run_expectation_mismatch_detector() {
1112
1188
  fi
1113
1189
 
1114
1190
  : > "$EXPECTATION_MISMATCH_WARNINGS_ARTIFACT"
1115
- if [ ! -s /results/git.diff ]; then
1116
- printf '[expectation-mismatch] skipped: /results/git.diff is empty\n' >> /results/progress.log
1191
+ if [ ! -s ${KASEKI_RESULTS_DIR}/git.diff ]; then
1192
+ printf '[expectation-mismatch] skipped: ${KASEKI_RESULTS_DIR}/git.diff is empty\n' >> ${KASEKI_RESULTS_DIR}/progress.log
1117
1193
  return 0
1118
1194
  fi
1119
1195
  if [ ! -f "$detector_script" ]; then
1120
- printf '[expectation-mismatch] skipped: detector script not found (%s)\n' "$detector_script" | tee -a /results/progress.log
1196
+ printf '[expectation-mismatch] skipped: detector script not found (%s)\n' "$detector_script" | tee -a ${KASEKI_RESULTS_DIR}/progress.log
1121
1197
  return 0
1122
1198
  fi
1123
1199
 
1124
1200
  if ! node "$detector_script" \
1125
- --repo /workspace/repo \
1126
- --diff /results/git.diff \
1201
+ --repo ${KASEKI_WORKSPACE_DIR}/repo \
1202
+ --diff ${KASEKI_RESULTS_DIR}/git.diff \
1127
1203
  --output "$EXPECTATION_MISMATCH_WARNINGS_ARTIFACT" \
1128
- --progress /results/progress.log; then
1129
- printf '[expectation-mismatch] warning: detector failed; continuing to validation\n' | tee -a /results/progress.log
1204
+ --progress ${KASEKI_RESULTS_DIR}/progress.log; then
1205
+ printf '[expectation-mismatch] warning: detector failed; continuing to validation\n' | tee -a ${KASEKI_RESULTS_DIR}/progress.log
1130
1206
  fi
1131
1207
  }
1132
1208
 
@@ -1162,9 +1238,22 @@ if [ ! -r "$SCOUTING_ALLOWLIST_HELPER" ] && [ -r /app/scripts/scouting-allowlist
1162
1238
  SCOUTING_ALLOWLIST_HELPER="/app/scripts/scouting-allowlist.js"
1163
1239
  fi
1164
1240
  # shellcheck source=scripts/allowlist-helper.sh
1165
- . "$ALLOWLIST_HELPER"
1241
+ . "$ALLOWLIST_HELPER" || {
1242
+ printf 'ERROR: Failed to source %s (exit code: %d)\n' "$ALLOWLIST_HELPER" $? >&2
1243
+ exit 1
1244
+ }
1245
+
1246
+ # Verify the helper was sourced successfully by checking for the required function
1247
+ if ! declare -f build_allowlist_regex >/dev/null 2>&1; then
1248
+ printf 'ERROR: build_allowlist_regex function not found after sourcing %s\n' "$ALLOWLIST_HELPER" >&2
1249
+ exit 1
1250
+ fi
1251
+
1166
1252
  if [ "${KASEKI_AGENT_HELPER_RESOLUTION_CHECK:-0}" = "1" ]; then
1167
- build_allowlist_regex "${KASEKI_CHANGED_FILES_ALLOWLIST:-}" >/dev/null
1253
+ build_allowlist_regex "${KASEKI_CHANGED_FILES_ALLOWLIST:-}" >/dev/null 2>&1 || {
1254
+ printf 'ERROR: build_allowlist_regex exited with status %d\n' $? >&2
1255
+ exit 1
1256
+ }
1168
1257
  printf 'allowlist_helper=%s\n' "$ALLOWLIST_HELPER"
1169
1258
  exit 0
1170
1259
  fi
@@ -1224,7 +1313,7 @@ run_scouting_allowlist_coverage() {
1224
1313
  local scouting_artifact agent_patterns validation_patterns
1225
1314
  scouting_artifact="${1:?missing scouting artifact path}"
1226
1315
 
1227
- if [ ! -f "$scouting_artifact" ] || [ ! -f /results/changed-files.txt ]; then
1316
+ if [ ! -f "$scouting_artifact" ] || [ ! -f ${KASEKI_RESULTS_DIR}/changed-files.txt ]; then
1228
1317
  return 0
1229
1318
  fi
1230
1319
 
@@ -1239,7 +1328,7 @@ run_scouting_allowlist_coverage() {
1239
1328
  validation_warnings=""
1240
1329
 
1241
1330
  if [ -n "$agent_patterns" ] && command -v dry-run-allowlist.sh >/dev/null 2>&1; then
1242
- agent_coverage="$(dry-run-allowlist.sh --result-dir /results --allowlist "$agent_patterns" 2>/dev/null | grep -oP '(?<=Coverage: )\d+(?=%)' | head -n 1 || true)"
1331
+ agent_coverage="$(dry-run-allowlist.sh --result-dir ${KASEKI_RESULTS_DIR} --allowlist "$agent_patterns" 2>/dev/null | grep -oP '(?<=Coverage: )\d+(?=%)' | head -n 1 || true)"
1243
1332
  [ -z "$agent_coverage" ] && agent_coverage="0"
1244
1333
 
1245
1334
  # Check for problematic coverage
@@ -1251,7 +1340,7 @@ run_scouting_allowlist_coverage() {
1251
1340
  fi
1252
1341
 
1253
1342
  if [ -n "$validation_patterns" ] && command -v dry-run-allowlist.sh >/dev/null 2>&1; then
1254
- validation_coverage="$(dry-run-allowlist.sh --result-dir /results --allowlist "$validation_patterns" 2>/dev/null | grep -oP '(?<=Coverage: )\d+(?=%)' | head -n 1 || true)"
1343
+ validation_coverage="$(dry-run-allowlist.sh --result-dir ${KASEKI_RESULTS_DIR} --allowlist "$validation_patterns" 2>/dev/null | grep -oP '(?<=Coverage: )\d+(?=%)' | head -n 1 || true)"
1255
1344
  [ -z "$validation_coverage" ] && validation_coverage="0"
1256
1345
 
1257
1346
  if [ "$validation_coverage" -lt 30 ]; then
@@ -1285,12 +1374,12 @@ run_scouting_allowlist_coverage() {
1285
1374
  if [ -n "$validation_warnings" ]; then
1286
1375
  printf ' ⚠ validation_phase warning: %s\n' "$validation_warnings"
1287
1376
  fi
1288
- } | tee -a /results/scouting-report.md >> /results/quality.log
1377
+ } | tee -a ${KASEKI_RESULTS_DIR}/scouting-report.md >> ${KASEKI_RESULTS_DIR}/quality.log
1289
1378
  fi
1290
1379
  }
1291
1380
 
1292
1381
  restore_disallowed_changes() {
1293
- if [ "$KASEKI_RESTORE_DISALLOWED_CHANGES" != "1" ] || [ ! -d /workspace/repo/.git ]; then
1382
+ if [ "$KASEKI_RESTORE_DISALLOWED_CHANGES" != "1" ] || [ ! -d ${KASEKI_WORKSPACE_DIR}/repo/.git ]; then
1294
1383
  return 0
1295
1384
  fi
1296
1385
 
@@ -1303,7 +1392,7 @@ restore_disallowed_changes() {
1303
1392
  coverage=0
1304
1393
 
1305
1394
  # Initialize restoration tracking file
1306
- : > /results/restoration.jsonl
1395
+ : > ${KASEKI_RESULTS_DIR}/restoration.jsonl
1307
1396
 
1308
1397
  while IFS= read -r changed_file || [ -n "$changed_file" ]; do
1309
1398
  [ -z "$changed_file" ] && continue
@@ -1313,21 +1402,21 @@ restore_disallowed_changes() {
1313
1402
  {
1314
1403
  printf '{"timestamp":"%s","event":"file_evaluated","file":"%s","status":"kept","reason":"matched_allowlist"}\n' \
1315
1404
  "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$(printf '%s' "$changed_file" | sed 's/"/\\"/g')"
1316
- } >> /results/restoration.jsonl
1405
+ } >> ${KASEKI_RESULTS_DIR}/restoration.jsonl
1317
1406
  continue
1318
1407
  fi
1319
1408
  # File did not match allowlist - restore it
1320
1409
  restored_count=$((restored_count + 1))
1321
- printf -- 'Restoring changed file outside allowlist before validation: %s\n' "$changed_file" | tee -a /results/quality.log
1410
+ printf -- 'Restoring changed file outside allowlist before validation: %s\n' "$changed_file" | tee -a ${KASEKI_RESULTS_DIR}/quality.log
1322
1411
  emit_event "quality_gate_rule_evaluated" "rule=allowlist_restore" "passed=true" "file=$changed_file"
1323
1412
  {
1324
1413
  printf '{"timestamp":"%s","event":"file_restored","file":"%s","status":"restored","reason":"not_in_allowlist"}\n' \
1325
1414
  "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$(printf '%s' "$changed_file" | sed 's/"/\\"/g')"
1326
- } >> /results/restoration.jsonl
1327
- git -C /workspace/repo restore --staged --worktree -- "$changed_file" 2>/dev/null || true
1328
- git -C /workspace/repo clean -f -- "$changed_file" 2>/dev/null || true
1415
+ } >> ${KASEKI_RESULTS_DIR}/restoration.jsonl
1416
+ git -C ${KASEKI_WORKSPACE_DIR}/repo restore --staged --worktree -- "$changed_file" 2>/dev/null || true
1417
+ git -C ${KASEKI_WORKSPACE_DIR}/repo clean -f -- "$changed_file" 2>/dev/null || true
1329
1418
  restored_any=1
1330
- done < /results/changed-files.txt
1419
+ done < ${KASEKI_RESULTS_DIR}/changed-files.txt
1331
1420
 
1332
1421
  # Emit restoration summary to quality.log with actionable guidance
1333
1422
  if [ $((restored_count + kept_count)) -gt 0 ]; then
@@ -1338,12 +1427,12 @@ restore_disallowed_changes() {
1338
1427
  printf '\n[allowlist summary] Restored: %d files; Kept: %d files (coverage: %d%%)\n' "$restored_count" "$kept_count" "$coverage"
1339
1428
  if [ "$restored_count" -gt 0 ] && [ "$coverage" -lt 50 ]; then
1340
1429
  printf '[allowlist note] Low coverage detected. To improve:\n'
1341
- printf ' 1. Run: ./scripts/suggest-allowlist.sh /results (or /agents/kaseki-results/<instance>)\n'
1430
+ printf ' 1. Run: ./scripts/suggest-allowlist.sh ${KASEKI_RESULTS_DIR} (or /agents/kaseki-results/<instance>)\n'
1342
1431
  printf ' 2. Review suggested patterns in allowlist-suggestions.md\n'
1343
1432
  printf ' 3. Update KASEKI_CHANGED_FILES_ALLOWLIST and re-run\n'
1344
1433
  printf 'See docs/QUALITY_GATES.md for more guidance.\n'
1345
1434
  fi
1346
- } | tee -a /results/quality.log
1435
+ } | tee -a ${KASEKI_RESULTS_DIR}/quality.log
1347
1436
  emit_event "allowlist_restoration_complete" "restored=$restored_count" "kept=$kept_count" "coverage=$coverage"
1348
1437
  fi
1349
1438
 
@@ -1353,7 +1442,7 @@ restore_disallowed_changes() {
1353
1442
  }
1354
1443
 
1355
1444
  generate_restoration_report() {
1356
- if [ ! -f /results/restoration.jsonl ]; then
1445
+ if [ ! -f ${KASEKI_RESULTS_DIR}/restoration.jsonl ]; then
1357
1446
  printf '[debug] restoration report: skipping - restoration.jsonl not found\n' >&2
1358
1447
  return 0
1359
1448
  fi
@@ -1362,7 +1451,7 @@ generate_restoration_report() {
1362
1451
 
1363
1452
  # Safely extract counts from restoration.jsonl with validation
1364
1453
  printf '[debug] restoration report: extracting counts from restoration.jsonl\n' >&2
1365
- restored_count=$(grep -c '"status":"restored"' /results/restoration.jsonl 2>/dev/null || true)
1454
+ restored_count=$(grep -c '"status":"restored"' ${KASEKI_RESULTS_DIR}/restoration.jsonl 2>/dev/null || true)
1366
1455
  restored_count=${restored_count:-0}
1367
1456
  printf '[debug] restoration report: restored_count="%s"\n' "$restored_count" >&2
1368
1457
  if ! validate_numeric "restored_count" "$restored_count"; then
@@ -1370,7 +1459,7 @@ generate_restoration_report() {
1370
1459
  return 1
1371
1460
  fi
1372
1461
 
1373
- kept_count=$(grep -c '"status":"kept"' /results/restoration.jsonl 2>/dev/null || true)
1462
+ kept_count=$(grep -c '"status":"kept"' ${KASEKI_RESULTS_DIR}/restoration.jsonl 2>/dev/null || true)
1374
1463
  kept_count=${kept_count:-0}
1375
1464
  printf '[debug] restoration report: kept_count="%s"\n' "$kept_count" >&2
1376
1465
  if ! validate_numeric "kept_count" "$kept_count"; then
@@ -1408,12 +1497,12 @@ generate_restoration_report() {
1408
1497
  if [ "$restored_count" -gt 0 ]; then
1409
1498
  printf '## Restored Files\n\n'
1410
1499
  printf 'These files were modified by the agent but restored because they fall outside the allowlist:\n\n'
1411
- grep '"status":"restored"' /results/restoration.jsonl | \
1500
+ grep '"status":"restored"' ${KASEKI_RESULTS_DIR}/restoration.jsonl | \
1412
1501
  sed "s/.*\"file\":\"\([^\"]*\)\".*/- \`\1\`/" | \
1413
- sort | uniq >> /results/restoration-report.md.tmp 2>/dev/null || true
1414
- if [ -f /results/restoration-report.md.tmp ]; then
1415
- cat /results/restoration-report.md.tmp
1416
- rm -f /results/restoration-report.md.tmp
1502
+ sort | uniq >> ${KASEKI_RESULTS_DIR}/restoration-report.md.tmp 2>/dev/null || true
1503
+ if [ -f ${KASEKI_RESULTS_DIR}/restoration-report.md.tmp ]; then
1504
+ cat ${KASEKI_RESULTS_DIR}/restoration-report.md.tmp
1505
+ rm -f ${KASEKI_RESULTS_DIR}/restoration-report.md.tmp
1417
1506
  fi
1418
1507
  printf '\n'
1419
1508
  fi
@@ -1421,12 +1510,12 @@ generate_restoration_report() {
1421
1510
  if [ "$kept_count" -gt 0 ]; then
1422
1511
  printf '## Kept Files (Allowlist Matches)\n\n'
1423
1512
  printf 'These files were in the allowlist and were kept:\n\n'
1424
- grep '"status":"kept"' /results/restoration.jsonl | \
1513
+ grep '"status":"kept"' ${KASEKI_RESULTS_DIR}/restoration.jsonl | \
1425
1514
  sed "s/.*\"file\":\"\([^\"]*\)\".*/- \`\1\`/" | \
1426
- sort | uniq >> /results/restoration-report.md.tmp 2>/dev/null || true
1427
- if [ -f /results/restoration-report.md.tmp ]; then
1428
- cat /results/restoration-report.md.tmp
1429
- rm -f /results/restoration-report.md.tmp
1515
+ sort | uniq >> ${KASEKI_RESULTS_DIR}/restoration-report.md.tmp 2>/dev/null || true
1516
+ if [ -f ${KASEKI_RESULTS_DIR}/restoration-report.md.tmp ]; then
1517
+ cat ${KASEKI_RESULTS_DIR}/restoration-report.md.tmp
1518
+ rm -f ${KASEKI_RESULTS_DIR}/restoration-report.md.tmp
1430
1519
  fi
1431
1520
  printf '\n'
1432
1521
  fi
@@ -1444,14 +1533,14 @@ generate_restoration_report() {
1444
1533
  printf 'KASEKI_CHANGED_FILES_ALLOWLIST="<your-pattern>" ./run-kaseki.sh\n'
1445
1534
  printf '```\n\n'
1446
1535
  printf "For help on allowlist patterns, see \`docs/QUALITY_GATES.md\`.\n"
1447
- } > /results/restoration-report.md
1536
+ } > ${KASEKI_RESULTS_DIR}/restoration-report.md
1448
1537
  }
1449
1538
 
1450
1539
  check_validation_allowlist() {
1451
1540
  if [ -z "$KASEKI_VALIDATION_ALLOWLIST" ]; then
1452
1541
  return 0
1453
1542
  fi
1454
- if [ ! -d /workspace/repo/.git ]; then
1543
+ if [ ! -d ${KASEKI_WORKSPACE_DIR}/repo/.git ]; then
1455
1544
  return 0
1456
1545
  fi
1457
1546
 
@@ -1460,9 +1549,9 @@ check_validation_allowlist() {
1460
1549
  allowlist_regex="$(build_allowlist_regex "$KASEKI_VALIDATION_ALLOWLIST")"
1461
1550
  [ -z "$allowlist_regex" ] && return 0
1462
1551
  validation_violation_count=0
1463
- validation_before_state_file="/results/validation-before-state.txt"
1464
- validation_after_state_file="/results/validation-after-state.txt"
1465
- validation_changed_file="/results/validation-changed-files.txt"
1552
+ validation_before_state_file="${KASEKI_RESULTS_DIR}/validation-before-state.txt"
1553
+ validation_after_state_file="${KASEKI_RESULTS_DIR}/validation-after-state.txt"
1554
+ validation_changed_file="${KASEKI_RESULTS_DIR}/validation-changed-files.txt"
1466
1555
  : > "$validation_changed_file"
1467
1556
 
1468
1557
  if [ -f "$validation_before_state_file" ] && [ -f "$validation_after_state_file" ]; then
@@ -1477,14 +1566,14 @@ check_validation_allowlist() {
1477
1566
  }
1478
1567
  }
1479
1568
  ' "$validation_before_state_file" "$validation_after_state_file" | LC_ALL=C sort -u > "$validation_changed_file"
1480
- elif [ -f /results/changed-files.txt ]; then
1481
- cp /results/changed-files.txt "$validation_changed_file"
1569
+ elif [ -f ${KASEKI_RESULTS_DIR}/changed-files.txt ]; then
1570
+ cp ${KASEKI_RESULTS_DIR}/changed-files.txt "$validation_changed_file"
1482
1571
  fi
1483
1572
 
1484
1573
  while IFS= read -r changed_file || [ -n "$changed_file" ]; do
1485
1574
  [ -z "$changed_file" ] && continue
1486
1575
  if ! printf '%s\n' "$changed_file" | grep -Eq "^(${allowlist_regex})$"; then
1487
- printf 'Validation-phase file outside allowlist: %s\n' "$changed_file" | tee -a /results/quality.log
1576
+ printf 'Validation-phase file outside allowlist: %s\n' "$changed_file" | tee -a ${KASEKI_RESULTS_DIR}/quality.log
1488
1577
  validation_violation_count=$((validation_violation_count + 1))
1489
1578
  emit_event "quality_gate_rule_evaluated" "rule=validation_allowlist" "passed=false" "file=$changed_file"
1490
1579
  else
@@ -1496,14 +1585,14 @@ check_validation_allowlist() {
1496
1585
  QUALITY_EXIT=7
1497
1586
  VALIDATION_ALLOWLIST_FAILURE_REASON="validation_allowlist_check: $validation_violation_count file(s) changed during validation outside KASEKI_VALIDATION_ALLOWLIST"
1498
1587
  QUALITY_FAILURE_REASON="$VALIDATION_ALLOWLIST_FAILURE_REASON"
1499
- printf '\n[validation-allowlist] %d file(s) modified during validation outside allowlist\n' "$validation_violation_count" | tee -a /results/quality.log
1588
+ printf '\n[validation-allowlist] %d file(s) modified during validation outside allowlist\n' "$validation_violation_count" | tee -a ${KASEKI_RESULTS_DIR}/quality.log
1500
1589
  return 1
1501
1590
  fi
1502
1591
  return 0
1503
1592
  }
1504
1593
 
1505
1594
  check_secret_scan_allowlist() {
1506
- local allowlist_file="/workspace/repo/.kaseki-secret-allowlist"
1595
+ local allowlist_file="${KASEKI_WORKSPACE_DIR}/repo/.kaseki-secret-allowlist"
1507
1596
 
1508
1597
  # If no allowlist file exists, all matches are failures (real leaks)
1509
1598
  if [ ! -f "$allowlist_file" ]; then
@@ -1516,7 +1605,7 @@ check_secret_scan_allowlist() {
1516
1605
 
1517
1606
  # Read the log into a temp variable to avoid SC2094 (read-write in same pipeline)
1518
1607
  local temp_log
1519
- temp_log=$(cat /results/secret-scan.log)
1608
+ temp_log=$(cat ${KASEKI_RESULTS_DIR}/secret-scan.log)
1520
1609
 
1521
1610
  while IFS= read -r match_line || [ -n "$match_line" ]; do
1522
1611
  [ -z "$match_line" ] && continue
@@ -1530,8 +1619,8 @@ check_secret_scan_allowlist() {
1530
1619
 
1531
1620
  [ -z "$pattern" ] && continue
1532
1621
 
1533
- # Normalize file path: remove leading /workspace/repo/, repo/, and ./ if present
1534
- file_path="${file_path#/workspace/repo/}"
1622
+ # Normalize file path: remove leading ${KASEKI_WORKSPACE_DIR}/repo/, repo/, and ./ if present
1623
+ file_path="${file_path#${KASEKI_WORKSPACE_DIR}/repo/}"
1535
1624
  file_path="${file_path#repo/}"
1536
1625
  file_path="${file_path#./}"
1537
1626
 
@@ -1556,7 +1645,7 @@ check_secret_scan_allowlist() {
1556
1645
  for match in "${secret_matches[@]}"; do
1557
1646
  printf '%s\n' "$match"
1558
1647
  done
1559
- } > /results/secret-scan.log
1648
+ } > ${KASEKI_RESULTS_DIR}/secret-scan.log
1560
1649
 
1561
1650
  # Exit code 6 only if there are unallowlisted matches
1562
1651
  if [ "$unallowlisted_count" -gt 0 ]; then
@@ -1590,9 +1679,9 @@ finish() {
1590
1679
  printf '[unexpected-failure] Exit code: %d\n' "$code"
1591
1680
  printf '[unexpected-failure] Last command: %s\n' "$LAST_COMMAND"
1592
1681
  printf '[unexpected-failure] Current stage: %s\n' "$CURRENT_STAGE"
1593
- if [ -f /results/progress.log ]; then
1682
+ if [ -f ${KASEKI_RESULTS_DIR}/progress.log ]; then
1594
1683
  printf '[unexpected-failure] Last 5 progress entries:\n'
1595
- tail -5 /results/progress.log | sed 's/^/ /'
1684
+ tail -5 ${KASEKI_RESULTS_DIR}/progress.log | sed 's/^/ /'
1596
1685
  fi
1597
1686
  } | tee -a "$LAST_COMMAND_LOG" >&2
1598
1687
  emit_error_event "unexpected_shell_failure" "Uncaught shell error (exit $code) in stage '$CURRENT_STAGE'. Last command: $LAST_COMMAND. See $LAST_COMMAND_LOG for context." "exit"
@@ -1601,16 +1690,16 @@ finish() {
1601
1690
  maybe_call_finish_helper collect_git_artifacts
1602
1691
 
1603
1692
  # Analyze test failures and compare baseline vs. working results
1604
- if [ "$KASEKI_BASELINE_VALIDATION_ENABLED" = "1" ] && [ -f /results/validation-baseline.log ] && [ -f /results/pre-validation.log ]; then
1693
+ if [ "$KASEKI_BASELINE_VALIDATION_ENABLED" = "1" ] && [ -f ${KASEKI_RESULTS_DIR}/validation-baseline.log ] && [ -f ${KASEKI_RESULTS_DIR}/pre-validation.log ]; then
1605
1694
  set_current_stage "test failure analysis"
1606
1695
  if analyze_test_failures_baseline; then
1607
1696
  TEST_FAILURE_CLASSIFICATION_STATUS="completed"
1608
1697
  # Try to extract newly_introduced_failures_count from JSON output (if jq available)
1609
- if [ -f /results/test-baseline-comparison.json ] && command -v jq >/dev/null 2>&1; then
1610
- NEWLY_INTRODUCED_FAILURES_COUNT=$(jq -r '.summary.total_newly_introduced // 0' /results/test-baseline-comparison.json 2>/dev/null || printf '0')
1611
- elif [ -f /results/test-baseline-comparison.json ]; then
1698
+ if [ -f ${KASEKI_RESULTS_DIR}/test-baseline-comparison.json ] && command -v jq >/dev/null 2>&1; then
1699
+ NEWLY_INTRODUCED_FAILURES_COUNT=$(jq -r '.summary.total_newly_introduced // 0' ${KASEKI_RESULTS_DIR}/test-baseline-comparison.json 2>/dev/null || printf '0')
1700
+ elif [ -f ${KASEKI_RESULTS_DIR}/test-baseline-comparison.json ]; then
1612
1701
  # Fallback: try to extract with grep/sed if jq not available
1613
- NEWLY_INTRODUCED_FAILURES_COUNT=$(grep -o '"total_newly_introduced": [0-9]*' /results/test-baseline-comparison.json 2>/dev/null | grep -o '[0-9]*$' || printf '0')
1702
+ NEWLY_INTRODUCED_FAILURES_COUNT=$(grep -o '"total_newly_introduced": [0-9]*' ${KASEKI_RESULTS_DIR}/test-baseline-comparison.json 2>/dev/null | grep -o '[0-9]*$' || printf '0')
1614
1703
  fi
1615
1704
  else
1616
1705
  TEST_FAILURE_CLASSIFICATION_STATUS="failed"
@@ -1624,8 +1713,8 @@ finish() {
1624
1713
  fi
1625
1714
 
1626
1715
  # Debug output for restoration report generation
1627
- if [ -f /results/restoration.jsonl ]; then
1628
- printf '[debug] restoration.jsonl exists (size=%d bytes)\n' "$(wc -c < /results/restoration.jsonl)" >&2
1716
+ if [ -f ${KASEKI_RESULTS_DIR}/restoration.jsonl ]; then
1717
+ printf '[debug] restoration.jsonl exists (size=%d bytes)\n' "$(wc -c < ${KASEKI_RESULTS_DIR}/restoration.jsonl)" >&2
1629
1718
  else
1630
1719
  printf '[debug] restoration.jsonl does not exist\n' >&2
1631
1720
  fi
@@ -1643,12 +1732,12 @@ finish() {
1643
1732
 
1644
1733
  # Calculate and record maturity score
1645
1734
  if [ -x /app/scripts/kaseki-maturity-score.sh ]; then
1646
- /app/scripts/kaseki-maturity-score.sh /workspace/repo /results/maturity-score.json 2>/dev/null || true
1735
+ /app/scripts/kaseki-maturity-score.sh ${KASEKI_WORKSPACE_DIR}/repo ${KASEKI_RESULTS_DIR}/maturity-score.json 2>/dev/null || true
1647
1736
  fi
1648
1737
 
1649
1738
  # Calculate and record performance metrics
1650
- if [ -x /app/scripts/kaseki-performance-metrics.sh ] && [ -f /results/stage-timings.tsv ]; then
1651
- /app/scripts/kaseki-performance-metrics.sh /results/stage-timings.tsv /results/performance-metrics.json 2>/dev/null || true
1739
+ if [ -x /app/scripts/kaseki-performance-metrics.sh ] && [ -f ${KASEKI_RESULTS_DIR}/stage-timings.tsv ]; then
1740
+ /app/scripts/kaseki-performance-metrics.sh ${KASEKI_RESULTS_DIR}/stage-timings.tsv ${KASEKI_RESULTS_DIR}/performance-metrics.json 2>/dev/null || true
1652
1741
  fi
1653
1742
 
1654
1743
  maybe_call_finish_helper write_result_summary
@@ -1656,7 +1745,7 @@ finish() {
1656
1745
  # Generate inspect-report.md for inspect mode on success
1657
1746
  if [ "$KASEKI_TASK_MODE" = "inspect" ] && [ "$STATUS" -eq 0 ]; then
1658
1747
  if [ -x /app/scripts/generate-inspect-report.js ]; then
1659
- node /app/scripts/generate-inspect-report.js /results 2>/dev/null || true
1748
+ node /app/scripts/generate-inspect-report.js ${KASEKI_RESULTS_DIR} 2>/dev/null || true
1660
1749
  fi
1661
1750
  fi
1662
1751
 
@@ -1701,7 +1790,7 @@ run_step_dry() {
1701
1790
  printf '\n==> %s (DRY-RUN: simulated)\n' "$label"
1702
1791
  emit_progress "$label" "started (dry-run)"
1703
1792
  # Show what commands would be run without executing them
1704
- printf '%s\n' "$@" >> /results/validation.log
1793
+ printf '%s\n' "$@" >> ${KASEKI_RESULTS_DIR}/validation.log
1705
1794
  step_end="$(date +%s)"
1706
1795
  emit_progress "$label" "finished (dry-run, simulated exit 0)"
1707
1796
  record_stage_timing "$label" "0" "$((step_end - step_start))" "dry-run"
@@ -1736,9 +1825,9 @@ is_valid_git_mirror() {
1736
1825
  }
1737
1826
 
1738
1827
  run_direct_clone() {
1739
- rm -rf /workspace/repo
1828
+ rm -rf ${KASEKI_WORKSPACE_DIR}/repo
1740
1829
  GIT_CLONE_STRATEGY="direct_shallow"
1741
- git clone --depth 1 --branch "$GIT_REF" "$REPO_URL" /workspace/repo
1830
+ git clone --depth 1 --branch "$GIT_REF" "$REPO_URL" ${KASEKI_WORKSPACE_DIR}/repo
1742
1831
  }
1743
1832
 
1744
1833
  clone_with_git_cache() {
@@ -1815,25 +1904,25 @@ clone_with_git_cache() {
1815
1904
  fi
1816
1905
  flock -u 9 || true
1817
1906
 
1818
- rm -rf /workspace/repo
1907
+ rm -rf ${KASEKI_WORKSPACE_DIR}/repo
1819
1908
  GIT_CLONE_STRATEGY="reference_shallow"
1820
- git clone --reference-if-able "$mirror" --depth 1 --branch "$GIT_REF" "$REPO_URL" /workspace/repo
1909
+ git clone --reference-if-able "$mirror" --depth 1 --branch "$GIT_REF" "$REPO_URL" ${KASEKI_WORKSPACE_DIR}/repo
1821
1910
  clone_rc=$?
1822
1911
  if [ "$clone_rc" -eq 0 ]; then
1823
1912
  return 0
1824
1913
  fi
1825
1914
 
1826
- rm -rf /workspace/repo
1915
+ rm -rf ${KASEKI_WORKSPACE_DIR}/repo
1827
1916
  GIT_CLONE_STRATEGY="mirror_local"
1828
1917
  emit_error_event "git_cache_reference_clone_failed" "Reference clone failed for key=$GIT_CACHE_KEY exit=$clone_rc; trying local mirror clone" "try_mirror_clone"
1829
- git clone --branch "$GIT_REF" "$mirror" /workspace/repo
1918
+ git clone --branch "$GIT_REF" "$mirror" ${KASEKI_WORKSPACE_DIR}/repo
1830
1919
  clone_rc=$?
1831
- if [ "$clone_rc" -eq 0 ] && git -C /workspace/repo rev-parse --verify HEAD >/dev/null 2>&1; then
1832
- git -C /workspace/repo remote set-url origin "$REPO_URL" >/dev/null 2>&1 || true
1920
+ if [ "$clone_rc" -eq 0 ] && git -C ${KASEKI_WORKSPACE_DIR}/repo rev-parse --verify HEAD >/dev/null 2>&1; then
1921
+ git -C ${KASEKI_WORKSPACE_DIR}/repo remote set-url origin "$REPO_URL" >/dev/null 2>&1 || true
1833
1922
  return 0
1834
1923
  fi
1835
1924
 
1836
- rm -rf /workspace/repo
1925
+ rm -rf ${KASEKI_WORKSPACE_DIR}/repo
1837
1926
  GIT_CACHE_STATUS="mirror_clone_failed"
1838
1927
  emit_error_event "git_cache_mirror_clone_failed" "Mirror clone failed for key=$GIT_CACHE_KEY exit=$clone_rc; using direct clone" "fallback_direct_clone"
1839
1928
  run_direct_clone
@@ -2110,7 +2199,7 @@ record_skipped_npm_script_command() {
2110
2199
  local command="$1"
2111
2200
  local script_name="$2"
2112
2201
  local duration_seconds="$3"
2113
- local log_file="${4:-/results/validation.log}"
2202
+ local log_file="${4:-${KASEKI_RESULTS_DIR}/validation.log}"
2114
2203
  local timings_file="${5:-$VALIDATION_TIMINGS_FILE}"
2115
2204
  local skip_label="${6:-skipped}"
2116
2205
  local classification="${7:-}"
@@ -2144,7 +2233,7 @@ classify_auto_lint_cleanup_command_exit() {
2144
2233
  }
2145
2234
 
2146
2235
  record_skipped_validation_command() {
2147
- record_skipped_npm_script_command "$1" "$2" "$3" "${4:-/results/validation.log}" "${5:-$VALIDATION_TIMINGS_FILE}" "skipped"
2236
+ record_skipped_npm_script_command "$1" "$2" "$3" "${4:-${KASEKI_RESULTS_DIR}/validation.log}" "${5:-$VALIDATION_TIMINGS_FILE}" "skipped"
2148
2237
  }
2149
2238
 
2150
2239
  has_typescript_project() {
@@ -2213,9 +2302,9 @@ run_typescript_precheck() {
2213
2302
  if ! has_npm_build_command "$KASEKI_TS_CHECK_COMMAND"; then
2214
2303
  local missing_script
2215
2304
  missing_script="$(npm_run_script_name "$KASEKI_TS_CHECK_COMMAND")" || missing_script="$KASEKI_TS_CHECK_COMMAND"
2216
- printf '\n==> TypeScript pre-check\n' | tee -a /results/pre-validation-ts-check.log
2217
- printf 'Command: %s\n' "$KASEKI_TS_CHECK_COMMAND" | tee -a /results/pre-validation-ts-check.log
2218
- printf 'skipped: npm script "%s" not found in package.json\n' "$missing_script" | tee -a /results/pre-validation-ts-check.log
2305
+ printf '\n==> TypeScript pre-check\n' | tee -a ${KASEKI_RESULTS_DIR}/pre-validation-ts-check.log
2306
+ printf 'Command: %s\n' "$KASEKI_TS_CHECK_COMMAND" | tee -a ${KASEKI_RESULTS_DIR}/pre-validation-ts-check.log
2307
+ printf 'skipped: npm script "%s" not found in package.json\n' "$missing_script" | tee -a ${KASEKI_RESULTS_DIR}/pre-validation-ts-check.log
2219
2308
  emit_error_event "typescript_precheck_skipped_missing_script" "TypeScript check skipped: npm script '$missing_script' not defined" "continue"
2220
2309
  emit_progress "typescript precheck" "skipped (npm script '$missing_script' not found)"
2221
2310
  record_stage_timing "typescript precheck" 0 0 "skipped_missing_script"
@@ -2230,8 +2319,8 @@ run_typescript_precheck() {
2230
2319
  {
2231
2320
  printf '\n==> TypeScript pre-check\n'
2232
2321
  printf 'Command: %s\n' "$KASEKI_TS_CHECK_COMMAND"
2233
- eval "cd /workspace/repo && $KASEKI_TS_CHECK_COMMAND" 2>&1
2234
- } 2>&1 | tee -a /results/pre-validation-ts-check.log
2322
+ eval "cd ${KASEKI_WORKSPACE_DIR}/repo && $KASEKI_TS_CHECK_COMMAND" 2>&1
2323
+ } 2>&1 | tee -a ${KASEKI_RESULTS_DIR}/pre-validation-ts-check.log
2235
2324
  ts_check_exit="${PIPESTATUS[0]}"
2236
2325
 
2237
2326
  ts_check_end="$(date +%s)"
@@ -2255,7 +2344,7 @@ run_typescript_precheck() {
2255
2344
  append_validation_failure_tail() {
2256
2345
  local raw_log="$1"
2257
2346
  local visible_log="$2"
2258
- local quality_log="${3:-/results/quality.log}"
2347
+ local quality_log="${3:-${KASEKI_RESULTS_DIR}/quality.log}"
2259
2348
 
2260
2349
  if ! [ -s "$raw_log" ]; then
2261
2350
  return 0
@@ -2304,14 +2393,14 @@ run_trailing_whitespace_cleanup_for_changed_tracked_text_files() {
2304
2393
  collect_changed_file_set() {
2305
2394
  local output_file="$1"
2306
2395
  : > "$output_file"
2307
- if [ ! -d /workspace/repo/.git ]; then
2396
+ if [ ! -d ${KASEKI_WORKSPACE_DIR}/repo/.git ]; then
2308
2397
  return 0
2309
2398
  fi
2310
2399
 
2311
2400
  {
2312
- git -C /workspace/repo diff --name-only -- . 2>/dev/null || true
2313
- git -C /workspace/repo diff --name-only --cached -- . 2>/dev/null || true
2314
- git -C /workspace/repo ls-files --others --exclude-standard 2>/dev/null || true
2401
+ git -C ${KASEKI_WORKSPACE_DIR}/repo diff --name-only -- . 2>/dev/null || true
2402
+ git -C ${KASEKI_WORKSPACE_DIR}/repo diff --name-only --cached -- . 2>/dev/null || true
2403
+ git -C ${KASEKI_WORKSPACE_DIR}/repo ls-files --others --exclude-standard 2>/dev/null || true
2315
2404
  } | sed '/^$/d' | LC_ALL=C sort -u > "$output_file"
2316
2405
  }
2317
2406
 
@@ -2319,7 +2408,7 @@ collect_changed_file_state() {
2319
2408
  local output_file="$1"
2320
2409
  local changed_files_file path staged_hash unstaged_hash content_hash state
2321
2410
  : > "$output_file"
2322
- if [ ! -d /workspace/repo/.git ]; then
2411
+ if [ ! -d ${KASEKI_WORKSPACE_DIR}/repo/.git ]; then
2323
2412
  return 0
2324
2413
  fi
2325
2414
 
@@ -2328,14 +2417,14 @@ collect_changed_file_state() {
2328
2417
 
2329
2418
  while IFS= read -r path || [ -n "$path" ]; do
2330
2419
  [ -z "$path" ] && continue
2331
- if git -C /workspace/repo ls-files --error-unmatch -- "$path" >/dev/null 2>&1; then
2332
- staged_hash="$(git -C /workspace/repo diff --binary --cached -- "$path" 2>/dev/null | sha256sum | awk '{print $1}')"
2333
- unstaged_hash="$(git -C /workspace/repo diff --binary -- "$path" 2>/dev/null | sha256sum | awk '{print $1}')"
2420
+ if git -C ${KASEKI_WORKSPACE_DIR}/repo ls-files --error-unmatch -- "$path" >/dev/null 2>&1; then
2421
+ staged_hash="$(git -C ${KASEKI_WORKSPACE_DIR}/repo diff --binary --cached -- "$path" 2>/dev/null | sha256sum | awk '{print $1}')"
2422
+ unstaged_hash="$(git -C ${KASEKI_WORKSPACE_DIR}/repo diff --binary -- "$path" 2>/dev/null | sha256sum | awk '{print $1}')"
2334
2423
  state="tracked:staged=${staged_hash}:unstaged=${unstaged_hash}"
2335
- elif [ -f "/workspace/repo/$path" ]; then
2336
- content_hash="$(git -C /workspace/repo hash-object --no-filters -- "$path" 2>/dev/null || sha256sum "/workspace/repo/$path" 2>/dev/null | awk '{print $1}')"
2424
+ elif [ -f "${KASEKI_WORKSPACE_DIR}/repo/$path" ]; then
2425
+ content_hash="$(git -C ${KASEKI_WORKSPACE_DIR}/repo hash-object --no-filters -- "$path" 2>/dev/null || sha256sum "${KASEKI_WORKSPACE_DIR}/repo/$path" 2>/dev/null | awk '{print $1}')"
2337
2426
  state="untracked:file=${content_hash}"
2338
- elif [ -d "/workspace/repo/$path" ]; then
2427
+ elif [ -d "${KASEKI_WORKSPACE_DIR}/repo/$path" ]; then
2339
2428
  state="untracked:directory"
2340
2429
  else
2341
2430
  state="untracked:missing"
@@ -2353,25 +2442,25 @@ restore_cleanup_disallowed_changes() {
2353
2442
 
2354
2443
  while IFS= read -r changed_file || [ -n "$changed_file" ]; do
2355
2444
  [ -z "$changed_file" ] && continue
2356
- printf 'Restoring cleanup-created file outside allowlist: %s\n' "$changed_file" | tee -a "$AUTO_LINT_CLEANUP_LOG" /results/quality.log
2445
+ printf 'Restoring cleanup-created file outside allowlist: %s\n' "$changed_file" | tee -a "$AUTO_LINT_CLEANUP_LOG" ${KASEKI_RESULTS_DIR}/quality.log
2357
2446
  emit_event "auto_lint_cleanup_file_restored" "file=$changed_file" "reason=not_in_cleanup_allowlist"
2358
- git -C /workspace/repo restore --staged --worktree -- "$changed_file" 2>/dev/null || true
2359
- git -C /workspace/repo clean -f -- "$changed_file" 2>/dev/null || true
2447
+ git -C ${KASEKI_WORKSPACE_DIR}/repo restore --staged --worktree -- "$changed_file" 2>/dev/null || true
2448
+ git -C ${KASEKI_WORKSPACE_DIR}/repo clean -f -- "$changed_file" 2>/dev/null || true
2360
2449
  done < "$disallowed_file"
2361
2450
  }
2362
2451
 
2363
2452
  check_auto_lint_cleanup_allowlist() {
2364
2453
  local before_file="$1"
2365
2454
  local after_file="$2"
2366
- local cleanup_created_file="/results/auto-lint-cleanup-created-files.txt"
2367
- local disallowed_file="/results/auto-lint-cleanup-disallowed-files.txt"
2368
- local post_restore_file="/results/auto-lint-cleanup-post-restore-files.txt"
2455
+ local cleanup_created_file="${KASEKI_RESULTS_DIR}/auto-lint-cleanup-created-files.txt"
2456
+ local disallowed_file="${KASEKI_RESULTS_DIR}/auto-lint-cleanup-disallowed-files.txt"
2457
+ local post_restore_file="${KASEKI_RESULTS_DIR}/auto-lint-cleanup-post-restore-files.txt"
2369
2458
  local allowlist_patterns allowlist_regex changed_file disallowed_count unrestored_count
2370
2459
 
2371
2460
  : > "$cleanup_created_file"
2372
2461
  : > "$disallowed_file"
2373
2462
  : > "$post_restore_file"
2374
- if [ ! -d /workspace/repo/.git ]; then
2463
+ if [ ! -d ${KASEKI_WORKSPACE_DIR}/repo/.git ]; then
2375
2464
  return 0
2376
2465
  fi
2377
2466
 
@@ -2386,7 +2475,7 @@ check_auto_lint_cleanup_allowlist() {
2386
2475
  if [ -n "$allowlist_regex" ] && printf '%s\n' "$changed_file" | grep -Eq "^(${allowlist_regex})$"; then
2387
2476
  emit_event "quality_gate_rule_evaluated" "rule=auto_lint_cleanup_allowlist" "passed=true" "file=$changed_file"
2388
2477
  else
2389
- printf 'Auto lint cleanup created changed file outside allowlist: %s\n' "$changed_file" | tee -a "$AUTO_LINT_CLEANUP_LOG" /results/quality.log
2478
+ printf 'Auto lint cleanup created changed file outside allowlist: %s\n' "$changed_file" | tee -a "$AUTO_LINT_CLEANUP_LOG" ${KASEKI_RESULTS_DIR}/quality.log
2390
2479
  printf '%s\n' "$changed_file" >> "$disallowed_file"
2391
2480
  emit_event "quality_gate_rule_evaluated" "rule=auto_lint_cleanup_allowlist" "passed=false" "file=$changed_file"
2392
2481
  fi
@@ -2403,12 +2492,12 @@ check_auto_lint_cleanup_allowlist() {
2403
2492
  while IFS= read -r changed_file || [ -n "$changed_file" ]; do
2404
2493
  [ -z "$changed_file" ] && continue
2405
2494
  if grep -Fxq -- "$changed_file" "$post_restore_file"; then
2406
- printf 'ERROR: Cleanup-created disallowed change could not be restored: %s\n' "$changed_file" | tee -a "$AUTO_LINT_CLEANUP_LOG" /results/quality.log
2495
+ printf 'ERROR: Cleanup-created disallowed change could not be restored: %s\n' "$changed_file" | tee -a "$AUTO_LINT_CLEANUP_LOG" ${KASEKI_RESULTS_DIR}/quality.log
2407
2496
  unrestored_count=$((unrestored_count + 1))
2408
2497
  fi
2409
2498
  done < "$disallowed_file"
2410
2499
  if [ "$unrestored_count" -eq 0 ]; then
2411
- printf 'Auto lint cleanup restored %s cleanup-created file(s) outside allowlist.\n' "$disallowed_count" | tee -a "$AUTO_LINT_CLEANUP_LOG" /results/quality.log
2500
+ printf 'Auto lint cleanup restored %s cleanup-created file(s) outside allowlist.\n' "$disallowed_count" | tee -a "$AUTO_LINT_CLEANUP_LOG" ${KASEKI_RESULTS_DIR}/quality.log
2412
2501
  emit_event "auto_lint_cleanup_allowlist_restoration_complete" "restored=$disallowed_count" "unrestored=0"
2413
2502
  collect_git_artifacts
2414
2503
  return 0
@@ -2421,7 +2510,7 @@ check_auto_lint_cleanup_allowlist() {
2421
2510
  AUTO_LINT_CLEANUP_FAILURE_CLASSIFICATION="cleanup_allowlist_failed"
2422
2511
  QUALITY_EXIT=7
2423
2512
  QUALITY_FAILURE_REASON="auto_lint_cleanup_allowlist: $disallowed_count cleanup-created file(s) outside KASEKI_CHANGED_FILES_ALLOWLIST/KASEKI_VALIDATION_ALLOWLIST"
2424
- printf 'ERROR: %s\n' "$QUALITY_FAILURE_REASON" | tee -a "$AUTO_LINT_CLEANUP_LOG" /results/quality.log
2513
+ printf 'ERROR: %s\n' "$QUALITY_FAILURE_REASON" | tee -a "$AUTO_LINT_CLEANUP_LOG" ${KASEKI_RESULTS_DIR}/quality.log
2425
2514
  emit_error_event "auto_lint_cleanup_allowlist_failed" "$QUALITY_FAILURE_REASON" "continue"
2426
2515
  return 1
2427
2516
  }
@@ -2481,12 +2570,12 @@ run_auto_lint_cleanup() {
2481
2570
  return 0
2482
2571
  fi
2483
2572
 
2484
- if ! [ -d /workspace/repo ]; then
2573
+ if ! [ -d ${KASEKI_WORKSPACE_DIR}/repo ]; then
2485
2574
  AUTO_LINT_CLEANUP_EXIT=1
2486
2575
  AUTO_LINT_CLEANUP_RESULT="failed"
2487
2576
  AUTO_LINT_CLEANUP_CLASSIFICATION="directory_missing"
2488
2577
  AUTO_LINT_CLEANUP_FAILURE_CLASSIFICATION="directory_missing"
2489
- printf 'ERROR: Working directory /workspace/repo does not exist before auto lint cleanup.\n' | tee -a "$AUTO_LINT_CLEANUP_LOG"
2578
+ printf 'ERROR: Working directory ${KASEKI_WORKSPACE_DIR}/repo does not exist before auto lint cleanup.\n' | tee -a "$AUTO_LINT_CLEANUP_LOG"
2490
2579
  printf 'workspace_missing\t%s\t0\tclassification=directory_missing\n' "$AUTO_LINT_CLEANUP_EXIT" >> "$AUTO_LINT_CLEANUP_TIMINGS_FILE"
2491
2580
  record_stage_timing "$stage_label" "$AUTO_LINT_CLEANUP_EXIT" "$(($(date +%s) - stage_start))" "directory_missing classification=directory_missing"
2492
2581
  emit_event "auto_lint_cleanup_finished" "exit_code=$AUTO_LINT_CLEANUP_EXIT" "result=failed" "classification=directory_missing" "reason=directory_missing"
@@ -2494,8 +2583,8 @@ run_auto_lint_cleanup() {
2494
2583
  return 0
2495
2584
  fi
2496
2585
 
2497
- cleanup_before_file="/results/auto-lint-cleanup-before-files.txt"
2498
- cleanup_after_file="/results/auto-lint-cleanup-after-files.txt"
2586
+ cleanup_before_file="${KASEKI_RESULTS_DIR}/auto-lint-cleanup-before-files.txt"
2587
+ cleanup_after_file="${KASEKI_RESULTS_DIR}/auto-lint-cleanup-after-files.txt"
2499
2588
  collect_changed_file_set "$cleanup_before_file"
2500
2589
 
2501
2590
  set +e
@@ -2585,7 +2674,7 @@ baseline_validation_cache_key() {
2585
2674
  }
2586
2675
 
2587
2676
  baseline_validation_cache_dir() {
2588
- local cache_root="${KASEKI_BASELINE_CACHE_ROOT:-/cache/kaseki-baseline}"
2677
+ local cache_root="${KASEKI_BASELINE_CACHE_ROOT:-${KASEKI_CACHE_DIR}/kaseki-baseline}"
2589
2678
  local cache_key
2590
2679
  cache_key="$(baseline_validation_cache_key)"
2591
2680
  printf '%s/%s' "$cache_root" "$cache_key"
@@ -2619,19 +2708,19 @@ restore_baseline_validation_from_cache() {
2619
2708
  fi
2620
2709
 
2621
2710
  # Restore cached files to results directory
2622
- mkdir -p /results
2711
+ mkdir -p ${KASEKI_RESULTS_DIR}
2623
2712
 
2624
- if ! cp "$cache_dir/validation.log" /results/validation-baseline.log 2>/dev/null; then
2713
+ if ! cp "$cache_dir/validation.log" ${KASEKI_RESULTS_DIR}/validation-baseline.log 2>/dev/null; then
2625
2714
  return 1
2626
2715
  fi
2627
- if ! cp "$cache_dir/validation-raw.log" /results/validation-baseline-raw.log 2>/dev/null; then
2716
+ if ! cp "$cache_dir/validation-raw.log" ${KASEKI_RESULTS_DIR}/validation-baseline-raw.log 2>/dev/null; then
2628
2717
  return 1
2629
2718
  fi
2630
- if ! cp "$cache_dir/validation-timings.tsv" /results/validation-baseline-timings.tsv 2>/dev/null; then
2719
+ if ! cp "$cache_dir/validation-timings.tsv" ${KASEKI_RESULTS_DIR}/validation-baseline-timings.tsv 2>/dev/null; then
2631
2720
  return 1
2632
2721
  fi
2633
2722
  if [ -f "$cache_dir/validation-env.log" ]; then
2634
- cp "$cache_dir/validation-env.log" /results/validation-baseline-env.log 2>/dev/null || true
2723
+ cp "$cache_dir/validation-env.log" ${KASEKI_RESULTS_DIR}/validation-baseline-env.log 2>/dev/null || true
2635
2724
  fi
2636
2725
 
2637
2726
  return 0
@@ -2648,27 +2737,27 @@ save_baseline_validation_to_cache() {
2648
2737
  mkdir -p "$cache_dir" || return 1
2649
2738
 
2650
2739
  # Save validation results to cache
2651
- if [ -f /results/validation-baseline.log ]; then
2652
- cp /results/validation-baseline.log "$cache_dir/validation.log" || return 1
2740
+ if [ -f ${KASEKI_RESULTS_DIR}/validation-baseline.log ]; then
2741
+ cp ${KASEKI_RESULTS_DIR}/validation-baseline.log "$cache_dir/validation.log" || return 1
2653
2742
  fi
2654
2743
 
2655
- if [ -f /results/validation-baseline-raw.log ]; then
2656
- cp /results/validation-baseline-raw.log "$cache_dir/validation-raw.log" || return 1
2744
+ if [ -f ${KASEKI_RESULTS_DIR}/validation-baseline-raw.log ]; then
2745
+ cp ${KASEKI_RESULTS_DIR}/validation-baseline-raw.log "$cache_dir/validation-raw.log" || return 1
2657
2746
  fi
2658
2747
 
2659
- if [ -f /results/validation-baseline-timings.tsv ]; then
2660
- cp /results/validation-baseline-timings.tsv "$cache_dir/validation-timings.tsv" || return 1
2748
+ if [ -f ${KASEKI_RESULTS_DIR}/validation-baseline-timings.tsv ]; then
2749
+ cp ${KASEKI_RESULTS_DIR}/validation-baseline-timings.tsv "$cache_dir/validation-timings.tsv" || return 1
2661
2750
  fi
2662
2751
 
2663
- if [ -f /results/validation-baseline-env.log ]; then
2664
- cp /results/validation-baseline-env.log "$cache_dir/validation-env.log" 2>/dev/null || true
2752
+ if [ -f ${KASEKI_RESULTS_DIR}/validation-baseline-env.log ]; then
2753
+ cp ${KASEKI_RESULTS_DIR}/validation-baseline-env.log "$cache_dir/validation-env.log" 2>/dev/null || true
2665
2754
  fi
2666
2755
 
2667
2756
  return 0
2668
2757
  }
2669
2758
 
2670
2759
  checkout_baseline_repo() {
2671
- local baseline_dir="/workspace-baseline"
2760
+ local baseline_dir="${KASEKI_WORKSPACE_BASELINE_DIR}"
2672
2761
 
2673
2762
  # Clean up any existing baseline
2674
2763
  rm -rf "$baseline_dir" 2>/dev/null || true
@@ -2687,20 +2776,20 @@ checkout_baseline_repo() {
2687
2776
  emit_progress "baseline preparation" "installing baseline dependencies"
2688
2777
  if ! cd "$baseline_dir" && npm ci --prefer-offline 2>>"$KASEKI_LOG_DIR/baseline-npm-ci.log"; then
2689
2778
  emit_error_event "baseline_deps_failed" "Failed to install baseline dependencies" "continue"
2690
- cd /workspace/repo
2779
+ cd ${KASEKI_WORKSPACE_DIR}/repo
2691
2780
  return 1
2692
2781
  fi
2693
- cd /workspace/repo
2782
+ cd ${KASEKI_WORKSPACE_DIR}/repo
2694
2783
  fi
2695
2784
 
2696
2785
  return 0
2697
2786
  }
2698
2787
 
2699
2788
  run_baseline_validation() {
2700
- local baseline_dir="/workspace-baseline"
2701
- local baseline_log="/results/validation-baseline.log"
2702
- local baseline_raw_log="/results/validation-baseline-raw.log"
2703
- local baseline_timings="/results/validation-baseline-timings.tsv"
2789
+ local baseline_dir="${KASEKI_WORKSPACE_BASELINE_DIR}"
2790
+ local baseline_log="${KASEKI_RESULTS_DIR}/validation-baseline.log"
2791
+ local baseline_raw_log="${KASEKI_RESULTS_DIR}/validation-baseline-raw.log"
2792
+ local baseline_timings="${KASEKI_RESULTS_DIR}/validation-baseline-timings.tsv"
2704
2793
  local baseline_exit_var="BASELINE_VALIDATION_EXIT"
2705
2794
  local baseline_detail_var="BASELINE_VALIDATION_FAILED_COMMAND_DETAIL"
2706
2795
  local baseline_reason_var="BASELINE_VALIDATION_FAILURE_REASON"
@@ -2722,7 +2811,7 @@ run_baseline_validation() {
2722
2811
  "$baseline_log" \
2723
2812
  "$baseline_raw_log" \
2724
2813
  "$baseline_timings" \
2725
- "/results/validation-baseline-env.log" \
2814
+ "${KASEKI_RESULTS_DIR}/validation-baseline-env.log" \
2726
2815
  "baseline_validation_failed" \
2727
2816
  "$baseline_exit_var" \
2728
2817
  "$baseline_detail_var" \
@@ -2742,10 +2831,10 @@ run_baseline_validation() {
2742
2831
  }
2743
2832
 
2744
2833
  analyze_test_failures_baseline() {
2745
- local baseline_log="/results/validation-baseline.log"
2746
- local working_log="/results/pre-validation.log"
2747
- local output_file="/results/test-baseline-comparison.json"
2748
- local results_dir="/results"
2834
+ local baseline_log="${KASEKI_RESULTS_DIR}/validation-baseline.log"
2835
+ local working_log="${KASEKI_RESULTS_DIR}/pre-validation.log"
2836
+ local output_file="${KASEKI_RESULTS_DIR}/test-baseline-comparison.json"
2837
+ local results_dir="${KASEKI_RESULTS_DIR}"
2749
2838
 
2750
2839
  if [ ! -f "$baseline_log" ] || [ ! -f "$working_log" ]; then
2751
2840
  emit_progress "test failure analysis" "skipped (baseline or working log missing)"
@@ -2786,11 +2875,11 @@ analyze_test_failures_baseline() {
2786
2875
  }
2787
2876
 
2788
2877
  analyze_validation_failure_causality() {
2789
- local baseline_log="/results/validation-baseline.log"
2790
- local post_change_log="/results/validation.log"
2791
- local git_diff="/results/git.diff"
2792
- local changed_files="/results/changed-files.txt"
2793
- local output_file="/results/validation-causality-analysis.json"
2878
+ local baseline_log="${KASEKI_RESULTS_DIR}/validation-baseline.log"
2879
+ local post_change_log="${KASEKI_RESULTS_DIR}/validation.log"
2880
+ local git_diff="${KASEKI_RESULTS_DIR}/git.diff"
2881
+ local changed_files="${KASEKI_RESULTS_DIR}/changed-files.txt"
2882
+ local output_file="${KASEKI_RESULTS_DIR}/validation-causality-analysis.json"
2794
2883
 
2795
2884
  # Skip if no baseline (first run)
2796
2885
  if [ ! -f "$baseline_log" ]; then
@@ -2936,13 +3025,13 @@ run_validation_commands() {
2936
3025
  record_stage_timing "$stage_label" 0 0 "skipped_by_config"
2937
3026
  else
2938
3027
  # Checkpoint: Verify working directory exists before validation.
2939
- if ! [ -d /workspace/repo ]; then
2940
- printf 'ERROR: Working directory /workspace/repo does not exist before %s\n' "$stage_label" | tee -a "$log_file"
3028
+ if ! [ -d ${KASEKI_WORKSPACE_DIR}/repo ]; then
3029
+ printf 'ERROR: Working directory ${KASEKI_WORKSPACE_DIR}/repo does not exist before %s\n' "$stage_label" | tee -a "$log_file"
2941
3030
  printf 'Current pwd: %s\n' "$(pwd 2>&1 || echo '<pwd failed>')" | tee -a "$log_file"
2942
3031
  printf 'Filesystem state:\n' | tee -a "$log_file"
2943
3032
  find /workspace -maxdepth 3 -type f 2>&1 | head -100 | tee -a "$log_file"
2944
3033
  validation_exit_ref=1
2945
- validation_detail_ref="Working directory /workspace/repo missing before $stage_label"
3034
+ validation_detail_ref="Working directory ${KASEKI_WORKSPACE_DIR}/repo missing before $stage_label"
2946
3035
  validation_reason_ref="$failure_reason_prefix: workspace_missing"
2947
3036
  record_stage_timing "$stage_label" "$validation_exit_ref" "$(($(date +%s) - stage_start))" "directory_missing"
2948
3037
  else
@@ -2968,7 +3057,7 @@ run_validation_commands() {
2968
3057
  printf '[validation command] working_directory=%s\n' "$(pwd 2>&1 || echo '<pwd failed>')"
2969
3058
  printf '[validation command] node_version=%s\n' "$(node --version 2>&1 || echo '<node not found>')"
2970
3059
  printf '[validation command] npm_version=%s\n' "$(npm --version 2>&1 || echo '<npm not found>')"
2971
- printf '[validation command] disk_available=%s\n' "$(df -h /results 2>/dev/null | tail -1 | awk '{print $4}' || echo '<df failed>')"
3060
+ printf '[validation command] disk_available=%s\n' "$(df -h ${KASEKI_RESULTS_DIR} 2>/dev/null | tail -1 | awk '{print $4}' || echo '<df failed>')"
2972
3061
  } | tee -a "$env_log"
2973
3062
  # Use pipefail to catch errors in any stage of the pipe.
2974
3063
  pipefail_was_enabled=0
@@ -3030,7 +3119,7 @@ run_validation_commands() {
3030
3119
  {
3031
3120
  printf '\n[DIAGNOSTICS] Validation pipeline stderr from filter/tee (last 50 lines):\n'
3032
3121
  printf '%s\n' "$FILTER_STDERR_TAIL"
3033
- } | tee -a "$log_file" /results/quality.log
3122
+ } | tee -a "$log_file" ${KASEKI_RESULTS_DIR}/quality.log
3034
3123
  {
3035
3124
  printf '\n[validation pipeline stderr tail]\n'
3036
3125
  printf '%s\n' "$FILTER_STDERR_TAIL"
@@ -3058,7 +3147,7 @@ run_validation_commands() {
3058
3147
  else
3059
3148
  printf ' (No stderr captured from filter/tee)\n'
3060
3149
  fi
3061
- } | tee -a "$log_file" /results/quality.log "$FILTER_DIAGNOSTICS_LOG"
3150
+ } | tee -a "$log_file" ${KASEKI_RESULTS_DIR}/quality.log "$FILTER_DIAGNOSTICS_LOG"
3062
3151
  fi
3063
3152
 
3064
3153
  if [ "$validation_infra_failure" = "true" ] && [ "$validation_exit_ref" -eq 0 ]; then
@@ -3082,13 +3171,13 @@ run_validation_commands() {
3082
3171
  printf '\n[DIAGNOSTICS] Validation command failed with directory access error:\n'
3083
3172
  printf 'Working directory status:\n'
3084
3173
  printf ' Current pwd: %s\n' "$(pwd 2>&1 || echo '<pwd failed>')"
3085
- printf ' /workspace/repo exists: %s\n' "$([ -d /workspace/repo ] && echo 'yes' || echo 'no')"
3086
- if [ -L /workspace/repo/node_modules ]; then
3087
- printf ' node_modules is symlink → %s\n' "$(readlink /workspace/repo/node_modules 2>&1 || echo '<readlink failed>')"
3174
+ printf ' ${KASEKI_WORKSPACE_DIR}/repo exists: %s\n' "$([ -d ${KASEKI_WORKSPACE_DIR}/repo ] && echo 'yes' || echo 'no')"
3175
+ if [ -L ${KASEKI_WORKSPACE_DIR}/repo/node_modules ]; then
3176
+ printf ' node_modules is symlink → %s\n' "$(readlink ${KASEKI_WORKSPACE_DIR}/repo/node_modules 2>&1 || echo '<readlink failed>')"
3088
3177
  fi
3089
3178
  printf 'Last 20 lines of validation log:\n'
3090
3179
  tail -20 "$log_file"
3091
- } | tee -a /results/quality.log
3180
+ } | tee -a ${KASEKI_RESULTS_DIR}/quality.log
3092
3181
  fi
3093
3182
  # Fail-fast: if enabled, stop validation loop at first failure.
3094
3183
  if [ "$KASEKI_VALIDATION_FAIL_FAST" -eq 1 ]; then
@@ -3169,7 +3258,7 @@ write_repo_memory_summary() {
3169
3258
  return 0
3170
3259
  fi
3171
3260
  local updated_at
3172
- REPO_MEMORY_COMMIT_SHA="$(git -C /workspace/repo rev-parse HEAD 2>/dev/null || printf 'unknown')"
3261
+ REPO_MEMORY_COMMIT_SHA="$(git -C ${KASEKI_WORKSPACE_DIR}/repo rev-parse HEAD 2>/dev/null || printf 'unknown')"
3173
3262
  updated_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
3174
3263
  node - "$KASEKI_REPO_MEMORY_MAX_BYTES" "$REPO_MEMORY_FILE" "$KASEKI_RESULTS_DIR" "$REPO_URL" "$GIT_REF" "$REPO_MEMORY_COMMIT_SHA" "$updated_at" "$KASEKI_TASK_MODE" "$STATUS" "$PI_EXIT" "$VALIDATION_EXIT" "$QUALITY_EXIT" "$SECRET_SCAN_EXIT" <<'NODE' || {
3175
3264
  const fs = require('fs');
@@ -3349,9 +3438,9 @@ is_transient_goal_setting_failure() {
3349
3438
  local stderr_content="$2"
3350
3439
 
3351
3440
  # First, check if we have an explicit validation reason code from our helper
3352
- if [ -f /results/goal-setting-validation-reason.txt ]; then
3441
+ if [ -f ${KASEKI_RESULTS_DIR}/goal-setting-validation-reason.txt ]; then
3353
3442
  local reason_code
3354
- reason_code=$(cat /results/goal-setting-validation-reason.txt 2>/dev/null || echo "")
3443
+ reason_code=$(cat ${KASEKI_RESULTS_DIR}/goal-setting-validation-reason.txt 2>/dev/null || echo "")
3355
3444
  case "$reason_code" in
3356
3445
  valid)
3357
3446
  return 1
@@ -3530,7 +3619,7 @@ validate_goal_setting_artifact() {
3530
3619
  local candidate_artifact="$1"
3531
3620
  local final_artifact="$2"
3532
3621
  local reason_file="$3"
3533
- local results_dir="${KASEKI_RESULTS_DIR:-/results}"
3622
+ local results_dir="${KASEKI_RESULTS_DIR:-${KASEKI_RESULTS_DIR}}"
3534
3623
 
3535
3624
  if ! [ -f "$candidate_artifact" ]; then
3536
3625
  {
@@ -3570,7 +3659,7 @@ validate_goal_setting_artifact() {
3570
3659
  validate_goal_setting_artifact_with_node() {
3571
3660
  local candidate_artifact="$1"
3572
3661
  local reason_file="$2"
3573
- local results_dir="${KASEKI_RESULTS_DIR:-/results}"
3662
+ local results_dir="${KASEKI_RESULTS_DIR:-${KASEKI_RESULTS_DIR}}"
3574
3663
 
3575
3664
  local validation_output
3576
3665
  validation_output=$(node -e "
@@ -3755,13 +3844,13 @@ run_goal_setting_agent() {
3755
3844
  set_current_stage "pi goal-setting agent"
3756
3845
 
3757
3846
  if [ "$KASEKI_GOAL_SETTING" = "0" ]; then
3758
- printf 'Pi goal-setting agent skipped because KASEKI_GOAL_SETTING=0.\n' | tee -a /results/goal-setting-stderr.log
3847
+ printf 'Pi goal-setting agent skipped because KASEKI_GOAL_SETTING=0.\n' | tee -a ${KASEKI_RESULTS_DIR}/goal-setting-stderr.log
3759
3848
  record_stage_timing "pi goal-setting agent" 0 0 "skipped_by_config"
3760
3849
  return 0
3761
3850
  fi
3762
3851
 
3763
3852
  if [ "$KASEKI_DRY_RUN" = "1" ]; then
3764
- printf 'DRY-RUN: Pi goal-setting agent would upgrade the task prompt into a mature goal.\n' | tee -a /results/goal-setting-stderr.log
3853
+ printf 'DRY-RUN: Pi goal-setting agent would upgrade the task prompt into a mature goal.\n' | tee -a ${KASEKI_RESULTS_DIR}/goal-setting-stderr.log
3765
3854
  record_stage_timing "pi goal-setting agent" 0 0 "dry_run=true"
3766
3855
  return 0
3767
3856
  fi
@@ -3773,23 +3862,23 @@ run_goal_setting_agent() {
3773
3862
  OPENROUTER_API_KEY="$openrouter_api_key" \
3774
3863
  timeout --signal=SIGTERM "$KASEKI_GOAL_SETTING_TIMEOUT_SECONDS" \
3775
3864
  pi --mode json --no-session --provider "$KASEKI_PROVIDER" --model "$KASEKI_GOAL_SETTING_MODEL" "$goal_setting_prompt" \
3776
- 2> >(tee -a /results/goal-setting-stderr.log >&2) \
3865
+ 2> >(tee -a ${KASEKI_RESULTS_DIR}/goal-setting-stderr.log >&2) \
3777
3866
  | tee "$GOAL_SETTING_RAW_EVENTS" \
3778
- | kaseki-pi-progress-stream /results/progress.jsonl /results/progress.log
3867
+ | kaseki-pi-progress-stream ${KASEKI_RESULTS_DIR}/progress.jsonl ${KASEKI_RESULTS_DIR}/progress.log
3779
3868
  GOAL_SETTING_EXIT="${PIPESTATUS[0]}"
3780
3869
  GOAL_SETTING_DURATION_SECONDS=$(($(date +%s) - goal_setting_start))
3781
3870
  unset goal_setting_prompt
3782
3871
  set +e
3783
3872
 
3784
- if [ "$GOAL_SETTING_EXIT" -eq 0 ] && ! validate_goal_setting_artifact "$GOAL_SETTING_CANDIDATE_ARTIFACT" "$GOAL_SETTING_ARTIFACT" "/results/goal-setting-validation-reason.txt"; then
3873
+ if [ "$GOAL_SETTING_EXIT" -eq 0 ] && ! validate_goal_setting_artifact "$GOAL_SETTING_CANDIDATE_ARTIFACT" "$GOAL_SETTING_ARTIFACT" "${KASEKI_RESULTS_DIR}/goal-setting-validation-reason.txt"; then
3785
3874
  GOAL_SETTING_EXIT=86
3786
- goal_setting_validation_summary="$(cat /results/goal-setting-validation-summary.txt 2>/dev/null || printf 'goal-setting artifact validation failed')"
3787
- emit_error_event "pi_goal_setting_artifact_invalid" "Pi goal-setting artifact invalid: $goal_setting_validation_summary (full details: /results/goal-setting-validation-errors.jsonl)" "continue"
3875
+ goal_setting_validation_summary="$(cat ${KASEKI_RESULTS_DIR}/goal-setting-validation-summary.txt 2>/dev/null || printf 'goal-setting artifact validation failed')"
3876
+ emit_error_event "pi_goal_setting_artifact_invalid" "Pi goal-setting artifact invalid: $goal_setting_validation_summary (full details: ${KASEKI_RESULTS_DIR}/goal-setting-validation-errors.jsonl)" "continue"
3788
3877
  fi
3789
3878
 
3790
3879
  rm -f "$GOAL_SETTING_CANDIDATE_ARTIFACT"
3791
- kaseki-pi-event-filter "$GOAL_SETTING_RAW_EVENTS" /results/goal-setting-events.jsonl /results/goal-setting-summary.json 2>> /results/goal-setting-stderr.log || cp "$GOAL_SETTING_RAW_EVENTS" /results/goal-setting-events.raw.jsonl 2>/dev/null || true
3792
- GOAL_SETTING_ACTUAL_MODEL="$(node -e 'try { const s=require("/results/goal-setting-summary.json"); const v=String(s.selected_model || s.model || "").trim(); console.log(v && v !== "unknown" && v !== "null" ? v : "unknown"); } catch { console.log("unknown"); }' 2>/dev/null)"
3880
+ kaseki-pi-event-filter "$GOAL_SETTING_RAW_EVENTS" ${KASEKI_RESULTS_DIR}/goal-setting-events.jsonl ${KASEKI_RESULTS_DIR}/goal-setting-summary.json 2>> ${KASEKI_RESULTS_DIR}/goal-setting-stderr.log || cp "$GOAL_SETTING_RAW_EVENTS" ${KASEKI_RESULTS_DIR}/goal-setting-events.raw.jsonl 2>/dev/null || true
3881
+ GOAL_SETTING_ACTUAL_MODEL="$(node -e 'try { const s=require(process.env.KASEKI_RESULTS_DIR + "/goal-setting-summary.json"); const v=String(s.selected_model || s.model || "").trim(); console.log(v && v !== "unknown" && v !== "null" ? v : "unknown"); } catch { console.log("unknown"); }' 2>/dev/null)"
3793
3882
 
3794
3883
  record_stage_timing "pi goal-setting agent" "$GOAL_SETTING_EXIT" "$GOAL_SETTING_DURATION_SECONDS" "artifact=$GOAL_SETTING_ARTIFACT timeout_seconds=$KASEKI_GOAL_SETTING_TIMEOUT_SECONDS"
3795
3884
 
@@ -3799,7 +3888,7 @@ run_goal_setting_agent() {
3799
3888
  fi
3800
3889
 
3801
3890
  emit_progress "pi goal-setting agent" "wrote goal-setting artifact"
3802
- rm -f /results/goal-setting-validation-reason.txt 2>/dev/null || true
3891
+ rm -f ${KASEKI_RESULTS_DIR}/goal-setting-validation-reason.txt 2>/dev/null || true
3803
3892
 
3804
3893
  return 0
3805
3894
  }
@@ -3834,7 +3923,7 @@ write_goal_setting_metrics() {
3834
3923
  model: '${GOAL_SETTING_ACTUAL_MODEL:-unknown}',
3835
3924
  timeout_seconds: ${KASEKI_GOAL_SETTING_TIMEOUT_SECONDS:-300}
3836
3925
  };
3837
- fs.writeFileSync('/results/goal-setting-metrics.json', JSON.stringify(metrics, null, 2) + '\n');
3926
+ fs.writeFileSync('${KASEKI_RESULTS_DIR}/goal-setting-metrics.json', JSON.stringify(metrics, null, 2) + '\n');
3838
3927
  " 2>/dev/null || {
3839
3928
  # Fallback to jq or printf if node fails
3840
3929
  {
@@ -3850,7 +3939,7 @@ write_goal_setting_metrics() {
3850
3939
  printf ' "model": "%s",\n' "${GOAL_SETTING_ACTUAL_MODEL:-unknown}"
3851
3940
  printf ' "timeout_seconds": %d\n' "${KASEKI_GOAL_SETTING_TIMEOUT_SECONDS:-300}"
3852
3941
  printf '}\n'
3853
- } > /results/goal-setting-metrics.json
3942
+ } > ${KASEKI_RESULTS_DIR}/goal-setting-metrics.json
3854
3943
  }
3855
3944
  }
3856
3945
 
@@ -3859,9 +3948,9 @@ classify_goal_setting_error() {
3859
3948
  local stderr_content="$2"
3860
3949
 
3861
3950
  # Check validation reason file first (most authoritative)
3862
- if [ -f /results/goal-setting-validation-reason.txt ]; then
3951
+ if [ -f ${KASEKI_RESULTS_DIR}/goal-setting-validation-reason.txt ]; then
3863
3952
  local reason_code
3864
- reason_code=$(cat /results/goal-setting-validation-reason.txt 2>/dev/null || echo "")
3953
+ reason_code=$(cat ${KASEKI_RESULTS_DIR}/goal-setting-validation-reason.txt 2>/dev/null || echo "")
3865
3954
  case "$reason_code" in
3866
3955
  schema_mismatch)
3867
3956
  echo "GOAL_SETTING_SCHEMA_MISMATCH"
@@ -3940,7 +4029,7 @@ run_goal_setting_agent_with_retry() {
3940
4029
  set -e
3941
4030
 
3942
4031
  # Append stderr to results for logging
3943
- cat "$goal_setting_stderr_capture" >> /results/goal-setting-stderr.log 2>/dev/null || true
4032
+ cat "$goal_setting_stderr_capture" >> ${KASEKI_RESULTS_DIR}/goal-setting-stderr.log 2>/dev/null || true
3944
4033
  goal_setting_last_stderr="$(cat "$goal_setting_stderr_capture" 2>/dev/null || true)"
3945
4034
  rm -f "$goal_setting_stderr_capture"
3946
4035
 
@@ -3972,7 +4061,7 @@ run_goal_setting_agent_with_retry() {
3972
4061
  attempt=$((attempt + 1))
3973
4062
  # Reset goal-setting artifacts for retry
3974
4063
  rm -f "$GOAL_SETTING_ARTIFACT" "$GOAL_SETTING_RAW_EVENTS" 2>/dev/null || true
3975
- rm -f /results/goal-setting-validation-reason.txt 2>/dev/null || true
4064
+ rm -f ${KASEKI_RESULTS_DIR}/goal-setting-validation-reason.txt 2>/dev/null || true
3976
4065
  continue
3977
4066
  fi
3978
4067
  else
@@ -4006,9 +4095,9 @@ is_transient_scouting_failure() {
4006
4095
  local stderr_content="$2"
4007
4096
 
4008
4097
  # First, check if we have an explicit validation reason code from our helper
4009
- if [ -f /results/scouting-validation-reason.txt ]; then
4098
+ if [ -f ${KASEKI_RESULTS_DIR}/scouting-validation-reason.txt ]; then
4010
4099
  local reason_code
4011
- reason_code=$(cat /results/scouting-validation-reason.txt 2>/dev/null || echo "")
4100
+ reason_code=$(cat ${KASEKI_RESULTS_DIR}/scouting-validation-reason.txt 2>/dev/null || echo "")
4012
4101
  case "$reason_code" in
4013
4102
  valid)
4014
4103
  # This shouldn't happen when exit_code=86, but just in case
@@ -4143,48 +4232,48 @@ run_scouting_agent() {
4143
4232
  printf '\n==> pi scouting agent\n'
4144
4233
  set_current_stage "pi scouting agent"
4145
4234
  if [ "$KASEKI_SCOUTING" = "0" ]; then
4146
- printf 'Pi scouting agent skipped because KASEKI_SCOUTING=0.\n' | tee -a /results/scouting-stderr.log
4235
+ printf 'Pi scouting agent skipped because KASEKI_SCOUTING=0.\n' | tee -a ${KASEKI_RESULTS_DIR}/scouting-stderr.log
4147
4236
  record_stage_timing "pi scouting agent" 0 0 "skipped_by_config"
4148
4237
  return 0
4149
4238
  fi
4150
4239
  if [ "$KASEKI_DRY_RUN" = "1" ]; then
4151
- printf 'DRY-RUN: Pi scouting agent would inspect the task before coding.\n' | tee -a /results/scouting-stderr.log
4240
+ printf 'DRY-RUN: Pi scouting agent would inspect the task before coding.\n' | tee -a ${KASEKI_RESULTS_DIR}/scouting-stderr.log
4152
4241
  record_stage_timing "pi scouting agent" 0 0 "dry_run=true"
4153
4242
  return 0
4154
4243
  fi
4155
4244
 
4156
4245
  scouting_prompt="$(build_scouting_prompt)"
4157
4246
  scouting_start="$(date +%s)"
4158
- scout_dirty_before="$(git status --porcelain 2>> /results/scouting-stderr.log || true)"
4159
- chmod -R a-w /workspace/repo 2>> /results/scouting-stderr.log || true
4247
+ scout_dirty_before="$(git status --porcelain 2>> ${KASEKI_RESULTS_DIR}/scouting-stderr.log || true)"
4248
+ chmod -R a-w ${KASEKI_WORKSPACE_DIR}/repo 2>> ${KASEKI_RESULTS_DIR}/scouting-stderr.log || true
4160
4249
  set +e
4161
4250
  OPENROUTER_API_KEY="$openrouter_api_key" \
4162
4251
  timeout --signal=SIGTERM "$KASEKI_SCOUTING_TIMEOUT_SECONDS" \
4163
4252
  pi --mode json --no-session --provider "$KASEKI_PROVIDER" --model "$KASEKI_SCOUTING_MODEL" "$scouting_prompt" \
4164
- 2> >(tee -a /results/scouting-stderr.log >&2) \
4253
+ 2> >(tee -a ${KASEKI_RESULTS_DIR}/scouting-stderr.log >&2) \
4165
4254
  | tee "$SCOUTING_RAW_EVENTS" \
4166
- | kaseki-pi-progress-stream /results/progress.jsonl /results/progress.log
4255
+ | kaseki-pi-progress-stream ${KASEKI_RESULTS_DIR}/progress.jsonl ${KASEKI_RESULTS_DIR}/progress.log
4167
4256
  SCOUTING_EXIT="${PIPESTATUS[0]}"
4168
4257
  SCOUTING_DURATION_SECONDS=$(($(date +%s) - scouting_start))
4169
4258
  unset scouting_prompt
4170
4259
  set +e
4171
- chmod -R u+w /workspace/repo 2>> /results/scouting-stderr.log || true
4260
+ chmod -R u+w ${KASEKI_WORKSPACE_DIR}/repo 2>> ${KASEKI_RESULTS_DIR}/scouting-stderr.log || true
4172
4261
 
4173
- if [ "$SCOUTING_EXIT" -eq 0 ] && ! validate_scouting_artifact "$SCOUTING_CANDIDATE_ARTIFACT" "$SCOUTING_ARTIFACT" "/results/scouting-validation-reason.txt"; then
4262
+ if [ "$SCOUTING_EXIT" -eq 0 ] && ! validate_scouting_artifact "$SCOUTING_CANDIDATE_ARTIFACT" "$SCOUTING_ARTIFACT" "${KASEKI_RESULTS_DIR}/scouting-validation-reason.txt"; then
4174
4263
  SCOUTING_EXIT=86
4175
- scouting_validation_summary="$(cat /results/scouting-validation-summary.txt 2>/dev/null || printf 'scouting artifact validation failed')"
4176
- emit_error_event "pi_scouting_artifact_invalid" "Pi scouting handoff invalid: $scouting_validation_summary (full details: /results/scouting-validation-errors.jsonl)" "exit"
4264
+ scouting_validation_summary="$(cat ${KASEKI_RESULTS_DIR}/scouting-validation-summary.txt 2>/dev/null || printf 'scouting artifact validation failed')"
4265
+ emit_error_event "pi_scouting_artifact_invalid" "Pi scouting handoff invalid: $scouting_validation_summary (full details: ${KASEKI_RESULTS_DIR}/scouting-validation-errors.jsonl)" "exit"
4177
4266
  fi
4178
- scout_dirty_after="$(git status --porcelain 2>> /results/scouting-stderr.log || true)"
4267
+ scout_dirty_after="$(git status --porcelain 2>> ${KASEKI_RESULTS_DIR}/scouting-stderr.log || true)"
4179
4268
  if [ "$SCOUTING_EXIT" -eq 0 ] && [ "$scout_dirty_before" != "$scout_dirty_after" ]; then
4180
4269
  SCOUTING_EXIT=86
4181
4270
  emit_error_event "pi_scouting_workspace_modified" "Read-only scouting changed repository state before coding" "exit"
4182
4271
  fi
4183
4272
  rm -f "$SCOUTING_CANDIDATE_ARTIFACT"
4184
- git reset --hard -q HEAD 2>> /results/scouting-stderr.log || true
4185
- git clean -fd -q 2>> /results/scouting-stderr.log || true
4186
- kaseki-pi-event-filter "$SCOUTING_RAW_EVENTS" /results/scouting-events.jsonl /results/scouting-summary.json 2>> /results/scouting-stderr.log || cp "$SCOUTING_RAW_EVENTS" /results/scouting-events.raw.jsonl 2>/dev/null || true
4187
- SCOUTING_ACTUAL_MODEL="$(node -e 'try { const s=require("/results/scouting-summary.json"); const v=String(s.selected_model || s.model || "").trim(); console.log(v && v !== "unknown" && v !== "null" ? v : "unknown"); } catch { console.log("unknown"); }' 2>/dev/null)"
4273
+ git reset --hard -q HEAD 2>> ${KASEKI_RESULTS_DIR}/scouting-stderr.log || true
4274
+ git clean -fd -q 2>> ${KASEKI_RESULTS_DIR}/scouting-stderr.log || true
4275
+ kaseki-pi-event-filter "$SCOUTING_RAW_EVENTS" ${KASEKI_RESULTS_DIR}/scouting-events.jsonl ${KASEKI_RESULTS_DIR}/scouting-summary.json 2>> ${KASEKI_RESULTS_DIR}/scouting-stderr.log || cp "$SCOUTING_RAW_EVENTS" ${KASEKI_RESULTS_DIR}/scouting-events.raw.jsonl 2>/dev/null || true
4276
+ SCOUTING_ACTUAL_MODEL="$(node -e 'try { const s=require(process.env.KASEKI_RESULTS_DIR + "/scouting-summary.json"); const v=String(s.selected_model || s.model || "").trim(); console.log(v && v !== "unknown" && v !== "null" ? v : "unknown"); } catch { console.log("unknown"); }' 2>/dev/null)"
4188
4277
  record_stage_timing "pi scouting agent" "$SCOUTING_EXIT" "$SCOUTING_DURATION_SECONDS" "artifact=$SCOUTING_ARTIFACT timeout_seconds=$KASEKI_SCOUTING_TIMEOUT_SECONDS"
4189
4278
  if [ "$SCOUTING_EXIT" -ne 0 ]; then
4190
4279
  STATUS="$SCOUTING_EXIT"
@@ -4194,7 +4283,7 @@ run_scouting_agent() {
4194
4283
  fi
4195
4284
  emit_progress "pi scouting agent" "wrote scouting artifact"
4196
4285
  # Clean up validation reason file on success
4197
- rm -f /results/scouting-validation-reason.txt 2>/dev/null || true
4286
+ rm -f ${KASEKI_RESULTS_DIR}/scouting-validation-reason.txt 2>/dev/null || true
4198
4287
  return 0
4199
4288
  }
4200
4289
 
@@ -4222,7 +4311,7 @@ run_scouting_agent_with_retry() {
4222
4311
  set -e
4223
4312
 
4224
4313
  # Append stderr to results for logging
4225
- cat "$scouting_stderr_capture" >> /results/scouting-stderr.log 2>/dev/null || true
4314
+ cat "$scouting_stderr_capture" >> ${KASEKI_RESULTS_DIR}/scouting-stderr.log 2>/dev/null || true
4226
4315
  scouting_last_stderr="$(cat "$scouting_stderr_capture" 2>/dev/null || true)"
4227
4316
  rm -f "$scouting_stderr_capture"
4228
4317
 
@@ -4241,7 +4330,7 @@ run_scouting_agent_with_retry() {
4241
4330
  # Reset scouting artifacts for retry
4242
4331
  rm -f "$SCOUTING_ARTIFACT" "$SCOUTING_RAW_EVENTS" 2>/dev/null || true
4243
4332
  # Clean up validation reason file from previous attempt
4244
- rm -f /results/scouting-validation-reason.txt 2>/dev/null || true
4333
+ rm -f ${KASEKI_RESULTS_DIR}/scouting-validation-reason.txt 2>/dev/null || true
4245
4334
  continue
4246
4335
  fi
4247
4336
  else
@@ -4265,13 +4354,13 @@ run_scouting_agent_with_retry() {
4265
4354
 
4266
4355
  snapshot_attempt_artifacts() {
4267
4356
  local attempt_dir
4268
- attempt_dir="/results/attempt-$1"
4357
+ attempt_dir="${KASEKI_RESULTS_DIR}/attempt-$1"
4269
4358
  mkdir -p "$attempt_dir" 2>/dev/null || return 0
4270
4359
  for artifact in \
4271
4360
  pi-events.jsonl pi-summary.json pi-stderr.log git.diff git.status changed-files.txt \
4272
4361
  quality.log validation.log validation-raw.log validation-timings.tsv goal-check.json; do
4273
- if [ -e "/results/$artifact" ]; then
4274
- cp "/results/$artifact" "$attempt_dir/$artifact" 2>/dev/null || true
4362
+ if [ -e "${KASEKI_RESULTS_DIR}/$artifact" ]; then
4363
+ cp "${KASEKI_RESULTS_DIR}/$artifact" "$attempt_dir/$artifact" 2>/dev/null || true
4275
4364
  fi
4276
4365
  done
4277
4366
  }
@@ -4279,7 +4368,7 @@ snapshot_attempt_artifacts() {
4279
4368
  collect_goal_check_feedback() {
4280
4369
  local instance_name="$1"
4281
4370
  local goal_setting_path="$GOAL_SETTING_ARTIFACT"
4282
- local results_dir="${KASEKI_RESULTS_DIR:-/results}"
4371
+ local results_dir="${KASEKI_RESULTS_DIR:-${KASEKI_RESULTS_DIR}}"
4283
4372
  local goal_check_path="$results_dir/goal-check.json"
4284
4373
  local metadata_path="$results_dir/metadata.json"
4285
4374
  local feedback_file="$results_dir/goal-feedback.jsonl"
@@ -4295,9 +4384,9 @@ collect_goal_check_feedback() {
4295
4384
 
4296
4385
  collect_run_evaluation_feedback() {
4297
4386
  local instance_name="$1"
4298
- local run_evaluation_path="/results/run-evaluation.json"
4299
- local metadata_path="/results/metadata.json"
4300
- local feedback_file="/results/kaseki-improvements.jsonl"
4387
+ local run_evaluation_path="${KASEKI_RESULTS_DIR}/run-evaluation.json"
4388
+ local metadata_path="${KASEKI_RESULTS_DIR}/metadata.json"
4389
+ local feedback_file="${KASEKI_RESULTS_DIR}/kaseki-improvements.jsonl"
4301
4390
 
4302
4391
  # Only collect if run-evaluation succeeded and artifacts exist
4303
4392
  if [ ! -f "$run_evaluation_path" ] || [ "$RUN_EVALUATION_EXIT" -ne 0 ]; then
@@ -4311,14 +4400,14 @@ collect_run_evaluation_feedback() {
4311
4400
 
4312
4401
  build_goal_check_prompt() {
4313
4402
  local validation_tail progress_tail goal_setting_context validation_context test_impact_context causality_context
4314
- validation_tail="$(tail -80 /results/validation.log 2>/dev/null || true)"
4403
+ validation_tail="$(tail -80 ${KASEKI_RESULTS_DIR}/validation.log 2>/dev/null || true)"
4315
4404
  if [ -n "$(printf '%s' "$validation_tail" | tr -d '[:space:]')" ]; then
4316
4405
  validation_context="Validation log tail (last 80 lines):
4317
4406
  $validation_tail"
4318
4407
  else
4319
- validation_context="Validation log: /results/validation.log is empty or has not been produced yet. Treat validation logs as optional evidence for this pre-validation check; rely on the goal-setting output, scouting output, changed files, and git diff to determine whether the goal requirements are satisfied."
4408
+ validation_context="Validation log: ${KASEKI_RESULTS_DIR}/validation.log is empty or has not been produced yet. Treat validation logs as optional evidence for this pre-validation check; rely on the goal-setting output, scouting output, changed files, and git diff to determine whether the goal requirements are satisfied."
4320
4409
  fi
4321
- progress_tail="$(tail -80 /results/progress.log 2>/dev/null || true)"
4410
+ progress_tail="$(tail -80 ${KASEKI_RESULTS_DIR}/progress.log 2>/dev/null || true)"
4322
4411
  if [ -s "$TEST_IMPACT_WARNINGS_ARTIFACT" ]; then
4323
4412
  test_impact_context="Static test-impact warnings artifact ($TEST_IMPACT_WARNINGS_ARTIFACT):
4324
4413
  $(cat "$TEST_IMPACT_WARNINGS_ARTIFACT" 2>/dev/null)
@@ -4344,7 +4433,7 @@ $(head -n 200 "$GOAL_SETTING_ARTIFACT" 2>/dev/null)
4344
4433
  fi
4345
4434
 
4346
4435
  # Include causality assessment if available (helps interpret validation failures)
4347
- if [ -f /results/validation-causality-analysis.json ]; then
4436
+ if [ -f ${KASEKI_RESULTS_DIR}/validation-causality-analysis.json ]; then
4348
4437
  # shellcheck disable=SC2016
4349
4438
  causality_context="VALIDATION FAILURE CAUSALITY ASSESSMENT:
4350
4439
 
@@ -4404,11 +4493,11 @@ Determine if the agent successfully met the requirements specified in the goal-s
4404
4493
 
4405
4494
  **Agent Artifacts** (use to verify requirements were met):
4406
4495
  - Scouting report: $SCOUTING_ARTIFACT
4407
- - Changed files: /results/changed-files.txt
4408
- - Git diff: /results/git.diff
4409
- - Validation outcomes (optional evidence; may be absent during pre-validation checks): /results/validation-timings.tsv and /results/validation.log
4496
+ - Changed files: ${KASEKI_RESULTS_DIR}/changed-files.txt
4497
+ - Git diff: ${KASEKI_RESULTS_DIR}/git.diff
4498
+ - Validation outcomes (optional evidence; may be absent during pre-validation checks): ${KASEKI_RESULTS_DIR}/validation-timings.tsv and ${KASEKI_RESULTS_DIR}/validation.log
4410
4499
  - Static test-impact warnings (non-blocking): $TEST_IMPACT_WARNINGS_ARTIFACT
4411
- - Coding-agent events: /results/pi-summary.json and /results/pi-events.jsonl
4500
+ - Coding-agent events: ${KASEKI_RESULTS_DIR}/pi-summary.json and ${KASEKI_RESULTS_DIR}/pi-events.jsonl
4412
4501
 
4413
4502
  ## Evaluation Framework: SMART Criteria Check
4414
4503
 
@@ -4467,7 +4556,7 @@ Example: "Null handling is done (parseRole returns 'Unnamed Role'), but test cov
4467
4556
  - Do not print, inspect, or expose environment variables, secrets, credentials, API keys, or mounted secret files.
4468
4557
  - Decide whether the goal requirements were realized. Do not evaluate code style, architecture, or elegance.
4469
4558
  - If anti-patterns were specified in goal-setting (do_not_modify, do_not_break), verify they were respected.
4470
- - Validation logs are optional evidence. If /results/validation.log is empty or absent, do not fail solely because validation evidence is unavailable; rely on goal-setting output, scouting output, changed files, and git diff.
4559
+ - Validation logs are optional evidence. If ${KASEKI_RESULTS_DIR}/validation.log is empty or absent, do not fail solely because validation evidence is unavailable; rely on goal-setting output, scouting output, changed files, and git diff.
4471
4560
 
4472
4561
  ## Required JSON artifact
4473
4562
 
@@ -4518,12 +4607,12 @@ run_goal_check() {
4518
4607
  printf '\n==> goal check\n'
4519
4608
  set_current_stage "goal check"
4520
4609
  if [ "$KASEKI_GOAL_CHECK" != "1" ]; then
4521
- printf 'Goal check skipped because KASEKI_GOAL_CHECK=%s.\n' "$KASEKI_GOAL_CHECK" | tee -a /results/goal-check-stderr.log
4610
+ printf 'Goal check skipped because KASEKI_GOAL_CHECK=%s.\n' "$KASEKI_GOAL_CHECK" | tee -a ${KASEKI_RESULTS_DIR}/goal-check-stderr.log
4522
4611
  record_stage_timing "goal check" 0 0 "skipped_by_config attempt=$attempt"
4523
4612
  return 0
4524
4613
  fi
4525
4614
  if [ ! -s "$SCOUTING_ARTIFACT" ]; then
4526
- printf 'Goal check skipped because scouting artifact is unavailable.\n' | tee -a /results/goal-check-stderr.log
4615
+ printf 'Goal check skipped because scouting artifact is unavailable.\n' | tee -a ${KASEKI_RESULTS_DIR}/goal-check-stderr.log
4527
4616
  record_stage_timing "goal check" 0 0 "skipped_no_scouting attempt=$attempt"
4528
4617
  return 0
4529
4618
  fi
@@ -4534,15 +4623,15 @@ run_goal_check() {
4534
4623
  OPENROUTER_API_KEY="$openrouter_api_key" \
4535
4624
  timeout --signal=SIGTERM "$KASEKI_GOAL_CHECK_TIMEOUT_SECONDS" \
4536
4625
  pi --mode json --no-session --provider "$KASEKI_PROVIDER" --model "$KASEKI_GOAL_CHECK_MODEL" "$goal_prompt" \
4537
- 2> >(tee -a /results/goal-check-stderr.log >&2) \
4626
+ 2> >(tee -a ${KASEKI_RESULTS_DIR}/goal-check-stderr.log >&2) \
4538
4627
  | tee "$GOAL_CHECK_RAW_EVENTS" \
4539
- | kaseki-pi-progress-stream /results/progress.jsonl /results/progress.log
4628
+ | kaseki-pi-progress-stream ${KASEKI_RESULTS_DIR}/progress.jsonl ${KASEKI_RESULTS_DIR}/progress.log
4540
4629
  GOAL_CHECK_EXIT="${PIPESTATUS[0]}"
4541
4630
  unset goal_prompt
4542
4631
  GOAL_CHECK_DURATION_SECONDS=$((GOAL_CHECK_DURATION_SECONDS + $(date +%s) - goal_start))
4543
4632
  set +e
4544
4633
 
4545
- kaseki-pi-event-filter "$GOAL_CHECK_RAW_EVENTS" /results/goal-check-events.jsonl /results/goal-check-summary.json 2>> /results/goal-check-stderr.log || true
4634
+ kaseki-pi-event-filter "$GOAL_CHECK_RAW_EVENTS" ${KASEKI_RESULTS_DIR}/goal-check-events.jsonl ${KASEKI_RESULTS_DIR}/goal-check-summary.json 2>> ${KASEKI_RESULTS_DIR}/goal-check-stderr.log || true
4546
4635
 
4547
4636
  if [ "$GOAL_CHECK_EXIT" -eq 0 ] && [ ! -f "$GOAL_CHECK_CANDIDATE_ARTIFACT" ]; then
4548
4637
  # Recover from goal-check agents that printed the verdict in assistant text instead of writing the artifact.
@@ -4640,38 +4729,38 @@ if (valid.size === 1) {
4640
4729
  const recovered = [...valid.values()][0];
4641
4730
  fs.writeFileSync(candidatePath, JSON.stringify(recovered, null, 2) + "\n");
4642
4731
  const note = { timestamp: new Date().toISOString(), attempt, event: "goal_check_artifact_recovered_from_assistant_text", artifact: candidatePath, raw_events: rawPath, filtered_events: filteredPath };
4643
- fs.appendFileSync("/results/goal-check-stderr.log", JSON.stringify(note) + "\n");
4732
+ fs.appendFileSync(process.env.KASEKI_RESULTS_DIR + "/goal-check-stderr.log", JSON.stringify(note) + "\n");
4644
4733
  }
4645
- ' "$GOAL_CHECK_CANDIDATE_ARTIFACT" "$GOAL_CHECK_RAW_EVENTS" /results/goal-check-events.jsonl "$attempt" 2>> /results/goal-check-stderr.log || true
4734
+ ' "$GOAL_CHECK_CANDIDATE_ARTIFACT" "$GOAL_CHECK_RAW_EVENTS" ${KASEKI_RESULTS_DIR}/goal-check-events.jsonl "$attempt" 2>> ${KASEKI_RESULTS_DIR}/goal-check-stderr.log || true
4646
4735
  fi
4647
4736
 
4648
- if [ "$GOAL_CHECK_EXIT" -eq 0 ] && ! validate_goal_check_artifact "$GOAL_CHECK_CANDIDATE_ARTIFACT" /results/goal-check.json "$attempt" /results/goal-check-validation-reason.txt; then
4737
+ if [ "$GOAL_CHECK_EXIT" -eq 0 ] && ! validate_goal_check_artifact "$GOAL_CHECK_CANDIDATE_ARTIFACT" ${KASEKI_RESULTS_DIR}/goal-check.json "$attempt" ${KASEKI_RESULTS_DIR}/goal-check-validation-reason.txt; then
4649
4738
  GOAL_CHECK_EXIT=86
4650
- goal_check_validation_reason="$(cat /results/goal-check-validation-reason.txt 2>/dev/null || printf 'schema_mismatch')"
4651
- goal_check_validation_summary="$(cat /results/goal-check-validation-summary.txt 2>/dev/null || printf 'goal-check artifact validation failed')"
4739
+ goal_check_validation_reason="$(cat ${KASEKI_RESULTS_DIR}/goal-check-validation-reason.txt 2>/dev/null || printf 'schema_mismatch')"
4740
+ goal_check_validation_summary="$(cat ${KASEKI_RESULTS_DIR}/goal-check-validation-summary.txt 2>/dev/null || printf 'goal-check artifact validation failed')"
4652
4741
  case "$goal_check_validation_reason" in
4653
4742
  missing_file)
4654
4743
  GOAL_CHECK_FAILURE_REASON="goal_check_artifact_missing"
4655
- emit_error_event "goal_check_artifact_missing" "Goal-check candidate artifact was missing: $GOAL_CHECK_CANDIDATE_ARTIFACT ($goal_check_validation_summary; full details: /results/goal-check-validation-errors.jsonl)" "continue"
4744
+ emit_error_event "goal_check_artifact_missing" "Goal-check candidate artifact was missing: $GOAL_CHECK_CANDIDATE_ARTIFACT ($goal_check_validation_summary; full details: ${KASEKI_RESULTS_DIR}/goal-check-validation-errors.jsonl)" "continue"
4656
4745
  ;;
4657
4746
  malformed_json)
4658
4747
  GOAL_CHECK_FAILURE_REASON="goal_check_artifact_malformed"
4659
- emit_error_event "goal_check_artifact_malformed" "Goal-check Pi wrote malformed JSON: $goal_check_validation_summary (full details: /results/goal-check-validation-errors.jsonl)" "continue"
4748
+ emit_error_event "goal_check_artifact_malformed" "Goal-check Pi wrote malformed JSON: $goal_check_validation_summary (full details: ${KASEKI_RESULTS_DIR}/goal-check-validation-errors.jsonl)" "continue"
4660
4749
  ;;
4661
4750
  *)
4662
4751
  GOAL_CHECK_FAILURE_REASON="goal_check_artifact_invalid"
4663
- emit_error_event "goal_check_artifact_invalid" "Goal-check Pi did not write a schema-valid JSON verdict: $goal_check_validation_summary (full details: /results/goal-check-validation-errors.jsonl)" "continue"
4752
+ emit_error_event "goal_check_artifact_invalid" "Goal-check Pi did not write a schema-valid JSON verdict: $goal_check_validation_summary (full details: ${KASEKI_RESULTS_DIR}/goal-check-validation-errors.jsonl)" "continue"
4664
4753
  ;;
4665
4754
  esac
4666
4755
  fi
4667
4756
  rm -f "$GOAL_CHECK_CANDIDATE_ARTIFACT"
4668
- GOAL_CHECK_ACTUAL_MODEL="$(node -e 'try { const s=require("/results/goal-check-summary.json"); const v=String(s.selected_model || s.model || "").trim(); console.log(v && v !== "unknown" && v !== "null" ? v : "unknown"); } catch { console.log("unknown"); }' 2>/dev/null)"
4757
+ GOAL_CHECK_ACTUAL_MODEL="$(node -e 'try { const s=require(process.env.KASEKI_RESULTS_DIR + "/goal-check-summary.json"); const v=String(s.selected_model || s.model || "").trim(); console.log(v && v !== "unknown" && v !== "null" ? v : "unknown"); } catch { console.log("unknown"); }' 2>/dev/null)"
4669
4758
 
4670
4759
  if [ "$GOAL_CHECK_EXIT" -eq 0 ]; then
4671
- verdict_met="$(node -e 'const v=require("/results/goal-check.json"); console.log(v.met ? "true" : "false")' 2>/dev/null || printf 'false')"
4672
- retry_prompt="$(node -e 'const v=require("/results/goal-check.json"); console.log(v.retry_prompt || "")' 2>/dev/null || true)"
4673
- verdict_summary="$(node -e 'const v=require("/results/goal-check.json"); console.log(v.summary || "")' 2>/dev/null || true)"
4674
- confidence="$(node -e 'const v=require("/results/goal-check.json"); console.log(v.confidence || "unknown")' 2>/dev/null || true)"
4760
+ verdict_met="$(node -e 'try { const v=require(process.argv[1]); console.log(v.met ? "true" : "false"); } catch { console.log("false"); }' "${KASEKI_RESULTS_DIR}/goal-check.json" 2>/dev/null || printf 'false')"
4761
+ retry_prompt="$(node -e 'try { const v=require(process.argv[1]); console.log(v.retry_prompt || ""); } catch { console.log(""); }' "${KASEKI_RESULTS_DIR}/goal-check.json" 2>/dev/null || true)"
4762
+ verdict_summary="$(node -e 'try { const v=require(process.argv[1]); console.log(v.summary || ""); } catch { console.log(""); }' "${KASEKI_RESULTS_DIR}/goal-check.json" 2>/dev/null || true)"
4763
+ confidence="$(node -e 'try { const v=require(process.argv[1]); console.log(v.confidence || "unknown"); } catch { console.log("unknown"); }' "${KASEKI_RESULTS_DIR}/goal-check.json" 2>/dev/null || true)"
4675
4764
  if [ "$verdict_met" = "true" ]; then
4676
4765
  GOAL_CHECK_MET=true
4677
4766
  GOAL_CHECK_RETRY_PROMPT=""
@@ -4694,12 +4783,12 @@ if (valid.size === 1) {
4694
4783
 
4695
4784
  build_run_evaluation_prompt() {
4696
4785
  local validation_tail progress_tail stage_timings dependency_cache restoration_report draft_pr_body metadata_text goal_setting_context test_impact_context
4697
- validation_tail="$(tail -80 /results/validation.log 2>/dev/null || true)"
4698
- progress_tail="$(tail -80 /results/progress.log 2>/dev/null || true)"
4699
- stage_timings="$(tail -80 /results/stage-timings.tsv 2>/dev/null || true)"
4700
- dependency_cache="$(tail -80 /results/dependency-cache.log 2>/dev/null || true)"
4701
- restoration_report="$(tail -80 /results/restoration-report.md 2>/dev/null || true)"
4702
- metadata_text="$(cat /results/metadata.json 2>/dev/null || true)"
4786
+ validation_tail="$(tail -80 ${KASEKI_RESULTS_DIR}/validation.log 2>/dev/null || true)"
4787
+ progress_tail="$(tail -80 ${KASEKI_RESULTS_DIR}/progress.log 2>/dev/null || true)"
4788
+ stage_timings="$(tail -80 ${KASEKI_RESULTS_DIR}/stage-timings.tsv 2>/dev/null || true)"
4789
+ dependency_cache="$(tail -80 ${KASEKI_RESULTS_DIR}/dependency-cache.log 2>/dev/null || true)"
4790
+ restoration_report="$(tail -80 ${KASEKI_RESULTS_DIR}/restoration-report.md 2>/dev/null || true)"
4791
+ metadata_text="$(cat ${KASEKI_RESULTS_DIR}/metadata.json 2>/dev/null || true)"
4703
4792
  draft_pr_body="$(build_pr_body)"
4704
4793
  if [ -s "$TEST_IMPACT_WARNINGS_ARTIFACT" ]; then
4705
4794
  test_impact_context="Static test-impact warnings artifact ($TEST_IMPACT_WARNINGS_ARTIFACT):
@@ -4745,15 +4834,15 @@ This is NOT another goal-check. The goal-check evaluator already determined if t
4745
4834
  - Quality metrics, SMART criteria, anti-patterns
4746
4835
 
4747
4836
  **Agent Artifacts** (verify goal was realized):
4748
- - Goal-check verdict: /results/goal-check.json
4749
- - Scouting report: /results/scouting.json
4750
- - Changed files: /results/changed-files.txt
4751
- - Git diff and status: /results/git.diff, /results/git.status
4752
- - Validation timings/logs: /results/pre-validation-timings.tsv, /results/validation-timings.tsv, /results/validation.log
4837
+ - Goal-check verdict: ${KASEKI_RESULTS_DIR}/goal-check.json
4838
+ - Scouting report: ${KASEKI_RESULTS_DIR}/scouting.json
4839
+ - Changed files: ${KASEKI_RESULTS_DIR}/changed-files.txt
4840
+ - Git diff and status: ${KASEKI_RESULTS_DIR}/git.diff, ${KASEKI_RESULTS_DIR}/git.status
4841
+ - Validation timings/logs: ${KASEKI_RESULTS_DIR}/pre-validation-timings.tsv, ${KASEKI_RESULTS_DIR}/validation-timings.tsv, ${KASEKI_RESULTS_DIR}/validation.log
4753
4842
  - Static test-impact warnings (non-blocking): $TEST_IMPACT_WARNINGS_ARTIFACT
4754
- - Stage timings: /results/stage-timings.tsv
4755
- - Progress log: /results/progress.log
4756
- - Metadata: /results/metadata.json
4843
+ - Stage timings: ${KASEKI_RESULTS_DIR}/stage-timings.tsv
4844
+ - Progress log: ${KASEKI_RESULTS_DIR}/progress.log
4845
+ - Metadata: ${KASEKI_RESULTS_DIR}/metadata.json
4757
4846
 
4758
4847
  ## Evaluation Framework
4759
4848
 
@@ -4967,12 +5056,12 @@ run_run_evaluation() {
4967
5056
  printf '\n==> run evaluation\n'
4968
5057
  set_current_stage "run evaluation"
4969
5058
  if [ "$KASEKI_RUN_EVALUATION" != "1" ]; then
4970
- printf 'Run evaluation skipped because KASEKI_RUN_EVALUATION=%s.\n' "$KASEKI_RUN_EVALUATION" | tee -a /results/run-evaluation-stderr.log
5059
+ printf 'Run evaluation skipped because KASEKI_RUN_EVALUATION=%s.\n' "$KASEKI_RUN_EVALUATION" | tee -a ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log
4971
5060
  record_stage_timing "run evaluation" 0 0 "skipped_by_config"
4972
5061
  return 0
4973
5062
  fi
4974
5063
  if [ "$KASEKI_DRY_RUN" = "1" ]; then
4975
- printf 'Run evaluation skipped for dry-run/startup-check mode.\n' | tee -a /results/run-evaluation-stderr.log
5064
+ printf 'Run evaluation skipped for dry-run/startup-check mode.\n' | tee -a ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log
4976
5065
  record_stage_timing "run evaluation" 0 0 "dry_run=true"
4977
5066
  return 0
4978
5067
  fi
@@ -4981,19 +5070,19 @@ run_run_evaluation() {
4981
5070
  write_metadata "$STATUS"
4982
5071
  evaluation_prompt="$(build_run_evaluation_prompt)"
4983
5072
  evaluation_start="$(date +%s)"
4984
- eval_dirty_before="$(git status --porcelain 2>> /results/run-evaluation-stderr.log || true)"
4985
- chmod -R a-w /workspace/repo 2>> /results/run-evaluation-stderr.log || true
5073
+ eval_dirty_before="$(git status --porcelain 2>> ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log || true)"
5074
+ chmod -R a-w ${KASEKI_WORKSPACE_DIR}/repo 2>> ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log || true
4986
5075
  set +e
4987
5076
  OPENROUTER_API_KEY="$openrouter_api_key" \
4988
5077
  timeout --signal=SIGTERM "$KASEKI_RUN_EVALUATION_TIMEOUT_SECONDS" \
4989
5078
  pi --mode json --no-session --provider "$KASEKI_PROVIDER" --model "$KASEKI_RUN_EVALUATION_MODEL" "$evaluation_prompt" \
4990
- 2> >(tee -a /results/run-evaluation-stderr.log >&2) \
5079
+ 2> >(tee -a ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log >&2) \
4991
5080
  | tee "$RUN_EVALUATION_RAW_EVENTS" \
4992
- | kaseki-pi-progress-stream /results/progress.jsonl /results/progress.log
5081
+ | kaseki-pi-progress-stream ${KASEKI_RESULTS_DIR}/progress.jsonl ${KASEKI_RESULTS_DIR}/progress.log
4993
5082
  RUN_EVALUATION_EXIT="${PIPESTATUS[0]}"
4994
5083
  unset evaluation_prompt
4995
5084
  RUN_EVALUATION_DURATION_SECONDS=$((RUN_EVALUATION_DURATION_SECONDS + $(date +%s) - evaluation_start))
4996
- chmod -R u+w /workspace/repo 2>> /results/run-evaluation-stderr.log || true
5085
+ chmod -R u+w ${KASEKI_WORKSPACE_DIR}/repo 2>> ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log || true
4997
5086
  set +e
4998
5087
 
4999
5088
  if [ "$RUN_EVALUATION_EXIT" -eq 0 ] && ! node -e '
@@ -5023,15 +5112,15 @@ artifact.timestamp = new Date().toISOString();
5023
5112
  artifact.model = model;
5024
5113
  artifact.actual_model = actualModel;
5025
5114
  fs.writeFileSync(output, JSON.stringify(artifact, null, 2) + "\n");
5026
- ' "$RUN_EVALUATION_CANDIDATE_ARTIFACT" "$RUN_EVALUATION_ARTIFACT" "$KASEKI_RUN_EVALUATION_MODEL" "$RUN_EVALUATION_ACTUAL_MODEL" 2>> /results/run-evaluation-stderr.log; then
5115
+ ' "$RUN_EVALUATION_CANDIDATE_ARTIFACT" "$RUN_EVALUATION_ARTIFACT" "$KASEKI_RUN_EVALUATION_MODEL" "$RUN_EVALUATION_ACTUAL_MODEL" 2>> ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log; then
5027
5116
  RUN_EVALUATION_EXIT=86
5028
5117
  emit_error_event "run_evaluation_artifact_invalid" "Run-evaluation Pi did not write a schema-valid JSON artifact" "continue"
5029
5118
  fi
5030
5119
  rm -f "$RUN_EVALUATION_CANDIDATE_ARTIFACT"
5031
- kaseki-pi-event-filter "$RUN_EVALUATION_RAW_EVENTS" /results/run-evaluation-events.jsonl /results/run-evaluation-summary.json 2>> /results/run-evaluation-stderr.log || true
5032
- RUN_EVALUATION_ACTUAL_MODEL="$(node -e 'try { const s=require("/results/run-evaluation-summary.json"); const v=String(s.selected_model || s.model || "").trim(); console.log(v && v !== "unknown" && v !== "null" ? v : "unknown"); } catch { console.log("unknown"); }' 2>/dev/null)"
5120
+ kaseki-pi-event-filter "$RUN_EVALUATION_RAW_EVENTS" ${KASEKI_RESULTS_DIR}/run-evaluation-events.jsonl ${KASEKI_RESULTS_DIR}/run-evaluation-summary.json 2>> ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log || true
5121
+ RUN_EVALUATION_ACTUAL_MODEL="$(node -e 'try { const s=require(process.env.KASEKI_RESULTS_DIR + "/run-evaluation-summary.json"); const v=String(s.selected_model || s.model || "").trim(); console.log(v && v !== "unknown" && v !== "null" ? v : "unknown"); } catch { console.log("unknown"); }' 2>/dev/null)"
5033
5122
  if [ -s "$RUN_EVALUATION_ARTIFACT" ]; then
5034
- node - "$RUN_EVALUATION_ARTIFACT" "$RUN_EVALUATION_ACTUAL_MODEL" <<'NODE' 2>> /results/run-evaluation-stderr.log || true
5123
+ node - "$RUN_EVALUATION_ARTIFACT" "$RUN_EVALUATION_ACTUAL_MODEL" <<'NODE' 2>> ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log || true
5035
5124
  const fs = require('fs');
5036
5125
  const [file, actualModel] = process.argv.slice(2);
5037
5126
  const artifact = JSON.parse(fs.readFileSync(file, 'utf8'));
@@ -5040,12 +5129,12 @@ fs.writeFileSync(file, JSON.stringify(artifact, null, 2) + '\n');
5040
5129
  NODE
5041
5130
  fi
5042
5131
 
5043
- eval_dirty_after="$(git status --porcelain 2>> /results/run-evaluation-stderr.log || true)"
5132
+ eval_dirty_after="$(git status --porcelain 2>> ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log || true)"
5044
5133
  if [ "$eval_dirty_before" != "$eval_dirty_after" ]; then
5045
5134
  RUN_EVALUATION_EXIT=86
5046
5135
  emit_error_event "run_evaluation_workspace_modified" "Read-only run evaluation changed repository state; restoring workspace" "continue"
5047
- git reset --hard -q HEAD 2>> /results/run-evaluation-stderr.log || true
5048
- git clean -fd -q 2>> /results/run-evaluation-stderr.log || true
5136
+ git reset --hard -q HEAD 2>> ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log || true
5137
+ git clean -fd -q 2>> ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log || true
5049
5138
  fi
5050
5139
 
5051
5140
  if [ "$RUN_EVALUATION_EXIT" -ne 0 ] || [ ! -s "$RUN_EVALUATION_ARTIFACT" ]; then
@@ -5142,19 +5231,19 @@ META
5142
5231
  log_github_private_key_metadata() {
5143
5232
  local key_file="$1"
5144
5233
  local health_log="$2"
5145
- local metadata_file="/results/github-app-private-key-metadata.json"
5234
+ local metadata_file="${KASEKI_RESULTS_DIR}/github-app-private-key-metadata.json"
5146
5235
  github_private_key_metadata_json "$key_file" > "$metadata_file"
5147
5236
  printf '[health-check] GitHub App private key metadata: %s\n' "$(tr -d '\n' < "$metadata_file")" | tee -a "$health_log"
5148
5237
  }
5149
5238
 
5150
5239
 
5151
5240
  github_askpass_runtime_dir() {
5152
- printf '%s\n' "${KASEKI_GITHUB_ASKPASS_DIR:-/results}"
5241
+ printf '%s\n' "${KASEKI_GITHUB_ASKPASS_DIR:-${KASEKI_RESULTS_DIR}}"
5153
5242
  }
5154
5243
 
5155
5244
  create_github_askpass_helper() {
5156
5245
  local log_file log_prefix askpass_dir askpass_file username_smoke_output password_smoke_output
5157
- log_file="${1:-/results/git-push.log}"
5246
+ log_file="${1:-${KASEKI_RESULTS_DIR}/git-push.log}"
5158
5247
  log_prefix="${2:-[github-askpass]}"
5159
5248
  GITHUB_ASKPASS_FILE=""
5160
5249
 
@@ -5232,7 +5321,7 @@ EOF_ASKPASS
5232
5321
  check_github_operations_health() {
5233
5322
  # Preflight health check for github operations before pi agent runs
5234
5323
  # Tests: GitHub App secrets, git config, Node.js token generation capability
5235
- local health_log="${KASEKI_HEALTH_LOG:-/results/github-health-check.log}"
5324
+ local health_log="${KASEKI_HEALTH_LOG:-${KASEKI_RESULTS_DIR}/github-health-check.log}"
5236
5325
  github_preflight_fail() {
5237
5326
  local classification="$1"
5238
5327
  local remediation="$2"
@@ -5486,7 +5575,7 @@ validate_github_api_response() {
5486
5575
  local http_status response log_file error_type error_message json_valid
5487
5576
  http_status="$1"
5488
5577
  response="$2"
5489
- log_file="${3:-/results/git-push.log}"
5578
+ log_file="${3:-${KASEKI_RESULTS_DIR}/git-push.log}"
5490
5579
 
5491
5580
  # Try to parse error info from response
5492
5581
  error_type="unknown"
@@ -5594,7 +5683,7 @@ apply_github_pr_labels() {
5594
5683
  repo="$2"
5595
5684
  issue_number="$3"
5596
5685
  token="$4"
5597
- log_file="${5:-/results/git-push.log}"
5686
+ log_file="${5:-${KASEKI_RESULTS_DIR}/git-push.log}"
5598
5687
 
5599
5688
  if [ -z "$owner" ] || [ -z "$repo" ] || [ -z "$issue_number" ] || [ -z "$token" ]; then
5600
5689
  printf 'Warning: skipping PR label application because owner, repo, issue number, or token is missing\n' | tee -a "$log_file" >&2
@@ -5648,7 +5737,7 @@ request_owner_review() {
5648
5737
  local pr_response token log_file owner_login owner_type pr_number repo owner
5649
5738
  pr_response="$1"
5650
5739
  token="$2"
5651
- log_file="${3:-/results/git-push.log}"
5740
+ log_file="${3:-${KASEKI_RESULTS_DIR}/git-push.log}"
5652
5741
 
5653
5742
  if [ -z "$pr_response" ] || [ -z "$token" ]; then
5654
5743
  printf 'Warning: skipping owner review request because PR response or token is missing\n' | tee -a "$log_file" >&2
@@ -5713,7 +5802,7 @@ request_owner_review() {
5713
5802
 
5714
5803
  # Request owner review with retry logic
5715
5804
  local retry_count=0 max_retries=2 request_success=0 backoff_delay=2
5716
- local review_request_log="/results/owner-review-request.log"
5805
+ local review_request_log="${KASEKI_RESULTS_DIR}/owner-review-request.log"
5717
5806
  : > "$review_request_log"
5718
5807
 
5719
5808
  while [ $retry_count -le "$max_retries" ]; do
@@ -5906,7 +5995,7 @@ derive_pr_title() {
5906
5995
 
5907
5996
  candidate="$(printf '%s' "${TASK_PROMPT:-}" | sanitize_pr_metadata_text)"
5908
5997
  prompt_for_prefix="$candidate"
5909
- if [ -s /results/result-summary.md ]; then
5998
+ if [ -s ${KASEKI_RESULTS_DIR}/result-summary.md ]; then
5910
5999
  summary_candidate="$(
5911
6000
  awk '
5912
6001
  /^##[[:space:]]+Summary[[:space:]]*$/ { in_summary=1; next }
@@ -5917,13 +6006,13 @@ derive_pr_title() {
5917
6006
  sub(/^[[:space:]]*[0-9]+[.)][[:space:]]+/, "", line)
5918
6007
  if (line !~ /^[[:space:]]*$/) { print line; exit }
5919
6008
  }
5920
- ' /results/result-summary.md 2>/dev/null | sanitize_pr_metadata_text
6009
+ ' ${KASEKI_RESULTS_DIR}/result-summary.md 2>/dev/null | sanitize_pr_metadata_text
5921
6010
  )"
5922
6011
  fi
5923
6012
  if [ -n "$summary_candidate" ]; then
5924
6013
  candidate="$summary_candidate"
5925
- elif [ -z "$candidate" ] && [ -s /results/result-summary.md ]; then
5926
- candidate="$(sed -n '/^- Status:/p; /^- Changed files:/p; /^- Validation:/p' /results/result-summary.md 2>/dev/null | head -n 3 | sanitize_pr_metadata_text)"
6014
+ elif [ -z "$candidate" ] && [ -s ${KASEKI_RESULTS_DIR}/result-summary.md ]; then
6015
+ candidate="$(sed -n '/^- Status:/p; /^- Changed files:/p; /^- Validation:/p' ${KASEKI_RESULTS_DIR}/result-summary.md 2>/dev/null | head -n 3 | sanitize_pr_metadata_text)"
5927
6016
  fi
5928
6017
 
5929
6018
  candidate="$(printf '%s' "$candidate" | sed -E 's/^[[:space:]]*([0-9]+[.)]|[-*])[[:space:]]+//' | tr '\n' ' ' | sed -E 's/[[:space:]]+/ /g; s/(^|[[:space:]])[0-9]+[.)][[:space:]]+/\1/g; s/(^|[[:space:]])[-*][[:space:]]+/\1/g; s/userfacing/user-facing/Ig; s/customerfacing/customer-facing/Ig; s/front[ -]?end/frontend/Ig; s/back[ -]?end/backend/Ig; s/full[ -]?stack/full-stack/Ig; s/^[[:space:]]+//; s/[[:space:]]+$//')"
@@ -5933,8 +6022,8 @@ derive_pr_title() {
5933
6022
  candidate="$stripped"
5934
6023
  fi
5935
6024
 
5936
- if [ -s /results/changed-files.txt ]; then
5937
- changed_files="$(sanitize_pr_metadata_text < /results/changed-files.txt || true)"
6025
+ if [ -s ${KASEKI_RESULTS_DIR}/changed-files.txt ]; then
6026
+ changed_files="$(sanitize_pr_metadata_text < ${KASEKI_RESULTS_DIR}/changed-files.txt || true)"
5938
6027
  else
5939
6028
  changed_files=""
5940
6029
  fi
@@ -6005,7 +6094,7 @@ format_pr_command_results() {
6005
6094
  }
6006
6095
 
6007
6096
  format_pr_changed_files() {
6008
- local changed_files_file="/results/changed-files.txt"
6097
+ local changed_files_file="${KASEKI_RESULTS_DIR}/changed-files.txt"
6009
6098
  local details_threshold=8
6010
6099
  if [ ! -s "$changed_files_file" ]; then
6011
6100
  printf '0 files changed.\n'
@@ -6095,8 +6184,8 @@ build_pr_agent_review() {
6095
6184
  case "$validation_pass_flag" in
6096
6185
  ''|*[!0-9-]*) validation_pass_flag=0 ;;
6097
6186
  esac
6098
- local goal_file="/results/goal-check.json"
6099
- local scouting_file="/results/scouting.json"
6187
+ local goal_file="${KASEKI_RESULTS_DIR}/goal-check.json"
6188
+ local scouting_file="${KASEKI_RESULTS_DIR}/scouting.json"
6100
6189
  local goal_summary evidence missing validation_notes risks
6101
6190
 
6102
6191
  goal_summary=""
@@ -6145,7 +6234,7 @@ NODE
6145
6234
  }
6146
6235
 
6147
6236
  build_pr_agent_evaluation() {
6148
- local evaluation_file="/results/run-evaluation.json"
6237
+ local evaluation_file="${KASEKI_RESULTS_DIR}/run-evaluation.json"
6149
6238
  if [ ! -s "$evaluation_file" ]; then
6150
6239
  return 0
6151
6240
  fi
@@ -6243,8 +6332,8 @@ NODE
6243
6332
  }
6244
6333
 
6245
6334
  build_pr_improvements_summary() {
6246
- local changed_files_file="/results/changed-files.txt"
6247
- local diff_file="/results/git.diff"
6335
+ local changed_files_file="${KASEKI_RESULTS_DIR}/changed-files.txt"
6336
+ local diff_file="${KASEKI_RESULTS_DIR}/git.diff"
6248
6337
  local total=0 source_count=0 test_count=0 docs_count=0 config_count=0 other_count=0
6249
6338
  local path lower additions deletions summary_rows=0 summary_source=""
6250
6339
  local artifact raw_line line safe_line summary_capture=0 content json_text
@@ -6264,10 +6353,10 @@ build_pr_improvements_summary() {
6264
6353
  done < "$changed_files_file"
6265
6354
  fi
6266
6355
 
6267
- if [ -s /results/result-summary.md ]; then
6268
- summary_source="/results/result-summary.md"
6356
+ if [ -s ${KASEKI_RESULTS_DIR}/result-summary.md ]; then
6357
+ summary_source="${KASEKI_RESULTS_DIR}/result-summary.md"
6269
6358
  else
6270
- for artifact in /results/analysis.md /results/pi-summary.json; do
6359
+ for artifact in ${KASEKI_RESULTS_DIR}/analysis.md ${KASEKI_RESULTS_DIR}/pi-summary.json; do
6271
6360
  if [ -s "$artifact" ]; then
6272
6361
  summary_source="$artifact"
6273
6362
  break
@@ -6327,7 +6416,7 @@ EOF_JSON_SUMMARY
6327
6416
  continue
6328
6417
  ;;
6329
6418
  esac
6330
- if [ "$summary_capture" -eq 0 ] && [ "$summary_source" = "/results/result-summary.md" ]; then
6419
+ if [ "$summary_capture" -eq 0 ] && [ "$summary_source" = "${KASEKI_RESULTS_DIR}/result-summary.md" ]; then
6331
6420
  continue
6332
6421
  fi
6333
6422
  line="$(printf '%s' "$raw_line" | sanitize_pr_metadata_text)"
@@ -6453,7 +6542,7 @@ $(build_pr_improvements_summary)
6453
6542
  ## Agent review
6454
6543
  $(build_pr_agent_review "$all_validation_statuses_pass")
6455
6544
 
6456
- $(if [ -s /results/run-evaluation.json ]; then printf '## Agent evaluation\n%s\n\n' "$(build_pr_agent_evaluation)"; fi)
6545
+ $(if [ -s ${KASEKI_RESULTS_DIR}/run-evaluation.json ]; then printf '## Agent evaluation\n%s\n\n' "$(build_pr_agent_evaluation)"; fi)
6457
6546
  ## Validation
6458
6547
  ### Validation statuses
6459
6548
  - Pre-agent validation: $pre_validation_status
@@ -6502,11 +6591,11 @@ run_github_operations() {
6502
6591
  owner="$GITHUB_REPO_OWNER"
6503
6592
  repo="$GITHUB_REPO_NAME"
6504
6593
  else
6505
- printf -- 'Cannot parse GitHub repo URL: %s\n' "$REPO_URL" | tee -a /results/git-push.log >&2
6594
+ printf -- 'Cannot parse GitHub repo URL: %s\n' "$REPO_URL" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6506
6595
  return 7
6507
6596
  fi
6508
6597
 
6509
- printf -- 'GitHub operations: owner=%s, repo=%s\n' "$owner" "$repo" | tee -a /results/git-push.log
6598
+ printf -- 'GitHub operations: owner=%s, repo=%s\n' "$owner" "$repo" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6510
6599
  GITHUB_OPERATION_PHASE="setup"
6511
6600
 
6512
6601
  # Set git user for commits
@@ -6515,7 +6604,7 @@ run_github_operations() {
6515
6604
 
6516
6605
  # Generate GitHub App installation token
6517
6606
  GITHUB_OPERATION_PHASE="token_generation"
6518
- printf 'Generating GitHub App installation token...\n' | tee -a /results/git-push.log
6607
+ printf 'Generating GitHub App installation token...\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6519
6608
  local github_app_token_helper="${KASEKI_GITHUB_APP_TOKEN_HELPER:-/usr/local/bin/github-app-token}"
6520
6609
  local token_stdout_tmp token_stderr_tmp token_exit_code token_stderr token_parse_result token_error token_http_status
6521
6610
  token_stdout_tmp="$(mktemp /tmp/github-app-token-stdout.XXXXXX)" || { printf 'Failed to create token stdout temp file\n' >&2; return 7; }
@@ -6536,7 +6625,7 @@ run_github_operations() {
6536
6625
  if [ "$token_parse_result" != "$token_error" ]; then
6537
6626
  token_http_status="${token_parse_result#*$'\t'}"
6538
6627
  fi
6539
- printf 'Failed to generate token: %s\n' "$token_error" | tee -a /results/git-push.log >&2
6628
+ printf 'Failed to generate token: %s\n' "$token_error" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6540
6629
  GITHUB_API_ERROR_TYPE="github_app_token_error"
6541
6630
  GITHUB_API_ERROR_MESSAGE="$token_error"
6542
6631
  GITHUB_API_HTTP_STATUS="$token_http_status"
@@ -6546,83 +6635,83 @@ run_github_operations() {
6546
6635
  fi
6547
6636
 
6548
6637
  # Use helper to extract token from JSON response
6549
- if ! run_node_subprocess token "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); process.stdout.write(d.token || '')" "$token_data" /results/git-push.log; then
6550
- printf -- 'Failed to extract token from response: %s\n' "$token_data" | tee -a /results/git-push.log >&2
6638
+ if ! run_node_subprocess token "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); process.stdout.write(d.token || '')" "$token_data" ${KASEKI_RESULTS_DIR}/git-push.log; then
6639
+ printf -- 'Failed to extract token from response: %s\n' "$token_data" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6551
6640
  GITHUB_PUSH_EXIT=7
6552
6641
  return 7
6553
6642
  fi
6554
6643
 
6555
6644
  if [ -z "$token" ]; then
6556
- printf -- 'Failed to extract token from response (empty result)\n' | tee -a /results/git-push.log >&2
6645
+ printf -- 'Failed to extract token from response (empty result)\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6557
6646
  GITHUB_PUSH_EXIT=7
6558
6647
  return 7
6559
6648
  fi
6560
6649
 
6561
- printf 'Token generated successfully\n' | tee -a /results/git-push.log
6650
+ printf 'Token generated successfully\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6562
6651
 
6563
6652
  # Create and push feature branch
6564
6653
  GITHUB_OPERATION_PHASE="branch_creation"
6565
6654
  feature_branch="kaseki/$INSTANCE_NAME"
6566
- printf -- 'Creating feature branch: %s\n' "$feature_branch" | tee -a /results/git-push.log
6655
+ printf -- 'Creating feature branch: %s\n' "$feature_branch" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6567
6656
  git checkout -b "$feature_branch" || {
6568
- printf 'Failed to create branch\n' | tee -a /results/git-push.log >&2
6657
+ printf 'Failed to create branch\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6569
6658
  GITHUB_PUSH_EXIT=7
6570
6659
  return 7
6571
6660
  }
6572
6661
 
6573
6662
  # Commit changes (git should already have changes from pi agent)
6574
6663
  GITHUB_OPERATION_PHASE="commit"
6575
- printf 'Committing changes...\n' | tee -a /results/git-push.log
6576
- if [ ! -s /results/changed-files.txt ]; then
6577
- printf 'No changed files to stage\n' | tee -a /results/git-push.log >&2
6664
+ printf 'Committing changes...\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6665
+ if [ ! -s ${KASEKI_RESULTS_DIR}/changed-files.txt ]; then
6666
+ printf 'No changed files to stage\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6578
6667
  GITHUB_PUSH_EXIT=7
6579
6668
  return 7
6580
6669
  fi
6581
6670
  while IFS= read -r changed_file || [ -n "$changed_file" ]; do
6582
6671
  [ -z "$changed_file" ] && continue
6583
6672
  git add -- "$changed_file" || {
6584
- printf -- 'Failed to stage changed file: %s\n' "$changed_file" | tee -a /results/git-push.log >&2
6673
+ printf -- 'Failed to stage changed file: %s\n' "$changed_file" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6585
6674
  GITHUB_PUSH_EXIT=7
6586
6675
  return 7
6587
6676
  }
6588
- done < /results/changed-files.txt
6677
+ done < ${KASEKI_RESULTS_DIR}/changed-files.txt
6589
6678
  if ! git commit -m "Kaseki: $INSTANCE_NAME"; then
6590
- printf 'No changes to commit or commit failed\n' | tee -a /results/git-push.log >&2
6679
+ printf 'No changes to commit or commit failed\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6591
6680
  GITHUB_PUSH_EXIT=7
6592
6681
  return 7
6593
6682
  fi
6594
6683
 
6595
6684
  # Push branch
6596
6685
  GITHUB_OPERATION_PHASE="push"
6597
- printf 'Pushing branch to GitHub...\n' | tee -a /results/git-push.log
6686
+ printf 'Pushing branch to GitHub...\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6598
6687
  local askpass_file
6599
- if ! create_github_askpass_helper /results/git-push.log 'GitHub credential helper'; then
6688
+ if ! create_github_askpass_helper ${KASEKI_RESULTS_DIR}/git-push.log 'GitHub credential helper'; then
6600
6689
  return 8
6601
6690
  fi
6602
6691
  askpass_file="$GITHUB_ASKPASS_FILE"
6603
6692
 
6604
6693
  KASEKI_GITHUB_TOKEN="$token" GIT_ASKPASS="$askpass_file" GIT_TERMINAL_PROMPT=0 \
6605
- git push "https://github.com/$owner/$repo.git" "$feature_branch" --force-with-lease 2>&1 | tee -a /results/git-push.log
6694
+ git push "https://github.com/$owner/$repo.git" "$feature_branch" --force-with-lease 2>&1 | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6606
6695
  git_push_exit="${PIPESTATUS[0]:-1}"
6607
6696
  if [ "$git_push_exit" -eq 0 ]; then
6608
- printf 'Branch pushed successfully\n' | tee -a /results/git-push.log
6697
+ printf 'Branch pushed successfully\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6609
6698
  else
6610
6699
  rm -f "$askpass_file"
6611
- printf 'Failed to push branch (exit %s)\n' "$git_push_exit" | tee -a /results/git-push.log >&2
6700
+ printf 'Failed to push branch (exit %s)\n' "$git_push_exit" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6612
6701
  GITHUB_PUSH_EXIT="$git_push_exit"
6613
6702
  return "$git_push_exit"
6614
6703
  fi
6615
6704
  rm -f "$askpass_file"
6616
6705
 
6617
6706
  if [ "$KASEKI_PUBLISH_MODE" = "branch" ]; then
6618
- printf 'Publish mode branch: skipping pull request creation.\n' | tee -a /results/git-push.log
6707
+ printf 'Publish mode branch: skipping pull request creation.\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6619
6708
  GITHUB_PR_EXIT=0
6620
6709
  GITHUB_OPERATION_PHASE="completed"
6621
6710
  unset token
6622
6711
  return 0
6623
6712
  fi
6624
6713
  if ! is_pr_creation_mode; then
6625
- printf 'Publish mode %s: skipping pull request creation.\n' "$KASEKI_PUBLISH_MODE" | tee -a /results/git-push.log
6714
+ printf 'Publish mode %s: skipping pull request creation.\n' "$KASEKI_PUBLISH_MODE" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6626
6715
  GITHUB_PR_EXIT=0
6627
6716
  GITHUB_OPERATION_PHASE="completed"
6628
6717
  unset token
@@ -6632,7 +6721,7 @@ run_github_operations() {
6632
6721
  # Create pull request. Both pr and draft_pr push a branch and create a PR;
6633
6722
  # only draft_pr marks the GitHub Pulls API request as draft.
6634
6723
  GITHUB_OPERATION_PHASE="pr_creation"
6635
- printf 'Creating pull request...\n' | tee -a /results/git-push.log
6724
+ printf 'Creating pull request...\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6636
6725
  emit_progress "github operations" "pr_creation_starting"
6637
6726
  local pr_title pr_body pr_response pr_url pr_number pr_http_status pr_draft_json
6638
6727
  pr_title="$(derive_pr_title)"
@@ -6656,7 +6745,7 @@ run_github_operations() {
6656
6745
  - Generated at (UTC): $fallback_timestamp
6657
6746
  EOF
6658
6747
  )
6659
- printf 'WARN: build_pr_body returned empty content after sanitization; using fallback PR body.\n' | tee -a /results/git-push.log >&2
6748
+ printf 'WARN: build_pr_body returned empty content after sanitization; using fallback PR body.\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6660
6749
  fi
6661
6750
  if is_pr_draft_mode; then
6662
6751
  pr_draft_json=true
@@ -6670,7 +6759,7 @@ EOF
6670
6759
 
6671
6760
  while [ $retry_count -le "$max_retries" ]; do
6672
6761
  if [ $retry_count -gt 0 ]; then
6673
- printf 'Retrying PR creation (attempt %d of %d) after %ds delay...\n' $((retry_count + 1)) "$max_retries" "$backoff_delay" | tee -a /results/git-push.log
6762
+ printf 'Retrying PR creation (attempt %d of %d) after %ds delay...\n' $((retry_count + 1)) "$max_retries" "$backoff_delay" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6674
6763
  emit_progress "github operations" "pr_creation_attempt $((retry_count + 1))/$max_retries"
6675
6764
  sleep "$backoff_delay"
6676
6765
  # Exponential backoff: 2s, 4s, 8s
@@ -6680,31 +6769,31 @@ EOF
6680
6769
 
6681
6770
  # Capture both response and HTTP status code
6682
6771
  local pr_response_file temp_status_file
6683
- pr_response_file="$(mktemp /tmp/kaseki-pr-response.XXXXXX)" || { printf 'Failed to create temp file for PR response\n' | tee -a /results/git-push.log >&2; GITHUB_PR_EXIT=8; return 8; }
6684
- temp_status_file="$(mktemp /tmp/kaseki-pr-status.XXXXXX)" || { printf 'Failed to create temp file for PR status\n' | tee -a /results/git-push.log >&2; GITHUB_PR_EXIT=8; return 8; }
6772
+ pr_response_file="$(mktemp /tmp/kaseki-pr-response.XXXXXX)" || { printf 'Failed to create temp file for PR response\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2; GITHUB_PR_EXIT=8; return 8; }
6773
+ temp_status_file="$(mktemp /tmp/kaseki-pr-status.XXXXXX)" || { printf 'Failed to create temp file for PR status\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2; GITHUB_PR_EXIT=8; return 8; }
6685
6774
 
6686
6775
  if [ $retry_count -eq 0 ] && [ "${KASEKI_DEBUG:-0}" = "1" ]; then
6687
- printf 'Debug: Creating PR with head=%s, base=%s, draft=%s\n' "$feature_branch" "$GIT_REF" "$pr_draft_json" | tee -a /results/git-push.log
6776
+ printf 'Debug: Creating PR with head=%s, base=%s, draft=%s\n' "$feature_branch" "$GIT_REF" "$pr_draft_json" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6688
6777
  fi
6689
6778
 
6690
6779
  # Encode PR title and body as JSON strings
6691
6780
  local pr_title_json pr_body_json
6692
6781
  pr_title_json='""'
6693
6782
  pr_body_json='""'
6694
- if ! run_node_subprocess pr_title_json "console.log(JSON.stringify(require('fs').readFileSync(0, 'utf8')))" "$pr_title" /results/git-push.log; then
6695
- printf 'ERROR: Failed to JSON encode PR title\n' | tee -a /results/git-push.log >&2
6783
+ if ! run_node_subprocess pr_title_json "console.log(JSON.stringify(require('fs').readFileSync(0, 'utf8')))" "$pr_title" ${KASEKI_RESULTS_DIR}/git-push.log; then
6784
+ printf 'ERROR: Failed to JSON encode PR title\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6696
6785
  GITHUB_PR_EXIT=8
6697
6786
  return 8
6698
6787
  fi
6699
- if ! run_node_subprocess pr_body_json "console.log(JSON.stringify(require('fs').readFileSync(0, 'utf8')))" "$pr_body" /results/git-push.log; then
6700
- printf 'ERROR: Failed to JSON encode PR body\n' | tee -a /results/git-push.log >&2
6788
+ if ! run_node_subprocess pr_body_json "console.log(JSON.stringify(require('fs').readFileSync(0, 'utf8')))" "$pr_body" ${KASEKI_RESULTS_DIR}/git-push.log; then
6789
+ printf 'ERROR: Failed to JSON encode PR body\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6701
6790
  GITHUB_PR_EXIT=8
6702
6791
  return 8
6703
6792
  fi
6704
6793
 
6705
6794
  # Validate both variables are non-empty before using in curl
6706
6795
  if [ -z "$pr_title_json" ] || [ -z "$pr_body_json" ]; then
6707
- printf 'ERROR: JSON encoding produced empty values (title=%s, body=%s)\n' "$pr_title_json" "$pr_body_json" | tee -a /results/git-push.log >&2
6796
+ printf 'ERROR: JSON encoding produced empty values (title=%s, body=%s)\n' "$pr_title_json" "$pr_body_json" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6708
6797
  GITHUB_PR_EXIT=8
6709
6798
  return 8
6710
6799
  fi
@@ -6729,7 +6818,7 @@ EOF
6729
6818
 
6730
6819
  if [ $curl_exit -ne 0 ]; then
6731
6820
  # curl command itself failed (network error, timeout, etc.)
6732
- printf 'GitHub PR API curl command failed with exit code %d (attempt %d)\n' "$curl_exit" $((retry_count + 1)) | tee -a /results/git-push.log >&2
6821
+ printf 'GitHub PR API curl command failed with exit code %d (attempt %d)\n' "$curl_exit" $((retry_count + 1)) | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6733
6822
  GITHUB_API_HTTP_STATUS="0"
6734
6823
  if is_github_pr_error_retryable "0" "curl_error" && [ "$retry_count" -lt "$((max_retries - 1))" ]; then
6735
6824
  retry_count=$((retry_count + 1))
@@ -6747,43 +6836,43 @@ EOF
6747
6836
  fi
6748
6837
 
6749
6838
  if [ "${KASEKI_DEBUG:-0}" = "1" ]; then
6750
- printf 'Debug: PR API response HTTP status: %s (attempt %d)\n' "$pr_http_status" $((retry_count + 1)) | tee -a /results/git-push.log
6839
+ printf 'Debug: PR API response HTTP status: %s (attempt %d)\n' "$pr_http_status" $((retry_count + 1)) | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6751
6840
  fi
6752
6841
 
6753
6842
  # Validate the API response
6754
- if validate_github_api_response "$pr_http_status" "$pr_response" /results/git-push.log; then
6843
+ if validate_github_api_response "$pr_http_status" "$pr_response" ${KASEKI_RESULTS_DIR}/git-push.log; then
6755
6844
  # API returned success (201); now extract the URL and issue number using helper
6756
- if ! run_node_subprocess pr_url "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); process.stdout.write(d.html_url || '')" "$pr_response" /results/git-push.log; then
6757
- printf 'ERROR: Failed to extract PR URL from API response\n' | tee -a /results/git-push.log >&2
6845
+ if ! run_node_subprocess pr_url "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); process.stdout.write(d.html_url || '')" "$pr_response" ${KASEKI_RESULTS_DIR}/git-push.log; then
6846
+ printf 'ERROR: Failed to extract PR URL from API response\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6758
6847
  emit_error_event "github_pr_response_malformed" "Failed to parse PR API response to extract html_url" "exit"
6759
6848
  GITHUB_PR_EXIT=9
6760
6849
  pr_url=""
6761
6850
  fi
6762
- if ! run_node_subprocess pr_number "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); if (Number.isInteger(d.number)) process.stdout.write(String(d.number));" "$pr_response" /results/git-push.log; then
6763
- printf 'Warning: failed to extract PR number from API response; leaving PR unlabeled\n' | tee -a /results/git-push.log >&2
6851
+ if ! run_node_subprocess pr_number "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); if (Number.isInteger(d.number)) process.stdout.write(String(d.number));" "$pr_response" ${KASEKI_RESULTS_DIR}/git-push.log; then
6852
+ printf 'Warning: failed to extract PR number from API response; leaving PR unlabeled\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6764
6853
  pr_number=""
6765
6854
  fi
6766
6855
 
6767
6856
  if [ -n "$pr_url" ]; then
6768
6857
  GITHUB_PR_URL="$pr_url"
6769
6858
  GITHUB_PR_EXIT=0
6770
- printf 'Pull request created: %s\n' "$pr_url" | tee -a /results/git-push.log
6859
+ printf 'Pull request created: %s\n' "$pr_url" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6771
6860
  if [ -n "$pr_number" ]; then
6772
- apply_github_pr_labels "$owner" "$repo" "$pr_number" "$token" /results/git-push.log || true
6861
+ apply_github_pr_labels "$owner" "$repo" "$pr_number" "$token" ${KASEKI_RESULTS_DIR}/git-push.log || true
6773
6862
  # Request repository owner as reviewer for personal repos
6774
- request_owner_review "$pr_response" "$token" /results/git-push.log || true
6863
+ request_owner_review "$pr_response" "$token" ${KASEKI_RESULTS_DIR}/git-push.log || true
6775
6864
  else
6776
- printf 'Warning: PR API response missing number field; leaving PR unlabeled\n' | tee -a /results/git-push.log >&2
6865
+ printf 'Warning: PR API response missing number field; leaving PR unlabeled\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6777
6866
  fi
6778
6867
  pr_created=1
6779
6868
  rm -f "$pr_response_file"
6780
6869
  break
6781
6870
  else
6782
6871
  # HTTP 201 but no html_url in response - malformed response
6783
- printf 'Pull request API returned success (201) but response missing html_url field\n' | tee -a /results/git-push.log >&2
6872
+ printf 'Pull request API returned success (201) but response missing html_url field\n' | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6784
6873
  emit_error_event "github_pr_response_malformed" "GitHub PR API returned 201 but response missing html_url field" "exit"
6785
6874
  if [ "${KASEKI_DEBUG:-0}" = "1" ]; then
6786
- printf 'Debug: Full API response:\n%s\n' "$pr_response" | tee -a /results/git-push.log
6875
+ printf 'Debug: Full API response:\n%s\n' "$pr_response" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6787
6876
  fi
6788
6877
  GITHUB_PR_EXIT=9
6789
6878
  pr_created=0
@@ -6793,17 +6882,17 @@ EOF
6793
6882
  else
6794
6883
  # API returned an error
6795
6884
  if is_github_pr_error_retryable "$pr_http_status" "$GITHUB_API_ERROR_TYPE" && [ "$retry_count" -lt "$((max_retries - 1))" ]; then
6796
- printf 'GitHub API returned retryable error (attempt %d): %s (HTTP %s)\n' $((retry_count + 1)) "$GITHUB_API_ERROR_TYPE" "$pr_http_status" | tee -a /results/git-push.log
6885
+ printf 'GitHub API returned retryable error (attempt %d): %s (HTTP %s)\n' $((retry_count + 1)) "$GITHUB_API_ERROR_TYPE" "$pr_http_status" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6797
6886
  retry_count=$((retry_count + 1))
6798
6887
  rm -f "$pr_response_file"
6799
6888
  continue
6800
6889
  else
6801
6890
  # Permanent error, give up
6802
- printf 'Failed to create PR. API error: %s\n' "$GITHUB_API_ERROR_MESSAGE" | tee -a /results/git-push.log >&2
6891
+ printf 'Failed to create PR. API error: %s\n' "$GITHUB_API_ERROR_MESSAGE" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
6803
6892
  emit_error_event "github_pr_api_failed" "GitHub API error ($GITHUB_API_ERROR_TYPE): $GITHUB_API_ERROR_MESSAGE (HTTP $GITHUB_API_HTTP_STATUS)" "exit"
6804
6893
  if [ "${KASEKI_DEBUG:-0}" = "1" ]; then
6805
- printf 'Debug: API error type: %s, HTTP status: %s\n' "$GITHUB_API_ERROR_TYPE" "$GITHUB_API_HTTP_STATUS" | tee -a /results/git-push.log
6806
- printf 'Debug: Full response:\n%s\n' "$pr_response" | tee -a /results/git-push.log
6894
+ printf 'Debug: API error type: %s, HTTP status: %s\n' "$GITHUB_API_ERROR_TYPE" "$GITHUB_API_HTTP_STATUS" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6895
+ printf 'Debug: Full response:\n%s\n' "$pr_response" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
6807
6896
  fi
6808
6897
  GITHUB_PR_EXIT=9
6809
6898
  pr_created=0
@@ -6836,7 +6925,7 @@ if [ "$GITHUB_APP_ENABLED" = "1" ]; then
6836
6925
  printf 'ERROR: GitHub operations preflight health check failed\n' >&2
6837
6926
  printf 'GitHub App is enabled but configuration or dependencies are missing.\n' >&2
6838
6927
  printf 'Proceeding with kaseki run, but GitHub operations will be skipped or fail.\n' >&2
6839
- emit_error_event "github_preflight_failed" "GitHub operations health check failed; check /results/github-health-check.log for details" "continue"
6928
+ emit_error_event "github_preflight_failed" "GitHub operations health check failed; check ${KASEKI_RESULTS_DIR}/github-health-check.log for details" "continue"
6840
6929
  fi
6841
6930
  fi
6842
6931
 
@@ -6860,7 +6949,7 @@ unset OPENROUTER_API_KEY secret_content
6860
6949
  if [ -z "$openrouter_api_key" ]; then
6861
6950
  set_current_stage "agent setup"
6862
6951
  openrouter_api_key_file="${OPENROUTER_API_KEY_FILE:-/agents/secrets/openrouter_api_key}"
6863
- printf 'Missing OpenRouter API key. Set OPENROUTER_API_KEY or provide %s.\n' "$openrouter_api_key_file" | tee -a /results/pi-stderr.log >&2
6952
+ printf 'Missing OpenRouter API key. Set OPENROUTER_API_KEY or provide %s.\n' "$openrouter_api_key_file" | tee -a ${KASEKI_RESULTS_DIR}/pi-stderr.log >&2
6864
6953
  : > "$RAW_EVENTS"
6865
6954
  PI_EXIT=2
6866
6955
  STATUS=2
@@ -6871,7 +6960,7 @@ fi
6871
6960
  if ! run_clone_repository; then
6872
6961
  exit 0
6873
6962
  fi
6874
- cd /workspace/repo || { STATUS=1; FAILED_COMMAND="enter repository"; exit "$STATUS"; }
6963
+ cd ${KASEKI_WORKSPACE_DIR}/repo || { STATUS=1; FAILED_COMMAND="enter repository"; exit "$STATUS"; }
6875
6964
 
6876
6965
  prepare_dependencies() {
6877
6966
  if [ ! -f package.json ]; then
@@ -7101,7 +7190,7 @@ if [ "$KASEKI_BASELINE_VALIDATION_ENABLED" = "1" ] && [ "$KASEKI_PRE_AGENT_VALID
7101
7190
  emit_progress "baseline validation cache" "failed to save (non-blocking)"
7102
7191
  fi
7103
7192
  # Cleanup baseline workspace to save space
7104
- rm -rf /workspace-baseline 2>/dev/null || true
7193
+ rm -rf ${KASEKI_WORKSPACE_BASELINE_DIR} 2>/dev/null || true
7105
7194
  else
7106
7195
  BASELINE_CACHE_STATUS="checkout_failed"
7107
7196
  emit_error_event "baseline_checkout_failed" "Failed to setup baseline for test failure comparison; continuing without baseline" "continue"
@@ -7118,13 +7207,13 @@ if [ "$KASEKI_PRE_AGENT_VALIDATION" = "0" ]; then
7118
7207
  printf '\n==> pre-agent validation\n'
7119
7208
  set_current_stage "pre-agent validation"
7120
7209
  emit_progress "pre-agent validation" "skipped by KASEKI_PRE_AGENT_VALIDATION=0"
7121
- printf 'Pre-agent validation skipped because KASEKI_PRE_AGENT_VALIDATION=0.\n' | tee -a /results/pre-validation.log
7210
+ printf 'Pre-agent validation skipped because KASEKI_PRE_AGENT_VALIDATION=0.\n' | tee -a ${KASEKI_RESULTS_DIR}/pre-validation.log
7122
7211
  record_stage_timing "pre-agent validation" 0 0 "skipped_by_config"
7123
7212
  else
7124
7213
  run_validation_commands \
7125
7214
  "pre-agent validation" \
7126
7215
  "$KASEKI_PRE_AGENT_VALIDATION_COMMANDS" \
7127
- /results/pre-validation.log \
7216
+ ${KASEKI_RESULTS_DIR}/pre-validation.log \
7128
7217
  "$PRE_VALIDATION_RAW_LOG" \
7129
7218
  "$PRE_VALIDATION_TIMINGS_FILE" \
7130
7219
  "$PRE_VALIDATION_ENV_LOG" \
@@ -7153,7 +7242,7 @@ set_current_stage "typescript precheck"
7153
7242
  if ! run_typescript_precheck; then
7154
7243
  if [ "$KASEKI_SCOUTING" = "1" ]; then
7155
7244
  # If scouting is enabled (experimental path), continue anyway with warning
7156
- printf 'WARNING: TypeScript pre-check failed, but continuing due to scouting mode being enabled.\n' | tee -a /results/quality.log
7245
+ printf 'WARNING: TypeScript pre-check failed, but continuing due to scouting mode being enabled.\n' | tee -a ${KASEKI_RESULTS_DIR}/quality.log
7157
7246
  else
7158
7247
  # Without scouting, TypeScript failures are fatal
7159
7248
  STATUS="$TS_PRE_CHECK_EXIT"
@@ -7169,16 +7258,16 @@ printf 'Pi version: %s\n' "$PI_VERSION"
7169
7258
  # === Phase 1: Early Filesystem Diagnostics (Before Scouting) ===
7170
7259
  # Detects read-only filesystem constraints that would cause silent scouting failures
7171
7260
  check_filesystem_capabilities() {
7172
- local results_dir="${KASEKI_RESULTS_DIR:-/results}"
7261
+ local results_dir="${KASEKI_RESULTS_DIR:-${KASEKI_RESULTS_DIR}}"
7173
7262
  local filesystem_writable=true
7174
7263
  local readonly_reason=""
7175
7264
 
7176
7265
  emit_progress "filesystem capabilities check" "verifying write capabilities for artifacts"
7177
7266
 
7178
- # Test /results/ writability
7267
+ # Test ${KASEKI_RESULTS_DIR}/ writability
7179
7268
  if [ ! -w "$results_dir" ]; then
7180
7269
  filesystem_writable=false
7181
- readonly_reason="/results is READ-ONLY (Docker mounted with :ro or container --read-only flag)"
7270
+ readonly_reason="${KASEKI_RESULTS_DIR} is READ-ONLY (Docker mounted with :ro or container --read-only flag)"
7182
7271
  emit_error_event "readonly_filesystem_detected" "$readonly_reason" "continue"
7183
7272
  {
7184
7273
  printf '\n[FILESYSTEM DIAGNOSTIC] READ-ONLY FILESYSTEM DETECTED\n'
@@ -7188,35 +7277,35 @@ check_filesystem_capabilities() {
7188
7277
  printf ' - Container UID: %d\n' "$(id -u)"
7189
7278
  printf ' - Expected reason: Docker mounted with :ro flag or container --read-only\n'
7190
7279
  printf '\nImpact:\n'
7191
- printf ' - Scouting Pi agent will exit 0 but /results/scouting-candidate.json will be MISSING\n'
7280
+ printf ' - Scouting Pi agent will exit 0 but ${KASEKI_RESULTS_DIR}/scouting-candidate.json will be MISSING\n'
7192
7281
  printf ' - Validation logs and artifacts cannot be written\n'
7193
7282
  printf ' - This causes exit code 86 (scouting validation failure)\n'
7194
- printf '\nFix: Remount /results as read-write\n'
7195
- printf ' docker run -v /path/to/results:/results:rw ...\n'
7283
+ printf '\nFix: Remount ${KASEKI_RESULTS_DIR} as read-write\n'
7284
+ printf ' docker run -v /path/to${KASEKI_RESULTS_DIR}:${KASEKI_RESULTS_DIR}:rw ...\n'
7196
7285
  printf 'Or remove --read-only flag from Docker run command\n'
7197
- } | tee -a /results/scouting-stderr.log
7286
+ } | tee -a ${KASEKI_RESULTS_DIR}/scouting-stderr.log
7198
7287
  else
7199
7288
  # Test actual write capability
7200
7289
  local test_file="$results_dir/.kaseki-fs-test-$$"
7201
7290
  if ! touch "$test_file" 2>/dev/null; then
7202
7291
  filesystem_writable=false
7203
- readonly_reason="/results is not writable (touch failed despite appearing writable)"
7292
+ readonly_reason="${KASEKI_RESULTS_DIR} is not writable (touch failed despite appearing writable)"
7204
7293
  emit_error_event "filesystem_write_test_failed" "$readonly_reason" "continue"
7205
7294
  else
7206
7295
  rm -f "$test_file" 2>/dev/null || true
7207
- emit_progress "filesystem capabilities check" "✓ /results is writable"
7296
+ emit_progress "filesystem capabilities check" "✓ ${KASEKI_RESULTS_DIR} is writable"
7208
7297
  fi
7209
7298
  fi
7210
7299
 
7211
7300
  # Record in metadata for post-mortem analysis
7212
- printf '%s\n' "$filesystem_writable" > /results/filesystem-writable-at-start.txt
7213
- [ -n "$readonly_reason" ] && printf '%s\n' "$readonly_reason" > /results/filesystem-readonly-reason.txt
7301
+ printf '%s\n' "$filesystem_writable" > ${KASEKI_RESULTS_DIR}/filesystem-writable-at-start.txt
7302
+ [ -n "$readonly_reason" ] && printf '%s\n' "$readonly_reason" > ${KASEKI_RESULTS_DIR}/filesystem-readonly-reason.txt
7214
7303
 
7215
7304
  if [ "$filesystem_writable" = "false" ]; then
7216
7305
  if [ "$KASEKI_BASELINE_VALIDATION_ENABLED" = "1" ]; then
7217
7306
  emit_progress "baseline validation preparation" "DISABLED due to read-only filesystem detected"
7218
7307
  KASEKI_BASELINE_VALIDATION_ENABLED="0"
7219
- printf '[filesystem-diagnostic] Baseline validation auto-disabled due to read-only filesystem\n' | tee -a /results/quality.log
7308
+ printf '[filesystem-diagnostic] Baseline validation auto-disabled due to read-only filesystem\n' | tee -a ${KASEKI_RESULTS_DIR}/quality.log
7220
7309
  fi
7221
7310
  return 1
7222
7311
  fi
@@ -7266,7 +7355,7 @@ if [ "$KASEKI_SCOUTING" = "1" ] && [ -f "$SCOUTING_ARTIFACT" ]; then
7266
7355
  export KASEKI_VALIDATION_ALLOWLIST="$merged_validation_allowlist"
7267
7356
 
7268
7357
  # Log merge decisions with structured JSON construction so pattern text is escaped safely.
7269
- append_jsonl_object /results/metadata.jsonl \
7358
+ append_jsonl_object ${KASEKI_RESULTS_DIR}/metadata.jsonl \
7270
7359
  "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
7271
7360
  "event=allowlist_merge" \
7272
7361
  "scouting_agent_patterns=$scouting_agent_patterns" \
@@ -7279,14 +7368,14 @@ if [ "$KASEKI_SCOUTING" = "1" ] && [ -f "$SCOUTING_ARTIFACT" ]; then
7279
7368
  allowlist_merge_status="merged"
7280
7369
 
7281
7370
  # Run coverage validation with dry-run
7282
- if [ -s /results/changed-files.txt ]; then
7283
- run_scouting_allowlist_coverage "$SCOUTING_ARTIFACT" 2>&1 | tee -a /results/quality.log
7371
+ if [ -s ${KASEKI_RESULTS_DIR}/changed-files.txt ]; then
7372
+ run_scouting_allowlist_coverage "$SCOUTING_ARTIFACT" 2>&1 | tee -a ${KASEKI_RESULTS_DIR}/quality.log
7284
7373
  fi
7285
7374
 
7286
7375
  emit_progress "derive allowlist from scouting" "finished (status=$allowlist_merge_status)"
7287
7376
  else
7288
7377
  # Pattern validation failed - fail fast
7289
- printf 'ERROR: Derived allowlist patterns failed validation. Cannot proceed.\n' | tee -a /results/quality.log >&2
7378
+ printf 'ERROR: Derived allowlist patterns failed validation. Cannot proceed.\n' | tee -a ${KASEKI_RESULTS_DIR}/quality.log >&2
7290
7379
  STATUS=86
7291
7380
  FAILED_COMMAND="allowlist pattern validation"
7292
7381
  emit_error_event "scouting_allowlist_invalid" "Derived allowlist patterns failed validation" "exit"
@@ -7294,7 +7383,7 @@ if [ "$KASEKI_SCOUTING" = "1" ] && [ -f "$SCOUTING_ARTIFACT" ]; then
7294
7383
  fi
7295
7384
  else
7296
7385
  # Derivation failed - log and fail fast
7297
- printf 'ERROR: Failed to derive allowlist from scouting artifact: %s\n' "$scouting_output" | tee -a /results/quality.log >&2
7386
+ printf 'ERROR: Failed to derive allowlist from scouting artifact: %s\n' "$scouting_output" | tee -a ${KASEKI_RESULTS_DIR}/quality.log >&2
7298
7387
  STATUS=86
7299
7388
  FAILED_COMMAND="allowlist derivation from scouting"
7300
7389
  emit_error_event "scouting_allowlist_derivation_failed" "Failed to derive allowlist from scouting artifact" "exit"
@@ -7332,7 +7421,7 @@ if [ "$KASEKI_DRY_RUN" = "1" ]; then
7332
7421
  printf ' Model: %s\n' "$KASEKI_MODEL"
7333
7422
  printf ' Timeout: %s seconds\n' "$KASEKI_AGENT_TIMEOUT_SECONDS"
7334
7423
  printf ' Task: %s\n' "$TASK_PROMPT"
7335
- } | tee -a /results/pi-stderr.log
7424
+ } | tee -a ${KASEKI_RESULTS_DIR}/pi-stderr.log
7336
7425
  emit_progress "pi coding agent" "skipped (dry-run)"
7337
7426
  record_stage_timing "pi coding agent" "0" "$PI_DURATION_SECONDS" "dry_run=true"
7338
7427
  else
@@ -7344,9 +7433,9 @@ else
7344
7433
  OPENROUTER_API_KEY="$openrouter_api_key" \
7345
7434
  timeout --signal=SIGTERM "$KASEKI_AGENT_TIMEOUT_SECONDS" \
7346
7435
  pi --mode json --no-session --provider "$KASEKI_PROVIDER" --model "$KASEKI_MODEL" "$agent_prompt" \
7347
- 2> >(tee -a /results/pi-stderr.log >&2) \
7436
+ 2> >(tee -a ${KASEKI_RESULTS_DIR}/pi-stderr.log >&2) \
7348
7437
  | tee "$RAW_EVENTS" \
7349
- | kaseki-pi-progress-stream /results/progress.jsonl /results/progress.log
7438
+ | kaseki-pi-progress-stream ${KASEKI_RESULTS_DIR}/progress.jsonl ${KASEKI_RESULTS_DIR}/progress.log
7350
7439
  PI_EXIT="${PIPESTATUS[0]}"
7351
7440
  unset agent_prompt
7352
7441
  PI_DURATION_SECONDS=$(($(date +%s) - PI_START_EPOCH))
@@ -7355,7 +7444,7 @@ else
7355
7444
  record_stage_timing "pi coding agent" "$PI_EXIT" "$PI_DURATION_SECONDS" "timeout_seconds=$KASEKI_AGENT_TIMEOUT_SECONDS"
7356
7445
 
7357
7446
  if [ "$KASEKI_DEBUG_RAW_EVENTS" = "1" ]; then
7358
- cp "$RAW_EVENTS" /results/pi-events.raw.jsonl
7447
+ cp "$RAW_EVENTS" ${KASEKI_RESULTS_DIR}/pi-events.raw.jsonl
7359
7448
  fi
7360
7449
 
7361
7450
  PI_EXTRACTION_DEPS_OK=1
@@ -7366,7 +7455,7 @@ else
7366
7455
  missing_executables+=("$required_exec")
7367
7456
  fi
7368
7457
  done
7369
- for helper_file in /app/lib/event-aggregator.js /app/lib/timestamp-tracker.js /app/lib/progress-stream-utils.js; do
7458
+ for helper_file in ${KASEKI_APP_LIB_DIR}/event-aggregator.js ${KASEKI_APP_LIB_DIR}/timestamp-tracker.js ${KASEKI_APP_LIB_DIR}/progress-stream-utils.js; do
7370
7459
  if [ ! -f "$helper_file" ]; then
7371
7460
  missing_helpers+=("$helper_file")
7372
7461
  fi
@@ -7379,34 +7468,34 @@ else
7379
7468
  [ -z "$missing_helpers_joined" ] && missing_helpers_joined="none"
7380
7469
  extraction_error=$(node -e "console.log(JSON.stringify({error:'pi_extraction_dependency_missing',missing_executables:process.argv[1],missing_helpers:process.argv[2],action:'Ensure required Pi binaries are on PATH and helper files exist in the image before running extraction'}))" "$missing_execs_joined" "$missing_helpers_joined")
7381
7470
  printf '%s
7382
- ' "$extraction_error" | tee -a /results/pi-stderr.log /results/quality.log >&2
7471
+ ' "$extraction_error" | tee -a ${KASEKI_RESULTS_DIR}/pi-stderr.log ${KASEKI_RESULTS_DIR}/quality.log >&2
7383
7472
  emit_error_event "pi_extraction_dependency_missing" "missing executables: $missing_execs_joined; missing helpers: $missing_helpers_joined; ensure Pi binaries are in PATH and /app/lib helpers are present" "abort_extraction"
7384
7473
  if [ "$STATUS" -eq 0 ]; then
7385
7474
  STATUS=87
7386
7475
  FAILED_COMMAND="pi artifact extraction dependency validation"
7387
7476
  fi
7388
- cp "$RAW_EVENTS" /results/pi-events.raw.jsonl 2>/dev/null || true
7477
+ cp "$RAW_EVENTS" ${KASEKI_RESULTS_DIR}/pi-events.raw.jsonl 2>/dev/null || true
7389
7478
  fi
7390
7479
 
7391
7480
  FILTER_EXIT=0
7392
7481
  if [ "$PI_EXTRACTION_DEPS_OK" -eq 1 ]; then
7393
7482
  set +e
7394
- kaseki-pi-event-filter "$RAW_EVENTS" /results/pi-events.jsonl /results/pi-summary.json
7483
+ kaseki-pi-event-filter "$RAW_EVENTS" ${KASEKI_RESULTS_DIR}/pi-events.jsonl ${KASEKI_RESULTS_DIR}/pi-summary.json
7395
7484
  FILTER_EXIT=$?
7396
7485
  set +e
7397
7486
  fi
7398
7487
  if [ "$FILTER_EXIT" -ne 0 ]; then
7399
- printf 'pi-event-filter failed with exit %s; raw events preserved as fallback artifact\n' "$FILTER_EXIT" | tee -a /results/quality.log
7400
- printf 'ERROR: kaseki-pi-event-filter failed with exit %s while exporting Pi events\n' "$FILTER_EXIT" | tee -a /results/pi-stderr.log >&2
7488
+ printf 'pi-event-filter failed with exit %s; raw events preserved as fallback artifact\n' "$FILTER_EXIT" | tee -a ${KASEKI_RESULTS_DIR}/quality.log
7489
+ printf 'ERROR: kaseki-pi-event-filter failed with exit %s while exporting Pi events\n' "$FILTER_EXIT" | tee -a ${KASEKI_RESULTS_DIR}/pi-stderr.log >&2
7401
7490
  emit_error_event "pi_event_filter_failed" "kaseki-pi-event-filter exited with code $FILTER_EXIT" "continue"
7402
7491
  if [ "$STATUS" -eq 0 ]; then
7403
7492
  STATUS="$FILTER_EXIT"
7404
7493
  FAILED_COMMAND="kaseki-pi-event-filter"
7405
7494
  fi
7406
- cp "$RAW_EVENTS" /results/pi-events.raw.jsonl 2>/dev/null || true
7495
+ cp "$RAW_EVENTS" ${KASEKI_RESULTS_DIR}/pi-events.raw.jsonl 2>/dev/null || true
7407
7496
  fi
7408
- if [ -s "$RAW_EVENTS" ] && { [ ! -s /results/pi-events.jsonl ] || [ ! -s /results/pi-summary.json ]; }; then
7409
- printf 'ERROR: pi event export incomplete; raw events are non-empty but event artifacts are missing/empty\n' | tee -a /results/pi-stderr.log >&2
7497
+ if [ -s "$RAW_EVENTS" ] && { [ ! -s ${KASEKI_RESULTS_DIR}/pi-events.jsonl ] || [ ! -s ${KASEKI_RESULTS_DIR}/pi-summary.json ]; }; then
7498
+ printf 'ERROR: pi event export incomplete; raw events are non-empty but event artifacts are missing/empty\n' | tee -a ${KASEKI_RESULTS_DIR}/pi-stderr.log >&2
7410
7499
  emit_error_event "pi_event_export_incomplete" "RAW_EVENTS has data but exported artifacts are empty or missing" "continue"
7411
7500
  if [ "$STATUS" -eq 0 ]; then
7412
7501
  STATUS=86
@@ -7415,16 +7504,16 @@ else
7415
7504
  fi
7416
7505
 
7417
7506
  # Process hashline_edit events (non-fatal phase; failures don't block pipeline)
7418
- if [ "$KASEKI_HASHLINE_EDITS" != "0" ] && [ -s /results/pi-events.jsonl ]; then
7507
+ if [ "$KASEKI_HASHLINE_EDITS" != "0" ] && [ -s ${KASEKI_RESULTS_DIR}/pi-events.jsonl ]; then
7419
7508
  emit_progress "hashline validation" "started"
7420
7509
  HASHLINE_EXIT=0
7421
7510
  set +e
7422
- npx tsx /app/lib/hashline-event-handler-cli.js /results/pi-events.jsonl /workspace /results/hashline-events.jsonl /results/hashline-summary.json 2>> /results/hashline-validation.log
7511
+ npx tsx ${KASEKI_APP_LIB_DIR}/hashline-event-handler-cli.js ${KASEKI_RESULTS_DIR}/pi-events.jsonl /workspace ${KASEKI_RESULTS_DIR}/hashline-events.jsonl ${KASEKI_RESULTS_DIR}/hashline-summary.json 2>> ${KASEKI_RESULTS_DIR}/hashline-validation.log
7423
7512
  HASHLINE_EXIT=$?
7424
7513
  set +e
7425
7514
 
7426
7515
  if [ "$HASHLINE_EXIT" -ne 0 ]; then
7427
- printf 'Warning: hashline validation exited with code %s (non-fatal; continuing pipeline)\n' "$HASHLINE_EXIT" | tee -a /results/hashline-validation.log
7516
+ printf 'Warning: hashline validation exited with code %s (non-fatal; continuing pipeline)\n' "$HASHLINE_EXIT" | tee -a ${KASEKI_RESULTS_DIR}/hashline-validation.log
7428
7517
  emit_event "warning" "warning_type=hashline_validation_failed" "detail=hashline_edit processing exited with code $HASHLINE_EXIT"
7429
7518
  else
7430
7519
  emit_progress "hashline validation" "completed"
@@ -7455,7 +7544,7 @@ else
7455
7544
  }
7456
7545
  var m='';
7457
7546
  try{
7458
- var summary=require('/results/pi-summary.json');
7547
+ var summary=require('${KASEKI_RESULTS_DIR}/pi-summary.json');
7459
7548
  m=clean(summary.selected_model)||clean(summary.model)||fromSummaryModels(summary);
7460
7549
  }catch{}
7461
7550
  if(!m){
@@ -7481,7 +7570,7 @@ fi
7481
7570
 
7482
7571
  if [ "$KASEKI_DRY_RUN" != "1" ]; then
7483
7572
  if [ "$PI_EXIT" -eq 124 ]; then
7484
- printf 'pi timeout after %ss (exit 124)\n' "$KASEKI_AGENT_TIMEOUT_SECONDS" | tee -a /results/pi-stderr.log >&2
7573
+ printf 'pi timeout after %ss (exit 124)\n' "$KASEKI_AGENT_TIMEOUT_SECONDS" | tee -a ${KASEKI_RESULTS_DIR}/pi-stderr.log >&2
7485
7574
  if [ "$STATUS" -eq 0 ]; then
7486
7575
  STATUS=124
7487
7576
  FAILED_COMMAND="pi coding agent timeout"
@@ -7520,11 +7609,11 @@ printf '\n==> quality checks\n'
7520
7609
  set_current_stage "quality checks"
7521
7610
  emit_progress "quality checks" "started"
7522
7611
  stage_start="$(date +%s)"
7523
- diff_size="$(wc -c < /results/git.diff | tr -d ' ')"
7612
+ diff_size="$(wc -c < ${KASEKI_RESULTS_DIR}/git.diff | tr -d ' ')"
7524
7613
  if [ "$diff_size" -gt "$KASEKI_MAX_DIFF_BYTES" ]; then
7525
7614
  QUALITY_EXIT=4
7526
7615
  QUALITY_FAILURE_REASON="max_diff_bytes: $diff_size bytes exceeds limit of $KASEKI_MAX_DIFF_BYTES bytes"
7527
- printf 'git.diff is too large: %s bytes > %s bytes\n' "$diff_size" "$KASEKI_MAX_DIFF_BYTES" | tee -a /results/quality.log
7616
+ printf 'git.diff is too large: %s bytes > %s bytes\n' "$diff_size" "$KASEKI_MAX_DIFF_BYTES" | tee -a ${KASEKI_RESULTS_DIR}/quality.log
7528
7617
  emit_event "quality_gate_rule_evaluated" "rule=max_diff_bytes" "passed=false" "actual=$diff_size" "limit=$KASEKI_MAX_DIFF_BYTES"
7529
7618
  else
7530
7619
  emit_event "quality_gate_rule_evaluated" "rule=max_diff_bytes" "passed=true" "actual=$diff_size" "limit=$KASEKI_MAX_DIFF_BYTES"
@@ -7539,17 +7628,17 @@ if [ -n "$allowlist_regex" ]; then
7539
7628
  if ! printf '%s\n' "$changed_file" | grep -Eq "^(${allowlist_regex})$"; then
7540
7629
  QUALITY_EXIT=5
7541
7630
  QUALITY_FAILURE_REASON="allowlist_check: file '$changed_file' not in allowlist"
7542
- printf 'changed file outside allowlist: %s\n' "$changed_file" | tee -a /results/quality.log
7631
+ printf 'changed file outside allowlist: %s\n' "$changed_file" | tee -a ${KASEKI_RESULTS_DIR}/quality.log
7543
7632
  emit_event "quality_gate_rule_evaluated" "rule=allowlist_check" "passed=false" "file=$changed_file"
7544
7633
  else
7545
7634
  emit_event "quality_gate_rule_evaluated" "rule=allowlist_check" "passed=true" "file=$changed_file"
7546
7635
  fi
7547
- done < /results/changed-files.txt
7636
+ done < ${KASEKI_RESULTS_DIR}/changed-files.txt
7548
7637
  fi
7549
7638
 
7550
7639
  if [ -f package.json ] && node -e "const p=require('./package.json'); process.exit(p.scripts && p.scripts['format:check'] ? 0 : 1)" 2>/dev/null; then
7551
7640
  format_command="npm run format:check"
7552
- printf '%s\n' "$format_command" >> /results/format-check-command.txt
7641
+ printf '%s\n' "$format_command" >> ${KASEKI_RESULTS_DIR}/format-check-command.txt
7553
7642
  fi
7554
7643
  record_stage_timing "quality checks" "$QUALITY_EXIT" "$(($(date +%s) - stage_start))" "diff_size_bytes=$diff_size"
7555
7644
 
@@ -7558,7 +7647,7 @@ run_expectation_mismatch_detector
7558
7647
 
7559
7648
  pre_validation_goal_check_diff_hash=""
7560
7649
  if [ "$STATUS" -eq 0 ] && [ "$PI_EXIT" -eq 0 ] && [ "$QUALITY_EXIT" -eq 0 ]; then
7561
- pre_validation_goal_check_diff_hash="$(sha256sum /results/git.diff 2>/dev/null | awk '{print $1}')"
7650
+ pre_validation_goal_check_diff_hash="$(sha256sum ${KASEKI_RESULTS_DIR}/git.diff 2>/dev/null | awk '{print $1}')"
7562
7651
  run_goal_check "$coding_attempt"
7563
7652
  collect_goal_check_feedback "$INSTANCE_NAME"
7564
7653
  snapshot_attempt_artifacts "$coding_attempt"
@@ -7590,18 +7679,18 @@ log_validation_environment() {
7590
7679
  printf '[validation environment] PATH=%s\n' "$PATH"
7591
7680
  printf '[validation environment] NODE_OPTIONS=%s\n' "${NODE_OPTIONS:-<not set>}"
7592
7681
  printf '[validation environment] NODE_PATH=%s\n' "${NODE_PATH:-<not set>}"
7593
- printf '[validation environment] disk_space_available=%s\n' "$(df -h /results 2>/dev/null | tail -1 | awk '{print $4}' || echo '<df failed>')"
7594
- printf '[validation environment] disk_space_used=%s\n' "$(du -sh /results 2>/dev/null | cut -f1 || echo '<du failed>')"
7595
- } | tee -a /results/validation.log "$VALIDATION_ENV_LOG"
7682
+ printf '[validation environment] disk_space_available=%s\n' "$(df -h ${KASEKI_RESULTS_DIR} 2>/dev/null | tail -1 | awk '{print $4}' || echo '<df failed>')"
7683
+ printf '[validation environment] disk_space_used=%s\n' "$(du -sh ${KASEKI_RESULTS_DIR} 2>/dev/null | cut -f1 || echo '<du failed>')"
7684
+ } | tee -a ${KASEKI_RESULTS_DIR}/validation.log "$VALIDATION_ENV_LOG"
7596
7685
  }
7597
7686
  log_validation_environment
7598
- collect_changed_file_state /results/validation-before-state.txt
7687
+ collect_changed_file_state ${KASEKI_RESULTS_DIR}/validation-before-state.txt
7599
7688
 
7600
7689
  if [ "$KASEKI_DRY_RUN" = "1" ] || [ -z "$KASEKI_VALIDATION_COMMANDS" ] || [ "$KASEKI_VALIDATION_COMMANDS" = "none" ]; then
7601
7690
  run_validation_commands \
7602
7691
  "validation" \
7603
7692
  "$KASEKI_VALIDATION_COMMANDS" \
7604
- /results/validation.log \
7693
+ ${KASEKI_RESULTS_DIR}/validation.log \
7605
7694
  "$VALIDATION_RAW_LOG" \
7606
7695
  "$VALIDATION_TIMINGS_FILE" \
7607
7696
  "$VALIDATION_ENV_LOG" \
@@ -7610,7 +7699,7 @@ elif [ "$QUALITY_EXIT" -ne 0 ]; then
7610
7699
  printf '\n==> validation\n'
7611
7700
  set_current_stage "validation"
7612
7701
  emit_progress "validation" "started"
7613
- printf 'Validation skipped because quality gates failed with exit %s.\n' "$QUALITY_EXIT" | tee -a /results/validation.log
7702
+ printf 'Validation skipped because quality gates failed with exit %s.\n' "$QUALITY_EXIT" | tee -a ${KASEKI_RESULTS_DIR}/validation.log
7614
7703
  VALIDATION_EXIT="$QUALITY_EXIT"
7615
7704
  if [ -z "$VALIDATION_FAILURE_REASON" ]; then
7616
7705
  VALIDATION_FAILURE_REASON="quality_gate_failed: $QUALITY_FAILURE_REASON"
@@ -7621,14 +7710,14 @@ elif [ "$PI_EXIT" -ne 0 ] && [ "$KASEKI_VALIDATE_AFTER_AGENT_FAILURE" != "1" ];
7621
7710
  printf '\n==> validation\n'
7622
7711
  set_current_stage "validation"
7623
7712
  emit_progress "validation" "started"
7624
- printf 'Validation skipped because pi coding agent failed with exit %s. Set KASEKI_VALIDATE_AFTER_AGENT_FAILURE=1 to run validation anyway.\n' "$PI_EXIT" | tee -a /results/validation.log
7713
+ printf 'Validation skipped because pi coding agent failed with exit %s. Set KASEKI_VALIDATE_AFTER_AGENT_FAILURE=1 to run validation anyway.\n' "$PI_EXIT" | tee -a ${KASEKI_RESULTS_DIR}/validation.log
7625
7714
  record_stage_timing "validation" "$PI_EXIT" 0 "skipped_after_agent_failure"
7626
7715
  emit_progress "validation" "finished with exit $VALIDATION_EXIT"
7627
7716
  else
7628
7717
  run_validation_commands \
7629
7718
  "validation" \
7630
7719
  "$KASEKI_VALIDATION_COMMANDS" \
7631
- /results/validation.log \
7720
+ ${KASEKI_RESULTS_DIR}/validation.log \
7632
7721
  "$VALIDATION_RAW_LOG" \
7633
7722
  "$VALIDATION_TIMINGS_FILE" \
7634
7723
  "$VALIDATION_ENV_LOG" \
@@ -7642,18 +7731,18 @@ fi
7642
7731
 
7643
7732
  # Check validation-phase allowlist (if configured)
7644
7733
  if [ "$VALIDATION_EXIT" -eq 0 ]; then
7645
- collect_changed_file_state /results/validation-after-state.txt
7734
+ collect_changed_file_state ${KASEKI_RESULTS_DIR}/validation-after-state.txt
7646
7735
  collect_git_artifacts
7647
7736
  if ! check_validation_allowlist; then
7648
7737
  : # Exit code already set in check_validation_allowlist
7649
7738
  fi
7650
7739
  fi
7651
7740
 
7652
- post_validation_goal_check_diff_hash="$(sha256sum /results/git.diff 2>/dev/null | awk '{print $1}')"
7741
+ post_validation_goal_check_diff_hash="$(sha256sum ${KASEKI_RESULTS_DIR}/git.diff 2>/dev/null | awk '{print $1}')"
7653
7742
  if [ "$STATUS" -eq 0 ] && [ "$PI_EXIT" -eq 0 ] && [ "$QUALITY_EXIT" -eq 0 ] && [ "$VALIDATION_EXIT" -eq 0 ] && \
7654
7743
  [ -n "$pre_validation_goal_check_diff_hash" ] && [ -n "$post_validation_goal_check_diff_hash" ] && \
7655
7744
  [ "$post_validation_goal_check_diff_hash" != "$pre_validation_goal_check_diff_hash" ]; then
7656
- printf 'Validation commands changed the final git diff; re-running goal check against post-validation artifacts.\n' | tee -a /results/goal-check-stderr.log
7745
+ printf 'Validation commands changed the final git diff; re-running goal check against post-validation artifacts.\n' | tee -a ${KASEKI_RESULTS_DIR}/goal-check-stderr.log
7657
7746
  emit_progress "goal check" "re-running after validation changed the final diff (attempt $coding_attempt)"
7658
7747
  run_goal_check "$coding_attempt"
7659
7748
  collect_goal_check_feedback "$INSTANCE_NAME"
@@ -7687,14 +7776,14 @@ printf '\n==> secret scan\n'
7687
7776
  set_current_stage "secret scan"
7688
7777
  emit_progress "secret scan" "started"
7689
7778
  stage_start="$(date +%s)"
7690
- : > /results/secret-scan.log
7779
+ : > ${KASEKI_RESULTS_DIR}/secret-scan.log
7691
7780
  if [ "$KASEKI_DRY_RUN" = "1" ]; then
7692
- printf '🔄 DRY-RUN MODE: Skipping secret scan (no artifacts to scan)\n' | tee -a /results/secret-scan.log
7781
+ printf '🔄 DRY-RUN MODE: Skipping secret scan (no artifacts to scan)\n' | tee -a ${KASEKI_RESULTS_DIR}/secret-scan.log
7693
7782
  SECRET_SCAN_EXIT=0
7694
7783
  record_stage_timing "secret scan" "0" "$(($(date +%s) - stage_start))" "dry_run=true"
7695
7784
  else
7696
7785
  # Run the initial scan
7697
- if grep -R -n -E 'sk-or-[A-Za-z0-9_-]{20,}' /results /workspace/repo/.git /workspace/repo/src /workspace/repo/tests 2>/dev/null | grep -v '/secret-scan.log:' > /results/secret-scan.log; then
7786
+ if grep -R -n -E 'sk-or-[A-Za-z0-9_-]{20,}' ${KASEKI_RESULTS_DIR} ${KASEKI_WORKSPACE_DIR}/repo/.git ${KASEKI_WORKSPACE_DIR}/repo/src ${KASEKI_WORKSPACE_DIR}/repo/tests 2>/dev/null | grep -v '/secret-scan.log:' > ${KASEKI_RESULTS_DIR}/secret-scan.log; then
7698
7787
  # Matches found - check against allowlist
7699
7788
  if check_secret_scan_allowlist; then
7700
7789
  # All matches are allowlisted
@@ -7747,7 +7836,7 @@ printf '\n==> github operations\n'
7747
7836
  set_current_stage "github operations"
7748
7837
  emit_progress "github operations" "started"
7749
7838
  stage_start="$(date +%s)"
7750
- : > /results/git-push.log
7839
+ : > ${KASEKI_RESULTS_DIR}/git-push.log
7751
7840
  build_github_skip_reasons
7752
7841
  if [ "${#GITHUB_SKIP_REASONS[@]}" -eq 0 ]; then
7753
7842
  github_app_id_file="$(resolve_github_secret_file "GITHUB_APP_ID_FILE" "github_app_id")"
@@ -7766,7 +7855,7 @@ if [ "${#GITHUB_SKIP_REASONS[@]}" -eq 0 ]; then
7766
7855
  else
7767
7856
  GITHUB_SKIP_REASONS+=("github_app_secrets_missing")
7768
7857
  GITHUB_OPERATION_PHASE="secrets"
7769
- printf -- 'GitHub operations: skipped (reasons: %s)\n' "$(IFS=,; printf '%s' "${GITHUB_SKIP_REASONS[*]}")" | tee -a /results/git-push.log >&2
7858
+ printf -- 'GitHub operations: skipped (reasons: %s)\n' "$(IFS=,; printf '%s' "${GITHUB_SKIP_REASONS[*]}")" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log >&2
7770
7859
  emit_progress "github operations" "skipped: $(IFS=,; printf '%s' "${GITHUB_SKIP_REASONS[*]}")"
7771
7860
  GITHUB_PUSH_EXIT=7
7772
7861
  fi
@@ -7778,7 +7867,7 @@ else
7778
7867
  "$([ "$QUALITY_EXIT" -eq 0 ] && printf 'passed' || printf 'failed')" \
7779
7868
  "$([ "$SECRET_SCAN_EXIT" -eq 0 ] && printf 'passed' || printf 'failed')" \
7780
7869
  "$DIFF_NONEMPTY" \
7781
- "$GITHUB_APP_ENABLED" | tee -a /results/git-push.log
7870
+ "$GITHUB_APP_ENABLED" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
7782
7871
  emit_progress "github operations" "skipped: $(IFS=,; printf '%s' "${GITHUB_SKIP_REASONS[*]}")"
7783
7872
  fi
7784
7873
  if [ "$GITHUB_APP_ENABLED" = "1" ]; then