Files
skills/tests/traceability_test.sh
T
mathias d6e892ac4c
CI / Check / Test (push) Successful in 2s
CI / Tag release (push) Successful in 1s
feat(skill): traceability — a chain of checked links, and a tool that says what it cannot check
Forward trace: requirement → decision → commit → gate → verification → CI on
that sha → release → closure. The distinction the skill exists for is that each
link is either checked or named as unchecked; a trace where every link is
asserted is a narrative, and narratives are what audits exist to defeat.

`trace.sh` does the mechanical part — commit, files, test files touched, release
containing it, referenced issues as URLs — and then always prints the four things
git cannot prove. The GREEN run's own accounting: "the tool did the mechanical
15%. Its real contribution is the 'Cannot verify from here' block, which is a
checklist, not an output." Kept, because it is true.

The RED run established the shape by tracing a commit by hand in sixteen steps.
Its decisive moves were reading issue COMMENTS — where the decisions actually
live — and running a probe that falsified a claim. Both are now steps.

The GREEN run then used step 5 against a gate I shipped this morning, and broke
it. It also found four things wrong with what I had written:

- The slug sed used `[^/]+?` expecting a lazy quantifier. POSIX ERE has none, so
  `.git` was swallowed and every emitted evidence URL carried the suffix. It
  happened to work against Gitea and would 404 on GitHub — a bad failure for the
  one tool whose pitch is that an unchecked link must not look checked.
- "Prints every issue it references" was overclaimed: it greps the commit body,
  so it structurally cannot find the issue that cites the COMMIT. That is the
  commoner break — the issue filed afterwards that falsifies the outcome — and
  it is now step 6 with the two searches that find it.
- Step 3 told you to check which job passed, and the estate's own
  `workflow_run_status` returns no jobs. A step whose tooling dead-ends is where
  a trace becomes "the run was green"; the REST fallback is now written out.
- The output format did not survive contact. It grew four slots, each forced by
  a real trace: two requirements differing in kind (a defect report and a
  standing bar), the gate as an artifact separate from the test, the RED/GREEN
  baseline against the parent, and the commit's factual claims checked. Without
  the last one, "commit → verified" means only "this sha exists".

Also: `git describe --contains` reads local tags, so the tool now says so on
every hit rather than only when it finds nothing.

Closes #10
Bump-Type: minor
2026-08-29 15:34:11 +02:00

82 lines
2.6 KiB
Bash

#!/bin/sh
# Tests for skills/traceability/trace.sh.
#
# The RED run for #10 established what a forward trace costs by hand: sixteen
# commands, of which the decisive ones were reading issue comments and running a
# probe against a claim. This tool does the mechanical half — what git can prove
# — and, more importantly, states what it CANNOT prove, so an unverifiable link
# never reads as a verified one.
set -u
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
SUT="$ROOT/skills/traceability/trace.sh"
pass=0
fail=0
WORK=${TMPDIR:-/tmp}/traceability_test.$$
if [ ! -f "$SUT" ]; then
printf 'FAIL: %s does not exist\n' "$SUT"
exit 1
fi
cleanup() { rm -rf "$WORK"; }
trap cleanup EXIT INT TERM
mkdir -p "$WORK/repo/tests"
cd "$WORK/repo" || exit 1
git init -q .
git config user.email t@example.com
git config user.name test
# A remote is needed for the tool to turn "#37" into a checkable URL, which is
# the point of that section.
git remote add origin https://git.d-ma.be/mathias/demo.git
printf 'baseline\n' > README.md
git add -A && git commit -qm "chore: baseline"
printf 'gate\n' > gate.sh
printf 'case\n' > tests/gate_test.sh
git add -A && git commit -qm "feat(gate): add the thing
Implements the decision recorded in #5, filed as #37.
Closes #37"
SHA=$(git rev-parse --short HEAD)
git tag -a v1.2.0 -m "release v1.2.0"
printf 'more\n' >> gate.sh
git add -A && git commit -qm "chore: later work"
cd - >/dev/null || exit 1
out=$(sh "$SUT" "$SHA" "$WORK/repo" 2>&1)
check() {
if printf '%s' "$out" | grep -q "$1"; then
pass=$((pass + 1)); printf ' ok %s\n' "$2"
else
fail=$((fail + 1)); printf ' FAIL %s\n output had no %s\n%s\n' "$2" "$1" "$out"
fi
}
check '#5' 'lists every issue the commit references, not just the closing one'
check '#37' 'lists the closing reference'
check 'tests/gate_test.sh' 'names the test files the commit touched'
check 'v1.2.0' 'names the release that contains it'
check 'Cannot verify' 'states what it could not verify'
check 'issues/37' 'emits a checkable URL for the tracker link it cannot follow'
# A commit in no release must say so — "no tag" and "not checked" are different
# answers and only one of them is about the commit.
last=$(git -C "$WORK/repo" rev-parse --short HEAD)
out=$(sh "$SUT" "$last" "$WORK/repo" 2>&1)
if printf '%s' "$out" | grep -qi 'no release contains'; then
pass=$((pass + 1)); printf ' ok an unreleased commit is reported as unreleased\n'
else
fail=$((fail + 1)); printf ' FAIL an unreleased commit is reported as unreleased\n%s\n' "$out"
fi
printf '\ntraceability: %d passed, %d failed\n' "$pass" "$fail"
[ "$fail" -eq 0 ]