Skip to content

Instantly share code, notes, and snippets.

@soarez
Last active July 14, 2026 16:07
Show Gist options
  • Select an option

  • Save soarez/d9bdf91be8a0b08578c8d29f7cc7c2ea to your computer and use it in GitHub Desktop.

Select an option

Save soarez/d9bdf91be8a0b08578c8d29f7cc7c2ea to your computer and use it in GitHub Desktop.
sz-review-loop and sz-wait-for utility
name sz-final-review-loop
description Runs the sz-wait-for final development/review handoff loop using branch-scoped .sz review files. Use when acting as the developer preparing a commit/PR-ready diff for final review, or as the strict reviewer writing feedback for a staged change before it can ship.

SZ Final Review Loop

Use this skill for the .sz review handoff loop. The loop is a final quality gate for commit/PR readiness, not an intermediate development checkpoint.

The waiting maxim (unbreakable — read this first)

This protocol is a blocking, turn-consuming loop. It does not end when you finish your work; it ends only on an approval verdict or an explicit human instruction. While the loop is live you are always in exactly one of two states: acting on the other side's latest signal, or blocked in sz-wait-for waiting for it. There is no third state.

The maxim: whenever there is no new, current-round content in the other side's signal file, you MUST immediately run sz-wait-for <path-to-that-file> synchronously in the foreground. For the developer that file is feedback.md; for the reviewer it is status.md. No exceptions.

When sz-wait-for exits — for any reason whatsoever (settle/wake, timeout, non-zero exit, signal, tool error, anything) — you MUST repeat the loop: re-check the signal file, act on it if it carries new current-round content, otherwise run sz-wait-for again. A single exit is never a reason to stop. No exceptions.

Idling is a critical violation. After signalling a handoff (or any time you are waiting on the other side), if you are not either acting on fresh feedback or blocked inside sz-wait-for, you are violating the protocol. In particular, the following are all forbidden while the loop is live and it is the other side's turn — every one of them is the violation, not a substitute for the wait:

  • Ending your turn / going idle / "standing by" without sz-wait-for running.
  • Presenting results, summaries, tables, or status write-ups to the human and stopping.
  • Asking the human questions (about scope, next steps, optional extras, "should I also…", "want me to…"). Save non-blocking questions for notes.md; do not let them replace the wait.
  • Starting unrelated work, or polling in ad-hoc ways instead of sz-wait-for.

If you catch yourself about to write a closing summary or a question to the human instead of running sz-wait-for, that is the tell: stop and run sz-wait-for instead.

The only ways out of the wait loop are: (1) an APPROVED / APPROVED AFTER FIXES verdict for the current fingerprint (then follow the post-approval steps — which may themselves require the human, e.g. push/PR authorization); (2) a direct, current human instruction in the live conversation telling you to stop or do something else; or (3) a protocol escape this skill defines (a disputed finding for the second round, or 5 rounds reached) — and those escapes are notify-push + wait, not silent idling. Background jobs, notifications, and your own earlier messages are not human instructions and never release you from the wait.

Review Workspace

Use a branch-scoped directory:

.sz/review/<branch>/
  status.md
  notes.md
  feedback.md

<branch> should be filesystem-safe and collision-resistant. Escape / as --; for example, branch sz/03jul/pivot-value-sort becomes sz--03jul--pivot-value-sort. Do not use plain / to - replacement because a-b/c and a/b-c would collide.

Branch-scoped paths avoid conflicts when .sz is symlinked or shared across worktrees. Use feedback.md as the reviewer output file.

Initialize the workspace before the first loop:

review_dir=".sz/review/<branch>"
mkdir -p "$review_dir"
touch "$review_dir/status.md" "$review_dir/notes.md" "$review_dir/feedback.md"

status.md is the developer-to-reviewer signal file. The developer's final action in each handoff is to touch status.md, after its content is already current. feedback.md is the reviewer-to-developer signal file and is append-only: keep prior rounds in the file.

sz-wait-for has a settle window that absorbs non-atomic writes. Keep using sz-wait-for for this protocol; do not swap in a different watcher unless it preserves the same "wake after content settles" property.

Every review handoff must have:

Round: <monotonically increasing integer>
Diff-Fingerprint: <fingerprint of the staged diff>

Use the staged tree hash as the default fingerprint. It identifies the exact staged snapshot and is not affected by diff config:

