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.
- package/README.md +11 -2
- package/dist/cli.js +232 -16
- package/dist/exports.js +232 -16
- package/package.json +1 -1
- package/templates/languages/rust-patterns.md +5 -2
- package/templates/pi/context/checklists.md +15 -1
- package/templates/pi/domain/exploration.md +9 -0
- package/templates/pi/domain/ubiquitous-language.md +12 -0
- package/templates/pi/extensions/architect-lib/generators.ts +158 -7
- package/templates/pi/extensions/architect-lib/helpers.ts +49 -0
- package/templates/pi/extensions/architect-lib/types.ts +44 -2
- package/templates/pi/extensions/architect.ts +102 -42
- package/templates/pi/extensions/ask-user-question.ts +2 -2
- package/templates/pi/extensions/bash-guard.ts +2 -2
- package/templates/pi/extensions/config-reload.ts +12 -5
- package/templates/pi/extensions/coordinator.ts +2 -2
- package/templates/pi/extensions/curator.ts +1 -1
- package/templates/pi/extensions/domain-explorer.ts +50 -3
- package/templates/pi/extensions/filechanges.ts +2 -2
- package/templates/pi/extensions/goal-loop.ts +3 -3
- package/templates/pi/extensions/hooks.ts +1 -1
- package/templates/pi/extensions/pi-env.d.ts +96 -0
- package/templates/pi/extensions/pipeline.ts +1 -1
- package/templates/pi/extensions/project-scaffolder.ts +1 -1
- package/templates/pi/extensions/read-only-mode.ts +5 -1
- package/templates/pi/extensions/tsconfig.json +9 -2
- package/templates/pi/preflight_report.json +1 -1
- package/templates/pi/prompts/ci-blueprint.md +2 -2
- package/templates/pi/scripts/check_model_judgment.sh +28 -0
- package/templates/pi/scripts/ci/check_judgment_contracts.sh +93 -0
- package/templates/pi/scripts/ci/run_preflight.sh +3 -3
- package/templates/pi/scripts/ci/run_stage.sh +1 -1
- package/templates/pi/scripts/ci/stage_lint.sh +1 -1
- package/templates/pi/scripts/coverage.sh +58 -0
- package/templates/pi/scripts/install_coverage_tools.sh +43 -0
- package/templates/pi/scripts/languages/go/check_model_judgment.sh +70 -0
- package/templates/pi/scripts/languages/go/check_runtime_reachability.sh +34 -0
- package/templates/pi/scripts/languages/go/coverage.sh +78 -0
- package/templates/pi/scripts/languages/go/install_coverage_tools.sh +17 -0
- package/templates/pi/scripts/languages/java/check_model_judgment.sh +142 -0
- package/templates/pi/scripts/languages/java/check_runtime_reachability.sh +31 -0
- package/templates/pi/scripts/languages/java/coverage.sh +103 -0
- package/templates/pi/scripts/languages/java/install_coverage_tools.sh +40 -0
- package/templates/pi/scripts/languages/java/validate-architecture.sh +20 -0
- package/templates/pi/scripts/languages/python/check_model_judgment.sh +67 -0
- package/templates/pi/scripts/languages/python/check_runtime_reachability.sh +34 -0
- package/templates/pi/scripts/languages/python/coverage.sh +88 -0
- package/templates/pi/scripts/languages/python/install_coverage_tools.sh +22 -0
- package/templates/pi/scripts/languages/rust/check_model_judgment.sh +61 -0
- package/templates/pi/scripts/languages/rust/check_runtime_reachability.sh +40 -0
- package/templates/pi/scripts/languages/rust/coverage.sh +79 -0
- package/templates/pi/scripts/languages/rust/install_coverage_tools.sh +20 -0
- package/templates/pi/scripts/languages/rust/validate-tests.sh +9 -6
- package/templates/pi/scripts/languages/typescript/check_model_judgment.sh +81 -0
- package/templates/pi/scripts/languages/typescript/check_runtime_reachability.sh +41 -0
- package/templates/pi/scripts/languages/typescript/coverage.sh +154 -0
- package/templates/pi/scripts/languages/typescript/install_coverage_tools.sh +41 -0
- package/templates/pi/scripts/languages/typescript/validate-canonical.sh +6 -2
- package/templates/pi/scripts/languages/typescript/validate-ci.sh +2 -2
- package/templates/pi/scripts/validate-architecture-readiness.sh +29 -0
- package/templates/pi/scripts/validate-exploration-depth.sh +112 -0
- package/templates/pi/scripts/validate-runtime-wiring.sh +35 -0
- package/templates/pi/skills/agents/architecture-validator.md +28 -0
- package/templates/pi/skills/agents/test-validator.md +1 -1
- package/templates/pi/skills/go-enterprise-codegen.md +46 -0
- package/templates/pi/skills/java-spring-enterprise-codegen.md +50 -0
- package/templates/pi/skills/python-enterprise-codegen.md +46 -0
- package/templates/pi/skills/rust-enterprise-codegen.md +46 -0
- 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
|