guardian-framework 0.1.104 → 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 (71) 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 +163 -12
  10. package/templates/pi/extensions/architect-lib/helpers.ts +49 -0
  11. package/templates/pi/extensions/architect-lib/tdd-generator.ts +1 -1
  12. package/templates/pi/extensions/architect-lib/types.ts +44 -2
  13. package/templates/pi/extensions/architect.ts +105 -41
  14. package/templates/pi/extensions/ask-user-question.ts +2 -2
  15. package/templates/pi/extensions/bash-guard.ts +2 -2
  16. package/templates/pi/extensions/config-reload.ts +12 -5
  17. package/templates/pi/extensions/coordinator.ts +2 -2
  18. package/templates/pi/extensions/curator.ts +1 -1
  19. package/templates/pi/extensions/domain-explorer.ts +50 -3
  20. package/templates/pi/extensions/filechanges.ts +2 -2
  21. package/templates/pi/extensions/goal-loop.ts +3 -3
  22. package/templates/pi/extensions/hooks.ts +1 -1
  23. package/templates/pi/extensions/pi-env.d.ts +96 -0
  24. package/templates/pi/extensions/pipeline.ts +1 -1
  25. package/templates/pi/extensions/project-scaffolder.ts +1 -1
  26. package/templates/pi/extensions/read-only-mode.ts +5 -1
  27. package/templates/pi/extensions/tsconfig.json +9 -2
  28. package/templates/pi/preflight_report.json +1 -1
  29. package/templates/pi/prompts/ci-blueprint.md +2 -2
  30. package/templates/pi/scripts/check_model_judgment.sh +28 -0
  31. package/templates/pi/scripts/ci/check_judgment_contracts.sh +93 -0
  32. package/templates/pi/scripts/ci/run_preflight.sh +3 -3
  33. package/templates/pi/scripts/ci/run_stage.sh +1 -1
  34. package/templates/pi/scripts/ci/stage_lint.sh +1 -1
  35. package/templates/pi/scripts/coverage.sh +58 -0
  36. package/templates/pi/scripts/install_coverage_tools.sh +43 -0
  37. package/templates/pi/scripts/languages/go/check_model_judgment.sh +70 -0
  38. package/templates/pi/scripts/languages/go/check_runtime_reachability.sh +34 -0
  39. package/templates/pi/scripts/languages/go/coverage.sh +78 -0
  40. package/templates/pi/scripts/languages/go/install_coverage_tools.sh +17 -0
  41. package/templates/pi/scripts/languages/java/check_model_judgment.sh +142 -0
  42. package/templates/pi/scripts/languages/java/check_runtime_reachability.sh +31 -0
  43. package/templates/pi/scripts/languages/java/coverage.sh +103 -0
  44. package/templates/pi/scripts/languages/java/install_coverage_tools.sh +40 -0
  45. package/templates/pi/scripts/languages/java/validate-architecture.sh +20 -0
  46. package/templates/pi/scripts/languages/python/check_model_judgment.sh +67 -0
  47. package/templates/pi/scripts/languages/python/check_runtime_reachability.sh +34 -0
  48. package/templates/pi/scripts/languages/python/coverage.sh +88 -0
  49. package/templates/pi/scripts/languages/python/install_coverage_tools.sh +22 -0
  50. package/templates/pi/scripts/languages/rust/check_model_judgment.sh +61 -0
  51. package/templates/pi/scripts/languages/rust/check_runtime_reachability.sh +40 -0
  52. package/templates/pi/scripts/languages/rust/coverage.sh +79 -0
  53. package/templates/pi/scripts/languages/rust/install_coverage_tools.sh +20 -0
  54. package/templates/pi/scripts/languages/rust/validate-tests.sh +9 -6
  55. package/templates/pi/scripts/languages/typescript/check_model_judgment.sh +81 -0
  56. package/templates/pi/scripts/languages/typescript/check_runtime_reachability.sh +41 -0
  57. package/templates/pi/scripts/languages/typescript/coverage.sh +154 -0
  58. package/templates/pi/scripts/languages/typescript/install_coverage_tools.sh +41 -0
  59. package/templates/pi/scripts/languages/typescript/validate-canonical.sh +6 -2
  60. package/templates/pi/scripts/languages/typescript/validate-ci.sh +2 -2
  61. package/templates/pi/scripts/validate-architecture-readiness.sh +29 -0
  62. package/templates/pi/scripts/validate-exploration-depth.sh +112 -0
  63. package/templates/pi/scripts/validate-runtime-wiring.sh +35 -0
  64. package/templates/pi/skills/agents/architecture-validator.md +28 -0
  65. package/templates/pi/skills/agents/test-validator.md +1 -1
  66. package/templates/pi/skills/go-enterprise-codegen.md +46 -0
  67. package/templates/pi/skills/java-spring-enterprise-codegen.md +50 -0
  68. package/templates/pi/skills/python-enterprise-codegen.md +46 -0
  69. package/templates/pi/skills/rust-enterprise-codegen.md +46 -0
  70. package/templates/pi/skills/tdd-practice.md +17 -9
  71. package/templates/pi/skills/typescript-enterprise-codegen.md +46 -0
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # validate-exploration-depth.sh — ADR-008 modeling-depth rules over a
4
+ # domain-exploration session's structured result (JSON).
5
+ #
6
+ # Deterministic, machine-parseable checks (no LLM):
7
+ # depth-subdomain every bounded context has subdomainType + rationale
8
+ # depth-invariants invariant entries carry rule + illegalStatePrevented
9
+ # event-triage every domain event passes >=1 triage question
10
+ # roots-justified aggregate-root entities claim identity (identityPersists)
11
+ # ul-cross-context no term reused across contexts without a context-map edge
12
+ #
13
+ # Usage:
14
+ # bash .pi/scripts/validate-exploration-depth.sh [<session-id>]
15
+ # without id: validates the most recent *.raw.json session
16
+ # exit 0 = all pass ; exit 1 = violations (one '[rule-id] error:' per line)
17
+ # ============================================================================
18
+ set -uo pipefail
19
+
20
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
21
+ EXPLORATION_DIR="${1:-}"
22
+ RAW=""
23
+
24
+ if [ -z "$EXPLORATION_DIR" ]; then
25
+ # No session id given: locate the most recent session raw JSON.
26
+ RAW="$(ls -1t "${SCRIPT_DIR}/../domain/exploration/"*.raw.json 2>/dev/null | head -1)"
27
+ elif [[ "$EXPLORATION_DIR" == */* || "$EXPLORATION_DIR" == *.json ]]; then
28
+ RAW="$EXPLORATION_DIR"
29
+ else
30
+ RAW="${SCRIPT_DIR}/../domain/exploration/${EXPLORATION_DIR}.raw.json"
31
+ fi
32
+
33
+ if [ -z "$RAW" ] || [ ! -f "$RAW" ]; then
34
+ echo "no exploration session raw.json found (expected at .pi/domain/exploration/<session>.raw.json)" >&2
35
+ exit 1
36
+ fi
37
+
38
+ python3 - "$RAW" <<'PY'
39
+ import json, sys
40
+
41
+ path = sys.argv[1]
42
+ try:
43
+ with open(path, encoding="utf-8") as fh:
44
+ data = json.load(fh)
45
+ except Exception as exc: # noqa: BLE001
46
+ print(f"[json-invalid] error: {path} is not valid JSON — {exc}")
47
+ sys.exit(1)
48
+
49
+ errors = []
50
+
51
+ def err(rule, msg):
52
+ errors.append(f"[{rule}] error: {msg}")
53
+
54
+ # Rule 1: subdomain classification present
55
+ for bc in data.get("boundedContexts", []):
56
+ name = bc.get("name", "?")
57
+ st = bc.get("subdomainType")
58
+ if st not in ("core", "supporting", "generic"):
59
+ err("depth-subdomain", f"bounded context '{name}' missing subdomainType (core|supporting|generic)")
60
+ if not bc.get("subdomainRationale"):
61
+ err("depth-subdomain", f"bounded context '{name}' missing subdomainRationale")
62
+
63
+ # Rule 2: invariants well-formed
64
+ for bc in data.get("boundedContexts", []):
65
+ name = bc.get("name", "?")
66
+ for inv in bc.get("invariants", []) or []:
67
+ if not inv.get("rule"):
68
+ err("depth-invariants", f"context '{name}' has an invariant with an empty rule")
69
+ if not inv.get("illegalStatePrevented"):
70
+ err("depth-invariants", f"context '{name}' invariant '{inv.get('rule','')}' missing illegalStatePrevented")
71
+
72
+ # Rule 3: event triage (>=1 question answered yes)
73
+ for ev in data.get("domainEvents", []):
74
+ triage = ev.get("triage") or {}
75
+ hits = sum(bool(triage.get(k)) for k in ("domainExpertCares", "crossesAggregateBoundary", "needsAudit"))
76
+ if hits == 0:
77
+ err("event-triage", f"event '{ev.get('name','?')}' fails all triage questions (implementation detail, not a domain event)")
78
+
79
+ # Rule 4: aggregate roots claim identity
80
+ for ent in data.get("entities", []):
81
+ if ent.get("type") == "aggregate-root":
82
+ just = ent.get("justification") or {}
83
+ if not just.get("identityPersists"):
84
+ err("roots-justified", f"aggregate-root '{ent.get('name','?')}' must justify identityPersists=true")
85
+
86
+ # Rule 5: cross-context term reuse needs a context-map edge
87
+ term_contexts = {}
88
+ for t in data.get("ubiquitousLanguage", []):
89
+ term = t.get("term", "")
90
+ ctx = t.get("boundedContext", "")
91
+ if not term or not ctx:
92
+ continue
93
+ term_contexts.setdefault(term, set()).add(ctx)
94
+ edges = {
95
+ (e.get("from"), e.get("to"))
96
+ for e in data.get("contextMap", [])
97
+ if e.get("from") and e.get("to")
98
+ }
99
+ for term, contexts in term_contexts.items():
100
+ ctxs = sorted(contexts)
101
+ if len(ctxs) > 1:
102
+ connected = any((ctxs[0], c) in edges or (c, ctxs[0]) in edges for c in ctxs[1:])
103
+ if not connected:
104
+ err("ul-cross-context", f"term '{term}' used in contexts {', '.join(ctxs)} without a context-map edge or UL conflicts entry")
105
+
106
+ if errors:
107
+ print("\n".join(errors))
108
+ sys.exit(1)
109
+ print("depth validation passed (subdomain classification, invariants, event triage, aggregate roots, UL contexts)")
110
+ sys.exit(0)
111
+ PY
112
+ exit $?
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # validate-runtime-wiring.sh — ADR-009 production-reachability dispatcher
4
+ #
5
+ # Deterministic check: every application-layer service must be referenced from
6
+ # production code OUTSIDE its own module and OUTSIDE tests. A service whose
7
+ # only callers are its own tests is implemented-but-unwired (dead code with a
8
+ # test suite) — the recurring rigorix pattern ADR-009 fixes.
9
+ #
10
+ # Delegates to languages/<lang>/check_runtime_reachability.sh.
11
+ # Usage: bash .pi/scripts/validate-runtime-wiring.sh [--root <dir>]
12
+ # Exit: 0 all services reachable (or language not yet covered)
13
+ # 1 unreachable service(s) — one 'file: [runtime-reachability] ...' line each
14
+ # ============================================================================
15
+ set -uo pipefail
16
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
17
+ ROOT="$(pwd)"
18
+ if [ "${1:-}" = "--root" ] && [ -n "${2:-}" ]; then ROOT="$2"; fi
19
+ cd "$ROOT" || exit 1
20
+
21
+ detect_language() {
22
+ if [ -f "Cargo.toml" ]; then echo "rust"
23
+ elif [ -f "package.json" ]; then echo "typescript"
24
+ elif [ -f "go.mod" ]; then echo "go"
25
+ elif [ -f "pyproject.toml" ] || [ -f "requirements.txt" ]; then echo "python"
26
+ elif [ -f "pom.xml" ] || [ -f "build.gradle" ]; then echo "java"
27
+ else echo "unknown"; fi
28
+ }
29
+ LANG="$(detect_language)"
30
+ SCRIPT="${SCRIPT_DIR}/languages/${LANG}/check_runtime_reachability.sh"
31
+ if [ -f "$SCRIPT" ]; then
32
+ exec bash "$SCRIPT" --root "$ROOT"
33
+ fi
34
+ echo "runtime-wiring: no reachability check for '${LANG}' yet (ADR-009 planned: go/python/java)"
35
+ exit 0
@@ -65,6 +65,34 @@ grep -r "registry.register" [src paths]
65
65
  grep -r "#\[from\]" [error file]
66
66
  ```
67
67
 
68
+
69
+ ## Model Judgment Review (ADR-008) — all languages
70
+
71
+ **MANDATORY** when the reviewed scope touches domain/application code. Record
72
+ each finding as WARN with `file:line: <finding>` — never a merge block on its
73
+ own; promotion to a gate requires a written decision after a trial. Evidence
74
+ commands are per-language; hard gates (deterministic) live in
75
+ `.pi/scripts/languages/<lang>/check_model_judgment.sh`.
76
+
77
+ - **R1 anemic-model** — app/application layer reads object state to decide
78
+ (`if (x.getStatus()…)`, `x.status =`, `x.State ==`) then mutates via
79
+ setters; business rules outside the domain object. Evidence per language:
80
+ Java/TS getter/setter chains; Go struct field pokes in services; Python
81
+ attribute mutation on domain objects; Rust `.field` writes / `set_*` from
82
+ services.
83
+ - **R2 LSP / fragile-base** — domain entities inherit from a non-abstract
84
+ base (Java/TS `extends`, Go embedding, Python subclassing, Rust — prefer
85
+ composition over inheritance, flag base-class aggregates).
86
+ - **R3 cohesion** — `Helper | Utils | Manager | Orchestrator`-style catch-all
87
+ classes/structs/types in the domain layer.
88
+ - **R4 primitive obsession** — >2 raw primitives on a domain service method
89
+ signature where a value object/struct/record should exist (IDs, money,
90
+ ranges, statuses).
91
+ - **R5 production reachability (ADR-009)** — an `implemented` application
92
+ service with no production caller outside its own module and outside tests
93
+ is dead code: report it and require the Runtime-Integration issue. Evidence:
94
+ `grep -rn "<ServiceName>" <src> | grep -v tests` per language.
95
+
68
96
  ## Output
69
97
  Use format from `.pi/context/output-formats.md` → "Validation Report"
70
98
 
@@ -17,7 +17,7 @@ You validate test coverage and quality.
17
17
  ## Checks
18
18
 
19
19
  1. **All tests pass** — Run full test suite
20
- 2. **Coverage ≥ 80%** — Run coverage tool
20
+ 2. **Coverage meets the project's real threshold** — Run the project's real coverage tool/gate (no per-module heuristic *_coverage.sh)
21
21
  3. **New code tested** — Every new function has tests
22
22
  4. **AAA pattern** — Arrange-Act-Assert structure
23
23
  5. **No flaky tests** — Deterministic, no timing dependencies
@@ -471,3 +471,49 @@ go.mod
471
471
  *Version: 1.0.0*
472
472
  *Last updated: 2026-07-03*
473
473
  *Source: Guardian DDD patterns + Clean Architecture + context7 (/wesionaryteam/go_clean_architecture, /golang/go)*
474
+
475
+ ## Judgment Tables (ADR-008)
476
+
477
+ Compact decision tables distilled from professional OOP/DDD practice (token-lean;
478
+ the full rationale lives in the curriculum, not here).
479
+
480
+ ### Entity vs Value Object
481
+ | Question | Entity | Value Object |
482
+ |----------|--------|--------------|
483
+ | Identity persists across state changes? | yes | no |
484
+ | Change = mutate in place? | yes | replace entirely |
485
+ | Equality by identity or by attributes? | identity | all attributes |
486
+ | Immutable by nature? | no | yes |
487
+ | Track change history? | yes | no |
488
+
489
+ ### Aggregate Boundaries
490
+ - An aggregate is a **consistency boundary**, not a data grouping.
491
+ - One aggregate modified per transaction; everything else via events.
492
+ - Reference other aggregates **by identity**, never by object graph.
493
+ - Design **small** aggregates; the root is the only entry point — behavior methods, no setters.
494
+
495
+ ### Service Classifier
496
+ | Service | Layer | Contains | Named by |
497
+ |---------|-------|----------|----------|
498
+ | Domain service | domain/ | business process spanning entities | ubiquitous language |
499
+ | Application service | application/ | use-case orchestration (thin) | technical intent |
500
+ | Infrastructure service | infrastructure/ | technical impl (persistence, http, events) | implementation |
501
+ | Persistence port (interface) | domain/ | aggregate persistence contract | aggregate language |
502
+ | Persistence adapter | infrastructure/ | framework impl (ORM/DB) | — |
503
+
504
+ ### Event Triage (3 questions)
505
+ Keep a candidate event only if ≥1 is true: (1) would a domain expert care?
506
+ (2) does it cross an aggregate boundary? (3) do we need an audit trail?
507
+ Otherwise it is an implementation detail. Publish from the aggregate;
508
+ dispatch **after** save from the application service.
509
+
510
+ ### Transaction & Persistence Placement
511
+ | Concern | Location | Why |
512
+ |---------|----------|-----|
513
+ | `sql.Tx` / transaction scope | application service only | one aggregate per transaction |
514
+ | Repository interface | domain/ package | dependency inversion |
515
+ | Repository impl (sqlx/gorm) | infrastructure/ | framework out of domain |
516
+ | Value object | domain/ (struct, immutable by convention) | replace, don't mutate |
517
+ | Wiring | explicit constructors in `cmd/` | no framework magic |
518
+
519
+ *Distilled 2026-09-03 from professional OOP/DDD curriculum (Modules 0–6) — ADR-008/009.*
@@ -540,3 +540,53 @@ pom.xml (or build.gradle)
540
540
  *Version: 1.0.0*
541
541
  *Last updated: 2026-07-03*
542
542
  *Source: Guardian DDD patterns + Spring Boot best practices + context7 (/jkazama/ddd-java, /websites/spring_io_spring-boot)*
543
+
544
+ ## 10. Judgment Tables (ADR-008)
545
+
546
+ Compact decision tables distilled from professional OOP/DDD/Spring practice
547
+ (keep token-lean — the full rationale lives in the curriculum, not here).
548
+
549
+ ### Entity vs Value Object
550
+ | Question | Entity | Value Object |
551
+ |----------|--------|--------------|
552
+ | Identity persists across state changes? | yes | no |
553
+ | Change = mutate in place? | yes | replace entirely |
554
+ | Equality by identity or by attributes? | identity | all attributes |
555
+ | Immutable by nature? | no | yes |
556
+ | Track change history? | yes | no |
557
+
558
+ ### Aggregate Boundaries
559
+ - An aggregate is a **consistency boundary**, not a data grouping.
560
+ - One aggregate modified per transaction; everything else via events.
561
+ - Reference other aggregates **by identity**, never by object graph.
562
+ - Design **small** aggregates (size by consistency, not relationships).
563
+ - Root is the only entry point — state changes via behavior methods (no setters).
564
+
565
+ ### Service Classifier
566
+ | Service | Layer | Contains | Named by |
567
+ |---------|-------|----------|----------|
568
+ | Domain service | domain/ | business process spanning entities | ubiquitous language |
569
+ | Application service | application/ | use-case orchestration (thin) | technical intent |
570
+ | Infrastructure service | infrastructure/ | technical impl (email, storage, publishing) | implementation |
571
+ | Repository (interface) | domain/ | aggregate persistence contract | aggregate language |
572
+ | Repository (impl) | infrastructure/ | JPA/ORM adapter | — |
573
+
574
+ ### Event Triage (3 questions)
575
+ Ask of every candidate event; keep it only if ≥1 is true:
576
+ 1. Would a domain expert care?
577
+ 2. Does it cross an aggregate boundary?
578
+ 3. Do we need an audit trail?
579
+ Otherwise it is an implementation detail, not a domain event.
580
+ Publish events from the aggregate (`releaseEvents()`), dispatch **after** save
581
+ from the application service.
582
+
583
+ ### Transaction & Persistence Placement
584
+ | Concern | Location | Why |
585
+ |---------|----------|-----|
586
+ | `@Transactional` | application service methods only | one aggregate per transaction |
587
+ | repository `save`/`find` | no `@Transactional` | don't open transactions per call |
588
+ | VO → `@Embeddable` | domain value objects | immutable + replaceable |
589
+ | Aggregate id | VO (`@EmbeddedId`) | type-safe identity |
590
+ | Optimistic lock | version field on aggregate | concurrency safety |
591
+
592
+ *Distilled 2026-09-03 from professional OOP/DDD/Spring curriculum (Modules 0–6) — ADR-008.*
@@ -401,3 +401,49 @@ pyproject.toml
401
401
  *Version: 1.0.0*
402
402
  *Last updated: 2026-07-03*
403
403
  *Source: Guardian DDD patterns + Python best practices + context7 (/fastapi/fastapi)*
404
+
405
+ ## Judgment Tables (ADR-008)
406
+
407
+ Compact decision tables distilled from professional OOP/DDD practice (token-lean;
408
+ the full rationale lives in the curriculum, not here).
409
+
410
+ ### Entity vs Value Object
411
+ | Question | Entity | Value Object |
412
+ |----------|--------|--------------|
413
+ | Identity persists across state changes? | yes | no |
414
+ | Change = mutate in place? | yes | replace entirely |
415
+ | Equality by identity or by attributes? | identity | all attributes |
416
+ | Immutable by nature? | no | yes |
417
+ | Track change history? | yes | no |
418
+
419
+ ### Aggregate Boundaries
420
+ - An aggregate is a **consistency boundary**, not a data grouping.
421
+ - One aggregate modified per transaction; everything else via events.
422
+ - Reference other aggregates **by identity**, never by object graph.
423
+ - Design **small** aggregates; the root is the only entry point — behavior methods, no setters.
424
+
425
+ ### Service Classifier
426
+ | Service | Layer | Contains | Named by |
427
+ |---------|-------|----------|----------|
428
+ | Domain service | domain/ | business process spanning entities | ubiquitous language |
429
+ | Application service | application/ | use-case orchestration (thin) | technical intent |
430
+ | Infrastructure service | infrastructure/ | technical impl (persistence, http, events) | implementation |
431
+ | Persistence port (interface) | domain/ | aggregate persistence contract | aggregate language |
432
+ | Persistence adapter | infrastructure/ | framework impl (ORM/DB) | — |
433
+
434
+ ### Event Triage (3 questions)
435
+ Keep a candidate event only if ≥1 is true: (1) would a domain expert care?
436
+ (2) does it cross an aggregate boundary? (3) do we need an audit trail?
437
+ Otherwise it is an implementation detail. Publish from the aggregate;
438
+ dispatch **after** save from the application service.
439
+
440
+ ### Transaction & Persistence Placement
441
+ | Concern | Location | Why |
442
+ |---------|----------|-----|
443
+ | Session / commit scope | application service only | one aggregate per transaction |
444
+ | Repository Protocol/ABC | domain/ | dependency inversion |
445
+ | Repository impl (SQLAlchemy/Django) | infrastructure/ | ORM out of domain |
446
+ | Value object | domain/ (dataclass frozen / attrs) | replace, don't mutate |
447
+ | Framework (FastAPI/Django) | interfaces/ | web out of domain |
448
+
449
+ *Distilled 2026-09-03 from professional OOP/DDD curriculum (Modules 0–6) — ADR-008/009.*
@@ -1021,3 +1021,49 @@ live-tests = [] # Flag for tests that hit real APIs
1021
1021
  *Version: 1.0.0*
1022
1022
  *Last updated: 2026-07-03*
1023
1023
  *Source: Guardian DDD patterns + context7 DDD reference (/jkazama/ddd-java, /ardalis/cleanarchitecture)*
1024
+
1025
+ ## Judgment Tables (ADR-008)
1026
+
1027
+ Compact decision tables distilled from professional OOP/DDD practice (token-lean;
1028
+ the full rationale lives in the curriculum, not here).
1029
+
1030
+ ### Entity vs Value Object
1031
+ | Question | Entity | Value Object |
1032
+ |----------|--------|--------------|
1033
+ | Identity persists across state changes? | yes | no |
1034
+ | Change = mutate in place? | yes | replace entirely |
1035
+ | Equality by identity or by attributes? | identity | all attributes |
1036
+ | Immutable by nature? | no | yes |
1037
+ | Track change history? | yes | no |
1038
+
1039
+ ### Aggregate Boundaries
1040
+ - An aggregate is a **consistency boundary**, not a data grouping.
1041
+ - One aggregate modified per transaction; everything else via events.
1042
+ - Reference other aggregates **by identity**, never by object graph.
1043
+ - Design **small** aggregates; the root is the only entry point — behavior methods, no setters.
1044
+
1045
+ ### Service Classifier
1046
+ | Service | Layer | Contains | Named by |
1047
+ |---------|-------|----------|----------|
1048
+ | Domain service | domain/ | business process spanning entities | ubiquitous language |
1049
+ | Application service | application/ | use-case orchestration (thin) | technical intent |
1050
+ | Infrastructure service | infrastructure/ | technical impl (persistence, http, events) | implementation |
1051
+ | Persistence port (interface) | domain/ | aggregate persistence contract | aggregate language |
1052
+ | Persistence adapter | infrastructure/ | framework impl (ORM/DB) | — |
1053
+
1054
+ ### Event Triage (3 questions)
1055
+ Keep a candidate event only if ≥1 is true: (1) would a domain expert care?
1056
+ (2) does it cross an aggregate boundary? (3) do we need an audit trail?
1057
+ Otherwise it is an implementation detail. Publish from the aggregate;
1058
+ dispatch **after** save from the application service.
1059
+
1060
+ ### Transaction & Persistence Placement
1061
+ | Concern | Location | Why |
1062
+ |---------|----------|-----|
1063
+ | Transaction handle scope | application service only | one aggregate per transaction |
1064
+ | Repository trait | domain/ (crate) | dependency inversion |
1065
+ | Repository impl (sqlx/diesel) | infrastructure/ | framework out of domain |
1066
+ | Value object | domain/ (struct/type, immutable) | replace, don't mutate |
1067
+ | HTTP (axum/actix) | interfaces/ | web out of domain |
1068
+
1069
+ *Distilled 2026-09-03 from professional OOP/DDD curriculum (Modules 0–6) — ADR-008/009.*
@@ -6,7 +6,9 @@ description: TDD practice guide for Guardian implementation agents. Use when TDD
6
6
  # TDD Practice — Red → Green → Refactor
7
7
 
8
8
  > This skill applies when TDD mode is enabled on the current epic (`--tdd`).
9
- > Pre-generated failing test files exist in `tests/unit/<module>/<component>/`.
9
+ > Pre-generated failing test files exist in the project's test directory
10
+ > (language-specific: `tests/unit/` for TypeScript, `src/test/java/` for Java,
11
+ > `tests/` for Python/Go/Rust).
10
12
  > These tests were generated deterministically from architecture contracts — no LLM involvement.
11
13
 
12
14
  ## Core Principle
@@ -24,8 +26,14 @@ Do NOT treat the generated tests as rigid specs you must match exactly. Treat th
24
26
 
25
27
  ### 1. Red — See the test fail
26
28
 
27
- ```
28
- bun test tests/unit/<module>/<component>/<component>.test.ts
29
+ Run the project's test runner on the generated test file:
30
+
31
+ ```bash
32
+ # TypeScript: bun test tests/unit/<module>/<component>/<component>.test.ts
33
+ # Java: mvn test -Dtest=<Component>Test
34
+ # Python: pytest tests/<module>/<component>/test_<component>.py
35
+ # Go: go test ./tests/<module>/<component>/
36
+ # Rust: cargo test --test <component>
29
37
  ```
30
38
 
31
39
  Confirm the test fails before writing any implementation code. This validates that:
@@ -41,7 +49,7 @@ Write the minimal implementation to make the failing test pass.
41
49
  - Write only enough code to pass the test
42
50
  - Do not over-engineer — YAGNI
43
51
  - Use the test's TODO comments as a guide for what the API should look like
44
- - The test file is in `tests/unit/`, implementation files go in `src/`
52
+ - The test file is in the project's test directory (`tests/unit/`, `src/test/java/`, or `tests/`), implementation files go in `src/`
45
53
  - Import the implementation module into the test file
46
54
 
47
55
  If the generated test assumes an API shape that doesn't fit, **update the test first**, then implement.
@@ -107,11 +115,11 @@ The generated tests are **suggestions, not constraints**. You should modify them
107
115
 
108
116
  ## File Locations
109
117
 
110
- | Artifact | Path |
111
- |----------|------|
112
- | Tests | `tests/unit/<module>/<component>/<component>.test.ts` |
113
- | Implementation | `src/<module>/<component>.ts` (or appropriate layer) |
114
- | TDD guide | `.pi/skills/tdd-practice.md` |
118
+ | Artifact | TypeScript | Java | Python | Go | Rust |
119
+ |----------|------------|------|--------|-----|------|
120
+ | Tests | `tests/unit/.../component.test.ts` | `src/test/java/.../ComponentTest.java` | `tests/.../test_component.py` | `tests/.../component_test.go` | `tests/.../mod.rs` |
121
+ | Implementation | `src/<module>/` | `src/main/java/.../` | `src/<module>/` | `src/<module>/` | `src/<module>/` |
122
+ | TDD guide | `.pi/skills/tdd-practice.md` | same | same | same | same |
115
123
 
116
124
  ## Anti-Patterns
117
125
 
@@ -431,3 +431,49 @@ tsconfig.json
431
431
  *Version: 1.0.0*
432
432
  *Last updated: 2026-07-03*
433
433
  *Source: Guardian DDD patterns + TypeScript best practices + context7 (/microsoft/typescript)*
434
+
435
+ ## Judgment Tables (ADR-008)
436
+
437
+ Compact decision tables distilled from professional OOP/DDD practice (token-lean;
438
+ the full rationale lives in the curriculum, not here).
439
+
440
+ ### Entity vs Value Object
441
+ | Question | Entity | Value Object |
442
+ |----------|--------|--------------|
443
+ | Identity persists across state changes? | yes | no |
444
+ | Change = mutate in place? | yes | replace entirely |
445
+ | Equality by identity or by attributes? | identity | all attributes |
446
+ | Immutable by nature? | no | yes |
447
+ | Track change history? | yes | no |
448
+
449
+ ### Aggregate Boundaries
450
+ - An aggregate is a **consistency boundary**, not a data grouping.
451
+ - One aggregate modified per transaction; everything else via events.
452
+ - Reference other aggregates **by identity**, never by object graph.
453
+ - Design **small** aggregates; the root is the only entry point — behavior methods, no setters.
454
+
455
+ ### Service Classifier
456
+ | Service | Layer | Contains | Named by |
457
+ |---------|-------|----------|----------|
458
+ | Domain service | domain/ | business process spanning entities | ubiquitous language |
459
+ | Application service | application/ | use-case orchestration (thin) | technical intent |
460
+ | Infrastructure service | infrastructure/ | technical impl (persistence, http, events) | implementation |
461
+ | Persistence port (interface) | domain/ | aggregate persistence contract | aggregate language |
462
+ | Persistence adapter | infrastructure/ | framework impl (ORM/DB) | — |
463
+
464
+ ### Event Triage (3 questions)
465
+ Keep a candidate event only if ≥1 is true: (1) would a domain expert care?
466
+ (2) does it cross an aggregate boundary? (3) do we need an audit trail?
467
+ Otherwise it is an implementation detail. Publish from the aggregate;
468
+ dispatch **after** save from the application service.
469
+
470
+ ### Transaction & Persistence Placement
471
+ | Concern | Location | Why |
472
+ |---------|----------|-----|
473
+ | Unit of Work / transaction scope | application service only | one aggregate per transaction |
474
+ | Repository interface | domain/ (interfaces/port) | dependency inversion |
475
+ | Repository impl (TypeORM/Prisma) | infrastructure/ | framework out of domain |
476
+ | Value object | domain/ (class/record, immutable) | replace, don't mutate |
477
+ | DI wiring | composition root (infrastructure) | explicit assembly |
478
+
479
+ *Distilled 2026-09-03 from professional OOP/DDD curriculum (Modules 0–6) — ADR-008/009.*