@@ -172,6 +172,157 @@ final class AgentTaskRegistry {
172172 self . flushTail = flushTail
173173 }
174174
175+ #if DEBUG
176+ /// Seeds stable, human-readable rows for the opt-in marketing screenshot.
177+ /// The fixture is presentation-only: it is never persisted or indexed as
178+ /// live terminal ownership, and production builds contain no entry point.
179+ func seedMarketingInboxForUITesting( at referenceDate: Date = Date ( ) ) {
180+ guard tasks. isEmpty else { return }
181+ tasks = [
182+ marketingTask ( . init(
183+ seed: 1 ,
184+ projectName: " Pine Demo " ,
185+ worktreeName: nil ,
186+ title: " Approve the release announcement " ,
187+ agentType: . codex,
188+ state: . waitingInput,
189+ liveness: . live,
190+ lifecycle: . active,
191+ attention: . waitingInput,
192+ isUnread: true ,
193+ startedSecondsAgo: 7 * 60 ,
194+ verifiedSecondsAgo: 18
195+ ) , referenceDate: referenceDate) ,
196+ marketingTask ( . init(
197+ seed: 2 ,
198+ projectName: " Launch Site " ,
199+ worktreeName: nil ,
200+ title: " Verify signed DMG and Homebrew install " ,
201+ agentType: . claudeCode,
202+ state: . done,
203+ liveness: . terminated,
204+ lifecycle: . completed,
205+ attention: . completed,
206+ isUnread: true ,
207+ startedSecondsAgo: 18 * 60 ,
208+ verifiedSecondsAgo: 2 * 60
209+ ) , referenceDate: referenceDate) ,
210+ marketingTask ( . init(
211+ seed: 3 ,
212+ projectName: " Pine Demo " ,
213+ worktreeName: " release-2.0 " ,
214+ title: " Capture Agent Inbox screenshots " ,
215+ agentType: . gemini,
216+ state: . executing,
217+ liveness: . live,
218+ lifecycle: . active,
219+ attention: . none,
220+ isUnread: false ,
221+ startedSecondsAgo: 4 * 60 ,
222+ verifiedSecondsAgo: 8
223+ ) , referenceDate: referenceDate) ,
224+ marketingTask ( . init(
225+ seed: 4 ,
226+ projectName: " Documentation " ,
227+ worktreeName: nil ,
228+ title: " Translate the Pine 2.0 highlights " ,
229+ agentType: . openCode,
230+ state: . thinking,
231+ liveness: . live,
232+ lifecycle: . active,
233+ attention: . none,
234+ isUnread: false ,
235+ startedSecondsAgo: 2 * 60 ,
236+ verifiedSecondsAgo: 5
237+ ) , referenceDate: referenceDate) ,
238+ ]
239+ }
240+
241+ private struct MarketingTaskFixture {
242+ let seed : Int
243+ let projectName : String
244+ let worktreeName : String ?
245+ let title : String
246+ let agentType : AgentType
247+ let state : AgentRunState
248+ let liveness : AgentRunLiveness
249+ let lifecycle : AgentTaskLifecycle
250+ let attention : AgentTaskAttention
251+ let isUnread : Bool
252+ let startedSecondsAgo : TimeInterval
253+ let verifiedSecondsAgo : TimeInterval
254+ }
255+
256+ private func marketingTask(
257+ _ fixture: MarketingTaskFixture ,
258+ referenceDate: Date
259+ ) -> AgentTask {
260+ let projectPath = " /Pine Marketing/ \( fixture. projectName) "
261+ let worktreePath = fixture. worktreeName. map {
262+ " \( projectPath) /.pine-worktrees/ \( $0) "
263+ } ?? projectPath
264+ let terminalID = marketingUUID ( fixture. seed + 1_000 )
265+ let observedAt = referenceDate. addingTimeInterval (
266+ - fixture. verifiedSecondsAgo
267+ )
268+ let startedAt = referenceDate. addingTimeInterval (
269+ - fixture. startedSecondsAgo
270+ )
271+ let context = AgentTaskBridgeContext (
272+ project: AgentTaskProjectIdentity (
273+ canonicalProjectPath: projectPath,
274+ canonicalWorktreePath: worktreePath
275+ ) ,
276+ route: AgentTaskRoute (
277+ paneID: marketingUUID ( fixture. seed) ,
278+ tabID: terminalID,
279+ terminalID: terminalID,
280+ availability: fixture. liveness == . live
281+ ? . available
282+ : . missing
283+ ) ,
284+ origin: . pineLaunched,
285+ observedAt: startedAt
286+ )
287+ var task = AgentTask (
288+ descriptor: AgentDescriptor ( agentType: fixture. agentType) ,
289+ context: context,
290+ title: fixture. title,
291+ objective: fixture. title,
292+ createdAt: startedAt
293+ )
294+ task. runs = [ AgentTaskRun ( AgentTaskRunInput (
295+ id: marketingUUID ( fixture. seed + 2_000 ) ,
296+ terminalID: terminalID,
297+ process: AgentProcessEvidence (
298+ processIdentifier: Int32 ( 20_000 + fixture. seed) ,
299+ processGeneration: UInt64 ( fixture. seed) ,
300+ startIdentifier: " marketing-fixture- \( fixture. seed) " ,
301+ observedStartedAt: startedAt,
302+ startIsAuthoritative: true
303+ ) ,
304+ status: AgentTaskRunStatus (
305+ state: fixture. state,
306+ liveness: fixture. liveness,
307+ observedAt: observedAt
308+ )
309+ ) ) ]
310+ task. lifecycle = fixture. lifecycle
311+ task. attention = fixture. attention
312+ task. isUnread = fixture. isUnread
313+ task. updatedAt = observedAt
314+ task. lastActivityAt = observedAt
315+ task. completedAt = fixture. lifecycle == . completed ? observedAt : nil
316+ return task
317+ }
318+
319+ private func marketingUUID( _ seed: Int ) -> UUID {
320+ let suffix = String ( format: " %012llX " , UInt64 ( seed) )
321+ return UUID ( uuidString: " 00000000-0000-0000-0000- \( suffix) " )
322+ ?? UUID ( )
323+ }
324+ #endif
325+
175326 func task( for id: UUID ) -> AgentTask ? {
176327 tasks. first { $0. id == id }
177328 }
0 commit comments