guardian-framework 0.1.105 → 0.1.110

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 (69) hide show
  1. package/README.md +11 -2
  2. package/dist/cli.js +232 -16
  3. package/dist/exports.js +232 -16
  4. package/package.json +1 -1
  5. package/templates/languages/rust-patterns.md +5 -2
  6. package/templates/pi/context/checklists.md +15 -1
  7. package/templates/pi/domain/exploration.md +9 -0
  8. package/templates/pi/domain/ubiquitous-language.md +12 -0
  9. package/templates/pi/extensions/architect-lib/generators.ts +158 -7
  10. package/templates/pi/extensions/architect-lib/helpers.ts +49 -0
  11. package/templates/pi/extensions/architect-lib/types.ts +44 -2
  12. package/templates/pi/extensions/architect.ts +102 -42
  13. package/templates/pi/extensions/ask-user-question.ts +2 -2
  14. package/templates/pi/extensions/bash-guard.ts +2 -2
  15. package/templates/pi/extensions/config-reload.ts +12 -5
  16. package/templates/pi/extensions/coordinator.ts +2 -2
  17. package/templates/pi/extensions/curator.ts +1 -1
  18. package/templates/pi/extensions/domain-explorer.ts +50 -3
  19. package/templates/pi/extensions/filechanges.ts +2 -2
  20. package/templates/pi/extensions/goal-loop.ts +3 -3
  21. package/templates/pi/extensions/hooks.ts +1 -1
  22. package/templates/pi/extensions/pi-env.d.ts +96 -0
  23. package/templates/pi/extensions/pipeline.ts +1 -1
  24. package/templates/pi/extensions/project-scaffolder.ts +1 -1
  25. package/templates/pi/extensions/read-only-mode.ts +5 -1
  26. package/templates/pi/extensions/tsconfig.json +9 -2
  27. package/templates/pi/preflight_report.json +1 -1
  28. package/templates/pi/prompts/ci-blueprint.md +2 -2
  29. package/templates/pi/scripts/check_model_judgment.sh +28 -0
  30. package/templates/pi/scripts/ci/check_judgment_contracts.sh +93 -0
  31. package/templates/pi/scripts/ci/run_preflight.sh +3 -3
  32. package/templates/pi/scripts/ci/run_stage.sh +1 -1
  33. package/templates/pi/scripts/ci/stage_lint.sh +1 -1
  34. package/templates/pi/scripts/coverage.sh +58 -0
  35. package/templates/pi/scripts/install_coverage_tools.sh +43 -0
  36. package/templates/pi/scripts/languages/go/check_model_judgment.sh +70 -0
  37. package/templates/pi/scripts/languages/go/check_runtime_reachability.sh +34 -0
  38. package/templates/pi/scripts/languages/go/coverage.sh +78 -0
  39. package/templates/pi/scripts/languages/go/install_coverage_tools.sh +17 -0
  40. package/templates/pi/scripts/languages/java/check_model_judgment.sh +142 -0
  41. package/templates/pi/scripts/languages/java/check_runtime_reachability.sh +31 -0
  42. package/templates/pi/scripts/languages/java/coverage.sh +103 -0
  43. package/templates/pi/scripts/languages/java/install_coverage_tools.sh +40 -0
  44. package/templates/pi/scripts/languages/java/validate-architecture.sh +20 -0
  45. package/templates/pi/scripts/languages/python/check_model_judgment.sh +67 -0
  46. package/templates/pi/scripts/languages/python/check_runtime_reachability.sh +34 -0
  47. package/templates/pi/scripts/languages/python/coverage.sh +88 -0
  48. package/templates/pi/scripts/languages/python/install_coverage_tools.sh +22 -0
  49. package/templates/pi/scripts/languages/rust/check_model_judgment.sh +61 -0
  50. package/templates/pi/scripts/languages/rust/check_runtime_reachability.sh +40 -0
  51. package/templates/pi/scripts/languages/rust/coverage.sh +79 -0
  52. package/templates/pi/scripts/languages/rust/install_coverage_tools.sh +20 -0
  53. package/templates/pi/scripts/languages/rust/validate-tests.sh +9 -6
  54. package/templates/pi/scripts/languages/typescript/check_model_judgment.sh +81 -0
  55. package/templates/pi/scripts/languages/typescript/check_runtime_reachability.sh +41 -0
  56. package/templates/pi/scripts/languages/typescript/coverage.sh +154 -0
  57. package/templates/pi/scripts/languages/typescript/install_coverage_tools.sh +41 -0
  58. package/templates/pi/scripts/languages/typescript/validate-canonical.sh +6 -2
  59. package/templates/pi/scripts/languages/typescript/validate-ci.sh +2 -2
  60. package/templates/pi/scripts/validate-architecture-readiness.sh +29 -0
  61. package/templates/pi/scripts/validate-exploration-depth.sh +112 -0
  62. package/templates/pi/scripts/validate-runtime-wiring.sh +35 -0
  63. package/templates/pi/skills/agents/architecture-validator.md +28 -0
  64. package/templates/pi/skills/agents/test-validator.md +1 -1
  65. package/templates/pi/skills/go-enterprise-codegen.md +46 -0
  66. package/templates/pi/skills/java-spring-enterprise-codegen.md +50 -0
  67. package/templates/pi/skills/python-enterprise-codegen.md +46 -0
  68. package/templates/pi/skills/rust-enterprise-codegen.md +46 -0
  69. package/templates/pi/skills/typescript-enterprise-codegen.md +46 -0
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # install_coverage_tools.sh — Install the project's real coverage tooling
4
+ # (dispatcher)
5
+ #
6
+ # Delegates to languages/<lang>/install_coverage_tools.sh based on the
7
+ # detected language. Run this before `.pi/scripts/coverage.sh --gate` in CI
8
+ # or on a fresh checkout.
9
+ #
10
+ # Usage:
11
+ # bash .pi/scripts/install_coverage_tools.sh
12
+ #
13
+ # Exit codes:
14
+ # 0 — tooling available / installed / not required
15
+ # 1 — install failed
16
+ # ============================================================================
17
+ set -uo pipefail
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
20
+
21
+ detect_language() {
22
+ if [ -f "Cargo.lock" ] || [ -f "Cargo.toml" ]; then echo "rust"
23
+ elif [ -f "poetry.lock" ] || [ -f "pyproject.toml" ] || [ -f "requirements.txt" ]; then echo "python"
24
+ elif [ -f "go.mod" ]; then echo "go"
25
+ elif [ -f "package.json" ]; then echo "typescript"
26
+ elif [ -f "pom.xml" ] || [ -f "build.gradle" ] || [ -f "build.gradle.kts" ]; then echo "java"
27
+ else echo "unknown"; fi
28
+ }
29
+
30
+ LANG="$(detect_language)"
31
+
32
+ if [ "$LANG" = "unknown" ]; then
33
+ echo "⚠️ No project language detected — nothing to install." >&2
34
+ exit 0
35
+ fi
36
+
37
+ LANG_SCRIPT="${SCRIPT_DIR}/languages/${LANG}/install_coverage_tools.sh"
38
+ if [ ! -f "$LANG_SCRIPT" ]; then
39
+ echo "⚠️ No coverage installer for '${LANG}' yet (${LANG_SCRIPT})." >&2
40
+ exit 0
41
+ fi
42
+
43
+ exec bash "$LANG_SCRIPT" "$@"
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # check_model_judgment.sh — Go (ADR-008/009)
4
+ # Deterministic hard gates:
5
+ # dip-domain domain/ imports gorm/sqlx/gin/echo
6
+ # txn-placement sql.Tx / BeginTx outside application/
7
+ # interface-ownership (tactical) infra adapters without a domain interface
8
+ # adapter-purity (adapter modules) model code in generic subdomains
9
+ # Usage: bash check_model_judgment.sh [--root <dir>]
10
+ # ============================================================================
11
+ set -uo pipefail
12
+ ROOT="$(pwd)"
13
+ if [ "${1:-}" = "--root" ] && [ -n "${2:-}" ]; then ROOT="$2"; fi
14
+ cd "$ROOT" || exit 1
15
+ VIOLATIONS=0
16
+ report() { echo "$1:$2: [$3] $4"; VIOLATIONS=$((VIOLATIONS + 1)); }
17
+ [ -d src ] || { echo "model-judgment (go): no src/"; exit 0; }
18
+
19
+ while IFS= read -r f; do
20
+ [ -n "$f" ] || continue
21
+ case "$f" in */domain/*)
22
+ while IFS=: read -r ln rest; do
23
+ case "$rest" in
24
+ *gorm.io*|*database/sql*|*sqlx*|*gin-gonic*|*labstack*|*uptrace/bun*)
25
+ report "$f" "$ln" "dip-domain" "domain imports persistence/web framework: $rest" ;;
26
+ esac
27
+ done < <(grep -nE '^\s*("|_ "|import )' "$f" | grep -E 'gorm|sqlx|database/sql|gin-gonic|labstack|bun' || true) ;;
28
+ esac
29
+ case "$f" in */application/*) ;; *)
30
+ if grep -qE "sql\.Tx|BeginTx|\.Commit\(\)" "$f" 2>/dev/null; then
31
+ report "$f" "1" "txn-placement" "transaction handling outside application layer"
32
+ fi ;;
33
+ esac
34
+ done < <(find src -type f -name "*.go" 2>/dev/null | grep -vE "_test\.go$|/tests?/" || true)
35
+
36
+ if [ -d ".pi/architecture/modules" ]; then
37
+ for doc in .pi/architecture/modules/*.md; do
38
+ [ -f "$doc" ] || continue
39
+ doc_name="$(basename "$doc" .md)"
40
+ depth="$(grep -m1 '^\*\*Modeling Depth:\*\*' "$doc" | sed 's/.*\*\*Modeling Depth:\*\* *//' | cut -d' ' -f1 || true)"
41
+ docnorm="$(echo "$doc_name" | sed 's/^module[-_]//' | tr '[:upper:]' '[:lower:]' | tr '-' '_')"
42
+ match_dir=""
43
+ while IFS= read -r d; do
44
+ norm="$(basename "$d" | tr '[:upper:]' '[:lower:]' | tr '-' '_')"
45
+ if [ "$norm" = "$docnorm" ]; then match_dir="$d"; break; fi
46
+ done < <(find src -mindepth 1 -maxdepth 1 -type d 2>/dev/null || true)
47
+ [ -n "$match_dir" ] || continue
48
+ if [ "$depth" = "adapter" ]; then
49
+ for layer in domain application; do
50
+ while IFS= read -r f; do [ -n "$f" ] || continue; report "$f" "1" "adapter-purity" "generic subdomain '${doc_name}' must not contain ${layer}/ model code (ADR-008)"; done < <(find "$match_dir/$layer" -type f -name "*.go" 2>/dev/null || true)
51
+ done
52
+ elif [ "$depth" = "tactical" ]; then
53
+ while IFS= read -r impl; do
54
+ [ -n "$impl" ] || continue
55
+ simple="$(basename "$impl" .go)"
56
+ stem="$(echo "$simple" | sed -E 's/Impl$//;s/impl$//')"
57
+ if ! grep -rqiE "type ${stem} interface|type ${simple} interface|${stem}" "$match_dir/domain" 2>/dev/null; then
58
+ compact="$(printf '%s' "$simple" | tr -d '_-')"
59
+ if [ -z "$compact" ] || ! grep -rqil "$compact" "$match_dir/domain" 2>/dev/null; then
60
+ report "$impl" "1" "interface-ownership" "adapter without a domain/-owned interface (${simple})"
61
+ fi
62
+ fi
63
+ done < <(find "$match_dir/infrastructure" -type f -name "*.go" 2>/dev/null || true)
64
+ fi
65
+ done
66
+ fi
67
+
68
+ if [ "$VIOLATIONS" -gt 0 ]; then echo "model-judgment (go): ${VIOLATIONS} violation(s)" >&2; exit 1; fi
69
+ echo "model-judgment (go): all gates passed (dip-domain, txn-placement, interface-ownership, adapter-purity)"
70
+ exit 0
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # check_runtime_reachability.sh — Go (ADR-009)
4
+ #
5
+ # Every service file under src/<module>/application/ must be referenced from
6
+ # production code OUTSIDE its own module and OUTSIDE tests. Candidates: the
7
+ # file stem (snake), its PascalCase type name, and the separator-stripped
8
+ # form (case-insensitive).
9
+ # Usage: bash check_runtime_reachability.sh [--root <dir>]
10
+ # Exit: 0 all reachable ; 1 dead service(s)
11
+ # ============================================================================
12
+ set -uo pipefail
13
+ ROOT="$(pwd)"
14
+ if [ "${1:-}" = "--root" ] && [ -n "${2:-}" ]; then ROOT="$2"; fi
15
+ cd "$ROOT" || exit 1
16
+ VIOLATIONS=0
17
+ [ -d src ] || { echo "runtime-reachability (go): no src/"; exit 0; }
18
+
19
+ while IFS= read -r svc; do
20
+ [ -n "$svc" ] || continue
21
+ mod="$(echo "$svc" | sed 's|^src/||; s|/application/.*||')"
22
+ stem="$(basename "$svc" .go)"
23
+ pascal="$(echo "$stem" | python3 -c 'import sys; s=sys.stdin.read().strip(); print("".join(w.capitalize() for w in s.split("_")) if s else "")' 2>/dev/null || echo "$stem")"
24
+ compact="$(echo "$stem" | tr -d '_-')"
25
+ refs="$(grep -rliE "\b(${pascal}|${stem}|${compact})\b" src 2>/dev/null | grep -vE "_test\.go$|/tests?/|/test/" | grep -v "^src/${mod}/" | grep -v "^${svc}$" | grep -cv '^$' || true)"
26
+ if [ "${refs:-0}" -eq 0 ]; then
27
+ echo "$svc: [runtime-reachability] application service has no production caller outside its module (tests-only callers don't count — ADR-009)"
28
+ VIOLATIONS=$((VIOLATIONS + 1))
29
+ fi
30
+ done < <(find src -path "*/application/*.go" -type f 2>/dev/null | grep -vE "_test\.go$" || true)
31
+
32
+ if [ "$VIOLATIONS" -gt 0 ]; then echo "runtime-wiring (go): ${VIOLATIONS} unwired service(s)" >&2; exit 1; fi
33
+ echo "runtime-reachability (go): all application services reachable from production code"
34
+ exit 0
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # coverage.sh — Go: real line coverage via `go test -coverprofile`
4
+ #
5
+ # Runs the full test suite with a coverage profile and reports total line
6
+ # coverage from `go tool cover -func`.
7
+ #
8
+ # Usage (normally via the .pi/scripts/coverage.sh dispatcher):
9
+ # bash coverage.sh # measure coverage
10
+ # bash coverage.sh --gate # fail if lines < COVERAGE_THRESHOLD
11
+ # bash coverage.sh --lcov # keep coverage.out profile artifact
12
+ #
13
+ # Env:
14
+ # COVERAGE_THRESHOLD minimum line-coverage % (default: 60)
15
+ # ============================================================================
16
+ set -uo pipefail
17
+
18
+ COVERAGE_THRESHOLD="${COVERAGE_THRESHOLD:-60}"
19
+ GATE=0
20
+ LCOV=0
21
+
22
+ for arg in "$@"; do
23
+ case "$arg" in
24
+ --gate) GATE=1 ;;
25
+ --lcov) LCOV=1 ;;
26
+ *) echo "unknown arg: $arg" >&2; exit 2 ;;
27
+ esac
28
+ done
29
+
30
+ # Walk up from CWD to the module root (go.mod).
31
+ ROOT="$(pwd)"
32
+ while [ "$ROOT" != "/" ] && [ ! -f "$ROOT/go.mod" ]; do
33
+ ROOT="$(dirname "$ROOT")"
34
+ done
35
+ if [ ! -f "$ROOT/go.mod" ]; then
36
+ echo "✗ No go.mod found in this directory tree" >&2
37
+ exit 1
38
+ fi
39
+ cd "$ROOT" || exit 1
40
+
41
+ if ! command -v go >/dev/null 2>&1; then
42
+ echo "✗ go toolchain not found on PATH" >&2
43
+ exit 1
44
+ fi
45
+
46
+ echo "═══ Coverage (go test -coverprofile) ═══"
47
+ go test -coverprofile=coverage.out ./... 2>&1 | tail -10
48
+
49
+ if [ ! -f "coverage.out" ]; then
50
+ echo "✗ No coverage profile generated (go test -coverprofile failed)" >&2
51
+ exit 1
52
+ fi
53
+
54
+ FUNC_OUT="$(go tool cover -func=coverage.out 2>/dev/null || true)"
55
+ COVERAGE="$(echo "$FUNC_OUT" | awk '/^total:/{print $NF}' | tr -d '%')"
56
+ echo ""
57
+ echo "Line coverage: ${COVERAGE:-unavailable}% (threshold: ${COVERAGE_THRESHOLD}%)"
58
+
59
+ if [ -z "$COVERAGE" ]; then
60
+ echo "✗ Could not extract total coverage from coverage profile" >&2
61
+ exit 1
62
+ fi
63
+
64
+ if [ "$LCOV" -eq 1 ]; then
65
+ echo "coverage profile artifact kept: coverage.out"
66
+ else
67
+ rm -f coverage.out
68
+ fi
69
+
70
+ if [ "$GATE" -eq 1 ]; then
71
+ if awk "BEGIN { exit !($COVERAGE >= $COVERAGE_THRESHOLD) }"; then
72
+ echo "✓ Coverage gate passed"
73
+ else
74
+ echo "✗ Coverage below threshold (${COVERAGE}% < ${COVERAGE_THRESHOLD}%)" >&2
75
+ exit 1
76
+ fi
77
+ fi
78
+ exit 0
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # install_coverage_tools.sh — Go: no extra install required
4
+ #
5
+ # `go test -coverprofile` is built into the Go toolchain. This script exists
6
+ # so the language dispatcher is uniform; it only verifies the toolchain is
7
+ # present.
8
+ # ============================================================================
9
+ set -euo pipefail
10
+
11
+ if command -v go >/dev/null 2>&1; then
12
+ echo "Go coverage tooling is built-in (go version $(go version | awk '{print $3}'))."
13
+ exit 0
14
+ fi
15
+
16
+ echo "✗ go toolchain not found — install Go (https://go.dev/dl/) to use the coverage gate." >&2
17
+ exit 1
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # check_model_judgment_java.sh — ADR-008 Java model-judgment HARD GATES
4
+ #
5
+ # Deterministic, language-scoped checks (contract §5). Fuzzy smells (anemic
6
+ # model, LSP base classes, Helper/Utils, primitive obsession) are NOT gated
7
+ # here — they surface as architecture-validator review questions instead.
8
+ #
9
+ # Rules (stable ids):
10
+ # txn-placement @Transactional outside the application layer
11
+ # dip-domain domain/ imports jakarta/javax.persistence,
12
+ # springframework or hibernate
13
+ # interface-ownership (tactical modules) repository implementation in
14
+ # infrastructure/ without a domain/-owned interface
15
+ # adapter-purity (adapter modules) production java under domain/ or
16
+ # application/ of a generic subdomain
17
+ #
18
+ # Usage:
19
+ # bash check_model_judgment_java.sh [--root <dir>] (default: cwd)
20
+ # Output: one 'file:line: [rule-id] message' per violation.
21
+ # Exit: 0 all pass ; 1 violations found.
22
+ # ============================================================================
23
+ set -uo pipefail
24
+
25
+ ROOT="$(pwd)"
26
+ if [ "${1:-}" = "--root" ] && [ -n "${2:-}" ]; then
27
+ ROOT="$2"
28
+ fi
29
+ cd "$ROOT" || exit 1
30
+
31
+ VIOLATIONS=0
32
+
33
+ report() { # file line rule message
34
+ echo "$1:$2: [$3] $4"
35
+ VIOLATIONS=$((VIOLATIONS + 1))
36
+ }
37
+
38
+ is_prod_java() { # path
39
+ case "$1" in
40
+ */test/*|*/src/test/*) return 1 ;;
41
+ *Test.java|*Tests.java) return 1 ;;
42
+ esac
43
+ return 0
44
+ }
45
+
46
+ # ── Rule 2: DIP — domain imports framework types ─────────────────────────────
47
+ while IFS= read -r f; do
48
+ [ -n "$f" ] || continue
49
+ is_prod_java "$f" || continue
50
+ case "$f" in
51
+ */domain/*)
52
+ while IFS=: read -r ln rest; do
53
+ case "$rest" in
54
+ *jakarta.persistence*|*javax.persistence*|*org.springframework*|*org.hibernate*)
55
+ imp="$(echo "$rest" | sed 's/^[[:space:]]*import[[:space:]]*//; s/;//')"
56
+ report "$f" "$ln" "dip-domain" "domain layer imports framework type: $imp"
57
+ ;;
58
+ esac
59
+ done < <(grep -nE '^[[:space:]]*import[[:space:]]+' "$f" || true)
60
+ ;;
61
+ esac
62
+ done < <(find src -type f -name "*.java" 2>/dev/null || true)
63
+
64
+ # ── Rule 1: transactions only in the application layer ───────────────────────
65
+ while IFS= read -r f; do
66
+ [ -n "$f" ] || continue
67
+ is_prod_java "$f" || continue
68
+ case "$f" in
69
+ */application/*) continue ;;
70
+ esac
71
+ while IFS=: read -r ln rest; do
72
+ case "$rest" in
73
+ *@Transactional*)
74
+ report "$f" "$ln" "txn-placement" "@Transactional must live on application-layer services (transaction boundaries)"
75
+ ;;
76
+ esac
77
+ done < <(grep -nE '@Transactional' "$f" || true)
78
+ done < <(find src -type f -name "*.java" 2>/dev/null || true)
79
+
80
+ # ── Rules 3 & 4: module docs drive depth-aware checks ─────────────────────────
81
+ if [ -d ".pi/architecture/modules" ]; then
82
+ for doc in .pi/architecture/modules/*.md; do
83
+ [ -f "$doc" ] || continue
84
+ doc_name="$(basename "$doc" .md)"
85
+ depth="$(grep -m1 '^\*\*Modeling Depth:\*\*' "$doc" | sed 's/.*\*\*Modeling Depth:\*\* *//' | cut -d' ' -f1 || true)"
86
+ docnorm="$(echo "$doc_name" | sed 's/^module[-_]//' | tr '[:upper:]' '[:lower:]' | tr '-' '_')"
87
+
88
+ match_dir=""
89
+ while IFS= read -r d; do
90
+ [ -n "$d" ] || continue
91
+ norm="$(basename "$d" | tr '[:upper:]' '[:lower:]' | tr '-' '_')"
92
+ if [ "$norm" = "$docnorm" ]; then
93
+ match_dir="$d"
94
+ break
95
+ fi
96
+ done < <(find src -mindepth 1 -maxdepth 1 -type d 2>/dev/null || true)
97
+
98
+ [ -n "$match_dir" ] || continue
99
+
100
+ if [ "$depth" = "adapter" ]; then
101
+ # adapter-purity: no production java under domain/ or application/
102
+ for layer in domain application; do
103
+ while IFS= read -r f; do
104
+ [ -n "$f" ] || continue
105
+ is_prod_java "$f" || continue
106
+ report "$f" "1" "adapter-purity" "generic subdomain '${doc_name}' must not contain ${layer}/ model code (ADR-008)"
107
+ done < <(find "$match_dir/$layer" -type f -name "*.java" 2>/dev/null || true)
108
+ done
109
+ elif [ "$depth" = "tactical" ]; then
110
+ # interface-ownership: infra repository implementations need a
111
+ # domain/-owned interface of the same simple name.
112
+ while IFS= read -r impl; do
113
+ [ -n "$impl" ] || continue
114
+ simple="$(basename "$impl")"
115
+ # Candidate interface names: as-is, Impl-suffix stripped, and
116
+ # common impl prefixes (Jpa/Jdbc/Hibernate/Spring) stripped.
117
+ candidates="$simple
118
+ $(echo "$simple" | sed -E 's/Impl\.java$/.java/')
119
+ $(echo "$simple" | sed -E 's/^(Jpa|Jdbc|Hibernate|Spring)([A-Z].*\.java)$/\2/')"
120
+ domain_iface=""
121
+ while IFS= read -r cand; do
122
+ [ -n "$cand" ] || continue
123
+ if [ -f "$match_dir/domain/$cand" ]; then
124
+ domain_iface="$match_dir/domain/$cand"
125
+ break
126
+ fi
127
+ done <<< "$candidates"
128
+ if [ ! -f "$domain_iface" ]; then
129
+ report "$impl" "1" "interface-ownership" "repository implementation without a domain/-owned interface (interface-ownership)"
130
+ fi
131
+ done < <(find "$match_dir/infrastructure" -type f -name "*Repository*.java" 2>/dev/null || true)
132
+ fi
133
+ done
134
+ fi
135
+
136
+ if [ "$VIOLATIONS" -gt 0 ]; then
137
+ echo "model-judgment: ${VIOLATIONS} violation(s) found" >&2
138
+ exit 1
139
+ fi
140
+
141
+ echo "model-judgment (java): all gates passed (txn-placement, dip-domain, interface-ownership, adapter-purity)"
142
+ exit 0
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # check_runtime_reachability.sh — Java (ADR-009)
4
+ #
5
+ # Every application-layer class under src/<module>/application/ must be
6
+ # referenced from production code OUTSIDE its own module and OUTSIDE tests.
7
+ # Java file == public class: the candidate is the file stem.
8
+ # Usage: bash check_runtime_reachability.sh [--root <dir>]
9
+ # Exit: 0 all reachable ; 1 dead service(s)
10
+ # ============================================================================
11
+ set -uo pipefail
12
+ ROOT="$(pwd)"
13
+ if [ "${1:-}" = "--root" ] && [ -n "${2:-}" ]; then ROOT="$2"; fi
14
+ cd "$ROOT" || exit 1
15
+ VIOLATIONS=0
16
+ [ -d src ] || { echo "runtime-reachability (java): no src/"; exit 0; }
17
+
18
+ while IFS= read -r svc; do
19
+ [ -n "$svc" ] || continue
20
+ mod="$(echo "$svc" | sed 's|^src/||; s|/application/.*||')"
21
+ stem="$(basename "$svc" .java)"
22
+ refs="$(grep -rl "\b${stem}\b" src 2>/dev/null | grep -vE "src/test/|/tests?/|Test[s]?\.java$" | grep -v "^src/${mod}/" | grep -v "^${svc}$" | grep -cv '^$' || true)"
23
+ if [ "${refs:-0}" -eq 0 ]; then
24
+ echo "$svc: [runtime-reachability] application service has no production caller outside its module (tests-only callers don't count — ADR-009)"
25
+ VIOLATIONS=$((VIOLATIONS + 1))
26
+ fi
27
+ done < <(find src -path "*/application/*.java" -type f 2>/dev/null | grep -vE "Test[s]?\.java$" || true)
28
+
29
+ if [ "$VIOLATIONS" -gt 0 ]; then echo "runtime-wiring (java): ${VIOLATIONS} unwired service(s)" >&2; exit 1; fi
30
+ echo "runtime-reachability (java): all application services reachable from production code"
31
+ exit 0
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # coverage.sh — Java: real line coverage via JaCoCo (maven/gradle)
4
+ #
5
+ # Runs tests and the JaCoCo report, then parses the TOTAL line-coverage
6
+ # percentage from the generated HTML report.
7
+ #
8
+ # Usage (normally via the .pi/scripts/coverage.sh dispatcher):
9
+ # bash coverage.sh # measure coverage
10
+ # bash coverage.sh --gate # fail if lines < COVERAGE_THRESHOLD
11
+ # bash coverage.sh --lcov # keep the jacoco.xml report artifact
12
+ #
13
+ # Env:
14
+ # COVERAGE_THRESHOLD minimum line-coverage % (default: 60)
15
+ # ============================================================================
16
+ set -uo pipefail
17
+
18
+ COVERAGE_THRESHOLD="${COVERAGE_THRESHOLD:-60}"
19
+ GATE=0
20
+ LCOV=0
21
+
22
+ for arg in "$@"; do
23
+ case "$arg" in
24
+ --gate) GATE=1 ;;
25
+ --lcov) LCOV=1 ;;
26
+ *) echo "unknown arg: $arg" >&2; exit 2 ;;
27
+ esac
28
+ done
29
+
30
+ # Walk up from CWD to the project root (pom.xml / build.gradle[.kts]).
31
+ ROOT="$(pwd)"
32
+ while [ "$ROOT" != "/" ] \
33
+ && [ ! -f "$ROOT/pom.xml" ] \
34
+ && [ ! -f "$ROOT/build.gradle" ] \
35
+ && [ ! -f "$ROOT/build.gradle.kts" ]; do
36
+ ROOT="$(dirname "$ROOT")"
37
+ done
38
+ if [ ! -f "$ROOT/pom.xml" ] && [ ! -f "$ROOT/build.gradle" ] \
39
+ && [ ! -f "$ROOT/build.gradle.kts" ]; then
40
+ echo "✗ No Maven/Gradle build file found in this directory tree" >&2
41
+ exit 1
42
+ fi
43
+ cd "$ROOT" || exit 1
44
+
45
+ REPORT_HTML=""
46
+ COVERAGE=""
47
+
48
+ echo "═══ Coverage (JaCoCo) ═══"
49
+ if [ -f "pom.xml" ]; then
50
+ if ! grep -q "jacoco" pom.xml; then
51
+ echo "✗ JaCoCo not configured in pom.xml — add org.jacoco:jacoco-maven-plugin (with jacoco:report) to enable the coverage gate." >&2
52
+ exit 1
53
+ fi
54
+ if ! command -v mvn >/dev/null 2>&1; then
55
+ echo "✗ mvn not found on PATH" >&2
56
+ exit 1
57
+ fi
58
+ mvn test -q >/dev/null 2>&1 || true
59
+ mvn jacoco:report -q >/dev/null 2>&1 || true
60
+ REPORT_HTML="target/site/jacoco/index.html"
61
+ XML_ARTIFACT="target/site/jacoco/jacoco.xml"
62
+ elif [ -f "build.gradle" ] || [ -f "build.gradle.kts" ]; then
63
+ if ! grep -q "jacoco" build.gradle build.gradle.kts 2>/dev/null; then
64
+ echo "✗ JaCoCo not configured in the Gradle build — add the org.gradle.jacoco plugin to enable the coverage gate." >&2
65
+ exit 1
66
+ fi
67
+ if ! command -v gradle >/dev/null 2>&1; then
68
+ echo "✗ gradle not found on PATH" >&2
69
+ exit 1
70
+ fi
71
+ gradle test jacocoTestReport -q >/dev/null 2>&1 || true
72
+ REPORT_HTML="build/reports/jacoco/test/html/index.html"
73
+ XML_ARTIFACT="build/reports/jacoco/test/jacocoTestReport.xml"
74
+ fi
75
+
76
+ if [ -f "$REPORT_HTML" ]; then
77
+ COVERAGE="$(grep -oP 'Total.*?([0-9]+)%' "$REPORT_HTML" 2>/dev/null | grep -oP '[0-9]+' | head -1 || echo "")"
78
+ fi
79
+ echo ""
80
+ echo "Line coverage: ${COVERAGE:-unavailable}% (threshold: ${COVERAGE_THRESHOLD}%)"
81
+
82
+ if [ -z "$COVERAGE" ]; then
83
+ echo "✗ JaCoCo report not found / percentage not detected at ${REPORT_HTML}" >&2
84
+ exit 1
85
+ fi
86
+
87
+ if [ "$LCOV" -eq 1 ]; then
88
+ if [ -f "$XML_ARTIFACT" ]; then
89
+ echo "jacoco report artifact kept: $XML_ARTIFACT"
90
+ else
91
+ echo "⚠️ jacoco.xml artifact not found at ${XML_ARTIFACT}" >&2
92
+ fi
93
+ fi
94
+
95
+ if [ "$GATE" -eq 1 ]; then
96
+ if [ "$COVERAGE" -ge "$COVERAGE_THRESHOLD" ] 2>/dev/null; then
97
+ echo "✓ Coverage gate passed"
98
+ else
99
+ echo "✗ Coverage below threshold (${COVERAGE}% < ${COVERAGE_THRESHOLD}%)" >&2
100
+ exit 1
101
+ fi
102
+ fi
103
+ exit 0
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # install_coverage_tools.sh — Java: JaCoCo is configured in the build, not
4
+ # installed
5
+ #
6
+ # Enables/verifies the JaCoCo plugin for Maven or Gradle. The coverage gate
7
+ # (.pi/scripts/languages/java/coverage.sh) reads its HTML/XML reports.
8
+ # ============================================================================
9
+ set -euo pipefail
10
+
11
+ if [ -f "pom.xml" ]; then
12
+ if grep -q "jacoco" pom.xml; then
13
+ echo "JaCoCo already configured in pom.xml."
14
+ else
15
+ echo "Adding org.jacoco:jacoco-maven-plugin to pom.xml requires an edit — add to <build><plugins>:"
16
+ cat <<'EOF'
17
+ <plugin>
18
+ <groupId>org.jacoco</groupId>
19
+ <artifactId>jacoco-maven-plugin</artifactId>
20
+ <version>0.8.12</version>
21
+ <executions><execution><goals><goal>prepare-agent</goal><goal>report</goal></goals></execution></executions>
22
+ </plugin>
23
+ EOF
24
+ echo "✗ Not editing pom.xml automatically — apply the snippet above, then re-run." >&2
25
+ exit 1
26
+ fi
27
+ elif [ -f "build.gradle" ] || [ -f "build.gradle.kts" ]; then
28
+ if grep -q "jacoco" build.gradle build.gradle.kts 2>/dev/null; then
29
+ echo "JaCoCo already configured in the Gradle build."
30
+ else
31
+ echo "Add to build.gradle: apply plugin: 'jacoco' (then: gradle jacocoTestReport)" >&2
32
+ echo "✗ Not editing the Gradle build automatically — apply the plugin, then re-run." >&2
33
+ exit 1
34
+ fi
35
+ else
36
+ echo "✗ No Maven/Gradle build file found" >&2
37
+ exit 1
38
+ fi
39
+
40
+ echo "JaCoCo coverage tooling ready."
@@ -137,6 +137,26 @@ else
137
137
  fi
