fix(nomos): generation-relative plan seq + real activity timestamps
The plan recorded false history after a re-plan and the activity panel showed fabricated, churning timestamps. Two bugs compounding on one event stream. Plan drift (P0.1): - proposePlan seq is now 1..N per generation; (session,generation,seq) is the addressing key. The model's 1-based update_plan_step calls always map to the CURRENT plan after a re-plan, instead of resurrecting a superseded `replaced` row as done while the live work went unrecorded. - updatePlanStep resolves against MAX(generation); a stale/out-of-range seq returns errPlanStepNotFound (never touches a superseded generation). - getPlanSteps returns only the current generation by default; ?all=true keeps the audit/eval view (plan_generations assertion). - completeTask auto-close scopes to the current gen, stamps started_at, and emits one plan.step.finished per closed step so the panel converges instead of freezing on "running" after completion (P1.1). - propose_plan result enumerates step seqs; writeback detector matches "write back"/"writeback"/"upsert_knowledge" so a natural-language final step isn't doubled (P1.2). - migration 029 renumbers existing seq per generation + unique index. Activity panel (P0.2 / P1.1, web): - computeActivityLog uses the real message created_at for tool calls; live entries fall back to wall-clock frozen on first sight, killing the 3s poll churn. Steps use real started_at. - dropped plan-step events warn + count instead of a silent no-op. Tests: TestProposePlan updated; + generation-relative-seq and auto-close event-emission regression tests; + web activity purity/timestamp tests. VERSION: 0.14.0 -> 0.14.1
This commit is contained in:
@@ -93,12 +93,30 @@ function scheduleSessionsRefresh() {
|
||||
refreshTimer = setTimeout(() => loadSessions(), 300)
|
||||
}
|
||||
|
||||
// A dropped plan-step event is one that matched no step on screen —
|
||||
// historically a silent return, which made a disagreeing backend look like a
|
||||
// dead UI (the panel froze showing pending steps while work happened
|
||||
// elsewhere). Surface it so the next divergence is visible. Exported so a
|
||||
// debug surface (or a test) can read the count.
|
||||
export let droppedPlanStepEvents = 0
|
||||
export function resetDroppedPlanStepEvents(): void {
|
||||
droppedPlanStepEvents = 0
|
||||
}
|
||||
|
||||
function applyPlanStepEventTo(ws: WorkspaceState, data: PlanStepEventData) {
|
||||
const stepID = data?.step_id
|
||||
const seq = data?.seq
|
||||
ws.planSteps.update((steps) => {
|
||||
const i = steps.findIndex((s) => (stepID && s.id === stepID) || (seq != null && s.seq === seq))
|
||||
if (i === -1) return steps
|
||||
if (i === -1) {
|
||||
droppedPlanStepEvents++
|
||||
console.warn('plan step event matched no step on screen', {
|
||||
stepID,
|
||||
seq,
|
||||
status: data?.status
|
||||
})
|
||||
return steps
|
||||
}
|
||||
const next = [...steps]
|
||||
next[i] = {
|
||||
...next[i],
|
||||
|
||||
Reference in New Issue
Block a user