guardian-framework 0.1.147 → 0.1.149
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -16
- package/dist/cli.js +1 -1
- package/dist/exports.js +1 -1
- package/package.json +1 -1
- package/templates/pi/architecture/wiring-claims.tsv +13 -0
- package/templates/pi/coverage-floors.tsv +12 -0
- package/templates/pi/extensions/architect.ts +3 -3
- package/templates/pi/extensions/pipeline.ts +7 -1
- package/templates/pi/guards.tsv +33 -0
- package/templates/pi/mirrors.tsv +9 -0
- package/templates/pi/preflight_report.json +2 -2
- package/templates/pi/script-drift-allow.tsv +13 -0
- package/templates/pi/scripts/ci/check_architecture_conformance.sh +6 -6
- package/templates/pi/scripts/ci/run_hardening_stages.sh +7 -7
- package/templates/pi/scripts/ci/run_preflight.sh +6 -6
- package/templates/pi/scripts/ci/stage_docs_policy.sh +3 -3
- package/templates/pi/scripts/ci/stage_lint.sh +2 -2
- package/templates/pi/scripts/ci/stage_remaining.sh +3 -3
- package/templates/pi/scripts/ci/stage_static_analysis.sh +4 -4
- package/templates/pi/scripts/ci/stage_unit.sh +2 -2
- package/templates/pi/scripts/ci/validate_agent_output.sh +11 -11
- package/templates/pi/scripts/languages/python/validate-canonical.sh +3 -3
- package/templates/pi/scripts/validate-coverage-floors.sh +174 -0
- package/templates/pi/scripts/validate-guards.sh +171 -0
- package/templates/pi/scripts/validate-script-drift.sh +213 -0
- package/templates/pi/scripts/validate-shell-exit-status.sh +120 -0
- package/templates/pi/scripts/validate-wiring-claims.sh +228 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# validate-wiring-claims.sh — keep "it is wired" claims honest (ADR-009)
|
|
4
|
+
# ============================================================================
|
|
5
|
+
# The repeating failure this guards: code that exists, compiles, passes its own
|
|
6
|
+
# tests, and is never reached — while the module doc says it is wired. Grep
|
|
7
|
+
# finds the symbol; only a checked claim answers the question, and a one-off
|
|
8
|
+
# audit is true for exactly one commit.
|
|
9
|
+
#
|
|
10
|
+
# The manifest `.pi/architecture/wiring-claims.tsv` is that audit in
|
|
11
|
+
# machine-readable form:
|
|
12
|
+
#
|
|
13
|
+
# claim<TAB>module<TAB>status<TAB>evidence<TAB>note
|
|
14
|
+
#
|
|
15
|
+
# status = CONFIRMED the wiring exists; `evidence` (a path or path:symbol)
|
|
16
|
+
# must still be found — fails when it disappears.
|
|
17
|
+
# status = GAP the wiring is missing and the doc must say so; fails
|
|
18
|
+
# when callers/evidence appear (promote the row) or when
|
|
19
|
+
# the module doc stops declaring the gap.
|
|
20
|
+
#
|
|
21
|
+
# Both directions matter: docs drift back into claiming wiring after it was
|
|
22
|
+
# known missing, and gaps get silently closed without the doc being updated.
|
|
23
|
+
#
|
|
24
|
+
# Usage:
|
|
25
|
+
# bash .pi/scripts/validate-wiring-claims.sh # check (gating)
|
|
26
|
+
# bash .pi/scripts/validate-wiring-claims.sh --list # print the table
|
|
27
|
+
# bash .pi/scripts/validate-wiring-claims.sh --self-test
|
|
28
|
+
# bash .pi/scripts/validate-wiring-claims.sh --claims F --root D
|
|
29
|
+
# ============================================================================
|
|
30
|
+
set -uo pipefail
|
|
31
|
+
|
|
32
|
+
# Resolve the project root: the nearest ancestor of this script that looks like
|
|
33
|
+
# a project (has .git or package.json). Works whether the script is invoked from
|
|
34
|
+
# the canonical tree (templates/pi/scripts) or the shipped copy (.pi/scripts).
|
|
35
|
+
resolve_root() {
|
|
36
|
+
local d
|
|
37
|
+
d="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
38
|
+
while [ "$d" != "/" ]; do
|
|
39
|
+
if [ -e "$d/.git" ] || [ -e "$d/package.json" ]; then
|
|
40
|
+
echo "$d"
|
|
41
|
+
return
|
|
42
|
+
fi
|
|
43
|
+
d="$(dirname "$d")"
|
|
44
|
+
done
|
|
45
|
+
echo "$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
46
|
+
}
|
|
47
|
+
ROOT="$(resolve_root)"
|
|
48
|
+
CLAIMS="$ROOT/.pi/architecture/wiring-claims.tsv"
|
|
49
|
+
MODE="check"
|
|
50
|
+
|
|
51
|
+
while [ $# -gt 0 ]; do
|
|
52
|
+
case "$1" in
|
|
53
|
+
--self-test) MODE="self-test"; shift ;;
|
|
54
|
+
--list) MODE="list"; shift ;;
|
|
55
|
+
--claims) CLAIMS="${2:-}"; shift 2 ;;
|
|
56
|
+
--root) ROOT="${2:-}"; shift 2 ;;
|
|
57
|
+
-h | --help) sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
58
|
+
*)
|
|
59
|
+
echo "unknown argument: $1" >&2
|
|
60
|
+
exit 2
|
|
61
|
+
;;
|
|
62
|
+
esac
|
|
63
|
+
done
|
|
64
|
+
|
|
65
|
+
claims_check() {
|
|
66
|
+
# $1 = root, $2 = claims file, $3 = mode
|
|
67
|
+
# (--self-test plants its own manifest, so no project table is required)
|
|
68
|
+
python3 - "$1" "$2" "$3" <<'PY'
|
|
69
|
+
import os, sys
|
|
70
|
+
|
|
71
|
+
root, claims_path, mode = sys.argv[1:4]
|
|
72
|
+
|
|
73
|
+
if not os.path.exists(claims_path) and mode != "selftest":
|
|
74
|
+
print(f"missing wiring-claims manifest: {claims_path}", file=sys.stderr)
|
|
75
|
+
print(" create .pi/architecture/wiring-claims.tsv "
|
|
76
|
+
"(header comments + claim<TAB>module<TAB>status<TAB>evidence<TAB>note)",
|
|
77
|
+
file=sys.stderr)
|
|
78
|
+
sys.exit(2)
|
|
79
|
+
|
|
80
|
+
rows, errors = [], []
|
|
81
|
+
for lineno, line in enumerate(open(claims_path, encoding="utf-8"), 1):
|
|
82
|
+
if line.startswith("#") or not line.strip():
|
|
83
|
+
continue
|
|
84
|
+
parts = line.rstrip("\n").split("\t")
|
|
85
|
+
if len(parts) < 4:
|
|
86
|
+
errors.append(f"{lineno}: malformed row (need claim<TAB>module<TAB>status<TAB>evidence[<TAB>note])")
|
|
87
|
+
continue
|
|
88
|
+
claim, module, status, evidence = parts[:4]
|
|
89
|
+
note = parts[4] if len(parts) > 4 else ""
|
|
90
|
+
# A leading/uncommented header row is the column name, not a claim.
|
|
91
|
+
if (claim, module, status) == ("claim", "module", "status"):
|
|
92
|
+
continue
|
|
93
|
+
if status not in ("CONFIRMED", "GAP"):
|
|
94
|
+
errors.append(f"{lineno}: bad status {status!r} (want CONFIRMED|GAP)")
|
|
95
|
+
continue
|
|
96
|
+
rows.append((claim, module, status, evidence, note, lineno))
|
|
97
|
+
|
|
98
|
+
def evidence_found(ev):
|
|
99
|
+
"""Evidence is `path` or `path:symbol`; both halves must resolve."""
|
|
100
|
+
path, _, symbol = ev.partition(":")
|
|
101
|
+
full = os.path.join(root, path)
|
|
102
|
+
if not os.path.exists(full):
|
|
103
|
+
return False
|
|
104
|
+
if not symbol:
|
|
105
|
+
return True
|
|
106
|
+
if os.path.isdir(full):
|
|
107
|
+
for dp, _dn, fs in os.walk(full):
|
|
108
|
+
for f in fs:
|
|
109
|
+
try:
|
|
110
|
+
if symbol in open(os.path.join(dp, f), encoding="utf-8", errors="ignore").read():
|
|
111
|
+
return True
|
|
112
|
+
except OSError:
|
|
113
|
+
pass
|
|
114
|
+
return False
|
|
115
|
+
try:
|
|
116
|
+
return symbol in open(full, encoding="utf-8", errors="ignore").read()
|
|
117
|
+
except OSError:
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
def doc_declares_gap(module):
|
|
121
|
+
"""A GAP row requires the module doc to still declare the gap."""
|
|
122
|
+
for cand in (
|
|
123
|
+
os.path.join(root, ".pi/architecture/modules", f"{module}.md"),
|
|
124
|
+
os.path.join(root, ".pi/architecture/modules", f"{module.replace('_','-')}.md"),
|
|
125
|
+
):
|
|
126
|
+
if os.path.exists(cand):
|
|
127
|
+
text = open(cand, encoding="utf-8", errors="ignore").read().lower()
|
|
128
|
+
return any(k in text for k in ("not wired", "wiring gap", "no runtime integration", "bootstrap only"))
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
if mode == "list":
|
|
132
|
+
fmt = "{:<34} {:<26} {:<10} {}"
|
|
133
|
+
print(fmt.format("claim", "module", "status", "evidence"))
|
|
134
|
+
for claim, module, status, evidence, _n, _l in rows:
|
|
135
|
+
print(fmt.format(claim, module, status, evidence))
|
|
136
|
+
print(f"\n{len(rows)} claim(s).")
|
|
137
|
+
sys.exit(0)
|
|
138
|
+
|
|
139
|
+
if errors:
|
|
140
|
+
print("wiring-claims manifest invalid:", file=sys.stderr)
|
|
141
|
+
for e in errors:
|
|
142
|
+
print(f" {e}", file=sys.stderr)
|
|
143
|
+
sys.exit(2)
|
|
144
|
+
|
|
145
|
+
problems = []
|
|
146
|
+
for claim, module, status, evidence, _note, lineno in rows:
|
|
147
|
+
found = evidence_found(evidence)
|
|
148
|
+
if status == "CONFIRMED" and not found:
|
|
149
|
+
problems.append(
|
|
150
|
+
f"{lineno}: CONFIRMED claim '{claim}' has no evidence — "
|
|
151
|
+
f"'{evidence}' disappeared while the claim stands "
|
|
152
|
+
f"(module {module})")
|
|
153
|
+
elif status == "GAP":
|
|
154
|
+
if found:
|
|
155
|
+
problems.append(
|
|
156
|
+
f"{lineno}: GAP claim '{claim}' now has evidence — "
|
|
157
|
+
f"'{evidence}' exists, promote the row to CONFIRMED")
|
|
158
|
+
elif not doc_declares_gap(module):
|
|
159
|
+
problems.append(
|
|
160
|
+
f"{lineno}: GAP claim '{claim}' is not declared in the module doc — "
|
|
161
|
+
f".pi/architecture/modules/{module}.md must state the gap")
|
|
162
|
+
|
|
163
|
+
if problems:
|
|
164
|
+
print(f"wiring claims: {len(rows)} row(s), {len(problems)} unresolved:", file=sys.stderr)
|
|
165
|
+
for p in problems:
|
|
166
|
+
print(f" {p}", file=sys.stderr)
|
|
167
|
+
print("\n CONFIRMED rows need live evidence; GAP rows need the gap documented "
|
|
168
|
+
"and must be promoted once the wiring lands.", file=sys.stderr)
|
|
169
|
+
sys.exit(1)
|
|
170
|
+
|
|
171
|
+
print(f"wiring claims OK — {len(rows)} row(s): "
|
|
172
|
+
f"{sum(1 for r in rows if r[2]=='CONFIRMED')} confirmed, "
|
|
173
|
+
f"{sum(1 for r in rows if r[2]=='GAP')} documented gap(s).")
|
|
174
|
+
PY
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
case "$MODE" in
|
|
178
|
+
--self-test | self-test)
|
|
179
|
+
TD="$(mktemp -d "${TMPDIR:-/tmp}/claims-selftest.XXXXXX")"
|
|
180
|
+
ok=0
|
|
181
|
+
fail=0
|
|
182
|
+
bump() { # $1 = exit status, $2 = label
|
|
183
|
+
if [ "$1" -eq 0 ]; then ok=$((ok + 1)); else fail=$((fail + 1)); echo " x case $2" >&2; fi
|
|
184
|
+
}
|
|
185
|
+
mk() { mkdir -p "$TD/$1/.pi/architecture/modules" "$TD/$1/src"; }
|
|
186
|
+
|
|
187
|
+
mk c1
|
|
188
|
+
printf 'def handler(): pass\n' >"$TD/c1/src/app.py"
|
|
189
|
+
|
|
190
|
+
# 1. CONFIRMED with live evidence -> pass
|
|
191
|
+
printf 'claim\tmodule\tstatus\tevidence\nh1\tm1\tCONFIRMED\tsrc/app.py:handler\n' >"$TD/c1/claims.tsv"
|
|
192
|
+
claims_check "$TD/c1" "$TD/c1/claims.tsv" selftest >/dev/null 2>&1
|
|
193
|
+
bump $? 1
|
|
194
|
+
|
|
195
|
+
# 2. CONFIRMED whose evidence vanished -> fail
|
|
196
|
+
printf 'claim\tmodule\tstatus\tevidence\nh1\tm1\tCONFIRMED\tsrc/app.py:gone_symbol\n' >"$TD/c1/claims.tsv"
|
|
197
|
+
claims_check "$TD/c1" "$TD/c1/claims.tsv" selftest >/dev/null 2>&1
|
|
198
|
+
[ $? -ne 0 ]
|
|
199
|
+
bump $? 2
|
|
200
|
+
|
|
201
|
+
# 3. GAP with callers present -> fail (must be promoted)
|
|
202
|
+
printf 'claim\tmodule\tstatus\tevidence\nh1\tm1\tGAP\tsrc/app.py:handler\n' >"$TD/c1/claims.tsv"
|
|
203
|
+
claims_check "$TD/c1" "$TD/c1/claims.tsv" selftest >/dev/null 2>&1
|
|
204
|
+
[ $? -ne 0 ]
|
|
205
|
+
bump $? 3
|
|
206
|
+
|
|
207
|
+
# 4. GAP declared in the module doc, still unwired -> pass
|
|
208
|
+
printf 'claim\tmodule\tstatus\tevidence\nh1\tm1\tGAP\tsrc/nowhere.py\n' >"$TD/c1/claims.tsv"
|
|
209
|
+
printf '# m1\n\nThis module is not wired by design.\n' >"$TD/c1/.pi/architecture/modules/m1.md"
|
|
210
|
+
claims_check "$TD/c1" "$TD/c1/claims.tsv" selftest >/dev/null 2>&1
|
|
211
|
+
bump $? 4
|
|
212
|
+
|
|
213
|
+
# 5. bad status rejected (exit 2)
|
|
214
|
+
printf 'claim\tmodule\tstatus\tevidence\nh1\tm1\tMAYBE\tsrc/x\n' >"$TD/c1/claims.tsv"
|
|
215
|
+
claims_check "$TD/c1" "$TD/c1/claims.tsv" selftest >/dev/null 2>&1
|
|
216
|
+
[ $? -eq 2 ]
|
|
217
|
+
bump $? 5
|
|
218
|
+
|
|
219
|
+
echo "self-test: $ok ok, $fail failed"
|
|
220
|
+
[ "$fail" -eq 0 ]
|
|
221
|
+
;;
|
|
222
|
+
--list | list)
|
|
223
|
+
claims_check "$ROOT" "$CLAIMS" list
|
|
224
|
+
;;
|
|
225
|
+
*)
|
|
226
|
+
claims_check "$ROOT" "$CLAIMS" check
|
|
227
|
+
;;
|
|
228
|
+
esac
|