@cyanautomation/kaseki-agent 1.64.0 → 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.
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"
@@ -470,143 +534,15 @@ validate_scouting_artifact_with_node() {
470
534
  local final_artifact="$2"
471
535
  local validation_error_file="$3"
472
536
 
473
- # shellcheck disable=SC2016
474
- node -e '
475
- const fs = require("node:fs");
476
- const input = process.argv[1];
477
- const output = process.argv[2];
478
- const errorLog = process.argv[3];
479
- const jsonlLog = "/results/scouting-validation-errors.jsonl";
480
-
481
- function actualType(value) {
482
- if (value === null) return "null";
483
- if (Array.isArray(value)) return "array";
484
- return typeof value;
485
- }
486
-
487
- function appendValidationFailure(reasonCode, error) {
488
- fs.appendFileSync(jsonlLog, JSON.stringify({
489
- timestamp: new Date().toISOString(),
490
- reason_code: reasonCode,
491
- ...error,
492
- }) + "\n");
493
- }
494
-
495
- function summarize(errors) {
496
- const critical = errors.filter((error) => error.severity === "critical").length;
497
- const warning = errors.filter((error) => error.severity === "warning").length;
498
- const counts = [];
499
- if (critical) counts.push(`${critical} critical`);
500
- if (warning) counts.push(`${warning} warning`);
501
- const fields = errors.slice(0, 2).map((error) => error.field).join(", ");
502
- const suffix = errors.length > 2 ? `, +${errors.length - 2} more` : "";
503
- return `${counts.join(", ")} scouting validation ${errors.length === 1 ? "error" : "errors"}: ${fields}${suffix}`;
504
- }
505
-
506
- let artifact;
507
- try {
508
- artifact = JSON.parse(fs.readFileSync(input, "utf8"));
509
- } catch (err) {
510
- const reasonCode = "malformed_json";
511
- const error = {
512
- field: "root",
513
- expected: "exactly one valid JSON object",
514
- actual: err && err.message ? String(err.message) : "JSON parse failed",
515
- severity: "critical",
516
- suggestion: "ensure exactly one valid JSON object is written to /results/scouting-candidate.json",
517
- };
518
- appendValidationFailure(reasonCode, error);
519
- fs.writeFileSync(errorLog, JSON.stringify({
520
- reason_code: reasonCode,
521
- details: summarize([error]),
522
- errors: [error],
523
- }) + "\n");
524
- process.exit(1);
525
- }
526
-
527
- const errors = [];
528
- const addError = (field, expected, actual, severity, suggestion) => {
529
- errors.push({ field, expected, actual, severity, suggestion });
530
- };
531
- const arrayKeys = ["requirements", "relevant_files", "observations", "plan", "validation", "risks", "test_impact"];
532
-
533
- if (!artifact || Array.isArray(artifact) || typeof artifact !== "object") {
534
- addError("root", "object", actualType(artifact), "critical", "Scouting artifact must be a JSON object, not an array/null/primitive");
535
- } else {
536
- if (typeof artifact.task !== "string" || !artifact.task.trim()) {
537
- addError("task", "non-empty string", typeof artifact.task === "string" ? "empty string" : actualType(artifact.task), "critical", "task must be a non-empty string describing the requested work");
538
- }
539
- for (const key of arrayKeys) {
540
- if (!Array.isArray(artifact[key])) {
541
- addError(key, "array", actualType(artifact[key]), "critical", `${key} must be an array in the scouting handoff`);
542
- }
543
- }
544
- if (Array.isArray(artifact.relevant_files)) {
545
- artifact.relevant_files.forEach((item, index) => {
546
- if (!item || typeof item.path !== "string" || typeof item.reason !== "string") {
547
- addError(`relevant_files[${index}]`, "object with string path and string reason", actualType(item), "warning", "Each relevant_files entry must include path and reason strings");
548
- }
549
- });
550
- }
551
- if (Array.isArray(artifact.test_impact)) {
552
- artifact.test_impact.forEach((item, index) => {
553
- if (!item || typeof item.path !== "string" || !item.path.trim() || typeof item.reason !== "string" || !item.reason.trim()) {
554
- addError(`test_impact[${index}]`, "object with non-empty string path and non-empty string reason", actualType(item), "critical", "Each test_impact entry must include the impacted test path and expectation reason strings");
555
- }
556
- // Validate optional test_examples field
557
- if (item.test_examples !== undefined) {
558
- if (!Array.isArray(item.test_examples)) {
559
- addError(`test_impact[${index}].test_examples`, "array of example objects or undefined", actualType(item.test_examples), "warning", "test_examples must be an array of objects with type, pattern, description, before, and after fields");
560
- } else {
561
- item.test_examples.forEach((example, exIdx) => {
562
- if (!example || typeof example !== "object" || !["added_assertion", "modified_assertion", "added_test_case", "added_pattern"].includes(example.type)) {
563
- addError(`test_impact[${index}].test_examples[${exIdx}].type`, "added_assertion|modified_assertion|added_test_case|added_pattern", actualType(example && example.type), "warning", "Each test_example must have a valid type");
564
- }
565
- if (!example || typeof example.pattern !== "string") {
566
- addError(`test_impact[${index}].test_examples[${exIdx}].pattern`, "string", actualType(example && example.pattern), "warning", "Each test_example must have a pattern string");
567
- }
568
- if (!example || typeof example.before !== "string" || typeof example.after !== "string") {
569
- addError(`test_impact[${index}].test_examples[${exIdx}]`, "before and after strings", "missing or invalid", "warning", "Each test_example must have before and after code snippets");
570
- }
571
- });
572
- }
573
- }
574
- });
575
- }
576
-
577
- // Validate suggested_allowlist (optional but if present, must be valid)
578
- if (artifact.suggested_allowlist) {
579
- if (typeof artifact.suggested_allowlist !== "object" || Array.isArray(artifact.suggested_allowlist)) {
580
- addError("suggested_allowlist", "object", actualType(artifact.suggested_allowlist), "warning", "suggested_allowlist must be an object with agent_patterns and validation_patterns arrays");
581
- } else {
582
- if (!Array.isArray(artifact.suggested_allowlist.agent_patterns)) {
583
- addError("suggested_allowlist.agent_patterns", "array of strings", actualType(artifact.suggested_allowlist.agent_patterns), "warning", "agent_patterns must be an array of glob pattern strings");
584
- } else if (!artifact.suggested_allowlist.agent_patterns.every((p) => typeof p === "string")) {
585
- addError("suggested_allowlist.agent_patterns", "array of strings", "array with non-strings", "warning", "All agent_patterns entries must be strings");
586
- }
587
- if (!Array.isArray(artifact.suggested_allowlist.validation_patterns)) {
588
- addError("suggested_allowlist.validation_patterns", "array of strings", actualType(artifact.suggested_allowlist.validation_patterns), "warning", "validation_patterns must be an array of glob pattern strings");
589
- } else if (!artifact.suggested_allowlist.validation_patterns.every((p) => typeof p === "string")) {
590
- addError("suggested_allowlist.validation_patterns", "array of strings", "array with non-strings", "warning", "All validation_patterns entries must be strings");
591
- }
592
- }
593
- }
537
+ node "$SCOUTING_ALLOWLIST_HELPER" validate \
538
+ "$candidate_artifact" \
539
+ "$final_artifact" \
540
+ "$validation_error_file" \
541
+ "${KASEKI_RESULTS_DIR}/scouting-validation-errors.jsonl" \
542
+ >/dev/null \
543
+ 2>> ${KASEKI_RESULTS_DIR}/scouting-stderr.log
594
544
  }
595
545
 
596
- if (errors.length) {
597
- const onlyTaskMissing = errors.length === 1 && errors[0].field === "task";
598
- const reasonCode = onlyTaskMissing ? "missing_required_fields" : "schema_mismatch";
599
- for (const error of errors) appendValidationFailure(reasonCode, error);
600
- fs.writeFileSync(errorLog, JSON.stringify({
601
- reason_code: reasonCode,
602
- details: summarize(errors),
603
- errors,
604
- }) + "\n");
605
- process.exit(1);
606
- }
607
- fs.writeFileSync(output, JSON.stringify(artifact, null, 2) + "\n");
608
- ' "$candidate_artifact" "$final_artifact" "$validation_error_file" 2>> /results/scouting-stderr.log
609
- }
610
546
  # Validate scouting artifact and emit structured reason code
