guardian-framework 0.1.27 → 0.1.29

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 CHANGED
@@ -35,6 +35,9 @@ Domain Exploration ←─ you and the agent explore, iterate, refine
35
35
  Architecture Modules ←─ you review, adjust, approve
36
36
 
37
37
 
38
+ Enrich Module Docs ←─ agent fills DDD content from exploration
39
+
40
+
38
41
  ADRs, Diagrams, Specs ←─ you challenge decisions, finalize
39
42
 
40
43
 
@@ -75,15 +78,49 @@ You go back and forth until the model reflects your actual business. Only then d
75
78
  /domain --architect-scaffold <session-id>
76
79
  ```
77
80
 
78
- Guardian generates one architecture module per bounded context, with components, dependencies, and contract interfaces:
81
+ Guardian generates one architecture module per bounded context, with architecture layers, aggregates, repository contracts, and domain events:
79
82
 
80
83
  ```markdown
81
84
  # Audit Ingestion
82
85
 
83
- ## HMAC Signature Verifier
84
- status: planned
85
- description: Verifies HMAC-SHA256 signatures on incoming audit records.
86
- depends: none
86
+ ## Architecture
87
+ | Layer | Responsibility |
88
+ |-------|--------------|
89
+ | **Domain** | Aggregates, entities, repository interfaces | src/audit-ingestion/domain/ |
90
+ | **Application** | Use cases, DTOs | src/audit-ingestion/application/ |
91
+
92
+ ## Aggregates
93
+ ### AuditRecord
94
+ **Repository Interface:**
95
+ ```rust
96
+ pub trait AuditRecordRepository: Send + Sync {
97
+ async fn find_by_id(&self, id: &AuditRecordId) -> Result<Option<AuditRecord>, DomainError>;
98
+ async fn save(&self, entity: &AuditRecord) -> Result<(), DomainError>;
99
+ async fn delete(&self, id: &AuditRecordId) -> Result<(), DomainError>;
100
+ }
101
+ ```
102
+
103
+ ## Domain Events
104
+ | Event | Description | Trigger |
105
+ |-------|-------------|--------|
106
+ | AuditRecordIngested | ... | ... |
107
+ ```
108
+
109
+ ### 3.5 Enrich with Full DDD Content
110
+
111
+ ```
112
+ /domain --enrich <session-id>
113
+ ```
114
+
115
+ The agent reads the domain exploration and enhances each module stub with:
116
+ - Mermaid data flow diagrams
117
+ - Real Rust struct stubs
118
+ - API endpoint tables
119
+ - Ubiquitous Language per context
120
+ - Cross-context dependencies
121
+ - Security considerations
122
+
123
+ This produces production-quality architecture docs (5–20 KB each) that an implementer can build from directly.
87
124
 
88
125
  ## Idempotency Handler
89
126
  status: planned
@@ -162,9 +199,12 @@ npx guardian-framework init
162
199
  # 3. Generate architecture from exploration
163
200
  /domain --architect-scaffold <session-id>
164
201
 
165
- # 4. Review and refine (ADRs, diagrams, modules — human-in-the-loop)
202
+ # 4. Enrich module docs with full DDD content
203
+ /domain --enrich <session-id>
204
+
205
+ # 5. Review and refine (ADRs, diagrams, modules — human-in-the-loop)
166
206
 
167
- # 5. Adopt the roadmap
207
+ # 6. Adopt the roadmap
168
208
  /architect --roadmap
169
209
  /architect --adopt-roadmap
170
210
 
package/dist/cli.js CHANGED
@@ -1335,7 +1335,7 @@ __export(exports_package, {
1335
1335
  bin: () => bin,
1336
1336
  author: () => author
1337
1337
  });
1338
- var name = "guardian-framework", version = "0.1.27", 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;
1338
+ var name = "guardian-framework", version = "0.1.29", 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;
1339
1339
  var init_package = __esm(() => {
1340
1340
  exports = {
1341
1341
  ".": {
@@ -10011,6 +10011,10 @@ function coerceEntityType(val) {
10011
10011
  return "value-object";
10012
10012
  if (val === "aggregate-root")
10013
10013
  return "aggregate-root";
10014
+ if (val === "Aggregate Root")
10015
+ return "aggregate-root";
10016
+ if (val === "Value Object")
10017
+ return "value-object";
10014
10018
  if (val === "ui-concept")
10015
10019
  return "ui-concept";
10016
10020
  if (val === "user-intent")
@@ -10250,6 +10254,9 @@ function readExplorationSession(sessionId, projectDir) {
10250
10254
  aggregateRoots
10251
10255
  };
10252
10256
  }
10257
+ function stripMarkdown(text) {
10258
+ return text.replace(/\*\*(.+?)\*\*/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/~~(.+?)~~/g, "$1").replace(/`(.+?)`/g, "$1").replace(/\[(.+?)\]\(.+?\)/g, "$1").trim();
10259
+ }
10253
10260
  function extractTable(body, sectionName) {
10254
10261
  const sectionRegex = new RegExp(`## ${sectionName}\\n+([\\s\\S]*?)(?:\\n##|$)`);
