import io, sys
p = sys.argv[1]
s = io.open(p, encoding='utf-8', newline='').read().replace('\r\n', '\n')

def sub(o, n, cnt=1):
    global s
    assert s.count(o) == cnt, (s.count(o), o[:70])
    s = s.replace(o, n)

old = """ci_gate() { # TAG
  local tag="$1" lf="$R/ci-$1-list.json"
  # `gh run list` REJECTS --json jobs; `gh run view --json status` freezes. Hence the
  # two-step: list carries status/conclusion, then view carries jobs.
  gh run list --repo "$CI_REPO" --limit 10 --json databaseId,headSha,status,conclusion,workflowName > "$lf" 2>"$R/ci-$1-list.err"
  local rc=$?
  exits "ci_${tag}_list_EXIT=$rc"
  [ "$rc" -eq 0 ] || die "CI query for '$tag' against $CI_REPO exited $rc — an unread CI axis is not a free box"
  local live
  live=$(python -c 'import json,sys; d=json.load(open(sys.argv[1],encoding="utf-8")); print(" ".join(str(r["databaseId"]) for r in d if r.get("status") not in ("completed",)))' "$lf" 2>"$R/ci-$1-parse.err")
  local prc=$?
  exits "ci_${tag}_parse_EXIT=$prc"
  [ "$prc" -eq 0 ] || die "could not parse the CI listing for '$tag' — see ci-$1-parse.err"
  if [ -n "$live" ]; then
    local id
    for id in $live; do
      gh run view "$id" --repo "$CI_REPO" --json jobs > "$R/ci-$tag-run-$id-jobs.json" 2>>"$R/ci-$1-view.err"
      exits "ci_${tag}_view_${id}_EXIT=$?"
    done
    die "CI on $CI_REPO has non-terminal run(s) [$live] at '$tag' — per-job detail captured; the box is not free and a merge-push re-occupies the runner"
  fi
  record "CI_AXIS ok at '$tag': no non-terminal runs on $CI_REPO"
}
"""

new = """# THE ACTIVE STATUSES, asked for BY NAME (r9). The previous gate listed the latest 10 runs
# and filtered them for non-terminal status, which can only ever see an active run that is
# also RECENT. A run queued behind 40 newer completed ones is invisible to it, and the gate
# reported "no non-terminal runs" -- a clean zero over a window that never contained the
# answer. Admission must not depend on active jobs being recent.
# Values are gh's own (gh 2.89.0, `gh run list --help`): queued, in_progress, requested,
# waiting and pending are the non-terminal set; completed and the conclusion values are not
# statuses a run can still be occupying the box in.
CI_ACTIVE_STATUSES='queued in_progress requested waiting pending'
CI_STATUS_LIMIT=100

ci_scan_file() { # FILE -> echoes ids; rc 0 = read, 2 = unreadable
  # PURE over a file, so the decision can be exercised without a network call.
  local f="$1" out
  out=$(python -c 'import json,sys
d = json.load(open(sys.argv[1], encoding="utf-8"))
if not isinstance(d, list): raise SystemExit("not a JSON array")
for r in d:
    if "databaseId" not in r: raise SystemExit("row without databaseId")
print(" ".join(str(r["databaseId"]) for r in d))' "$f" 2>/dev/null) || return 2
  echo "$out"
}

ci_gate() { # TAG
  local tag="$1" st lf rc ids n live=''
  # ONE QUERY PER ACTIVE STATUS. A status-filtered listing is not bounded by recency, so an
  # old queued run appears in it; the previous recency-bounded form could not ask the
  # question at all.
  for st in $CI_ACTIVE_STATUSES; do
    lf="$R/ci-$tag-$st.json"
    gh run list --repo "$CI_REPO" --status "$st" --limit "$CI_STATUS_LIMIT" --json databaseId,headSha,status,workflowName > "$lf" 2>"$R/ci-$tag-$st.err"
    rc=$?
    exits "ci_${tag}_${st}_EXIT=$rc"
    [ "$rc" -eq 0 ] || die "CI '$st' query for '$tag' against $CI_REPO exited $rc — an unread CI axis is not a free box, and a status this gate cannot ask about is not a status with no runs"
    ids=$(ci_scan_file "$lf"); local prc=$?
    exits "ci_${tag}_${st}_parse_EXIT=$prc"
    [ "$prc" -eq 0 ] || die "could not parse the CI '$st' listing for '$tag' — an unparsed answer is not an empty one"
    n=$(printf '%s' "$ids" | wc -w | tr -d ' ')
    record "CI_STATUS '$tag' status=$st count=$n limit=$CI_STATUS_LIMIT"
    # A FULL PAGE MAY BE A TRUNCATED PAGE. Refusing here is the fail-closed answer: an
    # unread remainder is not an empty remainder, and this gate exists to say the box is
    # free only when it has actually seen that it is.
    [ "$n" -lt "$CI_STATUS_LIMIT" ] || die "the CI '$st' query returned a FULL page ($n == limit $CI_STATUS_LIMIT) for '$tag' — the answer may be truncated and the unread remainder cannot be assumed empty"
    live="$live $ids"
  done
  live=$(printf '%s' "$live" | tr -s ' ' | sed 's/^ //;s/ $//')
  if [ -n "$live" ]; then
    local id
    for id in $live; do
      gh run view "$id" --repo "$CI_REPO" --json jobs > "$R/ci-$tag-run-$id-jobs.json" 2>>"$R/ci-$tag-view.err"
      exits "ci_${tag}_view_${id}_EXIT=$?"
    done
    die "CI on $CI_REPO has active run(s) [$live] at '$tag' — per-job detail captured; the box is not free and a merge-push re-occupies the runner"
  fi
  record "CI_AXIS ok at '$tag': every active status queried BY NAME ($CI_ACTIVE_STATUSES) returned zero runs on $CI_REPO — not merely zero among the most recent"
}
"""
sub(old, new)

sub("# FIELD DRIVER v5 r8 — subject 85f84d73. FOR SOURCE REVIEW ONLY.\n",
    "# FIELD DRIVER v5 r9 — subject 85f84d73. FOR SOURCE REVIEW ONLY.\n") if s.count("# FIELD DRIVER v5 r8") else None

io.open(p, 'w', encoding='utf-8', newline='\r\n').write(s)
print('r9 ok')
