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
package/README.md
CHANGED
|
@@ -41,15 +41,24 @@ Enrich Module Docs ←─ agent fills DDD content from exploration
|
|
|
41
41
|
ADRs, Diagrams, Specs ←─ you challenge decisions, finalize
|
|
42
42
|
│
|
|
43
43
|
▼
|
|
44
|
-
Roadmap Plan ←─
|
|
44
|
+
Roadmap Plan ←─ one module per phase (Phase 0 = bootstrap)
|
|
45
45
|
│
|
|
46
46
|
▼
|
|
47
|
-
Epic Execution ───
|
|
47
|
+
Epic Execution ─── one branch + one PR per epic, local CI per issue
|
|
48
|
+
│ └─ parallel epics: isolated worktrees, contract-freeze gate
|
|
49
|
+
▼
|
|
50
|
+
Wiring Check ─── every module must be INSTALLED in the app shell
|
|
48
51
|
│
|
|
49
52
|
▼
|
|
50
53
|
Enforcement Pipeline ─── CI blocks merges on violation
|
|
51
54
|
```
|
|
52
55
|
|
|
56
|
+
> New since 0.1.128: exploration is terminal and self-driving, stubs follow the
|
|
57
|
+
> detected language, epics ship as one PR, parallel epics are gated on frozen
|
|
58
|
+
> contracts, and five enforcement controls make "can this actually block a
|
|
59
|
+
> merge?" a machine-readable answer. See
|
|
60
|
+
> [manual §30–39](docs/USER_MANUAL.md#30-exploration--roadmap--epic-the-current-pipeline).
|
|
61
|
+
|
|
53
62
|
---
|
|
54
63
|
|
|
55
64
|
## Walkthrough: From Intent to Enforcement
|
|
@@ -207,30 +216,42 @@ If someone (or an AI) adds a dependency from the domain layer to infrastructure,
|
|
|
207
216
|
## Quick Start
|
|
208
217
|
|
|
209
218
|
```bash
|
|
210
|
-
# 1.
|
|
219
|
+
# 1. Init (installs .pi/ — prompts, extensions, validators)
|
|
211
220
|
cd your-project
|
|
212
221
|
npx guardian-framework init
|
|
213
222
|
|
|
214
|
-
# 2. Explore
|
|
215
|
-
|
|
216
|
-
/domain --
|
|
217
|
-
|
|
218
|
-
# 3. Generate architecture from exploration (module docs gain Modeling Depth + invariants)
|
|
219
|
-
/domain --architect-scaffold <session-id>
|
|
223
|
+
# 2. Explore — the agent classifies core/supporting/generic; YOU gate the depth.
|
|
224
|
+
# Delivers the full protocol in one message, then STOPS. Resume with --resume.
|
|
225
|
+
/domain --explore "your business intent"
|
|
220
226
|
|
|
221
|
-
#
|
|
222
|
-
|
|
227
|
+
# 3. Validate the analysis against the files that were written (drift gate)
|
|
228
|
+
guardian domain validate <session-id>
|
|
223
229
|
|
|
224
|
-
#
|
|
230
|
+
# 4. Create module docs — never overwrites existing ones
|
|
231
|
+
guardian domain scaffold --session <session-id>
|
|
232
|
+
guardian domain enrich --session <session-id> # full DDD content, session-scoped
|
|
225
233
|
|
|
226
|
-
#
|
|
234
|
+
# 5. Roadmap — ONE module per phase, Phase 0 is Project Bootstrap
|
|
227
235
|
/architect --roadmap
|
|
228
|
-
/architect --adopt-roadmap
|
|
229
236
|
|
|
230
|
-
# 6. Execute
|
|
231
|
-
/architect --
|
|
237
|
+
# 6. Execute — epic mode is the default: one branch, local CI per issue, one PR
|
|
238
|
+
/architect --epic "Policy Engine"
|
|
239
|
+
|
|
240
|
+
# ...or run several in parallel (gated on frozen contracts)
|
|
241
|
+
bash .pi/scripts/epic-registry.sh freeze "Policy Engine" --frozen
|
|
242
|
+
bash .pi/scripts/epic-parallel.sh "Policy Engine" # isolated worktree
|
|
243
|
+
|
|
244
|
+
# 7. Prove it is actually wired, not just implemented
|
|
245
|
+
bash .pi/scripts/check_composition_root.sh
|
|
246
|
+
bash .pi/scripts/validate-wiring-claims.sh
|
|
247
|
+
|
|
248
|
+
# 8. See what can block a merge
|
|
249
|
+
bash .pi/scripts/validate-guards.sh --list
|
|
232
250
|
```
|
|
233
251
|
|
|
252
|
+
Minimal project bootstrap is the default (shared kernel only). Add bounded-context
|
|
253
|
+
folders with `--withContexts`, or let the roadmap phases create them.
|
|
254
|
+
|
|
234
255
|
---
|
|
235
256
|
|
|
236
257
|
## What This Means
|
|
@@ -248,6 +269,8 @@ Guardian was proven on [Rigorix](https://github.com/arman-jalili/rigorix-oss): 1
|
|
|
248
269
|
| | |
|
|
249
270
|
|---|---|
|
|
250
271
|
| **User Manual** | [docs/USER_MANUAL.md](docs/USER_MANUAL.md) — every workflow, command, and extension |
|
|
272
|
+
| **Pipeline (0.1.148)** | [manual §30–39](docs/USER_MANUAL.md#30-exploration--roadmap--epic-the-current-pipeline) — explore → roadmap → epic → wiring → controls |
|
|
273
|
+
| **Design note** | [docs/design/exploration-and-roadmap-pipeline.md](docs/design/exploration-and-roadmap-pipeline.md) |
|
|
251
274
|
| **Proven on** | [Rigorix](https://github.com/arman-jalili/rigorix-oss) — 146K LOC Rust, 30 modules |
|
|
252
275
|
| **Source** | [github.com/arman-jalili/guardian-framework](https://github.com/arman-jalili/guardian-framework) |
|
|
253
276
|
| **License** | MIT |
|
package/dist/cli.js
CHANGED
|
@@ -3206,7 +3206,7 @@ __export(exports_package, {
|
|
|
3206
3206
|
bin: () => bin,
|
|
3207
3207
|
author: () => author
|
|
3208
3208
|
});
|
|
3209
|
-
var name = "guardian-framework", version = "0.1.
|
|
3209
|
+
var name = "guardian-framework", version = "0.1.149", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
|
|
3210
3210
|
var init_package = __esm(() => {
|
|
3211
3211
|
exports = {
|
|
3212
3212
|
".": {
|
package/dist/exports.js
CHANGED
|
@@ -1991,7 +1991,7 @@ __export(exports_package, {
|
|
|
1991
1991
|
bin: () => bin,
|
|
1992
1992
|
author: () => author
|
|
1993
1993
|
});
|
|
1994
|
-
var name = "guardian-framework", version = "0.1.
|
|
1994
|
+
var name = "guardian-framework", version = "0.1.149", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
|
|
1995
1995
|
var init_package = __esm(() => {
|
|
1996
1996
|
exports = {
|
|
1997
1997
|
".": {
|
package/package.json
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Wiring claims (ADR-009): make "it is wired" claims checkable.
|
|
2
|
+
#
|
|
3
|
+
# validate-wiring-claims.sh fails both ways:
|
|
4
|
+
# * a CONFIRMED row whose evidence disappears — the wiring was removed and
|
|
5
|
+
# the doc still claims it;
|
|
6
|
+
# * a GAP row whose evidence appears (promote it) or whose module doc stops
|
|
7
|
+
# declaring the gap.
|
|
8
|
+
#
|
|
9
|
+
# Columns (tab-separated): claim module status evidence note
|
|
10
|
+
# status CONFIRMED | GAP
|
|
11
|
+
# evidence path or path:symbol that proves (or disproves) the wiring
|
|
12
|
+
#
|
|
13
|
+
# claim module status evidence note
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Coverage floors (ADR-010) — a floor is a RATCHET.
|
|
2
|
+
#
|
|
3
|
+
# Raise a floor when a layer improves; never lower one to make a build pass.
|
|
4
|
+
# validate-coverage-floors.sh fails when a floor decreases or a layer
|
|
5
|
+
# disappears, compared against the merge base.
|
|
6
|
+
#
|
|
7
|
+
# Columns (tab-separated): layer floor measured target note
|
|
8
|
+
# floor the value that gates (ratchet; never decreases)
|
|
9
|
+
# measured last measured coverage (informational)
|
|
10
|
+
# target the roadmap criterion (gap printed when unmet)
|
|
11
|
+
#
|
|
12
|
+
# layer floor measured target note
|
|
@@ -635,7 +635,7 @@ export default function architectExtension(pi: ExtensionAPI) {
|
|
|
635
635
|
items: existingIssues,
|
|
636
636
|
steps: [
|
|
637
637
|
{ name: "implement", prompt: ".pi/prompts/issue-implementation-series.md", acceptance: { type: "validator", validators: ["ci"] } },
|
|
638
|
-
{ name: "validate", acceptance: { type: "validator", validators: ["ci", "tests", "security", "wiring"] } },
|
|
638
|
+
{ name: "validate", acceptance: { type: "validator", validators: ["ci", "tests", "security", "wiring", "guards", "drift", "shell", "claims", "floors"] } },
|
|
639
639
|
{ name: "create-mr", prompt: ".pi/prompts/epic-closeout.md", acceptance: { type: "none" } },
|
|
640
640
|
{ name: "merge", prompt: ".pi/prompts/epic-closeout.md", acceptance: { type: "validator", validators: ["ci", "canonical"] } },
|
|
641
641
|
],
|
|
@@ -697,7 +697,7 @@ export default function architectExtension(pi: ExtensionAPI) {
|
|
|
697
697
|
items,
|
|
698
698
|
steps: [
|
|
699
699
|
{ name: "implement", prompt: ".pi/prompts/issue-implementation-series.md", acceptance: { type: "validator", validators: ["ci"] } },
|
|
700
|
-
{ name: "validate", acceptance: { type: "validator", validators: ["ci", "tests", "security", "wiring"] } },
|
|
700
|
+
{ name: "validate", acceptance: { type: "validator", validators: ["ci", "tests", "security", "wiring", "guards", "drift", "shell", "claims", "floors"] } },
|
|
701
701
|
{ name: "create-mr", prompt: ".pi/prompts/epic-closeout.md", acceptance: { type: "none" } },
|
|
702
702
|
{ name: "merge", prompt: ".pi/prompts/epic-closeout.md", acceptance: { type: "validator", validators: ["ci", "canonical"] } },
|
|
703
703
|
],
|
|
@@ -964,7 +964,7 @@ export default function architectExtension(pi: ExtensionAPI) {
|
|
|
964
964
|
items,
|
|
965
965
|
steps: [
|
|
966
966
|
{ name: "implement", prompt: ".pi/prompts/issue-implementation-series.md", acceptance: { type: "validator", validators: ["ci"] } },
|
|
967
|
-
{ name: "validate", acceptance: { type: "validator", validators: ["ci", "tests", "security", "wiring"] } },
|
|
967
|
+
{ name: "validate", acceptance: { type: "validator", validators: ["ci", "tests", "security", "wiring", "guards", "drift", "shell", "claims", "floors"] } },
|
|
968
968
|
{ name: "create-mr", prompt: ".pi/prompts/epic-closeout.md", acceptance: { type: "none" } },
|
|
969
969
|
{ name: "merge", prompt: ".pi/prompts/epic-closeout.md", acceptance: { type: "validator", validators: ["ci", "canonical"] } },
|
|
970
970
|
],
|
|
@@ -45,6 +45,12 @@ const VALIDATOR_SCRIPTS: Record<string, string> = {
|
|
|
45
45
|
integration: ".pi/scripts/validate-integration.sh",
|
|
46
46
|
// ADR-009: implemented modules must be INSTALLED in the composition root
|
|
47
47
|
wiring: ".pi/scripts/check_composition_root.sh",
|
|
48
|
+
// ADR-010: controls that make the other controls enforceable
|
|
49
|
+
guards: ".pi/scripts/validate-guards.sh",
|
|
50
|
+
drift: ".pi/scripts/validate-script-drift.sh",
|
|
51
|
+
claims: ".pi/scripts/validate-wiring-claims.sh",
|
|
52
|
+
floors: ".pi/scripts/validate-coverage-floors.sh",
|
|
53
|
+
shell: ".pi/scripts/validate-shell-exit-status.sh",
|
|
48
54
|
};
|
|
49
55
|
|
|
50
56
|
// ── Helpers ──
|
|
@@ -1445,7 +1451,7 @@ function buildSteps(stepNames: string[]): StepConfig[] {
|
|
|
1445
1451
|
},
|
|
1446
1452
|
validate: {
|
|
1447
1453
|
name: "validate",
|
|
1448
|
-
acceptance: { type: "validator", validators: ["ci", "tests", "security", "wiring"] },
|
|
1454
|
+
acceptance: { type: "validator", validators: ["ci", "tests", "security", "wiring", "guards", "drift", "shell", "claims", "floors"] },
|
|
1449
1455
|
},
|
|
1450
1456
|
"create-mr": {
|
|
1451
1457
|
name: "create-mr",
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Self-owned guard registry (ADR-010).
|
|
2
|
+
#
|
|
3
|
+
# Every root .pi/scripts/validate-*.sh MUST appear here. `validate-guards.sh`
|
|
4
|
+
# fails when a guard exists but is unclassified, so a new guard cannot be
|
|
5
|
+
# silently left un-gated.
|
|
6
|
+
#
|
|
7
|
+
# Columns (tab-separated):
|
|
8
|
+
# guard path to the guard script, relative to the repo root
|
|
9
|
+
# class the pattern class it owns
|
|
10
|
+
# tier gating | advisory
|
|
11
|
+
# gating = a non-zero exit fails a mandatory path
|
|
12
|
+
# advisory = informational; it does not stop a merge
|
|
13
|
+
# run_here yes | no (yes = `validate-guards.sh --run` executes it)
|
|
14
|
+
# owner who is accountable for the class
|
|
15
|
+
# notes how it gates / what it does
|
|
16
|
+
#
|
|
17
|
+
# guard class tier run_here owner notes
|
|
18
|
+
.pi/scripts/validate-guards.sh guards advisory no architecture meta: registry integrity; --run executes the gating set
|
|
19
|
+
.pi/scripts/validate-script-drift.sh script-drift gating yes architecture mirrored .pi/scripts differ and are unrecorded
|
|
20
|
+
.pi/scripts/validate-wiring-claims.sh wiring-claims gating yes architecture wiring claims need live evidence; gaps must stay documented
|
|
21
|
+
.pi/scripts/validate-coverage-floors.sh coverage-floors gating yes quality a coverage floor may not be lowered or removed
|
|
22
|
+
.pi/scripts/validate-shell-exit-status.sh shell-exit gating yes quality arithmetic statement returns 1 on a passing path
|
|
23
|
+
.pi/scripts/validate-architecture.sh architecture gating yes architecture dispatches the per-language architecture checks
|
|
24
|
+
.pi/scripts/validate-canonical.sh canonical gating yes architecture canonical headers and references resolve
|
|
25
|
+
.pi/scripts/validate-tests.sh tests gating yes quality tests exist, run and cover the contract
|
|
26
|
+
.pi/scripts/validate-security.sh security gating yes security secret scan and unsafe-pattern gates
|
|
27
|
+
.pi/scripts/validate-ci.sh ci gating yes platform CI pipeline matches the blueprint
|
|
28
|
+
.pi/scripts/validate-integration.sh integration gating yes architecture components integrate through their declared seams
|
|
29
|
+
.pi/scripts/validate-operations.sh operations gating yes platform readiness: observability, error handling, cancellation
|
|
30
|
+
.pi/scripts/validate-runtime-wiring.sh runtime-wiring gating yes architecture implemented modules are reachable at runtime
|
|
31
|
+
.pi/scripts/validate-architecture-readiness.sh readiness advisory no architecture architecture docs are complete enough to implement
|
|
32
|
+
.pi/scripts/validate-exploration-depth.sh exploration-depth advisory no architecture domain exploration depth is sufficient
|
|
33
|
+
.pi/scripts/validate-ubiquitous-language.sh ubiquitous-language gating yes architecture code and docs share the canonical glossary
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Extra trees compared by validate-script-drift.sh (one path per line).
|
|
2
|
+
#
|
|
3
|
+
Each path is a directory containing a `scripts/` subtree whose files are
|
|
4
|
+
compared against every other tree by path. The repo root .pi/ and every
|
|
5
|
+
immediate subdirectory carrying .pi/scripts are always compared; list here
|
|
6
|
+
trees that are NOT in that shape.
|
|
7
|
+
#
|
|
8
|
+
This repo ships a canonical tree (templates/pi) and mirrors it to .pi/.
|
|
9
|
+
templates/pi
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"timestamp": "2026-11-
|
|
2
|
+
"timestamp": "2026-11-07T14:25:39Z",
|
|
3
3
|
"mode": "all",
|
|
4
4
|
"stages_run": [],
|
|
5
5
|
"summary": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"failed": 1,
|
|
9
9
|
"skipped": 12
|
|
10
10
|
},
|
|
11
|
-
"duration_seconds":
|
|
11
|
+
"duration_seconds": 1,
|
|
12
12
|
"results": [
|
|
13
13
|
{
|
|
14
14
|
"name": "check_mr_traceability.sh",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Recorded per-tree differences for mirrored .pi/scripts (ADR-010).
|
|
2
|
+
#
|
|
3
|
+
# validate-script-drift.sh fails when a file mirrored across trees (the repo
|
|
4
|
+
# root plus any subdirectory carrying a .pi/ directory) differs and is NOT
|
|
5
|
+
# matched by a pattern here.
|
|
6
|
+
#
|
|
7
|
+
# Columns (tab-separated): pattern trees reason
|
|
8
|
+
# pattern glob matched against the path relative to <tree>/.pi/scripts
|
|
9
|
+
# trees which trees carry the file (informational)
|
|
10
|
+
# reason why the difference is tolerated
|
|
11
|
+
#
|
|
12
|
+
# pattern trees reason
|
|
13
|
+
ci/stage_security.sh templates/pi,.pi guardian generates a project-specific security stage (secret scan + dep audit) into .pi/scripts/ci; the template carries the dispatcher stub, same as stage_build.sh and stage_test.sh
|
|
@@ -149,7 +149,7 @@ check_tenant_isolation() {
|
|
|
149
149
|
has_tenant_filter=true
|
|
150
150
|
fi
|
|
151
151
|
if [[ "$has_tenant_filter" == "false" ]]; then
|
|
152
|
-
((violations
|
|
152
|
+
violations=$((violations + 1))
|
|
153
153
|
log_fail "$check_name" "Model $(basename "$model_file") has tenant_id but no tenant-scoped queries"
|
|
154
154
|
fi
|
|
155
155
|
fi
|
|
@@ -521,7 +521,7 @@ check_runtime_baseline() {
|
|
|
521
521
|
while IFS= read -r var; do
|
|
522
522
|
var_name=$(echo "$var" | cut -d= -f1)
|
|
523
523
|
if [[ -z "${!var_name:-}" ]]; then
|
|
524
|
-
((unbound
|
|
524
|
+
unbound=$((unbound + 1))
|
|
525
525
|
fi
|
|
526
526
|
done < <(grep -v "^#" .env.example 2>/dev/null | grep "=" || true)
|
|
527
527
|
if [[ $unbound -eq 0 ]]; then
|
|
@@ -603,7 +603,7 @@ check_arch_sanity() {
|
|
|
603
603
|
local orphaned_imports=0
|
|
604
604
|
for f in $(find . -name "*.py" -o -name "*.ts" -o -name "*.rs" -o -name "*.go" 2>/dev/null | head -30); do
|
|
605
605
|
if grep -qE "^import.*UNUSED|^from.*UNUSED" "$f" 2>/dev/null; then
|
|
606
|
-
((orphaned_imports
|
|
606
|
+
orphaned_imports=$((orphaned_imports + 1))
|
|
607
607
|
fi
|
|
608
608
|
done
|
|
609
609
|
if [[ $orphaned_imports -gt 0 ]]; then
|
|
@@ -636,7 +636,7 @@ check_arch_sanity() {
|
|
|
636
636
|
example_val=$(grep "^${var_name}=" ".env.example" | cut -d= -f2-)
|
|
637
637
|
actual_val=$(grep "^${var_name}=" ".env" | cut -d= -f2-)
|
|
638
638
|
if [[ "$example_val" == "$actual_val" ]]; then
|
|
639
|
-
((collisions
|
|
639
|
+
collisions=$((collisions + 1))
|
|
640
640
|
fi
|
|
641
641
|
fi
|
|
642
642
|
done < <(grep -v "^#" .env.example 2>/dev/null | grep "=" || true)
|
|
@@ -683,13 +683,13 @@ check_import_boundaries() {
|
|
|
683
683
|
[[ -f "$file" ]] || continue
|
|
684
684
|
if [[ "$layer_dir" == *"domain" ]]; then
|
|
685
685
|
if grep -qE "from.*infrastructure|from.*api|from.*interfaces|import.*infrastructure|import.*api|import.*interfaces" "$file" 2>/dev/null; then
|
|
686
|
-
((violations
|
|
686
|
+
violations=$((violations + 1))
|
|
687
687
|
log_fail "$check_name" "Domain layer imports from outer layer in $(basename "$file")"
|
|
688
688
|
fi
|
|
689
689
|
fi
|
|
690
690
|
if [[ "$layer_dir" == *"application" ]]; then
|
|
691
691
|
if grep -qE "from.*api|from.*interfaces|import.*api|import.*interfaces" "$file" 2>/dev/null; then
|
|
692
|
-
((violations
|
|
692
|
+
violations=$((violations + 1))
|
|
693
693
|
log_fail "$check_name" "Application layer imports from api/interfaces in $(basename "$file")"
|
|
694
694
|
fi
|
|
695
695
|
fi
|
|
@@ -102,14 +102,14 @@ run_stage() {
|
|
|
102
102
|
return 0
|
|
103
103
|
fi
|
|
104
104
|
|
|
105
|
-
((TOTAL_STAGES
|
|
105
|
+
TOTAL_STAGES=$((TOTAL_STAGES + 1))
|
|
106
106
|
|
|
107
107
|
# Check conditional
|
|
108
108
|
case "$conditional" in
|
|
109
109
|
main_only)
|
|
110
110
|
if [[ "${CI_COMMIT_BRANCH:-}" != "main" && "${GITHUB_REF_NAME:-}" != "main" ]]; then
|
|
111
111
|
log_stage_skip "$stage_name" "Not on main branch"
|
|
112
|
-
((SKIPPED_STAGES
|
|
112
|
+
SKIPPED_STAGES=$((SKIPPED_STAGES + 1))
|
|
113
113
|
return 0
|
|
114
114
|
fi
|
|
115
115
|
;;
|
|
@@ -122,14 +122,14 @@ run_stage() {
|
|
|
122
122
|
fi
|
|
123
123
|
if [[ "$has_migration" == "false" ]]; then
|
|
124
124
|
log_stage_skip "$stage_name" "No migration changes detected"
|
|
125
|
-
((SKIPPED_STAGES
|
|
125
|
+
SKIPPED_STAGES=$((SKIPPED_STAGES + 1))
|
|
126
126
|
return 0
|
|
127
127
|
fi
|
|
128
128
|
;;
|
|
129
129
|
sonar_required)
|
|
130
130
|
if [[ -z "${SONAR_TOKEN:-}" || -z "${SONAR_HOST_URL:-}" ]]; then
|
|
131
131
|
log_stage_skip "$stage_name" "SONAR_TOKEN or SONAR_HOST_URL not set"
|
|
132
|
-
((SKIPPED_STAGES
|
|
132
|
+
SKIPPED_STAGES=$((SKIPPED_STAGES + 1))
|
|
133
133
|
return 0
|
|
134
134
|
fi
|
|
135
135
|
;;
|
|
@@ -139,21 +139,21 @@ run_stage() {
|
|
|
139
139
|
|
|
140
140
|
if [[ ! -f "$stage_script" ]]; then
|
|
141
141
|
log_stage_skip "$stage_name" "Script not found: $stage_script"
|
|
142
|
-
((SKIPPED_STAGES
|
|
142
|
+
SKIPPED_STAGES=$((SKIPPED_STAGES + 1))
|
|
143
143
|
return 0
|
|
144
144
|
fi
|
|
145
145
|
|
|
146
146
|
local output
|
|
147
147
|
if output=$(bash "$stage_script" 2>&1); then
|
|
148
148
|
log_stage_pass "$stage_name"
|
|
149
|
-
((PASSED_STAGES
|
|
149
|
+
PASSED_STAGES=$((PASSED_STAGES + 1))
|
|
150
150
|
STAGE_RESULTS+=("PASS:$stage_name")
|
|
151
151
|
if [[ "$VERBOSE" == "true" ]]; then
|
|
152
152
|
echo "$output"
|
|
153
153
|
fi
|
|
154
154
|
else
|
|
155
155
|
log_stage_fail "$stage_name" "$output"
|
|
156
|
-
((FAILED_STAGES
|
|
156
|
+
FAILED_STAGES=$((FAILED_STAGES + 1))
|
|
157
157
|
STAGE_RESULTS+=("FAIL:$stage_name:$(echo "$output" | head -5)")
|
|
158
158
|
fi
|
|
159
159
|
}
|
|
@@ -65,8 +65,8 @@ log_pass() {
|
|
|
65
65
|
if [[ "$JSON" == "false" ]]; then
|
|
66
66
|
echo -e " ${GREEN}[PASS]${NC} $1 (${2}s)"
|
|
67
67
|
fi
|
|
68
|
-
((PASSED
|
|
69
|
-
((TOTAL
|
|
68
|
+
PASSED=$((PASSED + 1))
|
|
69
|
+
TOTAL=$((TOTAL + 1))
|
|
70
70
|
RESULTS+=("{\"name\": \"$1\", \"status\": \"pass\", \"message\": \"\", \"duration\": $2}")
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -74,8 +74,8 @@ log_fail() {
|
|
|
74
74
|
if [[ "$JSON" == "false" ]]; then
|
|
75
75
|
echo -e " ${RED}[FAIL]${NC} $1 — $2 (${3}s)"
|
|
76
76
|
fi
|
|
77
|
-
((FAILED
|
|
78
|
-
((TOTAL
|
|
77
|
+
FAILED=$((FAILED + 1))
|
|
78
|
+
TOTAL=$((TOTAL + 1))
|
|
79
79
|
RESULTS+=("{\"name\": \"$1\", \"status\": \"fail\", \"message\": \"$2\", \"duration\": $3}")
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -83,8 +83,8 @@ log_skip() {
|
|
|
83
83
|
if [[ "$JSON" == "false" ]]; then
|
|
84
84
|
echo -e " ${YELLOW}[SKIP]${NC} $1 — $2"
|
|
85
85
|
fi
|
|
86
|
-
((SKIPPED
|
|
87
|
-
((TOTAL
|
|
86
|
+
SKIPPED=$((SKIPPED + 1))
|
|
87
|
+
TOTAL=$((TOTAL + 1))
|
|
88
88
|
RESULTS+=("{\"name\": \"$1\", \"status\": \"skip\", \"message\": \"$2\", \"duration\": 0}")
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -13,8 +13,8 @@ ARCH_DIR="${PI_DIR}/architecture"
|
|
|
13
13
|
PASS=0
|
|
14
14
|
FAIL=0
|
|
15
15
|
|
|
16
|
-
log_pass() { echo " ✓ PASS: $1"; ((PASS
|
|
17
|
-
log_fail() { echo " ✗ FAIL: $1 — $2"; ((FAIL
|
|
16
|
+
log_pass() { echo " ✓ PASS: $1"; PASS=$((PASS + 1)); }
|
|
17
|
+
log_fail() { echo " ✗ FAIL: $1 — $2"; FAIL=$((FAIL + 1)); }
|
|
18
18
|
|
|
19
19
|
echo " Checking MR traceability..."
|
|
20
20
|
|
|
@@ -26,7 +26,7 @@ if command -v git &>/dev/null; then
|
|
|
26
26
|
files_without_ref=0
|
|
27
27
|
while IFS= read -r file; do
|
|
28
28
|
if ! grep -q "Canonical Reference\|canonical.*reference" "$file" 2>/dev/null; then
|
|
29
|
-
((files_without_ref
|
|
29
|
+
files_without_ref=$((files_without_ref + 1))
|
|
30
30
|
log_fail "MR traceability" "${file} missing canonical reference to architecture"
|
|
31
31
|
fi
|
|
32
32
|
done <<< "$changed_files"
|
|
@@ -11,8 +11,8 @@ set -euo pipefail
|
|
|
11
11
|
FAIL=0
|
|
12
12
|
PASS=0
|
|
13
13
|
|
|
14
|
-
log_pass() { echo " ✓ PASS: $1"; ((PASS
|
|
15
|
-
log_fail() { echo " ✗ FAIL: $1 — $2"; ((FAIL
|
|
14
|
+
log_pass() { echo " ✓ PASS: $1"; PASS=$((PASS + 1)); }
|
|
15
|
+
log_fail() { echo " ✗ FAIL: $1 — $2"; FAIL=$((FAIL + 1)); }
|
|
16
16
|
|
|
17
17
|
echo " Running lint checks..."
|
|
18
18
|
|
|
@@ -13,8 +13,8 @@ PI_DIR=".pi"
|
|
|
13
13
|
FAIL=0
|
|
14
14
|
PASS=0
|
|
15
15
|
|
|
16
|
-
log_pass() { echo " ✓ PASS: $1"; ((PASS
|
|
17
|
-
log_fail() { echo " ✗ FAIL: $1 — $2"; ((FAIL
|
|
16
|
+
log_pass() { echo " ✓ PASS: $1"; PASS=$((PASS + 1)); }
|
|
17
|
+
log_fail() { echo " ✗ FAIL: $1 — $2"; FAIL=$((FAIL + 1)); }
|
|
18
18
|
|
|
19
19
|
STAGE="${1:-all}"
|
|
20
20
|
|
|
@@ -69,7 +69,7 @@ run_stage_7_security() {
|
|
|
69
69
|
local secrets_found=0
|
|
70
70
|
for pattern in "sk-[A-Za-z0-9]{32,}" "ghp_[A-Za-z0-9]{36}" "AKIA[0-9A-Z]{16}" "BEGIN (RSA |EC )?PRIVATE KEY"; do
|
|
71
71
|
if grep -rE "$pattern" . --include="*.py" --include="*.ts" --include="*.env" 2>/dev/null | grep -v ".git" | grep -v "node_modules" | head -1 | grep -q .; then
|
|
72
|
-
((secrets_found
|
|
72
|
+
secrets_found=$((secrets_found + 1))
|
|
73
73
|
log_fail "secret scan" "Potential secret pattern detected: $pattern"
|
|
74
74
|
fi
|
|
75
75
|
done
|
|
@@ -12,8 +12,8 @@ set -euo pipefail
|
|
|
12
12
|
FAIL=0
|
|
13
13
|
PASS=0
|
|
14
14
|
|
|
15
|
-
log_pass() { echo " ✓ PASS: $1"; ((PASS
|
|
16
|
-
log_fail() { echo " ✗ FAIL: $1 — $2"; ((FAIL
|
|
15
|
+
log_pass() { echo " ✓ PASS: $1"; PASS=$((PASS + 1)); }
|
|
16
|
+
log_fail() { echo " ✗ FAIL: $1 — $2"; FAIL=$((FAIL + 1)); }
|
|
17
17
|
|
|
18
18
|
echo " Running static analysis..."
|
|
19
19
|
|
|
@@ -61,7 +61,7 @@ else
|
|
|
61
61
|
if [[ -d "app/domain" ]]; then
|
|
62
62
|
while IFS= read -r file; do
|
|
63
63
|
if grep -qE "from.*infrastructure|from.*api|import.*infrastructure" "$file" 2>/dev/null; then
|
|
64
|
-
((violations
|
|
64
|
+
violations=$((violations + 1))
|
|
65
65
|
log_fail "import boundary" "$(basename "$file") imports from infrastructure/api"
|
|
66
66
|
fi
|
|
67
67
|
done < <(find app/domain -name "*.py" 2>/dev/null)
|
|
@@ -93,7 +93,7 @@ if [[ -f ".env.example" && -f ".env" ]]; then
|
|
|
93
93
|
example_val=$(grep "^${var_name}=" ".env.example" 2>/dev/null | cut -d= -f2-)
|
|
94
94
|
actual_val=$(grep "^${var_name}=" ".env" 2>/dev/null | cut -d= -f2-)
|
|
95
95
|
if [[ "$example_val" == "$actual_val" && -n "$example_val" ]]; then
|
|
96
|
-
((collisions
|
|
96
|
+
collisions=$((collisions + 1))
|
|
97
97
|
log_fail "env collision" "${var_name} in .env matches .env.example value"
|
|
98
98
|
fi
|
|
99
99
|
fi
|
|
@@ -14,8 +14,8 @@ FAIL=0
|
|
|
14
14
|
PASS=0
|
|
15
15
|
COVERAGE_PCT=0
|
|
16
16
|
|
|
17
|
-
log_pass() { echo " ✓ PASS: $1"; ((PASS
|
|
18
|
-
log_fail() { echo " ✗ FAIL: $1 — $2"; ((FAIL
|
|
17
|
+
log_pass() { echo " ✓ PASS: $1"; PASS=$((PASS + 1)); }
|
|
18
|
+
log_fail() { echo " ✗ FAIL: $1 — $2"; FAIL=$((FAIL + 1)); }
|
|
19
19
|
|
|
20
20
|
echo " Running unit tests..."
|
|
21
21
|
|
|
@@ -48,13 +48,13 @@ check_section_present() {
|
|
|
48
48
|
local section="$1"
|
|
49
49
|
if echo "$CONTENT" | grep -qi "$section"; then
|
|
50
50
|
RESULTS+=("{\"check\": \"$section\", \"status\": \"pass\"}")
|
|
51
|
-
((CHECKS_PASSED
|
|
51
|
+
CHECKS_PASSED=$((CHECKS_PASSED + 1))
|
|
52
52
|
if [[ "$JSON" == "false" ]]; then
|
|
53
53
|
echo -e " ${GREEN}✓${NC} Section present: $section"
|
|
54
54
|
fi
|
|
55
55
|
else
|
|
56
56
|
RESULTS+=("{\"check\": \"$section\", \"status\": \"fail\", \"message\": \"Section '$section' not found\"}")
|
|
57
|
-
((CHECKS_FAILED
|
|
57
|
+
CHECKS_FAILED=$((CHECKS_FAILED + 1))
|
|
58
58
|
if [[ "$JSON" == "false" ]]; then
|
|
59
59
|
echo -e " ${RED}✗${NC} Missing section: $section"
|
|
60
60
|
fi
|
|
@@ -73,20 +73,20 @@ check_no_contradictions() {
|
|
|
73
73
|
must_count=$(echo "$CONTENT" | grep -ci "${topic}.*must " || echo 0)
|
|
74
74
|
must_not_count=$(echo "$CONTENT" | grep -ci "${topic}.*must not\|${topic}.*must not " || echo 0)
|
|
75
75
|
if [[ $must_count -gt 0 && $must_not_count -gt 0 ]]; then
|
|
76
|
-
((contradictions
|
|
76
|
+
contradictions=$((contradictions + 1))
|
|
77
77
|
fi
|
|
78
78
|
fi
|
|
79
79
|
done < <(echo "$CONTENT" | grep -E "^\s*[-*]\s+\*\*" || true)
|
|
80
80
|
|
|
81
81
|
if [[ $contradictions -eq 0 ]]; then
|
|
82
82
|
RESULTS+=("{\"check\": \"no_contradictions\", \"status\": \"pass\"}")
|
|
83
|
-
((CHECKS_PASSED
|
|
83
|
+
CHECKS_PASSED=$((CHECKS_PASSED + 1))
|
|
84
84
|
if [[ "$JSON" == "false" ]]; then
|
|
85
85
|
echo -e " ${GREEN}✓${NC} No internal contradictions"
|
|
86
86
|
fi
|
|
87
87
|
else
|
|
88
88
|
RESULTS+=("{\"check\": \"no_contradictions\", \"status\": \"fail\", \"message\": \"Found $contradictions contradictions\"}")
|
|
89
|
-
((CHECKS_FAILED
|
|
89
|
+
CHECKS_FAILED=$((CHECKS_FAILED + 1))
|
|
90
90
|
if [[ "$JSON" == "false" ]]; then
|
|
91
91
|
echo -e " ${RED}✗${NC} Found $contradictions contradictions"
|
|
92
92
|
fi
|
|
@@ -100,13 +100,13 @@ check_decision_consistency() {
|
|
|
100
100
|
|
|
101
101
|
if [[ $decisions -eq 0 ]] || [[ $evidence -gt 0 ]]; then
|
|
102
102
|
RESULTS+=("{\"check\": \"decision_consistency\", \"status\": \"pass\"}")
|
|
103
|
-
((CHECKS_PASSED
|
|
103
|
+
CHECKS_PASSED=$((CHECKS_PASSED + 1))
|
|
104
104
|
if [[ "$JSON" == "false" ]]; then
|
|
105
105
|
echo -e " ${GREEN}✓${NC} Decision consistency"
|
|
106
106
|
fi
|
|
107
107
|
else
|
|
108
108
|
RESULTS+=("{\"check\": \"decision_consistency\", \"status\": \"fail\", \"message\": \"Decisions without supporting evidence\"}")
|
|
109
|
-
((CHECKS_FAILED
|
|
109
|
+
CHECKS_FAILED=$((CHECKS_FAILED + 1))
|
|
110
110
|
if [[ "$JSON" == "false" ]]; then
|
|
111
111
|
echo -e " ${RED}✗${NC} Decisions without supporting evidence"
|
|
112
112
|
fi
|
|
@@ -120,13 +120,13 @@ check_acceptance_criteria() {
|
|
|
120
120
|
|
|
121
121
|
if [[ $has_criteria -gt 0 ]]; then
|
|
122
122
|
RESULTS+=("{\"check\": \"acceptance_criteria\", \"status\": \"pass\"}")
|
|
123
|
-
((CHECKS_PASSED
|
|
123
|
+
CHECKS_PASSED=$((CHECKS_PASSED + 1))
|
|
124
124
|
if [[ "$JSON" == "false" ]]; then
|
|
125
125
|
echo -e " ${GREEN}✓${NC} Acceptance criteria present"
|
|
126
126
|
fi
|
|
127
127
|
else
|
|
128
128
|
RESULTS+=("{\"check\": \"acceptance_criteria\", \"status\": \"fail\", \"message\": \"No acceptance criteria found\"}")
|
|
129
|
-
((CHECKS_FAILED
|
|
129
|
+
CHECKS_FAILED=$((CHECKS_FAILED + 1))
|
|
130
130
|
if [[ "$JSON" == "false" ]]; then
|
|
131
131
|
echo -e " ${RED}✗${NC} No acceptance criteria found"
|
|
132
132
|
fi
|
|
@@ -138,13 +138,13 @@ check_canonical_references() {
|
|
|
138
138
|
|
|
139
139
|
if [[ $has_refs -gt 0 ]]; then
|
|
140
140
|
RESULTS+=("{\"check\": \"canonical_references\", \"status\": \"pass\"}")
|
|
141
|
-
((CHECKS_PASSED
|
|
141
|
+
CHECKS_PASSED=$((CHECKS_PASSED + 1))
|
|
142
142
|
if [[ "$JSON" == "false" ]]; then
|
|
143
143
|
echo -e " ${GREEN}✓${NC} Canonical references present"
|
|
144
144
|
fi
|
|
145
145
|
else
|
|
146
146
|
RESULTS+=("{\"check\": \"canonical_references\", \"status\": \"fail\", \"message\": \"No canonical references found\"}")
|
|
147
|
-
((CHECKS_FAILED
|
|
147
|
+
CHECKS_FAILED=$((CHECKS_FAILED + 1))
|
|
148
148
|
if [[ "$JSON" == "false" ]]; then
|
|
149
149
|
echo -e " ${RED}✗${NC} No canonical references found"
|
|
150
150
|
fi
|
|
@@ -55,7 +55,7 @@ if [ -d ".pi/architecture/modules" ]; then
|
|
|
55
55
|
if [ -n "$impl_path" ]; then
|
|
56
56
|
if [ ! -f "$impl_path" ]; then
|
|
57
57
|
warn "Architecture references $impl_path but file does not exist"
|
|
58
|
-
((MISSING
|
|
58
|
+
MISSING=$((MISSING + 1))
|
|
59
59
|
fi
|
|
60
60
|
fi
|
|
61
61
|
done < <(grep -oE '`[a-z_/]+\.py`' "$module_file" 2>/dev/null || true)
|
|
@@ -76,9 +76,9 @@ INIT_DOCS=0
|
|
|
76
76
|
INIT_WITHOUT_DOCS=0
|
|
77
77
|
for init_file in $(find "$SRC_DIR" -name "__init__.py" 2>/dev/null | head -20); do
|
|
78
78
|
if grep -qE '"""|"""' "$init_file" 2>/dev/null; then
|
|
79
|
-
((INIT_DOCS
|
|
79
|
+
INIT_DOCS=$((INIT_DOCS + 1))
|
|
80
80
|
else
|
|
81
|
-
((INIT_WITHOUT_DOCS
|
|
81
|
+
INIT_WITHOUT_DOCS=$((INIT_WITHOUT_DOCS + 1))
|
|
82
82
|
fi
|
|
83
83
|
done
|
|
84
84
|
if [ "$INIT_DOCS" -gt 0 ]; then
|