diff --git a/docs-ai/064-agent-completion-signals/004-s2-work-note.md b/docs-ai/064-agent-completion-signals/004-s2-work-note.md index 60a52413..9055edb4 100644 --- a/docs-ai/064-agent-completion-signals/004-s2-work-note.md +++ b/docs-ai/064-agent-completion-signals/004-s2-work-note.md @@ -117,6 +117,9 @@ recorded in the existing 064 plan/action documents. - 2026-08-23 — Review follow-up distinguished a real transition into working from metadata churn while already working. Animated title emissions no longer erase fresh exact terminal evidence or clear dispatch attention/incomplete replay. +- 2026-08-23 — Adversarial review round 2 covered the detector's presence-release edge: a + same-process `nil` → working re-detection now counts as activity and clears stale terminal + evidence, while working → working metadata churn remains inert. ## Fresh Debug E2E diff --git a/supacode/Features/Terminal/BusinessLogic/AgentObservationStore.swift b/supacode/Features/Terminal/BusinessLogic/AgentObservationStore.swift index d8a12d52..2b820740 100644 --- a/supacode/Features/Terminal/BusinessLogic/AgentObservationStore.swift +++ b/supacode/Features/Terminal/BusinessLogic/AgentObservationStore.swift @@ -100,8 +100,8 @@ final class AgentObservationStore { var record = records[entry.surfaceID] ?? SurfaceRecord() guard record.agent != entry else { return false } let beganWorking = - record.agent.map { $0.displayState != .working && entry.displayState == .working } - ?? false + record.agent?.displayState != .working + && entry.displayState == .working record.agent = entry if beganWorking { record.activeTerminalSignal = nil diff --git a/supacodeTests/AgentEvidenceEpochTests.swift b/supacodeTests/AgentEvidenceEpochTests.swift index 11bd351a..13c61e48 100644 --- a/supacodeTests/AgentEvidenceEpochTests.swift +++ b/supacodeTests/AgentEvidenceEpochTests.swift @@ -91,6 +91,24 @@ struct AgentEvidenceEpochTests { #expect(store.currentSignalEvidence(surfaceID: surfaceID).activeTerminal == terminal) } + @Test func workingRedetectionAfterRemovalInvalidatesTerminalEvidence() { + let store = AgentObservationStore(bufferCapacity: 8) + let surfaceID = UUID() + let generation = AgentProcessGeneration(pid: 42, startedAt: Date(timeIntervalSince1970: 1)) + store.updateEvidenceEpoch(surfaceID: surfaceID, processGeneration: generation, sessionID: nil) + store.publishAgentChanged(agentEntry(surfaceID: surfaceID, title: "Idle", displayState: .idle)) + let terminal = signal(.turnEnded, source: .cooperativeCLI) + store.publishSignal(terminal, binding: .current, surfaceID: surfaceID) + store.publishAgentRemoved(surfaceID: surfaceID) + + let beganWorking = store.publishAgentChanged( + agentEntry(surfaceID: surfaceID, title: "Working", displayState: .working) + ) + + #expect(beganWorking) + #expect(store.currentSignalEvidence(surfaceID: surfaceID).activeTerminal == nil) + } + @Test func unboundSignalIsDiagnosticAndNeverCreatesObservedCoverage() { let store = AgentObservationStore(bufferCapacity: 8) let surfaceID = UUID() @@ -121,7 +139,11 @@ struct AgentEvidenceEpochTests { ]) } - private func agentEntry(surfaceID: UUID, title: String) -> ActiveAgentEntry { + private func agentEntry( + surfaceID: UUID, + title: String, + displayState: AgentDisplayState = .working + ) -> ActiveAgentEntry { ActiveAgentEntry( id: surfaceID, worktreeID: "wt", @@ -133,8 +155,8 @@ struct AgentEvidenceEpochTests { paneIndex: 0, iconLookupToken: "pi", agent: .pi, - rawState: .working, - displayState: .working, + rawState: displayState == .working ? .working : .idle, + displayState: displayState, lastChangedAt: Date(timeIntervalSince1970: 10) ) }