# Module Specification Checklist

> **Authoritative minimums:** See `_shared.md` for the single source of truth on all minimum thresholds.
> **CRITICAL:** This checklist MUST be FULLY COMPLETED before marking module status = "specified".
> **Rationale:** Prevents incomplete modules from reaching handoff, reduces business-analyse-develop failures.

## Checklist Structure

**Execute BEFORE updating module status to "specified":**

```javascript
const checklist = {
  // SECTION 1: DATA MODEL (BLOCKING)
  entities: {
    minimum: 2,
    actual: analysis.entities.length,
    status: actual >= minimum ? "PASS" : "FAIL",
    blocking: true,
    details: "At least 2 entities required for a meaningful module"
  },

  entityAttributes: {
    check: "All entities have ≥3 attributes",
    status: validateAllEntities(e => e.attributes.length >= 3) ? "PASS" : "FAIL",
    blocking: true,
    details: "Entities with <3 attributes are likely incomplete"
  },

  entityRelationships: {
    check: "All entities have relationships defined (or explicitly marked as standalone)",
    status: validateEntityRelationships() ? "PASS" : "FAIL",
    blocking: false,  // WARNING only
    details: "Standalone entities should be rare in business modules"
  },

  entitySchemaCompliance: {
    check: "All entities use canonical format: attributes[] (not fields[]), relationships[] array present",
    status: analysis.entities.every(e =>
      Array.isArray(e.attributes) && e.attributes.length > 0 &&
      !e.fields && !e.tableName && !e.primaryKey
    ) ? "PASS" : "FAIL",
    blocking: true,
    details: "Entities with 'fields' instead of 'attributes' break HTML data model display and PRD extraction. " +
             "Check: every entity must have attributes[] (not fields[]), every entity must have relationships[], " +
             "no entity should have tableName or primaryKey (technical fields belong in implementation, not analysis)"
  },

  // SECTION 2: BUSINESS RULES (BLOCKING)
  businessRules: {
    minimum: 4,
    actual: analysis.businessRules.length,
    status: actual >= minimum ? "PASS" : "FAIL",
    blocking: true,
    details: "Minimum 4 BRs (mix of VAL/CALC/WF/SEC/DATA)"
  },

  businessRuleCategories: {
    check: "BRs cover ≥2 categories (VAL, CALC, WF, SEC, DATA)",
    status: countUniqueCategories(analysis.businessRules) >= 2 ? "PASS" : "FAIL",
    blocking: true,
    details: "Single-category modules are likely incomplete"
  },

  businessRulePrefixes: {
    check: "All BR IDs use module prefix (e.g., BR-VAL-{PREFIX}-NNN)",
    status: validateBRPrefixes() ? "PASS" : "FAIL",
    blocking: true,
    details: "Missing prefixes cause ID collisions in multi-module apps"
  },

  // SECTION 3: USE CASES & REQUIREMENTS (BLOCKING)
  useCases: {
    minimum: 6,
    actual: specification.useCases.length,
    status: actual >= minimum ? "PASS" : "FAIL",
    blocking: true,
    details: "Minimum 6 UCs (CRUD + 2 business-specific)"
  },

  useCasePrefixes: {
    check: "All UC IDs use module prefix (UC-{PREFIX}-NNN)",
    status: validateUCPrefixes() ? "PASS" : "FAIL",
    blocking: true,
    details: "Missing prefixes cause ID collisions"
  },

  functionalRequirements: {
    minimum: 4,
    actual: specification.functionalRequirements.length,
    status: actual >= minimum ? "PASS" : "FAIL",
    blocking: true,
    details: "FRs must cover key functionality"
  },

  ucFrLinkage: {
    check: "Every UC has ≥1 linked FR",
    status: validateUCtoFRLinkage() ? "PASS" : "FAIL",
    blocking: true,
    details: "Orphan UCs indicate incomplete requirements"
  },

  frBrLinkage: {
    check: "Every FR has ≥1 linked BR",
    status: validateFRtoBRLinkage() ? "PASS" : "FAIL",
    blocking: false,  // WARNING only
    details: "FRs without BRs may lack validation/calculation logic"
  },

  // SECTION 4: PERMISSIONS (BLOCKING)
  permissions: {
    minimum: 5,
    actual: specification.permissionMatrix.permissions.length,
    status: actual >= minimum ? "PASS" : "FAIL",
    blocking: true,
    details: "Minimum 5 permissions (CRUD + 1 business action)"
  },

  permissionFormat: {
    check: "All permissions use correct format: module-level {app}.{module}.{action} or section-level {app}.{module}.{section}.{action}",
    status: validatePermissionFormat() ? "PASS" : "FAIL",
    blocking: true,
    details: "Wrong format breaks RBAC system. Module-level = 3 segments, section-level = 4 segments."
  },

  rolePermissions: {
    check: "All application roles have ≥1 permission assigned",
    status: validateRoleAssignments() ? "PASS" : "FAIL",
    blocking: true,
    details: "Roles without permissions are useless"
  },

  // SECTION 5: UI & NAVIGATION (BLOCKING)
  sections: {
    minimum: 2,
    actual: specification.sections.length,
    status: actual >= minimum ? "PASS" : "FAIL",
    blocking: true,
    details: "Modules need ≥2 sections (list + form minimum)"
  },

  wireframes: {
    minimum: specification.sections.length,  // 1 wireframe PER section
    actual: (specification.uiWireframes || specification.wireframes || []).length,  // Check BOTH key names
    status: actual >= minimum ? "PASS" : "FAIL",
    blocking: true,
    details: "EVERY section MUST have a wireframe (ASCII/SVG). Check both uiWireframes and wireframes keys."
  },

  wireframeSchemaCompliance: {
    check: "All wireframes have required fields: screen, section, mockupFormat, elements[], layout (object), permissionsRequired[]",
    status: (specification.uiWireframes || specification.wireframes || []).every(wf =>
      (wf.screen || wf.title) && wf.section && (wf.mockupFormat || wf.mockup || wf.ascii) &&
      Array.isArray(wf.elements) && wf.elements.length > 0 &&
      typeof wf.layout === 'object' && wf.layout !== null &&
      Array.isArray(wf.permissionsRequired)
    ) ? "PASS" : "FAIL",
    blocking: true,
    details: "Wireframes missing metadata render as empty frames. " +
             "Check: screen (not name/id/title), section, mockupFormat, elements[] non-empty, " +
             "layout as object (not string). " +
             "Also see references/acceptance-criteria.md for bash-verifiable checks."
  },

  navigation: {
    check: "Module has ≥1 navigation entry",
    status: specification.navigation.entries.length >= 1 ? "PASS" : "FAIL",
    blocking: true,
    details: "Module must be accessible in menu"
  },

  navigationRoutePatterns: {
    check: "Navigation routes do NOT contain CRUD suffixes (/list, /detail/:id)",
    status: (() => {
      const allRoutes = [
        ...(specification.seedDataCore?.navigationSections || []).map(s => s.route),
        ...(specification.sections || []).map(s => s.route),
        ...(specification.navigation?.entries || []).map(e => e.route)
      ].filter(Boolean);
      const forbidden = allRoutes.filter(r => r.match(/\/list$/) || r.match(/\/detail\/:id$/));
      return forbidden.length === 0 ? "PASS" : "FAIL";
    })(),
    blocking: true,
    details: "FORBIDDEN: /module/list (use /module), /module/detail/:id (use /module/:id). " +
             "list route = module route (React Router index route). " +
             "detail route = module route + /:id (React Router :id child). " +
             "Other sections (dashboard, approve) add /{section} normally."
  },

  // SECTION 6: I18N & MESSAGES (BLOCKING)
  i18nKeys: {
    minimum: 42,  // Realistic minimum for a module
    actual: Object.keys(specification.i18nKeys || {}).length,
    status: actual >= minimum ? "PASS" : "FAIL",
    blocking: true,
    details: "Keys needed: entities (×2), fields, messages, validation, navigation"
  },

  i18nLanguages: {
    check: "All i18n keys have 4 languages (fr, en, it, de)",
    status: validateI18nCompleteness() ? "PASS" : "FAIL",
    blocking: true,
    details: "Missing translations break multi-language support"
  },

  messages: {
    minimum: 4,
    actual: specification.messages.length,
    status: actual >= minimum ? "PASS" : "FAIL",
    blocking: true,
    details: "Minimum: 1 success, 1 error, 1 warning, 1 info"
  },

  // SECTION 7: SEED DATA (BLOCKING)
  seedDataCore: {
    check: "All 9 CORE seed data sections present",
    requiredSections: [
      "navigationApplications",
      "applicationRoles",
      "navigationModules",
      "navigationSections",
      "navigationResources",
      "navigationTranslations",
      "permissions",
      "rolePermissions",
      "permissionConstants"
    ],
    status: validateSeedDataCore() ? "PASS" : "FAIL",
    blocking: true,
    details: "Missing CORE seed data breaks module deployment"
  },

  seedDataBusiness: {
    check: "Business seed data template defined (if applicable — generated during implementation, not BA)",
    status: specification.seedDataBusiness !== undefined ? "PASS" : "WARN",
    blocking: false,  // WARNING only — business seed data is generated during business-analyse-develop implementation, not during BA phase
    details: "Business seed data (reference types, categories, statuses) is generated during implementation (business-analyse-develop). " +
             "BA phase ensures seedDataCore is complete (navigation, permissions). seedDataBusiness is optional at this stage."
  },

  // SECTION 8: API ENDPOINTS (BLOCKING)
  apiEndpoints: {
    minimum: 5,
    actual: specification.apiEndpoints.length,
    status: actual >= minimum ? "PASS" : "FAIL",
    blocking: true,
    details: "Minimum 5 endpoints (CRUD + 1 business action)"
  },

  apiPermissions: {
    check: "Every API endpoint has permission defined",
    status: validateAPIPermissions() ? "PASS" : "FAIL",
    blocking: true,
    details: "Endpoints without permissions are security holes"
  },

  // SECTION 9: VALIDATIONS (BLOCKING)
  validations: {
    minimum: 1,
    actual: specification.validations.length,
    status: actual >= minimum ? "PASS" : "FAIL",
    blocking: true,
    details: "Modules need field validation rules"
  },

  // SECTION 10: GHERKIN SCENARIOS (BLOCKING for format, WARNING for count)
  gherkinFormat: {
    check: "gherkinScenarios is ARRAY (not single object)",
    status: Array.isArray(specification.gherkinScenarios) ? "PASS" : "FAIL",
    blocking: true,
    details: "gherkinScenarios MUST be an array [{feature, scenarios}], not a single object. " +
             "Auto-fix: wrap in array [gherkinScenarios]. See step-03 ABSOLUTE FORMAT CHECKS."
  },

  gherkinContent: {
    check: "Each gherkin entry has feature (string) + scenarios (array)",
    status: (Array.isArray(specification.gherkinScenarios) &&
      specification.gherkinScenarios.every(g => g.feature && Array.isArray(g.scenarios))
    ) ? "PASS" : "FAIL",
    blocking: true,
    details: "Each gherkin entry must have {feature: string, scenarios: [{name, tags, given[], when[], then[]}]}"
  },

  gherkinScenarioCount: {
    minimum: 2,
    actual: (Array.isArray(specification.gherkinScenarios)
      ? specification.gherkinScenarios.reduce((sum, g) => sum + (g.scenarios || []).length, 0)
      : 0),
    status: actual >= minimum ? "PASS" : "FAIL",
    blocking: false,  // WARNING only — count is advisory
    details: "Gherkin scenarios enable automated testing"
  }
};
```

## Blocking Check Logic

```javascript
// BLOCKING CHECK: Count failures in BLOCKING items
const blockingFailures = Object.entries(checklist)
  .filter(([key, check]) => check.blocking === true && check.status === "FAIL")
  .map(([key, check]) => ({ section: key, ...check }));

IF blockingFailures.length > 0:
  **BLOCKING ERROR:** Module specification incomplete

  Display table:
  | Section | Required | Actual | Status | Details |
  |---------|----------|--------|--------|---------|
  {for each blocking failure}

  ACTIONS REQUIRED:
  1. Fix ALL blocking failures listed above
  2. Re-run step-03 validation
  3. DO NOT mark module as "specified" until ALL blocking checks pass

  STOP - DO NOT PROCEED TO UPDATE STATUS
ELSE:
  All blocking checks passed ✓
  {count} warnings (non-blocking)
  Proceed to mark module as "specified"
```

## Display Format

```
═══════════════════════════════════════════════════════════════
  MODULE SPECIFICATION CHECKLIST - {currentModule}
═══════════════════════════════════════════════════════════════

| Category | Checks | Passed | Failed | Warnings |
|----------|--------|--------|--------|----------|
| Data Model | 4 | {n} | {n} | {n} |
| Business Rules | 3 | {n} | {n} | {n} |
| Use Cases & FRs | 4 | {n} | {n} | {n} |
| Permissions | 3 | {n} | {n} | {n} |
| UI & Navigation | 4 | {n} | {n} | {n} |
| I18N & Messages | 3 | {n} | {n} | {n} |
| Seed Data | 2 | {n} | {n} | {n} |
| API Endpoints | 2 | {n} | {n} | {n} |
| Validations | 1 | {n} | {n} | {n} |
| Gherkin | 3 | {n} | {n} | {n} |

TOTAL: {total_checks} checks | {passed} ✓ | {failed} ✗ | {warnings} ⚠

STATUS: {failed === 0 ? "READY FOR SPECIFIED" : "INCOMPLETE - FIX REQUIRED"}
═══════════════════════════════════════════════════════════════
```

**IF ALL BLOCKING CHECKS PASS → Proceed to update module status to "specified"**
