Tier 1.3 Phase 3: Add evidence management API
Implements data layer functions for managing and querying spec/evidence data. New export functions: - insertMilestoneEvidence(): Append evidence for milestone - insertSliceEvidence(): Append evidence for slice - insertTaskEvidence(): Append evidence for task - getMilestoneAuditTrail(): Query full audit trail (spec + evidence + runtime) - getSliceAuditTrail(): Query slice audit trail with joined spec/evidence - getTaskAuditTrail(): Query task audit trail with joined spec/evidence - getMilestoneSpec(): Get spec only (immutable intent) - getSliceSpec(): Get slice spec only - getTaskSpec(): Get task spec only Key properties: - Evidence functions use timestamp for recording time (set at insertion) - Audit trail queries JOIN runtime, spec, and evidence tables - All queries support data archaeology (reconstruct decision history) - Spec-only queries useful for validation and re-planning - All functions include JSDoc with purpose and consumer This completes Phase 3 of Tier 1.3 implementation. Phase 4 (tool updates) and Phase 5 (integration tests) follow in next PRs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
f3761d7f46
commit
076e8c4894
1 changed files with 143 additions and 0 deletions
|
|
@ -5605,3 +5605,146 @@ export function deleteMemoryEmbedding(memoryId) {
|
|||
.run({ ":id": memoryId });
|
||||
return (res?.changes ?? 0) > 0;
|
||||
}
|
||||
// ─── Tier 1.3: Spec/Runtime/Evidence Schema ──────────────────────────────────
|
||||
// Functions for managing evidence in the new spec schema (v32+)
|
||||
|
||||
/**
|
||||
* Record evidence for a milestone. Appends to milestone_evidence table.
|
||||
* Purpose: Create audit trail of decisions, verifications, and incidents.
|
||||
* Consumer: complete-milestone, reassess-milestone, and other tools.
|
||||
*/
|
||||
export function insertMilestoneEvidence(milestoneId, evidenceType, content, phaseName, recordedBy) {
|
||||
if (!currentDb) throw new SFError(SF_STALE_STATE, "sf-db: No database open");
|
||||
currentDb
|
||||
.prepare(`INSERT INTO milestone_evidence (milestone_id, evidence_type, content, recorded_at, phase_name, recorded_by)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`)
|
||||
.run(milestoneId, evidenceType, content, new Date().toISOString(), phaseName || "", recordedBy || "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Record evidence for a slice. Appends to slice_evidence table.
|
||||
* Purpose: Create audit trail of slice decisions, verifications, and incidents.
|
||||
* Consumer: complete-slice, execute-slice, and other tools.
|
||||
*/
|
||||
export function insertSliceEvidence(milestoneId, sliceId, evidenceType, content, phaseName, recordedBy) {
|
||||
if (!currentDb) throw new SFError(SF_STALE_STATE, "sf-db: No database open");
|
||||
currentDb
|
||||
.prepare(`INSERT INTO slice_evidence (milestone_id, slice_id, evidence_type, content, recorded_at, phase_name, recorded_by)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)`)
|
||||
.run(milestoneId, sliceId, evidenceType, content, new Date().toISOString(), phaseName || "", recordedBy || "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Record evidence for a task. Appends to task_evidence table.
|
||||
* Purpose: Create audit trail of task decisions, verifications, and incidents.
|
||||
* Consumer: complete-task, execute-task, and other tools.
|
||||
*/
|
||||
export function insertTaskEvidence(milestoneId, sliceId, taskId, evidenceType, content, phaseName, recordedBy) {
|
||||
if (!currentDb) throw new SFError(SF_STALE_STATE, "sf-db: No database open");
|
||||
currentDb
|
||||
.prepare(`INSERT INTO task_evidence (milestone_id, slice_id, task_id, evidence_type, content, recorded_at, phase_name, recorded_by)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
|
||||
.run(milestoneId, sliceId, taskId, evidenceType, content, new Date().toISOString(), phaseName || "", recordedBy || "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Query milestone audit trail (spec + evidence). Returns rows with spec intent and evidence history.
|
||||
* Purpose: Support data archaeology and decision-tree reconstruction.
|
||||
* Consumer: forensics tools, doctor checks, audit/compliance queries.
|
||||
*/
|
||||
export function getMilestoneAuditTrail(milestoneId) {
|
||||
if (!currentDb) return [];
|
||||
return currentDb
|
||||
.prepare(`
|
||||
SELECT
|
||||
r.id, r.title, r.status,
|
||||
s.vision, s.spec_version,
|
||||
e.evidence_type, e.content, e.recorded_at, e.phase_name, e.recorded_by
|
||||
FROM milestones r
|
||||
LEFT JOIN milestone_specs s ON r.id = s.id
|
||||
LEFT JOIN milestone_evidence e ON r.id = e.milestone_id
|
||||
WHERE r.id = ?
|
||||
ORDER BY e.recorded_at ASC
|
||||
`)
|
||||
.all(milestoneId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query slice audit trail (spec + evidence).
|
||||
* Purpose: Support data archaeology and decision-tree reconstruction.
|
||||
* Consumer: forensics tools, doctor checks, audit/compliance queries.
|
||||
*/
|
||||
export function getSliceAuditTrail(milestoneId, sliceId) {
|
||||
if (!currentDb) return [];
|
||||
return currentDb
|
||||
.prepare(`
|
||||
SELECT
|
||||
r.id, r.title, r.status,
|
||||
s.goal, s.spec_version,
|
||||
e.evidence_type, e.content, e.recorded_at, e.phase_name, e.recorded_by
|
||||
FROM slices r
|
||||
LEFT JOIN slice_specs s ON r.milestone_id = s.milestone_id AND r.id = s.slice_id
|
||||
LEFT JOIN slice_evidence e ON r.milestone_id = e.milestone_id AND r.id = e.slice_id
|
||||
WHERE r.milestone_id = ? AND r.id = ?
|
||||
ORDER BY e.recorded_at ASC
|
||||
`)
|
||||
.all(milestoneId, sliceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query task audit trail (spec + evidence).
|
||||
* Purpose: Support data archaeology and decision-tree reconstruction.
|
||||
* Consumer: forensics tools, doctor checks, audit/compliance queries.
|
||||
*/
|
||||
export function getTaskAuditTrail(milestoneId, sliceId, taskId) {
|
||||
if (!currentDb) return [];
|
||||
return currentDb
|
||||
.prepare(`
|
||||
SELECT
|
||||
r.id, r.title, r.status,
|
||||
s.verify, s.spec_version,
|
||||
e.evidence_type, e.content, e.recorded_at, e.phase_name, e.recorded_by
|
||||
FROM tasks r
|
||||
LEFT JOIN task_specs s ON r.milestone_id = s.milestone_id AND r.slice_id = s.slice_id AND r.id = s.task_id
|
||||
LEFT JOIN task_evidence e ON r.milestone_id = e.milestone_id AND r.slice_id = e.slice_id AND r.id = e.task_id
|
||||
WHERE r.milestone_id = ? AND r.slice_id = ? AND r.id = ?
|
||||
ORDER BY e.recorded_at ASC
|
||||
`)
|
||||
.all(milestoneId, sliceId, taskId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get milestone spec only (immutable intent, no runtime state).
|
||||
* Purpose: Retrieve spec intent for re-planning or spec validation.
|
||||
* Consumer: plan-milestone and spec validation tools.
|
||||
*/
|
||||
export function getMilestoneSpec(milestoneId) {
|
||||
if (!currentDb) return null;
|
||||
return currentDb
|
||||
.prepare("SELECT * FROM milestone_specs WHERE id = ?")
|
||||
.get(milestoneId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get slice spec only (immutable intent, no runtime state).
|
||||
* Purpose: Retrieve spec intent for re-planning or spec validation.
|
||||
* Consumer: plan-slice and spec validation tools.
|
||||
*/
|
||||
export function getSliceSpec(milestoneId, sliceId) {
|
||||
if (!currentDb) return null;
|
||||
return currentDb
|
||||
.prepare("SELECT * FROM slice_specs WHERE milestone_id = ? AND slice_id = ?")
|
||||
.get(milestoneId, sliceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task spec only (immutable intent, no runtime state).
|
||||
* Purpose: Retrieve spec intent for re-planning or spec validation.
|
||||
* Consumer: plan-task and spec validation tools.
|
||||
*/
|
||||
export function getTaskSpec(milestoneId, sliceId, taskId) {
|
||||
if (!currentDb) return null;
|
||||
return currentDb
|
||||
.prepare("SELECT * FROM task_specs WHERE milestone_id = ? AND slice_id = ? AND task_id = ?")
|
||||
.get(milestoneId, sliceId, taskId);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue