Files
satellite-provider/_docs/03_implementation/batch_01_cycle5_report.md
T
Oleksandr Bezdieniezhnykh ab437a15df [AZ-504] Fix grep | wc -l pipefail crash in PT-08 batch counting
scripts/run-performance-tests.sh:416-417 used `grep -o ... | wc -l`
to count `"status":"accepted"` and `"status":"rejected"` markers in
the PT-08 batch response. On the happy path (rejected=0) grep -o
exits 1, and under `set -o pipefail` + `set -e` (line 16) the
pipeline killed the script before reaching any of PT-08's reporting
code — reproducing twice in the cycle-3 perf-harness leftover
(replay #2 + #3 post-AZ-500).

Fix: neutralise grep's no-match exit locally with `|| true` on the
grep stage of each pipeline. `grep -o | wc -l` is kept (not swapped
for `grep -c`) because the PT-08 response is compact JSON — all
items live on one line, so `grep -c` would always return 1 and lose
occurrence-count semantics. An 8-line comment explains why grep
cannot fail for I/O at this code path (file is curl-written, HTTP
200 gated).

AC-1 + AC-2 verified in-place against a standalone harness under
`set -e -o pipefail` (compact-JSON, mixed-status, edge-empty
cases). AC-3 + AC-4 are Step 15 (Performance Test) obligations by
spec design — the leftover deletion (AC-4) is "in the same commit"
as the green full perf run.

Batch report: _docs/03_implementation/batch_01_cycle5_report.md.
Code review: _docs/03_implementation/reviews/batch_01_cycle5_review.md
— PASS, no findings.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 16:32:36 +03:00

4.7 KiB

Batch Report

Batch: 01 (cycle 5) Tasks: AZ-504 — Perf script: fix grep | wc -l pipefail crash in PT-08 Date: 2026-05-12

Task Results

Task Status Files Modified Tests AC Coverage Issues
AZ-504_perf_script_grep_pipefail_fix Done (AC-1/AC-2 in-place verified; AC-3/AC-4 deferred to Step 15) 1 file pass (in-place harness, 4 cases under set -e -o pipefail) 2/4 covered now; 2/4 deferred to Step 15 by spec design None

Changes

scripts/run-performance-tests.sh:416-417 — replaced

accepted=$(grep -o '"status":"accepted"' "$PERF_TMP_DIR/pt08_resp.json" | wc -l | tr -d ' ')
rejected=$(grep -o '"status":"rejected"' "$PERF_TMP_DIR/pt08_resp.json" | wc -l | tr -d ' ')

with

# AZ-504: grep exits 1 on zero matches. Under `set -o pipefail` (line 16)
# that kills the assignment and crashes the script on the happy path
# (rejected=0). Neutralise the no-match case locally with `|| true` so
# the pipeline still produces a count. The response is compact JSON
# (one line, all items) so `grep -o | wc -l` is required to count
# occurrences — `grep -c` would only count matching lines (=1). The
# file is guaranteed-readable here (curl wrote it earlier in this
# iteration on the HTTP 200 branch), so grep cannot fail for I/O.
accepted=$({ grep -o '"status":"accepted"' "$PERF_TMP_DIR/pt08_resp.json" || true; } | wc -l | tr -d ' ')
rejected=$({ grep -o '"status":"rejected"' "$PERF_TMP_DIR/pt08_resp.json" || true; } | wc -l | tr -d ' ')

Why || true on grep instead of grep -c: PT-08 response is compact JSON written by ASP.NET Core's default serializer — all items live on a single line of the response body. grep -c counts MATCHING LINES, so it would return 1 regardless of whether there are 1 or 10 accepted items (Risk 1 of the task spec). grep -o + wc -l preserves occurrence semantics. The || true is the minimum local neutralisation of pipefail and is justified by the coderule.mdc exception "If an error is truly safe to ignore, log it or comment why" — the 8-line comment explains why grep cannot fail for I/O at this code path (file is curl-written, HTTP-200-gated).

Shellcheck pass over the file (Risk 2 of task spec): surveyed all grep ... | wc -l and grep -[oc] sites. Only two grep -o ... | wc -l sites exist in the script — both at lines 416-417 (the ones fixed). The third grep -o site (line 141, region status polling) is already protected by head -1 || true and is not vulnerable. No other defensive work required.

AC Test Coverage

AC Status Where verified
AC-1 — PT-08 completes on zero-rejected response Covered Standalone harness reproduces the exact pipeline under set -e -o pipefail with accepted=2 rejected=0; pipeline returns count 0 without script exit.
AC-2 — PT-08 completes on zero-accepted response (defensive) Covered Same harness with accepted=0 rejected=2; pipeline returns count 0 for accepted without script exit.
AC-3 — PT-08 summary line prints in full run Deferred to Step 15 Requires docker-compose up -d --build + full default-parameter ./scripts/run-performance-tests.sh (PERF_REPEAT_COUNT=20 PERF_UAV_BATCH_SIZE=10). This is exactly the Step 15 (Performance Test) gate for cycle 5. The spec is staged: AC-4 GIVEN clause depends on AC-3 + "the full perf run is green", so the natural verification environment is Step 15.
AC-4 — Leftover deletion on green full run Deferred to Step 15 By spec design — AC-4 says the leftover file is deleted "in the same commit" as the green full perf run. That commit IS the Step 15 deliverable. Confirmed in the perf-cycle3 leftover's "Replay attempt #4" entry.

Spec-Gap? No. AC-3 + AC-4 are not gaps for Step 10 — they are explicit Step 15 deliverables baked into the spec itself (AC-4 GIVEN clause). Step 10 (this batch) delivers the script fix that makes Step 15 possible.

Test Strategy Note

This project has no shell-script unit test infrastructure (no BATS, no scripts/test_*.sh). The established pattern is "the script is the test" — scripts/run-tests.sh and scripts/run-performance-tests.sh ARE the verification surface. Adding BATS for a 1-SP single-line fix would be infra creep explicitly disallowed by coderule.mdc ("Avoid boilerplate and unnecessary indirection ... follow established project patterns"). The standalone harness recorded above is repeatable on demand and produces evidence equivalent to a BATS smoke test.

Code Review Verdict: PASS

Auto-Fix Attempts: 0

Stuck Agents: None

Next Batch: AZ-503 — Tile identity → UUIDv5 + integer UPSERT + bulk-list endpoint (3 SP)