guardian-framework 0.1.105 → 0.1.109

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,34 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # check_runtime_reachability.sh — Python (ADR-009)
4
+ #
5
+ # Every module under src/<module>/application/ must be referenced from
6
+ # production code OUTSIDE its own module and OUTSIDE tests. Candidates: the
7
+ # file stem, its PascalCase class name, and the separator-stripped form
8
+ # (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 (python): 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" .py)"
23
+ pascal="$(echo "$stem" | python3 -c 'import sys; s=sys.stdin.read().strip(); print("".join(w.capitalize() for w in s.replace("-","_").split("_")))' 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 "/tests?/|test_.*\.py$|_test\.py$|/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/*.py" -type f 2>/dev/null | grep -vE "/tests?/|test_.*\.py$|_test\.py$" || true)
31
+
32
+ if [ "$VIOLATIONS" -gt 0 ]; then echo "runtime-wiring (python): ${VIOLATIONS} unwired service(s)" >&2; exit 1; fi
33
+ echo "runtime-reachability (python): all application services reachable from production code"
34
+ exit 0
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # coverage.sh — Python: real line coverage via coverage.py
4
+ #
5
+ # Runs the test suite under coverage.py and reports the TOTAL line coverage.
6
+ # Uses pytest when available, unittest otherwise.
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 # also write coverage.xml (cobertura) 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 (pyproject.toml / setup.py /
31
+ # setup.cfg / requirements.txt).
32
+ ROOT="$(pwd)"
33
+ while [ "$ROOT" != "/" ] \
34
+ && [ ! -f "$ROOT/pyproject.toml" ] \
35
+ && [ ! -f "$ROOT/setup.py" ] \
36
+ && [ ! -f "$ROOT/setup.cfg" ] \
37
+ && [ ! -f "$ROOT/requirements.txt" ]; do
38
+ ROOT="$(dirname "$ROOT")"
39
+ done
40
+ if [ ! -f "$ROOT/pyproject.toml" ] && [ ! -f "$ROOT/setup.py" ] \
41
+ && [ ! -f "$ROOT/setup.cfg" ] && [ ! -f "$ROOT/requirements.txt" ]; then
42
+ echo "✗ No Python project marker found in this directory tree" >&2
43
+ exit 1
44
+ fi
45
+ cd "$ROOT" || exit 1
46
+
47
+ if ! python3 -m coverage --version >/dev/null 2>&1; then
48
+ echo "coverage.py not found — run .pi/scripts/install_coverage_tools.sh" >&2
49
+ exit 1
50
+ fi
51
+
52
+ echo "═══ Coverage (coverage.py) ═══"
53
+ # Scope measurement to the project source (src/ layout when present, else
54
+ # the project root) so site-packages / pytest internals are not counted.
55
+ COV_SOURCE="src"
56
+ [ -d "src" ] || COV_SOURCE="."
57
+ if python3 -m pytest --version >/dev/null 2>&1; then
58
+ python3 -m coverage run --source="$COV_SOURCE" -m pytest -q >/dev/null 2>&1 || true
59
+ else
60
+ python3 -m coverage run --source="$COV_SOURCE" -m unittest discover -s tests >/dev/null 2>&1 || true
61
+ fi
62
+
63
+ REPORT="$(python3 -m coverage report 2>&1 || true)"
64
+ echo "$REPORT" | tail -20
65
+ COVERAGE="$(echo "$REPORT" | awk '/^TOTAL/{print $NF}' | tr -d '%')"
66
+ echo ""
67
+ echo "Line coverage: ${COVERAGE:-unavailable}% (threshold: ${COVERAGE_THRESHOLD}%)"
68
+
69
+ if [ -z "$COVERAGE" ]; then
70
+ echo "✗ Could not extract TOTAL coverage from coverage.py output" >&2
71
+ exit 1
72
+ fi
73
+
74
+ if [ "$LCOV" -eq 1 ]; then
75
+ python3 -m coverage xml -o coverage.xml >/dev/null 2>&1 \
76
+ && echo "cobertura artifact written: coverage.xml" \
77
+ || echo "⚠️ could not write coverage.xml" >&2
78
+ fi
79
+
80
+ if [ "$GATE" -eq 1 ]; then
81
+ if awk "BEGIN { exit !($COVERAGE >= $COVERAGE_THRESHOLD) }"; then
82
+ echo "✓ Coverage gate passed"
83
+ else
84
+ echo "✗ Coverage below threshold (${COVERAGE}% < ${COVERAGE_THRESHOLD}%)" >&2
85
+ exit 1
86
+ fi
87
+ fi
88
+ exit 0
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # install_coverage_tools.sh — Python: install coverage.py (+ pytest)
4
+ #
5
+ # Installs the real coverage tooling used by
6
+ # .pi/scripts/languages/python/coverage.sh.
7
+ # ============================================================================
8
+ set -euo pipefail
9
+
10
+ if python3 -m coverage --version >/dev/null 2>&1; then
11
+ echo "coverage.py already installed: $(python3 -m coverage --version | head -1)"
12
+ else
13
+ echo "Installing coverage.py..."
14
+ python3 -m pip install coverage
15
+ fi
16
+
17
+ if ! python3 -m pytest --version >/dev/null 2>&1; then
18
+ echo "Installing pytest (optional, for test discovery)..."
19
+ python3 -m pip install pytest || true
20
+ fi
21
+
22
+ echo "coverage tooling ready."
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # check_model_judgment.sh — Rust (ADR-008/009)
4
+ # Deterministic hard gates:
5
+ # dip-domain domain/ (crate/module) imports sqlx/diesel/web frameworks
6
+ # interface-ownership (tactical) infra impl without a domain/-owned trait
7
+ # adapter-purity (adapter modules) model code in generic subdomains
8
+ # Usage: bash check_model_judgment.sh [--root <dir>]
9
+ # ============================================================================
10
+ set -uo pipefail
11
+ ROOT="$(pwd)"
12
+ if [ "${1:-}" = "--root" ] && [ -n "${2:-}" ]; then ROOT="$2"; fi
13
+ cd "$ROOT" || exit 1
14
+ VIOLATIONS=0
15
+ report() { echo "$1:$2: [$3] $4"; VIOLATIONS=$((VIOLATIONS + 1)); }
16
+ [ -d src ] || { echo "model-judgment (rust): no src/"; exit 0; }
17
+
18
+ while IFS= read -r f; do
19
+ [ -n "$f" ] || continue
20
+ case "$f" in */domain/*)
21
+ while IFS=: read -r ln rest; do
22
+ case "$rest" in
23
+ *sqlx*|*diesel*|*sea_orm*|*axum*|*actix*|*tokio-postgres*|*mongodb*|*redis*)
24
+ report "$f" "$ln" "dip-domain" "domain imports framework/persistence crate: $(echo "$rest" | sed 's/^use //; s/;.*//')" ;;
25
+ esac
26
+ done < <(grep -nE "^use |^extern crate " "$f" || true) ;;
27
+ esac
28
+ done < <(find src -type f -name "*.rs" 2>/dev/null | grep -vE "_test\.rs$|/tests?/" || true)
29
+
30
+ if [ -d ".pi/architecture/modules" ]; then
31
+ for doc in .pi/architecture/modules/*.md; do
32
+ [ -f "$doc" ] || continue
33
+ doc_name="$(basename "$doc" .md)"
34
+ depth="$(grep -m1 '^\*\*Modeling Depth:\*\*' "$doc" | sed 's/.*\*\*Modeling Depth:\*\* *//' | cut -d' ' -f1 || true)"
35
+ docnorm="$(echo "$doc_name" | sed 's/^module[-_]//' | tr '[:upper:]' '[:lower:]' | tr '-' '_')"
36
+ match_dir=""
37
+ while IFS= read -r d; do
38
+ norm="$(basename "$d" | tr '[:upper:]' '[:lower:]' | tr '-' '_')"
39
+ if [ "$norm" = "$docnorm" ]; then match_dir="$d"; break; fi
40
+ done < <(find src -mindepth 1 -maxdepth 1 -type d 2>/dev/null || true)
41
+ [ -n "$match_dir" ] || continue
42
+ if [ "$depth" = "adapter" ]; then
43
+ for layer in domain application; do
44
+ 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 "*.rs" 2>/dev/null || true)
45
+ done
46
+ elif [ "$depth" = "tactical" ]; then
47
+ while IFS= read -r impl; do
48
+ [ -n "$impl" ] || continue
49
+ simple="$(basename "$impl" .rs)"
50
+ stem="$(echo "$simple" | sed -E 's/_impl$//')"
51
+ if ! grep -rqiE "(pub )?trait ${stem}\\b|trait ${simple}\\b|${stem}" "$match_dir/domain" 2>/dev/null; then
52
+ report "$impl" "1" "interface-ownership" "repository/adapter impl without a domain/-owned trait (${simple})"
53
+ fi
54
+ done < <(find "$match_dir/infrastructure" -type f \( -name "*repository*.rs" -o -name "*repo*.rs" -o -name "*gateway*.rs" \) 2>/dev/null || true)
55
+ fi
56
+ done
57
+ fi
58
+
59
+ if [ "$VIOLATIONS" -gt 0 ]; then echo "model-judgment (rust): ${VIOLATIONS} violation(s)" >&2; exit 1; fi
60
+ echo "model-judgment (rust): all gates passed (dip-domain, interface-ownership, adapter-purity)"
61
+ exit 0
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # check_runtime_reachability.sh — Rust (ADR-009)
4
+ #
5
+ # Every service struct under src/<module>/application/ must be referenced from
6
+ # production code outside its own module and outside tests (tests dirs,
7
+ # *_test.rs, and #[cfg(test)] units inside the same file do not count — we
8
+ # require a reference from a different production file outside the module).
9
+ #
10
+ # Usage: bash check_runtime_reachability.sh [--root <dir>]
11
+ # Exit: 0 all reachable ; 1 dead service(s)
12
+ # ============================================================================
13
+ set -uo pipefail
14
+ ROOT="$(pwd)"
15
+ if [ "${1:-}" = "--root" ] && [ -n "${2:-}" ]; then ROOT="$2"; fi
16
+ cd "$ROOT" || exit 1
17
+
18
+ VIOLATIONS=0
19
+ [ -d src ] || { echo "runtime-reachability (rust): no src/"; exit 0; }
20
+
21
+ while IFS= read -r svc; do
22
+ [ -n "$svc" ] || continue
23
+ mod="$(echo "$svc" | sed 's|^src/||; s|/application/.*||')"
24
+ stem="$(basename "$svc" .rs)"
25
+ # candidate type names: file stem in PascalCase and snake_case both
26
+ 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")"
27
+ # production references: outside the module, excluding tests
28
+ refs="$(grep -rlE "\b(${pascal}|${stem})\b" src 2>/dev/null | grep -vE "/tests?/|_test\.rs$|/test/" | grep -v "^src/${mod}/" | grep -v "^${svc}$" | grep -cv '^$' || true)"
29
+ if [ "${refs:-0}" -eq 0 ]; then
30
+ echo "$svc: [runtime-reachability] application service has no production caller outside its module (tests-only callers don't count — ADR-009)"
31
+ VIOLATIONS=$((VIOLATIONS + 1))
32
+ fi
33
+ done < <(find src -path "*/application/*.rs" -type f 2>/dev/null || true)
34
+
35
+ if [ "$VIOLATIONS" -gt 0 ]; then
36
+ echo "runtime-wiring (rust): ${VIOLATIONS} unwired service(s)" >&2
37
+ exit 1
38
+ fi
39
+ echo "runtime-reachability (rust): all application services reachable from production code"
40
+ exit 0
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # coverage.sh — Rust: real line coverage via cargo-llvm-cov
4
+ #
5
+ # cargo-llvm-cov uses native LLVM instrumentation and works with incremental
6
+ # compilation — ~3x faster than cargo-tarpaulin's full instrumented
7
+ # recompilation. Produces the REAL workspace line-coverage number.
8
+ #
9
+ # Usage (normally via the .pi/scripts/coverage.sh dispatcher):
10
+ # bash coverage.sh # measure workspace coverage
11
+ # bash coverage.sh --gate # fail if lines < COVERAGE_THRESHOLD
12
+ # bash coverage.sh --lcov # also write target/coverage.lcov artifact
13
+ #
14
+ # Env:
15
+ # COVERAGE_THRESHOLD minimum line-coverage % (default: 60)
16
+ # ============================================================================
17
+ set -uo pipefail
18
+
19
+ COVERAGE_THRESHOLD="${COVERAGE_THRESHOLD:-60}"
20
+ GATE=0
21
+ LCOV=0
22
+
23
+ for arg in "$@"; do
24
+ case "$arg" in
25
+ --gate) GATE=1 ;;
26
+ --lcov) LCOV=1 ;;
27
+ *) echo "unknown arg: $arg" >&2; exit 2 ;;
28
+ esac
29
+ done
30
+
31
+ # Walk up from CWD to the crate/workspace root (Cargo.toml).
32
+ ROOT="$(pwd)"
33
+ while [ "$ROOT" != "/" ] && [ ! -f "$ROOT/Cargo.toml" ]; do
34
+ ROOT="$(dirname "$ROOT")"
35
+ done
36
+ if [ ! -f "$ROOT/Cargo.toml" ]; then
37
+ echo "✗ No Cargo.toml found in this directory tree" >&2
38
+ exit 1
39
+ fi
40
+ cd "$ROOT" || exit 1
41
+
42
+ if ! command -v cargo-llvm-cov >/dev/null 2>&1; then
43
+ echo "cargo-llvm-cov not found — run .pi/scripts/install_coverage_tools.sh" >&2
44
+ exit 1
45
+ fi
46
+
47
+ echo "═══ Workspace coverage (cargo llvm-cov) ═══"
48
+ # --summary-only always prints the table we parse. When --lcov is also
49
+ # requested, write the lcov artifact in a second (instrumentation-cached,
50
+ # fast) pass — the tool suppresses the console summary in lcov-output mode.
51
+ SUMMARY="$(cargo llvm-cov --workspace --summary-only)"
52
+ if [ "$LCOV" -eq 1 ]; then
53
+ mkdir -p target
54
+ cargo llvm-cov --workspace --lcov --output-path target/coverage.lcov >/dev/null 2>&1 \
55
+ && echo "lcov artifact written: target/coverage.lcov" \
56
+ || echo "⚠️ could not write target/coverage.lcov" >&2
57
+ fi
58
+
59
+ # Print the per-file table + TOTAL row (summary tail), then extract the
60
+ # TOTAL line-coverage percentage from the final line.
61
+ echo "$SUMMARY" | tail -12
62
+ COVERAGE="$(echo "$SUMMARY" | tail -1 | grep -oE '[0-9]+\.[0-9]+%' | head -1 | sed 's/%//')"
63
+ echo ""
64
+ echo "Line coverage: ${COVERAGE:-unavailable}% (threshold: ${COVERAGE_THRESHOLD}%)"
65
+
66
+ if [ -z "$COVERAGE" ]; then
67
+ echo "✗ Could not extract coverage from cargo llvm-cov output" >&2
68
+ exit 1
69
+ fi
70
+
71
+ if [ "$GATE" -eq 1 ]; then
72
+ if awk "BEGIN { exit !($COVERAGE >= $COVERAGE_THRESHOLD) }"; then
73
+ echo "✓ Coverage gate passed"
74
+ else
75
+ echo "✗ Coverage below threshold (${COVERAGE}% < ${COVERAGE_THRESHOLD}%)" >&2
76
+ exit 1
77
+ fi
78
+ fi
79
+ exit 0
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # install_coverage_tools.sh — Rust: install cargo-llvm-cov
4
+ #
5
+ # cargo-llvm-cov needs the llvm-tools-preview rustup component. It is the
6
+ # real coverage tool used by .pi/scripts/languages/rust/coverage.sh.
7
+ # ============================================================================
8
+ set -euo pipefail
9
+
10
+ rustup component add llvm-tools-preview || echo "llvm-tools-preview unavailable on this toolchain"
11
+
12
+ if command -v cargo-llvm-cov >/dev/null 2>&1; then
13
+ echo "cargo-llvm-cov already installed: $(cargo-llvm-cov --version)"
14
+ exit 0
15
+ fi
16
+
17
+ echo "Installing cargo-llvm-cov..."
18
+ cargo install cargo-llvm-cov --locked
19
+
20
+ echo "cargo-llvm-cov installed: $(cargo-llvm-cov --version)"
@@ -97,13 +97,14 @@ else
97
97
  fi
