textmachine/platform/docs/p8-review/axis3-queue/mutations.sh

63 lines
3.5 KiB
Bash
Executable file

#!/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"