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 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 ←─ you adopt phases, set priorities
44
+ Roadmap Plan ←─ one module per phase (Phase 0 = bootstrap)
45
45
  │
46
46
  ▼
47
- Epic Execution ─── agents implement, you review PRs
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. Scaffold
219
+ # 1. Init (installs .pi/ — prompts, extensions, validators)
211
220
  cd your-project
212
221
  npx guardian-framework init
213
222
 
214
- # 2. Explore (iterative — review, refine, repeat)
215
- /domain --explore "your business intent" # agent classifies core/supporting/generic — YOU gate it
216
- /domain --validate <session-id> # structural + UL + ADR-008 depth rules
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
- # 4. Enrich module docs with full DDD content
222
- /domain --enrich <session-id>
227
+ # 3. Validate the analysis against the files that were written (drift gate)
228
+ guardian domain validate <session-id>
223
229
 
224
- # 5. Review and refine (ADRs, diagrams, modules — human-in-the-loop)
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
- # 6. Adopt the roadmap
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 --phase "Phase 0"
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.147", 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;
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.147", 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;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardian-framework",
3
- "version": "0.1.147",
3
+ "version": "0.1.149",
4
4
  "description": "Token-optimized agentic framework scaffolder with pi-first architecture",
5
5
  "type": "module",
6
6
  "main": "dist/exports.js",
@@ -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-07T13:34:25Z",
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": 0,
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