98
98
 
99
99
  # ---------------------------------------------------------------------------
100
- # Coverage
100
+ # Coverage (uses cargo-llvm-cov — native LLVM instrumentation, ~3x faster than tarpaulin)
101
101
  # ---------------------------------------------------------------------------
102
102
  echo ""
103
103
  echo "--- Coverage ---"
104
- if command -v cargo &>/dev/null && cargo tarpaulin --version &>/dev/null; then
105
- TARP_OUT=$(cargo tarpaulin --out Html 2>&1 || true)
106
- COVERAGE_PCT=$(echo "$TARP_OUT" | grep -oE '[0-9]+(\.[0-9]+)?%' | head -1 | tr -d '%' || echo "")
104
+ if command -v cargo &>/dev/null && cargo llvm-cov --version &>/dev/null; then
105
+ LLCV_OUT=$(cargo llvm-cov --html --fail-under-lines 80 2>&1 || true)
106
+ LLCV_EXIT=$?
107
+ COVERAGE_PCT=$(echo "$LLCV_OUT" | grep -oE '[0-9]+(\.[0-9]+)?%' | head -1 | tr -d '%' || echo "")
107
108
  if [ -n "$COVERAGE_PCT" ]; then
108
109
  # Compare coverage against 80% threshold using awk
109
110
  MEETS_THRESHOLD=$(echo "$COVERAGE_PCT" | awk '{print ($1 >= 80) ? "yes" : "no"}')
