Merge branch 'feat/disaggregated' into p5/c1

# Conflicts:
#	MobileGL/MG_Remote/Client/ClientSession.cpp
#	MobileGL/MG_Test/Wire/CMakeLists.txt
This commit is contained in:
2026-09-16 07:23:35 -04:00
35 changed files with 2640 additions and 396 deletions
+106
View File
@@ -0,0 +1,106 @@
#!/bin/bash
# R-16 FOR THE CI NEGATIVE CONTROLS THEMSELVES: a control-run smoke test.
#
# BRIEF-P5 13 (R-16) says a negative control must assert its own failure reason and that every gate
# carries a line saying "I made it red once, by doing X". The two controls this file exercises ARE
# gates, and until ID-46 finding 8 nobody could make either of them red, because a workflow `run:`
# block only executes on a runner. The wave-1 verification agent had to hand-copy the blocks into
# throwaway harnesses to show they were broken (wave1-codex-verify.md 8). This file is that
# experiment, kept: it runs the REAL control scripts - the same files .github/workflows/test.yml
# invokes, not copies of them - against a stubbed ctest, and checks that each one passes exactly
# when it should.
#
# The case that matters is the first one. A stubbed ctest reports a NON-EMPTY selection and then
# fails with UNRELATED_CONTROL_FAILURE: a reason that has nothing to do with the knob the control
# turns. Before ID-48's fix both controls printed their success message and the step exited 0. They
# must now report FAILED.
#
# usage: control_smoke_test.sh
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
WORK="$(mktemp -d)"
trap 'rm -rf "${WORK}"' EXIT
STUB_DIR="${WORK}/stub"
mkdir -p "${STUB_DIR}"
cp "${HERE}/testdata/stub_ctest.sh" "${STUB_DIR}/ctest"
chmod +x "${STUB_DIR}/ctest"
passes=0
failures=0
# expect <expected: PASSED|FAILED> <label> -- <command...>
expect() {
want="$1"; label="$2"; shift 3 # shift past the literal "--"
outfile="${WORK}/run.out"
"$@" > "${outfile}" 2>&1
rc=$?
if [ "${rc}" -eq 0 ]; then got="PASSED"; else got="FAILED"; fi
if [ "${got}" = "${want}" ]; then
passes=$((passes + 1))
printf 'ok %-58s %s (rc=%d)\n' "${label}" "${got}" "${rc}"
else
failures=$((failures + 1))
printf 'NOT OK %-58s expected %s, got %s (rc=%d)\n' "${label}" "${want}" "${got}" "${rc}"
sed 's/^/ | /' "${outfile}"
fi
}
run_split() { # $1 = STUB_MODE
env -i PATH="${STUB_DIR}:/usr/bin:/bin" STUB_MODE="$1" \
CTEST=ctest CONTROL_TMPDIR="${WORK}/tmp-$1" \
bash "${HERE}/split_negative_controls.sh"
}
run_retrace() { # $1 = STUB_MODE
cd "${WORK}" || return 127
mkdir -p "${WORK}/OpenRA"
env -i PATH="${STUB_DIR}:/usr/bin:/bin" STUB_MODE="$1" \
CTEST=ctest CONTROL_TMPDIR="${WORK}/tmp-$1" \
PULL_LIBRARY="${WORK}/pull.so" FROZEN_LIBRARY="${WORK}/frozen.so" \
bash "${HERE}/retrace_pull_library_control.sh" OpenRA DirectGLES
}
echo "=== the split lane's E1 / E3(a) controls (scripts/ci/split_negative_controls.sh)"
# THE FINDING, REPRODUCED. Non-empty selection, green baseline, and a red that is not the knob's.
expect FAILED "unrelated failure with a non-empty selection" -- run_split unrelated
# ... and the same control on the same stub, failing for its own reason: it must PASS.
expect PASSED "the scenarios' own diagnostic" -- run_split evidence
# The pre-existing half of the control, which was never broken: a knob that reds nothing.
expect FAILED "the knob leaves the selection green" -- run_split green
# The arming counter's half of the finding: a baseline that is already red cannot arm anything.
expect FAILED "the baseline is already red" -- run_split red-baseline
# The disarmed lane, which is a legitimate exit 0 while c1/s1/v1 are landing.
expect PASSED "every split entry skipped (lane not armed)" -- run_split all-skipped
echo
echo "=== the retrace lane's pull-library control (scripts/ci/retrace_pull_library_control.sh)"
# A pull-shaped library the nm identity check accepts: a real ELF .so defining no MG_Remote symbol.
if command -v cc > /dev/null 2>&1; then
printf '%s\n' 'int mobilegl_pull_only(void) { return 1; }' > "${WORK}/pull.c"
cc -shared -fPIC -o "${WORK}/pull.so" "${WORK}/pull.c" || { echo "cannot build the stand-in library"; exit 1; }
else
echo "no cc available; the retrace half of this smoke test needs one" >&2
exit 1
fi
: > "${WORK}/frozen.so"
# THE FINDING, part (b): a regex matching no tests. --no-tests=error exits non-zero and the old
# control read that as "the pull library turned it red".
expect FAILED "empty selection (--no-tests=error exit)" -- run_retrace retrace-noselect
# A red that never names the transport: a fixture failure, a loader failure, a timeout.
expect FAILED "red without the transport-resolution message" -- run_retrace retrace-unrelated
# The real thing - and note the stub emits it CMake-wrapped across two lines, which a line-oriented
# grep for the literal sentence would miss.
expect PASSED "run_trace_case.cmake's own sentence, wrapped" -- run_retrace retrace-evidence
# The pull library replaying green is the failure this control exists to catch.
expect FAILED "a pull library passed the split retrace" -- run_retrace retrace-green
echo
echo "smoke test: ${passes} passed, ${failures} failed"
if [ "${failures}" -gt 0 ]; then
echo "CONTROL_SMOKE_TEST_FAILED"
exit 1
fi
echo "CONTROL_SMOKE_TEST_OK"
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""Tally a ctest --output-junit file as "passed failed skipped".
Split out of .github/workflows/test.yml's negative-control step so that the workflow, the local
gate and the control smoke test all count a run the same way.
WHY THIS EXISTS AT ALL (ID-46 finding 8, second half). The counter this replaces lived inline in
the workflow and counted a case as having "run" when it was merely not <skipped/>:
if case.find('skipped') is None and case.get('status') not in ('notrun', 'disabled'):
ran += 1
so a case that RAN AND FAILED armed the negative controls below it. Combined with the `|| true`
that hid the baseline's exit code, a lane in which every split entry was already red reported
itself armed, and a control that turns an already-red entry red then "passed". Passed, failed and
skipped are three different answers and the caller needs all three.
"""
import sys
import xml.etree.ElementTree as ET
def tally(path):
passed = failed = skipped = 0
for case in ET.parse(path).getroot().iter('testcase'):
if case.find('failure') is not None or case.find('error') is not None:
failed += 1
elif case.find('skipped') is not None or case.get('status') in ('notrun', 'disabled'):
skipped += 1
else:
passed += 1
return passed, failed, skipped
def main():
if len(sys.argv) != 2:
print("usage: junit_tally.py <junit.xml>", file=sys.stderr)
return 2
try:
passed, failed, skipped = tally(sys.argv[1])
except Exception as exc: # a malformed file is not "zero of everything"
print(f"junit_tally: cannot parse {sys.argv[1]}: {exc}", file=sys.stderr)
return 1
print(f"{passed} {failed} {skipped}")
return 0
if __name__ == '__main__':
raise SystemExit(main())
+71
View File
@@ -0,0 +1,71 @@
#!/bin/bash
# R-16's "I made it red once, by doing X" for scripts/ci/control_smoke_test.sh, mechanised so the
# claim can be re-checked rather than believed.
#
# X = revert the message check in each control, i.e. put the controls back in the state ID-46
# finding 8 found them in: a non-zero ctest exit is accepted whatever the failure was.
#
# The smoke test must then FAIL, and it must fail on the two cases that exist for this defect -
# "unrelated failure with a non-empty selection" and "red without the transport-resolution
# message" - and not merely somewhere. A smoke test that goes red for any other reason when the
# evidence check is removed would not be pinning the evidence check.
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
WORK="$(mktemp -d)"
trap 'cp "${WORK}/split.orig" "${HERE}/split_negative_controls.sh"; cp "${WORK}/retrace.orig" "${HERE}/retrace_pull_library_control.sh"; rm -rf "${WORK}"' EXIT
cp "${HERE}/split_negative_controls.sh" "${WORK}/split.orig"
cp "${HERE}/retrace_pull_library_control.sh" "${WORK}/retrace.orig"
echo "=== baseline: the smoke test must be GREEN before anything is perturbed"
if ! bash "${HERE}/control_smoke_test.sh" > "${WORK}/before.log" 2>&1; then
echo "the smoke test is ALREADY RED; the red-check below would prove nothing"
cat "${WORK}/before.log"
exit 1
fi
tail -1 "${WORK}/before.log"
echo
echo "=== perturbation: remove the evidence check from both controls"
python3 - "${HERE}/split_negative_controls.sh" "${HERE}/retrace_pull_library_control.sh" <<'PY'
import sys
split, retrace = sys.argv[1], sys.argv[2]
for path, needle in ((split, 'grep -qE "${evidence}"'), (retrace, 'grep -qF "${EVIDENCE}"')):
text = open(path).read()
out, hit = [], 0
for line in text.splitlines(keepends=True):
if needle in line and line.lstrip().startswith('if ! '):
indent = line[:len(line) - len(line.lstrip())]
out.append(f"{indent}if false; then\n")
hit += 1
else:
out.append(line)
if hit != 1:
raise SystemExit(f"expected exactly one evidence check in {path}, found {hit}")
open(path, 'w').write(''.join(out))
print("both evidence checks reverted to 'any non-zero ctest exit is accepted'")
PY
echo
echo "=== the smoke test on the reverted controls (it MUST be red, on those two cases)"
bash "${HERE}/control_smoke_test.sh" > "${WORK}/after.log" 2>&1
rc=$?
cat "${WORK}/after.log"
if [ "${rc}" -eq 0 ]; then
echo
echo "RED-CHECK FAILED: the controls accept an unrelated failure again and the smoke test still passed."
exit 1
fi
missed=0
grep -q "NOT OK unrelated failure with a non-empty selection" "${WORK}/after.log" || missed=1
grep -q "NOT OK red without the transport-resolution message" "${WORK}/after.log" || missed=1
if [ "${missed}" -ne 0 ]; then
echo
echo "RED-CHECK FAILED: the smoke test went red, but not on the two cases the evidence check exists for."
exit 1
fi
echo
echo "P5_T1_CONTROL_SMOKE_REDCHECK_OK - removing the evidence check reds exactly the two cases that pin it"
+117
View File
@@ -0,0 +1,117 @@
#!/bin/bash
# THE RETRACE-SPLIT LANE'S NEGATIVE CONTROL: a PULL library must red this split retrace.
#
# This file is the body of .github/workflows/test.yml's "Negative control - the PULL library must
# red this split retrace" step, extracted for the reason given at the top of
# scripts/ci/split_negative_controls.sh: a `run:` block is unreviewable and untestable off a
# runner, and scripts/ci/control_smoke_test.sh now runs THIS file rather than a hand-made copy.
#
# WHAT THE CONTROL IS FOR. The retrace-split job replays a trace against the SPLIT runtime under
# MOBILEGL_TRANSPORT=inproc. OpenRA scores ssim 1.000000 against a MONOLITH library too - measured -
# so the picture is not and cannot be this lane's gate. What stands between the job and a green that
# ran monolith end to end is run_trace_case.cmake's transport-resolution assertion
# (run_trace_case.cmake:265-289): the library must have logged
# "MOBILEGL_TRANSPORT=inproc - the MGPipe record stream", which exists only in
# ConfigLoader::InitTransport's InProcess arm, which exists only under MOBILEGL_BUILD_DISAGGREGATED.
# This control swaps the pull library over the frozen path and requires the same replay to fail FOR
# THAT REASON.
#
# WHAT THE REVIEW FOUND (ID-46 finding 8 part (b), CONFIRMED by execution against the REAL ctest in
# a REAL build tree; ID-48 assigns it here). Two holes, both of which let the control pass while
# asserting nothing:
#
# 1. NO SELECTION GUARD AT ALL - unlike the split lane's run_control, which has had one since
# review finding M-4. With a case/backend regex matching no tests, `--no-tests=error` exits 8,
# and the old `if [ "${control_rc}" -eq 0 ]` accepted 8 as "the pull library turned it red".
# The verifier measured exactly that: "real ctest exit for a regex matching NO tests: 8",
# HARNESS_EXIT=0. An empty selection was the one thing this control could not tell apart from
# a working transport-identity assertion.
# 2. ONLY "non-zero ctest" WAS CHECKED after the nm identity check. The nm check establishes that
# the library IS a pull build; it says nothing about why the replay failed. A loader failure, a
# missing fixture or a timeout all passed the control.
#
# Both are closed below: the selection is counted before the run, and the red must carry
# run_trace_case.cmake's own words.
#
# Usage: retrace_pull_library_control.sh <case> <backend>
# CTEST ctest binary (default: ctest)
# CONTROL_TMPDIR scratch dir (default: ${RUNNER_TEMP:-/tmp})
# PULL_LIBRARY the pull libMobileGL.so to swap in
# FROZEN_LIBRARY the path every case has baked in, which PULL_LIBRARY is copied over
set -u
CASE="${1:?usage: retrace_pull_library_control.sh <case> <backend>}"
BACKEND="${2:?usage: retrace_pull_library_control.sh <case> <backend>}"
CTEST="${CTEST:-ctest}"
CONTROL_TMPDIR="${CONTROL_TMPDIR:-${RUNNER_TEMP:-/tmp}}"
PULL_LIBRARY="${PULL_LIBRARY:?PULL_LIBRARY must name the pull build libMobileGL.so}"
FROZEN_LIBRARY="${FROZEN_LIBRARY:?FROZEN_LIBRARY must name the path the cases have baked in}"
mkdir -p "${CONTROL_TMPDIR}"
# run_trace_case.cmake's own sentence for "this library never resolved the transport". Anchored on
# the distinctive clause rather than on the whole paragraph, which carries substituted paths.
EVIDENCE='never reported resolving it'
selector="^MobileGLTraceReplay\.${CASE}\.${BACKEND}$"
# The rerun replays into the same case directory, so the good run's images are put aside and
# restored whichever way the control goes; "Upload actual image" runs `if: always()` and would
# otherwise ship the deliberately-wrong run's output under the good run's name.
GOOD_OUTPUT="${CONTROL_TMPDIR}/split-verified-output"
rm -rf "${GOOD_OUTPUT}"
if [ -d "${CASE}" ]; then cp -a "${CASE}" "${GOOD_OUTPUT}"; fi
restore_good_output() {
if [ -d "${GOOD_OUTPUT}" ]; then
rm -rf "${CASE}"; mv "${GOOD_OUTPUT}" "${CASE}"
echo "restored the verified run's output over the control's"
fi
}
# HOLE 1: COUNT THE SELECTION FIRST. `--no-tests=error` turns an empty selection into a non-zero
# exit, which is indistinguishable from a working control unless the selection is counted.
matched=$("${CTEST}" -N -R "${selector}" | grep -cE '^ *Test *#[0-9]+:')
if [ "${matched}" -lt 1 ]; then
restore_good_output
echo "::error::the control selected ${matched} tests with -R '${selector}', so there is nothing for the pull library to red. --no-tests=error would have exited non-zero on the empty selection and this control used to read that as success (ID-46 finding 8b, measured: ctest exit 8, step green)."
exit 1
fi
# The pull library, unpacked from build-linux's artifact, over the frozen path every case has baked
# in. It defines no MG_Remote symbol, so ConfigLoader has no transport parser and
# MOBILEGL_TRANSPORT=inproc is accepted and ignored - the exact shape of "the split lane ran
# monolith".
cp "${PULL_LIBRARY}" "${FROZEN_LIBRARY}"
if nm --defined-only "${FROZEN_LIBRARY}" | grep -q -i MG_Remote; then
restore_good_output
echo "::error::the control's own library defines MG_Remote symbols, so it is not a pull build and this control would prove nothing"
exit 1
fi
out="${CONTROL_TMPDIR}/retrace-control-output.txt"
export MOBILEGL_TRANSPORT=inproc
"${CTEST}" -V --no-tests=error --timeout 10800 -R "${selector}" > "${out}" 2>&1
control_rc=$?
cat "${out}"
restore_good_output
if [ "${control_rc}" -eq 0 ]; then
echo "::error::a PULL library passed the split retrace. OpenRA scores ssim 1.000000 under a monolith library too (measured), so the picture is not and cannot be this lane's gate - run_trace_case.cmake's transport-resolution assertion is, and it has stopped working. Every green in this job is then a monolith run under a name that says split."
exit 1
fi
# HOLE 2: THE RED MUST BE THIS CONTROL'S RED.
#
# Whitespace is normalised across the WHOLE file before the match, newlines included, because the
# sentence is emitted by CMake's message(FATAL_ERROR ...) and CMake re-wraps that text to its own
# width: "never reported resolving it" arrives split over two lines with a two-space continuation
# indent, and a line-oriented grep for the literal finds nothing. That is not hypothetical - it is
# the shape the stub reproduces in scripts/ci/testdata/stub_ctest.sh.
if ! tr -s '[:space:]' ' ' < "${out}" | grep -qF "${EVIDENCE}"; then
echo "::error::the split retrace went red (ctest exit ${control_rc}) with the pull library in place, but the failure never says the library did not resolve the transport - run_trace_case.cmake's \"${EVIDENCE}\" is absent from the output. A loader failure, a missing fixture, a timeout or an SSIM drop all land here, and none of them establishes that the transport-identity assertion is what caught the pull library. Only 'non-zero ctest' used to be checked (ID-46 finding 8b)."
exit 1
fi
echo "the pull library turned the split retrace red for its own reason (ctest exit ${control_rc}): ${matched} selected case(s) named the transport, not the picture"
+151
View File
@@ -0,0 +1,151 @@
#!/bin/bash
# EXIT GATE E1's NEGATIVE CONTROL and EXIT GATE E3(a)'s.
#
# This file is the body of .github/workflows/test.yml's "Negative controls - the verb barrier and
# the persistent-map push must be load-bearing" step. It lives in the repository rather than inline
# in the workflow for one reason: a workflow `run:` block cannot be executed anywhere except on a
# runner, so the logic below was unreviewable and untestable until it ran in CI - and when the
# wave-1 cross-family review claimed it was broken, confirming the claim needed a hand-made copy of
# these lines with their inputs stubbed (wave1-codex-verify.md 8). A copy is not the thing. The
# smoke test at scripts/ci/control_smoke_test.sh now runs THIS file, so the lines CI executes and
# the lines the smoke test proves are the same lines.
#
# WHAT THE REVIEW FOUND (ID-46 finding 8, CONFIRMED by execution; ID-48 assigns it here).
# The previous version accepted ANY non-zero ctest exit as "the knob is load-bearing". A timeout, a
# setup abort, an unrelated assertion, a harness that died before it read the knob at all - every
# one of them printed "turned N selected entries red, as it must" and the step went green. The
# verifier demonstrated it: a stubbed ctest with a NON-EMPTY selection that failed with
# `UNRELATED_CONTROL_FAILURE` produced both controls' success messages and HARNESS_EXIT=0.
#
# So each control now has to say WHY the red is its own:
#
# 1. THE BASELINE MUST BE GREEN. The arming run below used to end in `|| true` and count every
# case that was not <skipped/> as "ran" - so a case that RAN AND FAILED armed the controls,
# and a control that turns an already-red entry red proves nothing at all. It now counts
# PASSED cases, and a baseline with any failure in it is a hard error rather than an arming
# signal.
# 2. THE RED MUST CARRY THE SELECTED CASE'S OWN FAILURE TEXT. Each control names a regex of the
# diagnostics its scenarios emit when that knob is off, and the red is refused if the output
# carries none of them.
#
# WHY THE EVIDENCE IS THE SCENARIO'S ASSERTION TEXT AND NOT THE KNOB'S OWN LOG LINE.
# ConfigLoader logs a named line for both knobs (ConfigLoader.cpp:385-393, "is the R-1 NEGATIVE
# CONTROL", "is the E3(a) NEGATIVE CONTROL"), and it is tempting to grep for that. It is not
# evidence: it is written at config load, by every process in the run, whatever happens next. A
# setup abort would carry it too. It proves the knob was READ, never that the knob caused the red.
# Only the failing case's own diagnostic does that. (It is also unreachable from here: the three
# DirectGLES.Split. lanes set no MOBILEGL_LOG_FILE_PATH, and the library's console sink is compiled
# out of this configuration, so no MGLOG_ output of any level reaches ctest's transcript. Measured:
# ~/w7/p5-v1-joint-isplit-barrier0.log, 18 aborted entries, zero occurrences of the string "Fatal".)
#
# Usage: split_negative_controls.sh
# CTEST ctest binary (default: ctest)
# CONTROL_TMPDIR scratch dir for the junit + output (default: ${RUNNER_TEMP:-/tmp})
set -u
CTEST="${CTEST:-ctest}"
CONTROL_TMPDIR="${CONTROL_TMPDIR:-${RUNNER_TEMP:-/tmp}}"
mkdir -p "${CONTROL_TMPDIR}"
junit="${CONTROL_TMPDIR}/isplit.xml"
# ---- the baseline ---------------------------------------------------------------------------
#
# THE ARMED STATE IS DERIVED FROM BEHAVIOUR, not from a marker string in the generated ctest files.
# The first version read MGITEST_REMOTE_CLIENT_PRESENT out of *_tests.cmake, which was a
# restatement of the CMake source probe review finding M-1 falsified; the arming condition is a
# runtime fact inside each test process (MG_Config::Transport, ClientSession::Active(),
# ImplementedVerbCount(), read by Harness/SplitRuntimePeek), so the only honest way to ask it from a
# shell is to look at what the entries DID.
"${CTEST}" -L integration-split -j 4 --no-tests=error --output-junit "${junit}"
baseline_rc=$?
if [ ! -f "${junit}" ]; then
echo "::error::the baseline run wrote no ${junit} (ctest exit ${baseline_rc}), so nothing below can tell an armed lane from a broken one"
exit 1
fi
tally=$(python3 "$(dirname "$0")/junit_tally.py" "${junit}")
if [ -z "${tally}" ]; then
echo "::error::could not tally ${junit}; a run whose result cannot be read is not an arming signal"
exit 1
fi
baseline_passed=$(echo "${tally}" | cut -d' ' -f1)
baseline_failed=$(echo "${tally}" | cut -d' ' -f2)
baseline_skipped=$(echo "${tally}" | cut -d' ' -f3)
echo "split entries - passed: ${baseline_passed}, failed: ${baseline_failed}, skipped: ${baseline_skipped} (ctest exit ${baseline_rc})"
# A RED BASELINE DISARMS THE CONTROLS RATHER THAN ARMING THEM (review finding 8, second half).
# `|| true` plus a "not skipped" counter used to treat a case that ran and FAILED as evidence the
# lane was live. Turning an already-red entry red is not a measurement.
if [ "${baseline_failed}" -gt 0 ]; then
echo "::error::${baseline_failed} DirectGLES.Split. entries are ALREADY RED with both knobs at their defaults, so neither negative control below can attribute its red to the knob it turns. Fix the lane first; a control measured against a red baseline is not a control. (This used to be swallowed by an unconditional '|| true' and counted as 'the lane is armed'.)"
exit 1
fi
if [ "${baseline_passed}" -lt 1 ]; then
echo "::warning::every DirectGLES.Split. entry SKIPPED, so neither negative control can fire. The arming condition is a runtime fact - MG_Config::Transport, ClientSession::Active() and ImplementedVerbCount(), read by Harness/SplitRuntimePeek - and it becomes true on the commit that lands the last of c1/s1/v1. This step becomes a gate then, with no edit; it is not a green that asserted anything today."
exit 0
fi
# ---- the controls ---------------------------------------------------------------------------
#
# run_control <name> <filter> <evidence regex> <VAR=VALUE>...
run_control() {
name="$1"; filter="$2"; evidence="$3"; shift 3
matched=$("${CTEST}" -N -L integration-split -R "${filter}" | grep -cE '^ *Test *#[0-9]+:')
if [ "${matched}" -lt 1 ]; then
echo "::error::${name} selected ${matched} tests; its filter no longer matches anything"
exit 1
fi
out="${CONTROL_TMPDIR}/control-output.txt"
env "$@" "${CTEST}" --output-on-failure -L integration-split -R "${filter}" --no-tests=error > "${out}" 2>&1
control_rc=$?
cat "${out}"
if [ "${control_rc}" -eq 0 ]; then
echo "::error::${name} left ${matched} split entries GREEN, so the knob it turns is not load-bearing and the gate it controls proves nothing."
exit 1
fi
# THE HALF THAT WAS MISSING. A non-zero exit is necessary and nowhere near sufficient.
# Whitespace is normalised across the whole file first, for the reason given in
# retrace_pull_library_control.sh: a diagnostic that arrives wrapped is still the diagnostic.
if ! tr -s '[:space:]' ' ' < "${out}" | grep -qE "${evidence}"; then
echo "::error::${name} turned ${matched} selected entries red, but the red carries NONE of the diagnostics those scenarios emit when this knob is off, so it is not this control's red. Required one of: ${evidence}. A timeout, a setup abort, a harness that died before it read the knob, or any unrelated assertion lands here - and every one of them used to print the success message below and leave this step green (ID-46 finding 8). If the entries aborted with no output at all, that is the barrier path having no named diagnostic of its own: see t1-v2.md, it is a debt on the server package, not a reason to accept the red."
exit 1
fi
echo "${name} turned ${matched} selected entries red, and the red carries the scenario's own diagnostic, as it must"
}
# E1: R-1's lockstep verb barrier. Without it the client keeps pulling fields from a live GLContext
# while the server runs ahead, so the server reads future values.
#
# THE SELECTION INCLUDES THE SmallRing LANE, and that is not cosmetic. Measured on v1's joint tree
# (~/w7/p5-v1-joint-isplit-barrier0.log): with the barrier off, every entry the OLD filter selected
# aborted with no output whatsoever, and the one entry in the whole run that failed with a readable
# assertion - ClearThenReadPixelsScenario.cpp:290/:295, reading back 0 where >200 was cleared - was
# a DirectGLES.Split.SmallRing. entry, which the old filter excluded. A control whose selection
# contains no case able to say why it failed cannot assert its own failure reason. The SmallRing
# entries are the same two scenarios under the same transport with SEG_CMD/SEG_STAGE at their floor,
# so including them widens E1's selection strictly within E1's charter.
run_control "negative control E1 (MOBILEGL_IPC_VERB_BARRIER=0)" \
'DirectGLES\.Split\.(SmallRing\.)?(Triangle|ClearThenReadPixels)' \
'ClearThenReadPixelsScenario\.cpp:(290|295)|the bottom band should be red after the resolve|the top band should be blue after the resolve|TriangleScenario\.cpp:[0-9]+: Failure' \
MOBILEGL_IPC_VERB_BARRIER=0
# E3(a): the persistent-map push. 0 is admitted by ConfigLoader on purpose and is documented there
# as this control. The evidence is the scenario's own wording for "the second write never arrived":
# with the push off, the write that no GL call announces cannot reach its draw, which is exactly
# what TwoWritesThroughTheCoherentPointerEachReachTheirOwnDraw and
# AWriteAfterAFrameBoundaryReachesTheNextFramesDraw read back
# (PersistentCoherentMapScenario.cpp:414-417, :442-443). The counting case's pmap= assertion
# (:531-541) is listed too, for the lane where it is the one that runs.
run_control "negative control E3(a) (MOBILEGL_IPC_PERSISTENT_BLOCK_KB=0)" \
'DirectGLES\.Split\.(SmallRing\.)?PersistentCoherentMapScenario' \
"the SECOND write through the same mapping, announced by nothing|frame 1's write through the SAME mapping|cannot have pushed|PersistentCoherentMapScenario\.cpp:[0-9]+: Failure" \
MOBILEGL_IPC_PERSISTENT_BLOCK_KB=0
+119
View File
@@ -0,0 +1,119 @@
#!/bin/bash
# A stubbed `ctest` for scripts/ci/control_smoke_test.sh.
#
# Descended from the verification agent's stub (wave1-codex-verify.md 8, ~/w7/p5-verify-f8-stub/ctest),
# which is what CONFIRMED that the negative controls accepted an unrelated failure. Every mode below
# is deliberately the BEST case for the control under test: the selection is never empty except in
# the mode that exists to test the empty-selection guard, and the baseline is green except in the
# mode that exists to test the red-baseline guard. If a control passes here it is because the
# control's logic is wrong, not because the stub starved it.
#
# STUB_MODE:
# unrelated baseline green; the control's own run fails with UNRELATED_CONTROL_FAILURE
# evidence baseline green; the control's own run fails with the scenarios' own wording
# green baseline green; the control's own run PASSES (the knob is not load-bearing)
# red-baseline the baseline itself has a failed entry
# all-skipped the baseline is entirely skipped (the disarmed lane, a legitimate exit 0)
# retrace-noselect `ctest -N` matches nothing; the run exits 8 the way --no-tests=error does
# retrace-unrelated one match; the run fails without naming the transport
# retrace-evidence one match; the run fails with run_trace_case.cmake's own sentence
# retrace-green one match; the run PASSES
set -u
mode="${STUB_MODE:?STUB_MODE must be set}"
listing=1
junit=""
prev=""
for a in "$@"; do
[ "$a" = "-N" ] && listing_requested=1
if [ "$prev" = "--output-junit" ]; then junit="$a"; fi
prev="$a"
done
listing_requested="${listing_requested:-0}"
emit_listing() {
echo "Test project /stub"
if [ "${mode}" = "retrace-noselect" ]; then
echo "Total Tests: 0"
return
fi
echo " Test #1: DirectGLES.Split.ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels"
echo "Total Tests: 1"
}
write_junit() {
case "${mode}" in
red-baseline)
body='<testcase name="DirectGLES.Split.TriangleScenario.AVboBackedTriangleReachesReadPixels" status="failed"><failure message="already red"/></testcase>'
;;
all-skipped)
body='<testcase name="DirectGLES.Split.TriangleScenario.AVboBackedTriangleReachesReadPixels" status="notrun"><skipped/></testcase>'
;;
*)
body='<testcase name="DirectGLES.Split.TriangleScenario.AVboBackedTriangleReachesReadPixels" status="run" time="0.3"/>'
;;
esac
printf '%s\n' '<?xml version="1.0" encoding="UTF-8"?>' "<testsuite name=\"stub\">" " ${body}" '</testsuite>' > "$1"
}
if [ "${listing_requested}" = "1" ]; then
emit_listing
exit 0
fi
if [ -n "${junit}" ]; then
write_junit "${junit}"
case "${mode}" in
red-baseline) echo "1/1 Test #1: ... ***Failed"; exit 8 ;;
*) echo "100% tests passed, 0 tests failed out of 1"; exit 0 ;;
esac
fi
# The control's own run.
case "${mode}" in
unrelated)
echo "1/1 Test #1: DirectGLES.Split.ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels ...***Failed"
echo "UNRELATED_CONTROL_FAILURE: the harness aborted in setup before the knob was read"
exit 8
;;
evidence)
# Both controls' required wording, so one stub serves E1 and E3(a). Copied from the real
# diagnostics: ~/w7/p5-v1-joint-isplit-barrier0.log for the first, and
# PersistentCoherentMapScenario.cpp:414-417 for the second.
echo "1/1 Test #1: DirectGLES.Split.ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels ...***Failed"
echo "../MobileGL/MG_IntegrationTest/Scenarios/ClearThenReadPixelsScenario.cpp:290: Failure"
echo "Expected: (bottom.r) > (200), actual: '\\0' vs 200"
echo "the SECOND write through the same mapping, announced by nothing: this is exit gate E3(b)"
exit 8
;;
green)
echo "100% tests passed, 0 tests failed out of 1"
exit 0
;;
retrace-noselect)
echo "No tests were found!!!"
exit 8
;;
retrace-unrelated)
echo "1/1 Test #1: MobileGLTraceReplay.OpenRA.DirectGLES ...***Failed"
echo "CMake Error: the fixture could not be unpacked"
exit 8
;;
retrace-evidence)
echo "1/1 Test #1: MobileGLTraceReplay.OpenRA.DirectGLES ...***Failed"
echo "CMake Error at run_trace_case.cmake:279 (message):"
echo " MOBILEGL_TRANSPORT=inproc is set for OpenRA DirectGLES and the library never"
echo " reported resolving it: mobilegl.log carries no"
echo ' "MOBILEGL_TRANSPORT=inproc - the MGPipe record stream".'
exit 8
;;
retrace-green)
echo "100% tests passed, 0 tests failed out of 1"
exit 0
;;
*)
echo "stub_ctest: unknown STUB_MODE '${mode}'" >&2
exit 127
;;
esac
+70 -12
View File
@@ -1389,27 +1389,54 @@ def write(path, text, check, changed):
handle.write(text)
def expect_trip(name, fn):
"""Runs one negative control; a gate that lets it through is the failure."""
def expect_trip(name, because, fn, quiet=False):
"""Runs one negative control; a gate that lets it through, or trips for a reason that is
not ITS OWN, is the failure.
The first version of this function caught any SystemExit - including an unrelated
diagnostic, and even a CLEAN sys.exit(0) - and asked nothing about which one, so the
codex cross-family review's finding 9 could replace a control's callback with either and
the "nine negative controls all trip" line stayed true while the guard it named never
fired. gen_pipe_field_ownership.py's M-1 was the same defect in the sibling generator;
this mirrors that fix.
`because` is a substring the control's OWN message must contain, and an exit code of 0
can never count as a trip."""
try:
fn()
except SystemExit as trip:
print("gen_pipe: self-test %s: tripped as expected (%s)" % (name, str(trip).splitlines()[0][:100]))
return 1
print("gen_pipe: self-test %s: DID NOT TRIP" % name, file=sys.stderr)
code = trip.code
message = str(code) if code is not None else ""
if code != 0 and because in message:
if not quiet:
print("gen_pipe: self-test %s: tripped as expected (%s)"
% (name, message.splitlines()[0][:100]))
return 1
if not quiet:
print("gen_pipe: self-test %s: tripped for SOMEONE ELSE'S reason:\n"
" expected to contain: %s\n"
" actually said (code=%r): %s"
% (name, because, code, message.splitlines()[0][:200] if message else "<empty>"),
file=sys.stderr)
return 0
if not quiet:
print("gen_pipe: self-test %s: DID NOT TRIP" % name, file=sys.stderr)
return 0
def self_test(accessors):
"""The negative controls (check_include_closure.py's shape): each gate must go red for
its reason, and zero trips is itself an error."""
its OWN reason, and zero trips is itself an error."""
canned_struct = "struct Canned {\n Uint32 A;\n Uint32 B, C;\n Uint8 Pad0[3];\n void F() { return; }\n};\n"
controls = [
("struct member without F(...)",
"member(s) with no F(...) in PipeFields.def",
lambda: check_field_lists_cover_struct_members({"Canned": ["A", "B"]}, ["Canned"], [canned_struct])),
("F(...) that is not a member",
"F(...) name(s) that are not members",
lambda: check_field_lists_cover_struct_members({"Canned": ["A", "B", "C", "D"]}, ["Canned"], [canned_struct])),
("payload with no struct",
"struct not found",
lambda: check_field_lists_cover_struct_members({"Nowhere": ["A"]}, ["Nowhere"], [canned_struct])),
]
fill_text = read(os.path.join(PIPE_DIR, "FillPoints.def"))
@@ -1417,16 +1444,24 @@ def self_test(accessors):
field_row = re.compile(r"X\(\s*kDraw\s*,\s*GetBoundVertexArray\s*\)")
if not verb_row.search(fill_text) or not field_row.search(fill_text):
sys.exit("gen_pipe: self-test: FillPoints.def lost the rows the controls edit")
controls.append(("verb missing from FillPoints.def", lambda: parse_fill_points(
controls.append(("verb missing from FillPoints.def",
"GLFunctionsTable member(s) without a verb row",
lambda: parse_fill_points(
accessors, text=verb_row.sub("", fill_text, count=1))))
controls.append(("verb that is not a GLFunctionsTable member", lambda: parse_fill_points(
controls.append(("verb that is not a GLFunctionsTable member",
"verb(s) that are not GLFunctionsTable members",
lambda: parse_fill_points(
accessors, text=verb_row.sub("X(DrawArrays, kDraw) X(NotAVerb, kDraw)", fill_text, count=1))))
controls.append(("field row naming a non-accessor", lambda: parse_fill_points(
controls.append(("field row naming a non-accessor",
"is not an accessor in Coverage.def",
lambda: parse_fill_points(
accessors, text=field_row.sub("X(kDraw, NotAnAccessor)", fill_text, count=1))))
# The EMITTED list's own gate: a row naming a call that is not in PipeCalls.def would
# generate an enumerator nothing can dispatch on.
calls_for_control = parse_calls()
controls.append(("emitted row naming a call that does not exist", lambda: gen_emitted_by(
controls.append(("emitted row naming a call that does not exist",
"which is not a call in PipeCalls.def",
lambda: gen_emitted_by(
[("GetViewport", "SetDynamicState")], calls_for_control, [("GetViewport", "NotACall")])))
# P5 R-13.4's gate. The flags are now a GENERATED TABLE six packages read instead of six
# hard-coded copies, so a token that is not an MGPipeCallFlags enumerator has to stop the
@@ -1434,13 +1469,36 @@ def self_test(accessors):
# kNone, the empty set, may not be OR'd with a real flag and quietly read as one.
flag_typo = Call(1, "Canned", "MGPHandleOnly", "kScreen", ["kHasBlobb"])
flag_kNone = Call(1, "Canned", "MGPHandleOnly", "kScreen", ["kNone", "kHasBlob"])
flag_typo_because = "which is not an MGPipeCallFlags enumerator"
controls.append(("call flag that is not an MGPipeCallFlags enumerator",
flag_typo_because,
lambda: check_call_flags_are_known([flag_typo])))
controls.append(("kNone combined with a real flag",
"combines kNone with",
lambda: check_call_flags_are_known([flag_kNone])))
# R-16 meta-control (codex review finding 9; verified by execution in
# p5-results/wave1-codex-verify.md §9). The exact perturbation there replaced the
# flag-typo control's callback with `sys.exit("unrelated parser failure")` and,
# separately, with `sys.exit(0)`; the OLD expect_trip counted both as "tripped as
# expected" because it never looked at the message or the code. Both must be REJECTED
# here, under the real control's own `because`. This is a quiet, unlisted assertion (not
# one of the nine controls below) so it cannot itself inflate the trip count.
# "Red once by doing X" = reverting expect_trip to `except SystemExit as trip: return 1`
# (accept any SystemExit) - that alone turns each of these two checks from a silent pass
# into the sys.exit below.
if expect_trip("(R-16 meta-control) unrelated diagnostic standing in for the flag-typo guard",
flag_typo_because, lambda: sys.exit("unrelated parser failure"), quiet=True) != 0:
sys.exit("gen_pipe: self-test: expect_trip counted an UNRELATED SystemExit as the "
"flag-typo guard's own trip - finding 9 / R-16 is back")
if expect_trip("(R-16 meta-control) sys.exit(0) standing in for the flag-typo guard",
flag_typo_because, lambda: sys.exit(0), quiet=True) != 0:
sys.exit("gen_pipe: self-test: expect_trip counted sys.exit(0) as a trip - "
"finding 9 / R-16 is back")
trips = 0
for name, fn in controls:
trips += expect_trip(name, fn)
for name, because, fn in controls:
trips += expect_trip(name, because, fn)
# The positive control: the canned struct's exact list passes, and the parser sees the
# padding member as padding and the function as not a member.
check_field_lists_cover_struct_members({"Canned": ["A", "B", "C"]}, ["Canned"], [canned_struct])
+183 -31
View File
@@ -37,7 +37,20 @@
# FlushPendingRangesNow at all, so a gate that hashed only that name protected text the shipping
# build never sees, and a tier-threshold edit made in the ladder that DOES ship would pass it. So
# both names are hashed. The pull build's ladder is compared against the base ref; the push build's
# is compared against a sha pinned at the commit where it was reviewed - see PINNED_FUNCTIONS.
# is compared, ALWAYS AND WHETHER OR NOT <ref-a> DEFINES IT, against a sha pinned at the commit
# where that body was reviewed - see PINNED_FUNCTIONS.
#
# "ALWAYS" IS INTEGRATOR DECISION ID-41 AND IT IS A CHANGE. Until P5 this text said "pinned" while
# the implementation consulted the pin only as a FALLBACK, when <ref-a> did not define the function
# at all. At P3a's own base ref it does not, so the two readings agreed and nobody noticed; from
# ff2994d9 onward it does, so the fallback stopped firing and the row silently went back to being
# ref-a-vs-ref-b. ID-41 rules that the pin is the baseline, because the pin is the REVIEWED text:
# P5 (b1) gave this body two defaulted parameters (hostBaseFrom/hostBaseTo), a
# MOBILEGL_PIPE_VERIFY-only StageSnapshotTooNarrow log and a tier-1 access computation moved into
# InvalidateFlushAccessFor, the pull arm (FlushPendingRangesNow) is byte-identical across that
# change and G1 reports .text +0, so it is the intended change to the eleventh row rather than
# drift - and the answer is to RE-PIN it, not to revert it and not to let the row stop being
# compared. The OTHER TEN stay ref-a-vs-ref-b: nothing about them moved.
#
# THE TENTH IS HERE BY INTEGRATOR DECISION ID-11, resolving a contradiction inside the brief.
# D-F's "Decision: nine functions" table omits FlushPendingRangesNow, but BRIEF-P3A.md:420 calls it
@@ -88,12 +101,22 @@ set -u -o pipefail
SOURCE_PATH=MobileGL/MG_Backend/DirectGLES/Managers.cpp
# The ten that exist at the P3a base ref, so their baseline is read out of <ref-a>.
FUNCTIONS="IsPoolable EnrollIntoPool AcquireFromPool TrimBufferPool ClearBufferPool ProcessDeferredBufferReleases CreateRingStorage RingAvailable RingAllocate FlushPendingRangesNow"
# The ELEVENTH (ID-15), and it is a different kind of row: it was BORN in P3a, so there is no
# body at the base ref to compare it with and its baseline is PINNED below, captured at
# 3e298c9a - the commit at which the two-arm shape was reviewed and accepted.
# The ELEVENTH (ID-15), and it is a different kind of row: it was BORN in P3a, so there was no
# body at the base ref to compare it with, and its baseline is PINNED below and consulted
# UNCONDITIONALLY (ID-41 - see the long note at the top of this file).
#
# THE PIN, AND WHAT RE-PINS IT. Whoever moves this body deliberately replaces BOTH lines and
# writes the decision beside them; a pin with no commit and no decision next to it is a number
# nobody can audit.
# 3e298c9a 37fc94ff... ID-15, P3a: the two-arm shape, reviewed and accepted
# 3dadd4c1 172b0222... ID-41, P5 (b1): [Fix] (DirectGLES): make the extent hostBase is good
# for a parameter of the flush ladder, so tier 1's widening refusal is
# live code the moment a SEG_STAGE snapshot is narrower than the queued
# range. <- CURRENT
PINNED_FUNCTIONS="FlushPendingRangesFrom"
PINNED_BASELINE_REF=3e298c9a
PINNED_SHA_FlushPendingRangesFrom=37fc94ffc5991923d222d585daa3af6511d2352d255623026ce35a3b6963c4a6
PINNED_BASELINE_REF=3dadd4c1
PINNED_BASELINE_DECISION=ID-41
PINNED_SHA_FlushPendingRangesFrom=172b022273db01b16e772d15b269ffcd797fe38c767f7354d83ce113a66040d0
ALL_FUNCTIONS="$FUNCTIONS $PINNED_FUNCTIONS"
EXPECTED_FUNCTION_COUNT=11
# The functions the self-test perturbs, one control each. ClearBufferPool is small, has no forward
@@ -247,7 +270,7 @@ def extract(path, names):
return rows, problems
def perturb(src, dst, names, target):
def perturb(src, dst, names, target, one_token=False):
text = open(src, encoding='utf-8', newline='').read()
masked = mask(text)
hits = find_definition(text, masked, target)
@@ -260,9 +283,17 @@ def perturb(src, dst, names, target):
# perturbation somewhere that is not the body, and the control would be proving the wrong
# thing. Offsets are identical between the two by construction (mask() preserves length).
brace = masked.index('{', begin)
patched = (text[:brace + 1] +
'\n // p3a_untouched_regions.sh --self-test: a body that MOVED.\n' +
text[brace + 1:])
if one_token:
# ONE TOKEN - a single empty statement - and nothing else. ID-41(d) asks the pinned row's
# control to perturb the LADDER rather than a comment beside it, and this is the smallest
# edit that is unambiguously code: a reader cannot answer "the gate only notices comments".
# The perturbed copy is never compiled, only hashed, so an empty statement is legal here in
# a way it would not be in the tree.
patched = text[:brace + 1] + ';' + text[brace + 1:]
else:
patched = (text[:brace + 1] +
'\n // p3a_untouched_regions.sh --self-test: a body that MOVED.\n' +
text[brace + 1:])
open(dst, 'w', encoding='utf-8', newline='').write(patched)
return 0
@@ -282,8 +313,8 @@ def main(argv):
for sha, name in rows:
sys.stdout.write('%s %s\n' % (sha, name))
return 2 if problems else 0
if mode == 'perturb':
return perturb(argv[2], argv[3], names, argv[4])
if mode in ('perturb', 'perturb-token'):
return perturb(argv[2], argv[3], names, argv[4], mode == 'perturb-token')
sys.stderr.write('[p3a-untouched] unknown mode %r\n' % mode)
return 2
@@ -304,38 +335,74 @@ extract_ref() {
return $?
}
# The BASELINE side (<ref-a>). Same extraction, with one difference that the eleventh row makes
# necessary: a function born in P3a has no body at the P3a base ref, and CI passes exactly that
# ref as <ref-a>. Asking for it there is not "the gate could not run" - it is the expected
# answer - so a name in PINNED_FUNCTIONS that is missing at <ref-a> takes the sha pinned at the
# top of this script instead. Everything else still has to be found: the fallback is entered
# only after a strict extraction failed, and it then re-extracts the pre-P3a ten strictly, so a
# genuine rename of one of THOSE is still exit 2 rather than a silently short list.
# True when $1 is one of the rows whose baseline is the PIN rather than <ref-a>.
is_pinned_row() {
local name candidate
for candidate in $PINNED_FUNCTIONS; do
[ "$candidate" = "$1" ] && return 0
done
return 1
}
# Overwrite the pinned rows of a `<sha> <name>` list with the shas PINNED at the top of this
# script. ONE spelling of the substitution, called both by extract_baseline and by --self-test's
# pin controls, so the control drives the gate's own path instead of re-spelling it - a control
# that re-implements what it checks proves only that the copy agrees with itself.
apply_pinned_shas() {
local file=$1 name pinned
for name in $PINNED_FUNCTIONS; do
eval "pinned=\$PINNED_SHA_$name"
if [ -z "$pinned" ] || [ "$pinned" = "PLACEHOLDER_SHA" ]; then
say "$name has no pinned baseline sha; the eleventh row cannot be compared"
return 2
fi
grep -v " $name\$" "$file" > "$file.unpinned" || true
mv -f "$file.unpinned" "$file" || return 2
# Appended LAST, which is also its position in the fixed order (it is the eleventh of eleven),
# so the header's "stdout is always the sha list in the fixed order" stays true and a baseline
# captured by redirect still diffs cleanly against a two-ref run.
printf '%s %s\n' "$pinned" "$name" >> "$file"
done
return 0
}
# The BASELINE side (<ref-a>). The TEN are extracted strictly, so a rename of one of THOSE is
# exit 2 rather than a silently short list. FlushPendingRangesFrom then takes the PINNED sha
# WHETHER OR NOT <ref-a> defines it (ID-41), and a <ref-a> that defines it DIFFERENTLY is
# reported - loudly - because the two answers disagreeing is itself a finding rather than a
# reason to prefer the ref.
extract_baseline() {
local ref=$1 out=$2 blob="$WORK_DIR/$2.cpp" name sha
local ref=$1 out=$2 blob="$WORK_DIR/$2.cpp" name pinned atRef
if ! git show "$ref:$SOURCE_PATH" > "$blob" 2>"$WORK_DIR/show.err"; then
say "cannot read $SOURCE_PATH at '$ref':"
sed 's/^/[p3a-untouched] /' "$WORK_DIR/show.err" >&2
return 2
fi
if python3 "$PY" extract "$blob" "$ALL_FUNCTIONS" > "$WORK_DIR/$out.sha" 2>"$WORK_DIR/$out.err"; then
return 0
fi
if ! python3 "$PY" extract "$blob" "$FUNCTIONS" > "$WORK_DIR/$out.sha"; then
if ! python3 "$PY" extract "$blob" "$FUNCTIONS" > "$WORK_DIR/$out.sha" 2>"$WORK_DIR/$out.err"; then
say "the baseline ref '$ref' does not define the pre-P3a ten exactly once each:"
sed 's/^/[p3a-untouched] /' "$WORK_DIR/$out.err" >&2
return 2
fi
for name in $PINNED_FUNCTIONS; do
eval "sha=\$PINNED_SHA_$name"
if [ -z "$sha" ] || [ "$sha" = "PLACEHOLDER_SHA" ]; then
say "$name has no pinned baseline sha; the eleventh row cannot be compared"
return 2
eval "pinned=\$PINNED_SHA_$name"
atRef=$(python3 "$PY" extract "$blob" "$name" 2>/dev/null | awk -v n="$name" '$2 == n { print $1 }')
if [ -z "$atRef" ]; then
say "$name is not defined at '$ref' (it was born in P3a): its baseline is the sha PINNED in"
say " this script, captured at $PINNED_BASELINE_REF ($PINNED_BASELINE_DECISION)"
elif [ "$atRef" != "$pinned" ]; then
say "NOTE: $name IS defined at '$ref' and hashes"
say " $atRef, which is not the pin"
say " ($pinned,"
say " captured at $PINNED_BASELINE_REF, $PINNED_BASELINE_DECISION). THE PIN IS WHAT IS"
say " COMPARED - it is the reviewed body - and this note is not a verdict in either"
say " direction. If '$ref' PREDATES $PINNED_BASELINE_REF the two SHOULD disagree: the pinned"
say " body is the change $PINNED_BASELINE_DECISION admitted, which is why it was re-pinned"
say " rather than reverted. If it does not predate it, the ladder that ships has moved away"
say " from the reviewed text without this gate being re-pinned - re-pin deliberately or"
say " revert, but do not leave them disagreeing."
fi
printf '%s %s\n' "$sha" "$name" >> "$WORK_DIR/$out.sha"
say "$name is not defined at '$ref' (it was born in P3a): its baseline is the sha PINNED in"
say " this script, captured at $PINNED_BASELINE_REF"
done
apply_pinned_shas "$WORK_DIR/$out.sha" || return 2
return 0
}
@@ -350,6 +417,16 @@ compare_lists() {
say "FIRST FUNCTION THAT MOVED: $name"
say " $labelA $shaA"
say " $labelB ${shaB:-<not found>}"
if is_pinned_row "$name"; then
# ITS OWN MESSAGE, and that is R-16 rather than decoration: the pinned row and the ten
# ref-a rows fail differently and are fixed differently, so a reader who sees only the
# generic paragraph below goes looking for a diff against <ref-a> that does not exist.
say " $name IS A PINNED ROW ($PINNED_BASELINE_DECISION): its baseline is ALWAYS the sha"
say " PINNED in this script - the body reviewed at $PINNED_BASELINE_REF - and never the"
say " body at '$labelA'. So this is not a diff against the base ref: the ladder that"
say " SHIPS has moved away from the text that was reviewed. Either re-pin deliberately,"
say " replacing the sha AND the commit AND the decision beside it, or revert the body."
fi
say " G5 (ARCHITECTURE.md:316, :515) says the buffer pool, the deferred-release drain, the"
say " three rings and BOTH arms of the three-tier flush drain - FlushPendingRangesNow in"
say " the pull build, FlushPendingRangesFrom in the push build (BRIEF-P3A.md:420, :1708,"
@@ -436,6 +513,81 @@ if [ "${1:-}" = "--self-test" ]; then
fi
say "negative control: a perturbed $target body is reported, and named"
done
# --- THE PINNED ROW (ID-41) -----------------------------------------------------------------
# TWO more controls, and they exist because none of the five above can see the pin at all: every
# one of them compares one extraction of the working tree against another, so they would all be
# green on a build of this script in which PINNED_SHA_* was never read by anything. The eleventh
# row's whole claim is "the baseline is the PIN, not <ref-a>", and that claim needs its own two.
for target in $PINNED_FUNCTIONS; do
eval "pinned=\$PINNED_SHA_$target"
# (1) PIN PRECEDENCE, positive. A baseline that carries some OTHER sha for the pinned row -
# which is the shape of every <ref-a> CI passes today, since ff2994d9 and 37da3c3a both DEFINE
# FlushPendingRangesFrom - must come out of apply_pinned_shas carrying the PIN. This is the
# control that would have caught the ID-41 defect itself: before it, the pin was consulted
# only when <ref-a> lacked the function, so the row silently reverted to ref-a-vs-ref-b the
# moment a base ref had one.
grep -v " $target\$" "$WORK_DIR/pristine.sha" > "$WORK_DIR/pinprec.sha" || true
printf '%s %s\n' \
"0000000000000000000000000000000000000000000000000000000000000000" "$target" \
>> "$WORK_DIR/pinprec.sha"
apply_pinned_shas "$WORK_DIR/pinprec.sha" || exit 2
got=$(awk -v n="$target" '$2 == n { print $1 }' "$WORK_DIR/pinprec.sha")
if [ "$got" != "$pinned" ]; then
say "PIN CONTROL FAILED: a baseline that carried a DIFFERENT sha for $target came out as"
say " '${got:-<absent>}' and not as the pin ($pinned). The eleventh row would be compared"
say " against <ref-a> again, which is exactly the defect $PINNED_BASELINE_DECISION closed."
exit 2
fi
say "pin control: a baseline that defines $target differently is overridden by the PIN"
# (2) A ONE-TOKEN EDIT TO THE PINNED LADDER, negative, AGAINST THE PIN. The comparison must go
# red, must name the row, and must say that the row is PINNED - R-16's "a control asserts its
# OWN failure string": the pinned row and the ten ref-a rows are fixed differently, and a
# reader who gets only the generic paragraph goes looking for a diff against <ref-a> that does
# not exist.
python3 "$PY" perturb-token "$WORK_DIR/pristine.cpp" "$WORK_DIR/pinperturbed.cpp" \
"$target" "$ALL_FUNCTIONS" || exit 2
python3 "$PY" extract "$WORK_DIR/pinperturbed.cpp" "$ALL_FUNCTIONS" \
> "$WORK_DIR/pinperturbed.sha" || exit 2
cp -f "$WORK_DIR/pristine.sha" "$WORK_DIR/pinbase.sha" || exit 2
apply_pinned_shas "$WORK_DIR/pinbase.sha" || exit 2
if compare_lists "$WORK_DIR/pinbase.sha" "$WORK_DIR/pinperturbed.sha" \
"PIN($PINNED_BASELINE_REF)" "one-token-perturbed" 2> "$WORK_DIR/pinperturbed.err"; then
say "NEGATIVE CONTROL DID NOT TRIP: one token was inserted into $target's body and the"
say "comparison AGAINST THE PIN still reported every function as identical. The pinned row is"
say "not being compared at all, so every green this gate has printed for it means nothing."
exit 2
fi
if ! grep -q "FIRST FUNCTION THAT MOVED: $target" "$WORK_DIR/pinperturbed.err"; then
say "NEGATIVE CONTROL TRIPPED FOR THE WRONG REASON: the comparison against the pin went red"
say "but did not name $target as the first function that moved. It said:"
sed 's/^/[p3a-untouched] /' "$WORK_DIR/pinperturbed.err" >&2
exit 2
fi
if ! grep -q "$target IS A PINNED ROW" "$WORK_DIR/pinperturbed.err"; then
say "NEGATIVE CONTROL TRIPPED FOR THE WRONG REASON: it went red and named $target, but did"
say "not say that this row's baseline is the PIN. That is the half a reader acts on, and a"
say "control that does not assert its own message is not a control (R-16). It said:"
sed 's/^/[p3a-untouched] /' "$WORK_DIR/pinperturbed.err" >&2
exit 2
fi
say "negative control: a ONE-TOKEN edit to the pinned $target body goes red AGAINST THE PIN,"
say " is named, and says the row is pinned"
# NOT a failure, deliberately: --self-test is about whether the comparison works, and it runs
# on the WORKING tree, which may legitimately carry an uncommitted edit. The two-ref gate is
# what fails when the committed ladder has left the pin. But say so, because a self-test that
# was green on a tree whose ladder no longer matches its pin is confusing in exactly one
# direction.
treeSha=$(awk -v n="$target" '$2 == n { print $1 }' "$WORK_DIR/pristine.sha")
if [ "$treeSha" != "$pinned" ]; then
say "NOTE: this working tree's $target hashes $treeSha, not the pin ($pinned). The"
say " self-test's verdict is unaffected; the two-ref gate will be RED until you re-pin or"
say " revert."
fi
done
say "self-test passed"
exit 0
fi
+181 -34
View File
@@ -110,12 +110,21 @@
# next reader of this file meets them:
# D-N/1 the namespace region kind, above.
# D-N/2 the PINNED baseline is CONSULTED UNCONDITIONALLY for FlushPendingRangesFrom, where the
# parent consults it only when <ref-a> does not define the function. D-N says that row is
# compared "against its pinned 3e298c9a sha", and at P4a's base ref the function DOES
# exist - so the parent's fallback would silently never fire and the pin would stop being
# the baseline the brief names. Both readings agree on this tree (measured: the body at
# 37da3c3a hashes to the pinned value); where they would ever disagree, this script says
# so on stderr and keeps the PIN, because the pin is the reviewed text.
# parent consulted it only when <ref-a> did not define the function. D-N says that row is
# compared "against its pinned sha", and at P4a's base ref the function DOES exist - so
# the parent's fallback would silently never fire and the pin would stop being the
# baseline the brief names. INTEGRATOR DECISION ID-41 has since made this the parent's
# reading too, so D-N/2 is no longer a deviation between the two scripts; it is kept here
# as the record of why this one got there first.
#
# The two answers now DISAGREE on every ref CI passes, and that is the expected state
# rather than a finding: P5 (b1) re-parameterised the shipping ladder
# (hostBaseFrom/hostBaseTo, a MOBILEGL_PIPE_VERIFY-only StageSnapshotTooNarrow log, the
# tier-1 access computation moved into InvalidateFlushAccessFor), the PULL arm is
# byte-identical across that change and G1 reports .text +0, and ID-41 ruled that the row
# be RE-PINNED on the reviewed P5 body rather than reverted or quietly left comparing
# against <ref-a>. This script says so on stderr, in both directions, and keeps the PIN -
# because the pin is the reviewed text.
set -u -o pipefail
# One row per region: <name>@<kind>@<path>. The ORDER is the fixed order the sha list is printed
@@ -143,11 +152,20 @@ ShouldUseCaveatTextureFormat@function@MobileGL/MG_Backend/DirectGLES/Utils.cpp"
EXPECTED_FUNCTION_COUNT=17
# The one region born in P3a, so there is no body at P4a's base ref that this phase reviewed: its
# baseline is the sha captured at 3e298c9a, the commit at which the two-arm shape was reviewed and
# accepted (ID-15). See DEVIATIONS D-N/2 for why it is consulted unconditionally.
# baseline is PINNED and consulted UNCONDITIONALLY (DEVIATIONS D-N/2, and ID-41 for the parent).
#
# THE PIN, AND WHAT RE-PINS IT. Whoever moves this body deliberately replaces BOTH lines and
# writes the decision beside them; a pin with no commit and no decision next to it is a number
# nobody can audit. The parent script carries the identical table and the two must not drift.
# 3e298c9a 37fc94ff... ID-15, P3a: the two-arm shape, reviewed and accepted
# 3dadd4c1 172b0222... ID-41, P5 (b1): [Fix] (DirectGLES): make the extent hostBase is good
# for a parameter of the flush ladder, so tier 1's widening refusal is
# live code the moment a SEG_STAGE snapshot is narrower than the queued
# range. <- CURRENT
PINNED_FUNCTIONS="FlushPendingRangesFrom"
PINNED_BASELINE_REF=3e298c9a
PINNED_SHA_FlushPendingRangesFrom=37fc94ffc5991923d222d585daa3af6511d2352d255623026ce35a3b6963c4a6
PINNED_BASELINE_REF=3dadd4c1
PINNED_BASELINE_DECISION=ID-41
PINNED_SHA_FlushPendingRangesFrom=172b022273db01b16e772d15b269ffcd797fe38c767f7354d83ce113a66040d0
# The regions the self-test perturbs, one negative control each. FOUR, exactly as D-N requires, and
# each is a different shape so that a control which only ever perturbed the easy one cannot leave
@@ -372,7 +390,7 @@ def extract(rows):
def perturb(rows, target, src, dst, where='head'):
"""Insert one line into a region's body, at its HEAD or at its TAIL.
"""Insert one line into a region's body at its HEAD or its TAIL, or one TOKEN at its head.
TWO POSITIONS, AND THE SECOND ONE IS REVIEW FINDING F-m2. Every control used to insert at the
very first byte after the opening brace, so all four of them would still have tripped if
@@ -393,7 +411,15 @@ def perturb(rows, target, src, dst, where='head'):
% (target, len(hits)))
return 2
begin, end = hits[0]
if where == 'tail':
if where == 'token':
# ONE TOKEN - a single empty statement at the head of the body - and nothing else.
# ID-41(d) asks the pinned row's control to perturb the LADDER rather than a comment
# beside it, and this is the smallest edit that is unambiguously code: a reader cannot
# answer "the gate only notices comments". The perturbed copy is never compiled, only
# hashed, so an empty statement is legal here in a way it would not be in the tree.
brace = masked.index('{', begin)
patched = text[:brace + 1] + ';' + text[brace + 1:]
elif where == 'tail':
# end is one PAST the closing brace (find_function / find_namespace both return
# `match_forward(...) + 1`), so end - 1 is the brace itself and this lands inside the
# body, one character before it ends.
@@ -495,31 +521,59 @@ extract_baseline() {
sed 's/^/[p4a-untouched] /' "$WORK_DIR/$out.err" >&2
return 2
fi
for name in $PINNED_FUNCTIONS; do
eval "pinned=\$PINNED_SHA_$name"
grep "^$name$(printf '\t')" "$WORK_DIR/$out.spec.all" > "$WORK_DIR/$out.spec.pinned" || true
atRef=$(python3 "$PY" extract "$WORK_DIR/$out.spec.pinned" 2>/dev/null | awk '{ print $1 }')
if [ -n "$atRef" ] && [ "$atRef" != "$pinned" ]; then
say "NOTE: $name IS defined at '$ref' and hashes"
say " $atRef, which is not the pin"
say " ($pinned,"
say " captured at $PINNED_BASELINE_REF, $PINNED_BASELINE_DECISION). THE PIN IS WHAT IS"
say " COMPARED - it is the reviewed body - and this note is not a verdict in either"
say " direction. If '$ref' PREDATES $PINNED_BASELINE_REF the two SHOULD disagree: the pinned"
say " body is the change $PINNED_BASELINE_DECISION admitted, which is why it was re-pinned"
say " rather than reverted. If it does not predate it, the ladder that ships has moved away"
say " from the reviewed text without this gate being re-pinned - re-pin deliberately or"
say " revert, but do not leave them disagreeing."
fi
done
# The pinned rows are appended and the list is put back into the FIXED ORDER, both inside
# apply_pinned_shas - ONE spelling of the substitution, which --self-test's pin controls drive
# as well, so a control cannot prove only that a copy of the logic agrees with itself.
apply_pinned_shas "$WORK_DIR/$out.sha" || return 2
return 0
}
# True when $1 is one of the rows whose baseline is the PIN rather than <ref-a>.
is_pinned_row() {
local name candidate
for candidate in $PINNED_FUNCTIONS; do
[ "$candidate" = "$1" ] && return 0
done
return 1
}
# Overwrite the pinned rows of a `<sha> <region>` list with the shas PINNED at the top of this
# script, then restore the FIXED ORDER (review F-m1: the pinned rows would otherwise be emitted
# LAST while extract_ref emits everything in REGIONS order. The gate itself never noticed -
# compare_lists looks rows up by name - but the documented capture workflow did: the header
# promises "stdout is always the sha list ... in the fixed order above - so a baseline capture is a
# plain redirect", and a baseline captured that way then diffed against a two-ref stdout showed
# seven spurious differences purely from row order).
apply_pinned_shas() {
local file=$1 name pinned
for name in $PINNED_FUNCTIONS; do
eval "pinned=\$PINNED_SHA_$name"
if [ -z "$pinned" ] || [ "$pinned" = "PLACEHOLDER_SHA" ]; then
say "$name has no pinned baseline sha; that row cannot be compared"
return 2
fi
grep "^$name$(printf '\t')" "$WORK_DIR/$out.spec.all" > "$WORK_DIR/$out.spec.pinned" || true
atRef=$(python3 "$PY" extract "$WORK_DIR/$out.spec.pinned" 2>/dev/null | awk '{ print $1 }')
if [ -n "$atRef" ] && [ "$atRef" != "$pinned" ]; then
say "NOTE: $name IS defined at '$ref' and hashes $atRef, which is NOT the sha pinned in this"
say " script ($pinned, captured at $PINNED_BASELINE_REF). The PIN is what is compared - it is"
say " the reviewed text (ID-15) - but the two disagreeing means the push ladder moved between"
say " $PINNED_BASELINE_REF and '$ref' without this gate being re-pinned. Re-pin deliberately or"
say " revert; do not leave them disagreeing."
fi
printf '%s %s\n' "$pinned" "$name" >> "$WORK_DIR/$out.sha"
grep -v " $name\$" "$file" > "$file.unpinned" || true
mv -f "$file.unpinned" "$file" || return 2
printf '%s %s\n' "$pinned" "$name" >> "$file"
done
# ...and put the list back into the FIXED ORDER (review F-m1). The pinned rows were stripped out
# of the spec above and appended here, so without this the baseline side emits them LAST while
# extract_ref emits everything in REGIONS order. The gate itself never noticed - compare_lists
# looks rows up by name - but the documented capture workflow did: the header promises "stdout is
# always the sha list ... in the fixed order above - so a baseline capture is a plain redirect",
# and a baseline captured that way then diffed against a two-ref stdout showed seven spurious
# differences purely from row order.
reorder_sha_list "$WORK_DIR/$out.sha" || return 2
reorder_sha_list "$file" || return 2
return 0
}
@@ -553,6 +607,18 @@ compare_lists() {
say "FIRST REGION THAT MOVED: $name"
say " $labelA ${shaA:-<not found>}"
say " $labelB ${shaB:-<not found>}"
if is_pinned_row "$name"; then
# ITS OWN MESSAGE, and that is R-16 rather than decoration: the pinned row and the
# sixteen ref-a rows fail differently and are fixed differently, so a reader who sees
# only the generic paragraph below goes looking for a diff against <ref-a> that does not
# exist.
say " $name IS A PINNED ROW ($PINNED_BASELINE_DECISION): its baseline is ALWAYS the sha"
say " PINNED in this script - the body reviewed at $PINNED_BASELINE_REF - and never the"
say " body at '$labelA'. So this is not a diff against the base ref: the ladder that"
say " SHIPS has moved away from the text that was reviewed. Either re-pin deliberately,"
say " replacing the sha AND the commit AND the decision beside it in BOTH this script"
say " and its parent scripts/p3a_untouched_regions.sh, or revert the body."
fi
say " G5 (ARCHITECTURE.md:318, :321, :515) says the Espryt do-not-touch list is literal:"
say " P3a's buffer pool, deferred-release drain, three rings and BOTH arms of the three-tier"
say " flush drain, plus P4a's unpack-PBO staging repack and its two ring helpers, the"
@@ -573,10 +639,12 @@ compare_lists() {
# --- self-test ------------------------------------------------------------------------------
# A gate that always says "identical" and a gate that is working produce the same green, so the
# comparison has to be shown failing. Both controls run: the POSITIVE ones (an untouched copy
# compares equal; an edit OUTSIDE the regions is invisible) rule out a comparison that reports
# every region as moved, and the eight NEGATIVE ones - D-N's four regions, each perturbed at the
# HEAD of its body and again at its TAIL - rule out both the comparison that never reports any and
# the extraction whose extent stops before the closing brace (F-m2).
# compares equal; an edit OUTSIDE the regions is invisible; a baseline that names another sha for
# the PINNED row is overridden by the pin) rule out a comparison that reports every region as
# moved, and the NINE NEGATIVE ones - D-N's four regions, each perturbed at the HEAD of its body
# and again at its TAIL, plus a ONE-TOKEN edit to the pinned ladder compared AGAINST THE PIN
# (ID-41) - rule out the comparison that never reports any, the extraction whose extent stops
# before the closing brace (F-m2), and a pin that nothing consults.
if [ "${1:-}" = "--self-test" ]; then
[ $# -eq 1 ] || { say "--self-test takes no other arguments"; exit 2; }
mkdir -p "$WORK_DIR/pristine" || exit 2
@@ -678,6 +746,85 @@ if [ "${1:-}" = "--self-test" ]; then
say "its tail), ran $controls"
exit 2
fi
# --- THE PINNED ROW (ID-41) -----------------------------------------------------------------
# TWO more controls, and they exist because none of the ten above can see the pin at all: every
# one of them compares one extraction of the working tree against another, so they would all be
# green on a build of this script in which PINNED_SHA_* was never read by anything. The pinned
# row's whole claim is "the baseline is the PIN, not <ref-a>" (D-N/2, ID-41), and that claim
# needs its own two.
for target in $PINNED_FUNCTIONS; do
eval "pinned=\$PINNED_SHA_$target"
targetSource=$(printf '%s\n' "$REGIONS" | awk -F@ -v n="$target" '$1 == n { print $3 }')
[ -n "$targetSource" ] || { say "$target is not one of the regions"; exit 2; }
# (1) PIN PRECEDENCE, positive. A baseline that carries some OTHER sha for the pinned row -
# which is the shape of every <ref-a> CI passes, since 37da3c3a DOES define
# FlushPendingRangesFrom and no longer hashes the pin - must come out of apply_pinned_shas
# carrying the PIN.
grep -v " $target\$" "$WORK_DIR/pristine.sha" > "$WORK_DIR/pinprec.sha" || true
printf '%s %s\n' \
"0000000000000000000000000000000000000000000000000000000000000000" "$target" \
>> "$WORK_DIR/pinprec.sha"
apply_pinned_shas "$WORK_DIR/pinprec.sha" || exit 2
got=$(awk -v n="$target" '$2 == n { print $1 }' "$WORK_DIR/pinprec.sha")
if [ "$got" != "$pinned" ]; then
say "PIN CONTROL FAILED: a baseline that carried a DIFFERENT sha for $target came out as"
say " '${got:-<absent>}' and not as the pin ($pinned). That row would be compared against"
say " <ref-a> again, which is exactly what D-N/2 and $PINNED_BASELINE_DECISION forbid."
exit 2
fi
say "pin control: a baseline that defines $target differently is overridden by the PIN"
# (2) A ONE-TOKEN EDIT TO THE PINNED LADDER, negative, AGAINST THE PIN. The comparison must go
# red, must name the region, and must say the region is PINNED - R-16's "a control asserts its
# OWN failure string": the pinned row and the sixteen ref-a rows are fixed differently, and a
# reader who gets only the generic paragraph goes looking for a diff against <ref-a> that does
# not exist.
rm -rf "$WORK_DIR/pinperturbed"
cp -r "$WORK_DIR/pristine" "$WORK_DIR/pinperturbed" || exit 2
write_spec "$WORK_DIR/pinperturbed" "$WORK_DIR/pinperturbed.spec"
python3 "$PY" perturb "$WORK_DIR/pinperturbed.spec" "$target" \
"$WORK_DIR/pristine/$(blob_name "$targetSource")" \
"$WORK_DIR/pinperturbed/$(blob_name "$targetSource")" token || exit 2
python3 "$PY" extract "$WORK_DIR/pinperturbed.spec" > "$WORK_DIR/pinperturbed.sha" || exit 2
cp -f "$WORK_DIR/pristine.sha" "$WORK_DIR/pinbase.sha" || exit 2
apply_pinned_shas "$WORK_DIR/pinbase.sha" || exit 2
if compare_lists "$WORK_DIR/pinbase.sha" "$WORK_DIR/pinperturbed.sha" \
"PIN($PINNED_BASELINE_REF)" "one-token-perturbed" 2> "$WORK_DIR/pinperturbed.err"; then
say "NEGATIVE CONTROL DID NOT TRIP: one token was inserted into $target's body and the"
say "comparison AGAINST THE PIN still reported every region as identical. The pinned row is"
say "not being compared at all, so every green this gate has printed for it means nothing."
exit 2
fi
if ! grep -q "FIRST REGION THAT MOVED: $target" "$WORK_DIR/pinperturbed.err"; then
say "NEGATIVE CONTROL TRIPPED FOR THE WRONG REASON: the comparison against the pin went red"
say "but did not name $target as the first region that moved. It said:"
sed 's/^/[p4a-untouched] /' "$WORK_DIR/pinperturbed.err" >&2
exit 2
fi
if ! grep -q "$target IS A PINNED ROW" "$WORK_DIR/pinperturbed.err"; then
say "NEGATIVE CONTROL TRIPPED FOR THE WRONG REASON: it went red and named $target, but did"
say "not say that this row's baseline is the PIN. That is the half a reader acts on, and a"
say "control that does not assert its own message is not a control (R-16). It said:"
sed 's/^/[p4a-untouched] /' "$WORK_DIR/pinperturbed.err" >&2
exit 2
fi
controls=$((controls + 1))
say "negative control $controls: a ONE-TOKEN edit to the pinned $target body goes red AGAINST"
say " THE PIN, is named, and says the row is pinned"
# NOT a failure, deliberately: --self-test is about whether the comparison works, and it runs
# on the WORKING tree, which may legitimately carry an uncommitted edit. The two-ref gate is
# what fails when the committed region has left the pin.
treeSha=$(awk -v n="$target" '$2 == n { print $1 }' "$WORK_DIR/pristine.sha")
if [ "$treeSha" != "$pinned" ]; then
say "NOTE: this working tree's $target hashes $treeSha, not the pin ($pinned). The"
say " self-test's verdict is unaffected; the two-ref gate will be RED until you re-pin or"
say " revert."
fi
done
say "self-test passed: $controls negative controls, all tripped and all named"
exit 0
fi