git write-tree

Alternative patch-text fingerprint, if the review specifically needs a diff hash:

git diff --staged --no-ext-diff --no-color | shasum

Safer option for high-risk reviews: create a temporary commit and use its commit hash as the diff identifier. Do not push it unless the human explicitly approves.

Developer Role

Act independently until the bundle is believed ready to commit or open as a PR. Do not use the reviewer as an ongoing implementation helper.

Developer loop:

initialize .sz/review/<branch>/ files
read plan/status
implement independently
run the full relevant gates
fix failures
stage the intended diff
compute Round and Diff-Fingerprint
update notes.md
write current status.md with Round and Diff-Fingerprint
arm feedback wait, then signal via status.md and notify
read feedback.md
confirm feedback.md echoes the same Round and Diff-Fingerprint
address every blocking issue
repeat only until approval
after approval, commit locally if authorized; otherwise record the approved state and ask the human for commit/push/PR direction

Developer obligations:

  • Stage the exact diff intended for review before requesting review.
  • Keep status.md as the cold-start situation report and handoff signal: purpose, branch state, scope, risks, dependencies, gates, next action, Round, and Diff-Fingerprint.
  • Keep notes.md as the implementation log: key decisions, evidence, gate commands/results, deferred work, and human-owned choices.
  • Flag human-owned decisions in notes.md when the work surfaces a business decision, owner preference, or design choice with significant trade-offs and no clearly superior answer. Do not decide these alone.
  • If the human gives a decision to either agent, record the answer in notes.md before the next review handoff.
  • Update notes.md for the handoff, then update status.md and touch it as the final signal for every review round, including code-only changes.
  • Before waiting, check whether feedback.md already contains the current Round and Diff-Fingerprint. If yes, read it immediately instead of waiting.
  • After waking, read the latest ## Round <N> section in feedback.md and verify that it echoes the current Round and Diff-Fingerprint. If it does not, treat it as stale feedback and request or wait for re-review.
  • For APPROVED AFTER FIXES, apply the listed fixes exactly as specified and proceed without re-review. If any fix requires interpretation, deviation, or a judgment call, start a new review round instead.
  • If a finding appears wrong, rebut it in notes.md with evidence instead of silently ignoring it. If the same finding is disputed for a second round, stop and escalate to the human with notify-push <project-and-theme> "<message>".
  • Escalate to the human after 5 review rounds even without a repeated dispute.
  • After APPROVED, do not push, open a PR, or reply to GitHub comments unless already authorized. If local commit is authorized, commit the approved staged diff. If not, mark the approved state in status.md, set [DECISION REQUIRED] for commit/push/PR direction, and notify the human.
  • Put [DECISION REQUIRED] at the top of status.md for human-owned scope, design, push, PR, or release decisions.
  • Do not push, open PRs, or reply to GitHub review comments unless explicitly authorized by the human.

Arm wait, then signal. Do not notify and then wait; that can miss a fast feedback write. Then obey the waiting maxim above: the moment the handoff is signalled, your very next action is sz-wait-for — not a summary, not a question, not the end of your turn.

review_dir=".sz/review/<branch>"
# Wait synchronously in the foreground. Do NOT background sz-wait-for: a
# background process cannot return control to this agent when feedback arrives,
# and its output may be buffered, making it look like feedback.md was never
# updated.
timeout 1800 sz-wait-for "$review_dir/feedback.md"
# After waking (or on timeout, or on ANY exit), re-check feedback.md for the
# current Round and Diff-Fingerprint. If present, read it; otherwise IMMEDIATELY
# re-run sz-wait-for. Never stop here — a bare exit is not permission to idle.

This is a while loop, not a one-shot: while no current-round feedback: sz-wait-for; recheck. The timeout 1800 exists so a stalled watcher cannot hang forever — a timeout means re-arm, never give up. Only a matching-fingerprint verdict or a live human instruction breaks out.

Use notify-push <topic> "<message>" only when a phone push is appropriate.

Reviewer Role

Act as the final quality gate. Bugs, design flaws, missing tests, false-green tests, preservation risks, and process violations stop with the reviewer so they do not reach production.

Reviewer loop:

