Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions .gaai/core/scripts/daemon-dispatch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3164,14 +3164,23 @@ _reconcile_yaml_status_on_exit() {
git reset --hard "origin/${TARGET_BRANCH:-staging}" --quiet 2>/dev/null || true
fi

# AC3: drift check — skip commit if operator has unstaged edits on the backlog
# AC1 (E222S06): on uncommitted backlog diff, commit the accumulated drift
# instead of writing the drift-marker and bailing. Drift-marker only when the
# commit genuinely fails (rebase-conflict or commit-failure) — matching the
# chore-commit pre-mark sweep's proven behavior.
if ! git diff --quiet HEAD -- "$backlog_rel" 2>/dev/null; then
echo "[WRAPPER-RECONCILE] $story_id : working-tree drift, skipping reconcile — daemon will retry"
printf '%s|commit|wrapper-reconcile-drift-%s\n' \
"$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo unknown)" "$story_id" \
> "$drift_marker" 2>/dev/null || true
rm -f "$_rip_marker" 2>/dev/null || true
return 0
local _drift_rc=0
_commit_accumulated_backlog_drift "$story_id" "$backlog_rel" "${TARGET_BRANCH:-staging}" \
"wrapper-reconcile" || _drift_rc=$?
if [[ "$_drift_rc" -ne 0 ]]; then
echo "[WRAPPER-RECONCILE] $story_id : drift commit failed — writing marker for operator attention"
printf '%s|commit|wrapper-reconcile-drift-%s\n' \
"$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo unknown)" "$story_id" \
> "$drift_marker" 2>/dev/null || true
rm -f "$_rip_marker" 2>/dev/null || true
return 0
fi
# Drift committed — fall through to apply YAML status update
fi

# Update YAML status field
Expand Down
17 changes: 14 additions & 3 deletions .gaai/core/scripts/delivery-daemon.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1359,9 +1359,20 @@ except Exception:
log "${YELLOW}[RECOVERY] $sid : merged-PR reconcile failed — falling through to drift-skip${NC}"
fi
fi
log "${YELLOW}[RECOVERY] $sid : working-tree drift (HEAD=in_progress/${ps:-empty}, WT=${wt_status:-?}/${wt_ps:-?}) — skipping relaunch this scan${NC}"
_write_drift_marker "scan" "drift-$sid"
drift_detected=1
# AC2 (E222S06): commit accumulated backlog drift instead of skip.
# Drift-marker only when the commit genuinely fails, matching the
# chore-commit pre-mark sweep's proven behavior.
local _drift_rc=0
_commit_accumulated_backlog_drift "$sid" "$BACKLOG_REL" "${TARGET_BRANCH:-staging}" \
"recovery-scan" || _drift_rc=$?
if [[ "$_drift_rc" -ne 0 ]]; then
log "${YELLOW}[RECOVERY] $sid : working-tree drift (HEAD=in_progress/${ps:-empty}, WT=${wt_status:-?}/${wt_ps:-?}) — drift commit failed, writing marker for operator attention${NC}"
_write_drift_marker "scan" "drift-$sid"
drift_detected=1
else
log "${GREEN}[RECOVERY] $sid : accumulated backlog drift committed — will relaunch on next scan${NC}"
_clear_drift_marker_if_clean
fi
continue
fi
fi
Expand Down
80 changes: 50 additions & 30 deletions .gaai/core/scripts/lib/chore-commit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,50 @@ _CHORE_HELPER_AVAILABLE=0
# _CHORE_HELPER_AVAILABLE=1
# fi

# _commit_accumulated_backlog_drift — shared helper for the pre-mark sweep,
# the wrapper-reconcile hook, and the recovery-scan drift path.
# All three paths face the same situation: accumulated daemon-written
# phase_status updates sitting uncommitted on disk. The proven behavior
# (from the pre-mark sweep) is to commit+push them before proceeding,
# falling back to the drift-marker only when the commit genuinely fails.
#
# Usage : _commit_accumulated_backlog_drift story_id backlog_rel target_branch [context_label]
# Returns: 0 = nothing to commit, or committed+pushed successfully
# 6 = genuine failure (rebase-conflict or commit-failure) — caller writes drift-marker
_commit_accumulated_backlog_drift() {
local story_id="$1" backlog_rel="$2" target_branch="$3" context_label="${4:-pre-mark}"
local _gdir="${PROJECT_DIR:-.}"
if git -C "$_gdir" diff --quiet HEAD -- "$backlog_rel" 2>/dev/null; then
return 0 # clean — nothing to do
fi
if git -C "$_gdir" add "$backlog_rel" 2>/dev/null \
&& git -C "$_gdir" commit \
-m "chore(daemon): commit accumulated backlog drift [$context_label $story_id]" \
--quiet -- "$backlog_rel" 2>/dev/null; then
if ! git -C "$_gdir" push origin "$target_branch" --quiet 2>/dev/null; then
# Push race — fetch then rebase
git -C "$_gdir" fetch origin "$target_branch" --quiet 2>/dev/null || true
if git -C "$_gdir" rebase "origin/$target_branch" --quiet 2>/dev/null; then
if ! git -C "$_gdir" push origin "$target_branch" --quiet 2>/dev/null; then
git -C "$_gdir" reset --hard "origin/$target_branch" --quiet 2>/dev/null || true
echo "[DRIFT-COMMIT] $story_id : re-synced to origin after push-race [$context_label]" >&2
fi
else
git -C "$_gdir" rebase --abort 2>/dev/null || true
git -C "$_gdir" reset --hard "origin/$target_branch" --quiet 2>/dev/null || true
echo "[DRIFT-COMMIT] $story_id : genuine rebase conflict [$context_label] — operator resolve required" >&2
return 6
fi
fi
echo "[DRIFT-COMMIT] $story_id : committed accumulated backlog drift [$context_label]" >&2
return 0
else
git -C "$_gdir" reset HEAD -- "$backlog_rel" 2>/dev/null || true
echo "[DRIFT-COMMIT] $story_id : commit failed [$context_label] — refuse-skip" >&2
return 6
fi
}

# Option A fallback: refuse if drift, else scheduler-write + commit + push.
# Used when yq is unavailable OR when the yq-formatting-pinning prerequisite
# is not met (the non-fallback path is currently disabled — see comment above).
Expand Down Expand Up @@ -89,36 +133,12 @@ _chore_option_a_fallback() {
# other wrappers and needs to land on origin anyway. If THAT commit
# itself fails, fall back to the old refuse-skip (operator may have
# genuine uncommitted edits we don't want to silently absorb).
if ! git diff --quiet HEAD -- "$backlog_rel" 2>/dev/null; then
if git add "$backlog_rel" 2>/dev/null \
&& git commit -m "chore(daemon): commit accumulated wrapper-progress writes [pre-mark $story_id]" --quiet -- "$backlog_rel" 2>/dev/null; then
if ! git push origin "$target_branch" --quiet 2>/dev/null; then
# Push race — fetch then distinguish genuine conflict from just-behind (AC4).
git fetch origin "$target_branch" --quiet 2>/dev/null || true
if git rebase "origin/$target_branch" --quiet 2>/dev/null; then
# Rebase clean — retry push; if push still fails, re-sync to origin (AC2+AC3).
if ! git push origin "$target_branch" --quiet 2>/dev/null; then
git reset --hard "origin/$target_branch" --quiet 2>/dev/null || true
echo "[CHORE-COMMIT] $story_id : pre-mark drift sweep re-synced to origin — will re-apply field" >&2
fi
else
# Genuine rebase conflict (not just behind) — AC4: distinct diagnosable message.
git rebase --abort 2>/dev/null || true
git reset --hard "origin/$target_branch" --quiet 2>/dev/null || true
echo "[CHORE-COMMIT] $story_id : pre-mark drift sweep — genuine rebase conflict, operator resolve required" >&2
_chore_a_done 6
return $?
fi
fi
echo "[CHORE-COMMIT] $story_id : swept accumulated wrapper-progress drift before mark-in-progress" >&2
else
# add+commit itself failed — likely no diff content despite git diff
# signalling drift (e.g. mode-only change). Revert to old refuse-skip.
git reset HEAD -- "$backlog_rel" 2>/dev/null || true
echo "[CHORE-COMMIT] $story_id : pre-mark drift sweep commit failed — refuse-skip" >&2
_chore_a_done 6
return $?
fi
local _drift_rc=0
_commit_accumulated_backlog_drift "$story_id" "$backlog_rel" "$target_branch" "pre-mark" \
|| _drift_rc=$?
if [[ "$_drift_rc" -ne 0 ]]; then
_chore_a_done 6
return $?
fi
while [[ $# -ge 2 ]]; do
"${SCHEDULER:-}" --set-field "$story_id" "$1" "$2" "$backlog_file" 2>/dev/null || true
Expand Down
Loading
Loading