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
@@ -122,10 +122,10 @@ function buildInScope(comp: ModuleComponent, primaryAcRows: AcRow[], implSteps:
122
122
  }
123
123
 
124
124
  /** Supported project languages. */
125
- type ProjectLanguage = "typescript" | "java" | "python" | "go" | "rust";
125
+ export type ProjectLanguage = "typescript" | "java" | "python" | "go" | "rust";
126
126
 
127
127
  /** Detect project language at `cwd` by checking for build files. */
128
- function detectLanguage(cwd?: string): ProjectLanguage {
128
+ export function detectLanguage(cwd?: string): ProjectLanguage {
129
129
  if (!cwd) return "typescript";
130
130
  try {
131
131
  if (existsSync(join(cwd, "pom.xml")) || existsSync(join(cwd, "build.gradle")) || existsSync(join(cwd, "build.gradle.kts")))
@@ -143,7 +143,7 @@ function detectLanguage(cwd?: string): ProjectLanguage {
143
143
  }
144
144
 
145
145
  /** Language-specific file extension for implementation files. */
146
- function implExtension(lang: ProjectLanguage): string {
146
+ export function implExtension(lang: ProjectLanguage): string {
147
147
  switch (lang) {
148
148
  case "java": return "java";
149
149
  case "python": return "py";
@@ -154,7 +154,7 @@ function implExtension(lang: ProjectLanguage): string {
154
154
  }
155
155
 
156
156
  /** Language-specific test directory base. */
157
- function testBaseDirFromLang(lang: ProjectLanguage): string {
157
+ export function testBaseDirFromLang(lang: ProjectLanguage): string {
158
158
  switch (lang) {
159
159
  case "java": return "src/test/java";
160
160
  case "python": return "tests";
@@ -165,7 +165,7 @@ function testBaseDirFromLang(lang: ProjectLanguage): string {
165
165
  }
166
166
 
167
167
  /** Language-specific test runner command hint. */
168
- function testRunnerHintFromLang(lang: ProjectLanguage): string {
168
+ export function testRunnerHintFromLang(lang: ProjectLanguage): string {
169
169
  switch (lang) {
170
170
  case "java": return "mvn test";
171
171
  case "python": return "pytest";
@@ -197,6 +197,26 @@ export function generateIssueMarkdown(
197
197
  const title = buildIssueTitle(comp, moduleId);
198
198
  const inScope = buildInScope(comp, compAcs, implSteps, moduleId);
199
199
 
200
+ // ADR-008: append compact modeling directives for aggregate/root components.
201
+ const isTactical = comp.modelingDepth === "tactical" || comp.aggregateRoot;
202
+ if (isTactical && (comp.invariants?.length || comp.events)) {
203
+ if (comp.invariants?.length) {
204
+ inScope.push("Honor the frozen aggregate invariants: " + comp.invariants.join("; "));
205
+ }
206
+ if (comp.events) {
207
+ const ev = [
208
+ ...(comp.events.publishes?.length ? ["publishes " + comp.events.publishes.join(", ")] : []),
209
+ ...(comp.events.subscribes?.length ? ["subscribes " + comp.events.subscribes.join(", ")] : []),
210
+ ].join(", ");
211
+ if (ev) inScope.push("Domain events: " + ev);
212
+ }
213
+ if (comp.aggregateRoot) {
214
+ inScope.push(
215
+ "Implement " + comp.name + " as the aggregate root — no setters; state changes via behavior methods; repository interface owned by the domain layer",
216
+ );
217
+ }
218
+ }
219
+
200
220
  // Language-aware test paths
201
221
  const lang = detectLanguage(cwd);
202
222
  const ext = implExtension(lang);
@@ -373,6 +393,30 @@ export function generateContractFreezeMarkdown(
373
393
  const implSteps = parseImplSequence(doc);
374
394
  const allAcs = parseAcTable(doc);
375
395
 
396
+ // ADR-008 modeling contract for components carrying depth metadata.
397
+ const modelingParts = slice.nextLogicalSlice
398
+ .filter((c: ModuleComponent) => c.modelingDepth === "tactical" || c.aggregateRoot || c.invariants?.length || c.events)
399
+ .map((c: ModuleComponent) => {
400
+ const lines: string[] = [`### ${c.name}`];
401
+ if (c.aggregateRoot) {
402
+ lines.push("- Aggregate root — all state changes flow through behavior methods (no setters)");
403
+ lines.push("- Repository interface is owned by the domain layer (interface-ownership, ADR-008)");
404
+ lines.push("- One aggregate per transaction; reference other aggregates by identity");
405
+ }
406
+ if (c.invariants?.length) {
407
+ lines.push("- Frozen invariants (become pre-generated failing tests):");
408
+ for (const inv of c.invariants) lines.push(` - ${inv}`);
409
+ }
410
+ if (c.events) {
411
+ if (c.events.publishes?.length) lines.push("- Publishes: " + c.events.publishes.join(", "));
412
+ if (c.events.subscribes?.length) lines.push("- Subscribes: " + c.events.subscribes.join(", "));
413
+ }
414
+ return lines.join("\n");
415
+ });
416
+ const modelingContract = modelingParts.length > 0
417
+ ? "## Modeling Contract (ADR-008)\n\n" + modelingParts.join("\n\n")
418
+ : "";
419
+
376
420
  return `---
377
421
  guardian_issue:
378
422
  id: "ISSUE-CONTRACT-FREEZE"
@@ -481,6 +525,8 @@ ${implSteps.length > 0
481
525
  ? `## Full Implementation Sequence (for reference)\n\n${implSteps.map((s) => '- ' + s).join("\n")}`
482
526
  : ""}
483
527
 
528
+ ${modelingContract}
529
+
484
530
  ## Implementation
485
531
 
486
532
  > **Agent:** Create interface-only files. No implementation. Use Clean Architecture layers:
@@ -552,7 +598,6 @@ guardian_issue:
552
598
 
553
599
  file_changes:
554
600
  - "create: .pi/scripts/ci/check_${moduleId}_contracts.sh"
555
- - "create: .pi/scripts/ci/check_${moduleId}_coverage.sh"
556
601
  - "modify: .pi/scripts/ci/run_hardening_stages.sh"
557
602
  ---
558
603
 
@@ -572,10 +617,15 @@ automatic — no human review needed for routine checks.
572
617
  - Verifies all interface methods are implemented
573
618
  - Reports violations with file:line references
574
619
 
575
- ### Coverage Threshold Check
576
- - Runs the project's coverage tool
577
- - Asserts each module meets minimum coverage (default 80%)
578
- - Fails the build if coverage drops
620
+ ### Coverage Threshold Check (real project coverage)
621
+ - Coverage is enforced with the PROJECT'S REAL coverage tooling and gate
622
+ (e.g. \`cargo llvm-cov\` via a \`.pi/scripts/coverage.sh --gate\`, \`pytest
623
+ --cov\`, or \`c8\`/\`nyc\` for TS — whatever the project's CI coverage job
624
+ uses); assert the aggregate threshold the project actually enforces
625
+ - Do NOT create a per-module \`*_coverage.sh\` script — grep-based module
626
+ coverage scripts are coverage-theater (count test functions, never a
627
+ percentage); the real coverage tool's artifact (lcov/cobertura) provides
628
+ the per-file breakdown instead
579
629
 
580
630
  ### CI Integration
581
631
  Each check becomes a CI stage in the hardening pipeline — it runs automatically
@@ -586,7 +636,6 @@ on every PR. No LLM cost. No human review. Just pass or fail.
586
636
  | Script | Purpose | Location |
587
637
  |--------|---------|----------|
588
638
  | check_${moduleId}_contracts.sh | Validate contract implementation | .pi/scripts/ci/ |
589
- | check_${moduleId}_coverage.sh | Enforce coverage thresholds | .pi/scripts/ci/ |
590
639
  | stage_${moduleId}_proofing.sh | CI stage wrapper | .pi/scripts/ci/ |
591
640
 
592
641
  ## CI Pipeline Update
@@ -604,7 +653,7 @@ run_stage "11" "${moduleId}_proofing" \\
604
653
  | # | Criterion | Script |
605
654
  |---|-----------|--------|
606
655
  | 1 | All interfaces have implementations | check_contracts.sh |
607
- | 2 | Coverage ≥ 80% per module | check_coverage.sh |
656
+ | 2 | Coverage meets the project's real coverage gate (project coverage tool + threshold in CI) | project coverage job (e.g. .pi/scripts/coverage.sh --gate) |
608
657
  | 3 | CI runs checks on every PR | run_hardening_stages.sh |
609
658
  | 4 | All scripts exit 0 on pass, 1 on fail | self-validating |
610
659
 
@@ -634,6 +683,108 @@ run_stage "11" "${moduleId}_proofing" \\
634
683
 
635
684
  // ── Architecture Readiness Generator (expanded) ──
636
685
 
686
+
687
+ /**
688
+ * ADR-009: Runtime Integration & Legacy Migration issue generator.
689
+ * Modules whose doc documents runtime integration get a mandatory epic item
690
+ * (after implementation, before proofing) so "implemented but never wired"
691
+ * can no longer be deferred via an honest note.
692
+ */
693
+ export function moduleHasRuntimeIntegration(cwd: string, moduleId: string): boolean {
694
+ const doc = readModuleDoc(cwd, moduleId);
695
+ if (!doc) return false;
696
+ return /^#{2,3}\s+(Integration|Runtime|Dispatch|Wiring|.*Dispatch.*)/im.test(doc);
697
+ }
698
+
699
+ export function generateRuntimeIntegrationMarkdown(
700
+ slice: ArchitectureSlice,
701
+ epicName: string,
702
+ cwd?: string,
703
+ ): string {
704
+ const moduleId = slice.module.replace(/^module-/, "");
705
+ const doc = cwd ? readModuleDoc(cwd, slice.module) : "";
706
+ // Pull the documented integration seam(s) as guidance for the implementer.
707
+ const seam = (() => {
708
+ for (const heading of ["Integration with Existing Modules", "Runtime Integration", "Integration"]) {
709
+ const body = extractSection(doc, heading);
710
+ if (body && body.trim().length > 0) return `## ${heading}\n\n${body.trim().slice(0, 1800)}`;
711
+ }
712
+ return "";
713
+ })();
714
+
715
+ return `---
716
+ guardian_issue:
717
+ id: "ISSUE-RUNTIME-INTEGRATION"
718
+ epic: "${epicName}"
719
+ component: "Runtime Integration & Legacy Migration"
720
+ module: "${slice.module}"
721
+ status: planned
722
+ priority: high
723
+ dependencies: []
724
+
725
+ in_scope:
726
+ - Wire the new service(s) into the REAL runtime path — every choke point the module doc
727
+ documents (primary dispatch loop PLUS parallel / resume / hook variants; not just one seam)
728
+ - Remove or delegate the legacy path the module replaces (legacy symbol deleted or calling
729
+ the new service); honor any declared migration rule (e.g. GAP-N)
730
+ - Flip the feature ADR from Proposed to Accepted once wired
731
+ - Add an end-to-end test through the PUBLIC API that exercises the new path
732
+
733
+ out_of_scope:
734
+ - New feature behavior beyond wiring
735
+ - Schema/data migrations (unless the module doc's migration rule requires them)
736
+
737
+ canonical_references:
738
+ - module: ".pi/architecture/modules/${slice.module}.md"
739
+ - adr: ".pi/architecture/decisions/ADR-009-runtime-wiring-multilang-judgment.md"
740
+
741
+ acceptance_criteria:
742
+ - "New service reachable from the runtime entry path (production, non-test caller)"
743
+ - "Legacy path removed or delegating; migration rule honored"
744
+ - "Feature ADR Accepted"
745
+ - "E2E through the public API exercises the new path"
746
+ - "validate-runtime-wiring.sh passes for the module (no dead service)"
747
+
748
+ validators:
749
+ - ci
750
+ - tests
751
+ - architecture
752
+ - operations
753
+ - canonical
754
+
755
+ implementation_notes: |
756
+ Run .pi/scripts/validate-runtime-wiring.sh before and after. If the new service
757
+ is only referenced by its own tests, it is NOT integrated. Converting the
758
+ documented dispatch choke point(s) is the acceptance slice — do not close this
759
+ issue with an honest deferral note.
760
+
761
+ file_changes:
762
+ - "modify: runtime dispatch / composition root (per module doc integration section)"
763
+ ---
764
+
765
+ # Runtime Integration & Legacy Migration: ${slice.module}
766
+
767
+ ## Intent
768
+
769
+ The implementation issues above delivered tested components. This issue makes them
770
+ **actually run**: wire them into the runtime dispatch path, retire the legacy path,
771
+ and prove it with an end-to-end test — per ADR-009 (implemented-but-unwired is the
772
+ gap this closes).
773
+
774
+ ${seam || "> No explicit integration section found in the module doc — trace the runtime entry path (dispatch/composition root) yourself."}
775
+
776
+ ## Acceptance Criteria
777
+
778
+ | # | Criterion | How to Verify |
779
+ |---|-----------|---------------|
780
+ | 1 | Service reachable from runtime entry path | grep production (non-test) caller outside the module |
781
+ | 2 | Legacy path removed or delegating | legacy symbol deleted / delegates to new service |
782
+ | 3 | Feature ADR Accepted | ADR Status not Proposed |
783
+ | 4 | E2E exercises the new path through the public API | integration/e2e test |
784
+ | 5 | validate-runtime-wiring.sh passes | script exit 0 |
785
+ `;
786
+ }
787
+
637
788
  export function generateArchitectureReadinessMarkdown(
638
789
  slice: ArchitectureSlice,
639
790
  epicName: string,
@@ -353,6 +353,11 @@ export function parseModuleFile(filePath: string): ModuleComponent[] {
353
353
  let currentStatus = "";
354
354
  let currentDesc = "";
355
355
  let currentDeps: string[] = [];
356
+ let currentDepth = undefined as ModuleComponent["modelingDepth"];
357
+ let currentAggregateRoot = false;
358
+ let currentInvariants: string[] = [];
359
+ let currentPublishes: string[] = [];
360
+ let currentSubscribes: string[] = [];
356
361
 
357
362
  function saveCurrent() {
358
363
  if (currentName) {
@@ -364,6 +369,17 @@ export function parseModuleFile(filePath: string): ModuleComponent[] {
364
369
  status: status as ModuleComponent["status"],
365
370
  description: desc.trim(),
366
371
  dependencies: currentDeps.length > 0 ? currentDeps : ["none"],
372
+ ...(currentDepth ? { modelingDepth: currentDepth } : {}),
373
+ ...(currentAggregateRoot ? { aggregateRoot: true } : {}),
374
+ ...(currentInvariants.length > 0 ? { invariants: currentInvariants } : {}),
375
+ ...(currentPublishes.length > 0 || currentSubscribes.length > 0
376
+ ? {
377
+ events: {
378
+ publishes: currentPublishes,
379
+ subscribes: currentSubscribes,
380
+ },
381
+ }
382
+ : {}),
367
383
  });
368
384
  }
369
385
  }
@@ -384,6 +400,11 @@ export function parseModuleFile(filePath: string): ModuleComponent[] {
384
400
  currentStatus = "";
385
401
  currentDesc = "";
386
402
  currentDeps = [];
403
+ currentDepth = undefined;
404
+ currentAggregateRoot = false;
405
+ currentInvariants = [];
406
+ currentPublishes = [];
407
+ currentSubscribes = [];
387
408
  inComponentSection = false;
388
409
  inDetailsSection = false;
389
410
  continue;
@@ -401,6 +422,11 @@ export function parseModuleFile(filePath: string): ModuleComponent[] {
401
422
  currentStatus = "";
402
423
  currentDesc = "";
403
424
  currentDeps = [];
425
+ currentDepth = undefined;
426
+ currentAggregateRoot = false;
427
+ currentInvariants = [];
428
+ currentPublishes = [];
429
+ currentSubscribes = [];
404
430
  continue;
405
431
  }
406
432
 
@@ -413,6 +439,29 @@ export function parseModuleFile(filePath: string): ModuleComponent[] {
413
439
  if (depsStr && depsStr !== "none" && depsStr !== "[TODO") {
414
440
  currentDeps = depsStr.split(",").map((d) => d.trim()).filter(Boolean);
415
441
  }
442
+ } else if (trimmed.startsWith("modelingDepth:")) {
443
+ const v = trimmed.replace("modelingDepth:", "").trim();
444
+ if (v === "tactical" || v === "service" || v === "adapter") currentDepth = v;
445
+ } else if (trimmed.startsWith("aggregateRoot:")) {
446
+ currentAggregateRoot = trimmed.replace("aggregateRoot:", "").trim() === "true";
447
+ } else if (trimmed.startsWith("invariants:")) {
448
+ currentInvariants = trimmed
449
+ .replace("invariants:", "")
450
+ .split(";")
451
+ .map((x) => x.trim())
452
+ .filter(Boolean);
453
+ } else if (trimmed.startsWith("publishes:")) {
454
+ currentPublishes = trimmed
455
+ .replace("publishes:", "")
456
+ .split(",")
457
+ .map((x) => x.trim())
458
+ .filter(Boolean);
459
+ } else if (trimmed.startsWith("subscribes:")) {
460
+ currentSubscribes = trimmed
461
+ .replace("subscribes:", "")
462
+ .split(",")
463
+ .map((x) => x.trim())
464
+ .filter(Boolean);
416
465
  } else if (trimmed.startsWith("**Purpose:**")) {
417
466
  currentDesc = trimmed.replace(/\*\*Purpose:\*\*\s*/, "").trim();
418
467
  } else if (!currentDesc && trimmed.length > 10 && !trimmed.startsWith("#") && !trimmed.startsWith("-") && !trimmed.startsWith("|") && !trimmed.startsWith(">") && !trimmed.startsWith("```")) {
@@ -81,7 +81,7 @@ export interface TddGenerateOptions {
81
81
  cwd: string;
82
82
  /** Language from manifest (default: "typescript") */
83
83
  language: string;
84
- /** Base test directory (default: "tests/unit") */
84
+ /** Base test directory (auto-detected from project language if not set) */
85
85
  testBaseDir?: string;
86
86
  /** Module name for path construction */
87
87
  moduleName?: string;
@@ -55,12 +55,16 @@ export type ExtensionContext = {
55
55
  notify(message: string, level?: string): void;
56
56
  setStatus(key: string, message: string | null): void;
57
57
  confirm(title: string, message: string): Promise<boolean>;
58
+ theme?: unknown;
58
59
  };
59
60
  tools: { execute(name: string, params: Record<string, unknown>): Promise<unknown> };
60
61
  };
61
62
 
62
63
  export type ExtensionAPI = {
63
- on(event: string, handler: (event: unknown, ctx: ExtensionContext) => void | Promise<void>): void;
64
+ on(
65
+ event: string,
66
+ handler: (event: unknown, ctx: ExtensionContext) => unknown | Promise<unknown>,
67
+ ): void;
64
68
  registerTool(options: {
65
69
  name: string;
66
70
  label: string;
@@ -70,7 +74,7 @@ export type ExtensionAPI = {
70
74
  toolCallId: string,
71
75
  params: Record<string, unknown>,
72
76
  signal: AbortSignal,
73
- onUpdate: (update: { type: string; message: string }) => void,
77
+ onUpdate: (update: { type?: string; message?: string; content?: unknown }) => void,
74
78
  ctx: ExtensionContext,
75
79
  ): unknown | Promise<unknown>;
76
80
  }): void;
@@ -96,6 +100,14 @@ export type ModuleComponent = {
96
100
  status: "planned" | "in-progress" | "implemented" | "deprecated";
97
101
  description: string;
98
102
  dependencies: string[];
103
+ /** ADR-008 modeling depth for this component's module (from exploration) */
104
+ modelingDepth?: "tactical" | "service" | "adapter";
105
+ /** True when this component is an aggregate root — slice selection keeps it atomic */
106
+ aggregateRoot?: boolean;
107
+ /** Aggregate invariants (ADR-008) — become frozen contract + TDD tests */
108
+ invariants?: string[];
109
+ /** Domain events this aggregate publishes/subscribes to (ADR-008) */
110
+ events?: { publishes: string[]; subscribes: string[] };
99
111
  };
100
112
 
101
113
  export type ArchitectureSlice = {
@@ -104,6 +116,36 @@ export type ArchitectureSlice = {
104
116
  nextLogicalSlice: ModuleComponent[];
105
117
  };
106
118
 
119
+
120
+ /** A module row inside a roadmap phase (implementation-roadmap.md). */
121
+ export interface RoadmapPhaseModule {
122
+ name: string;
123
+ deliverables: string;
124
+ doc: string;
125
+ }
126
+
127
+ /** A single delivery phase parsed from implementation-roadmap.md. */
128
+ export interface RoadmapPhase {
129
+ index: number;
130
+ title: string;
131
+ goal: string;
132
+ days: string;
133
+ modules: RoadmapPhaseModule[];
134
+ completedModules: string[];
135
+ dependencies: string[];
136
+ migrations: string[];
137
+ criteria: string[];
138
+ status: string; // "pending" | "in_progress" | "done"
139
+ }
140
+
141
+ /** Persisted roadmap execution state (.pi/.guardian-roadmap-state.json). */
142
+ export interface RoadmapState {
143
+ phases: RoadmapPhase[];
144
+ currentPhaseIndex: number;
145
+ startedAt: string;
146
+ updatedAt: string;
147
+ }
148
+
107
149
  export type EpicState = {
108
150
  name: string;
109
151
  trackingIssueId: string | null;