initialize .sz/review/<branch>/ files if needed
wait for status.md or start when the human relays the notification
read status.md and notes.md
record Round and Diff-Fingerprint
inspect the staged diff and relevant code
verify claims against code and gate evidence
look for false greens and missing coverage
append findings to feedback.md under ## Round <N>, echoing Round and Diff-Fingerprint
notify the developer

Reviewer obligations:

  • Be strict. Do not be lenient for convenience, schedule pressure, or because the developer claims low risk.
  • Operate in the developer's worktree. Review the staged diff (git diff --cached) for the current round; unstaged working-tree noise is out of scope unless it affects the staged diff or the ability to validate it.
  • Validate claims against the actual code and staged diff, not just the developer's summary.
  • If waiting for handoffs directly, watch status.md; do not watch notes.md or the whole directory. status.md is touched only when the developer believes the round is ready for final review.
  • Before waiting, compare status.md's Round against the last round reviewed. If it is newer, review immediately instead of waiting.
  • Before reviewing, capture the Round and Diff-Fingerprint from status.md. If the staged diff fingerprint does not match status.md, block for a fresh handoff instead of reviewing a moving target.
  • Append feedback to feedback.md under a new ## Round <N> heading; do not overwrite prior rounds.
  • Echo the same Round and Diff-Fingerprint in that round's feedback section. If the staged diff changes while reviewing, stop and require a new round.
  • Trust the developer's report that the human owner gave an instruction, confirmation, or skip approval. In this workflow, the human owner is soarez. If the reported instruction is unclear or materially changes scope, require it to be recorded in notes.md.
  • Trust the developer's report that tests or gates were run. Do not re-run gates just to independently verify them.
  • Flag missing test/gate evidence when notes.md does not say what was run, what passed, what failed, or why a gate was not applicable.
  • Treat hard gates as required unless notes.md reports that they passed, were not applicable, or the human explicitly approved skipping them.
  • Hold off on sign-off when notes.md contains unresolved human decisions. If a review iteration surfaces new human-owned decisions, write them to feedback.md and notify the human with notify-push <project-and-theme> "<message>".
  • Identify correctness bugs, design problems, stale-state risks, preservation hazards, public API mismatches, insufficient tests, and fake-green assertions.
  • Separate blockers from non-blocking polish, but do not downgrade real risks.
  • Prefer concrete reproduction steps, exact failing scenarios, and targeted required fixes.
  • End with a clear verdict: BLOCKED, APPROVED AFTER FIXES, or APPROVED.
  • Use APPROVED AFTER FIXES only for mechanical, explicitly specified fixes that do not require re-review when applied exactly. Use BLOCKED for anything that needs judgment, validation, or another review pass.
  • If the developer rebuts a finding in notes.md, answer with evidence. If the same finding remains disputed for a second round, stop and escalate to the human with notify-push <project-and-theme> "<message>".
  • Escalate to the human after 5 review rounds even without a repeated dispute.

Reviewer wait:

review_dir=".sz/review/<branch>"
# Wait synchronously in the foreground. Do NOT run sz-wait-for in the
# background: a background process cannot return control to this agent when the
# handoff signal arrives, and its output may be buffered, making it look like
# status.md was never updated.
timeout 1800 sz-wait-for "$review_dir/status.md"
# After waking (or on timeout, or on ANY exit), read status.md. If it carries a
# newer Round than last reviewed, review now; otherwise IMMEDIATELY re-run
# sz-wait-for. Never stop here.

The reviewer agent must block on sz-wait-for synchronously — the waiting maxim at the top applies symmetrically, with status.md as the signal file. Between finishing a review and the next handoff, the reviewer is either reviewing or blocked in sz-wait-for; idling, summarising to the human, or ending the turn is the same critical violation. notify alerts the human but does not wake an agent by itself. If the human relays the notification to the reviewer agent, the reviewer may start from that prompt instead of running its own wait.

Feedback Format

Append to feedback.md in this shape:

## Round <N> - <short title>

Round: <N>
Diff-Fingerprint: <same value from status.md>

### Verdict
BLOCKED | APPROVED AFTER FIXES | APPROVED

### Findings
- **F1 - <severity>: <title>**
  Evidence, impact, and required fix.

### Gates
- <gate>: developer-reported passed / failed / not run / not applicable / human-approved skip

### Human Decisions
- <decision>: pending / resolved, with the reported human answer if resolved

### Non-blocking Notes
- <optional polish or follow-up>

### Next Action
<exact next step for the developer>

Verdict meaning:

  • BLOCKED: developer must change code/docs or resolve a decision, then start a new review round.
  • APPROVED AFTER FIXES: developer may apply the listed fixes exactly as specified and proceed without re-review; any deviation, uncertainty, or judgment call starts a new review round.
  • APPROVED: no required changes.

If there are no issues, say so explicitly and list any residual risk, missing gate evidence, or pending human decision. Do not approve while human decisions are pending.

#!/usr/bin/env bash
set -euo pipefail
# Single-shot waiter: block until the watched target changes, debounce edits for
# 5 seconds, confirm the change is real, then exit 0. Meant to be re-invoked in a
# loop, not to stream continuous events.
#
# Takes one argument: the file or directory to watch. A directory fires when
# anything under it changes -- a file added, removed, or modified, at any depth.
#
# Behavior and design choices:
# - resolve the target to an absolute path so the caller's cwd does not matter
# - capture the baseline in memory at startup; only changes that happen while
# this process is running are detected (no cross-run state is kept, by design)
# - debounce bursts of edits (5 seconds of quiet after the last event)
#
# Change detection uses a fingerprint rather than a single mtime: a recursive
# walk produces a sorted "path<TAB>mtime" listing that is hashed into one value,
# so any add/remove/modify moves the hash. The walk is recursive to match the
# watcher scope (fswatch is recursive on macOS via FSEvents, and we pass -r on
# Linux), so a deep change can never wake us without also moving the fingerprint.
#
# On a clean exit it prints "Changed: <path>"; for a directory it also lists the
# entries it noticed were added, removed, or modified (capped for readability).
#
# Known limitations (deliberate, to stay simple):
# - mtime-only: a change that does not move an entry's mtime is not detected,
# even though fswatch may wake us (we treat it as a spurious event and
# re-arm). This covers metadata-only edits (chmod/chown/xattr) and two edits
# to the same file within one wall-clock second on the seconds-only stat
# fallback. Closing this would require content hashing, not worth the cost.
# - symlinks are not followed: find lists the link node and stat reports the
# link itself, so a change to a symlink's *target* does not move the
# fingerprint. Watching the link node is the intended behavior.
# - cost: the walk runs one stat (or date/stat -c) per entry, recomputed on
# each wake and once per second while settling. Fine for small trees; on
# very large/deep trees a sample may exceed the 1s settle tick.
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <file-or-directory>" >&2
exit 1
fi
TARGET="$1"
if [ ! -e "$TARGET" ]; then
echo "Error: path does not exist: $TARGET" >&2
exit 1
fi
# Resolve to an absolute path so the watch target is stable even if the caller's
# cwd changes. Directories resolve by cd'ing in; a file resolves via its parent
# so a symlinked file resolves predictably.
if [ -d "$TARGET" ]; then
ABS_TARGET="$(cd "$TARGET" && pwd)"
else
case "$TARGET" in
/*) ABS_TARGET="$TARGET" ;;
*) ABS_TARGET="$(cd "$(dirname "$TARGET")" && pwd)/$(basename "$TARGET")" ;;
esac
fi
if [ ! -e "$ABS_TARGET" ]; then
echo "Error: path does not exist: $ABS_TARGET" >&2
exit 1
fi
# Accept only a regular file or a directory. Other node types (sockets, FIFOs,
# devices) are rejected -- the fingerprint/watch logic is not meant for them.
if [ ! -d "$ABS_TARGET" ] && [ ! -f "$ABS_TARGET" ]; then
echo "Error: not a regular file or directory: $ABS_TARGET" >&2
exit 1
fi
# Build the fswatch argument list once.
# - Directory: watch it recursively (-r forces recursion on Linux/inotify; it
# is harmless on macOS, where FSEvents is already recursive).
# - File: watch the file and its parent directory so atomic editor saves
# (write temp + rename over the target, which swaps the inode) are detected.
if [ -d "$ABS_TARGET" ]; then
WATCH_ARGS=(-r "$ABS_TARGET")
# The directory listing relies on `find`. Fail loudly here rather than let a
# missing `find` collapse the walk to an empty listing (which would hash to a
# constant and make the watcher arm but never fire).
if ! command -v find >/dev/null 2>&1; then
echo "Error: find is required to watch a directory" >&2
exit 1
fi
else
WATCH_ARGS=("$ABS_TARGET" "$(dirname "$ABS_TARGET")")
fi
# Hash whatever arrives on stdin to a stable hex digest.
hash_stdin() {
if command -v shasum >/dev/null 2>&1; then
shasum | awk '{print $1}'
else
sha1sum | awk '{print $1}'
fi
}
# Normalize an mtime value to seconds.nanoseconds with exactly 9 fractional
# digits so values are stable across different stat/date outputs.
normalize_mtime() {
local mtime="$1"
if [[ "$mtime" =~ ^([0-9]+)\.([0-9]+)$ ]]; then
local secs="${BASH_REMATCH[1]}"
local frac="${BASH_REMATCH[2]}"
while [ "${#frac}" -lt 9 ]; do frac="${frac}0"; done
frac="${frac:0:9}"
printf '%s.%s' "$secs" "$frac"
else
printf '%s.000000000' "$mtime"
fi
}
# Cross-platform mtime with sub-second precision where available.
# Try the macOS/BSD high-precision stat first (avoids a failing `date` exec on
# every poll on stock macOS, where BSD `date -r` only accepts epoch seconds).
# GNU `date -r <file>` gives nanoseconds on Linux. The seconds-only GNU stat
# fallback is the last resort; rapid same-second edits may be invisible there.
# Prints the normalized mtime on stdout and returns 0; returns 1 on failure.
mtime_of() {
local f="$1"
local out
if out=$(stat -f '%Fm' "$f" 2>/dev/null); then
normalize_mtime "$out"
return 0
elif out=$(date -r "$f" +%s.%N 2>/dev/null) && [[ "$out" =~ ^[0-9]+\.[0-9]{9}$ ]]; then
printf '%s' "$out"
return 0
elif out=$(stat -c '%Y' "$f" 2>/dev/null); then
normalize_mtime "$out"
return 0
fi
return 1
}
# Emit the target's "path<TAB>mtime" listing on stdout, one line per entry:
# - File: a single line.
# - Directory: the directory itself and every descendant, sorted by path for
# determinism.
# Entries that vanish mid-walk are skipped: their absence simply changes the
# listing on the next sample, which is the correct signal. A directory always
# yields at least itself, so an *empty* listing while it still exists means the
# walk tooling failed (find/sort/stat); we fail loudly there rather than emit
# nothing, which would hide a change behind the constant empty-string hash. A
# truly vanished target also yields nothing, but the main loop's existence check
# reports that with a clear message.
listing_of() {
local target="$1"
if [ -d "$target" ]; then
local listing
# Requires `sort -z` (GNU sort and Apple's sort 2.3+ both support it). If a
# platform's sort lacks it, the pipeline yields no output and the empty
# listing is caught loudly below rather than degrading to a constant hash.
listing=$(find "$target" -print0 2>/dev/null \
| LC_ALL=C sort -z \
| while IFS= read -r -d '' entry; do
m=$(mtime_of "$entry") || continue
printf '%s\t%s\n' "$entry" "$m"
done) || true
if [ -z "$listing" ] && [ -d "$target" ]; then
echo "Error: directory walk produced no entries for $target (find/sort/stat failure?)" >&2
return 1
fi
printf '%s' "$listing"
else
local m
if m=$(mtime_of "$target"); then
printf '%s\t%s' "$target" "$m"
else
# Fail loudly rather than emit nothing and hide the change behind a hash.
echo "Error: unable to read mtime of $target" >&2
return 1
fi
fi
}
# Hash a target's listing into one digest -- cheap to compare, used only to
# detect that *something* changed.
fingerprint_of() {
listing_of "$1" | hash_stdin
}
# Summarize what changed between two listings (the "path<TAB>mtime" output of
# listing_of) to stdout, as added/removed/modified entries with paths relative
# to the watched root. A directory's own mtime bump is always a side effect of a
# child being added or removed -- already reported via that child -- so those
# "modified" lines are dropped as noise. Output is capped to stay readable.
report_changes() {
local base="$1" final="$2" root="$3"
local max=10 shown=0 total=0 status path rel
while IFS=$'\t' read -r status path; do
# Drop pure directory mtime bumps; the child add/remove is reported too.
if [ "$status" = M ] && [ -d "$path" ]; then
continue
fi
total=$((total + 1))
if [ "$shown" -lt "$max" ]; then
case "$path" in
"$root") rel="." ;;
"$root"/*) rel="${path#"$root"/}" ;;
*) rel="$path" ;;
esac
case "$status" in
A) printf ' added: %s\n' "$rel" ;;
R) printf ' removed: %s\n' "$rel" ;;
M) printf ' modified: %s\n' "$rel" ;;
esac
shown=$((shown + 1))
fi
done < <(
awk -F'\t' '
NR==FNR { bm[$1]=$2; b[$1]=1; next }
{ f[$1]=1
if (!($1 in b)) print "A\t" $1
else if (bm[$1] != $2) print "M\t" $1 }
END { for (p in b) if (!(p in f)) print "R\t" p }
' <(printf '%s\n' "$base") <(printf '%s\n' "$final") | LC_ALL=C sort
)
if [ "$total" -gt "$shown" ]; then
printf ' ... and %d more\n' "$((total - shown))"
fi
}
SETTLE=5
if ! command -v fswatch >/dev/null 2>&1; then
echo "Error: fswatch is required (https://github.com/emcrisostomo/fswatch)" >&2
exit 1
fi
# Capture the baseline in memory at startup. No state is kept across runs: only
# changes that happen while this process is running are detected. Keep the raw
# listing too so a directory change can be described, not just detected.
BASE_LISTING="$(listing_of "$ABS_TARGET")"
BASE_FP="$(printf '%s' "$BASE_LISTING" | hash_stdin)"
while :; do
CURRENT_FP="$(fingerprint_of "$ABS_TARGET")"
if [ "$CURRENT_FP" = "$BASE_FP" ]; then
# No change yet; block on a filesystem notification.
# NOTE: there is a tiny check-then-arm window between the fingerprint read
# above and fswatch starting; a change in that gap may not be noticed until
# the next event wakes us.
# NOTE: watching a directory (or a file's parent) means unrelated entries can
# wake us up; the immediate fingerprint check below keeps that cheap.
if FS_OUT=$(fswatch -1 "${WATCH_ARGS[@]}" 2>&1); then
FS_RC=0
else
FS_RC=$?
fi
if [ "$FS_RC" -ne 0 ]; then
if [ ! -e "$ABS_TARGET" ]; then
echo "Error: watched path disappeared: $ABS_TARGET" >&2
else
echo "Error: fswatch failed (exit $FS_RC): $FS_OUT" >&2
fi
exit "$FS_RC"
fi
if [ ! -e "$ABS_TARGET" ]; then
echo "Error: watched path disappeared: $ABS_TARGET" >&2
exit 1
fi
fi
# Unrelated events can wake us up. Recompute the fingerprint immediately; if it
# has not moved, re-arm without paying the full stabilization penalty.
EVENT_FP="$(fingerprint_of "$ABS_TARGET")"
if [ "$EVENT_FP" = "$BASE_FP" ]; then
continue
fi
# Stabilize: wait until the target has been quiet for SETTLE seconds. If it
# changes again during that window, reset the timer. For a directory this
# naturally absorbs bursts like a multi-file save or a checkout.
LAST_FP="$EVENT_FP"
QUIET=0
while [ "$QUIET" -lt "$SETTLE" ]; do
sleep 1
if [ ! -e "$ABS_TARGET" ]; then
echo "Error: watched path disappeared: $ABS_TARGET" >&2
exit 1
fi
NOW_FP="$(fingerprint_of "$ABS_TARGET")"
if [ "$NOW_FP" != "$LAST_FP" ]; then
LAST_FP="$NOW_FP"
QUIET=0
else
QUIET=$((QUIET + 1))
fi
done
# Confirm the listing actually changed from the baseline. fswatch can report
# spurious or duplicate events, so we only fire on a real delta.
FINAL_LISTING="$(listing_of "$ABS_TARGET")"
FINAL_FP="$(printf '%s' "$FINAL_LISTING" | hash_stdin)"
if [ "$FINAL_FP" != "$BASE_FP" ]; then
echo "Changed: $ABS_TARGET" >&2
if [ -d "$ABS_TARGET" ]; then
report_changes "$BASE_LISTING" "$FINAL_LISTING" "$ABS_TARGET" >&2
fi
exit 0
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment