#!/usr/bin/env bash # P8-REVIEW axis 3 — the six mutations of the report, planted and restored one at a time. # Run ONLY against a COPY of the platform tree. # # PLATFORM=/path/to/copy/platform TM_PLATFORM_TEST_DSN=... ./mutations.sh M1|M2|M3|M4|M5|M6|restore set -euo pipefail HERE="$(cd "$(dirname "$0")" && pwd)" PLATFORM="${PLATFORM:-$HERE/../platform}" BAK="${BAK:-$HERE/.mutation-backup}" # ⚠ Дописано при сдаче пака: гард против живого репозитория, по образцу axis2/mutations-axis2.sh. case "$PLATFORM" in *projects/textmachine*) echo "refusing to mutate the live repository"; exit 2;; esac mkdir -p "$BAK" save() { for f in "$@"; do mkdir -p "$BAK/$(dirname "$f")"; [ -f "$BAK/$f" ] || cp "$PLATFORM/$f" "$BAK/$f"; done; } case "${1:?M1|M2|M3|M4|M5|M6|restore}" in M1) # the two phases stop having separate shares of the pass (mechanism 1) save internal/runs/reconcile.go sed -i 's#return context.WithTimeout(ctx, time.Until(deadline)/2)#return context.WithTimeout(ctx, time.Until(deadline)) // MUTATION M1#' \ "$PLATFORM/internal/runs/reconcile.go" ;; M2) # the sweep's list stops honouring the deferral (mechanism 2, read side) save internal/pgstore/runs.go python3 - "$PLATFORM" <<'PY' import sys p=sys.argv[1]+'/internal/pgstore/runs.go'; L=open(p).read().split('\n') i=[n for n,l in enumerate(L) if 'a.reconcile_after is null or a.reconcile_after <= $1' in l][0] L[i]="\t\t and ($1::timestamptz is not null) -- MUTATION M2"; L[i+1]="\t\t and true" open(p,'w').write('\n'.join(L)) PY ;; M3) # the settlement phase stops deferring an item that spent its budget (mechanism 2, write side) save internal/runs/reconcile.go python3 - "$PLATFORM" <<'PY' import sys p=sys.argv[1]+'/internal/runs/reconcile.go'; s=open(p).read() old='\tc, cancel := context.WithTimeout(context.WithoutCancel(ctx), recordBudget)\n\tdefer cancel()\n\ts.deferItem(c, u, "the settlement took its whole budget without finishing")\n}' assert old in s open(p,'w').write(s.replace(old,'\t_ = recordBudget // MUTATION M3\n}')) PY ;; M4) # the readmodel pass's budget drops below ONE book's budget (the unpinned pair) save cmd/tmplatformd/runner.go sed -i 's#const refreshSweepBudget = 10 \* time.Minute#const refreshSweepBudget = 1 * time.Minute // MUTATION M4#' \ "$PLATFORM/cmd/tmplatformd/runner.go" ;; M5) # the reading-surface lease becomes a tenth of the work it covers save internal/readmodel/readmodel.go sed -i 's#ClaimReadModelDebt(ctx, b.ID, b.OwedAt, MaterializeBudget)#ClaimReadModelDebt(ctx, b.ID, b.OwedAt, MaterializeBudget/10) // MUTATION M5#' \ "$PLATFORM/internal/readmodel/readmodel.go" ;; M6) # the runs pass is no longer FIRST in the tick save cmd/tmplatformd/runner.go python3 - "$PLATFORM" <<'PY' import sys p=sys.argv[1]+'/cmd/tmplatformd/runner.go'; s=open(p).read() a='\tone := func() {\n\t\tpass("runs", sweepBudget, s.runs.Sweep)\n' assert a in s; s=s.replace(a,'\tone := func() { // MUTATION M6: the runs pass is last\n') b='\t\tc, cancel := context.WithTimeout(ctx, sweepBudget)\n\t\tdefer cancel()\n\t\tobserve(c, s, log)' assert b in s open(p,'w').write(s.replace(b,'\t\tpass("runs", sweepBudget, s.runs.Sweep) // MUTATION M6\n'+b)) PY ;; restore) cd "$BAK" && find . -name '*.go' -exec cp --parents {} "$PLATFORM/" \; && rm -rf "$BAK" && echo restored; exit 0 ;; esac cd "$PLATFORM" && gofmt -w $(cd "$BAK" && find . -name '*.go') 2>/dev/null || true go build ./... && echo "$1 planted"