@@ -112,8 +113,10 @@ if command -v cargo &>/dev/null && cargo tarpaulin --version &>/dev/null; then
112
113
  else
113
114
  fail "Code coverage: ${COVERAGE_PCT}% (< 80%)"
114
115
  fi
116
+ elif [ "$LLCV_EXIT" -eq 0 ]; then
117
+ pass "Code coverage meets 80% threshold"
115
118
  else
116
- warn "Could not extract coverage percentage from tarpaulin output"
119
+ warn "Could not extract coverage percentage from llvm-cov output"
117
120
  fi
118
121
  elif command -v grcov &>/dev/null; then
119
122
  # Use grcov as fallback
@@ -124,7 +127,7 @@ elif command -v grcov &>/dev/null; then
124
127
  pass "grcov coverage report generated"
125
128
  fi
126
129
  else
127
- warn "No coverage tools available (cargo-tarpaulin / grcov), skipping coverage check"
130
+ warn "No coverage tools available (cargo-llvm-cov / grcov), skipping coverage check"
128
131
  fi
129
132
 
130
133
  # ---------------------------------------------------------------------------
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # check_model_judgment.sh — TypeScript (ADR-008/009)
4
+ # Deterministic hard gates, mirrors the Java four:
5
+ # dip-domain domain/ imports framework/persistence types
6
+ # txn-placement transaction/UnitOfWork boundaries outside application/
7
+ # interface-ownership (tactical) infra adapters without a domain/-owned 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
+
16
+ VIOLATIONS=0
17
+ report() { echo "$1:$2: [$3] $4"; VIOLATIONS=$((VIOLATIONS + 1)); }
18
+
19
+ [ -d src ] || { echo "model-judgment (ts): no src/"; exit 0; }
20
+ while IFS= read -r f; do
21
+ [ -n "$f" ] || continue
22
+ case "$f" in */*test*/|*.test.ts|*.spec.ts) continue ;; esac
23
+ # dip-domain
24
+ case "$f" in */domain/*)
25
+ while IFS=: read -r ln rest; do
26
+ case "$rest" in
27
+ *typeorm*|*prisma*|*@nestjs*|*express*|*sequelize*)
28
+ report "$f" "$ln" "dip-domain" "domain imports framework: $(echo "$rest" | sed 's/^import //; s/;//')" ;;
29
+ esac
30
+ done < <(grep -nE "^import |require\\(" "$f" || true) ;;
31
+ esac
32
+ # txn-placement: transaction/UnitOfWork usage outside application/
33
+ case "$f" in */application/*) ;; *)
34
+ if grep -qE "transaction|unitOfWork|beginTransaction|\.manager\.(save|transaction)" "$f" 2>/dev/null; then
35
+ report "$f" "1" "txn-placement" "transaction/UnitOfWork logic outside application layer"
36
+ fi ;;
37
+ esac
38
+ done < <(find src -type f -name "*.ts" 2>/dev/null || true)
39
+
40
+ # module-doc depth rules (same layout as Java)
41
+ if [ -d ".pi/architecture/modules" ]; then
42
+ for doc in .pi/architecture/modules/*.md; do
43
+ [ -f "$doc" ] || continue
44
+ doc_name="$(basename "$doc" .md)"
45
+ depth="$(grep -m1 '^\*\*Modeling Depth:\*\*' "$doc" | sed 's/.*\*\*Modeling Depth:\*\* *//' | cut -d' ' -f1 || true)"
46
+ docnorm="$(echo "$doc_name" | sed 's/^module[-_]//' | tr '[:upper:]' '[:lower:]' | tr '-' '_')"
47
+ match_dir=""
48
+ while IFS= read -r d; do
49
+ norm="$(basename "$d" | tr '[:upper:]' '[:lower:]' | tr '-' '_')"
50
+ if [ "$norm" = "$docnorm" ]; then match_dir="$d"; break; fi
51
+ done < <(find src -mindepth 1 -maxdepth 1 -type d 2>/dev/null || true)
52
+ [ -n "$match_dir" ] || continue
53
+ if [ "$depth" = "adapter" ]; then
54
+ for layer in domain application; do
55
+ 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 "*.ts" 2>/dev/null || true)
56
+ done
57
+ elif [ "$depth" = "tactical" ]; then
58
+ while IFS= read -r impl; do
59
+ [ -n "$impl" ] || continue
60
+ simple="$(basename "$impl" .ts)"
61
+ cands="$simple
62
+ $(echo "$simple" | sed -E 's/Impl$//')"
63
+ found=""
64
+ while IFS= read -r cand; do
65
+ [ -n "$cand" ] || continue
66
+ if grep -rqiE "interface ${cand}\\b|abstract class ${cand}\\b|${cand}" "$match_dir/domain" 2>/dev/null; then found=1; break; fi
67
+ done <<< "$cands"
68
+ # separator-stripped fallback: invoice-repository <-> InvoiceRepository
69
+ if [ -z "$found" ]; then
70
+ compact="$(printf '%s' "$simple" | tr -d '_-')"
71
+ if [ -n "$compact" ] && grep -rqil "$compact" "$match_dir/domain" 2>/dev/null; then found=1; fi
72
+ fi
73
+ if [ -z "$found" ]; then report "$impl" "1" "interface-ownership" "adapter without a domain/-owned interface (${simple})"; fi
74
+ done < <(find "$match_dir/infrastructure" -type f \( -iname "*repository*.ts" -o -iname "*gateway*.ts" -o -iname "*service*.ts" \) 2>/dev/null || true)
75
+ fi
76
+ done
77
+ fi
78
+
79
+ if [ "$VIOLATIONS" -gt 0 ]; then echo "model-judgment (ts): ${VIOLATIONS} violation(s)" >&2; exit 1; fi
80
+ echo "model-judgment (ts): all gates passed (dip-domain, txn-placement, interface-ownership, adapter-purity)"
81
+ exit 0
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # check_runtime_reachability.sh — TypeScript (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. References inside
7
+ # the service's own module or in *.test.ts / __tests__ do not count.
8
+ #
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
+
17
+ VIOLATIONS=0
18
+ [ -d src ] || { echo "runtime-reachability (ts): no src/"; exit 0; }
19
+
20
+ while IFS= read -r svc; do
21
+ [ -n "$svc" ] || continue
22
+ case "$svc" in
23
+ */*test*/|*.test.ts|*.spec.ts) continue ;;
24
+ esac
25
+ mod="$(echo "$svc" | sed 's|^src/||; s|/application/.*||')"
26
+ name="$(basename "$svc" .ts)"
27
+ # production references: whole src, excluding tests and the service's own module
28
+ refs="$(grep -rl "\b${name}\b" src 2>/dev/null | grep -vE "/__tests__/|\.(test|spec)\.ts$|/tests?/|^src/${mod}/" | grep -v "^src/${mod}/" | grep -cv '^$' || true)"
29
+ # also drop the file itself from any residual (own-module exclusion above already does)
30
+ if [ "${refs:-0}" -eq 0 ]; then
31
+ echo "$svc: [runtime-reachability] application service has no production caller outside its module (tests-only callers don't count — ADR-009)"
32
+ VIOLATIONS=$((VIOLATIONS + 1))
33
+ fi
34
+ done < <(find src -path "*/application/*.ts" -type f 2>/dev/null || true)
35
+
36
+ if [ "$VIOLATIONS" -gt 0 ]; then
37
+ echo "runtime-wiring (ts): ${VIOLATIONS} unwired service(s)" >&2
38
+ exit 1
39
+ fi
40
+ echo "runtime-reachability (ts): all application services reachable from production code"
41
+ exit 0