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
package/dist/exports.js CHANGED
@@ -1894,7 +1894,12 @@ function getStoredHash(record) {
1894
1894
  return hash ? hash.replace("sha256:", "") : null;
1895
1895
  }
1896
1896
  function categorizeFile(relativePath) {
1897
- const userEditable = ["agent/AGENTS.md", "domain/", "architecture/CHANGELOG.md", "architecture/implementation-roadmap.md"];
1897
+ const userEditable = [
1898
+ "agent/AGENTS.md",
1899
+ "domain/",
1900
+ "architecture/CHANGELOG.md",
1901
+ "architecture/implementation-roadmap.md"
1902
+ ];
1898
1903
  if (userEditable.some((p2) => relativePath.includes(p2))) {
1899
1904
  return "user";
1900
1905
  }
@@ -1982,7 +1987,7 @@ __export(exports_package, {
1982
1987
  bin: () => bin,
1983
1988
  author: () => author
1984
1989
  });
1985
- var name = "guardian-framework", version = "0.1.104", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
1990
+ var name = "guardian-framework", version = "0.1.109", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
1986
1991
  var init_package = __esm(() => {
1987
1992
  exports = {
1988
1993
  ".": {
@@ -11949,6 +11954,19 @@ function buildExplorationPrompt(context) {
11949
11954
  const sanitized = sanitizeBusinessContext(context);
11950
11955
  return `You are a Domain-Driven Design expert. Analyze the following business description and extract a structured domain model.
11951
11956
 
11957
+ Modeling judgment required (professional DDD practice):
11958
+ - Classify EVERY bounded context as a subdomain type: "core" (competitive advantage — full tactical modeling),
11959
+ "supporting" (needed but not differentiating — simple services), or "generic" (off-the-shelf problem — buy/OSS,
11960
+ never model). Add a one-sentence subdomainRationale for each.
11961
+ - For every entity/model entry decide entity vs value object vs aggregate root using the identity test:
11962
+ does it have identity that persists across state changes? Is it immutable + equal by attributes? Record the
11963
+ justification booleans, not just a label.
11964
+ - Capture per-context invariants: business rules that must always hold and the illegal state each prevents.
11965
+ - Triage EVERY event candidate with the 3-question test (domain expert cares? crosses an aggregate boundary?
11966
+ needs audit trail?). Drop candidates failing all three — they are implementation details, not domain events.
11967
+ - Draw the context map between bounded contexts (partnership / shared-kernel / conformist / anticorruption-layer / open-host).
11968
+ - Record ubiquitous-language conflicts: terms used differently by different parts of the business.
11969
+
11952
11970
  Business Context:
11953
11971
  ${sanitized}
11954
11972
 
@@ -11992,7 +12010,20 @@ Respond with ONLY valid JSON matching this exact structure. Do not include markd
11992
12010
  {
11993
12011
  "name": "ContextName",
11994
12012
  "description": "Brief description of this bounded context",
11995
- "entities": ["EntityName1", "EntityName2"]
12013
+ "entities": ["EntityName1", "EntityName2"],
12014
+ "subdomainType": "core|supporting|generic",
12015
+ "subdomainRationale": "why this subdomain classification",
12016
+ "invariants": [
12017
+ { "rule": "business rule that must always hold", "illegalStatePrevented": "the illegal state this prevents" }
12018
+ ]
12019
+ }
12020
+ ],
12021
+ "contextMap": [
12022
+ {
12023
+ "from": "ContextName",
12024
+ "to": "OtherContextName",
12025
+ "relationship": "partnership|shared-kernel|conformist|anticorruption-layer|open-host",
12026
+ "note": "how the two contexts relate"
11996
12027
  }
11997
12028
  ],
11998
12029
  "entities": [
@@ -12000,7 +12031,12 @@ Respond with ONLY valid JSON matching this exact structure. Do not include markd
12000
12031
  "name": "EntityName",
12001
12032
  "context": "ContextName",
12002
12033
  "type": "entity|value-object|aggregate-root",
12003
- "description": "What this entity represents"
12034
+ "description": "What this entity represents",
12035
+ "justification": {
12036
+ "identityPersists": true,
12037
+ "immutable": false,
12038
+ "equalByState": false
12039
+ }
12004
12040
  }
12005
12041
  ],
12006
12042
  "domainEvents": [
@@ -12008,7 +12044,12 @@ Respond with ONLY valid JSON matching this exact structure. Do not include markd
12008
12044
  "name": "EventName",
12009
12045
  "context": "ContextName",
12010
12046
  "description": "What happened",
12011
- "triggeredBy": "What caused this event"
12047
+ "triggeredBy": "What caused this event",
12048
+ "triage": {
12049
+ "domainExpertCares": true,
12050
+ "crossesAggregateBoundary": false,
12051
+ "needsAudit": false
12052
+ }
12012
12053
  }
12013
12054
  ],
12014
12055
  "ubiquitousLanguage": [
@@ -12017,7 +12058,10 @@ Respond with ONLY valid JSON matching this exact structure. Do not include markd
12017
12058
  "definition": "Clear definition",
12018
12059
  "boundedContext": "ContextName",
12019
12060
  "aliases": ["AlternativeName1"],
12020
- "examples": "Usage example"
12061
+ "examples": "Usage example",
12062
+ "conflicts": [
12063
+ { "termA": "Term used one way", "termB": "Same term used differently", "whyConflicting": "why the meanings diverge" }
12064
+ ]
12021
12065
  }
12022
12066
  ],
12023
12067
  "openQuestions": "Any open questions about the domain",
@@ -12153,19 +12197,24 @@ function parseExplorationResponse(rawResponse, sessionId) {
12153
12197
  boundedContexts: Array.isArray(parsed.boundedContexts) ? parsed.boundedContexts.map((bc) => ({
12154
12198
  name: coerceString(bc.name, "Unknown"),
12155
12199
  description: coerceString(bc.description, ""),
12156
- entities: Array.isArray(bc.entities) ? bc.entities.map(String) : []
12200
+ entities: Array.isArray(bc.entities) ? bc.entities.map(String) : [],
12201
+ subdomainType: coerceSubdomainType(bc.subdomainType),
12202
+ subdomainRationale: coerceString(bc.subdomainRationale, ""),
12203
+ invariants: coerceInvariants(bc.invariants)
12157
12204
  })) : [],
12158
12205
  entities: Array.isArray(parsed.entities) ? parsed.entities.map((e) => ({
12159
12206
  name: coerceString(e.name, "Unknown"),
12160
12207
  context: coerceString(e.context, ""),
12161
12208
  type: coerceEntityType(e.type),
12162
- description: coerceString(e.description, "")
12209
+ description: coerceString(e.description, ""),
12210
+ justification: coerceJustification(e.justification)
12163
12211
  })) : [],
12164
12212
  domainEvents: Array.isArray(parsed.domainEvents) ? parsed.domainEvents.map((ev) => ({
12165
12213
  name: coerceString(ev.name, "Unknown"),
12166
12214
  context: coerceString(ev.context, ""),
12167
12215
  description: coerceString(ev.description, ""),
12168
- triggeredBy: coerceString(ev.triggeredBy, "")
12216
+ triggeredBy: coerceString(ev.triggeredBy, ""),
12217
+ triage: coerceTriage(ev.triage)
12169
12218
  })) : [],
12170
12219
  functionalRequirements: Array.isArray(parsed.functionalRequirements) ? parsed.functionalRequirements.map((fr) => ({
12171
12220
  id: coerceString(fr.id, "FR-???"),
@@ -12189,10 +12238,21 @@ function parseExplorationResponse(rawResponse, sessionId) {
12189
12238
  definition: coerceString(t2.definition, ""),
12190
12239
  boundedContext: coerceString(t2.boundedContext, ""),
12191
12240
  aliases: Array.isArray(t2.aliases) ? t2.aliases.map(String) : [],
12192
- examples: coerceString(t2.examples, "")
12241
+ examples: coerceString(t2.examples, ""),
12242
+ conflicts: Array.isArray(t2.conflicts) ? t2.conflicts.map((c3) => ({
12243
+ termA: coerceString(c3.termA, ""),
12244
+ termB: coerceString(c3.termB, ""),
12245
+ whyConflicting: coerceString(c3.whyConflicting, "")
12246
+ })) : undefined
12193
12247
  })) : [],
12194
12248
  openQuestions: coerceString(parsed.openQuestions, ""),
12195
- aggregateRoots: coerceString(parsed.aggregateRoots, "")
12249
+ aggregateRoots: coerceString(parsed.aggregateRoots, ""),
12250
+ contextMap: Array.isArray(parsed.contextMap) ? parsed.contextMap.map((e) => ({
12251
+ from: coerceString(e.from, ""),
12252
+ to: coerceString(e.to, ""),
12253
+ relationship: coerceContextRelationship(e.relationship),
12254
+ note: coerceString(e.note, "")
12255
+ })) : undefined
12196
12256
  };
12197
12257
  return { ok: true, value: result };
12198
12258
  } catch (err) {
@@ -12228,6 +12288,49 @@ function coerceEntityType(val) {
12228
12288
  return "app-shell";
12229
12289
  return "entity";
12230
12290
  }
12291
+ function coerceSubdomainType(val) {
12292
+ if (val === "core")
12293
+ return "core";
12294
+ if (val === "supporting")
12295
+ return "supporting";
12296
+ if (val === "generic")
12297
+ return "generic";
12298
+ return;
12299
+ }
12300
+ function coerceInvariants(val) {
12301
+ if (!Array.isArray(val))
12302
+ return;
12303
+ return val.map((i2) => ({
12304
+ rule: coerceString(i2.rule, ""),
12305
+ illegalStatePrevented: coerceString(i2.illegalStatePrevented, "")
12306
+ }));
12307
+ }
12308
+ function coerceJustification(val) {
12309
+ if (typeof val !== "object" || val === null)
12310
+ return;
12311
+ const j = val;
12312
+ return {
12313
+ identityPersists: Boolean(j.identityPersists),
12314
+ immutable: Boolean(j.immutable),
12315
+ equalByState: Boolean(j.equalByState)
12316
+ };
12317
+ }
12318
+ function coerceTriage(val) {
12319
+ if (typeof val !== "object" || val === null)
12320
+ return;
12321
+ const t2 = val;
12322
+ return {
12323
+ domainExpertCares: Boolean(t2.domainExpertCares),
12324
+ crossesAggregateBoundary: Boolean(t2.crossesAggregateBoundary),
12325
+ needsAudit: Boolean(t2.needsAudit)
12326
+ };
12327
+ }
12328
+ function coerceContextRelationship(val) {
12329
+ if (val === "partnership" || val === "shared-kernel" || val === "conformist" || val === "anticorruption-layer" || val === "open-host") {
12330
+ return val;
12331
+ }
12332
+ return "anticorruption-layer";
12333
+ }
12231
12334
  function coerceFrPriority(val) {
12232
12335
  if (val === "critical")
12233
12336
  return "critical";
@@ -12370,6 +12473,14 @@ function answerExploration(sessionId, responseJson, options) {
12370
12473
  function readExplorationSession(sessionId, projectDir) {
12371
12474
  const root = projectDir ?? process.cwd();
12372
12475
  const filePath = path11.join(root, ".pi", "domain", "exploration", `${sessionId}.md`);
12476
+ const rawPath = filePath.replace(/\.md$/, ".raw.json");
12477
+ if (fs12.existsSync(rawPath)) {
12478
+ try {
12479
+ const parsed = JSON.parse(fs12.readFileSync(rawPath, "utf-8"));
12480
+ if (parsed?.sessionId)
12481
+ return parsed;
12482
+ } catch {}
12483
+ }
12373
12484
  if (!fs12.existsSync(filePath)) {
12374
12485
  return null;
12375
12486
  }
@@ -12553,6 +12664,10 @@ function scaffoldFromExploration(sessionId, options) {
12553
12664
  const contextEntities = entitiesByContext[bc.name] || [];
12554
12665
  const contextEvents = result.domainEvents.filter((ev) => ev.context === bc.name);
12555
12666
  const contextTerms = result.ubiquitousLanguage.filter((t2) => t2.boundedContext === bc.name);
12667
+ const depth = bc.subdomainType === "core" ? "tactical" : bc.subdomainType === "generic" ? "adapter" : bc.subdomainType === "supporting" ? "service" : "unclassified";
12668
+ const depthHeaderNote = bc.subdomainType === undefined || bc.subdomainType === null ? " (unclassified — defaulted to tactical; re-run exploration with ADR-008 depth)" : "";
12669
+ const contextEdges = (result.contextMap || []).filter((e) => e.from === bc.name || e.to === bc.name);
12670
+ const invariants = bc.invariants || [];
12556
12671
  if (!dryRun && fs12.existsSync(filePath)) {
12557
12672
  warnings.push(`Module already exists, will be overwritten: ${fileName}`);
12558
12673
  }
@@ -12809,6 +12924,17 @@ None yet
12809
12924
 
12810
12925
  ${bc.description}
12811
12926
 
12927
+ `;
12928
+ content += `
12929
+ `;
12930
+ content += "**Modeling Depth:** ";
12931
+ content += depth === "tactical" ? "tactical (core domain)" : depth === "service" ? "service (supporting subdomain)" : depth === "adapter" ? "adapter (generic subdomain)" : "unclassified";
12932
+ content += `${depthHeaderNote}
12933
+ `;
12934
+ content += `**Subdomain Rationale:** ${bc.subdomainRationale || "—"}
12935
+ `;
12936
+ const edgeSummary = contextEdges.map((e) => (e.from === bc.name ? `${e.relationship} → ${e.to}` : `${e.from} → ${e.relationship}`) + (e.note ? ` (${e.note})` : "")).join("; ");
12937
+ content += `**Context Map:** ${edgeSummary || "—"}
12812
12938
  `;
12813
12939
  content += `
12814
12940
  `;
@@ -12889,15 +13015,22 @@ ${bc.description}
12889
13015
  content += "```";
12890
13016
  content += `
12891
13017
  `;
12892
- if (ownedEntities.length > 0 || ownedVOs.length > 0) {
13018
+ if (ownedEntities.length > 0 || ownedVOs.length > 0 || invariants.length > 0) {
12893
13019
  content += `**Aggregate Invariants:**
12894
13020
  `;
12895
- content += `- All operations go through ${ar.name} aggregate root
13021
+ if (invariants.length > 0) {
13022
+ for (const inv of invariants) {
13023
+ content += `- ${inv.rule} (prevents: ${inv.illegalStatePrevented})
13024
+ `;
13025
+ }
13026
+ } else {
13027
+ content += `- All operations go through ${ar.name} aggregate root
12896
13028
  `;
12897
- content += `- References to child entities resolve through ${ar.name}
13029
+ content += `- References to child entities resolve through ${ar.name}
12898
13030
  `;
12899
- content += `- ${ar.name}Id is the sole external identifier
13031
+ content += `- ${ar.name}Id is the sole external identifier
12900
13032
  `;
13033
+ }
12901
13034
  content += `
12902
13035
  `;
12903
13036
  }
@@ -12983,6 +13116,38 @@ ${bc.description}
12983
13116
  `;
12984
13117
  content += `
12985
13118
  `;
13119
+ if (depth === "service" || depth === "adapter") {
13120
+ content += `## Modeling Depth Note (ADR-008)
13121
+ `;
13122
+ content += `
13123
+ `;
13124
+ if (depth === "service") {
13125
+ content += `This bounded context is a **supporting subdomain** — model at service/CRUD depth.
13126
+ `;
13127
+ content += `Keep implementation to application services + persistence. Do NOT introduce new
13128
+ `;
13129
+ content += `aggregates, value objects, or domain events beyond what this exploration captured.
13130
+ `;
13131
+ } else {
13132
+ content += `This bounded context is a **generic subdomain** — model at adapter depth.
13133
+ `;
13134
+ content += `Integrate an off-the-shelf / vendor / OSS solution behind an anti-corruption layer.
13135
+ `;
13136
+ content += `Do NOT write domain entities or aggregates here.
13137
+ `;
13138
+ }
13139
+ if (contextEdges.length > 0) {
13140
+ content += `
13141
+ **Anti-corruption edges:**
13142
+ `;
13143
+ for (const e of contextEdges) {
13144
+ content += `- ${e.from} ${e.relationship} ${e.to} — ${e.note || "—"}
13145
+ `;
13146
+ }
13147
+ }
13148
+ content += `
13149
+ `;
13150
+ }
12986
13151
  content += `## Acceptance Criteria
12987
13152
  `;
12988
13153
  content += `
@@ -13040,6 +13205,11 @@ function loadExplorationTemplate() {
13040
13205
  }
13041
13206
  throw new Error("Exploration template not found in templates/pi/domain/exploration.md");
13042
13207
  }
13208
+ function indentBlock(text2, indent = " ") {
13209
+ return text2.split(`
13210
+ `).map((ln) => ln.length > 0 ? indent + ln : ln).join(`
13211
+ `);
13212
+ }
13043
13213
  function renderExplorationTemplate(result) {
13044
13214
  const template = loadExplorationTemplate();
13045
13215
  const actorsTable = result.actors.map((a2) => "| ${a.name} | ${a.description} | ${a.interactions} |").join(`
@@ -13058,9 +13228,51 @@ function renderExplorationTemplate(result) {
13058
13228
  `);
13059
13229
  const ubiquitousLanguageTable = result.ubiquitousLanguage.map((t2) => `| ${t2.term} | ${t2.definition} | ${t2.boundedContext} | ${t2.aliases.join(", ")} |`).join(`
13060
13230
  `);
13231
+ const subdomainRows = result.boundedContexts.filter((bc) => bc.subdomainType || bc.invariants?.length).map((bc) => {
13232
+ const inv = (bc.invariants || []).map((i2) => `- ${i2.rule} (prevents: ${i2.illegalStatePrevented})`).join(`
13233
+ `) || "—";
13234
+ return `### ${bc.name}
13235
+ - **Subdomain:** ${bc.subdomainType ?? "unclassified"} — ${bc.subdomainRationale ?? ""}
13236
+ - **Invariants:**
13237
+ ${indentBlock(inv)}`;
13238
+ }).join(`
13239
+
13240
+ `);
13241
+ const contextMapRows = (result.contextMap || []).map((e) => `| ${e.from} | ${e.to} | ${e.relationship} | ${e.note} |`).join(`
13242
+ `);
13243
+ const eventTriageRows = result.domainEvents.filter((ev) => ev.triage).map((ev) => `| ${ev.name} | ${ev.triage.domainExpertCares ? "yes" : "no"} | ${ev.triage.crossesAggregateBoundary ? "yes" : "no"} | ${ev.triage.needsAudit ? "yes" : "no"} |`).join(`
13244
+ `);
13245
+ const ulConflictRows = result.ubiquitousLanguage.flatMap((t2) => (t2.conflicts || []).map((c3) => `| ${c3.termA} | ${c3.termB} | ${c3.whyConflicting} |`)).join(`
13246
+ `);
13247
+ const modelingParts = [];
13248
+ if (subdomainRows) {
13249
+ modelingParts.push(`### Subdomain Classification & Invariants
13250
+ ${subdomainRows}`);
13251
+ }
13252
+ if (contextMapRows) {
13253
+ modelingParts.push(`### Context Map
13254
+ | From | To | Relationship | Note |
13255
+ |-----|----|--------------|------|
13256
+ ${contextMapRows}`);
13257
+ }
13258
+ if (eventTriageRows) {
13259
+ modelingParts.push(`### Domain Event Triage
13260
+ | Event | Domain expert cares | Crosses aggregate boundary | Needs audit trail |
13261
+ |-------|---------------------|---------------------------|-------------------|
13262
+ ${eventTriageRows}`);
13263
+ }
13264
+ if (ulConflictRows) {
13265
+ modelingParts.push(`### Ubiquitous Language Conflicts
13266
+ | Term A | Term B | Why conflicting |
13267
+ |--------|--------|-----------------|
13268
+ ${ulConflictRows}`);
13269
+ }
13270
+ const modelingJudgmentSection = modelingParts.length > 0 ? modelingParts.join(`
13271
+
13272
+ `) : "None captured yet.";
13061
13273
  const aggregateRoots = result.aggregateRoots ? result.aggregateRoots : "None identified yet.";
13062
13274
  const openQuestions = result.openQuestions ? result.openQuestions : "No open questions.";
13063
- return template.replaceAll("{{SESSION_ID}}", result.sessionId).replaceAll("{{CREATED_DATE}}", new Date().toISOString().split("T")[0]).replaceAll("{{BUSINESS_CONTEXT}}", escapePlaceholder(result.businessContext)).replaceAll("{{STATUS}}", result.status).replaceAll("{{ACTORS_TABLE}}", actorsTable || "None identified yet.").replaceAll("{{FUNCTIONAL_REQUIREMENTS_TABLE}}", functionalRequirementsTable || "None identified yet.").replaceAll("{{NON_FUNCTIONAL_REQUIREMENTS_TABLE}}", nonFunctionalRequirementsTable || "None identified yet.").replaceAll("{{ASSUMPTIONS_TABLE}}", assumptionsTable || "None identified yet.").replaceAll("{{BOUNDED_CONTEXTS_TABLE}}", boundedContextsTable || "None identified yet.").replaceAll("{{ENTITIES_TABLE}}", entitiesTable || "None identified yet.").replaceAll("{{DOMAIN_EVENTS_TABLE}}", domainEventsTable || "None identified yet.").replaceAll("{{UBIQUITOUS_LANGUAGE_TABLE}}", ubiquitousLanguageTable || "None identified yet.").replaceAll("{{OPEN_QUESTIONS}}", openQuestions).replaceAll("{{AGGREGATE_ROOTS}}", aggregateRoots);
13275
+ return template.replaceAll("{{SESSION_ID}}", result.sessionId).replaceAll("{{CREATED_DATE}}", new Date().toISOString().split("T")[0]).replaceAll("{{BUSINESS_CONTEXT}}", escapePlaceholder(result.businessContext)).replaceAll("{{STATUS}}", result.status).replaceAll("{{ACTORS_TABLE}}", actorsTable || "None identified yet.").replaceAll("{{FUNCTIONAL_REQUIREMENTS_TABLE}}", functionalRequirementsTable || "None identified yet.").replaceAll("{{NON_FUNCTIONAL_REQUIREMENTS_TABLE}}", nonFunctionalRequirementsTable || "None identified yet.").replaceAll("{{ASSUMPTIONS_TABLE}}", assumptionsTable || "None identified yet.").replaceAll("{{BOUNDED_CONTEXTS_TABLE}}", boundedContextsTable || "None identified yet.").replaceAll("{{ENTITIES_TABLE}}", entitiesTable || "None identified yet.").replaceAll("{{DOMAIN_EVENTS_TABLE}}", domainEventsTable || "None identified yet.").replaceAll("{{UBIQUITOUS_LANGUAGE_TABLE}}", ubiquitousLanguageTable || "None identified yet.").replaceAll("{{OPEN_QUESTIONS}}", openQuestions).replaceAll("{{AGGREGATE_ROOTS}}", aggregateRoots).replaceAll("{{MODELING_JUDGMENT_SECTION}}", modelingJudgmentSection);
13064
13276
  }
13065
13277
  function escapePlaceholder(value) {
13066
13278
  return value.replace(/"/g, "\\\"").replace(/\n/g, "\\n");
@@ -13215,6 +13427,10 @@ ${content}`;
13215
13427
  const tempPath = `${outputPath}.tmp`;
13216
13428
  fs12.writeFileSync(tempPath, content, "utf-8");
13217
13429
  fs12.renameSync(tempPath, outputPath);
13430
+ const rawPath = `${outputPath.replace(/\.md$/, "")}.raw.json`;
13431
+ const rawTemp = `${rawPath}.tmp`;
13432
+ fs12.writeFileSync(rawTemp, JSON.stringify(result, null, 2), "utf-8");
13433
+ fs12.renameSync(rawTemp, rawPath);
13218
13434
  return outputPath;
13219
13435
  }
13220
13436
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardian-framework",
3
- "version": "0.1.104",
3
+ "version": "0.1.109",
4
4
  "description": "Token-optimized agentic framework scaffolder with pi-first architecture",
5
5
  "type": "module",
6
6
  "main": "dist/exports.js",
@@ -211,8 +211,11 @@ cargo fmt --check
211
211
  # Security audit
212
212
  cargo audit
213
213
 
214
- # Coverage
215
- cargo tarpaulin --out Html
214
+ # Coverage (real line coverage via cargo-llvm-cov — native LLVM
215
+ # instrumentation, ~3x faster than tarpaulin). Install once:
216
+ # bash .pi/scripts/install_coverage_tools.sh
217
+ # Gate in CI: bash .pi/scripts/coverage.sh --gate --lcov
218
+ cargo llvm-cov --workspace --summary-only
216
219
  ```
217
220
 
218
221
  ---
@@ -23,6 +23,20 @@
23
23
  - [ ] E2E tests pass
24
24
  - [ ] Architecture contract tests pass
25
25
 
26
+
27
+ ### Model Judgment (Java, ADR-008) — hard gates
28
+ - [ ] txn-placement: @Transactional only in application/ (never domain/infrastructure/interfaces)
29
+ - [ ] dip-domain: domain/ imports no jakarta.persistence/javax.persistence/springframework/hibernate
30
+ - [ ] interface-ownership (tactical modules): every infrastructure/ repository has a domain/-owned interface
31
+ - [ ] adapter-purity: generic-subdomain modules contain no domain/application model code
32
+
33
+ ### Model Judgment (Java, ADR-008) — review questions (validator agent, warn-level)
34
+ - [ ] R1 anemic-model: app-service if/getStatus/setX chains on domain refs (grep evidence)
35
+ - [ ] R2 LSP: domain entity extends a non-abstract base class
36
+ - [ ] R3 cohesion: Helper/Utils/Manager/Orchestrator classes in domain/
37
+ - [ ] R4 primitive obsession: >2 raw primitives on domain service signatures
38
+ - [ ] R5 production-reachability (ADR-009): implemented application services have a production caller outside their module and outside tests (validate-runtime-wiring.sh)
39
+
26
40
  ## Security Validation
27
41
 
28
42
  ### All Code Changes
@@ -56,7 +70,7 @@
56
70
 
57
71
  ### Coverage
58
72
  - [ ] All tests pass
59
- - [ ] Coverage ≥ 80%
73
+ - [ ] Coverage meets the project's real coverage gate (run the project's coverage tool / CI coverage job — no per-module heuristic *_coverage.sh)
60
74
  - [ ] All new code has tests
61
75
  - [ ] Tests follow AAA pattern (Arrange-Act-Assert)
62
76
 
@@ -91,3 +91,12 @@ status: {{STATUS}}
91
91
  ## Aggregate Roots
92
92
 
93
93
  {{AGGREGATE_ROOTS}}
94
+ ---
95
+
96
+ ## Modeling Judgment (ADR-008)
97
+
98
+ Subdomain classification (core/supporting/generic), aggregate invariants,
99
+ context map, domain-event triage, and ubiquitous-language conflicts.
100
+
101
+ {{MODELING_JUDGMENT_SECTION}}
102
+
@@ -22,3 +22,15 @@
22
22
 
23
23
  > **Rule of thumb:** If two agents use different names for the same concept, add an entry.
24
24
  > The canonical term is the one used in the architecture module documents.
25
+
26
+ ## Term Conflicts (language gaps)
27
+
28
+ When the same term is used with different meanings by different parts of the
29
+ business (e.g. "transaction" = a database transaction for engineers but a
30
+ statement line for the business), that is a bounded-context boundary signal —
31
+ record the conflict here and resolve it in the exploration review:
32
+
33
+ | Term A | Term B | Why conflicting | Resolution |
34
+ |--------|--------|-----------------|-----------|
35
+ | <!-- e.g. Transaction | Booking | engineering vs business meaning | adopt "Booking" in Billing context --> |
36
+