611
547
  validate_scouting_artifact() {
612
548
  local candidate_artifact="$1"
@@ -618,7 +554,7 @@ validate_scouting_artifact() {
618
554
 
619
555
  : > "$validation_error_file"
620
556
  if [ ! -f "$candidate_artifact" ]; then
621
- if [ -f /results/filesystem-readonly-reason.txt ]; then
557
+ if [ -f ${KASEKI_RESULTS_DIR}/filesystem-readonly-reason.txt ]; then
622
558
  reason_code="readonly_filesystem"
623
559
  reason_details="1 critical scouting validation error: scouting-candidate.json missing due to read-only filesystem"
624
560
  else
@@ -626,15 +562,15 @@ validate_scouting_artifact() {
626
562
  reason_details="1 critical scouting validation error: scouting-candidate.json"
627
563
  fi
628
564
  # shellcheck disable=SC2016
629
- 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
630
566
  elif ! validate_scouting_artifact_with_node "$candidate_artifact" "$final_artifact" "$validation_error_file"; then
631
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')"
632
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')"
633
569
  fi
634
570
 
635
571
  printf '%s\n' "$reason_code" > "$reason_file"
636
- printf '%s\n' "$reason_details" > /results/scouting-validation-summary.txt
637
- 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
638
574
  rm -f "$validation_error_file" 2>/dev/null || true
639
575
  [ "$reason_code" = "valid" ]
640
576
  }
@@ -649,12 +585,14 @@ validate_goal_check_artifact_with_node() {
649
585
  # shellcheck disable=SC2016
650
586
  node -e '
651
587
  const fs = require("node:fs");
588
+ const path = require("node:path");
652
589
  const input = process.argv[1];
653
590
  const output = process.argv[2];
654
591
  const attempt = Number(process.argv[3]);
655
592
  const errorLog = process.argv[4];
656
- const jsonlLog = "/results/goal-check-validation-errors.jsonl";
657
- 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");
658
596
 
659
597
  function actualType(value) {
660
598
  if (value === null) return "null";
@@ -663,11 +601,16 @@ function actualType(value) {
663
601
  }
664
602
 
665
603
  function appendValidationFailure(error) {
666
- fs.appendFileSync(jsonlLog, JSON.stringify({
667
- timestamp: new Date().toISOString(),
668
- attempt,
669
- ...error,
670
- }) + "\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
+ }
671
614
  }
672
615
 
673
616
  function summarize(errors) {
@@ -700,7 +643,7 @@ try {
700
643
  expected: "valid JSON object",
701
644
  actual: error && error.message ? String(error.message) : String(error),
702
645
  severity: "critical",
703
- 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",
704
647
  }]);
705
648
  }
706
649
 
@@ -753,8 +696,13 @@ if (errors.length) {
753
696
  artifact.attempt = attempt;
754
697
  artifact.timestamp = new Date().toISOString();
755
698
  fs.writeFileSync(output, JSON.stringify(artifact, null, 2) + "\n");
756
- fs.appendFileSync(attemptsLog, JSON.stringify(artifact) + "\n");
757
- ' "$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"
758
706
  }
759
707
 
760
708
  validate_goal_check_artifact() {
@@ -762,7 +710,7 @@ validate_goal_check_artifact() {
762
710
  local final_artifact="$2"
763
711
  local attempt="$3"
764
712
  local reason_file="$4"
765
- local summary_file="${5:-/results/goal-check-validation-summary.txt}"
713
+ local summary_file="${5:-${KASEKI_RESULTS_DIR}/goal-check-validation-summary.txt}"
766
714
  local validation_error_file="/tmp/goal-check-validation-errors.json"
767
715
  local reason_code="valid"
768
716
  local reason_details="artifact validation passed"
@@ -772,7 +720,7 @@ validate_goal_check_artifact() {
772
720
  reason_code="missing_file"
773
721
  reason_details="1 critical goal-check validation error: goal-check-candidate.json"
774
722
  # shellcheck disable=SC2016
775
- 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
776
724
  elif ! validate_goal_check_artifact_with_node "$candidate_artifact" "$final_artifact" "$attempt" "$validation_error_file"; then
777
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')"
778
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')"
@@ -780,7 +728,7 @@ validate_goal_check_artifact() {
780
728
 
781
729
  printf '%s\n' "$reason_code" > "$reason_file"
782
730
  printf '%s\n' "$reason_details" > "$summary_file"
783
- 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
784
732
  rm -f "$validation_error_file" 2>/dev/null || true
785
733
  [ "$reason_code" = "valid" ]
786
734
  }
@@ -789,20 +737,20 @@ emit_progress() {
789
737
  local stage="$1"
790
738
  local detail="$2"
791
739
  local status="${3:-info}"
792
- append_jsonl_object /results/progress.jsonl \
740
+ append_jsonl_object ${KASEKI_RESULTS_DIR}/progress.jsonl \
793
741
  "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
794
742
  "component=kaseki-agent" \
795
743
  "stage=$stage" \
796
744
  "status=$status" \
797
745
  "instance=$INSTANCE_NAME" \
798
746
  "detail=$detail"
799
- 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
800
748
  }
801
749
 
802
750
  emit_event() {
803
751
  local event_type="$1"
804
752
  shift
805
- append_jsonl_object /results/progress.jsonl \
753
+ append_jsonl_object ${KASEKI_RESULTS_DIR}/progress.jsonl \
806
754
  "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
807
755
  "component=kaseki-agent" \
808
756
  "event_type=$event_type" \
@@ -815,7 +763,7 @@ emit_error_event() {
815
763
  local detail="$2"
816
764
  local recovery="${3:-continue}"
817
765
  emit_event "error" "error_type=$error_type" "detail=$detail" "recovery_action=$recovery"
818
- 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
819
767
  }
820
768
 
821
769
  write_metadata() {
@@ -833,7 +781,7 @@ write_metadata() {
833
781
  stages_json="[\"unknown\"]"
834
782
  fi
835
783
 
836
- cat > /results/metadata.json <<META
784
+ cat > ${KASEKI_RESULTS_DIR}/metadata.json <<META
837
785
  {
838
786
  "instance": $(printf '%s' "$INSTANCE_NAME" | json_encode),
839
787
  "repo_url": $(printf '%s' "$REPO_URL" | json_encode),
@@ -954,7 +902,7 @@ write_metadata() {
954
902
  "stages": $stages_json
955
903
  }
956
904
  META
957
- printf '%s\n' "$exit_code" > /results/exit_code
905
+ printf '%s\n' "$exit_code" > ${KASEKI_RESULTS_DIR}/exit_code
958
906
  }
959
907
 
960
908
  set_current_stage() {
@@ -1009,7 +957,7 @@ build_stages_array() {
1009
957
 
1010
958
  write_result_summary() {
1011
959
  local changed_files changed_files_markdown validation_status pr_status github_skip_reasons_summary goal_check_status
1012
- 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)"
1013
961
  if [ -n "$changed_files" ]; then
1014
962
  changed_files_markdown="$(printf '%s\n' "$changed_files" | sed 's/^/ - /')"
1015
963
  else
@@ -1062,7 +1010,7 @@ write_result_summary() {
1062
1010
  goal_check_status="not reached"
1063
1011
  fi
1064
1012
 
1065
- cat > /results/result-summary.md <<SUMMARY
1013
+ cat > ${KASEKI_RESULTS_DIR}/result-summary.md <<SUMMARY
1066
1014
  # Kaseki Result: $INSTANCE_NAME
1067
1015
 
1068
1016
  - Status: $(if [ "$STATUS" -eq 0 ]; then printf 'passed'; else printf 'failed'; fi)
@@ -1087,7 +1035,7 @@ $(if [ -n "$VALIDATION_ALLOWLIST_FAILURE_REASON" ]; then printf ' - Allowlist r
1087
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)
1088
1036
  - Test failure analysis: $TEST_FAILURE_CLASSIFICATION_STATUS
1089
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)
1090
- $(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)
1091
1039
  - Quality checks: $QUALITY_EXIT
1092
1040
  - Secret scan: $SECRET_SCAN_EXIT
1093
1041
  - GitHub PR: $pr_status
@@ -1129,12 +1077,12 @@ SUMMARY
1129
1077
  write_failure_json() {
1130
1078
  local exit_code="$1"
1131
1079
  local stderr_tail
1132
- 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)"
1133
1081
  if [ "$exit_code" -eq 0 ]; then
1134
- : > /results/failure.json
1082
+ : > ${KASEKI_RESULTS_DIR}/failure.json
1135
1083
  return 0
1136
1084
  fi
1137
- cat > /results/failure.json <<FAILURE
1085
+ cat > ${KASEKI_RESULTS_DIR}/failure.json <<FAILURE
1138
1086
  {
1139
1087
  "instance": $(printf '%s' "$INSTANCE_NAME" | json_encode),
1140
1088
  "exit_code": $exit_code,
@@ -1152,7 +1100,7 @@ write_failure_json() {
1152
1100
  "goal_check_met": $GOAL_CHECK_MET,
1153
1101
  "stage": $(printf '%s' "$CURRENT_STAGE" | json_encode),
1154
1102
  "stderr_tail": $(printf '%s' "$stderr_tail" | json_encode),
1155
- "artifacts_dir": "/results",
1103
+ "artifacts_dir": "${KASEKI_RESULTS_DIR}",
1156
1104
  "metadata": "metadata.json",
1157
1105
  "stderr": "stderr.log",
1158
1106
  "stdout": "stdout.log",
@@ -1164,28 +1112,28 @@ FAILURE
1164
1112
 
1165
1113
  collect_git_artifacts() {
1166
1114
  DIFF_NONEMPTY=false
1167
- if [ -d /workspace/repo/.git ]; then
1115
+ if [ -d ${KASEKI_WORKSPACE_DIR}/repo/.git ]; then
1168
1116
  while IFS= read -r untracked_file || [ -n "$untracked_file" ]; do
1169
1117
  [ -z "$untracked_file" ] && continue
1170
- git -C /workspace/repo add -N -- "$untracked_file" 2>/dev/null || true
1171
- done < <(git -C /workspace/repo ls-files --others --exclude-standard 2>/dev/null || true)
1172
- git -C /workspace/repo status --short > /results/git.status 2>/dev/null || true
1173
- git -C /workspace/repo diff -- . > /results/git.diff 2>/dev/null || true
1174
- git -C /workspace/repo diff --name-only -- . > /results/changed-files.txt 2>/dev/null || true
1175
- 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
1176
1124
  DIFF_NONEMPTY=true
1177
1125
  fi
1178
1126
  else
1179
- : > /results/git.status
1180
- : > /results/git.diff
1181
- : > /results/changed-files.txt
1127
+ : > ${KASEKI_RESULTS_DIR}/git.status
1128
+ : > ${KASEKI_RESULTS_DIR}/git.diff
1129
+ : > ${KASEKI_RESULTS_DIR}/changed-files.txt
1182
1130
  fi
1183
1131
  }
1184
1132
 
1185
1133
  run_static_test_impact_check() {
1186
- local changed_files_file="/results/changed-files.txt"
1187
- local diff_file="/results/git.diff"
1188
- 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}"
1189
1137
  local indicator_regex='(parse|parser|regex|regexp|stage|event|format|serialize|name)'
1190
1138
  local production_matches diff_matches warning_detail
1191
1139
 
@@ -1227,7 +1175,7 @@ run_static_test_impact_check() {
1227
1175
  "warning_type=test_impact_without_tests" \
1228
1176
  "artifact=$artifact" \
1229
1177
  "detail=$warning_detail"
1230
- 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
1231
1179
  return 0
1232
1180
  }
1233
1181
 
@@ -1240,70 +1188,89 @@ run_expectation_mismatch_detector() {
1240
1188
  fi
1241
1189
 
1242
1190
  : > "$EXPECTATION_MISMATCH_WARNINGS_ARTIFACT"
1243
- if [ ! -s /results/git.diff ]; then
1244
- 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
1245
1193
  return 0
1246
1194
  fi
1247
1195
  if [ ! -f "$detector_script" ]; then
1248
- 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
1249
1197
  return 0
1250
1198
  fi
1251
1199
 
1252
1200
  if ! node "$detector_script" \
1253
- --repo /workspace/repo \
1254
- --diff /results/git.diff \
1201
+ --repo ${KASEKI_WORKSPACE_DIR}/repo \
1202
+ --diff ${KASEKI_RESULTS_DIR}/git.diff \
1255
1203
  --output "$EXPECTATION_MISMATCH_WARNINGS_ARTIFACT" \
1256
- --progress /results/progress.log; then
1257
- 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
1206
+ fi
1207
+ }
1208
+
1209
+ resolve_allowlist_helper() {
1210
+ local script_dir="$1"
1211
+ local script_relative_helper="$script_dir/scripts/allowlist-helper.sh"
1212
+ local fallback_helper="${KASEKI_ALLOWLIST_HELPER_FALLBACK:-/app/scripts/allowlist-helper.sh}"
1213
+
1214
+ if [ -r "$script_relative_helper" ]; then
1215
+ printf '%s\n' "$script_relative_helper"
1216
+ return 0
1258
1217
  fi
1218
+
1219
+ if [ -r "$fallback_helper" ]; then
1220
+ printf '%s\n' "$fallback_helper"
1221
+ return 0
1222
+ fi
1223
+
1224
+ 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' \
1225
+ "$script_relative_helper" \
1226
+ "$fallback_helper" >&2
1227
+ return 66
1259
1228
  }
1260
1229
 
1261
1230
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
1262
- ALLOWLIST_HELPER="$SCRIPT_DIR/scripts/allowlist-helper.sh"
1263
- if [ ! -r "$ALLOWLIST_HELPER" ] && [ -r /app/scripts/allowlist-helper.sh ]; then
1264
- ALLOWLIST_HELPER="/app/scripts/allowlist-helper.sh"
1231
+ ALLOWLIST_HELPER="$(resolve_allowlist_helper "$SCRIPT_DIR")"
1232
+ allowlist_helper_status=$?
1233
+ if [ "$allowlist_helper_status" -ne 0 ]; then
1234
+ exit "$allowlist_helper_status"
1235
+ fi
1236
+ SCOUTING_ALLOWLIST_HELPER="$SCRIPT_DIR/scripts/scouting-allowlist.js"
1237
+ if [ ! -r "$SCOUTING_ALLOWLIST_HELPER" ] && [ -r /app/scripts/scouting-allowlist.js ]; then
1238
+ SCOUTING_ALLOWLIST_HELPER="/app/scripts/scouting-allowlist.js"
1265
1239
  fi
1266
1240
  # shellcheck source=scripts/allowlist-helper.sh
1267
- . "$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
+
1252
+ if [ "${KASEKI_AGENT_HELPER_RESOLUTION_CHECK:-0}" = "1" ]; then
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
+ }
1257
+ printf 'allowlist_helper=%s\n' "$ALLOWLIST_HELPER"
1258
+ exit 0
1259
+ fi
1268
1260
 
1269
1261
  derive_allowlist_from_scouting() {
1270
- local scouting_artifact agent_patterns validation_patterns
1262
+ local scouting_artifact
1271
1263
  scouting_artifact="${1:?missing scouting artifact path}"
1272
-
1264
+
1273
1265
  if [ ! -f "$scouting_artifact" ]; then
1274
1266
  printf 'derive_allowlist_from_scouting: scouting artifact not found: %s\n' "$scouting_artifact" >&2
1275
1267
  return 1
1276
1268
  fi
1277
-
1278
- # Extract patterns from scouting.json
1279
- agent_patterns="$(node -e "
1280
- try {
1281
- const fs = require('node:fs');
1282
- const artifact = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));
1283
- if (artifact && artifact.suggested_allowlist && Array.isArray(artifact.suggested_allowlist.agent_patterns)) {
1284
- console.log(artifact.suggested_allowlist.agent_patterns.join(' '));
1285
- }
1286
- } catch (e) {
1287
- console.error('Error parsing scouting artifact:', e.message);
1288
- }
1289
- " "$scouting_artifact" 2>/dev/null)"
1290
-
1291
- validation_patterns="$(node -e "
1292
- try {
1293
- const fs = require('node:fs');
1294
- const artifact = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));
1295
- if (artifact && artifact.suggested_allowlist && Array.isArray(artifact.suggested_allowlist.validation_patterns)) {
1296
- console.log(artifact.suggested_allowlist.validation_patterns.join(' '));
1297
- }
1298
- } catch (e) {
1299
- console.error('Error parsing scouting artifact:', e.message);
1300
- }
1301
- " "$scouting_artifact" 2>/dev/null)"
1302
-
1303
- printf '%s\n' "$agent_patterns"
1304
- printf '%s\n' "$validation_patterns"
1269
+
1270
+ node "$SCOUTING_ALLOWLIST_HELPER" derive "$scouting_artifact"
1305
1271
  }
1306
1272
 
1273
+
1307
1274
  validate_allowlist_patterns() {
1308
1275
  local patterns_str test_regex
1309
1276
  patterns_str="${1:-}"
@@ -1346,7 +1313,7 @@ run_scouting_allowlist_coverage() {
1346
1313
  local scouting_artifact agent_patterns validation_patterns
1347
1314
  scouting_artifact="${1:?missing scouting artifact path}"
1348
1315
 
1349
- if [ ! -f "$scouting_artifact" ] || [ ! -f /results/changed-files.txt ]; then
1316
+ if [ ! -f "$scouting_artifact" ] || [ ! -f ${KASEKI_RESULTS_DIR}/changed-files.txt ]; then
1350
1317
  return 0
1351
1318
  fi
1352
1319
 
@@ -1361,7 +1328,7 @@ run_scouting_allowlist_coverage() {
1361
1328
  validation_warnings=""
1362
1329
 
1363
1330
  if [ -n "$agent_patterns" ] && command -v dry-run-allowlist.sh >/dev/null 2>&1; then
1364
- 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)"
1365
1332
  [ -z "$agent_coverage" ] && agent_coverage="0"
1366
1333
 
1367
1334
  # Check for problematic coverage
@@ -1373,7 +1340,7 @@ run_scouting_allowlist_coverage() {
1373
1340
  fi
1374
1341
 
1375
1342
  if [ -n "$validation_patterns" ] && command -v dry-run-allowlist.sh >/dev/null 2>&1; then
1376
- 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)"
1377
1344
  [ -z "$validation_coverage" ] && validation_coverage="0"
1378
1345
 
1379
1346
  if [ "$validation_coverage" -lt 30 ]; then
@@ -1407,12 +1374,12 @@ run_scouting_allowlist_coverage() {
1407
1374
  if [ -n "$validation_warnings" ]; then
1408
1375
  printf ' ⚠ validation_phase warning: %s\n' "$validation_warnings"
1409
1376
  fi
1410
- } | tee -a /results/scouting-report.md >> /results/quality.log
1377
+ } | tee -a ${KASEKI_RESULTS_DIR}/scouting-report.md >> ${KASEKI_RESULTS_DIR}/quality.log
1411
1378
  fi
1412
1379
  }
1413
1380
 
1414
1381
  restore_disallowed_changes() {
1415
- 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
1416
1383
  return 0
1417
1384
  fi
1418
1385
 
@@ -1425,7 +1392,7 @@ restore_disallowed_changes() {
1425
1392
  coverage=0
1426
1393
 
1427
1394
  # Initialize restoration tracking file
1428
- : > /results/restoration.jsonl
1395
+ : > ${KASEKI_RESULTS_DIR}/restoration.jsonl
1429
1396
 
1430
1397
  while IFS= read -r changed_file || [ -n "$changed_file" ]; do
1431
1398
  [ -z "$changed_file" ] && continue
@@ -1435,21 +1402,21 @@ restore_disallowed_changes() {
1435
1402
  {
1436
1403
  printf '{"timestamp":"%s","event":"file_evaluated","file":"%s","status":"kept","reason":"matched_allowlist"}\n' \
1437
1404
  "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$(printf '%s' "$changed_file" | sed 's/"/\\"/g')"
1438
- } >> /results/restoration.jsonl
1405
+ } >> ${KASEKI_RESULTS_DIR}/restoration.jsonl
1439
1406
  continue
1440
1407
  fi
1441
1408
  # File did not match allowlist - restore it
1442
1409
  restored_count=$((restored_count + 1))
1443
- 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
1444
1411
  emit_event "quality_gate_rule_evaluated" "rule=allowlist_restore" "passed=true" "file=$changed_file"
1445
1412
  {
1446
1413
  printf '{"timestamp":"%s","event":"file_restored","file":"%s","status":"restored","reason":"not_in_allowlist"}\n' \
1447
1414
  "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$(printf '%s' "$changed_file" | sed 's/"/\\"/g')"
1448
- } >> /results/restoration.jsonl
1449
- git -C /workspace/repo restore --staged --worktree -- "$changed_file" 2>/dev/null || true
1450
- 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
1451
1418
  restored_any=1
1452
- done < /results/changed-files.txt
1419
+ done < ${KASEKI_RESULTS_DIR}/changed-files.txt
1453
1420
 
1454
1421
  # Emit restoration summary to quality.log with actionable guidance
1455
1422
  if [ $((restored_count + kept_count)) -gt 0 ]; then
@@ -1460,12 +1427,12 @@ restore_disallowed_changes() {
1460
1427
  printf '\n[allowlist summary] Restored: %d files; Kept: %d files (coverage: %d%%)\n' "$restored_count" "$kept_count" "$coverage"
1461
1428
  if [ "$restored_count" -gt 0 ] && [ "$coverage" -lt 50 ]; then
1462
1429
  printf '[allowlist note] Low coverage detected. To improve:\n'
1463
- 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'
1464
1431
  printf ' 2. Review suggested patterns in allowlist-suggestions.md\n'
1465
1432
  printf ' 3. Update KASEKI_CHANGED_FILES_ALLOWLIST and re-run\n'
1466
1433
  printf 'See docs/QUALITY_GATES.md for more guidance.\n'
1467
1434
  fi
1468
- } | tee -a /results/quality.log
1435
+ } | tee -a ${KASEKI_RESULTS_DIR}/quality.log
1469
1436
  emit_event "allowlist_restoration_complete" "restored=$restored_count" "kept=$kept_count" "coverage=$coverage"
1470
1437
  fi
1471
1438
 
@@ -1475,7 +1442,7 @@ restore_disallowed_changes() {
1475
1442
  }
1476
1443
 
1477
1444
  generate_restoration_report() {
1478
- if [ ! -f /results/restoration.jsonl ]; then
1445
+ if [ ! -f ${KASEKI_RESULTS_DIR}/restoration.jsonl ]; then
1479
1446
  printf '[debug] restoration report: skipping - restoration.jsonl not found\n' >&2
1480
1447
  return 0
1481
1448
  fi
@@ -1484,7 +1451,7 @@ generate_restoration_report() {
1484
1451
 
1485
1452
  # Safely extract counts from restoration.jsonl with validation
1486
1453
  printf '[debug] restoration report: extracting counts from restoration.jsonl\n' >&2
1487
- 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)
1488
1455
  restored_count=${restored_count:-0}
1489
1456
  printf '[debug] restoration report: restored_count="%s"\n' "$restored_count" >&2
1490
1457
  if ! validate_numeric "restored_count" "$restored_count"; then
@@ -1492,7 +1459,7 @@ generate_restoration_report() {
1492
1459
  return 1
1493
1460
  fi
1494
1461
 
1495
- 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)
1496
1463
  kept_count=${kept_count:-0}
1497
1464
  printf '[debug] restoration report: kept_count="%s"\n' "$kept_count" >&2
1498
1465
  if ! validate_numeric "kept_count" "$kept_count"; then
@@ -1530,12 +1497,12 @@ generate_restoration_report() {
1530
1497
  if [ "$restored_count" -gt 0 ]; then
1531
1498
  printf '## Restored Files\n\n'
1532
1499
  printf 'These files were modified by the agent but restored because they fall outside the allowlist:\n\n'
1533
- grep '"status":"restored"' /results/restoration.jsonl | \
1500
+ grep '"status":"restored"' ${KASEKI_RESULTS_DIR}/restoration.jsonl | \
1534
1501
  sed "s/.*\"file\":\"\([^\"]*\)\".*/- \`\1\`/" | \
1535
- sort | uniq >> /results/restoration-report.md.tmp 2>/dev/null || true
1536
- if [ -f /results/restoration-report.md.tmp ]; then
1537
- cat /results/restoration-report.md.tmp
1538
- 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
1539
1506
  fi
1540
1507
  printf '\n'
1541
1508
  fi
@@ -1543,12 +1510,12 @@ generate_restoration_report() {
1543
1510
  if [ "$kept_count" -gt 0 ]; then
1544
1511
  printf '## Kept Files (Allowlist Matches)\n\n'
1545
1512
  printf 'These files were in the allowlist and were kept:\n\n'
1546
- grep '"status":"kept"' /results/restoration.jsonl | \
1513
+ grep '"status":"kept"' ${KASEKI_RESULTS_DIR}/restoration.jsonl | \
1547
1514
  sed "s/.*\"file\":\"\([^\"]*\)\".*/- \`\1\`/" | \
1548
- sort | uniq >> /results/restoration-report.md.tmp 2>/dev/null || true
1549
- if [ -f /results/restoration-report.md.tmp ]; then
1550
- cat /results/restoration-report.md.tmp
1551
- 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
1552
1519
  fi
1553
1520
  printf '\n'
1554
1521
  fi
@@ -1566,14 +1533,14 @@ generate_restoration_report() {
1566
1533
  printf 'KASEKI_CHANGED_FILES_ALLOWLIST="<your-pattern>" ./run-kaseki.sh\n'
1567
1534
  printf '```\n\n'
1568
1535
  printf "For help on allowlist patterns, see \`docs/QUALITY_GATES.md\`.\n"
1569
- } > /results/restoration-report.md
1536
+ } > ${KASEKI_RESULTS_DIR}/restoration-report.md
1570
1537
  }
1571
1538
 
1572
1539
  check_validation_allowlist() {
1573
1540
  if [ -z "$KASEKI_VALIDATION_ALLOWLIST" ]; then
1574
1541
  return 0
1575
1542
  fi
1576
- if [ ! -d /workspace/repo/.git ]; then
1543
+ if [ ! -d ${KASEKI_WORKSPACE_DIR}/repo/.git ]; then
1577
1544
  return 0
1578
1545
  fi
1579
1546
 
@@ -1582,9 +1549,9 @@ check_validation_allowlist() {
1582
1549
  allowlist_regex="$(build_allowlist_regex "$KASEKI_VALIDATION_ALLOWLIST")"
1583
1550
  [ -z "$allowlist_regex" ] && return 0
1584
1551
  validation_violation_count=0
1585
- validation_before_state_file="/results/validation-before-state.txt"
1586
- validation_after_state_file="/results/validation-after-state.txt"
1587
- 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"
1588
1555
  : > "$validation_changed_file"
1589
1556
 
1590
1557
  if [ -f "$validation_before_state_file" ] && [ -f "$validation_after_state_file" ]; then
@@ -1599,14 +1566,14 @@ check_validation_allowlist() {
1599
1566
  }
1600
1567
  }
1601
1568
  ' "$validation_before_state_file" "$validation_after_state_file" | LC_ALL=C sort -u > "$validation_changed_file"
1602
- elif [ -f /results/changed-files.txt ]; then
1603
- 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"
1604
1571
  fi
1605
1572
 
1606
1573
  while IFS= read -r changed_file || [ -n "$changed_file" ]; do
1607
1574
  [ -z "$changed_file" ] && continue
1608
1575
  if ! printf '%s\n' "$changed_file" | grep -Eq "^(${allowlist_regex})$"; then
1609
- 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
1610
1577
  validation_violation_count=$((validation_violation_count + 1))
1611
1578
  emit_event "quality_gate_rule_evaluated" "rule=validation_allowlist" "passed=false" "file=$changed_file"
1612
1579
  else
@@ -1618,14 +1585,14 @@ check_validation_allowlist() {
1618
1585
  QUALITY_EXIT=7
1619
1586
  VALIDATION_ALLOWLIST_FAILURE_REASON="validation_allowlist_check: $validation_violation_count file(s) changed during validation outside KASEKI_VALIDATION_ALLOWLIST"
1620
1587
  QUALITY_FAILURE_REASON="$VALIDATION_ALLOWLIST_FAILURE_REASON"
1621
- 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
1622
1589
  return 1
1623
1590
  fi
1624
1591
  return 0
1625
1592
  }
1626
1593
 
1627
1594
  check_secret_scan_allowlist() {
1628
- local allowlist_file="/workspace/repo/.kaseki-secret-allowlist"
1595
+ local allowlist_file="${KASEKI_WORKSPACE_DIR}/repo/.kaseki-secret-allowlist"
1629
1596
 
1630
1597
  # If no allowlist file exists, all matches are failures (real leaks)
1631
1598
  if [ ! -f "$allowlist_file" ]; then
@@ -1638,7 +1605,7 @@ check_secret_scan_allowlist() {
1638
1605
 
1639
1606
  # Read the log into a temp variable to avoid SC2094 (read-write in same pipeline)
1640
1607
  local temp_log
1641
- temp_log=$(cat /results/secret-scan.log)
1608
+ temp_log=$(cat ${KASEKI_RESULTS_DIR}/secret-scan.log)
1642
1609
 
1643
1610
  while IFS= read -r match_line || [ -n "$match_line" ]; do
1644
1611
  [ -z "$match_line" ] && continue
@@ -1652,8 +1619,8 @@ check_secret_scan_allowlist() {
1652
1619
 
1653
1620
  [ -z "$pattern" ] && continue
1654
1621
 
1655
- # Normalize file path: remove leading /workspace/repo/, repo/, and ./ if present
1656
- 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/}"
1657
1624
  file_path="${file_path#repo/}"
1658
1625
  file_path="${file_path#./}"
1659
1626
 
@@ -1678,7 +1645,7 @@ check_secret_scan_allowlist() {
1678
1645
  for match in "${secret_matches[@]}"; do
1679
1646
  printf '%s\n' "$match"
1680
1647
  done
1681
- } > /results/secret-scan.log
1648
+ } > ${KASEKI_RESULTS_DIR}/secret-scan.log
1682
1649
 
1683
1650
  # Exit code 6 only if there are unallowlisted matches
1684
1651
  if [ "$unallowlisted_count" -gt 0 ]; then
@@ -1712,9 +1679,9 @@ finish() {
1712
1679
  printf '[unexpected-failure] Exit code: %d\n' "$code"
1713
1680
  printf '[unexpected-failure] Last command: %s\n' "$LAST_COMMAND"
1714
1681
  printf '[unexpected-failure] Current stage: %s\n' "$CURRENT_STAGE"
1715
- if [ -f /results/progress.log ]; then
1682
+ if [ -f ${KASEKI_RESULTS_DIR}/progress.log ]; then
1716
1683
  printf '[unexpected-failure] Last 5 progress entries:\n'
1717
- tail -5 /results/progress.log | sed 's/^/ /'
1684
+ tail -5 ${KASEKI_RESULTS_DIR}/progress.log | sed 's/^/ /'
1718
1685
  fi
1719
1686
  } | tee -a "$LAST_COMMAND_LOG" >&2
1720
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"
@@ -1723,16 +1690,16 @@ finish() {
1723
1690
  maybe_call_finish_helper collect_git_artifacts
1724
1691
 
1725
1692
  # Analyze test failures and compare baseline vs. working results
1726
- 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
1727
1694
  set_current_stage "test failure analysis"
1728
1695
  if analyze_test_failures_baseline; then
1729
1696
  TEST_FAILURE_CLASSIFICATION_STATUS="completed"
1730
1697
  # Try to extract newly_introduced_failures_count from JSON output (if jq available)
1731
- if [ -f /results/test-baseline-comparison.json ] && command -v jq >/dev/null 2>&1; then
1732
- NEWLY_INTRODUCED_FAILURES_COUNT=$(jq -r '.summary.total_newly_introduced // 0' /results/test-baseline-comparison.json 2>/dev/null || printf '0')
1733
- 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
1734
1701
  # Fallback: try to extract with grep/sed if jq not available
1735
- 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')
1736
1703
  fi
1737
1704
  else
1738
1705
  TEST_FAILURE_CLASSIFICATION_STATUS="failed"
@@ -1746,8 +1713,8 @@ finish() {
1746
1713
  fi
1747
1714
 
1748
1715
  # Debug output for restoration report generation
1749
- if [ -f /results/restoration.jsonl ]; then
1750
- 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
1751
1718
  else
1752
1719
  printf '[debug] restoration.jsonl does not exist\n' >&2
1753
1720
  fi
@@ -1765,12 +1732,12 @@ finish() {
1765
1732
 
1766
1733
  # Calculate and record maturity score
1767
1734
  if [ -x /app/scripts/kaseki-maturity-score.sh ]; then
1768
- /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
1769
1736
  fi
1770
1737
 
1771
1738
  # Calculate and record performance metrics
1772
- if [ -x /app/scripts/kaseki-performance-metrics.sh ] && [ -f /results/stage-timings.tsv ]; then
1773
- /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
1774
1741
  fi
1775
1742
 
1776
1743
  maybe_call_finish_helper write_result_summary
@@ -1778,7 +1745,7 @@ finish() {
1778
1745
  # Generate inspect-report.md for inspect mode on success
1779
1746
  if [ "$KASEKI_TASK_MODE" = "inspect" ] && [ "$STATUS" -eq 0 ]; then
1780
1747
  if [ -x /app/scripts/generate-inspect-report.js ]; then
1781
- 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
1782
1749
  fi
1783
1750
  fi
1784
1751
 
@@ -1823,7 +1790,7 @@ run_step_dry() {
1823
1790
  printf '\n==> %s (DRY-RUN: simulated)\n' "$label"
1824
1791
  emit_progress "$label" "started (dry-run)"
1825
1792
  # Show what commands would be run without executing them
1826
- printf '%s\n' "$@" >> /results/validation.log
1793
+ printf '%s\n' "$@" >> ${KASEKI_RESULTS_DIR}/validation.log
1827
1794
  step_end="$(date +%s)"
1828
1795
  emit_progress "$label" "finished (dry-run, simulated exit 0)"
1829
1796
  record_stage_timing "$label" "0" "$((step_end - step_start))" "dry-run"
@@ -1858,9 +1825,9 @@ is_valid_git_mirror() {
1858
1825
  }
1859
1826
 
1860
1827
  run_direct_clone() {
1861
- rm -rf /workspace/repo
1828
+ rm -rf ${KASEKI_WORKSPACE_DIR}/repo
1862
1829
  GIT_CLONE_STRATEGY="direct_shallow"
1863
- 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
1864
1831
  }
1865
1832
 
1866
1833
  clone_with_git_cache() {
@@ -1937,25 +1904,25 @@ clone_with_git_cache() {
1937
1904
  fi
1938
1905
  flock -u 9 || true
1939
1906
 
1940
- rm -rf /workspace/repo
1907
+ rm -rf ${KASEKI_WORKSPACE_DIR}/repo
1941
1908
  GIT_CLONE_STRATEGY="reference_shallow"
1942
- 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
1943
1910
  clone_rc=$?
1944
1911
  if [ "$clone_rc" -eq 0 ]; then
1945
1912
  return 0
1946
1913
  fi
1947
1914
 
1948
- rm -rf /workspace/repo
1915
+ rm -rf ${KASEKI_WORKSPACE_DIR}/repo
1949
1916
  GIT_CLONE_STRATEGY="mirror_local"
1950
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"
1951
- git clone --branch "$GIT_REF" "$mirror" /workspace/repo
1918
+ git clone --branch "$GIT_REF" "$mirror" ${KASEKI_WORKSPACE_DIR}/repo
1952
1919
  clone_rc=$?
1953
- if [ "$clone_rc" -eq 0 ] && git -C /workspace/repo rev-parse --verify HEAD >/dev/null 2>&1; then
1954
- 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
1955
1922
  return 0
1956
1923
  fi
1957
1924
 
1958
- rm -rf /workspace/repo
1925
+ rm -rf ${KASEKI_WORKSPACE_DIR}/repo
1959
1926
  GIT_CACHE_STATUS="mirror_clone_failed"
1960
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"
1961
1928
  run_direct_clone
@@ -2232,7 +2199,7 @@ record_skipped_npm_script_command() {
2232
2199
  local command="$1"
2233
2200
  local script_name="$2"
2234
2201
  local duration_seconds="$3"
2235
- local log_file="${4:-/results/validation.log}"
2202
+ local log_file="${4:-${KASEKI_RESULTS_DIR}/validation.log}"
2236
2203
  local timings_file="${5:-$VALIDATION_TIMINGS_FILE}"
2237
2204
  local skip_label="${6:-skipped}"
2238
2205
  local classification="${7:-}"
@@ -2266,7 +2233,7 @@ classify_auto_lint_cleanup_command_exit() {
2266
2233
  }
2267
2234
 
2268
2235
  record_skipped_validation_command() {
2269
- 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"
2270
2237
  }
2271
2238
 
2272
2239
  has_typescript_project() {
@@ -2335,9 +2302,9 @@ run_typescript_precheck() {
2335
2302
  if ! has_npm_build_command "$KASEKI_TS_CHECK_COMMAND"; then
2336
2303
  local missing_script
2337
2304
  missing_script="$(npm_run_script_name "$KASEKI_TS_CHECK_COMMAND")" || missing_script="$KASEKI_TS_CHECK_COMMAND"
2338
- printf '\n==> TypeScript pre-check\n' | tee -a /results/pre-validation-ts-check.log
2339
- printf 'Command: %s\n' "$KASEKI_TS_CHECK_COMMAND" | tee -a /results/pre-validation-ts-check.log
2340
- 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
2341
2308
  emit_error_event "typescript_precheck_skipped_missing_script" "TypeScript check skipped: npm script '$missing_script' not defined" "continue"
2342
2309
  emit_progress "typescript precheck" "skipped (npm script '$missing_script' not found)"
2343
2310
  record_stage_timing "typescript precheck" 0 0 "skipped_missing_script"
@@ -2352,8 +2319,8 @@ run_typescript_precheck() {
2352
2319
  {
2353
2320
  printf '\n==> TypeScript pre-check\n'
2354
2321
  printf 'Command: %s\n' "$KASEKI_TS_CHECK_COMMAND"
2355
- eval "cd /workspace/repo && $KASEKI_TS_CHECK_COMMAND" 2>&1
2356
- } 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
2357
2324
  ts_check_exit="${PIPESTATUS[0]}"
2358
2325
 
2359
2326
  ts_check_end="$(date +%s)"
@@ -2377,7 +2344,7 @@ run_typescript_precheck() {
2377
2344
  append_validation_failure_tail() {
2378
2345
  local raw_log="$1"
2379
2346
  local visible_log="$2"
2380
- local quality_log="${3:-/results/quality.log}"
2347
+ local quality_log="${3:-${KASEKI_RESULTS_DIR}/quality.log}"
2381
2348
 
2382
2349
  if ! [ -s "$raw_log" ]; then
2383
2350
  return 0
@@ -2426,14 +2393,14 @@ run_trailing_whitespace_cleanup_for_changed_tracked_text_files() {
2426
2393
  collect_changed_file_set() {
2427
2394
  local output_file="$1"
2428
2395
  : > "$output_file"
2429
- if [ ! -d /workspace/repo/.git ]; then
2396
+ if [ ! -d ${KASEKI_WORKSPACE_DIR}/repo/.git ]; then
2430
2397
  return 0
2431
2398
  fi
2432
2399
 
2433
2400
  {
2434
- git -C /workspace/repo diff --name-only -- . 2>/dev/null || true
2435
- git -C /workspace/repo diff --name-only --cached -- . 2>/dev/null || true
2436
- 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
2437
2404
  } | sed '/^$/d' | LC_ALL=C sort -u > "$output_file"
2438
2405
  }
2439
2406
 
@@ -2441,7 +2408,7 @@ collect_changed_file_state() {
2441
2408
  local output_file="$1"
2442
2409
  local changed_files_file path staged_hash unstaged_hash content_hash state
2443
2410
  : > "$output_file"
2444
- if [ ! -d /workspace/repo/.git ]; then
2411
+ if [ ! -d ${KASEKI_WORKSPACE_DIR}/repo/.git ]; then
2445
2412
  return 0
2446
2413
  fi
2447
2414
 
@@ -2450,14 +2417,14 @@ collect_changed_file_state() {
2450
2417
 
2451
2418
  while IFS= read -r path || [ -n "$path" ]; do
2452
2419
  [ -z "$path" ] && continue
2453
- if git -C /workspace/repo ls-files --error-unmatch -- "$path" >/dev/null 2>&1; then
2454
- staged_hash="$(git -C /workspace/repo diff --binary --cached -- "$path" 2>/dev/null | sha256sum | awk '{print $1}')"
2455
- 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}')"
2456
2423
  state="tracked:staged=${staged_hash}:unstaged=${unstaged_hash}"
2457
- elif [ -f "/workspace/repo/$path" ]; then
2458
- 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}')"
2459
2426
  state="untracked:file=${content_hash}"
2460
- elif [ -d "/workspace/repo/$path" ]; then
2427
+ elif [ -d "${KASEKI_WORKSPACE_DIR}/repo/$path" ]; then
2461
2428
  state="untracked:directory"
2462
2429
  else
2463
2430
  state="untracked:missing"
@@ -2475,25 +2442,25 @@ restore_cleanup_disallowed_changes() {
2475
2442
 
2476
2443
  while IFS= read -r changed_file || [ -n "$changed_file" ]; do
2477
2444
  [ -z "$changed_file" ] && continue
2478
- 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
2479
2446
  emit_event "auto_lint_cleanup_file_restored" "file=$changed_file" "reason=not_in_cleanup_allowlist"
2480
- git -C /workspace/repo restore --staged --worktree -- "$changed_file" 2>/dev/null || true
2481
- 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
2482
2449
  done < "$disallowed_file"
2483
2450
  }
2484
2451
 
2485
2452
  check_auto_lint_cleanup_allowlist() {
2486
2453
  local before_file="$1"
2487
2454
  local after_file="$2"
2488
- local cleanup_created_file="/results/auto-lint-cleanup-created-files.txt"
2489
- local disallowed_file="/results/auto-lint-cleanup-disallowed-files.txt"
2490
- 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"
2491
2458
  local allowlist_patterns allowlist_regex changed_file disallowed_count unrestored_count
2492
2459
 
2493
2460
  : > "$cleanup_created_file"
2494
2461
  : > "$disallowed_file"
2495
2462
  : > "$post_restore_file"
2496
- if [ ! -d /workspace/repo/.git ]; then
2463
+ if [ ! -d ${KASEKI_WORKSPACE_DIR}/repo/.git ]; then
2497
2464
  return 0
2498
2465
  fi
2499
2466
 
@@ -2508,7 +2475,7 @@ check_auto_lint_cleanup_allowlist() {
2508
2475
  if [ -n "$allowlist_regex" ] && printf '%s\n' "$changed_file" | grep -Eq "^(${allowlist_regex})$"; then
2509
2476
  emit_event "quality_gate_rule_evaluated" "rule=auto_lint_cleanup_allowlist" "passed=true" "file=$changed_file"
2510
2477
  else
2511
- 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
2512
2479
  printf '%s\n' "$changed_file" >> "$disallowed_file"
2513
2480
  emit_event "quality_gate_rule_evaluated" "rule=auto_lint_cleanup_allowlist" "passed=false" "file=$changed_file"
2514
2481
  fi
@@ -2525,12 +2492,12 @@ check_auto_lint_cleanup_allowlist() {
2525
2492
  while IFS= read -r changed_file || [ -n "$changed_file" ]; do
2526
2493
  [ -z "$changed_file" ] && continue
2527
2494
  if grep -Fxq -- "$changed_file" "$post_restore_file"; then
2528
- 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
2529
2496
  unrestored_count=$((unrestored_count + 1))
2530
2497
  fi
2531
2498
  done < "$disallowed_file"
2532
2499
  if [ "$unrestored_count" -eq 0 ]; then
2533
- 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
2534
2501
  emit_event "auto_lint_cleanup_allowlist_restoration_complete" "restored=$disallowed_count" "unrestored=0"
2535
2502
  collect_git_artifacts
2536
2503
  return 0
@@ -2543,7 +2510,7 @@ check_auto_lint_cleanup_allowlist() {
2543
2510
  AUTO_LINT_CLEANUP_FAILURE_CLASSIFICATION="cleanup_allowlist_failed"
2544
2511
  QUALITY_EXIT=7
2545
2512
  QUALITY_FAILURE_REASON="auto_lint_cleanup_allowlist: $disallowed_count cleanup-created file(s) outside KASEKI_CHANGED_FILES_ALLOWLIST/KASEKI_VALIDATION_ALLOWLIST"
2546
- 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
2547
2514
  emit_error_event "auto_lint_cleanup_allowlist_failed" "$QUALITY_FAILURE_REASON" "continue"
2548
2515
  return 1
2549
2516
  }
@@ -2603,12 +2570,12 @@ run_auto_lint_cleanup() {
2603
2570
  return 0
2604
2571
  fi
2605
2572
 
2606
- if ! [ -d /workspace/repo ]; then
2573
+ if ! [ -d ${KASEKI_WORKSPACE_DIR}/repo ]; then
2607
2574
  AUTO_LINT_CLEANUP_EXIT=1
2608
2575
  AUTO_LINT_CLEANUP_RESULT="failed"
2609
2576
  AUTO_LINT_CLEANUP_CLASSIFICATION="directory_missing"
2610
2577
  AUTO_LINT_CLEANUP_FAILURE_CLASSIFICATION="directory_missing"
2611
- 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"
2612
2579
  printf 'workspace_missing\t%s\t0\tclassification=directory_missing\n' "$AUTO_LINT_CLEANUP_EXIT" >> "$AUTO_LINT_CLEANUP_TIMINGS_FILE"
2613
2580
  record_stage_timing "$stage_label" "$AUTO_LINT_CLEANUP_EXIT" "$(($(date +%s) - stage_start))" "directory_missing classification=directory_missing"
2614
2581
  emit_event "auto_lint_cleanup_finished" "exit_code=$AUTO_LINT_CLEANUP_EXIT" "result=failed" "classification=directory_missing" "reason=directory_missing"
@@ -2616,8 +2583,8 @@ run_auto_lint_cleanup() {
2616
2583
  return 0
2617
2584
  fi
2618
2585
 
2619
- cleanup_before_file="/results/auto-lint-cleanup-before-files.txt"
2620
- 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"
2621
2588
  collect_changed_file_set "$cleanup_before_file"
2622
2589
 
2623
2590
  set +e
@@ -2707,7 +2674,7 @@ baseline_validation_cache_key() {
2707
2674
  }
2708
2675
 
2709
2676
  baseline_validation_cache_dir() {
2710
- local cache_root="${KASEKI_BASELINE_CACHE_ROOT:-/cache/kaseki-baseline}"
2677
+ local cache_root="${KASEKI_BASELINE_CACHE_ROOT:-${KASEKI_CACHE_DIR}/kaseki-baseline}"
2711
2678
  local cache_key
2712
2679
  cache_key="$(baseline_validation_cache_key)"
2713
2680
  printf '%s/%s' "$cache_root" "$cache_key"
@@ -2741,19 +2708,19 @@ restore_baseline_validation_from_cache() {
2741
2708
  fi
2742
2709
 
2743
2710
  # Restore cached files to results directory
2744
- mkdir -p /results
2711
+ mkdir -p ${KASEKI_RESULTS_DIR}
2745
2712
 
2746
- 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
2747
2714
  return 1
2748
2715
  fi
2749
- 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
2750
2717
  return 1
2751
2718
  fi
2752
- 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
2753
2720
  return 1
2754
2721
  fi
2755
2722
  if [ -f "$cache_dir/validation-env.log" ]; then
2756
- 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
2757
2724
  fi
2758
2725
 
2759
2726
  return 0
@@ -2770,27 +2737,27 @@ save_baseline_validation_to_cache() {
2770
2737
  mkdir -p "$cache_dir" || return 1
2771
2738
 
2772
2739
  # Save validation results to cache
2773
- if [ -f /results/validation-baseline.log ]; then
2774
- 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
2775
2742
  fi
2776
2743
 
2777
- if [ -f /results/validation-baseline-raw.log ]; then
2778
- 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
2779
2746
  fi
2780
2747
 
2781
- if [ -f /results/validation-baseline-timings.tsv ]; then
2782
- 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
2783
2750
  fi
2784
2751
 
2785
- if [ -f /results/validation-baseline-env.log ]; then
2786
- 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
2787
2754
  fi
2788
2755
 
2789
2756
  return 0
2790
2757
  }
2791
2758
 
2792
2759
  checkout_baseline_repo() {
2793
- local baseline_dir="/workspace-baseline"
2760
+ local baseline_dir="${KASEKI_WORKSPACE_BASELINE_DIR}"
2794
2761
 
2795
2762
  # Clean up any existing baseline
2796
2763
  rm -rf "$baseline_dir" 2>/dev/null || true
@@ -2809,20 +2776,20 @@ checkout_baseline_repo() {
2809
2776
  emit_progress "baseline preparation" "installing baseline dependencies"
2810
2777
  if ! cd "$baseline_dir" && npm ci --prefer-offline 2>>"$KASEKI_LOG_DIR/baseline-npm-ci.log"; then
2811
2778
  emit_error_event "baseline_deps_failed" "Failed to install baseline dependencies" "continue"
2812
- cd /workspace/repo
2779
+ cd ${KASEKI_WORKSPACE_DIR}/repo
2813
2780
  return 1
2814
2781
  fi
2815
- cd /workspace/repo
2782
+ cd ${KASEKI_WORKSPACE_DIR}/repo
2816
2783
  fi
2817
2784
 
2818
2785
  return 0
2819
2786
  }
2820
2787
 
2821
2788
  run_baseline_validation() {
2822
- local baseline_dir="/workspace-baseline"
2823
- local baseline_log="/results/validation-baseline.log"
2824
- local baseline_raw_log="/results/validation-baseline-raw.log"
2825
- 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"
2826
2793
  local baseline_exit_var="BASELINE_VALIDATION_EXIT"
2827
2794
  local baseline_detail_var="BASELINE_VALIDATION_FAILED_COMMAND_DETAIL"
2828
2795
  local baseline_reason_var="BASELINE_VALIDATION_FAILURE_REASON"
@@ -2844,7 +2811,7 @@ run_baseline_validation() {
2844
2811
  "$baseline_log" \
2845
2812
  "$baseline_raw_log" \
2846
2813
  "$baseline_timings" \
2847
- "/results/validation-baseline-env.log" \
2814
+ "${KASEKI_RESULTS_DIR}/validation-baseline-env.log" \
2848
2815
  "baseline_validation_failed" \
2849
2816
  "$baseline_exit_var" \
2850
2817
  "$baseline_detail_var" \
@@ -2864,10 +2831,10 @@ run_baseline_validation() {
2864
2831
  }
2865
2832
 
2866
2833
  analyze_test_failures_baseline() {
2867
- local baseline_log="/results/validation-baseline.log"
2868
- local working_log="/results/pre-validation.log"
2869
- local output_file="/results/test-baseline-comparison.json"
2870
- 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}"
2871
2838
 
2872
2839
  if [ ! -f "$baseline_log" ] || [ ! -f "$working_log" ]; then
2873
2840
  emit_progress "test failure analysis" "skipped (baseline or working log missing)"
@@ -2908,11 +2875,11 @@ analyze_test_failures_baseline() {
2908
2875
  }
2909
2876
 
2910
2877
  analyze_validation_failure_causality() {
2911
- local baseline_log="/results/validation-baseline.log"
2912
- local post_change_log="/results/validation.log"
2913
- local git_diff="/results/git.diff"
2914
- local changed_files="/results/changed-files.txt"
2915
- 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"
2916
2883
 
2917
2884
  # Skip if no baseline (first run)
2918
2885
  if [ ! -f "$baseline_log" ]; then
@@ -3058,13 +3025,13 @@ run_validation_commands() {
3058
3025
  record_stage_timing "$stage_label" 0 0 "skipped_by_config"
3059
3026
  else
3060
3027
  # Checkpoint: Verify working directory exists before validation.
3061
- if ! [ -d /workspace/repo ]; then
3062
- 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"
3063
3030
  printf 'Current pwd: %s\n' "$(pwd 2>&1 || echo '<pwd failed>')" | tee -a "$log_file"
3064
3031
  printf 'Filesystem state:\n' | tee -a "$log_file"
3065
3032
  find /workspace -maxdepth 3 -type f 2>&1 | head -100 | tee -a "$log_file"
3066
3033
  validation_exit_ref=1
3067
- 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"
3068
3035
  validation_reason_ref="$failure_reason_prefix: workspace_missing"
3069
3036
  record_stage_timing "$stage_label" "$validation_exit_ref" "$(($(date +%s) - stage_start))" "directory_missing"
3070
3037
  else
@@ -3090,7 +3057,7 @@ run_validation_commands() {
3090
3057
  printf '[validation command] working_directory=%s\n' "$(pwd 2>&1 || echo '<pwd failed>')"
3091
3058
  printf '[validation command] node_version=%s\n' "$(node --version 2>&1 || echo '<node not found>')"
3092
3059
  printf '[validation command] npm_version=%s\n' "$(npm --version 2>&1 || echo '<npm not found>')"
3093
- 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>')"
3094
3061
  } | tee -a "$env_log"
3095
3062
  # Use pipefail to catch errors in any stage of the pipe.
3096
3063
  pipefail_was_enabled=0
@@ -3152,7 +3119,7 @@ run_validation_commands() {
3152
3119
  {
3153
3120
  printf '\n[DIAGNOSTICS] Validation pipeline stderr from filter/tee (last 50 lines):\n'
3154
3121
  printf '%s\n' "$FILTER_STDERR_TAIL"
3155
- } | tee -a "$log_file" /results/quality.log
3122
+ } | tee -a "$log_file" ${KASEKI_RESULTS_DIR}/quality.log
3156
3123
  {
3157
3124
  printf '\n[validation pipeline stderr tail]\n'
3158
3125
  printf '%s\n' "$FILTER_STDERR_TAIL"
@@ -3180,7 +3147,7 @@ run_validation_commands() {
3180
3147
  else
3181
3148
  printf ' (No stderr captured from filter/tee)\n'
3182
3149
  fi
3183
- } | tee -a "$log_file" /results/quality.log "$FILTER_DIAGNOSTICS_LOG"
3150
+ } | tee -a "$log_file" ${KASEKI_RESULTS_DIR}/quality.log "$FILTER_DIAGNOSTICS_LOG"
3184
3151
  fi
3185
3152
 
3186
3153
  if [ "$validation_infra_failure" = "true" ] && [ "$validation_exit_ref" -eq 0 ]; then
@@ -3204,13 +3171,13 @@ run_validation_commands() {
3204
3171
  printf '\n[DIAGNOSTICS] Validation command failed with directory access error:\n'
3205
3172
  printf 'Working directory status:\n'
3206
3173
  printf ' Current pwd: %s\n' "$(pwd 2>&1 || echo '<pwd failed>')"
3207
- printf ' /workspace/repo exists: %s\n' "$([ -d /workspace/repo ] && echo 'yes' || echo 'no')"
3208
- if [ -L /workspace/repo/node_modules ]; then
3209
- 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>')"
3210
3177
  fi
3211
3178
  printf 'Last 20 lines of validation log:\n'
3212
3179
  tail -20 "$log_file"
3213
- } | tee -a /results/quality.log
3180
+ } | tee -a ${KASEKI_RESULTS_DIR}/quality.log
3214
3181
  fi
3215
3182
  # Fail-fast: if enabled, stop validation loop at first failure.
3216
3183
  if [ "$KASEKI_VALIDATION_FAIL_FAST" -eq 1 ]; then
@@ -3291,7 +3258,7 @@ write_repo_memory_summary() {
3291
3258
  return 0
3292
3259
  fi
3293
3260
  local updated_at
3294
- 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')"
3295
3262
  updated_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
3296
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' || {
3297
3264
  const fs = require('fs');
@@ -3471,9 +3438,9 @@ is_transient_goal_setting_failure() {
3471
3438
  local stderr_content="$2"
3472
3439
 
3473
3440
  # First, check if we have an explicit validation reason code from our helper
3474
- if [ -f /results/goal-setting-validation-reason.txt ]; then
3441
+ if [ -f ${KASEKI_RESULTS_DIR}/goal-setting-validation-reason.txt ]; then
3475
3442
  local reason_code
3476
- 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 "")
3477
3444
  case "$reason_code" in
3478
3445
  valid)
3479
3446
  return 1
@@ -3652,7 +3619,7 @@ validate_goal_setting_artifact() {
3652
3619
  local candidate_artifact="$1"
3653
3620
  local final_artifact="$2"
3654
3621
  local reason_file="$3"
3655
- local results_dir="${KASEKI_RESULTS_DIR:-/results}"
3622
+ local results_dir="${KASEKI_RESULTS_DIR:-${KASEKI_RESULTS_DIR}}"
3656
3623
 
3657
3624
  if ! [ -f "$candidate_artifact" ]; then
3658
3625
  {
@@ -3692,7 +3659,7 @@ validate_goal_setting_artifact() {
3692
3659
  validate_goal_setting_artifact_with_node() {
3693
3660
  local candidate_artifact="$1"
3694
3661
  local reason_file="$2"
3695
- local results_dir="${KASEKI_RESULTS_DIR:-/results}"
3662
+ local results_dir="${KASEKI_RESULTS_DIR:-${KASEKI_RESULTS_DIR}}"
3696
3663
 
3697
3664
  local validation_output
3698
3665
  validation_output=$(node -e "
@@ -3877,13 +3844,13 @@ run_goal_setting_agent() {
3877
3844
  set_current_stage "pi goal-setting agent"
3878
3845
 
3879
3846
  if [ "$KASEKI_GOAL_SETTING" = "0" ]; then
3880
- 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
3881
3848
  record_stage_timing "pi goal-setting agent" 0 0 "skipped_by_config"
3882
3849
  return 0
3883
3850
  fi
3884
3851
 
3885
3852
  if [ "$KASEKI_DRY_RUN" = "1" ]; then
3886
- 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
3887
3854
  record_stage_timing "pi goal-setting agent" 0 0 "dry_run=true"
3888
3855
  return 0
3889
3856
  fi
@@ -3895,23 +3862,23 @@ run_goal_setting_agent() {
3895
3862
  OPENROUTER_API_KEY="$openrouter_api_key" \
3896
3863
  timeout --signal=SIGTERM "$KASEKI_GOAL_SETTING_TIMEOUT_SECONDS" \
3897
3864
  pi --mode json --no-session --provider "$KASEKI_PROVIDER" --model "$KASEKI_GOAL_SETTING_MODEL" "$goal_setting_prompt" \
3898
- 2> >(tee -a /results/goal-setting-stderr.log >&2) \
3865
+ 2> >(tee -a ${KASEKI_RESULTS_DIR}/goal-setting-stderr.log >&2) \
3899
3866
  | tee "$GOAL_SETTING_RAW_EVENTS" \
3900
- | 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
3901
3868
  GOAL_SETTING_EXIT="${PIPESTATUS[0]}"
3902
3869
  GOAL_SETTING_DURATION_SECONDS=$(($(date +%s) - goal_setting_start))
3903
3870
  unset goal_setting_prompt
3904
3871
  set +e
3905
3872
 
3906
- 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
3907
3874
  GOAL_SETTING_EXIT=86
3908
- goal_setting_validation_summary="$(cat /results/goal-setting-validation-summary.txt 2>/dev/null || printf 'goal-setting artifact validation failed')"
3909
- 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"
3910
3877
  fi
3911
3878
 
3912
3879
  rm -f "$GOAL_SETTING_CANDIDATE_ARTIFACT"
3913
- 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
3914
- 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)"
3915
3882
 
3916
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"
3917
3884
 
@@ -3921,7 +3888,7 @@ run_goal_setting_agent() {
3921
3888
  fi
3922
3889
 
3923
3890
  emit_progress "pi goal-setting agent" "wrote goal-setting artifact"
3924
- 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
3925
3892
 
3926
3893
  return 0
3927
3894
  }
@@ -3956,7 +3923,7 @@ write_goal_setting_metrics() {
3956
3923
  model: '${GOAL_SETTING_ACTUAL_MODEL:-unknown}',
3957
3924
  timeout_seconds: ${KASEKI_GOAL_SETTING_TIMEOUT_SECONDS:-300}
3958
3925
  };
3959
- 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');
3960
3927
  " 2>/dev/null || {
3961
3928
  # Fallback to jq or printf if node fails
3962
3929
  {
@@ -3972,7 +3939,7 @@ write_goal_setting_metrics() {
3972
3939
  printf ' "model": "%s",\n' "${GOAL_SETTING_ACTUAL_MODEL:-unknown}"
3973
3940
  printf ' "timeout_seconds": %d\n' "${KASEKI_GOAL_SETTING_TIMEOUT_SECONDS:-300}"
3974
3941
  printf '}\n'
3975
- } > /results/goal-setting-metrics.json
3942
+ } > ${KASEKI_RESULTS_DIR}/goal-setting-metrics.json
3976
3943
  }
3977
3944
  }
3978
3945
 
@@ -3981,9 +3948,9 @@ classify_goal_setting_error() {
3981
3948
  local stderr_content="$2"
3982
3949
 
3983
3950
  # Check validation reason file first (most authoritative)
3984
- if [ -f /results/goal-setting-validation-reason.txt ]; then
3951
+ if [ -f ${KASEKI_RESULTS_DIR}/goal-setting-validation-reason.txt ]; then
3985
3952
  local reason_code
3986
- 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 "")
3987
3954
  case "$reason_code" in
3988
3955
  schema_mismatch)
3989
3956
  echo "GOAL_SETTING_SCHEMA_MISMATCH"
@@ -4062,7 +4029,7 @@ run_goal_setting_agent_with_retry() {
4062
4029
  set -e
4063
4030
 
4064
4031
  # Append stderr to results for logging
4065
- 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
4066
4033
  goal_setting_last_stderr="$(cat "$goal_setting_stderr_capture" 2>/dev/null || true)"
4067
4034
  rm -f "$goal_setting_stderr_capture"
4068
4035
 
@@ -4094,7 +4061,7 @@ run_goal_setting_agent_with_retry() {
4094
4061
  attempt=$((attempt + 1))
4095
4062
  # Reset goal-setting artifacts for retry
4096
4063
  rm -f "$GOAL_SETTING_ARTIFACT" "$GOAL_SETTING_RAW_EVENTS" 2>/dev/null || true
4097
- 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
4098
4065
  continue
4099
4066
  fi
4100
4067
  else
@@ -4128,9 +4095,9 @@ is_transient_scouting_failure() {
4128
4095
  local stderr_content="$2"
4129
4096
 
4130
4097
  # First, check if we have an explicit validation reason code from our helper
4131
- if [ -f /results/scouting-validation-reason.txt ]; then
4098
+ if [ -f ${KASEKI_RESULTS_DIR}/scouting-validation-reason.txt ]; then
4132
4099
  local reason_code
4133
- 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 "")
4134
4101
  case "$reason_code" in
4135
4102
  valid)
4136
4103
  # This shouldn't happen when exit_code=86, but just in case
@@ -4265,48 +4232,48 @@ run_scouting_agent() {
4265
4232
  printf '\n==> pi scouting agent\n'
4266
4233
  set_current_stage "pi scouting agent"
4267
4234
  if [ "$KASEKI_SCOUTING" = "0" ]; then
4268
- 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
4269
4236
  record_stage_timing "pi scouting agent" 0 0 "skipped_by_config"
4270
4237
  return 0
4271
4238
  fi
4272
4239
  if [ "$KASEKI_DRY_RUN" = "1" ]; then
4273
- 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
4274
4241
  record_stage_timing "pi scouting agent" 0 0 "dry_run=true"
4275
4242
  return 0
4276
4243
  fi
4277
4244
 
4278
4245
  scouting_prompt="$(build_scouting_prompt)"
4279
4246
  scouting_start="$(date +%s)"
4280
- scout_dirty_before="$(git status --porcelain 2>> /results/scouting-stderr.log || true)"
4281
- 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
4282
4249
  set +e
4283
4250
  OPENROUTER_API_KEY="$openrouter_api_key" \
4284
4251
  timeout --signal=SIGTERM "$KASEKI_SCOUTING_TIMEOUT_SECONDS" \
4285
4252
  pi --mode json --no-session --provider "$KASEKI_PROVIDER" --model "$KASEKI_SCOUTING_MODEL" "$scouting_prompt" \
4286
- 2> >(tee -a /results/scouting-stderr.log >&2) \
4253
+ 2> >(tee -a ${KASEKI_RESULTS_DIR}/scouting-stderr.log >&2) \
4287
4254
  | tee "$SCOUTING_RAW_EVENTS" \
4288
- | 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
4289
4256
  SCOUTING_EXIT="${PIPESTATUS[0]}"
4290
4257
  SCOUTING_DURATION_SECONDS=$(($(date +%s) - scouting_start))
4291
4258
  unset scouting_prompt
4292
4259
  set +e
4293
- 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
4294
4261
 
4295
- 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
4296
4263
  SCOUTING_EXIT=86
4297
- scouting_validation_summary="$(cat /results/scouting-validation-summary.txt 2>/dev/null || printf 'scouting artifact validation failed')"
4298
- 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"
4299
4266
  fi
4300
- 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)"
4301
4268
  if [ "$SCOUTING_EXIT" -eq 0 ] && [ "$scout_dirty_before" != "$scout_dirty_after" ]; then
4302
4269
  SCOUTING_EXIT=86
4303
4270
  emit_error_event "pi_scouting_workspace_modified" "Read-only scouting changed repository state before coding" "exit"
4304
4271
  fi
4305
4272
  rm -f "$SCOUTING_CANDIDATE_ARTIFACT"
4306
- git reset --hard -q HEAD 2>> /results/scouting-stderr.log || true
4307
- git clean -fd -q 2>> /results/scouting-stderr.log || true
4308
- 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
4309
- 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)"
4310
4277
  record_stage_timing "pi scouting agent" "$SCOUTING_EXIT" "$SCOUTING_DURATION_SECONDS" "artifact=$SCOUTING_ARTIFACT timeout_seconds=$KASEKI_SCOUTING_TIMEOUT_SECONDS"
4311
4278
  if [ "$SCOUTING_EXIT" -ne 0 ]; then
4312
4279
  STATUS="$SCOUTING_EXIT"
@@ -4316,7 +4283,7 @@ run_scouting_agent() {
4316
4283
  fi
4317
4284
  emit_progress "pi scouting agent" "wrote scouting artifact"
4318
4285
  # Clean up validation reason file on success
4319
- 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
4320
4287
  return 0
4321
4288
  }
4322
4289
 
@@ -4344,7 +4311,7 @@ run_scouting_agent_with_retry() {
4344
4311
  set -e
4345
4312
 
4346
4313
  # Append stderr to results for logging
4347
- 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
4348
4315
  scouting_last_stderr="$(cat "$scouting_stderr_capture" 2>/dev/null || true)"
4349
4316
  rm -f "$scouting_stderr_capture"
4350
4317
 
@@ -4363,7 +4330,7 @@ run_scouting_agent_with_retry() {
4363
4330
  # Reset scouting artifacts for retry
4364
4331
  rm -f "$SCOUTING_ARTIFACT" "$SCOUTING_RAW_EVENTS" 2>/dev/null || true
4365
4332
  # Clean up validation reason file from previous attempt
4366
- 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
4367
4334
  continue
4368
4335
  fi
4369
4336
  else
@@ -4387,13 +4354,13 @@ run_scouting_agent_with_retry() {
4387
4354
 
4388
4355
  snapshot_attempt_artifacts() {
4389
4356
  local attempt_dir
4390
- attempt_dir="/results/attempt-$1"
4357
+ attempt_dir="${KASEKI_RESULTS_DIR}/attempt-$1"
4391
4358
  mkdir -p "$attempt_dir" 2>/dev/null || return 0
4392
4359
  for artifact in \
4393
4360
  pi-events.jsonl pi-summary.json pi-stderr.log git.diff git.status changed-files.txt \
4394
4361
  quality.log validation.log validation-raw.log validation-timings.tsv goal-check.json; do
4395
- if [ -e "/results/$artifact" ]; then
4396
- 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
4397
4364
  fi
4398
4365
  done
4399
4366
  }
@@ -4401,7 +4368,7 @@ snapshot_attempt_artifacts() {
4401
4368
  collect_goal_check_feedback() {
4402
4369
  local instance_name="$1"
4403
4370
  local goal_setting_path="$GOAL_SETTING_ARTIFACT"
4404
- local results_dir="${KASEKI_RESULTS_DIR:-/results}"
4371
+ local results_dir="${KASEKI_RESULTS_DIR:-${KASEKI_RESULTS_DIR}}"
4405
4372
  local goal_check_path="$results_dir/goal-check.json"
4406
4373
  local metadata_path="$results_dir/metadata.json"
4407
4374
  local feedback_file="$results_dir/goal-feedback.jsonl"
@@ -4417,9 +4384,9 @@ collect_goal_check_feedback() {
4417
4384
 
4418
4385
  collect_run_evaluation_feedback() {
4419
4386
  local instance_name="$1"
4420
- local run_evaluation_path="/results/run-evaluation.json"
4421
- local metadata_path="/results/metadata.json"
4422
- 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"
4423
4390
 
4424
4391
  # Only collect if run-evaluation succeeded and artifacts exist
4425
4392
  if [ ! -f "$run_evaluation_path" ] || [ "$RUN_EVALUATION_EXIT" -ne 0 ]; then
@@ -4433,14 +4400,14 @@ collect_run_evaluation_feedback() {
4433
4400
 
4434
4401
  build_goal_check_prompt() {
4435
4402
  local validation_tail progress_tail goal_setting_context validation_context test_impact_context causality_context
4436
- 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)"
4437
4404
  if [ -n "$(printf '%s' "$validation_tail" | tr -d '[:space:]')" ]; then
4438
4405
  validation_context="Validation log tail (last 80 lines):
4439
4406
  $validation_tail"
4440
4407
  else
4441
- 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."
4442
4409
  fi
4443
- 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)"
4444
4411
  if [ -s "$TEST_IMPACT_WARNINGS_ARTIFACT" ]; then
4445
4412
  test_impact_context="Static test-impact warnings artifact ($TEST_IMPACT_WARNINGS_ARTIFACT):
4446
4413
  $(cat "$TEST_IMPACT_WARNINGS_ARTIFACT" 2>/dev/null)
@@ -4466,7 +4433,7 @@ $(head -n 200 "$GOAL_SETTING_ARTIFACT" 2>/dev/null)
4466
4433
  fi
4467
4434
 
4468
4435
  # Include causality assessment if available (helps interpret validation failures)
4469
- if [ -f /results/validation-causality-analysis.json ]; then
4436
+ if [ -f ${KASEKI_RESULTS_DIR}/validation-causality-analysis.json ]; then
4470
4437
  # shellcheck disable=SC2016
4471
4438
  causality_context="VALIDATION FAILURE CAUSALITY ASSESSMENT:
4472
4439
 
@@ -4526,11 +4493,11 @@ Determine if the agent successfully met the requirements specified in the goal-s
4526
4493
 
4527
4494
  **Agent Artifacts** (use to verify requirements were met):
4528
4495
  - Scouting report: $SCOUTING_ARTIFACT
4529
- - Changed files: /results/changed-files.txt
4530
- - Git diff: /results/git.diff
4531
- - 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
4532
4499
  - Static test-impact warnings (non-blocking): $TEST_IMPACT_WARNINGS_ARTIFACT
4533
- - 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
4534
4501
 
4535
4502
  ## Evaluation Framework: SMART Criteria Check
4536
4503
 
@@ -4542,6 +4509,14 @@ Determine if the agent successfully met the requirements specified in the goal-s
4542
4509
 
4543
4510
  For each dimension, provide evidence by citing specific file locations, test names, or validation results.
4544
4511
 
4512
+ ## Success Criteria Assessment Contract
4513
+
4514
+ Use this contract when reading the goal-setting artifact:
4515
+
4516
+ - GOAL_CHECK_CONTRACT_PER_CRITERION_SMART: Assess each success criterion or acceptance criterion independently against all five SMART dimensions (Specific, Measurable, Achievable, Relevant, Time-bound), not just the overall goal.
4517
+ - GOAL_CHECK_CONTRACT_EVIDENCE_REQUIRED: For every met or unmet criterion assessment, cite concrete evidence from the diff, changed files, tests, validation results, or other listed artifacts.
4518
+ - GOAL_CHECK_CONTRACT_MEASURABLE_ACCEPTANCE_CRITERIA: Treat measurable acceptance criteria as observable pass/fail conditions; flag vague goals or intent statements as insufficient unless the implementation evidence maps them to concrete, verifiable outcomes.
4519
+
4545
4520
  ## Evidence Requirements
4546
4521
 
4547
4522
  Evidence must be SPECIFIC and VERIFIABLE. Reference concrete artifacts:
@@ -4581,7 +4556,7 @@ Example: "Null handling is done (parseRole returns 'Unnamed Role'), but test cov
4581
4556
  - Do not print, inspect, or expose environment variables, secrets, credentials, API keys, or mounted secret files.
4582
4557
  - Decide whether the goal requirements were realized. Do not evaluate code style, architecture, or elegance.
4583
4558
  - If anti-patterns were specified in goal-setting (do_not_modify, do_not_break), verify they were respected.
4584
- - 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.
4585
4560
 
4586
4561
  ## Required JSON artifact
4587
4562
 
@@ -4632,12 +4607,12 @@ run_goal_check() {
4632
4607
  printf '\n==> goal check\n'
4633
4608
  set_current_stage "goal check"
4634
4609
  if [ "$KASEKI_GOAL_CHECK" != "1" ]; then
4635
- 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
4636
4611
  record_stage_timing "goal check" 0 0 "skipped_by_config attempt=$attempt"
4637
4612
  return 0
4638
4613
  fi
4639
4614
  if [ ! -s "$SCOUTING_ARTIFACT" ]; then
4640
- 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
4641
4616
  record_stage_timing "goal check" 0 0 "skipped_no_scouting attempt=$attempt"
4642
4617
  return 0
4643
4618
  fi
@@ -4648,15 +4623,15 @@ run_goal_check() {
4648
4623
  OPENROUTER_API_KEY="$openrouter_api_key" \
4649
4624
  timeout --signal=SIGTERM "$KASEKI_GOAL_CHECK_TIMEOUT_SECONDS" \
4650
4625
  pi --mode json --no-session --provider "$KASEKI_PROVIDER" --model "$KASEKI_GOAL_CHECK_MODEL" "$goal_prompt" \
4651
- 2> >(tee -a /results/goal-check-stderr.log >&2) \
4626
+ 2> >(tee -a ${KASEKI_RESULTS_DIR}/goal-check-stderr.log >&2) \
4652
4627
  | tee "$GOAL_CHECK_RAW_EVENTS" \
4653
- | 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
4654
4629
  GOAL_CHECK_EXIT="${PIPESTATUS[0]}"
4655
4630
  unset goal_prompt
4656
4631
  GOAL_CHECK_DURATION_SECONDS=$((GOAL_CHECK_DURATION_SECONDS + $(date +%s) - goal_start))
4657
4632
  set +e
4658
4633
 
4659
- 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
4660
4635
 
4661
4636
  if [ "$GOAL_CHECK_EXIT" -eq 0 ] && [ ! -f "$GOAL_CHECK_CANDIDATE_ARTIFACT" ]; then
4662
4637
  # Recover from goal-check agents that printed the verdict in assistant text instead of writing the artifact.
@@ -4754,38 +4729,38 @@ if (valid.size === 1) {
4754
4729
  const recovered = [...valid.values()][0];
4755
4730
  fs.writeFileSync(candidatePath, JSON.stringify(recovered, null, 2) + "\n");
4756
4731
  const note = { timestamp: new Date().toISOString(), attempt, event: "goal_check_artifact_recovered_from_assistant_text", artifact: candidatePath, raw_events: rawPath, filtered_events: filteredPath };
4757
- 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");
4758
4733
  }
4759
- ' "$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
4760
4735
  fi
4761
4736
 
4762
- 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
4763
4738
  GOAL_CHECK_EXIT=86
4764
- goal_check_validation_reason="$(cat /results/goal-check-validation-reason.txt 2>/dev/null || printf 'schema_mismatch')"
4765
- 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')"
4766
4741
  case "$goal_check_validation_reason" in
4767
4742
  missing_file)
4768
4743
  GOAL_CHECK_FAILURE_REASON="goal_check_artifact_missing"
4769
- 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"
4770
4745
  ;;
4771
4746
  malformed_json)
4772
4747
  GOAL_CHECK_FAILURE_REASON="goal_check_artifact_malformed"
4773
- 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"
4774
4749
  ;;
4775
4750
  *)
4776
4751
  GOAL_CHECK_FAILURE_REASON="goal_check_artifact_invalid"
4777
- 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"
4778
4753
  ;;
4779
4754
  esac
4780
4755
  fi
4781
4756
  rm -f "$GOAL_CHECK_CANDIDATE_ARTIFACT"
4782
- 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)"
4783
4758
 
4784
4759
  if [ "$GOAL_CHECK_EXIT" -eq 0 ]; then
4785
- verdict_met="$(node -e 'const v=require("/results/goal-check.json"); console.log(v.met ? "true" : "false")' 2>/dev/null || printf 'false')"
4786
- retry_prompt="$(node -e 'const v=require("/results/goal-check.json"); console.log(v.retry_prompt || "")' 2>/dev/null || true)"
4787
- verdict_summary="$(node -e 'const v=require("/results/goal-check.json"); console.log(v.summary || "")' 2>/dev/null || true)"
4788
- 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)"
4789
4764
  if [ "$verdict_met" = "true" ]; then
4790
4765
  GOAL_CHECK_MET=true
4791
4766
  GOAL_CHECK_RETRY_PROMPT=""
@@ -4808,12 +4783,12 @@ if (valid.size === 1) {
4808
4783
 
4809
4784
  build_run_evaluation_prompt() {
4810
4785
  local validation_tail progress_tail stage_timings dependency_cache restoration_report draft_pr_body metadata_text goal_setting_context test_impact_context
4811
- validation_tail="$(tail -80 /results/validation.log 2>/dev/null || true)"
4812
- progress_tail="$(tail -80 /results/progress.log 2>/dev/null || true)"
4813
- stage_timings="$(tail -80 /results/stage-timings.tsv 2>/dev/null || true)"
4814
- dependency_cache="$(tail -80 /results/dependency-cache.log 2>/dev/null || true)"
4815
- restoration_report="$(tail -80 /results/restoration-report.md 2>/dev/null || true)"
4816
- 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)"
4817
4792
  draft_pr_body="$(build_pr_body)"
4818
4793
  if [ -s "$TEST_IMPACT_WARNINGS_ARTIFACT" ]; then
4819
4794
  test_impact_context="Static test-impact warnings artifact ($TEST_IMPACT_WARNINGS_ARTIFACT):
@@ -4859,15 +4834,15 @@ This is NOT another goal-check. The goal-check evaluator already determined if t
4859
4834
  - Quality metrics, SMART criteria, anti-patterns
4860
4835
 
4861
4836
  **Agent Artifacts** (verify goal was realized):
4862
- - Goal-check verdict: /results/goal-check.json
4863
- - Scouting report: /results/scouting.json
4864
- - Changed files: /results/changed-files.txt
4865
- - Git diff and status: /results/git.diff, /results/git.status
4866
- - 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
4867
4842
  - Static test-impact warnings (non-blocking): $TEST_IMPACT_WARNINGS_ARTIFACT
4868
- - Stage timings: /results/stage-timings.tsv
4869
- - Progress log: /results/progress.log
4870
- - 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
4871
4846
 
4872
4847
  ## Evaluation Framework
4873
4848
 
@@ -5081,12 +5056,12 @@ run_run_evaluation() {
5081
5056
  printf '\n==> run evaluation\n'
5082
5057
  set_current_stage "run evaluation"
5083
5058
  if [ "$KASEKI_RUN_EVALUATION" != "1" ]; then
5084
- 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
5085
5060
  record_stage_timing "run evaluation" 0 0 "skipped_by_config"
5086
5061
  return 0
5087
5062
  fi
5088
5063
  if [ "$KASEKI_DRY_RUN" = "1" ]; then
5089
- 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
5090
5065
  record_stage_timing "run evaluation" 0 0 "dry_run=true"
5091
5066
  return 0
5092
5067
  fi
@@ -5095,19 +5070,19 @@ run_run_evaluation() {
5095
5070
  write_metadata "$STATUS"
5096
5071
  evaluation_prompt="$(build_run_evaluation_prompt)"
5097
5072
  evaluation_start="$(date +%s)"
5098
- eval_dirty_before="$(git status --porcelain 2>> /results/run-evaluation-stderr.log || true)"
5099
- 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
5100
5075
  set +e
5101
5076
  OPENROUTER_API_KEY="$openrouter_api_key" \
5102
5077
  timeout --signal=SIGTERM "$KASEKI_RUN_EVALUATION_TIMEOUT_SECONDS" \
5103
5078
  pi --mode json --no-session --provider "$KASEKI_PROVIDER" --model "$KASEKI_RUN_EVALUATION_MODEL" "$evaluation_prompt" \
5104
- 2> >(tee -a /results/run-evaluation-stderr.log >&2) \
5079
+ 2> >(tee -a ${KASEKI_RESULTS_DIR}/run-evaluation-stderr.log >&2) \
5105
5080
  | tee "$RUN_EVALUATION_RAW_EVENTS" \
5106
- | 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
5107
5082
  RUN_EVALUATION_EXIT="${PIPESTATUS[0]}"
5108
5083
  unset evaluation_prompt
5109
5084
  RUN_EVALUATION_DURATION_SECONDS=$((RUN_EVALUATION_DURATION_SECONDS + $(date +%s) - evaluation_start))
5110
- 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
5111
5086
  set +e
5112
5087
 
5113
5088
  if [ "$RUN_EVALUATION_EXIT" -eq 0 ] && ! node -e '
@@ -5137,15 +5112,15 @@ artifact.timestamp = new Date().toISOString();
5137
5112
  artifact.model = model;
5138
5113
  artifact.actual_model = actualModel;
5139
5114
  fs.writeFileSync(output, JSON.stringify(artifact, null, 2) + "\n");
5140
- ' "$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
5141
5116
  RUN_EVALUATION_EXIT=86
5142
5117
  emit_error_event "run_evaluation_artifact_invalid" "Run-evaluation Pi did not write a schema-valid JSON artifact" "continue"
5143
5118
  fi
5144
5119
  rm -f "$RUN_EVALUATION_CANDIDATE_ARTIFACT"
5145
- 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
5146
- 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)"
5147
5122
  if [ -s "$RUN_EVALUATION_ARTIFACT" ]; then
5148
- 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
5149
5124
  const fs = require('fs');
5150
5125
  const [file, actualModel] = process.argv.slice(2);
5151
5126
  const artifact = JSON.parse(fs.readFileSync(file, 'utf8'));
@@ -5154,12 +5129,12 @@ fs.writeFileSync(file, JSON.stringify(artifact, null, 2) + '\n');
5154
5129
  NODE
5155
5130
  fi
5156
5131
 
5157
- 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)"
5158
5133
  if [ "$eval_dirty_before" != "$eval_dirty_after" ]; then
5159
5134
  RUN_EVALUATION_EXIT=86
5160
5135
  emit_error_event "run_evaluation_workspace_modified" "Read-only run evaluation changed repository state; restoring workspace" "continue"
5161
- git reset --hard -q HEAD 2>> /results/run-evaluation-stderr.log || true
5162
- 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
5163
5138
  fi
5164
5139
 
5165
5140
  if [ "$RUN_EVALUATION_EXIT" -ne 0 ] || [ ! -s "$RUN_EVALUATION_ARTIFACT" ]; then
@@ -5256,19 +5231,19 @@ META
5256
5231
  log_github_private_key_metadata() {
5257
5232
  local key_file="$1"
5258
5233
  local health_log="$2"
5259
- local metadata_file="/results/github-app-private-key-metadata.json"
5234
+ local metadata_file="${KASEKI_RESULTS_DIR}/github-app-private-key-metadata.json"
5260
5235
  github_private_key_metadata_json "$key_file" > "$metadata_file"
5261
5236
  printf '[health-check] GitHub App private key metadata: %s\n' "$(tr -d '\n' < "$metadata_file")" | tee -a "$health_log"
5262
5237
  }
5263
5238
 
5264
5239
 
5265
5240
  github_askpass_runtime_dir() {
5266
- printf '%s\n' "${KASEKI_GITHUB_ASKPASS_DIR:-/results}"
5241
+ printf '%s\n' "${KASEKI_GITHUB_ASKPASS_DIR:-${KASEKI_RESULTS_DIR}}"
5267
5242
  }
5268
5243
 
5269
5244
  create_github_askpass_helper() {
5270
5245
  local log_file log_prefix askpass_dir askpass_file username_smoke_output password_smoke_output
5271
- log_file="${1:-/results/git-push.log}"
5246
+ log_file="${1:-${KASEKI_RESULTS_DIR}/git-push.log}"
5272
5247
  log_prefix="${2:-[github-askpass]}"
5273
5248
  GITHUB_ASKPASS_FILE=""
5274
5249
 
@@ -5346,7 +5321,7 @@ EOF_ASKPASS
5346
5321
  check_github_operations_health() {
5347
5322
  # Preflight health check for github operations before pi agent runs
5348
5323
  # Tests: GitHub App secrets, git config, Node.js token generation capability
5349
- 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}"
5350
5325
  github_preflight_fail() {
5351
5326
  local classification="$1"
5352
5327
  local remediation="$2"
@@ -5600,7 +5575,7 @@ validate_github_api_response() {
5600
5575
  local http_status response log_file error_type error_message json_valid
5601
5576
  http_status="$1"
5602
5577
  response="$2"
5603
- log_file="${3:-/results/git-push.log}"
5578
+ log_file="${3:-${KASEKI_RESULTS_DIR}/git-push.log}"
5604
5579
 
5605
5580
  # Try to parse error info from response
5606
5581
  error_type="unknown"
@@ -5708,7 +5683,7 @@ apply_github_pr_labels() {
5708
5683
  repo="$2"
5709
5684
  issue_number="$3"
5710
5685
  token="$4"
5711
- log_file="${5:-/results/git-push.log}"
5686
+ log_file="${5:-${KASEKI_RESULTS_DIR}/git-push.log}"
5712
5687
 
5713
5688
  if [ -z "$owner" ] || [ -z "$repo" ] || [ -z "$issue_number" ] || [ -z "$token" ]; then
5714
5689
  printf 'Warning: skipping PR label application because owner, repo, issue number, or token is missing\n' | tee -a "$log_file" >&2
@@ -5762,7 +5737,7 @@ request_owner_review() {
5762
5737
  local pr_response token log_file owner_login owner_type pr_number repo owner
5763
5738
  pr_response="$1"
5764
5739
  token="$2"
5765
- log_file="${3:-/results/git-push.log}"
5740
+ log_file="${3:-${KASEKI_RESULTS_DIR}/git-push.log}"
5766
5741
 
5767
5742
  if [ -z "$pr_response" ] || [ -z "$token" ]; then
5768
5743
  printf 'Warning: skipping owner review request because PR response or token is missing\n' | tee -a "$log_file" >&2
@@ -5827,7 +5802,7 @@ request_owner_review() {
5827
5802
 
5828
5803
  # Request owner review with retry logic
5829
5804
  local retry_count=0 max_retries=2 request_success=0 backoff_delay=2
5830
- local review_request_log="/results/owner-review-request.log"
5805
+ local review_request_log="${KASEKI_RESULTS_DIR}/owner-review-request.log"
5831
5806
  : > "$review_request_log"
5832
5807
 
5833
5808
  while [ $retry_count -le "$max_retries" ]; do
@@ -6020,7 +5995,7 @@ derive_pr_title() {
6020
5995
 
6021
5996
  candidate="$(printf '%s' "${TASK_PROMPT:-}" | sanitize_pr_metadata_text)"
6022
5997
  prompt_for_prefix="$candidate"
6023
- if [ -s /results/result-summary.md ]; then
5998
+ if [ -s ${KASEKI_RESULTS_DIR}/result-summary.md ]; then
6024
5999
  summary_candidate="$(
6025
6000
  awk '
6026
6001
  /^##[[:space:]]+Summary[[:space:]]*$/ { in_summary=1; next }
@@ -6031,13 +6006,13 @@ derive_pr_title() {
6031
6006
  sub(/^[[:space:]]*[0-9]+[.)][[:space:]]+/, "", line)
6032
6007
  if (line !~ /^[[:space:]]*$/) { print line; exit }
6033
6008
  }
6034
- ' /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
6035
6010
  )"
6036
6011
  fi
6037
6012
  if [ -n "$summary_candidate" ]; then
6038
6013
  candidate="$summary_candidate"
6039
- elif [ -z "$candidate" ] && [ -s /results/result-summary.md ]; then
6040
- 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)"
6041
6016
  fi
6042
6017
 
6043
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:]]+$//')"
@@ -6047,8 +6022,8 @@ derive_pr_title() {
6047
6022
  candidate="$stripped"
6048
6023
  fi
6049
6024
 
6050
- if [ -s /results/changed-files.txt ]; then
6051
- 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)"
6052
6027
  else
6053
6028
  changed_files=""
6054
6029
  fi
@@ -6119,7 +6094,7 @@ format_pr_command_results() {
6119
6094
  }
6120
6095
 
6121
6096
  format_pr_changed_files() {
6122
- local changed_files_file="/results/changed-files.txt"
6097
+ local changed_files_file="${KASEKI_RESULTS_DIR}/changed-files.txt"
6123
6098
  local details_threshold=8
6124
6099
  if [ ! -s "$changed_files_file" ]; then
6125
6100
  printf '0 files changed.\n'
@@ -6209,8 +6184,8 @@ build_pr_agent_review() {
6209
6184
  case "$validation_pass_flag" in
6210
6185
  ''|*[!0-9-]*) validation_pass_flag=0 ;;
6211
6186
  esac
6212
- local goal_file="/results/goal-check.json"
6213
- 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"
6214
6189
  local goal_summary evidence missing validation_notes risks
6215
6190
 
6216
6191
  goal_summary=""
@@ -6259,7 +6234,7 @@ NODE
6259
6234
  }
6260
6235
 
6261
6236
  build_pr_agent_evaluation() {
6262
- local evaluation_file="/results/run-evaluation.json"
6237
+ local evaluation_file="${KASEKI_RESULTS_DIR}/run-evaluation.json"
6263
6238
  if [ ! -s "$evaluation_file" ]; then
6264
6239
  return 0
6265
6240
  fi
@@ -6357,8 +6332,8 @@ NODE
6357
6332
  }
6358
6333
 
6359
6334
  build_pr_improvements_summary() {
6360
- local changed_files_file="/results/changed-files.txt"
6361
- 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"
6362
6337
  local total=0 source_count=0 test_count=0 docs_count=0 config_count=0 other_count=0
6363
6338
  local path lower additions deletions summary_rows=0 summary_source=""
6364
6339
  local artifact raw_line line safe_line summary_capture=0 content json_text
@@ -6378,10 +6353,10 @@ build_pr_improvements_summary() {
6378
6353
  done < "$changed_files_file"
6379
6354
  fi
6380
6355
 
6381
- if [ -s /results/result-summary.md ]; then
6382
- 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"
6383
6358
  else
6384
- 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
6385
6360
  if [ -s "$artifact" ]; then
6386
6361
  summary_source="$artifact"
6387
6362
  break
@@ -6441,7 +6416,7 @@ EOF_JSON_SUMMARY
6441
6416
  continue
6442
6417
  ;;
6443
6418
  esac
6444
- 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
6445
6420
  continue
6446
6421
  fi
6447
6422
  line="$(printf '%s' "$raw_line" | sanitize_pr_metadata_text)"
@@ -6567,7 +6542,7 @@ $(build_pr_improvements_summary)
6567
6542
  ## Agent review
6568
6543
  $(build_pr_agent_review "$all_validation_statuses_pass")
6569
6544
 
6570
- $(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)
6571
6546
  ## Validation
6572
6547
  ### Validation statuses
6573
6548
  - Pre-agent validation: $pre_validation_status
@@ -6616,11 +6591,11 @@ run_github_operations() {
6616
6591
  owner="$GITHUB_REPO_OWNER"
6617
6592
  repo="$GITHUB_REPO_NAME"
6618
6593
  else
6619
- 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
6620
6595
  return 7
6621
6596
  fi
6622
6597
 
6623
- 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
6624
6599
  GITHUB_OPERATION_PHASE="setup"
6625
6600
 
6626
6601
  # Set git user for commits
@@ -6629,7 +6604,7 @@ run_github_operations() {
6629
6604
 
6630
6605
  # Generate GitHub App installation token
6631
6606
  GITHUB_OPERATION_PHASE="token_generation"
6632
- 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
6633
6608
  local github_app_token_helper="${KASEKI_GITHUB_APP_TOKEN_HELPER:-/usr/local/bin/github-app-token}"
6634
6609
  local token_stdout_tmp token_stderr_tmp token_exit_code token_stderr token_parse_result token_error token_http_status
6635
6610
  token_stdout_tmp="$(mktemp /tmp/github-app-token-stdout.XXXXXX)" || { printf 'Failed to create token stdout temp file\n' >&2; return 7; }
@@ -6650,7 +6625,7 @@ run_github_operations() {
6650
6625
  if [ "$token_parse_result" != "$token_error" ]; then
6651
6626
  token_http_status="${token_parse_result#*$'\t'}"
6652
6627
  fi
6653
- 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
6654
6629
  GITHUB_API_ERROR_TYPE="github_app_token_error"
6655
6630
  GITHUB_API_ERROR_MESSAGE="$token_error"
6656
6631
  GITHUB_API_HTTP_STATUS="$token_http_status"
@@ -6660,83 +6635,83 @@ run_github_operations() {
6660
6635
  fi
6661
6636
 
6662
6637
  # Use helper to extract token from JSON response
6663
- 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
6664
- 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
6665
6640
  GITHUB_PUSH_EXIT=7
6666
6641
  return 7
6667
6642
  fi
6668
6643
 
6669
6644
  if [ -z "$token" ]; then
6670
- 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
6671
6646
  GITHUB_PUSH_EXIT=7
6672
6647
  return 7
6673
6648
  fi
6674
6649
 
6675
- 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
6676
6651
 
6677
6652
  # Create and push feature branch
6678
6653
  GITHUB_OPERATION_PHASE="branch_creation"
6679
6654
  feature_branch="kaseki/$INSTANCE_NAME"
6680
- 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
6681
6656
  git checkout -b "$feature_branch" || {
6682
- 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
6683
6658
  GITHUB_PUSH_EXIT=7
6684
6659
  return 7
6685
6660
  }
6686
6661
 
6687
6662
  # Commit changes (git should already have changes from pi agent)
6688
6663
  GITHUB_OPERATION_PHASE="commit"
6689
- printf 'Committing changes...\n' | tee -a /results/git-push.log
6690
- if [ ! -s /results/changed-files.txt ]; then
6691
- 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
6692
6667
  GITHUB_PUSH_EXIT=7
6693
6668
  return 7
6694
6669
  fi
6695
6670
  while IFS= read -r changed_file || [ -n "$changed_file" ]; do
6696
6671
  [ -z "$changed_file" ] && continue
6697
6672
  git add -- "$changed_file" || {
6698
- 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
6699
6674
  GITHUB_PUSH_EXIT=7
6700
6675
  return 7
6701
6676
  }
6702
- done < /results/changed-files.txt
6677
+ done < ${KASEKI_RESULTS_DIR}/changed-files.txt
6703
6678
  if ! git commit -m "Kaseki: $INSTANCE_NAME"; then
6704
- 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
6705
6680
  GITHUB_PUSH_EXIT=7
6706
6681
  return 7
6707
6682
  fi
6708
6683
 
6709
6684
  # Push branch
6710
6685
  GITHUB_OPERATION_PHASE="push"
6711
- 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
6712
6687
  local askpass_file
6713
- 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
6714
6689
  return 8
6715
6690
  fi
6716
6691
  askpass_file="$GITHUB_ASKPASS_FILE"
6717
6692
 
6718
6693
  KASEKI_GITHUB_TOKEN="$token" GIT_ASKPASS="$askpass_file" GIT_TERMINAL_PROMPT=0 \
6719
- 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
6720
6695
  git_push_exit="${PIPESTATUS[0]:-1}"
6721
6696
  if [ "$git_push_exit" -eq 0 ]; then
6722
- 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
6723
6698
  else
6724
6699
  rm -f "$askpass_file"
6725
- 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
6726
6701
  GITHUB_PUSH_EXIT="$git_push_exit"
6727
6702
  return "$git_push_exit"
6728
6703
  fi
6729
6704
  rm -f "$askpass_file"
6730
6705
 
6731
6706
  if [ "$KASEKI_PUBLISH_MODE" = "branch" ]; then
6732
- 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
6733
6708
  GITHUB_PR_EXIT=0
6734
6709
  GITHUB_OPERATION_PHASE="completed"
6735
6710
  unset token
6736
6711
  return 0
6737
6712
  fi
6738
6713
  if ! is_pr_creation_mode; then
6739
- 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
6740
6715
  GITHUB_PR_EXIT=0
6741
6716
  GITHUB_OPERATION_PHASE="completed"
6742
6717
  unset token
@@ -6746,7 +6721,7 @@ run_github_operations() {
6746
6721
  # Create pull request. Both pr and draft_pr push a branch and create a PR;
6747
6722
  # only draft_pr marks the GitHub Pulls API request as draft.
6748
6723
  GITHUB_OPERATION_PHASE="pr_creation"
6749
- 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
6750
6725
  emit_progress "github operations" "pr_creation_starting"
6751
6726
  local pr_title pr_body pr_response pr_url pr_number pr_http_status pr_draft_json
6752
6727
  pr_title="$(derive_pr_title)"
@@ -6770,7 +6745,7 @@ run_github_operations() {
6770
6745
  - Generated at (UTC): $fallback_timestamp
6771
6746
  EOF
6772
6747
  )
6773
- 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
6774
6749
  fi
6775
6750
  if is_pr_draft_mode; then
6776
6751
  pr_draft_json=true
@@ -6784,7 +6759,7 @@ EOF
6784
6759
 
6785
6760
  while [ $retry_count -le "$max_retries" ]; do
6786
6761
  if [ $retry_count -gt 0 ]; then
6787
- 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
6788
6763
  emit_progress "github operations" "pr_creation_attempt $((retry_count + 1))/$max_retries"
6789
6764
  sleep "$backoff_delay"
6790
6765
  # Exponential backoff: 2s, 4s, 8s
@@ -6794,31 +6769,31 @@ EOF
6794
6769
 
6795
6770
  # Capture both response and HTTP status code
6796
6771
  local pr_response_file temp_status_file
6797
- 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; }
6798
- 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; }
6799
6774
 
6800
6775
  if [ $retry_count -eq 0 ] && [ "${KASEKI_DEBUG:-0}" = "1" ]; then
6801
- 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
6802
6777
  fi
6803
6778
 
6804
6779
  # Encode PR title and body as JSON strings
6805
6780
  local pr_title_json pr_body_json
6806
6781
  pr_title_json='""'
6807
6782
  pr_body_json='""'
6808
- if ! run_node_subprocess pr_title_json "console.log(JSON.stringify(require('fs').readFileSync(0, 'utf8')))" "$pr_title" /results/git-push.log; then
6809
- 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
6810
6785
  GITHUB_PR_EXIT=8
6811
6786
  return 8
6812
6787
  fi
6813
- if ! run_node_subprocess pr_body_json "console.log(JSON.stringify(require('fs').readFileSync(0, 'utf8')))" "$pr_body" /results/git-push.log; then
6814
- 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
6815
6790
  GITHUB_PR_EXIT=8
6816
6791
  return 8
6817
6792
  fi
6818
6793
 
6819
6794
  # Validate both variables are non-empty before using in curl
6820
6795
  if [ -z "$pr_title_json" ] || [ -z "$pr_body_json" ]; then
6821
- 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
6822
6797
  GITHUB_PR_EXIT=8
6823
6798
  return 8
6824
6799
  fi
@@ -6843,7 +6818,7 @@ EOF
6843
6818
 
6844
6819
  if [ $curl_exit -ne 0 ]; then
6845
6820
  # curl command itself failed (network error, timeout, etc.)
6846
- 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
6847
6822
  GITHUB_API_HTTP_STATUS="0"
6848
6823
  if is_github_pr_error_retryable "0" "curl_error" && [ "$retry_count" -lt "$((max_retries - 1))" ]; then
6849
6824
  retry_count=$((retry_count + 1))
@@ -6861,43 +6836,43 @@ EOF
6861
6836
  fi
6862
6837
 
6863
6838
  if [ "${KASEKI_DEBUG:-0}" = "1" ]; then
6864
- 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
6865
6840
  fi
6866
6841
 
6867
6842
  # Validate the API response
6868
- 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
6869
6844
  # API returned success (201); now extract the URL and issue number using helper
6870
- 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
6871
- 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
6872
6847
  emit_error_event "github_pr_response_malformed" "Failed to parse PR API response to extract html_url" "exit"
6873
6848
  GITHUB_PR_EXIT=9
6874
6849
  pr_url=""
6875
6850
  fi
6876
- 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
6877
- 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
6878
6853
  pr_number=""
6879
6854
  fi
6880
6855
 
6881
6856
  if [ -n "$pr_url" ]; then
6882
6857
  GITHUB_PR_URL="$pr_url"
6883
6858
  GITHUB_PR_EXIT=0
6884
- 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
6885
6860
  if [ -n "$pr_number" ]; then
6886
- 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
6887
6862
  # Request repository owner as reviewer for personal repos
6888
- 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
6889
6864
  else
6890
- 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
6891
6866
  fi
6892
6867
  pr_created=1
6893
6868
  rm -f "$pr_response_file"
6894
6869
  break
6895
6870
  else
6896
6871
  # HTTP 201 but no html_url in response - malformed response
6897
- 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
6898
6873
  emit_error_event "github_pr_response_malformed" "GitHub PR API returned 201 but response missing html_url field" "exit"
6899
6874
  if [ "${KASEKI_DEBUG:-0}" = "1" ]; then
6900
- 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
6901
6876
  fi
6902
6877
  GITHUB_PR_EXIT=9
6903
6878
  pr_created=0
@@ -6907,17 +6882,17 @@ EOF
6907
6882
  else
6908
6883
  # API returned an error
6909
6884
  if is_github_pr_error_retryable "$pr_http_status" "$GITHUB_API_ERROR_TYPE" && [ "$retry_count" -lt "$((max_retries - 1))" ]; then
6910
- 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
6911
6886
  retry_count=$((retry_count + 1))
6912
6887
  rm -f "$pr_response_file"
6913
6888
  continue
6914
6889
  else
6915
6890
  # Permanent error, give up
6916
- 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
6917
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"
6918
6893
  if [ "${KASEKI_DEBUG:-0}" = "1" ]; then
6919
- printf 'Debug: API error type: %s, HTTP status: %s\n' "$GITHUB_API_ERROR_TYPE" "$GITHUB_API_HTTP_STATUS" | tee -a /results/git-push.log
6920
- 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
6921
6896
  fi
6922
6897
  GITHUB_PR_EXIT=9
6923
6898
  pr_created=0
@@ -6950,7 +6925,7 @@ if [ "$GITHUB_APP_ENABLED" = "1" ]; then
6950
6925
  printf 'ERROR: GitHub operations preflight health check failed\n' >&2
6951
6926
  printf 'GitHub App is enabled but configuration or dependencies are missing.\n' >&2
6952
6927
  printf 'Proceeding with kaseki run, but GitHub operations will be skipped or fail.\n' >&2
6953
- 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"
6954
6929
  fi
6955
6930
  fi
6956
6931
 
@@ -6974,7 +6949,7 @@ unset OPENROUTER_API_KEY secret_content
6974
6949
  if [ -z "$openrouter_api_key" ]; then
6975
6950
  set_current_stage "agent setup"
6976
6951
  openrouter_api_key_file="${OPENROUTER_API_KEY_FILE:-/agents/secrets/openrouter_api_key}"
6977
- 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
6978
6953
  : > "$RAW_EVENTS"
6979
6954
  PI_EXIT=2
6980
6955
  STATUS=2
@@ -6985,7 +6960,7 @@ fi
6985
6960
  if ! run_clone_repository; then
6986
6961
  exit 0
6987
6962
  fi
6988
- 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"; }
6989
6964
 
6990
6965
  prepare_dependencies() {
6991
6966
  if [ ! -f package.json ]; then
@@ -7215,7 +7190,7 @@ if [ "$KASEKI_BASELINE_VALIDATION_ENABLED" = "1" ] && [ "$KASEKI_PRE_AGENT_VALID
7215
7190
  emit_progress "baseline validation cache" "failed to save (non-blocking)"
7216
7191
  fi
7217
7192
  # Cleanup baseline workspace to save space
7218
- rm -rf /workspace-baseline 2>/dev/null || true
7193
+ rm -rf ${KASEKI_WORKSPACE_BASELINE_DIR} 2>/dev/null || true
7219
7194
  else
7220
7195
  BASELINE_CACHE_STATUS="checkout_failed"
7221
7196
  emit_error_event "baseline_checkout_failed" "Failed to setup baseline for test failure comparison; continuing without baseline" "continue"
@@ -7232,13 +7207,13 @@ if [ "$KASEKI_PRE_AGENT_VALIDATION" = "0" ]; then
7232
7207
  printf '\n==> pre-agent validation\n'
7233
7208
  set_current_stage "pre-agent validation"
7234
7209
  emit_progress "pre-agent validation" "skipped by KASEKI_PRE_AGENT_VALIDATION=0"
7235
- 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
7236
7211
  record_stage_timing "pre-agent validation" 0 0 "skipped_by_config"
7237
7212
  else
7238
7213
  run_validation_commands \
7239
7214
  "pre-agent validation" \
7240
7215
  "$KASEKI_PRE_AGENT_VALIDATION_COMMANDS" \
7241
- /results/pre-validation.log \
7216
+ ${KASEKI_RESULTS_DIR}/pre-validation.log \
7242
7217
  "$PRE_VALIDATION_RAW_LOG" \
7243
7218
  "$PRE_VALIDATION_TIMINGS_FILE" \
7244
7219
  "$PRE_VALIDATION_ENV_LOG" \
@@ -7267,7 +7242,7 @@ set_current_stage "typescript precheck"
7267
7242
  if ! run_typescript_precheck; then
7268
7243
  if [ "$KASEKI_SCOUTING" = "1" ]; then
7269
7244
  # If scouting is enabled (experimental path), continue anyway with warning
7270
- 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
7271
7246
  else
7272
7247
  # Without scouting, TypeScript failures are fatal
7273
7248
  STATUS="$TS_PRE_CHECK_EXIT"
@@ -7283,16 +7258,16 @@ printf 'Pi version: %s\n' "$PI_VERSION"
7283
7258
  # === Phase 1: Early Filesystem Diagnostics (Before Scouting) ===
7284
7259
  # Detects read-only filesystem constraints that would cause silent scouting failures
7285
7260
  check_filesystem_capabilities() {
7286
- local results_dir="${KASEKI_RESULTS_DIR:-/results}"
7261
+ local results_dir="${KASEKI_RESULTS_DIR:-${KASEKI_RESULTS_DIR}}"
7287
7262
  local filesystem_writable=true
7288
7263
  local readonly_reason=""
7289
7264
 
7290
7265
  emit_progress "filesystem capabilities check" "verifying write capabilities for artifacts"
7291
7266
 
7292
- # Test /results/ writability
7267
+ # Test ${KASEKI_RESULTS_DIR}/ writability
7293
7268
  if [ ! -w "$results_dir" ]; then
7294
7269
  filesystem_writable=false
7295
- 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)"
7296
7271
  emit_error_event "readonly_filesystem_detected" "$readonly_reason" "continue"
7297
7272
  {
7298
7273
  printf '\n[FILESYSTEM DIAGNOSTIC] READ-ONLY FILESYSTEM DETECTED\n'
@@ -7302,35 +7277,35 @@ check_filesystem_capabilities() {
7302
7277
  printf ' - Container UID: %d\n' "$(id -u)"
7303
7278
  printf ' - Expected reason: Docker mounted with :ro flag or container --read-only\n'
7304
7279
  printf '\nImpact:\n'
7305
- 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'
7306
7281
  printf ' - Validation logs and artifacts cannot be written\n'
7307
7282
  printf ' - This causes exit code 86 (scouting validation failure)\n'
7308
- printf '\nFix: Remount /results as read-write\n'
7309
- 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'
7310
7285
  printf 'Or remove --read-only flag from Docker run command\n'
7311
- } | tee -a /results/scouting-stderr.log
7286
+ } | tee -a ${KASEKI_RESULTS_DIR}/scouting-stderr.log
7312
7287
  else
7313
7288
  # Test actual write capability
7314
7289
  local test_file="$results_dir/.kaseki-fs-test-$$"
7315
7290
  if ! touch "$test_file" 2>/dev/null; then
7316
7291
  filesystem_writable=false
7317
- 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)"
7318
7293
  emit_error_event "filesystem_write_test_failed" "$readonly_reason" "continue"
7319
7294
  else
7320
7295
  rm -f "$test_file" 2>/dev/null || true
7321
- emit_progress "filesystem capabilities check" "✓ /results is writable"
7296
+ emit_progress "filesystem capabilities check" "✓ ${KASEKI_RESULTS_DIR} is writable"
7322
7297
  fi
7323
7298
  fi
7324
7299
 
7325
7300
  # Record in metadata for post-mortem analysis
7326
- printf '%s\n' "$filesystem_writable" > /results/filesystem-writable-at-start.txt
7327
- [ -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
7328
7303
 
7329
7304
  if [ "$filesystem_writable" = "false" ]; then
7330
7305
  if [ "$KASEKI_BASELINE_VALIDATION_ENABLED" = "1" ]; then
7331
7306
  emit_progress "baseline validation preparation" "DISABLED due to read-only filesystem detected"
7332
7307
  KASEKI_BASELINE_VALIDATION_ENABLED="0"
7333
- 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
7334
7309
  fi
7335
7310
  return 1
7336
7311
  fi
@@ -7380,7 +7355,7 @@ if [ "$KASEKI_SCOUTING" = "1" ] && [ -f "$SCOUTING_ARTIFACT" ]; then
7380
7355
  export KASEKI_VALIDATION_ALLOWLIST="$merged_validation_allowlist"
7381
7356
 
7382
7357
  # Log merge decisions with structured JSON construction so pattern text is escaped safely.
7383
- append_jsonl_object /results/metadata.jsonl \
7358
+ append_jsonl_object ${KASEKI_RESULTS_DIR}/metadata.jsonl \
7384
7359
  "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
7385
7360
  "event=allowlist_merge" \
7386
7361
  "scouting_agent_patterns=$scouting_agent_patterns" \
@@ -7393,14 +7368,14 @@ if [ "$KASEKI_SCOUTING" = "1" ] && [ -f "$SCOUTING_ARTIFACT" ]; then
7393
7368
  allowlist_merge_status="merged"
7394
7369
 
7395
7370
  # Run coverage validation with dry-run
7396
- if [ -s /results/changed-files.txt ]; then
7397
- 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
7398
7373
  fi
7399
7374
 
7400
7375
  emit_progress "derive allowlist from scouting" "finished (status=$allowlist_merge_status)"
7401
7376
  else
7402
7377
  # Pattern validation failed - fail fast
7403
- 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
7404
7379
  STATUS=86
7405
7380
  FAILED_COMMAND="allowlist pattern validation"
7406
7381
  emit_error_event "scouting_allowlist_invalid" "Derived allowlist patterns failed validation" "exit"
@@ -7408,7 +7383,7 @@ if [ "$KASEKI_SCOUTING" = "1" ] && [ -f "$SCOUTING_ARTIFACT" ]; then
7408
7383
  fi
7409
7384
  else
7410
7385
  # Derivation failed - log and fail fast
7411
- 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
7412
7387
  STATUS=86
7413
7388
  FAILED_COMMAND="allowlist derivation from scouting"
7414
7389
  emit_error_event "scouting_allowlist_derivation_failed" "Failed to derive allowlist from scouting artifact" "exit"
@@ -7446,7 +7421,7 @@ if [ "$KASEKI_DRY_RUN" = "1" ]; then
7446
7421
  printf ' Model: %s\n' "$KASEKI_MODEL"
7447
7422
  printf ' Timeout: %s seconds\n' "$KASEKI_AGENT_TIMEOUT_SECONDS"
7448
7423
  printf ' Task: %s\n' "$TASK_PROMPT"
7449
- } | tee -a /results/pi-stderr.log
7424
+ } | tee -a ${KASEKI_RESULTS_DIR}/pi-stderr.log
7450
7425
  emit_progress "pi coding agent" "skipped (dry-run)"
7451
7426
  record_stage_timing "pi coding agent" "0" "$PI_DURATION_SECONDS" "dry_run=true"
7452
7427
  else
@@ -7458,9 +7433,9 @@ else
7458
7433
  OPENROUTER_API_KEY="$openrouter_api_key" \
7459
7434
  timeout --signal=SIGTERM "$KASEKI_AGENT_TIMEOUT_SECONDS" \
7460
7435
  pi --mode json --no-session --provider "$KASEKI_PROVIDER" --model "$KASEKI_MODEL" "$agent_prompt" \
7461
- 2> >(tee -a /results/pi-stderr.log >&2) \
7436
+ 2> >(tee -a ${KASEKI_RESULTS_DIR}/pi-stderr.log >&2) \
7462
7437
  | tee "$RAW_EVENTS" \
7463
- | 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
7464
7439
  PI_EXIT="${PIPESTATUS[0]}"
7465
7440
  unset agent_prompt
7466
7441
  PI_DURATION_SECONDS=$(($(date +%s) - PI_START_EPOCH))
@@ -7469,7 +7444,7 @@ else
7469
7444
  record_stage_timing "pi coding agent" "$PI_EXIT" "$PI_DURATION_SECONDS" "timeout_seconds=$KASEKI_AGENT_TIMEOUT_SECONDS"
7470
7445
 
7471
7446
  if [ "$KASEKI_DEBUG_RAW_EVENTS" = "1" ]; then
7472
- cp "$RAW_EVENTS" /results/pi-events.raw.jsonl
7447
+ cp "$RAW_EVENTS" ${KASEKI_RESULTS_DIR}/pi-events.raw.jsonl
7473
7448
  fi
7474
7449
 
7475
7450
  PI_EXTRACTION_DEPS_OK=1
@@ -7480,7 +7455,7 @@ else
7480
7455
  missing_executables+=("$required_exec")
7481
7456
  fi
7482
7457
  done
7483
- 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
7484
7459
  if [ ! -f "$helper_file" ]; then
7485
7460
  missing_helpers+=("$helper_file")
7486
7461
  fi
@@ -7493,34 +7468,34 @@ else
7493
7468
  [ -z "$missing_helpers_joined" ] && missing_helpers_joined="none"
7494
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")
7495
7470
  printf '%s
7496
- ' "$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
7497
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"
7498
7473
  if [ "$STATUS" -eq 0 ]; then
7499
7474
  STATUS=87
7500
7475
  FAILED_COMMAND="pi artifact extraction dependency validation"
7501
7476
  fi
7502
- 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
7503
7478
  fi
7504
7479
 
7505
7480
  FILTER_EXIT=0
7506
7481
  if [ "$PI_EXTRACTION_DEPS_OK" -eq 1 ]; then
7507
7482
  set +e
7508
- 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
7509
7484
  FILTER_EXIT=$?
7510
7485
  set +e
7511
7486
  fi
7512
7487
  if [ "$FILTER_EXIT" -ne 0 ]; then
7513
- printf 'pi-event-filter failed with exit %s; raw events preserved as fallback artifact\n' "$FILTER_EXIT" | tee -a /results/quality.log
7514
- 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
7515
7490
  emit_error_event "pi_event_filter_failed" "kaseki-pi-event-filter exited with code $FILTER_EXIT" "continue"
7516
7491
  if [ "$STATUS" -eq 0 ]; then
7517
7492
  STATUS="$FILTER_EXIT"
7518
7493
  FAILED_COMMAND="kaseki-pi-event-filter"
7519
7494
  fi
7520
- 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
7521
7496
  fi
7522
- if [ -s "$RAW_EVENTS" ] && { [ ! -s /results/pi-events.jsonl ] || [ ! -s /results/pi-summary.json ]; }; then
7523
- 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
7524
7499
  emit_error_event "pi_event_export_incomplete" "RAW_EVENTS has data but exported artifacts are empty or missing" "continue"
7525
7500
  if [ "$STATUS" -eq 0 ]; then
7526
7501
  STATUS=86
@@ -7529,16 +7504,16 @@ else
7529
7504
  fi
7530
7505
 
7531
7506
  # Process hashline_edit events (non-fatal phase; failures don't block pipeline)
7532
- 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
7533
7508
  emit_progress "hashline validation" "started"
7534
7509
  HASHLINE_EXIT=0
7535
7510
  set +e
7536
- 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
7537
7512
  HASHLINE_EXIT=$?
7538
7513
  set +e
7539
7514
 
7540
7515
  if [ "$HASHLINE_EXIT" -ne 0 ]; then
7541
- 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
7542
7517
  emit_event "warning" "warning_type=hashline_validation_failed" "detail=hashline_edit processing exited with code $HASHLINE_EXIT"
7543
7518
  else
7544
7519
  emit_progress "hashline validation" "completed"
@@ -7569,7 +7544,7 @@ else
7569
7544
  }
7570
7545
  var m='';
7571
7546
  try{
7572
- var summary=require('/results/pi-summary.json');
7547
+ var summary=require('${KASEKI_RESULTS_DIR}/pi-summary.json');
7573
7548
  m=clean(summary.selected_model)||clean(summary.model)||fromSummaryModels(summary);
7574
7549
  }catch{}
7575
7550
  if(!m){
@@ -7595,7 +7570,7 @@ fi
7595
7570
 
7596
7571
  if [ "$KASEKI_DRY_RUN" != "1" ]; then
7597
7572
  if [ "$PI_EXIT" -eq 124 ]; then
7598
- 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
7599
7574
  if [ "$STATUS" -eq 0 ]; then
7600
7575
  STATUS=124
7601
7576
  FAILED_COMMAND="pi coding agent timeout"
@@ -7634,11 +7609,11 @@ printf '\n==> quality checks\n'
7634
7609
  set_current_stage "quality checks"
7635
7610
  emit_progress "quality checks" "started"
7636
7611
  stage_start="$(date +%s)"
7637
- diff_size="$(wc -c < /results/git.diff | tr -d ' ')"
7612
+ diff_size="$(wc -c < ${KASEKI_RESULTS_DIR}/git.diff | tr -d ' ')"
7638
7613
  if [ "$diff_size" -gt "$KASEKI_MAX_DIFF_BYTES" ]; then
7639
7614
  QUALITY_EXIT=4
7640
7615
  QUALITY_FAILURE_REASON="max_diff_bytes: $diff_size bytes exceeds limit of $KASEKI_MAX_DIFF_BYTES bytes"
7641
- 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
7642
7617
  emit_event "quality_gate_rule_evaluated" "rule=max_diff_bytes" "passed=false" "actual=$diff_size" "limit=$KASEKI_MAX_DIFF_BYTES"
7643
7618
  else
7644
7619
  emit_event "quality_gate_rule_evaluated" "rule=max_diff_bytes" "passed=true" "actual=$diff_size" "limit=$KASEKI_MAX_DIFF_BYTES"
@@ -7653,17 +7628,17 @@ if [ -n "$allowlist_regex" ]; then
7653
7628
  if ! printf '%s\n' "$changed_file" | grep -Eq "^(${allowlist_regex})$"; then
7654
7629
  QUALITY_EXIT=5
7655
7630
  QUALITY_FAILURE_REASON="allowlist_check: file '$changed_file' not in allowlist"
7656
- 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
7657
7632
  emit_event "quality_gate_rule_evaluated" "rule=allowlist_check" "passed=false" "file=$changed_file"
7658
7633
  else
7659
7634
  emit_event "quality_gate_rule_evaluated" "rule=allowlist_check" "passed=true" "file=$changed_file"
7660
7635
  fi
7661
- done < /results/changed-files.txt
7636
+ done < ${KASEKI_RESULTS_DIR}/changed-files.txt
7662
7637
  fi
7663
7638
 
7664
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
7665
7640
  format_command="npm run format:check"
7666
- printf '%s\n' "$format_command" >> /results/format-check-command.txt
7641
+ printf '%s\n' "$format_command" >> ${KASEKI_RESULTS_DIR}/format-check-command.txt
7667
7642
  fi
7668
7643
  record_stage_timing "quality checks" "$QUALITY_EXIT" "$(($(date +%s) - stage_start))" "diff_size_bytes=$diff_size"
7669
7644
 
@@ -7672,7 +7647,7 @@ run_expectation_mismatch_detector
7672
7647
 
7673
7648
  pre_validation_goal_check_diff_hash=""
7674
7649
  if [ "$STATUS" -eq 0 ] && [ "$PI_EXIT" -eq 0 ] && [ "$QUALITY_EXIT" -eq 0 ]; then
7675
- 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}')"
7676
7651
  run_goal_check "$coding_attempt"
7677
7652
  collect_goal_check_feedback "$INSTANCE_NAME"
7678
7653
  snapshot_attempt_artifacts "$coding_attempt"
@@ -7704,18 +7679,18 @@ log_validation_environment() {
7704
7679
  printf '[validation environment] PATH=%s\n' "$PATH"
7705
7680
  printf '[validation environment] NODE_OPTIONS=%s\n' "${NODE_OPTIONS:-<not set>}"
7706
7681
  printf '[validation environment] NODE_PATH=%s\n' "${NODE_PATH:-<not set>}"
7707
- printf '[validation environment] disk_space_available=%s\n' "$(df -h /results 2>/dev/null | tail -1 | awk '{print $4}' || echo '<df failed>')"
7708
- printf '[validation environment] disk_space_used=%s\n' "$(du -sh /results 2>/dev/null | cut -f1 || echo '<du failed>')"
7709
- } | 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"
7710
7685
  }
7711
7686
  log_validation_environment
7712
- collect_changed_file_state /results/validation-before-state.txt
7687
+ collect_changed_file_state ${KASEKI_RESULTS_DIR}/validation-before-state.txt
7713
7688
 
7714
7689
  if [ "$KASEKI_DRY_RUN" = "1" ] || [ -z "$KASEKI_VALIDATION_COMMANDS" ] || [ "$KASEKI_VALIDATION_COMMANDS" = "none" ]; then
7715
7690
  run_validation_commands \
7716
7691
  "validation" \
7717
7692
  "$KASEKI_VALIDATION_COMMANDS" \
7718
- /results/validation.log \
7693
+ ${KASEKI_RESULTS_DIR}/validation.log \
7719
7694
  "$VALIDATION_RAW_LOG" \
7720
7695
  "$VALIDATION_TIMINGS_FILE" \
7721
7696
  "$VALIDATION_ENV_LOG" \
@@ -7724,7 +7699,7 @@ elif [ "$QUALITY_EXIT" -ne 0 ]; then
7724
7699
  printf '\n==> validation\n'
7725
7700
  set_current_stage "validation"
7726
7701
  emit_progress "validation" "started"
7727
- 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
7728
7703
  VALIDATION_EXIT="$QUALITY_EXIT"
7729
7704
  if [ -z "$VALIDATION_FAILURE_REASON" ]; then
7730
7705
  VALIDATION_FAILURE_REASON="quality_gate_failed: $QUALITY_FAILURE_REASON"
@@ -7735,14 +7710,14 @@ elif [ "$PI_EXIT" -ne 0 ] && [ "$KASEKI_VALIDATE_AFTER_AGENT_FAILURE" != "1" ];
7735
7710
  printf '\n==> validation\n'
7736
7711
  set_current_stage "validation"
7737
7712
  emit_progress "validation" "started"
7738
- 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
7739
7714
  record_stage_timing "validation" "$PI_EXIT" 0 "skipped_after_agent_failure"
7740
7715
  emit_progress "validation" "finished with exit $VALIDATION_EXIT"
7741
7716
  else
7742
7717
  run_validation_commands \
7743
7718
  "validation" \
7744
7719
  "$KASEKI_VALIDATION_COMMANDS" \
7745
- /results/validation.log \
7720
+ ${KASEKI_RESULTS_DIR}/validation.log \
7746
7721
  "$VALIDATION_RAW_LOG" \
7747
7722
  "$VALIDATION_TIMINGS_FILE" \
7748
7723
  "$VALIDATION_ENV_LOG" \
@@ -7756,18 +7731,18 @@ fi
7756
7731
 
7757
7732
  # Check validation-phase allowlist (if configured)
7758
7733
  if [ "$VALIDATION_EXIT" -eq 0 ]; then
7759
- collect_changed_file_state /results/validation-after-state.txt
7734
+ collect_changed_file_state ${KASEKI_RESULTS_DIR}/validation-after-state.txt
7760
7735
  collect_git_artifacts
7761
7736
  if ! check_validation_allowlist; then
7762
7737
  : # Exit code already set in check_validation_allowlist
7763
7738
  fi
7764
7739
  fi
7765
7740
 
7766
- 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}')"
7767
7742
  if [ "$STATUS" -eq 0 ] && [ "$PI_EXIT" -eq 0 ] && [ "$QUALITY_EXIT" -eq 0 ] && [ "$VALIDATION_EXIT" -eq 0 ] && \
7768
7743
  [ -n "$pre_validation_goal_check_diff_hash" ] && [ -n "$post_validation_goal_check_diff_hash" ] && \
7769
7744
  [ "$post_validation_goal_check_diff_hash" != "$pre_validation_goal_check_diff_hash" ]; then
7770
- 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
7771
7746
  emit_progress "goal check" "re-running after validation changed the final diff (attempt $coding_attempt)"
7772
7747
  run_goal_check "$coding_attempt"
7773
7748
  collect_goal_check_feedback "$INSTANCE_NAME"
@@ -7801,14 +7776,14 @@ printf '\n==> secret scan\n'
7801
7776
  set_current_stage "secret scan"
7802
7777
  emit_progress "secret scan" "started"
7803
7778
  stage_start="$(date +%s)"
7804
- : > /results/secret-scan.log
7779
+ : > ${KASEKI_RESULTS_DIR}/secret-scan.log
7805
7780
  if [ "$KASEKI_DRY_RUN" = "1" ]; then
7806
- 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
7807
7782
  SECRET_SCAN_EXIT=0
7808
7783
  record_stage_timing "secret scan" "0" "$(($(date +%s) - stage_start))" "dry_run=true"
7809
7784
  else
7810
7785
  # Run the initial scan
7811
- 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
7812
7787
  # Matches found - check against allowlist
7813
7788
  if check_secret_scan_allowlist; then
7814
7789
  # All matches are allowlisted
@@ -7861,7 +7836,7 @@ printf '\n==> github operations\n'
7861
7836
  set_current_stage "github operations"
7862
7837
  emit_progress "github operations" "started"
7863
7838
  stage_start="$(date +%s)"
7864
- : > /results/git-push.log
7839
+ : > ${KASEKI_RESULTS_DIR}/git-push.log
7865
7840
  build_github_skip_reasons
7866
7841
  if [ "${#GITHUB_SKIP_REASONS[@]}" -eq 0 ]; then
7867
7842
  github_app_id_file="$(resolve_github_secret_file "GITHUB_APP_ID_FILE" "github_app_id")"
@@ -7880,7 +7855,7 @@ if [ "${#GITHUB_SKIP_REASONS[@]}" -eq 0 ]; then
7880
7855
  else
7881
7856
  GITHUB_SKIP_REASONS+=("github_app_secrets_missing")
7882
7857
  GITHUB_OPERATION_PHASE="secrets"
7883
- 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
7884
7859
  emit_progress "github operations" "skipped: $(IFS=,; printf '%s' "${GITHUB_SKIP_REASONS[*]}")"
7885
7860
  GITHUB_PUSH_EXIT=7
7886
7861
  fi
@@ -7892,7 +7867,7 @@ else
7892
7867
  "$([ "$QUALITY_EXIT" -eq 0 ] && printf 'passed' || printf 'failed')" \
7893
7868
  "$([ "$SECRET_SCAN_EXIT" -eq 0 ] && printf 'passed' || printf 'failed')" \
7894
7869
  "$DIFF_NONEMPTY" \
7895
- "$GITHUB_APP_ENABLED" | tee -a /results/git-push.log
7870
+ "$GITHUB_APP_ENABLED" | tee -a ${KASEKI_RESULTS_DIR}/git-push.log
7896
7871
  emit_progress "github operations" "skipped: $(IFS=,; printf '%s' "${GITHUB_SKIP_REASONS[*]}")"
7897
7872
  fi
7898
7873
  if [ "$GITHUB_APP_ENABLED" = "1" ]; then