tractatus/docs/analysis/PHASE_2_ERROR_ANALYSIS.md
TheFlow 2298d36bed fix(submissions): restructure Economist package and fix article display
- Create Economist SubmissionTracking package correctly:
  * mainArticle = full blog post content
  * coverLetter = 216-word SIR— letter
  * Links to blog post via blogPostId
- Archive 'Letter to The Economist' from blog posts (it's the cover letter)
- Fix date display on article cards (use published_at)
- Target publication already displaying via blue badge

Database changes:
- Make blogPostId optional in SubmissionTracking model
- Economist package ID: 68fa85ae49d4900e7f2ecd83
- Le Monde package ID: 68fa2abd2e6acd5691932150

Next: Enhanced modal with tabs, validation, export

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 08:47:42 +13:00

15 KiB

Phase 2 Migration API Error - Root Cause Analysis

Date: 2025-10-11 Component: Migration API (POST /api/admin/rules/migrate-from-claude-md) Error: GovernanceRule validation failed: source: 'claude_md_migration' is not a valid enum value for path 'source'.


Error Timeline

1. Initial Implementation (No Error Detection)

File: src/controllers/rules.controller.js Code Written:

// Line ~420 in migrateFromClaudeMd function
const newRule = new GovernanceRule({
  id: ruleId,
  text: suggested.text,
  scope: suggested.scope,
  quadrant: suggested.quadrant,
  persistence: suggested.persistence,
  source: 'claude_md_migration',  // ❌ ERROR: This value not in model enum
  // ... other fields
});

await newRule.save();

Problem: Developer used string literal 'claude_md_migration' without checking model schema.


2. Model Schema Definition

File: src/models/GovernanceRule.model.js Existing Code (Line 229):

source: {
  type: String,
  enum: ['user_instruction', 'framework_default', 'automated', 'migration', 'test'],
  //     ❌ Missing 'claude_md_migration'
  default: 'framework_default',
  description: 'How this rule was created'
}

Problem: Enum didn't include the new value needed for CLAUDE.md migration.


3. First Test Execution

Test: POST /api/admin/rules/migrate-from-claude-md

Response:

{
  "error": "GovernanceRule validation failed: source: `claude_md_migration` is not a valid enum value for path `source`."
}

Impact: Migration API completely broken, cannot create rules from CLAUDE.md.


4. Fix Attempt #1 (Model Update)

Action: Updated model enum to include 'claude_md_migration'

enum: ['user_instruction', 'framework_default', 'automated', 'migration', 'claude_md_migration', 'test']

Result: Still failed with same error


5. Fix Attempt #2 (Server Restart)

Problem Discovered: Node.js server process (PID 2984413) running with cached model

Action: Killed server and restarted

kill 2984412 2984413
npm start

Result: SUCCESS - Migration API working


Root Cause Categories

A. Schema-Code Mismatch

What Happened:

  • Controller code used a string value ('claude_md_migration')
  • Model schema didn't recognize this value as valid
  • No compile-time or runtime validation prevented this

Why This Is Dangerous:

  • Silent failures in development
  • Runtime errors only discovered during testing
  • Database constraints violated
  • Potential data corruption if constraint checks fail

How It Slipped Through:

  1. No schema validation at write-time
  2. No constant/enum definitions shared between files
  3. No type checking (TypeScript or JSDoc)
  4. No integration tests before completion
  5. No automated enum synchronization checks

B. Magic Strings (String Literals)

What Happened:

  • Source values hardcoded as strings: 'claude_md_migration', 'framework_default', etc.
  • No central definition of allowed values
  • Easy to typo or use wrong value

Example of the Problem:

// Controller uses:
source: 'claude_md_migration'  // Easy to typo

// Model defines:
enum: ['user_instruction', 'framework_default', 'automated', 'migration', 'test']

// No compiler/linter catches the mismatch!

Why This Is Dangerous:

  • Typos not caught until runtime
  • No autocomplete or IntelliSense
  • Refactoring requires find/replace across entire codebase
  • Documentation lives only in model file

C. Development Environment Cache Issues

What Happened:

  • Model schema updated in source code
  • Server process still running with old cached model
  • Tests executed against stale schema

Why This Is Dangerous:

  • False negatives in testing (fix appears not to work)
  • Wasted debugging time
  • Confusion about whether fix is correct
  • Risk of deploying untested code

Common Causes:

  1. Node.js caches require() modules
  2. Mongoose caches model definitions
  3. No automatic server restart on model changes
  4. Developer unaware restart is needed

D. Insufficient Testing Before Deployment

What Happened:

  • Code written and marked "complete"
  • No integration test executed before declaring done
  • Error discovered only during final API testing phase

Testing Gaps:

  1. No unit test for migration function
  2. No integration test hitting real database
  3. No schema validation test
  4. No enum value synchronization test

E. Documentation Gaps

What Happened:

  • Model enum values not documented centrally
  • No developer guide for adding new source types
  • No checklist for schema changes

Missing Documentation:

  1. List of all valid source enum values
  2. Process for adding new source type
  3. Schema change checklist
  4. Server restart requirements

Impact Analysis

Immediate Impact

  • Migration API non-functional
  • Cannot migrate CLAUDE.md files to database
  • Phase 2 testing blocked
  • ~30 minutes debugging time

Potential Impact (If Not Caught)

  • Production deployment with broken migration
  • Users unable to use migration wizard
  • Data corruption if fallback logic triggered
  • Support tickets and user frustration

Prevented By

  • Comprehensive API testing before deployment
  • Database-level validation (Mongoose schema)
  • Error handling returning clear error message

Prevention Strategies

1. Code-Level Prevention

A. Use Constants Instead of Magic Strings

Current (Bad):

// Controller
source: 'claude_md_migration'

// Model
enum: ['user_instruction', 'framework_default', 'automated', 'migration', 'test']

Recommended (Good):

// src/constants/governance.constants.js
const GOVERNANCE_SOURCES = {
  USER_INSTRUCTION: 'user_instruction',
  FRAMEWORK_DEFAULT: 'framework_default',
  AUTOMATED: 'automated',
  MIGRATION: 'migration',
  CLAUDE_MD_MIGRATION: 'claude_md_migration',
  TEST: 'test'
};

const GOVERNANCE_SOURCE_VALUES = Object.values(GOVERNANCE_SOURCES);

module.exports = { GOVERNANCE_SOURCES, GOVERNANCE_SOURCE_VALUES };

// Model
const { GOVERNANCE_SOURCE_VALUES } = require('../constants/governance.constants');

source: {
  type: String,
  enum: GOVERNANCE_SOURCE_VALUES,  // ✅ Single source of truth
  default: GOVERNANCE_SOURCES.FRAMEWORK_DEFAULT
}

// Controller
const { GOVERNANCE_SOURCES } = require('../constants/governance.constants');

source: GOVERNANCE_SOURCES.CLAUDE_MD_MIGRATION  // ✅ Autocomplete, typo-safe

Benefits:

  • Single source of truth
  • Autocomplete in IDE
  • Typo-safe (undefined error if constant doesn't exist)
  • Easy refactoring
  • Self-documenting code

B. Add JSDoc Type Annotations

Recommended:

/**
 * @typedef {Object} GovernanceRuleData
 * @property {string} id - Rule ID (e.g., 'inst_001')
 * @property {string} text - Rule text
 * @property {'UNIVERSAL'|'PROJECT_SPECIFIC'} scope
 * @property {'STRATEGIC'|'OPERATIONAL'|'TACTICAL'|'SYSTEM'|'STORAGE'} quadrant
 * @property {'HIGH'|'MEDIUM'|'LOW'} persistence
 * @property {'user_instruction'|'framework_default'|'automated'|'migration'|'claude_md_migration'|'test'} source
 */

/**
 * Migrate rules from CLAUDE.md
 * @param {Object} req
 * @param {Object} req.body
 * @param {Array<Object>} req.body.selectedCandidates
 * @returns {Promise<Object>}
 */
async function migrateFromClaudeMd(req, res) {
  // IDE now knows 'source' must be one of the specific enum values
  const newRule = new GovernanceRule({
    source: 'claude_md_migration'  // ✅ IDE warns if value not in type
  });
}

Benefits:

  • IDE type checking
  • Autocomplete suggestions
  • Documentation in code
  • Easier debugging

C. Schema Validation Before Save

Recommended:

const { GOVERNANCE_SOURCES, GOVERNANCE_SOURCE_VALUES } = require('../constants/governance.constants');

async function migrateFromClaudeMd(req, res) {
  const { selectedCandidates } = req.body;

  // Validate source value BEFORE creating model instance
  const sourceValue = GOVERNANCE_SOURCES.CLAUDE_MD_MIGRATION;

  if (!GOVERNANCE_SOURCE_VALUES.includes(sourceValue)) {
    throw new Error(`Invalid source value: ${sourceValue}. Must be one of: ${GOVERNANCE_SOURCE_VALUES.join(', ')}`);
  }

  const newRule = new GovernanceRule({
    source: sourceValue,
    // ... other fields
  });

  await newRule.save();
}

Benefits:

  • Early error detection
  • Clear error messages
  • Prevents database round-trip on invalid data
  • Self-checking code

2. Testing-Level Prevention

A. Schema Validation Tests

Create: tests/unit/models/GovernanceRule.test.js

const GovernanceRule = require('../../../src/models/GovernanceRule.model');
const { GOVERNANCE_SOURCES } = require('../../../src/constants/governance.constants');

describe('GovernanceRule Model Schema', () => {
  it('should accept all valid source enum values', async () => {
    const validSources = [
      GOVERNANCE_SOURCES.USER_INSTRUCTION,
      GOVERNANCE_SOURCES.FRAMEWORK_DEFAULT,
      GOVERNANCE_SOURCES.AUTOMATED,
      GOVERNANCE_SOURCES.MIGRATION,
      GOVERNANCE_SOURCES.CLAUDE_MD_MIGRATION,
      GOVERNANCE_SOURCES.TEST
    ];

    for (const source of validSources) {
      const rule = new GovernanceRule({
        id: `test_${source}`,
        text: 'Test rule',
        quadrant: 'SYSTEM',
        persistence: 'HIGH',
        source: source
      });

      // Should not throw validation error
      await expect(rule.validate()).resolves.not.toThrow();
    }
  });

  it('should reject invalid source values', async () => {
    const rule = new GovernanceRule({
      id: 'test_invalid',
      text: 'Test rule',
      quadrant: 'SYSTEM',
      persistence: 'HIGH',
      source: 'invalid_source_value'  // ❌ Not in enum
    });

    await expect(rule.validate()).rejects.toThrow(/is not a valid enum value/);
  });
});

B. Integration Tests Before Completion

Create: tests/integration/migration.test.js

const request = require('supertest');
const app = require('../../../src/app');
const { getAuthToken } = require('../helpers/auth.helper');

describe('Migration API Integration Tests', () => {
  let authToken;

  beforeAll(async () => {
    authToken = await getAuthToken('admin@tractatus.local', 'admin123');
  });

  it('should create rules from CLAUDE.md candidates', async () => {
    const response = await request(app)
      .post('/api/admin/rules/migrate-from-claude-md')
      .set('Authorization', `Bearer ${authToken}`)
      .send({
        selectedCandidates: [{
          originalText: 'Test rule',
          quadrant: 'SYSTEM',
          persistence: 'HIGH',
          suggestedRule: {
            text: 'Test rule',
            scope: 'UNIVERSAL',
            quadrant: 'SYSTEM',
            persistence: 'HIGH',
            variables: []
          }
        }]
      });

    expect(response.status).toBe(200);
    expect(response.body.success).toBe(true);
    expect(response.body.results.created).toHaveLength(1);
    expect(response.body.results.failed).toHaveLength(0);
  });
});

When to Run:

  • Before marking task complete
  • In CI/CD pipeline
  • Before deployment

3. Development Environment Prevention

A. Use Nodemon for Auto-Restart

Update: package.json

{
  "scripts": {
    "start": "node src/server.js",
    "dev": "nodemon --watch src --watch .env src/server.js",
    "dev:models": "nodemon --watch src/models --watch src/controllers --watch src/services src/server.js"
  },
  "devDependencies": {
    "nodemon": "^3.0.0"
  }
}

Usage:

npm run dev:models  # Auto-restarts when models, controllers, or services change

B. Cache Clearing Script

Create: scripts/clear-cache.sh

#!/bin/bash
# Clear Node.js module cache and restart server

echo "Clearing Node.js module cache..."
rm -rf node_modules/.cache
killall node 2>/dev/null
sleep 1
echo "Starting fresh server..."
npm start

4. Documentation Prevention

A. Schema Change Checklist

Create: docs/developer/SCHEMA_CHANGE_CHECKLIST.md

# Schema Change Checklist

When adding/modifying schema fields:

## Before Making Changes
- [ ] Identify all enum values needed
- [ ] Check if constants file exists for this enum
- [ ] Review existing code using this field

## Making Changes
- [ ] Update constants file (if exists) or create one
- [ ] Update model schema to use constants
- [ ] Update all controllers using this field
- [ ] Add JSDoc type annotations
- [ ] Update API documentation

## Testing
- [ ] Write/update unit tests for model validation
- [ ] Write/update integration tests for API endpoints
- [ ] Test with invalid values (negative test)
- [ ] Restart server to clear cache
- [ ] Verify changes in database

## Documentation
- [ ] Update API documentation
- [ ] Update developer guide
- [ ] Add migration notes (if breaking change)

B. Enum Values Documentation

Create: docs/developer/ENUM_VALUES.md

# Governance Rule Enum Values

## source
**Description**: How the rule was created
**Location**: `src/models/GovernanceRule.model.js`
**Constants**: `src/constants/governance.constants.js`

| Value | Constant | Description |
|-------|----------|-------------|
| `user_instruction` | `GOVERNANCE_SOURCES.USER_INSTRUCTION` | Created by user through UI |
| `framework_default` | `GOVERNANCE_SOURCES.FRAMEWORK_DEFAULT` | Default framework rule |
| `automated` | `GOVERNANCE_SOURCES.AUTOMATED` | Auto-generated by system |
| `migration` | `GOVERNANCE_SOURCES.MIGRATION` | Migrated from legacy system |
| `claude_md_migration` | `GOVERNANCE_SOURCES.CLAUDE_MD_MIGRATION` | Migrated from CLAUDE.md file |
| `test` | `GOVERNANCE_SOURCES.TEST` | Test data |

**To Add New Value**:
1. Add to `GOVERNANCE_SOURCES` in `src/constants/governance.constants.js`
2. Update this documentation
3. Follow Schema Change Checklist

Lessons Learned

1. Always Validate Against Schema Before Using

Don't assume enum values - check the model first.

2. Constants Over Magic Strings

Centralize all enum values in constants files.

3. Test Before Declaring Complete

Run integration tests that hit real database.

4. Document Schema Contracts

Make enum values discoverable and documented.

5. Restart Server After Model Changes

Node.js caches require() modules - restart is essential.

6. Type Annotations Prevent Runtime Errors

JSDoc or TypeScript catches mismatches at dev-time.

7. Fail Fast with Validation

Validate data before database operations.


Conclusion

This error was preventable with:

  1. Constants file for enum values
  2. JSDoc type annotations
  3. Pre-save validation
  4. Integration tests before completion
  5. Schema change checklist
  6. Nodemon for auto-restart

Time Lost: ~30 minutes debugging Time to Prevent: ~15 minutes setting up constants + tests

Prevention is cheaper than debugging.


Next Steps: Implement prevention strategies as governance rules.