feat(reconciliation): implement deterministic state reconciliation engine and post-commit hook

- Implement ReconciliationEngine in server/src/handlers/reconciliation.rs
- Auto-transition ADRs to implemented, resolve tech debts, and cascade task unblocking
- Ingest git commit events via POST /api/git/commit and scripts/git-reconcile.py post-commit hook
- Add universal pagination, search filtering, and keyboard navigation to dashboard
- Implement non-destructive task TTL expiry sweeper and gate verification
- Implements: ADR-0102, ADR-0103
This commit is contained in:
Riz Ashraf committed 2026-10-07 19:00:44 +01:00
1 parent ec977c24dd
commit 8f924b793a
16 files changed
+2622 -163

No files matched your search

+28 -1
View File
@@ -102,10 +102,37 @@ pub async fn ttl_sweeper_worker(state: Arc<MemoryState>) {
.unwrap_or_default()
.as_secs();
let mut expired_tasks = Vec::new();
state.project.tasks.modify(|tasks| {
tasks.retain(|t| t.expires_at.is_none_or(|exp| exp > now));
for t in tasks.iter_mut() {
if let Some(exp) = t.expires_at {
if exp <= now && t.is_active() {
t.status = "expired".to_string();
t.updated_at = now;
expired_tasks.push(t.id.clone());
}
}
}
});
for tid in expired_tasks {
state.record_activity(
"task_expired",
&format!("Task {} expired due to TTL", tid),
Some("expired"),
);
state.broadcast_task_event(crate::models::TaskEvent {
task_id: tid,
status: "expired".to_string(),
action: Some("ttl_expire".to_string()),
result: Some(serde_json::json!({ "status": "expired" })),
error: None,
timestamp: now,
session_id: None,
..Default::default()
});
}
state.telemetry.handoff_memos.modify(|memos| {
memos.retain(|m| m.expires_at.is_none_or(|exp| exp > now));
});