---
name: step-04a-collect
description: Collect module summaries and cross-module interactions
model: opus
next_step: steps/step-04b-analyze.md
---

> **Context files:** `_shared.md` | `_suggestions.md`

# Step 4a: Collect - Module Summaries & Cross-Module Interactions

## MANDATORY EXECUTION RULES
- ALWAYS load summaries of ALL completed modules
- NEVER duplicate module content - work with summaries only
- This step auto-validates for single-module (trivial consolidation)

## YOUR TASK
Collect module summaries and identify all cross-module interactions (FK references, shared entities, events, shared data).

---

## EXECUTION SEQUENCE

### 1. Read Module Summaries

```
ba-reader.getCompletedModulesSummary({feature_id})
→ For each module: code, entities[], key BRs, permissions[], dependencies
→ Max 100 lines per module summary

ba-reader.readApplicationContext({feature_id})
→ cadrage.applicationRoles, modules[], dependencyGraph
```

IF status already "consolidated":
  Display: "Consolidation already completed. Loading step-05a-handoff."
  Load: steps/step-05a-handoff.md
  STOP

### 2. Cross-Module Interactions

**2a. Foreign Key References**

For each dependency edge in dependencyGraph:
```json
{
  "from": "Orders",
  "to": "Customers",
  "type": "FK",
  "references": [
    {
      "sourceEntity": "Order",
      "sourceField": "CustomerId",
      "targetEntity": "Customer",
      "targetField": "Id",
      "cardinality": "N:1",
      "cascadeDelete": false,
      "required": true
    }
  ]
}
```

Verify each FK reference exists in the corresponding module's entities.
IF missing → WARNING + ask user to confirm or add.

**2b. Shared Entities**

Detect entities referenced by multiple modules:
```json
{
  "sharedEntities": [
    {
      "entity": "Customer",
      "definedIn": "Customers",
      "referencedBy": ["Orders", "Invoices"],
      "referenceType": "FK"
    }
  ]
}
```

**2c. Event-Based Interactions**

Identify event flows between modules:
```json
{
  "events": [
    {
      "producer": "Orders",
      "event": "OrderApproved",
      "consumers": ["Invoices"],
      "description": "When an order is approved, an invoice is auto-generated"
    }
  ]
}
```

**2d. Shared Reference Data**

Identify shared lookup/reference tables:
```json
{
  "sharedReferenceData": [
    {
      "table": "Currency",
      "usedBy": ["Orders", "Invoices"],
      "owner": "shared"
    }
  ]
}
```

**2e. Cross-Module Reference Validation (ENHANCED)**

> **CRITICAL:** Verify ALL cross-module entity references are resolvable.
> **Purpose:** Prevent broken FK references and runtime errors in ralph-loop.

**Process:**

1. **Build Entity Registry:**
   ```javascript
   const entityRegistry = {};
   for (const module of completedModules) {
     entityRegistry[module.code] = {
       entities: module.analysis.entities.map(e => ({
         name: e.name,
         attributes: e.attributes.map(a => a.name),
         pk: e.attributes.find(a => a.name === "Id" || a.unique)?.name || "Id"
       }))
     };
   }
   ```

2. **Extract Cross-Module References:**

   For EACH module, scan all entity relationships:
   ```javascript
   for (const entity of module.analysis.entities) {
     for (const rel of entity.relationships) {
       // Detect cross-module reference
       if (rel.target.includes(".")) {
         // Format: "ModuleName.EntityName.FieldName" or "ModuleName.EntityName"
         const [targetModule, targetEntity, targetField] = rel.target.split(".");

         // Validate: target module exists
         if (!entityRegistry[targetModule]) {
           BLOCKING_ERROR(`Module ${module.code}: Entity ${entity.name} references non-existent module "${targetModule}"`);
         }

         // Validate: target entity exists in target module
         const targetModuleEntities = entityRegistry[targetModule].entities;
         const resolvedEntity = targetModuleEntities.find(e => e.name === targetEntity);
         if (!resolvedEntity) {
           BLOCKING_ERROR(`Module ${module.code}: Entity ${entity.name} references non-existent entity "${targetEntity}" in module "${targetModule}"`);
         }

         // Validate: target field exists (if specified)
         if (targetField && !resolvedEntity.attributes.includes(targetField)) {
           BLOCKING_ERROR(`Module ${module.code}: Entity ${entity.name} references non-existent field "${targetField}" on "${targetModule}.${targetEntity}"`);
         }

         // Record valid reference
         crossModuleReferences.push({
           sourceModule: module.code,
           sourceEntity: entity.name,
           sourceField: rel.sourceField || `${targetEntity}Id`,
           targetModule,
           targetEntity,
           targetField: targetField || resolvedEntity.pk,
           type: rel.type,
           cardinality: rel.cardinality || "N:1",
           status: "RESOLVED"
         });
       }
     }
   }
   ```

