Files
proffesor-for-testing__agen…/scripts/embed-verification-matrix.sh
Dragan Spiridonov 4f340bc183 feat(verification): chaos workflow + VERIFICATION.md + matrix embed (#409, #410)
Closes the two remaining init-corpus follow-ups from #401 in one PR:
makes the verification layer visible to humans (#409) and adds
adversarial-rare coverage to complement the everyday-real release
gate (#410).

# #410 — Chaos workflow

Weekly cron-driven workflow that runs aqe init against 6 pathological
project shapes the everyday-real corpus deliberately doesn't cover.
Per-shape assertion is "exit anything except 124" — the watchdog is
the load-bearing thing under test, not init's ability to make sense
of garbage input. Fail-loudly in the Actions tab; no auto-issue
creation, matching the #411 mirror-test pattern.

Six shapes: utf16le-bom, mixed-line-endings, symlink-loop,
binary-as-text, minified-bundle, control-chars. Generated at runtime
into a tmpdir by tests/fixtures/init-chaos/generate.sh — synthetic
generation is appropriate here per the chaos README's "Why synthetic
generation here" section: chaos tests need controlled adversarial
inputs that don't exist in any real public repo.

Verified locally: ran the generator + ran aqe init against all 6
shapes. All 6 pass with rc=0 against the current build, proving the
generator produces valid shapes and the existing watchdog handles
them today. The workflow exists to catch the day a future change
breaks one of these paths.

# #409 — VERIFICATION.md + matrix embed

New docs/VERIFICATION.md is the maintainer-facing entry point: gate
architecture diagram, how to interpret a failed gate, how to add a
new fixture (links to corpus README), how to embed the verification
matrix into a release notes file.

scripts/embed-verification-matrix.sh downloads the init-corpus-logs
artifact from a given workflow run, parses summary.txt, and emits a
markdown matrix. Status-only (option C from the proposal) — Time and
KG-entries columns are tracked as future work because the gate's
current summary.txt doesn't carry that data and extending it would
touch load-bearing infrastructure that's out of scope.

docs/policies/release-verification.md is fully rewritten around the
post-#401 reality: the manual aqe init checklist that pre-dated the
corpus is removed (the gate now does what it tried to do, with
22 assertions per fixture × 4 fixtures × the load-bearing doubleInit
flag). The Version Update Policy + Workflow sections are preserved
intact — they cover the one class of pre-release error the gate
cannot catch.

CONTRIBUTING.md links to the new VERIFICATION.md from the
Documentation section.

# Housekeeping

Removes 3 stale rows from tests/fixtures/init-corpus/README.md's
deferred-items table: #408 (already merged), #409 and #410 (this
PR). Only #407 remains — its 4 trigger conditions still haven't
fired, so it stays deferred per its own definition.

# Verification of this PR (per #401 rules)

- actionlint passed on .github/workflows/init-chaos.yml (#408 lesson:
  js-yaml is not enough for workflow files)
- bash -n on both new shell scripts
- generator ran cleanly and produced all 6 shapes with the expected
  byte content (verified BOM, line endings, symlink targets, PNG
  signature, single-line bundle size, embedded NULs)
- embed script ran against synthetic summary files (PASS-only and
  mixed PASS/FAIL) — both render correctly
- live smoke test: ran aqe init against all 6 chaos shapes locally
  using the globally-installed aqe binary, all 6 exited rc=0 within
  the watchdog budget
- every link in docs/VERIFICATION.md and docs/policies/release-
  verification.md verified to point at a file that exists

Closes #409
Closes #410

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 09:52:29 +00:00

147 lines
4.0 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# scripts/embed-verification-matrix.sh
#
# Generates the verification matrix for a release notes file from the
# init-corpus gate's summary.txt artifact. Run this after npm-publish.yml
# completes for a tag, then redirect or paste the output into the
# corresponding docs/releases/vX.Y.Z.md file.
#
# Status-only matrix per #409 option C — Time and KG-entries columns are
# future work. The gate's summary.txt doesn't carry that data today, so
# populating richer columns would require either parsing per-fixture
# JSON files or extending run-gate.sh, both of which are out of scope.
#
# Usage:
# ./scripts/embed-verification-matrix.sh <run-id>
# downloads the init-corpus-logs artifact from the given workflow run,
# parses summary.txt, prints a markdown matrix to stdout
#
# ./scripts/embed-verification-matrix.sh --from-file <path-to-summary.txt>
# parses an existing summary.txt directly (for local testing)
#
# Exit codes:
# 0 matrix printed
# 1 usage error or required tool missing
# 2 artifact download failed
# 3 summary.txt missing from artifact
#
# Refs: https://github.com/proffesor-for-testing/agentic-qe/issues/409
set -euo pipefail
REPO_SLUG="proffesor-for-testing/agentic-qe"
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <run-id>" >&2
echo " $0 --from-file <path>" >&2
exit 1
fi
# Parse args
SUMMARY_FILE=""
RUN_ID=""
case "$1" in
--from-file)
if [[ $# -ne 2 ]]; then
echo "Usage: $0 --from-file <path>" >&2
exit 1
fi
SUMMARY_FILE="$2"
if [[ ! -f "$SUMMARY_FILE" ]]; then
echo "ERROR: summary file not found: $SUMMARY_FILE" >&2
exit 1
fi
;;
*)
RUN_ID="$1"
;;
esac
# Tool guards (only when downloading from GH)
if [[ -z "$SUMMARY_FILE" ]]; then
for tool in gh; do
if ! command -v "$tool" >/dev/null 2>&1; then
echo "ERROR: required tool '$tool' not found in PATH" >&2
exit 1
fi
done
fi
# Download artifact if we don't already have a summary file
if [[ -z "$SUMMARY_FILE" ]]; then
TMP_DIR=$(mktemp -d -t aqe-matrix-XXXXXX)
trap 'rm -rf "$TMP_DIR"' EXIT
echo "[matrix] downloading init-corpus-logs artifact from run $RUN_ID..." >&2
if ! gh run download "$RUN_ID" \
--repo "$REPO_SLUG" \
--name init-corpus-logs \
--dir "$TMP_DIR" 2>&1 >&2; then
echo "ERROR: failed to download init-corpus-logs artifact from run $RUN_ID" >&2
echo " - confirm the run ID is correct: gh run list --workflow=npm-publish.yml" >&2
echo " - confirm the run completed and the artifact wasn't pruned (30-day retention)" >&2
exit 2
fi
SUMMARY_FILE="$TMP_DIR/summary.txt"
if [[ ! -f "$SUMMARY_FILE" ]]; then
echo "ERROR: summary.txt not present in downloaded artifact" >&2
echo " contents of artifact:" >&2
ls -la "$TMP_DIR" >&2 || true
exit 3
fi
fi
# Resolve version + date for the header
VERSION="${AQE_VERSION:-$(node -p "require('./package.json').version" 2>/dev/null || echo "unknown")}"
DATE="${AQE_MATRIX_DATE:-$(date -u +%Y-%m-%d)}"
RUN_ID_DISPLAY="${RUN_ID:-(local file)}"
# Render the matrix
cat <<EOF
## Verification matrix (agentic-qe@${VERSION})
Generated from npm-publish.yml run ${RUN_ID_DISPLAY} on ${DATE}.
| Fixture | Status |
|---|---|
EOF
# summary.txt format: each line is either
# <id> PASS
# or
# <id> FAIL <code>
# Some fixtures may write multiple lines (one per assertion); the LAST
# line wins, since the gate halts on the first failure for a given
# fixture and any subsequent line would be from a different fixture.
# Use awk to keep the last line per fixture id.
awk '
{
id=$1
if ($2 == "PASS") {
status[id]="PASS"
} else if ($2 == "FAIL") {
code=""
for (i=3; i<=NF; i++) code = code (i==3 ? "" : " ") $i
status[id]="FAIL " code
last_failed[id]=1
}
order[++n]=id
}
END {
seen=""
for (i=1; i<=n; i++) {
id=order[i]
if (index(seen, " " id " ") == 0) {
printf "| %-16s | %s |\n", id, status[id]
seen = seen " " id " "
}
}
}
' "$SUMMARY_FILE"
echo ""