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

579 lines
15 KiB
Markdown

# 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**:
```javascript
// 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):
```javascript
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**:
```json
{
"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'`
```javascript
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
```bash
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**:
```javascript
// 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)**:
```javascript
// Controller
source: 'claude_md_migration'
// Model
enum: ['user_instruction', 'framework_default', 'automated', 'migration', 'test']
```
**Recommended (Good)**:
```javascript
// 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**:
```javascript
/**
* @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**:
```javascript
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`
```javascript
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`
```javascript
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`
```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**:
```bash
npm run dev:models # Auto-restarts when models, controllers, or services change
```
---
#### B. Cache Clearing Script
**Create**: `scripts/clear-cache.sh`
```bash
#!/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`
```markdown
# 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`
```markdown
# 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.