3. **Validate Dependency Graph Alignment:**

   ```javascript
   // For each cross-module reference, verify dependency edge exists
   for (const ref of crossModuleReferences) {
     const dependencyExists = dependencyGraph.edges.find(edge =>
       edge.from === ref.sourceModule && edge.to === ref.targetModule
     );

     if (!dependencyExists) {
       WARNING(`Dependency missing: ${ref.sourceModule} → ${ref.targetModule}`);
       WARNING(`Reason: ${ref.sourceEntity}.${ref.sourceField} references ${ref.targetModule}.${ref.targetEntity}`);

       // Auto-fix: add missing dependency edge
       dependencyGraph.edges.push({
         from: ref.sourceModule,
         to: ref.targetModule,
         type: "FK",
         reason: `${ref.sourceEntity}.${ref.sourceField} → ${ref.targetEntity}`
       });

       // Recalculate topological order
       topologicalOrder = calculateTopologicalOrder(dependencyGraph);
     }
   }
   ```

4. **Display Cross-Module Reference Map:**

   ```
   ═══════════════════════════════════════════════════════════════
     CROSS-MODULE REFERENCES VALIDATED
   ═══════════════════════════════════════════════════════════════

   | Source Module | Source Entity | Target | Type | Status |
   |---------------|---------------|--------|------|--------|
   | TimeTracking | TimeEntry | Projects.Project.Id | FK (N:1) | ✓ RESOLVED |
   | LeaveManagement | LeaveRequest | Projects.Employee.Id | FK (N:1) | ✓ RESOLVED |
   | AbsenceManagement | Absence | Projects.Employee.Id | FK (N:1) | ✓ RESOLVED |
   | Reporting | ProjectReport | Projects.Project.Id | FK (N:1) | ✓ RESOLVED |

   Total: {count} cross-module references
   All references RESOLVED ✓
   ═══════════════════════════════════════════════════════════════
   ```

5. **Detect Circular Dependencies:**

   ```javascript
   const cycles = detectCycles(dependencyGraph);

   if (cycles.length > 0) {
     BLOCKING_ERROR("Circular dependencies detected:");
     for (const cycle of cycles) {
       ERROR(`  ${cycle.join(" → ")}`);
     }

     ACTIONS:
     1. Review module dependencies
     2. Break cycles by:
        - Moving shared entities to a separate "Core" module
        - Using event-driven communication instead of FK
        - Removing unnecessary dependencies
     3. Re-run step-02 decomposition with fixed dependencies

     STOP - DO NOT PROCEED TO HANDOFF
   }
   ```

6. **Store Cross-Module Interaction Data:**

   ```javascript
   ba-writer.enrichSection({
     featureId: {feature_id},
     section: "consolidation",
     data: {
       crossModuleInteractions: crossModuleReferences.map(ref => ({
         fromModule: ref.sourceModule,
         toModule: ref.targetModule,
         interactionType: "FK_REFERENCE",
         description: `${ref.sourceEntity}.${ref.sourceField} → ${ref.targetModule}.${ref.targetEntity}.${ref.targetField}`,
         entities: [ref.sourceEntity, ref.targetEntity],
         cardinality: ref.cardinality,
         status: ref.status
       }))
     }
   });
   ```

**Success Criteria:**
- ✓ All cross-module references resolved
- ✓ Dependency graph aligned with actual references
- ✓ No circular dependencies
- ✓ Topological order valid

**Failure Handling:**
- BLOCKING errors for unresolved references
- Auto-fix missing dependency edges (with warning)
- Manual intervention required for circular dependencies

