guardian-framework 0.1.146 → 0.1.148

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.
Files changed (30) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/exports.js +1 -1
  3. package/package.json +1 -1
  4. package/templates/pi/architecture/wiring-claims.tsv +13 -0
  5. package/templates/pi/coverage-floors.tsv +12 -0
  6. package/templates/pi/extensions/architect.ts +44 -4
  7. package/templates/pi/extensions/epic.ts +113 -0
  8. package/templates/pi/extensions/pipeline.ts +7 -1
  9. package/templates/pi/guards.tsv +33 -0
  10. package/templates/pi/mirrors.tsv +9 -0
  11. package/templates/pi/preflight_report.json +2 -2
  12. package/templates/pi/prompts/epic-parallel.md +54 -0
  13. package/templates/pi/script-drift-allow.tsv +13 -0
  14. package/templates/pi/scripts/ci/check_architecture_conformance.sh +6 -6
  15. package/templates/pi/scripts/ci/run_hardening_stages.sh +7 -7
  16. package/templates/pi/scripts/ci/run_preflight.sh +6 -6
  17. package/templates/pi/scripts/ci/stage_docs_policy.sh +3 -3
  18. package/templates/pi/scripts/ci/stage_lint.sh +2 -2
  19. package/templates/pi/scripts/ci/stage_remaining.sh +3 -3
  20. package/templates/pi/scripts/ci/stage_static_analysis.sh +4 -4
  21. package/templates/pi/scripts/ci/stage_unit.sh +2 -2
  22. package/templates/pi/scripts/ci/validate_agent_output.sh +11 -11
  23. package/templates/pi/scripts/epic-parallel.sh +95 -0
  24. package/templates/pi/scripts/epic-registry.sh +138 -0
  25. package/templates/pi/scripts/languages/python/validate-canonical.sh +3 -3
  26. package/templates/pi/scripts/validate-coverage-floors.sh +174 -0
  27. package/templates/pi/scripts/validate-guards.sh +171 -0
  28. package/templates/pi/scripts/validate-script-drift.sh +213 -0
  29. package/templates/pi/scripts/validate-shell-exit-status.sh +120 -0
  30. 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