- 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>
579 lines
15 KiB
Markdown
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.
|