---

### 2f. Cross-Application Validation (Project Mode Only)

> **Only runs when `workflow.mode === "project"`** (multi-application feature).
> Validates cross-application interactions identified during step-01b.

```
IF workflow.mode !== "project":
  SKIP this section entirely
  → Proceed to section 6 storage
```

**Process:**

1. **Load project feature.json:**
   ```
   projectFeature = ba-reader.findProjectFeature()
   applications = projectFeature.applications
   applicationGraph = projectFeature.applicationDependencyGraph
   ```

2. **Cross-Application Shared Entities:**
   ```javascript
   const crossAppSharedEntities = [];
   for (const app1 of applications) {
     for (const app2 of applications) {
       if (app1.code === app2.code) continue;
       for (const mod1 of app1.modules) {
         for (const mod2 of app2.modules) {
           // Detect entity name overlap or FK references across apps
           const shared = findSharedEntities(mod1.entities, mod2.entities);
           if (shared.length > 0) {
             crossAppSharedEntities.push({
               entity: shared[0].name,
               definedInApp: app1.code,
               definedInModule: mod1.code,
               referencedByApp: app2.code,
               referencedByModule: mod2.code,
               referenceType: shared[0].type
             });
           }
         }
       }
     }
   }
   ```

3. **Cross-Application Permission Path Consistency:**
   ```javascript
   // Verify permission paths use correct context per application
   for (const app of applications) {
     const expectedPrefix = `${app.context}.${toKebabCase(app.code)}`;
     for (const mod of app.modules) {
       for (const perm of mod.permissions || []) {
         if (!perm.path.startsWith(expectedPrefix)) {
           WARNING(`Permission path mismatch in ${app.code}/${mod.code}: "${perm.path}" should start with "${expectedPrefix}"`);
         }
       }
     }
   }
   ```

4. **Cross-Application Role Name Consistency:**
   ```javascript
   // Check for role name conflicts across applications
   const allRoles = {};
   for (const app of applications) {
     for (const role of app.applicationRoles || []) {
       if (allRoles[role.role] && allRoles[role.role].level !== role.level) {
         WARNING(`Role "${role.role}" has different levels: ${allRoles[role.role].app}=${allRoles[role.role].level} vs ${app.code}=${role.level}`);
       }
       allRoles[role.role] = { app: app.code, level: role.level };
     }
   }
   ```

5. **Store Cross-Application Data:**
   ```javascript
   ba-writer.enrichSection({
     featureId: {project_id},
     section: "consolidation.crossApplicationInteractions",
     data: crossAppSharedEntities.map(shared => ({
       fromApplication: shared.definedInApp,
       fromModule: shared.definedInModule,
       toApplication: shared.referencedByApp,
       toModule: shared.referencedByModule,
       interactionType: shared.referenceType,
       entity: shared.entity,
       description: `${shared.entity} defined in ${shared.definedInApp}/${shared.definedInModule}, referenced by ${shared.referencedByApp}/${shared.referencedByModule}`
     }))
   });
   ```

6. **Display Cross-Application Interaction Map:**

   ```
   ═══════════════════════════════════════════════════════════════
     CROSS-APPLICATION INTERACTIONS
   ═══════════════════════════════════════════════════════════════

   | Source App | Source Module | Target App | Target Module | Entity | Type |
   |------------|-------------|------------|--------------|--------|------|
   | HR         | Employees   | SelfService | LeaveRequests | Employee | FK |

   Shared entities: {count}
   Permission paths: {valid_count}/{total_count} consistent
   Role conflicts: {conflict_count}
   ═══════════════════════════════════════════════════════════════
   ```

---

## SINGLE-MODULE MODE

When only 1 module:
1. Skip cross-module interactions (no other modules)
2. Display: "Module unique - interactions cross-module non applicables ✓"
3. Proceed directly to step-04c-decide.md (skip analysis)

---

## CONTEXT MANAGEMENT

| Loaded | Not Loaded |
|--------|------------|
| _shared.md | Full module feature.json files |
| Module summaries (100 lines each) | Questionnaires |
| Master feature.json | Templates |
| cadrage section | React viewer files |

---

## NEXT STEP

Load: `./step-04b-analyze.md`
