build: consume the shared traceability tooling via submodule
jray-project is added at scripts/vendor/jray-project and the extractor is used from there. Only two files are repo-local: traceability.toml, which carries everything repo-specific, and the CI workflow that invokes the vendored gate. The extractor is deliberately NOT copied in. One implementation, parameterised by config — a second copy would drift from the first, and the tool already proves it works unchanged against all three registers. Enables system_spec so PR/SR orphan checking runs: previously uncheckable, because the system spec lived outside every component's checkout. Gate is green at 0.0% of 63 requirements, which is correct — nothing is tagged yet. Eleven are flagged unverifiable on this CI host and excluded from the numerator rather than counted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,145 @@
|
|||||||
|
name: Traceability Validation
|
||||||
|
|
||||||
|
# Mirrors JellyTau's .gitea/workflows/traceability-check.yml. The extractor is
|
||||||
|
# stdlib Python, so there is no toolchain install step and no jq.
|
||||||
|
#
|
||||||
|
# This workflow is component-agnostic: every repo-specific setting - which ID
|
||||||
|
# prefixes count, which file suffixes are source, which directories to scan,
|
||||||
|
# the threshold - lives in traceability.toml at the repo root, and the same
|
||||||
|
# extractor is shared by all three JRay components. Copying this file into
|
||||||
|
# another component needs no edits.
|
||||||
|
#
|
||||||
|
# NOTE: the runner here is an Intel N100 with no discrete GPU. This job is only
|
||||||
|
# ever static analysis of source comments plus markdown parsing, so it is cheap;
|
||||||
|
# the requirements it reports as "tagged but unexecuted" are the ones that need
|
||||||
|
# a GPU host, and they are deliberately never counted as covered.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-traces:
|
||||||
|
runs-on: linux/amd64
|
||||||
|
name: Check requirement traces
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Check Python is available
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
command -v python3 >/dev/null 2>&1 || {
|
||||||
|
echo "python3 is missing from the runner image."
|
||||||
|
echo "The traceability tooling is stdlib-only Python;"
|
||||||
|
echo "3.9+ with CLI flags, 3.11+ to read traceability.toml."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
python3 --version
|
||||||
|
|
||||||
|
# The gate's own arithmetic is the thing being trusted, so its tests run
|
||||||
|
# before it does. JellyTau's gate was believed for months while it was
|
||||||
|
# dividing by frozen literals; untested gate logic is how that happens.
|
||||||
|
- name: Test the extractor
|
||||||
|
run: python3 scripts/vendor/jray-project/scripts/traceability/test_extract_traces.py
|
||||||
|
|
||||||
|
# Threshold policy and every other repo-specific setting live in
|
||||||
|
# traceability.toml, not here, so local runs and CI runs cannot disagree
|
||||||
|
# about what "passing" means. Denominators come from docs/requirements.md
|
||||||
|
# at run time and are never hardcoded -- in this file or anywhere else.
|
||||||
|
#
|
||||||
|
# A misconfigured run (zero requirements parsed, zero files scanned) is a
|
||||||
|
# hard failure rather than a plausible-looking 0%.
|
||||||
|
- name: Traceability gate
|
||||||
|
run: sh scripts/vendor/jray-project/scripts/traceability/traceability-gate.sh
|
||||||
|
|
||||||
|
- name: Check modified files for traces
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
echo "Checking modified sources for TRACES tags..."
|
||||||
|
|
||||||
|
# The extensions come from the report the gate just wrote, which got
|
||||||
|
# them from traceability.toml. Restating them here would be a second
|
||||||
|
# place for the source-file definition to live, and the two would
|
||||||
|
# drift the first time a language is added.
|
||||||
|
PATTERN=$(python3 -c "
|
||||||
|
import json, re, sys
|
||||||
|
suffixes = json.load(open('traces-report.json'))['config']['sourceSuffixes']
|
||||||
|
print('(' + '|'.join(re.escape(s) + '\$' for s in suffixes) + ')')
|
||||||
|
")
|
||||||
|
echo "Source suffixes from traceability.toml: $PATTERN"
|
||||||
|
|
||||||
|
CHANGED=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD" \
|
||||||
|
| grep -E "$PATTERN" || true)
|
||||||
|
|
||||||
|
if [ -z "$CHANGED" ]; then
|
||||||
|
echo "No source files changed."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Changed files:"
|
||||||
|
echo "$CHANGED" | sed 's/^/ /'
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Advisory by design: not every file implements a requirement, and a
|
||||||
|
# tag on every function is noise that rots faster than it helps
|
||||||
|
# (CLAUDE.md: tag the unit that decides). This step exists to prompt,
|
||||||
|
# not to block. The blocking checks are in the gate step above.
|
||||||
|
#
|
||||||
|
# Piped into the loop rather than a here-string, and `case` rather
|
||||||
|
# than `[[ == ]]`, so this works under dash as well as bash. The loop
|
||||||
|
# body runs in a subshell, so misses are recorded in a file.
|
||||||
|
MISSING=$(mktemp)
|
||||||
|
echo "$CHANGED" | while IFS= read -r file; do
|
||||||
|
case "$file" in
|
||||||
|
*/test_*.py|*_test.py|*Tests.cs|tests/*|*/tests/*) continue ;;
|
||||||
|
esac
|
||||||
|
[ -f "$file" ] || continue
|
||||||
|
if ! grep -q 'TRACES:' "$file"; then
|
||||||
|
echo " no TRACES tag: $file"
|
||||||
|
echo "$file" >> "$MISSING"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
COUNT=$(wc -l < "$MISSING" | tr -d ' ')
|
||||||
|
rm -f "$MISSING"
|
||||||
|
|
||||||
|
if [ "$COUNT" -gt 0 ]; then
|
||||||
|
echo ""
|
||||||
|
echo "$COUNT changed file(s) carry no requirement tag."
|
||||||
|
echo "Format: // TRACES: AR-012, AR-013 | SR-002"
|
||||||
|
echo " (pipe separates requirement types, comma separates IDs)"
|
||||||
|
echo "A deliberate invariant exception is tagged separately:"
|
||||||
|
echo " // EXCEPTION: AR-024 <reason>"
|
||||||
|
echo "See CLAUDE.md and SPEC.md section 6."
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Report summary
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
echo "Traceability matrix: docs/traceability.md"
|
||||||
|
echo ""
|
||||||
|
head -40 docs/traceability.md || true
|
||||||
|
|
||||||
|
- name: Save reports
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: traceability-reports
|
||||||
|
path: |
|
||||||
|
traces-report.json
|
||||||
|
docs/traceability.md
|
||||||
|
retention-days: 30
|
||||||
@@ -2,3 +2,6 @@
|
|||||||
path = external/KPN
|
path = external/KPN
|
||||||
url = https://gitea.tourolle.paris/dtourolle/KPN.git
|
url = https://gitea.tourolle.paris/dtourolle/KPN.git
|
||||||
branch = master
|
branch = master
|
||||||
|
[submodule "jray-project"]
|
||||||
|
path = scripts/vendor/jray-project
|
||||||
|
url = git@gitea.tourolle.paris:dtourolle/jray-project.git
|
||||||
|
|||||||
+1
Submodule scripts/vendor/jray-project added at 17106f3370
@@ -0,0 +1,34 @@
|
|||||||
|
# Traceability configuration for scene-actor-extraction.
|
||||||
|
#
|
||||||
|
# Read by the shared extractor (scripts/traceability/extract_traces.py), which
|
||||||
|
# is the same implementation every JRay component uses. Everything repo-specific
|
||||||
|
# lives here rather than in the tool; run `extract_traces.py
|
||||||
|
# --print-example-config` for the annotated schema.
|
||||||
|
#
|
||||||
|
# This file's directory is taken as the repo root, so the gate works from any
|
||||||
|
# subdirectory.
|
||||||
|
|
||||||
|
# The prefixes this repo's register defines. Nothing else enters the fraction:
|
||||||
|
# UT/IT are evidence for requirements, PR/SR belong to the system spec.
|
||||||
|
requirement_types = ["AR", "DP", "IR", "GR", "VR"]
|
||||||
|
|
||||||
|
# C++ pipeline plus the Python tooling, optimizer and validation scripts.
|
||||||
|
languages = ["cpp", "python"]
|
||||||
|
|
||||||
|
source_roots = ["src", "tests", "scripts", "experiments", "eval"]
|
||||||
|
|
||||||
|
# CI is an Intel N100 with no discrete GPU. T4 is deliberately absent: a
|
||||||
|
# requirement verifiable only on GPU hardware is reported as tagged but
|
||||||
|
# unexecuted and never counted as covered, because counting a test that cannot
|
||||||
|
# run is the same failure mode as JellyTau's 158% coverage bug.
|
||||||
|
ci_executable_tiers = ["T1", "T2", "T3", "static"]
|
||||||
|
|
||||||
|
# Threshold policy. 0 today because almost nothing is tagged yet - tags land as
|
||||||
|
# the pipeline is built. This is not a gate that cannot fail: orphan tags, a
|
||||||
|
# >100% ratio, a register that parses to nothing and an empty source scan are
|
||||||
|
# all hard failures already. Ratchet this up as tags land; never reset it down.
|
||||||
|
min_coverage = 0.0
|
||||||
|
|
||||||
|
# The system spec owning PR/SR is vendored per-component as a submodule. Point
|
||||||
|
# at it once that lands to turn on PR/SR orphan checking:
|
||||||
|
system_spec = "scripts/vendor/jray-project/SPEC.md"
|
||||||
Reference in New Issue
Block a user