138
138
 
139
139
  # ---------------------------------------------------------------------------
140
+
141
+ # ---------------------------------------------------------------------------
142
+ # ADR-008 model-judgment hard gates (contract §5)
143
+ # ---------------------------------------------------------------------------
144
+ MJ_SCRIPT="$(dirname "$0")/check_model_judgment.sh"
145
+ echo ""
146
+ echo "--- Model Judgment (ADR-008) ---"
147
+ if [ -f "$MJ_SCRIPT" ]; then
148
+ MJ_OUT="$(bash "$MJ_SCRIPT" --root "$(pwd)" 2>&1)" && MJ_EXIT=0 || MJ_EXIT=1
149
+ if [ "$MJ_EXIT" -eq 0 ]; then
150
+ pass "model-judgment hard gates (txn-placement, dip-domain, interface-ownership, adapter-purity)"
151
+ else
152
+ while IFS= read -r mj_line; do
153
+ [ -n "$mj_line" ] && fail "$mj_line"
154
+ done <<< "$MJ_OUT"
155
+ fi
156
+ else
157
+ warn "check_model_judgment.sh not found — model-judgment gates skipped"
158
+ fi
159
+
140
160
  # Summary
141
161
  # ---------------------------------------------------------------------------
142
162
  echo ""
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # check_model_judgment.sh — Python (ADR-008/009)
4
+ # Deterministic hard gates:
5
+ # dip-domain domain/ imports sqlalchemy/django/fastapi
6
+ # txn-placement session.commit/transaction outside application/
7
+ # interface-ownership (tactical) infra adapters without a domain Protocol/ABC
8
+ # adapter-purity (adapter modules) model code in generic subdomains
9
+ # Usage: bash check_model_judgment.sh [--root <dir>]
10
+ # ============================================================================
11
+ set -uo pipefail
12
+ ROOT="$(pwd)"
13
+ if [ "${1:-}" = "--root" ] && [ -n "${2:-}" ]; then ROOT="$2"; fi
14
+ cd "$ROOT" || exit 1
15
+ VIOLATIONS=0
16
+ report() { echo "$1:$2: [$3] $4"; VIOLATIONS=$((VIOLATIONS + 1)); }
17
+ [ -d src ] || { echo "model-judgment (python): no src/"; exit 0; }
18
+
19
+ while IFS= read -r f; do
20
+ [ -n "$f" ] || continue
21
+ case "$f" in */domain/*)
22
+ while IFS=: read -r ln rest; do
23
+ case "$rest" in
24
+ *sqlalchemy*|*django*|*fastapi*|*flask*|*peewee*|*tortoise*)
25
+ report "$f" "$ln" "dip-domain" "domain imports framework/ORM: $rest" ;;
26
+ esac
27
+ done < <(grep -nE "^\s*(import|from) " "$f" || true) ;;
28
+ esac
29
+ case "$f" in */application/*) ;; *)
30
+ if grep -qE "\.session\.(commit|begin)|\.commit\(\)|begin_nested|\.atomic" "$f" 2>/dev/null; then
31
+ report "$f" "1" "txn-placement" "transaction/session handling outside application layer"
32
+ fi ;;
33
+ esac
34
+ done < <(find src -type f -name "*.py" 2>/dev/null | grep -vE "/tests?/|test_.*\.py$|_test\.py$" || true)
35
+
36
+ if [ -d ".pi/architecture/modules" ]; then
37
+ for doc in .pi/architecture/modules/*.md; do
38
+ [ -f "$doc" ] || continue
39
+ doc_name="$(basename "$doc" .md)"
40
+ depth="$(grep -m1 '^\*\*Modeling Depth:\*\*' "$doc" | sed 's/.*\*\*Modeling Depth:\*\* *//' | cut -d' ' -f1 || true)"
41
+ docnorm="$(echo "$doc_name" | sed 's/^module[-_]//' | tr '[:upper:]' '[:lower:]' | tr '-' '_')"
42
+ match_dir=""
43
+ while IFS= read -r d; do
44
+ norm="$(basename "$d" | tr '[:upper:]' '[:lower:]' | tr '-' '_')"
45
+ if [ "$norm" = "$docnorm" ]; then match_dir="$d"; break; fi
46
+ done < <(find src -mindepth 1 -maxdepth 1 -type d 2>/dev/null || true)
47
+ [ -n "$match_dir" ] || continue
48
+ if [ "$depth" = "adapter" ]; then
49
+ for layer in domain application; do
50
+ while IFS= read -r f; do [ -n "$f" ] || continue; report "$f" "1" "adapter-purity" "generic subdomain '${doc_name}' must not contain ${layer}/ model code (ADR-008)"; done < <(find "$match_dir/$layer" -type f -name "*.py" 2>/dev/null || true)
51
+ done
52
+ elif [ "$depth" = "tactical" ]; then
53
+ while IFS= read -r impl; do
54
+ [ -n "$impl" ] || continue
55
+ simple="$(basename "$impl" .py)"
56
+ stem="$(echo "$simple" | sed -E 's/_impl$//;s/Impl$//')"
57
+ if ! grep -rqiE "class ${stem}|class ${simple}|Protocol|ABC" "$match_dir/domain" 2>/dev/null; then
58
+ report "$impl" "1" "interface-ownership" "adapter without a domain/-owned Protocol/ABC (${simple})"
59
+ fi
60
+ done < <(find "$match_dir/infrastructure" -type f -name "*.py" 2>/dev/null || true)
61
+ fi
62
+ done
63
+ fi
64
+
65
+ if [ "$VIOLATIONS" -gt 0 ]; then echo "model-judgment (python): ${VIOLATIONS} violation(s)" >&2; exit 1; fi
66
+ echo "model-judgment (python): all gates passed (dip-domain, txn-placement, interface-ownership, adapter-purity)"
67
+ exit 0