feat(FN-1567): add mission contract assertions model types and APIs

- Add mission contract assertions model with types: ContractAssertion, ContractAssertionStatus, ContractAssertionType
- Add assertions table to schema v29 with migration from v28
- Implement assertion APIs: create, update, link/unlink to features, list, getWithContext
- Add rollup computation (assertion counts, pass rates) for missions and milestones
- Add many-to-many feature-to-assertion linking via featureAssertions table
- Add project memory documentation for assertion lifecycle and patterns
- Update schema version assertions in db.test.ts, run-audit.test.ts, and task-documents.test.ts
This commit is contained in:
gsxdsm
2026-04-10 21:29:54 -07:00
parent c812bdbc2a
commit e258279a5f
9 changed files with 1312 additions and 27 deletions

View File

@@ -59,7 +59,7 @@ export function fromJson<T>(json: string | null | undefined): T | undefined {
// ── Schema Definition ────────────────────────────────────────────────
const SCHEMA_VERSION = 28;
const SCHEMA_VERSION = 29;
function normalizeTaskComments(
steeringComments: SteeringComment[] | undefined,
@@ -1031,6 +1031,55 @@ export class Database {
}
});
}
// Mission contract assertions (FN-1567)
// Adds explicit validation contract model for milestone behavioral assertions
// with feature linkage tracking and validation state rollup.
if (version < 29) {
this.applyMigration(29, () => {
// Add validationState column to milestones table
this.addColumnIfMissing("milestones", "validationState", "TEXT NOT NULL DEFAULT 'not_started'");
// Create mission_contract_assertions table for milestone validation contracts
this.db.exec(`
CREATE TABLE IF NOT EXISTS mission_contract_assertions (
id TEXT PRIMARY KEY,
milestoneId TEXT NOT NULL,
title TEXT NOT NULL,
assertion TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
orderIndex INTEGER NOT NULL DEFAULT 0,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL,
FOREIGN KEY (milestoneId) REFERENCES milestones(id) ON DELETE CASCADE
)
`);
// Create mission_feature_assertions link table for many-to-many relationships
this.db.exec(`
CREATE TABLE IF NOT EXISTS mission_feature_assertions (
featureId TEXT NOT NULL,
assertionId TEXT NOT NULL,
createdAt TEXT NOT NULL,
PRIMARY KEY (featureId, assertionId),
FOREIGN KEY (featureId) REFERENCES mission_features(id) ON DELETE CASCADE,
FOREIGN KEY (assertionId) REFERENCES mission_contract_assertions(id) ON DELETE CASCADE
)
`);
// Index for deterministic ordering when listing assertions for a milestone
// Covers: WHERE milestoneId = ? ORDER BY orderIndex ASC, createdAt ASC, id ASC
this.db.exec(`CREATE INDEX IF NOT EXISTS idxContractAssertionsMilestoneOrder ON mission_contract_assertions(milestoneId, orderIndex, createdAt, id)`);
// Index for finding all assertions linked to a feature
// Covers: WHERE featureId = ? (from mission_feature_assertions)
this.db.exec(`CREATE INDEX IF NOT EXISTS idxFeatureAssertionsFeatureId ON mission_feature_assertions(featureId)`);
// Index for finding all features linked to an assertion
// Covers: WHERE assertionId = ? (from mission_feature_assertions)
this.db.exec(`CREATE INDEX IF NOT EXISTS idxFeatureAssertionsAssertionId ON mission_feature_assertions(assertionId)`);
});
}
}
/**