Skip to content

Commit 1217cc9

Browse files
authored
Merge pull request #667 from Fr-e-d/contrib/sync-1786462571
sync: update 5 file(s) in core/
2 parents 56abfa1 + 1f53bc6 commit 1217cc9

5 files changed

Lines changed: 192 additions & 48 deletions

File tree

.gaai/core/scripts/daemon-start.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,8 @@ do_start() {
465465
[[ -n "${GAAI_CODEX_IGNORE_USER_CONFIG:-}" ]] && tmux_env_args+=(-e "GAAI_CODEX_IGNORE_USER_CONFIG=${GAAI_CODEX_IGNORE_USER_CONFIG}")
466466
[[ -n "${GAAI_DAEMON_HOME:-}" ]] && tmux_env_args+=(-e "GAAI_DAEMON_HOME=${GAAI_DAEMON_HOME}")
467467
[[ -n "${GAAI_REPO_ROOT:-}" ]] && tmux_env_args+=(-e "GAAI_REPO_ROOT=${GAAI_REPO_ROOT}")
468+
[[ -n "${GAAI_CI_TEST_GATE_TIMEOUT_SEC:-}" ]] && tmux_env_args+=(-e "GAAI_CI_TEST_GATE_TIMEOUT_SEC=${GAAI_CI_TEST_GATE_TIMEOUT_SEC}")
469+
[[ -n "${GAAI_CI_TEST_GATE_MATERIALIZE_SEC:-}" ]] && tmux_env_args+=(-e "GAAI_CI_TEST_GATE_MATERIALIZE_SEC=${GAAI_CI_TEST_GATE_MATERIALIZE_SEC}")
468470
tmux new-session -d -s gaai-daemon ${tmux_env_args[@]+"${tmux_env_args[@]}"} "$daemon_cmd"
469471

470472
# Give it a moment to start, then grab the PID

.gaai/core/scripts/lib/test-gate.sh

Lines changed: 99 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,27 @@
4949
# _test_gate_ci_scope_touched, _test_gate_poll_ci_verdict.
5050
#
5151
# CI-delegated merge gate env vars (all optional, have defaults):
52-
# GAAI_CI_TEST_GATE_TIMEOUT_SEC — bounded poll wait (default: 1200,
53-
# same order of magnitude as
54-
# GAAI_AGENT_HANG_THRESHOLD_SEC)
52+
# GAAI_CI_TEST_GATE_TIMEOUT_SEC — bounded poll wait (default: 2700 =
53+
# 45 min, derived from the CI
54+
# workflow's own declared job
55+
# timeouts along its critical path:
56+
# detect -> max(head, baseline) ->
57+
# test-gate. Covers the workflow's
58+
# full declared execution budget;
59+
# queue latency is absorbed on top
60+
# of it by this same bounded wait)
61+
# GAAI_CI_TEST_GATE_MATERIALIZE_SEC — bounded sub-wait for a workflow run
62+
# to first appear for the expected
63+
# SHA (default: 300 = 5 min). If no
64+
# run has been observed within this
65+
# sub-budget, the poll returns
66+
# "unavailable:no_run" immediately
67+
# instead of waiting out the full
68+
# budget — distinguishes "CI will
69+
# never run" from "CI is still
70+
# running". Once a run has been
71+
# observed in any state, the full
72+
# budget governs.
5573
# GAAI_CI_TEST_GATE_POLL_INTERVAL_SEC — sleep between polls (default: 20;
5674
# keeps wrapper.log fresh so the
5775
# hang-detector never fires on a
@@ -567,26 +585,40 @@ _test_gate_run_with_timeout() {
567585
# also sends that SHA to the REST merge endpoint as its atomic TOCTOU backstop.
568586
#
569587
# Bounded and sleep-based — never busy-waits. Prints exactly one line to
570-
# STDOUT: "pass", "fail", "blocked:head_moved", or
571-
# "unavailable:<reason>" (reason in timeout|api_error). Progress logging goes
572-
# to stderr because stdout is the signal captured by the caller. No scheduler
573-
# or notification side effects occur here.
588+
# STDOUT: "pass", "fail", "blocked:head_moved", or "unavailable:<reason>"
589+
# (reason in timeout|api_error|no_run — no_run means the materialize
590+
# sub-budget expired before any workflow run was ever observed for the
591+
# expected SHA; timeout means a run WAS observed but no decisive verdict
592+
# arrived before the full budget expired). Progress logging goes to stderr
593+
# because stdout is the signal captured by the caller. No scheduler or
594+
# notification side effects occur here.
574595
_test_gate_poll_ci_verdict() {
575596
local story_id="$1" pr_url="$2" expected_head_sha="$3"
576-
local timeout_sec poll_interval api_timeout_sec
577-
timeout_sec=$(_test_gate_positive_int "${GAAI_CI_TEST_GATE_TIMEOUT_SEC:-1200}" 1200 GAAI_CI_TEST_GATE_TIMEOUT_SEC)
597+
local timeout_sec materialize_sec poll_interval api_timeout_sec
598+
# Default derived from the CI workflow's own declared job timeouts along
599+
# its critical path (detect -> max(head, baseline) -> test-gate) — see the
600+
# file header for the full derivation. Not a wall-clock sample: it stays
601+
# correct independent of runner performance drift.
602+
timeout_sec=$(_test_gate_positive_int "${GAAI_CI_TEST_GATE_TIMEOUT_SEC:-2700}" 2700 GAAI_CI_TEST_GATE_TIMEOUT_SEC)
603+
materialize_sec=$(_test_gate_positive_int "${GAAI_CI_TEST_GATE_MATERIALIZE_SEC:-300}" 300 GAAI_CI_TEST_GATE_MATERIALIZE_SEC)
578604
poll_interval=$(_test_gate_positive_int "${GAAI_CI_TEST_GATE_POLL_INTERVAL_SEC:-20}" 20 GAAI_CI_TEST_GATE_POLL_INTERVAL_SEC)
579605
api_timeout_sec=$(_test_gate_positive_int "${GAAI_CI_TEST_GATE_API_TIMEOUT_SEC:-30}" 30 GAAI_CI_TEST_GATE_API_TIMEOUT_SEC)
580606

581-
local started_at deadline now remaining call_timeout sleep_for
607+
local started_at deadline materialize_deadline now remaining call_timeout sleep_for
582608
started_at=$(date +%s)
583609
deadline=$(( started_at + timeout_sec ))
610+
materialize_deadline=$(( started_at + materialize_sec ))
611+
local run_observed=0
584612
local api_err_streak=0
585613
local jq_expr='if (.workflow_runs | length) == 0 then "missing" else (.workflow_runs | sort_by(.created_at) | last | [(.status // "unknown"), (.conclusion // "pending"), (.head_sha // "missing")] | @tsv) end'
586614

587615
while :; do
588616
now=$(date +%s)
589617
(( now >= deadline )) && break
618+
if [[ "$run_observed" -eq 0 && "$now" -ge "$materialize_deadline" ]]; then
619+
echo "unavailable:no_run"
620+
return 0
621+
fi
590622
remaining=$(( deadline - now ))
591623
call_timeout="$api_timeout_sec"
592624
(( call_timeout > remaining )) && call_timeout="$remaining"
@@ -604,46 +636,52 @@ _test_gate_poll_ci_verdict() {
604636
IFS=$'\t' read -r run_status run_conclusion observed_head_sha <<< "$run_line"
605637
if [[ "$observed_head_sha" != "$expected_head_sha" ]]; then
606638
api_err_streak=$(( api_err_streak + 1 ))
607-
elif [[ "$run_status" == "completed" ]]; then
608-
# A completed blocking conclusion for the exact pushed SHA is already
609-
# decisive. Do not let an unrelated PR-head lookup outage downgrade a
610-
# known CI failure into the local fallback path.
611-
if [[ "$run_conclusion" != "success" && "$run_conclusion" != "cancelled" ]]; then
612-
echo "fail"
613-
return 0
614-
fi
615-
616-
# The workflow lookup may have consumed most of this iteration's
617-
# budget. Recompute the remaining wall clock before the second call.
618-
# Both success and cancellation need this check: success may authorize
619-
# only the current head, while cancellation caused by a superseding
620-
# push must report the moved head rather than masquerading as failure.
621-
now=$(date +%s)
622-
(( now >= deadline )) && break
623-
remaining=$(( deadline - now ))
624-
local head_call_timeout="$api_timeout_sec"
625-
(( head_call_timeout > remaining )) && head_call_timeout="$remaining"
626-
627-
local current_head_sha head_rc=0
628-
current_head_sha=$(_test_gate_run_with_timeout "$head_call_timeout" gh pr view "$pr_url" \
629-
--json headRefOid --jq .headRefOid) || head_rc=$?
630-
if [[ "$head_rc" -ne 0 || ! "$current_head_sha" =~ ^[0-9a-fA-F]{40}$ ]]; then
631-
api_err_streak=$(( api_err_streak + 1 ))
632-
elif [[ "$current_head_sha" != "$expected_head_sha" ]]; then
633-
echo "blocked:head_moved"
634-
return 0
635-
elif [[ "$run_conclusion" == "success" ]]; then
636-
echo "pass"
637-
return 0
639+
else
640+
# A run for the expected SHA has now been seen in SOME state — this
641+
# is "materialized" regardless of whether it is queued, in progress,
642+
# or already completed. From here on the full budget governs.
643+
run_observed=1
644+
if [[ "$run_status" == "completed" ]]; then
645+
# A completed blocking conclusion for the exact pushed SHA is already
646+
# decisive. Do not let an unrelated PR-head lookup outage downgrade a
647+
# known CI failure into the local fallback path.
648+
if [[ "$run_conclusion" != "success" && "$run_conclusion" != "cancelled" ]]; then
649+
echo "fail"
650+
return 0
651+
fi
652+
653+
# The workflow lookup may have consumed most of this iteration's
654+
# budget. Recompute the remaining wall clock before the second call.
655+
# Both success and cancellation need this check: success may authorize
656+
# only the current head, while cancellation caused by a superseding
657+
# push must report the moved head rather than masquerading as failure.
658+
now=$(date +%s)
659+
(( now >= deadline )) && break
660+
remaining=$(( deadline - now ))
661+
local head_call_timeout="$api_timeout_sec"
662+
(( head_call_timeout > remaining )) && head_call_timeout="$remaining"
663+
664+
local current_head_sha head_rc=0
665+
current_head_sha=$(_test_gate_run_with_timeout "$head_call_timeout" gh pr view "$pr_url" \
666+
--json headRefOid --jq .headRefOid) || head_rc=$?
667+
if [[ "$head_rc" -ne 0 || ! "$current_head_sha" =~ ^[0-9a-fA-F]{40}$ ]]; then
668+
api_err_streak=$(( api_err_streak + 1 ))
669+
elif [[ "$current_head_sha" != "$expected_head_sha" ]]; then
670+
echo "blocked:head_moved"
671+
return 0
672+
elif [[ "$run_conclusion" == "success" ]]; then
673+
echo "pass"
674+
return 0
675+
else
676+
# A same-head cancelled run is non-decisive: a manual re-run may
677+
# supersede it. Keep polling until a decisive run appears or the
678+
# bounded wait falls back locally.
679+
api_err_streak=0
680+
fi
638681
else
639-
# A same-head cancelled run is non-decisive: a manual re-run may
640-
# supersede it. Keep polling until a decisive run appears or the
641-
# bounded wait falls back locally.
682+
# queued/in_progress/waiting/requested/pending are valid observations.
642683
api_err_streak=0
643684
fi
644-
else
645-
# queued/in_progress/waiting/requested/pending are valid observations.
646-
api_err_streak=0
647685
fi
648686
else
649687
api_err_streak=$(( api_err_streak + 1 ))
@@ -656,9 +694,17 @@ _test_gate_poll_ci_verdict() {
656694

657695
now=$(date +%s)
658696
(( now >= deadline )) && break
697+
if [[ "$run_observed" -eq 0 && "$now" -ge "$materialize_deadline" ]]; then
698+
echo "unavailable:no_run"
699+
return 0
700+
fi
659701
remaining=$(( deadline - now ))
660702
sleep_for="$poll_interval"
661703
(( sleep_for > remaining )) && sleep_for="$remaining"
704+
if [[ "$run_observed" -eq 0 ]]; then
705+
local materialize_remaining=$(( materialize_deadline - now ))
706+
(( sleep_for > materialize_remaining )) && sleep_for="$materialize_remaining"
707+
fi
662708
echo "[TEST-GATE-CI] ${story_id} : waiting on ${pr_url}@${expected_head_sha} (elapsed=$(( now - started_at ))s/${timeout_sec}s)" >&2
663709
sleep "$sleep_for"
664710
done
@@ -718,7 +764,12 @@ _run_merge_test_gate() {
718764
;;
719765
*)
720766
reason="${verdict#unavailable:}"
721-
echo "[WARN] ${story_id} handle_commit_phase: CI test-gate result unavailable (${reason}) — falling back to local gate [class=TEST_GATE_CI_UNAVAILABLE_FALLBACK]"
767+
local reason_detail=""
768+
case "$reason" in
769+
timeout) reason_detail=" — CI run observed but still in progress at cutoff" ;;
770+
no_run) reason_detail=" — no CI run observed for this SHA" ;;
771+
esac
772+
echo "[WARN] ${story_id} handle_commit_phase: CI test-gate result unavailable (${reason})${reason_detail} — falling back to local gate [class=TEST_GATE_CI_UNAVAILABLE_FALLBACK]"
722773
_run_deterministic_test_gate "$story_id" "$worktree_path" "$qa_report_path"
723774
return $?
724775
;;

.gaai/core/scripts/tests/ci-merge-test-gate.test.sh

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@
4040
# lookup is unavailable; it can never be downgraded to local fallback.
4141
# T11/T12 (cancellation): same-head cancellation is non-decisive and falls back
4242
# on timeout, while cancellation caused by a superseding head blocks as moved.
43+
# T13 (materialize sub-budget + default, AC1+AC5+AC6): GAAI_CI_TEST_GATE_TIMEOUT_SEC
44+
# left unset (exercises the 2700s default) with a permanently-"missing" run —
45+
# the much shorter materialize sub-budget fires first, returning
46+
# "unavailable:no_run" well inside it rather than waiting out the full budget.
47+
# T14 (timeout classification, AC3): a run observed in_progress on every poll —
48+
# materialize sub-budget is satisfied immediately, the full (short, pinned)
49+
# budget then expires and reports "unavailable:timeout" with the
50+
# still-in-progress detail, distinct from T13's no_run detail.
4351
#
4452
# Run: bash .gaai/core/scripts/tests/ci-merge-test-gate.test.sh
4553
# Exit 0 = all pass.
@@ -206,9 +214,11 @@ OUT_T2="$SANDBOX/T2.out.log"
206214
: > "$GH_CALL_LOG"
207215
set_gh_run_lines "completed"$'\t'"success"$'\t'"$HEAD_T2"
208216
export GH_PR_HEAD_SHA="$HEAD_T2"
217+
export GAAI_CI_TEST_GATE_TIMEOUT_SEC=5
209218
unset GH_API_EXIT GH_API_HANG_SEC
210219
_run_merge_test_gate "T2" "$REPO_T2" "$QA_T2" "$PR_URL" "$HEAD_T2" > "$OUT_T2" 2>&1
211220
RC_T2=$?
221+
unset GAAI_CI_TEST_GATE_TIMEOUT_SEC
212222

213223
[[ "$RC_T2" -eq 0 ]] && pass "T2: returns 0 (CI pass)" || fail "T2: expected rc=0, got ${RC_T2}"
214224
grep -q "gh api.*head_sha=${HEAD_T2}" "$GH_CALL_LOG" \
@@ -235,9 +245,11 @@ OUT_T3="$SANDBOX/T3.out.log"
235245
: > "$NOTIFY_CALLS_LOG"
236246
set_gh_run_lines "completed"$'\t'"failure"$'\t'"$HEAD_T3"
237247
export GH_PR_HEAD_SHA="$HEAD_T3"
248+
export GAAI_CI_TEST_GATE_TIMEOUT_SEC=5
238249
unset GH_API_EXIT GH_API_HANG_SEC
239250
_run_merge_test_gate "T3" "$REPO_T3" "$QA_T3" "$PR_URL" "$HEAD_T3" > "$OUT_T3" 2>&1
240251
RC_T3=$?
252+
unset GAAI_CI_TEST_GATE_TIMEOUT_SEC
241253

242254
[[ "$RC_T3" -eq 1 ]] && pass "T3: returns 1 (CI fail blocks)" || fail "T3: expected rc=1, got ${RC_T3}"
243255
grep -q -- "--set-phase-status T3 failed" "$SCHEDULER_CALLS_LOG" \
@@ -357,10 +369,12 @@ OUT_T7="$SANDBOX/T7.out.log"
357369
: > "$NOTIFY_CALLS_LOG"
358370
set_gh_run_lines "completed"$'\t'"success"$'\t'"$HEAD_T7"
359371
export GH_PR_HEAD_SHA="$(printf '%040d' 1)"
372+
export GAAI_CI_TEST_GATE_TIMEOUT_SEC=5
360373
unset GH_API_EXIT GH_API_HANG_SEC
361374

362375
_run_merge_test_gate "T7" "$REPO_T7" "$QA_T7" "$PR_URL" "$HEAD_T7" > "$OUT_T7" 2>&1
363376
RC_T7=$?
377+
unset GAAI_CI_TEST_GATE_TIMEOUT_SEC
364378

365379
[[ "$RC_T7" -eq 1 ]] && pass "T7: moved PR head blocks" || fail "T7: expected rc=1, got ${RC_T7}"
366380
grep -q "test_gate_head_moved" "$NOTIFY_CALLS_LOG" \
@@ -504,9 +518,11 @@ OUT_T12="$SANDBOX/T12.out.log"
504518
: > "$NOTIFY_CALLS_LOG"
505519
set_gh_run_lines "completed"$'\t'"cancelled"$'\t'"$HEAD_T12"
506520
export GH_PR_HEAD_SHA="$(printf '%040d' 2)"
521+
export GAAI_CI_TEST_GATE_TIMEOUT_SEC=5
507522

508523
_run_merge_test_gate "T12" "$REPO_T12" "$QA_T12" "$PR_URL" "$HEAD_T12" > "$OUT_T12" 2>&1
509524
RC_T12=$?
525+
unset GAAI_CI_TEST_GATE_TIMEOUT_SEC
510526

511527
[[ "$RC_T12" -eq 1 ]] \
512528
&& pass "T12: superseding push blocks the cancelled old head" \
@@ -517,6 +533,70 @@ grep -q "test_gate_head_moved" "$NOTIFY_CALLS_LOG" \
517533

518534
unset GH_PR_HEAD_SHA
519535

536+
# ── T13: materialize sub-budget fast-fail + unset-default resolution (AC1+AC5+AC6) ─
537+
echo "--- T13: no run ever materialises, default budget unset ---"
538+
REPO_T13="$(setup_repo T13 "workers/fake-pkg/file.txt")"
539+
HEAD_T13=$(git -C "$REPO_T13" rev-parse HEAD)
540+
QA_T13="$SANDBOX/T13.qa-report.md"
541+
OUT_T13="$SANDBOX/T13.out.log"
542+
: > "$GH_CALL_LOG"
543+
set_gh_run_lines "missing"
544+
unset GAAI_CI_TEST_GATE_TIMEOUT_SEC
545+
export GAAI_CI_TEST_GATE_MATERIALIZE_SEC=1
546+
export GAAI_CI_TEST_GATE_POLL_INTERVAL_SEC=1
547+
export GAAI_CI_TEST_GATE_API_TIMEOUT_SEC=1
548+
unset GH_API_EXIT GH_API_HANG_SEC
549+
550+
_t13_start=$(date +%s)
551+
_run_merge_test_gate "T13" "$REPO_T13" "$QA_T13" "$PR_URL" "$HEAD_T13" > "$OUT_T13" 2>&1
552+
RC_T13=$?
553+
_t13_end=$(date +%s)
554+
_t13_elapsed=$(( _t13_end - _t13_start ))
555+
556+
[[ "$RC_T13" -eq 0 ]] && pass "T13: returns 0 (local fallback PASS)" || fail "T13: expected rc=0, got ${RC_T13}"
557+
[[ "$_t13_elapsed" -lt 10 ]] \
558+
&& pass "T13: no_run resolved well inside the materialize sub-budget (${_t13_elapsed}s < 10s)" \
559+
|| fail "T13: took ${_t13_elapsed}s — materialize sub-budget did not fast-fail"
560+
grep -q "unavailable (no_run)" "$OUT_T13" \
561+
&& pass "T13: fallback reason=no_run (distinct token, AC6)" \
562+
|| fail "T13: expected reason=no_run in output"
563+
grep -q "no CI run observed for this SHA" "$OUT_T13" \
564+
&& pass "T13: no_run detail present (AC3)" \
565+
|| fail "T13: no_run detail missing from output"
566+
grep -q '/2700s)' "$OUT_T13" \
567+
&& pass "T13: progress line reflects the unset-default full budget of 2700s (AC1+AC5)" \
568+
|| fail "T13: progress line does not show the 2700s default"
569+
570+
unset GAAI_CI_TEST_GATE_MATERIALIZE_SEC GAAI_CI_TEST_GATE_POLL_INTERVAL_SEC GAAI_CI_TEST_GATE_API_TIMEOUT_SEC
571+
572+
# ── T14: run observed but never decisive → timeout classification (AC3) ─────
573+
echo "--- T14: run observed in_progress throughout, budget exhausts ---"
574+
REPO_T14="$(setup_repo T14 "workers/fake-pkg/file.txt")"
575+
HEAD_T14=$(git -C "$REPO_T14" rev-parse HEAD)
576+
QA_T14="$SANDBOX/T14.qa-report.md"
577+
OUT_T14="$SANDBOX/T14.out.log"
578+
: > "$GH_CALL_LOG"
579+
set_gh_run_lines "in_progress"$'\t'"pending"$'\t'"$HEAD_T14"
580+
export GAAI_CI_TEST_GATE_TIMEOUT_SEC=2
581+
export GAAI_CI_TEST_GATE_MATERIALIZE_SEC=1
582+
export GAAI_CI_TEST_GATE_POLL_INTERVAL_SEC=1
583+
export GAAI_CI_TEST_GATE_API_TIMEOUT_SEC=1
584+
unset GH_API_EXIT GH_API_HANG_SEC
585+
586+
_run_merge_test_gate "T14" "$REPO_T14" "$QA_T14" "$PR_URL" "$HEAD_T14" > "$OUT_T14" 2>&1
587+
RC_T14=$?
588+
589+
[[ "$RC_T14" -eq 0 ]] && pass "T14: returns 0 (local fallback PASS)" || fail "T14: expected rc=0, got ${RC_T14}"
590+
grep -q "unavailable (timeout)" "$OUT_T14" \
591+
&& pass "T14: fallback reason=timeout (verdict shape unchanged)" \
592+
|| fail "T14: expected reason=timeout in output"
593+
grep -q "CI run observed but still in progress at cutoff" "$OUT_T14" \
594+
&& pass "T14: timeout detail present (AC3's second class)" \
595+
|| fail "T14: timeout detail missing from output"
596+
597+
unset GAAI_CI_TEST_GATE_TIMEOUT_SEC GAAI_CI_TEST_GATE_MATERIALIZE_SEC \
598+
GAAI_CI_TEST_GATE_POLL_INTERVAL_SEC GAAI_CI_TEST_GATE_API_TIMEOUT_SEC
599+
520600
# ── Summary ──────────────────────────────────────────────────────────────────
521601
echo ""
522602
echo " ${PASS_COUNT} passed, ${FAIL_COUNT} failed"

.gaai/core/scripts/tests/daemon-operator-state-reporoot.test.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ if grep -qE 'tmux_env_args\+=\(-e "GAAI_REPO_ROOT=' "$START"; then
5656
else
5757
fail "TC3-2: daemon-start.sh does not forward GAAI_REPO_ROOT via tmux env"
5858
fi
59+
if grep -qE 'tmux_env_args\+=\(-e "GAAI_CI_TEST_GATE_TIMEOUT_SEC=' "$START"; then
60+
pass "TC3-3: daemon-start.sh forwards GAAI_CI_TEST_GATE_TIMEOUT_SEC via tmux env"
61+
else
62+
fail "TC3-3: daemon-start.sh does not forward GAAI_CI_TEST_GATE_TIMEOUT_SEC via tmux env"
63+
fi
64+
if grep -qE 'tmux_env_args\+=\(-e "GAAI_CI_TEST_GATE_MATERIALIZE_SEC=' "$START"; then
65+
pass "TC3-4: daemon-start.sh forwards GAAI_CI_TEST_GATE_MATERIALIZE_SEC via tmux env"
66+
else
67+
fail "TC3-4: daemon-start.sh does not forward GAAI_CI_TEST_GATE_MATERIALIZE_SEC via tmux env"
68+
fi
5969

6070
echo "=== TC4: delivery-daemon.sh operator-state paths use _STATE_PROJECT_DIR (not raw GAAI_PROJECT_DIR) ==="
6171
for var in LOG_FILE LOG_DIR LOCK_DIR; do

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
## [Unreleased]
1111

1212
### Changed
13+
-: 'The CI test-gate wait outlasts a normal CI run, so local
1314
-: Produce and validate the two-axis QA handoff
1415

1516
## [2.49.0] - 2026-08-11

0 commit comments

Comments
 (0)