10255
10262
  const sectionMatch = body.match(sectionRegex);
@@ -10263,7 +10270,7 @@ function extractTable(body, sectionName) {
10263
10270
  const trimmed = line.trim();
10264
10271
  const content = trimmed.startsWith("|") ? trimmed.slice(1) : trimmed;
10265
10272
  const withoutTrailing = content.endsWith("|") ? content.slice(0, -1) : content;
10266
- return withoutTrailing.split("|").map((s) => s.trim());
10273
+ return withoutTrailing.split("|").map((s) => stripMarkdown(s.trim()));
10267
10274
  });
10268
10275
  }
10269
10276
  function scaffoldFromExploration(sessionId, options) {
@@ -10564,11 +10571,12 @@ None yet
10564
10571
 
10565
10572
  `;
10566
10573
  } else {
10567
- content = `# ${bc.name}
10574
+ content += `# ${bc.name}
10568
10575
 
10569
10576
  `;
10570
10577
  content += `## Module Status
10571
-
10578
+ `;
10579
+ content += `
10572
10580
  `;
10573
10581
  content += `**Status:** Planned
10574
10582
  `;
@@ -10582,57 +10590,186 @@ None yet
10582
10590
  ${bc.description}
10583
10591
 
10584
10592
  `;
10585
- content += `## Components
10593
+ content += `
10594
+ `;
10595
+ content += `## Architecture
10596
+ `;
10597
+ content += `
10598
+ `;
10599
+ content += `This module follows **Domain-Driven Design** with Clean Architecture layers:
10600
+ `;
10601
+ content += `
10602
+ `;
10603
+ content += `| Layer | Responsibility |
10604
+ `;
10605
+ content += `|-------|--------------|
10606
+ `;
10607
+ content += `| **Domain** | Aggregates, entities, value objects, domain services, repository interfaces | src/${moduleName}/domain/ |
10608
+ `;
10609
+ content += `| **Application** | Use cases / application services, DTOs, input validation | src/${moduleName}/application/ |
10610
+ `;
10611
+ content += `| **Infrastructure** | Repository implementations, DB models, external clients | src/${moduleName}/infrastructure/ |
10612
+ `;
10613
+ content += `| **Interfaces** | API handlers, event subscribers, serialization | src/${moduleName}/interfaces/ |
10614
+ `;
10615
+ content += `
10616
+ `;
10617
+ content += `## Aggregates
10618
+ `;
10619
+ content += `
10620
+ `;
10621
+ if (aggregateRoots.length > 0) {
10622
+ for (const ar of aggregateRoots) {
10623
+ content += `### ${ar.name}
10586
10624
 
10587
- ${componentsSection}
10625
+ `;
10626
+ content += ar.description === "" ? "_No description._" : ar.description;
10627
+ content += `
10628
+ `;
10629
+ const ownedEntities = regularEntities.filter((e2) => e2.description.includes(ar.name) || ar.description.includes(e2.name));
10630
+ const ownedVOs = valueObjects.filter((vo) => vo.description.includes(ar.name) || ar.description.includes(vo.name));
10631
+ content += `
10632
+ `;
10633
+ if (ownedEntities.length > 0) {
10634
+ content += `**Owned Entities:**
10635
+ `;
10636
+ for (const ent of ownedEntities) {
10637
+ content += `- ${ent.name}: ${ent.description}
10638
+ `;
10639
+ }
10640
+ content += `
10641
+ `;
10642
+ }
10643
+ if (ownedVOs.length > 0) {
10644
+ content += `**Value Objects:**
10645
+ `;
10646
+ for (const vo of ownedVOs) {
10647
+ content += `- ${vo.name}: ${vo.description}
10648
+ `;
10649
+ }
10650
+ content += `
10651
+ `;
10652
+ }
10653
+ const arVarName = ar.name.charAt(0).toLowerCase() + ar.name.slice(1);
10654
+ content += `**Repository Interface:**
10655
+ `;
10656
+ content += `
10657
+ `;
10658
+ content += "```rust\n";
10659
+ content += `pub trait ${ar.name}Repository: Send + Sync {
10660
+ `;
10661
+ content += ` async fn find_by_id(&self, id: &${ar.name}Id) -> Result<Option<${ar.name}>, DomainError>;
10662
+ `;
10663
+ content += ` async fn save(&self, ${arVarName}: &${ar.name}) -> Result<(), DomainError>;
10664
+ `;
10665
+ content += ` async fn delete(&self, id: &${ar.name}Id) -> Result<(), DomainError>;
10666
+ `;
10667
+ content += `}
10668
+ `;
10669
+ content += "```";
10670
+ content += `
10671
+ `;
10672
+ if (ownedEntities.length > 0 || ownedVOs.length > 0) {
10673
+ content += `**Aggregate Invariants:**
10674
+ `;
10675
+ content += `- All operations go through ${ar.name} aggregate root
10676
+ `;
10677
+ content += `- References to child entities resolve through ${ar.name}
10678
+ `;
10679
+ content += `- ${ar.name}Id is the sole external identifier
10680
+ `;
10681
+ content += `
10682
+ `;
10683
+ }
10684
+ }
10685
+ } else {
10686
+ content += `No aggregates defined yet.
10687
+ `;
10688
+ content += `
10689
+ `;
10690
+ }
10691
+ content += `
10588
10692
  `;
10589
10693
  content += `## Domain Events
10590
-
10694
+ `;
10695
+ content += `
10591
10696
  `;
10592
10697
  if (contextEvents.length === 0) {
10593
10698
  content += `None defined yet.
10594
-
10699
+ `;
10700
+ content += `
10595
10701
  `;
10596
10702
  } else {
10703
+ content += `| Event | Description | Trigger |
10704
+ `;
10705
+ content += `|-------|-------------|--------|
10706
+ `;
10597
10707
  for (const ev of contextEvents) {
10598
- content += `- **${ev.name}** - ${ev.description} (triggered by: ${ev.triggeredBy})
10708
+ content += `| ${ev.name} | ${ev.description} | ${ev.triggeredBy} |
10599
10709
  `;
10600
10710
  }
10601
10711
  content += `
10602
10712
  `;
10603
10713
  }
10714
+ content += `
10715
+ `;
10604
10716
  content += `## Ubiquitous Language
10605
-
10606
10717
  `;
10607
- if (contextTerms.length === 0) {
10608
- content += `None defined yet.
10609
-
10718
+ content += `
10610
10719
  `;
10720
+ if (contextTerms.length === 0) {
10721
+ content += "See `.pi/domain/ubiquitous-language.md` for full glossary.\n\n";
10611
10722
  } else {
10612
- content += `| Term | Definition | Aliases |
10723
+ content += `| Term | Definition |
10613
10724
  `;
10614
- content += `|------|-----------|---------|
10725
+ content += `|------|-----------|
10615
10726
  `;
10616
10727
  for (const t of contextTerms) {
10617
- content += `| ${t.term} | ${t.definition} | ${t.aliases.join(", ")} |
10728
+ content += `| ${t.term} | ${t.definition} |
10618
10729
  `;
10619
10730
  }
10620
10731
  content += `
10621
10732
  `;
10622
10733
  }
10734
+ content += `
10735
+ `;
10623
10736
  content += `## Dependencies
10624
-
10625
- ${depsSection}
10626
10737
  `;
10627
- content += `## Key Files
10628
-
10629
- None yet
10630
-
10738
+ content += `
10739
+ `;
10740
+ content += `${depsSection}
10741
+ `;
10742
+ content += `
10743
+ `;
10744
+ content += `## Implementation Notes
10745
+ `;
10746
+ content += `
10747
+ `;
10748
+ content += `Implementation should follow Rust DDD + Clean Architecture patterns:
10749
+ `;
10750
+ content += `
10751
+ `;
10752
+ content += `1. Domain layer: aggregates, entities, value objects, repository interfaces
10753
+ `;
10754
+ content += `2. Infrastructure: Postgres repository implementations via SQLx
10755
+ `;
10756
+ content += `3. Application: use case orchestration
10757
+ `;
10758
+ content += `4. Interfaces: HTTP handlers with Axum
10759
+ `;
10760
+ content += `
10761
+ `;
10762
+ content += `Each layer MUST NOT leak into adjacent layers.
10763
+ `;
10764
+ content += `
10631
10765
  `;
10632
10766
  content += `## ADRs
10633
-
10634
- None yet
10635
-
10767
+ `;
10768
+ content += `
10769
+ `;
10770
+ content += `None yet
10771
+ `;
10772
+ content += `
10636
10773
  `;
10637
10774
  }
10638
10775
  if (!dryRun) {
package/dist/exports.js CHANGED
@@ -995,7 +995,7 @@ __export(exports_package, {
995
995
  bin: () => bin,
996
996
  author: () => author
997
997
  });
998
- var name = "guardian-framework", version = "0.1.27", 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;
998
+ var name = "guardian-framework", version = "0.1.29", 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;
999
999
  var init_package = __esm(() => {
1000
1000
  exports = {
1001
1001
  ".": {
@@ -11212,6 +11212,10 @@ function coerceEntityType(val) {
11212
11212
  return "value-object";
11213
11213
  if (val === "aggregate-root")
11214
11214
  return "aggregate-root";
11215
+ if (val === "Aggregate Root")
11216
+ return "aggregate-root";
11217
+ if (val === "Value Object")
11218
+ return "value-object";
11215
11219
  if (val === "ui-concept")
11216
11220
  return "ui-concept";
11217
11221
  if (val === "user-intent")
@@ -11451,6 +11455,9 @@ function readExplorationSession(sessionId, projectDir) {
11451
11455
  aggregateRoots
11452
11456
  };
11453
11457
  }
11458
+ function stripMarkdown(text) {
11459
+ return text.replace(/\*\*(.+?)\*\*/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/~~(.+?)~~/g, "$1").replace(/`(.+?)`/g, "$1").replace(/\[(.+?)\]\(.+?\)/g, "$1").trim();
11460
+ }
11454
11461
  function extractTable(body, sectionName) {
11455
11462
  const sectionRegex = new RegExp(`## ${sectionName}\\n+([\\s\\S]*?)(?:\\n##|$)`);
11456
11463
  const sectionMatch = body.match(sectionRegex);
@@ -11464,7 +11471,7 @@ function extractTable(body, sectionName) {
11464
11471
  const trimmed = line.trim();
11465
11472
  const content = trimmed.startsWith("|") ? trimmed.slice(1) : trimmed;
11466
11473
  const withoutTrailing = content.endsWith("|") ? content.slice(0, -1) : content;
11467
- return withoutTrailing.split("|").map((s) => s.trim());
11474
+ return withoutTrailing.split("|").map((s) => stripMarkdown(s.trim()));
11468
11475
  });
11469
11476
  }
11470
11477
  function scaffoldFromExploration(sessionId, options) {
@@ -11765,11 +11772,12 @@ None yet
11765
11772
 
11766
11773
  `;
11767
11774
  } else {
11768
- content = `# ${bc.name}
11775
+ content += `# ${bc.name}
11769
11776
 
11770
11777
  `;
11771
11778
  content += `## Module Status
11772
-
11779
+ `;
11780
+ content += `
11773
11781
  `;
11774
11782
  content += `**Status:** Planned
11775
11783
  `;
@@ -11783,57 +11791,186 @@ None yet
11783
11791
  ${bc.description}
11784
11792
 
11785
11793
  `;
11786
- content += `## Components
11794
+ content += `
11795
+ `;
11796
+ content += `## Architecture
11797
+ `;
11798
+ content += `
11799
+ `;
11800
+ content += `This module follows **Domain-Driven Design** with Clean Architecture layers:
11801
+ `;
11802
+ content += `
11803
+ `;
11804
+ content += `| Layer | Responsibility |
11805
+ `;
11806
+ content += `|-------|--------------|
11807
+ `;
11808
+ content += `| **Domain** | Aggregates, entities, value objects, domain services, repository interfaces | src/${moduleName}/domain/ |
11809
+ `;
11810
+ content += `| **Application** | Use cases / application services, DTOs, input validation | src/${moduleName}/application/ |
11811
+ `;
11812
+ content += `| **Infrastructure** | Repository implementations, DB models, external clients | src/${moduleName}/infrastructure/ |
11813
+ `;
11814
+ content += `| **Interfaces** | API handlers, event subscribers, serialization | src/${moduleName}/interfaces/ |
11815
+ `;
11816
+ content += `
11817
+ `;
11818
+ content += `## Aggregates
11819
+ `;
11820
+ content += `
11821
+ `;
11822
+ if (aggregateRoots.length > 0) {
11823
+ for (const ar of aggregateRoots) {
11824
+ content += `### ${ar.name}
11787
11825
 
11788
- ${componentsSection}
11826
+ `;
11827
+ content += ar.description === "" ? "_No description._" : ar.description;
11828
+ content += `
11829
+ `;
11830
+ const ownedEntities = regularEntities.filter((e2) => e2.description.includes(ar.name) || ar.description.includes(e2.name));
11831
+ const ownedVOs = valueObjects.filter((vo) => vo.description.includes(ar.name) || ar.description.includes(vo.name));
11832
+ content += `
11833
+ `;
11834
+ if (ownedEntities.length > 0) {
11835
+ content += `**Owned Entities:**
11836
+ `;
11837
+ for (const ent of ownedEntities) {
11838
+ content += `- ${ent.name}: ${ent.description}
11839
+ `;
11840
+ }
11841
+ content += `
11842
+ `;
11843
+ }
11844
+ if (ownedVOs.length > 0) {
11845
+ content += `**Value Objects:**
11846
+ `;
11847
+ for (const vo of ownedVOs) {
11848
+ content += `- ${vo.name}: ${vo.description}
11849
+ `;
11850
+ }
11851
+ content += `
11852
+ `;
11853
+ }
11854
+ const arVarName = ar.name.charAt(0).toLowerCase() + ar.name.slice(1);
11855
+ content += `**Repository Interface:**
11856
+ `;
11857
+ content += `
11858
+ `;
11859
+ content += "```rust\n";
11860
+ content += `pub trait ${ar.name}Repository: Send + Sync {
11861
+ `;
11862
+ content += ` async fn find_by_id(&self, id: &${ar.name}Id) -> Result<Option<${ar.name}>, DomainError>;
11863
+ `;
11864
+ content += ` async fn save(&self, ${arVarName}: &${ar.name}) -> Result<(), DomainError>;
11865
+ `;
11866
+ content += ` async fn delete(&self, id: &${ar.name}Id) -> Result<(), DomainError>;
11867
+ `;
11868
+ content += `}
11869
+ `;
11870
+ content += "```";
11871
+ content += `
11872
+ `;
11873
+ if (ownedEntities.length > 0 || ownedVOs.length > 0) {
11874
+ content += `**Aggregate Invariants:**
11875
+ `;
11876
+ content += `- All operations go through ${ar.name} aggregate root
11877
+ `;
11878
+ content += `- References to child entities resolve through ${ar.name}
11879
+ `;
11880
+ content += `- ${ar.name}Id is the sole external identifier
11881
+ `;
11882
+ content += `
11883
+ `;
11884
+ }
11885
+ }
11886
+ } else {
11887
+ content += `No aggregates defined yet.
11888
+ `;
11889
+ content += `
11890
+ `;
11891
+ }
11892
+ content += `
11789
11893
  `;
11790
11894
  content += `## Domain Events
11791
-
11895
+ `;
11896
+ content += `
11792
11897
  `;
11793
11898
  if (contextEvents.length === 0) {
11794
11899
  content += `None defined yet.
11795
-
11900
+ `;
11901
+ content += `
11796
11902
  `;
11797
11903
  } else {
11904
+ content += `| Event | Description | Trigger |
11905
+ `;
11906
+ content += `|-------|-------------|--------|
11907
+ `;
11798
11908
  for (const ev of contextEvents) {
11799
- content += `- **${ev.name}** - ${ev.description} (triggered by: ${ev.triggeredBy})
11909
+ content += `| ${ev.name} | ${ev.description} | ${ev.triggeredBy} |
11800
11910
  `;
11801
11911
  }
11802
11912
  content += `
11803
11913
  `;
11804
11914
  }
11915
+ content += `
11916
+ `;
11805
11917
  content += `## Ubiquitous Language
11806
-
11807
11918
  `;
11808
- if (contextTerms.length === 0) {
11809
- content += `None defined yet.
11810
-
11919
+ content += `
11811
11920
  `;
11921
+ if (contextTerms.length === 0) {
11922
+ content += "See `.pi/domain/ubiquitous-language.md` for full glossary.\n\n";
11812
11923
  } else {
11813
- content += `| Term | Definition | Aliases |
11924
+ content += `| Term | Definition |
11814
11925
  `;
11815
- content += `|------|-----------|---------|
11926
+ content += `|------|-----------|
11816
11927
  `;
11817
11928
  for (const t of contextTerms) {
11818
- content += `| ${t.term} | ${t.definition} | ${t.aliases.join(", ")} |
11929
+ content += `| ${t.term} | ${t.definition} |
11819
11930
  `;
11820
11931
  }
11821
11932
  content += `
11822
11933
  `;
11823
11934
  }
11935
+ content += `
11936
+ `;
11824
11937
  content += `## Dependencies
11825
-
11826
- ${depsSection}
11827
11938
  `;
11828
- content += `## Key Files
11829
-
11830
- None yet
11831
-
11939
+ content += `
11940
+ `;
11941
+ content += `${depsSection}
11942
+ `;
11943
+ content += `
11944
+ `;
11945
+ content += `## Implementation Notes
11946
+ `;
11947
+ content += `
11948
+ `;
11949
+ content += `Implementation should follow Rust DDD + Clean Architecture patterns:
11950
+ `;
11951
+ content += `
11952
+ `;
11953
+ content += `1. Domain layer: aggregates, entities, value objects, repository interfaces
11954
+ `;
11955
+ content += `2. Infrastructure: Postgres repository implementations via SQLx
11956
+ `;
11957
+ content += `3. Application: use case orchestration
11958
+ `;
11959
+ content += `4. Interfaces: HTTP handlers with Axum
11960
+ `;
11961
+ content += `
11962
+ `;
11963
+ content += `Each layer MUST NOT leak into adjacent layers.
11964
+ `;
11965
+ content += `
11832
11966
  `;
11833
11967
  content += `## ADRs
11834
-
11835
- None yet
11836
-
11968
+ `;
11969
+ content += `
11970
+ `;
11971
+ content += `None yet
11972
+ `;
11973
+ content += `
11837
11974
  `;
11838
11975
  }
11839
11976
  if (!dryRun) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardian-framework",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "description": "Token-optimized agentic framework scaffolder with pi-first architecture",
5
5
  "type": "module",
6
6
  "main": "dist/exports.js",
@@ -456,6 +456,35 @@ class PipelineManager {
456
456
 
457
457
  if (this.state.currentItemIndex >= this.state.items.length) {
458
458
  this.state.status = "done";
459
+
460
+ // Auto-advance roadmap when module epic completes
461
+ const roadmapFile = join(this.cwd, ".pi/.guardian-roadmap-state.json");
462
+ if (existsSync(roadmapFile)) {
463
+ try {
464
+ const roadmap = JSON.parse(readFileSync(roadmapFile, "utf-8"));
465
+ const epicName = this.state.name;
466
+ const currentPhase = roadmap.phases?.[roadmap.currentPhaseIndex];
467
+ if (currentPhase && epicName) {
468
+ // Mark module as completed
469
+ const completed = new Set(currentPhase.completedModules || []);
470
+ completed.add(epicName);
471
+ currentPhase.completedModules = Array.from(completed);
472
+
473
+ // Check if all modules done → mark phase done
474
+ const allDone = currentPhase.modules.every(
475
+ (m: { name: string }) => completed.has(m.name),
476
+ );
477
+ if (allDone) {
478
+ currentPhase.status = "done";
479
+ }
480
+
481
+ roadmap.updatedAt = new Date().toISOString();
482
+ writeFileSync(roadmapFile, JSON.stringify(roadmap, null, 2));
483
+ }
484
+ } catch (err) {
485
+ // Silently fail — roadmap update is best-effort
486
+ }
487
+ }
459
488
  }
460
489
  }
461
490
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-07-03T11:06:29Z",
2
+ "timestamp": "2026-07-03T18:18:02Z",
3
3
  "mode": "all",
4
4
  "stages_run": [],
5
5
  "summary": {