From a4f0d9e125b688f1da1909918bf28f18eb60cf62 Mon Sep 17 00:00:00 2001 From: codeswithroh Date: Mon, 20 Jul 2026 05:42:00 +0530 Subject: [PATCH 1/4] feat: export replay evidence as portable tests --- src/main/agent-artifacts.test.ts | 113 +++++++++++++ src/main/agent-artifacts.ts | 188 ++++++++++++++++++++++ src/main/browser-evidence.test.ts | 5 + src/main/browser-evidence.ts | 39 +++-- src/main/index.ts | 60 +++++++ src/preload/index.ts | 6 +- src/renderer/src/styles.css | 87 +++++++++- src/renderer/src/workflow/SavedChecks.tsx | 172 +++++++++++++++++--- src/shared/agent-schema.ts | 43 ++++- src/shared/contracts.ts | 6 +- tests/e2e/mcp.spec.ts | 21 +++ 11 files changed, 699 insertions(+), 41 deletions(-) create mode 100644 src/main/agent-artifacts.test.ts create mode 100644 src/main/agent-artifacts.ts diff --git a/src/main/agent-artifacts.test.ts b/src/main/agent-artifacts.test.ts new file mode 100644 index 0000000..d414b4f --- /dev/null +++ b/src/main/agent-artifacts.test.ts @@ -0,0 +1,113 @@ +import { mkdir, mkdtemp, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' + +import { describe, expect, it } from 'vitest' + +import { bugSessionSchema } from '../shared/agent-schema.js' +import { + compileIssueReport, + compilePlaywrightSpec, + findSessionForWorkflow, + readAgentEvidence +} from './agent-artifacts.js' + +const SESSION_ID = '11111111-1111-4111-8111-111111111111' +const WORKFLOW_ID = '22222222-2222-4222-8222-222222222222' + +function sessionFixture() { + return bugSessionSchema.parse({ + version: 1, + id: SESSION_ID, + name: 'Category stays selected', + url: 'http://127.0.0.1:4173/', + expectedOutcome: 'Launch clip keeps the Video category.', + issueContext: 'The category changes after Save.', + status: 'completed', + createdAt: '2026-07-20T00:00:00.000Z', + completedAt: '2026-07-20T00:01:00.000Z', + actions: [ + { + id: '33333333-3333-4333-8333-333333333333', + type: 'select_option', + summary: 'Choose "Video" in the field labeled "Category".', + createdAt: '2026-07-20T00:00:10.000Z', + screenshotFile: 'screenshots/001-select-option.png', + command: { type: 'select_option', selector: { label: 'Category' }, value: 'Video' } + }, + { + id: '44444444-4444-4444-8444-444444444444', + type: 'click', + summary: 'Click the button named "Save asset".', + createdAt: '2026-07-20T00:00:20.000Z', + screenshotFile: 'screenshots/002-click.png', + command: { type: 'click', selector: { role: 'button', name: 'Save asset' } } + } + ], + console: [ + { + type: 'error', + summary: 'Category reset to Uncategorized', + createdAt: '2026-07-20T00:00:21.000Z' + } + ], + network: [], + initialScreenshotFile: 'screenshots/000-initial.png', + finalScreenshotFile: 'screenshots/final.png', + traceFile: 'trace.zip', + replayInstructions: 'Choose Video and save.', + workflowId: WORKFLOW_ID + }) +} + +describe('agent artifacts', () => { + it('exports structured actions as reviewable Playwright code', () => { + const source = compilePlaywrightSpec(sessionFixture()) + + expect(source).toContain('page.getByLabel("Category").selectOption("Video")') + expect(source).toContain( + 'page.getByRole("button", { name: "Save asset", exact: true }).click()' + ) + expect(source).toContain('toHaveScreenshot("category-stays-selected.png"') + expect(source).toContain('TASKTAPE_BASE_URL') + }) + + it('builds a ticket-ready report with steps and diagnostics', () => { + const report = compileIssueReport(sessionFixture()) + + expect(report).toContain('## Reproduction steps') + expect(report).toContain('Launch clip keeps the Video category.') + expect(report).toContain('Category reset to Uncategorized') + expect(report).toContain('trace.zip') + }) + + it('asks users to recapture sessions without structured replay commands', () => { + const legacySession = sessionFixture() + legacySession.actions[0].command = null + + expect(() => compilePlaywrightSpec(legacySession)).toThrow( + 'This check predates portable action capture. Record it again to export Playwright code.' + ) + }) + + it('finds persisted evidence and reports portable artifact availability', async () => { + const root = await mkdtemp(join(tmpdir(), 'tasktape-artifacts-')) + const directory = join(root, SESSION_ID) + await mkdir(directory, { recursive: true }) + await writeFile(join(directory, 'session.json'), JSON.stringify(sessionFixture())) + await writeFile(join(directory, 'trace.zip'), 'trace') + + const found = await findSessionForWorkflow(root, WORKFLOW_ID) + const summary = await readAgentEvidence(root, WORKFLOW_ID) + + expect(found?.session.id).toBe(SESSION_ID) + expect(summary).toMatchObject({ + workflowId: WORKFLOW_ID, + actionCount: 2, + screenshotCount: 4, + consoleCount: 1, + hasTrace: true, + canExportPlaywright: true + }) + }) +}) diff --git a/src/main/agent-artifacts.ts b/src/main/agent-artifacts.ts new file mode 100644 index 0000000..234811e --- /dev/null +++ b/src/main/agent-artifacts.ts @@ -0,0 +1,188 @@ +import { existsSync } from 'node:fs' +import { readFile, readdir } from 'node:fs/promises' +import { join } from 'node:path' + +import { + agentEvidenceSummarySchema, + bugSessionSchema, + type AgentEvidenceSummary, + type AgentReplayCommand, + type BrowserSelector, + type BugSession +} from '../shared/agent-schema.js' + +function quoted(value: string): string { + return JSON.stringify(value) +} + +function slug(value: string): string { + return ( + value + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-|-$/g, '') + .slice(0, 64) || 'tasktape-replay' + ) +} + +function locatorSource(selector: BrowserSelector): string { + if (selector.role) { + const options = selector.name ? `, { name: ${quoted(selector.name)}, exact: true }` : '' + return `page.getByRole(${quoted(selector.role)}${options})` + } + if (selector.label) return `page.getByLabel(${quoted(selector.label)})` + if (selector.text) return `page.getByText(${quoted(selector.text)}, { exact: true })` + return `page.locator(${quoted(selector.css ?? '')})` +} + +function commandSource(command: AgentReplayCommand): string { + switch (command.type) { + case 'click': + return `await ${locatorSource(command.selector)}.click()` + case 'fill': + return `await ${locatorSource(command.selector)}.fill(${quoted(command.value)})` + case 'select_option': + return `await ${locatorSource(command.selector)}.selectOption(${quoted(command.value)})` + case 'press_key': + return `await page.keyboard.press(${quoted(command.key)})` + case 'wait_for': + return command.text + ? `await expect(page.getByText(${quoted(command.text)}, { exact: false }).first()).toBeVisible()` + : `await page.waitForTimeout(${command.milliseconds ?? 100})` + } +} + +export function playwrightFileName(session: BugSession): string { + return `${slug(session.name)}.spec.ts` +} + +export function compilePlaywrightSpec(session: BugSession): string { + const commands = session.actions + .filter((action) => action.type !== 'note') + .map((action) => action.command) + + if (commands.length === 0 || commands.some((command) => command === null)) { + throw new Error( + 'This check predates portable action capture. Record it again to export Playwright code.' + ) + } + + const portableCommands = commands.filter( + (command): command is AgentReplayCommand => command !== null + ) + const steps = portableCommands.map((command) => ` ${commandSource(command)}`).join('\n') + const screenshotName = `${slug(session.name)}.png` + const expectedOutcome = session.expectedOutcome.replace(/[\r\n\u2028\u2029]+/g, ' ') + return ( + `import { expect, test } from '@playwright/test'\n\n` + + `test(${quoted(session.name)}, async ({ page }) => {\n` + + ` const targetUrl = process.env.TASKTAPE_BASE_URL ?? ${quoted(session.url)}\n` + + ` await page.goto(targetUrl)\n\n` + + `${steps}\n\n` + + ` // Expected result: ${expectedOutcome}\n` + + ` // Review once, then create the baseline with: npx playwright test --update-snapshots\n` + + ` await expect(page).toHaveScreenshot(${quoted(screenshotName)}, { fullPage: true })\n` + + `})\n` + ) +} + +export function compileIssueReport(session: BugSession): string { + const steps = session.actions + .filter((action) => action.type !== 'note') + .map((action, index) => `${index + 1}. ${action.summary}`) + const notes = session.actions + .filter((action) => action.type === 'note') + .map((action) => `- ${action.summary.replace(/^Agent note:\s*/, '')}`) + const consoleEvents = session.console.map((entry) => `- ${entry.type}: ${entry.summary}`) + const networkEvents = session.network.map((entry) => `- ${entry.type}: ${entry.summary}`) + + return [ + `# ${session.name}`, + '', + '## What happened', + '', + session.issueContext || 'The recorded browser flow did not produce the expected result.', + '', + '## Expected result', + '', + session.expectedOutcome, + '', + '## Reproduction steps', + '', + ...(steps.length ? steps : ['1. Open the recorded page and inspect the current state.']), + ...(notes.length ? ['', '## Observations', '', ...notes] : []), + ...(consoleEvents.length ? ['', '## Console', '', ...consoleEvents] : []), + ...(networkEvents.length ? ['', '## Network', '', ...networkEvents] : []), + '', + '## Evidence', + '', + `- Local URL: ${session.url}`, + `- Recorded actions: ${session.actions.filter((action) => action.type !== 'note').length}`, + `- Trace: ${session.traceFile}`, + `- Final screenshot: ${session.finalScreenshotFile ?? 'Not captured'}`, + '', + '_Captured locally with TaskTape Replay._', + '' + ].join('\n') +} + +export async function findSessionForWorkflow( + sessionsRoot: string, + workflowId: string +): Promise<{ session: BugSession; directory: string } | null> { + if (!existsSync(sessionsRoot)) return null + const directories = (await readdir(sessionsRoot, { withFileTypes: true })).filter((entry) => + entry.isDirectory() + ) + const matches: Array<{ session: BugSession; directory: string }> = [] + + for (const entry of directories) { + const directory = join(sessionsRoot, entry.name) + try { + const session = bugSessionSchema.parse( + JSON.parse(await readFile(join(directory, 'session.json'), 'utf8')) + ) + if (session.workflowId === workflowId && session.status === 'completed') { + matches.push({ session, directory }) + } + } catch { + // Ignore unrelated or incomplete local session folders. + } + } + + return ( + matches.sort((left, right) => + (right.session.completedAt ?? '').localeCompare(left.session.completedAt ?? '') + )[0] ?? null + ) +} + +export async function readAgentEvidence( + sessionsRoot: string, + workflowId: string +): Promise { + const match = await findSessionForWorkflow(sessionsRoot, workflowId) + if (!match) return null + const { session, directory } = match + const executableActions = session.actions.filter((action) => action.type !== 'note') + const screenshotFiles = new Set( + [ + session.initialScreenshotFile, + session.finalScreenshotFile, + ...session.actions.map((action) => action.screenshotFile) + ].filter((file): file is string => Boolean(file)) + ) + + return agentEvidenceSummarySchema.parse({ + workflowId, + sessionId: session.id, + url: session.url, + actionCount: executableActions.length, + screenshotCount: screenshotFiles.size, + consoleCount: session.console.length, + networkCount: session.network.length, + hasTrace: existsSync(join(directory, session.traceFile)), + canExportPlaywright: + executableActions.length > 0 && executableActions.every((action) => action.command !== null) + }) +} diff --git a/src/main/browser-evidence.test.ts b/src/main/browser-evidence.test.ts index 58767cc..b265dad 100644 --- a/src/main/browser-evidence.test.ts +++ b/src/main/browser-evidence.test.ts @@ -101,6 +101,11 @@ describe('browser evidence manager', () => { workflowId: workflow.id, finalScreenshotFile: 'screenshots/final.png' }) + expect(persistedSession.actions[0].command).toMatchObject({ + type: 'select_option', + selector: { label: 'Category' }, + value: 'Video' + }) expect(persistedSession.actions.map((action) => action.type)).toEqual([ 'select_option', 'click', diff --git a/src/main/browser-evidence.ts b/src/main/browser-evidence.ts index 7f74d97..1d9fc3f 100644 --- a/src/main/browser-evidence.ts +++ b/src/main/browser-evidence.ts @@ -25,6 +25,7 @@ import { waitForInputSchema, type BrowserSelector, type BugSession, + type AgentReplayCommand, type StartBugSessionInput } from '../shared/agent-schema.js' import type { SavedWorkflow } from '../shared/workflow-schema.js' @@ -249,7 +250,9 @@ export class BrowserEvidenceManager { const session = this.requireActive() const summary = `Click the ${selectorSummary(input.selector)}.` await resolveLocator(session.page, input.selector).click({ timeout: 10_000 }) - await this.recordAction('click', summary) + await this.recordAction('click', summary, { + command: { type: 'click', selector: input.selector } + }) return this.observe() } @@ -266,7 +269,10 @@ export class BrowserEvidenceManager { input.value.length > 120 ? `${input.value.slice(0, 117)}...` : input.value await this.recordAction( 'fill', - `Fill the ${selectorSummary(input.selector)} with "${displayedValue}".` + `Fill the ${selectorSummary(input.selector)} with "${displayedValue}".`, + { + command: { type: 'fill', selector: input.selector, value: input.value } + } ) return this.observe() } @@ -279,7 +285,8 @@ export class BrowserEvidenceManager { }) await this.recordAction( 'select_option', - `Choose "${input.value}" in the ${selectorSummary(input.selector)}.` + `Choose "${input.value}" in the ${selectorSummary(input.selector)}.`, + { command: { type: 'select_option', selector: input.selector, value: input.value } } ) return this.observe() } @@ -288,7 +295,9 @@ export class BrowserEvidenceManager { const input = pressKeyInputSchema.parse(rawInput) const session = this.requireActive() await session.page.keyboard.press(input.key) - await this.recordAction('press_key', `Press ${input.key}.`) + await this.recordAction('press_key', `Press ${input.key}.`, { + command: { type: 'press_key', key: input.key } + }) return this.observe() } @@ -300,17 +309,21 @@ export class BrowserEvidenceManager { state: 'visible', timeout: 10_000 }) - await this.recordAction('wait_for', `Wait until "${input.text}" is visible.`) + await this.recordAction('wait_for', `Wait until "${input.text}" is visible.`, { + command: { type: 'wait_for', text: input.text } + }) } else { await session.page.waitForTimeout(input.milliseconds ?? 100) - await this.recordAction('wait_for', `Wait ${input.milliseconds} milliseconds.`) + await this.recordAction('wait_for', `Wait ${input.milliseconds} milliseconds.`, { + command: { type: 'wait_for', milliseconds: input.milliseconds } + }) } return this.observe() } async addNote(rawInput: unknown): Promise { const input = addSessionNoteInputSchema.parse(rawInput) - await this.recordAction('note', `Agent note: ${input.note}`, false) + await this.recordAction('note', `Agent note: ${input.note}`, { screenshot: false }) return this.requireActive().data } @@ -395,16 +408,17 @@ export class BrowserEvidenceManager { private async recordAction( type: BugSession['actions'][number]['type'], summary: string, - screenshot = true + options: { screenshot?: boolean; command?: AgentReplayCommand } = {} ): Promise { const session = this.requireActive() if (session.data.actions.length >= MAX_ACTIONS) { throw new Error('This session reached the 100-action limit. Finish it before continuing.') } const index = session.data.actions.length + 1 - const screenshotFile = screenshot - ? `screenshots/${String(index).padStart(3, '0')}-${type.replace('_', '-')}.png` - : null + const screenshotFile = + (options.screenshot ?? true) + ? `screenshots/${String(index).padStart(3, '0')}-${type.replace('_', '-')}.png` + : null if (screenshotFile) { await session.page.screenshot({ path: join(session.directory, screenshotFile), @@ -417,7 +431,8 @@ export class BrowserEvidenceManager { type, summary, createdAt: new Date().toISOString(), - screenshotFile + screenshotFile, + command: options.command ?? null }) ) await this.persist() diff --git a/src/main/index.ts b/src/main/index.ts index 59abc17..d110f98 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,3 +1,4 @@ +import { mkdir, writeFile } from 'node:fs/promises' import { join, resolve } from 'node:path' import { pathToFileURL } from 'node:url' @@ -5,6 +6,7 @@ import { config } from 'dotenv' import { app, BrowserWindow, + clipboard, desktopCapturer, dialog, type IpcMainInvokeEvent, @@ -25,6 +27,7 @@ import type { SaveWorkflowInput, SetScheduleEnabledInput } from '../shared/workflow-schema.js' +import { workflowIdSchema } from '../shared/workflow-schema.js' import { analyzeRecording, requestOpenAIAnalysis, @@ -33,6 +36,13 @@ import { } from './analysis.js' import { TEST_COMPUTER_WORKFLOW_ANALYSIS, TEST_WORKFLOW_ANALYSIS } from './analysis-fixture.js' import { AgentMcpServer } from './agent-mcp.js' +import { + compileIssueReport, + compilePlaywrightSpec, + findSessionForWorkflow, + playwrightFileName, + readAgentEvidence +} from './agent-artifacts.js' import { clearApiKey, type CredentialCipher, @@ -365,6 +375,56 @@ function registerAgentIpc(): void { if (!agentMcpServer) throw new Error('The local agent connection is still starting.') return agentMcpServer.status }) + ipcMain.handle('agent:get-evidence', async (event, workflowId: string) => { + assertTrustedSender(event) + return readAgentEvidence(agentSessionsRoot(), workflowIdSchema.parse(workflowId)) + }) + ipcMain.handle('agent:copy-report', async (event, workflowId: string) => { + assertTrustedSender(event) + const match = await findSessionForWorkflow( + agentSessionsRoot(), + workflowIdSchema.parse(workflowId) + ) + if (!match) throw new Error('No agent evidence is attached to this check.') + clipboard.writeText(compileIssueReport(match.session)) + }) + ipcMain.handle('agent:export-playwright', async (event, workflowId: string) => { + assertTrustedSender(event) + const id = workflowIdSchema.parse(workflowId) + const match = await findSessionForWorkflow(agentSessionsRoot(), id) + if (!match) throw new Error('No agent evidence is attached to this check.') + const fileName = playwrightFileName(match.session) + let path: string | null + + if (process.env.TASKTAPE_E2E === '1') { + const directory = join(app.getPath('userData'), 'exports') + await mkdir(directory, { recursive: true }) + path = join(directory, fileName) + } else { + const owner = BrowserWindow.fromWebContents(event.sender) + const result = await dialog.showSaveDialog(owner ?? BrowserWindow.getAllWindows()[0], { + title: 'Export Playwright check', + defaultPath: fileName, + filters: [{ name: 'Playwright TypeScript', extensions: ['ts'] }] + }) + path = result.canceled ? null : (result.filePath ?? null) + } + + if (!path) return null + await writeFile(path, compilePlaywrightSpec(match.session), { mode: 0o600 }) + return { path, fileName } + }) + ipcMain.handle('agent:reveal-evidence', async (event, workflowId: string) => { + assertTrustedSender(event) + const match = await findSessionForWorkflow( + agentSessionsRoot(), + workflowIdSchema.parse(workflowId) + ) + if (!match) throw new Error('No agent evidence is attached to this check.') + if (process.env.TASKTAPE_E2E !== '1') { + shell.showItemInFolder(join(match.directory, 'session.json')) + } + }) } async function startAgentMcpServer(): Promise { diff --git a/src/preload/index.ts b/src/preload/index.ts index ce1bbbe..d68d6ca 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -32,7 +32,11 @@ const bridge: TaskTapeBridge = { clearApiKey: () => ipcRenderer.invoke('settings:clear-api-key') }, agent: { - getStatus: () => ipcRenderer.invoke('agent:get-status') + getStatus: () => ipcRenderer.invoke('agent:get-status'), + getEvidence: (workflowId) => ipcRenderer.invoke('agent:get-evidence', workflowId), + copyReport: (workflowId) => ipcRenderer.invoke('agent:copy-report', workflowId), + exportPlaywright: (workflowId) => ipcRenderer.invoke('agent:export-playwright', workflowId), + revealEvidence: (workflowId) => ipcRenderer.invoke('agent:reveal-evidence', workflowId) }, workflow: { list: () => ipcRenderer.invoke('workflow:list'), diff --git a/src/renderer/src/styles.css b/src/renderer/src/styles.css index 0f7003a..e58ed64 100644 --- a/src/renderer/src/styles.css +++ b/src/renderer/src/styles.css @@ -2382,7 +2382,7 @@ h1 { } .saved-checks > header > button, -.saved-check-row > button { +.saved-check-actions > button { display: grid; width: 38px; height: 38px; @@ -2398,14 +2398,17 @@ h1 { border-top: 1px solid var(--border); } +.saved-check-item { + border-bottom: 1px solid var(--border); +} + .saved-check-row { display: grid; - grid-template-columns: 38px minmax(0, 1fr) 38px; + grid-template-columns: 38px minmax(0, 1fr) auto; gap: 14px; align-items: center; min-height: 92px; padding: 16px 0; - border-bottom: 1px solid var(--border); } .saved-check-icon { @@ -2429,22 +2432,94 @@ h1 { font-size: 11px; } -.saved-check-row div > span { +.saved-check-copy > span { display: block; margin-top: 7px; color: var(--muted); font-size: 10px; } -.saved-check-row > button { +.saved-check-actions { + display: flex; + gap: 7px; +} + +.saved-check-actions > button { color: var(--primary); } -.saved-check-row > button:disabled { +.saved-check-actions > button:disabled { cursor: not-allowed; opacity: 0.45; } +.saved-check-actions > button[aria-expanded='true'] svg { + transform: rotate(180deg); +} + +.check-evidence { + display: grid; + gap: 15px; + padding: 16px 0 19px 52px; + border-top: 1px solid var(--border); +} + +.check-evidence > p { + color: var(--muted); + font-size: 11px; +} + +.evidence-summary { + display: flex; + flex-wrap: wrap; + gap: 8px 18px; + color: var(--muted); + font-size: 10px; +} + +.evidence-summary span { + white-space: nowrap; +} + +.evidence-summary strong { + margin-right: 3px; + color: var(--text); + font-weight: 650; +} + +.evidence-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.evidence-actions button { + display: inline-flex; + min-height: 34px; + align-items: center; + gap: 7px; + padding: 0 11px; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + color: var(--text); + background: var(--surface); + font-size: 11px; +} + +.evidence-actions button:hover:not(:disabled) { + border-color: var(--primary); + color: var(--primary); +} + +.evidence-actions button:disabled { + cursor: not-allowed; + opacity: 0.45; +} + +.check-evidence .evidence-feedback { + color: var(--primary); +} + .saved-check-error { margin-top: 12px; color: #a13d28; diff --git a/src/renderer/src/workflow/SavedChecks.tsx b/src/renderer/src/workflow/SavedChecks.tsx index 6997f58..89ef7fb 100644 --- a/src/renderer/src/workflow/SavedChecks.tsx +++ b/src/renderer/src/workflow/SavedChecks.tsx @@ -1,6 +1,16 @@ -import { Bot, Play, RefreshCw } from 'lucide-react' +import { + Bot, + Check, + ChevronDown, + Clipboard, + FileCode2, + FolderOpen, + Play, + RefreshCw +} from 'lucide-react' import { useCallback, useEffect, useState } from 'react' +import type { AgentEvidenceSummary } from '../../../shared/agent-schema' import type { SavedWorkflow } from '../../../shared/workflow-schema' interface SavedChecksProps { @@ -10,6 +20,10 @@ interface SavedChecksProps { export function SavedChecks({ onRun }: SavedChecksProps): React.JSX.Element | null { const [checks, setChecks] = useState([]) const [runningId, setRunningId] = useState(null) + const [expandedId, setExpandedId] = useState(null) + const [evidence, setEvidence] = useState(null) + const [evidenceBusy, setEvidenceBusy] = useState(false) + const [feedback, setFeedback] = useState(null) const [error, setError] = useState(null) const refresh = useCallback(async (): Promise => { @@ -44,6 +58,51 @@ export function SavedChecks({ onRun }: SavedChecksProps): React.JSX.Element | nu } } + const toggleEvidence = async (workflowId: string): Promise => { + if (expandedId === workflowId) { + setExpandedId(null) + setEvidence(null) + return + } + + setExpandedId(workflowId) + setEvidence(null) + setEvidenceBusy(true) + setFeedback(null) + setError(null) + try { + const result = await window.tasktape.agent.getEvidence(workflowId) + setEvidence(result) + if (!result) setFeedback('No agent evidence is attached to this check.') + } catch (caught) { + setError(caught instanceof Error ? caught.message : 'Unable to load the evidence.') + } finally { + setEvidenceBusy(false) + } + } + + const copyReport = async (): Promise => { + if (!evidence) return + setError(null) + try { + await window.tasktape.agent.copyReport(evidence.workflowId) + setFeedback('Bug report copied') + } catch (caught) { + setError(caught instanceof Error ? caught.message : 'Unable to copy the bug report.') + } + } + + const exportPlaywright = async (workflowId: string): Promise => { + setFeedback(null) + setError(null) + try { + const result = await window.tasktape.agent.exportPlaywright(workflowId) + setFeedback(result ? `${result.fileName} saved` : 'Export cancelled') + } catch (caught) { + setError(caught instanceof Error ? caught.message : 'Unable to export this check.') + } + } + return (
@@ -58,26 +117,99 @@ export function SavedChecks({ onRun }: SavedChecksProps): React.JSX.Element | nu
{checks.map((check) => ( -
- - - -
-

{check.name}

-

{check.goal}

- {check.capability === 'computer' && check.expectedOutcome ? ( - Expected: {check.expectedOutcome} - ) : null} +
+
+ + + +
+

{check.name}

+

{check.goal}

+ {check.capability === 'computer' && check.expectedOutcome ? ( + Expected: {check.expectedOutcome} + ) : null} +
+
+ {check.capability === 'computer' ? ( + + ) : null} + +
- + + {expandedId === check.id ? ( +
+ {evidenceBusy ?

Loading evidence...

: null} + {evidence ? ( + <> +
+ + {evidence.actionCount} actions + + + {evidence.screenshotCount} screenshots + + + {evidence.consoleCount} console events + + + {evidence.networkCount} network events + + + {evidence.hasTrace ? 'Ready' : 'Missing'} trace + +
+
+ + + +
+ {feedback ?

{feedback}

: null} + + ) : null} +
+ ) : null}
))}
diff --git a/src/shared/agent-schema.ts b/src/shared/agent-schema.ts index b7cdaa7..f610057 100644 --- a/src/shared/agent-schema.ts +++ b/src/shared/agent-schema.ts @@ -108,12 +108,33 @@ export const finishBugSessionInputSchema = z.object({ replayInstructions: z.string().trim().max(2_000).optional() }) +export const agentReplayCommandSchema = z.discriminatedUnion('type', [ + z.object({ type: z.literal('click'), selector: browserSelectorSchema }), + z.object({ + type: z.literal('fill'), + selector: browserSelectorSchema, + value: z.string().max(4_000) + }), + z.object({ + type: z.literal('select_option'), + selector: browserSelectorSchema, + value: z.string().trim().min(1).max(240) + }), + z.object({ type: z.literal('press_key'), key: pressKeyInputSchema.shape.key }), + z.object({ + type: z.literal('wait_for'), + text: z.string().trim().min(1).max(240).optional(), + milliseconds: z.number().int().min(100).max(5_000).optional() + }) +]) + export const agentActionSchema = z.object({ id: z.string().uuid(), type: z.enum(['click', 'fill', 'select_option', 'press_key', 'wait_for', 'note']), summary: z.string().min(1).max(1_000), createdAt: z.string().datetime(), - screenshotFile: z.string().min(1).max(160).nullable() + screenshotFile: z.string().min(1).max(160).nullable(), + command: agentReplayCommandSchema.nullable().default(null) }) const browserLogSchema = z.object({ @@ -155,7 +176,27 @@ export const agentServerStatusSchema = z.object({ .nullable() }) +export const agentEvidenceSummarySchema = z.object({ + workflowId: z.string().uuid(), + sessionId: bugSessionIdSchema, + url: browserUrlSchema, + actionCount: z.number().int().nonnegative(), + screenshotCount: z.number().int().nonnegative(), + consoleCount: z.number().int().nonnegative(), + networkCount: z.number().int().nonnegative(), + hasTrace: z.boolean(), + canExportPlaywright: z.boolean() +}) + +export const exportedArtifactSchema = z.object({ + path: z.string().min(1), + fileName: z.string().min(1) +}) + export type AgentServerStatus = z.infer +export type AgentEvidenceSummary = z.infer +export type AgentReplayCommand = z.infer export type BrowserSelector = z.infer export type BugSession = z.infer +export type ExportedArtifact = z.infer export type StartBugSessionInput = z.infer diff --git a/src/shared/contracts.ts b/src/shared/contracts.ts index e389b02..27de36b 100644 --- a/src/shared/contracts.ts +++ b/src/shared/contracts.ts @@ -69,6 +69,10 @@ export interface TaskTapeBridge { } agent: { getStatus: () => Promise + getEvidence: (workflowId: string) => Promise + copyReport: (workflowId: string) => Promise + exportPlaywright: (workflowId: string) => Promise + revealEvidence: (workflowId: string) => Promise } workflow: { list: () => Promise @@ -89,7 +93,7 @@ import type { TranscribeIntentResult } from './analysis-contracts.js' import type { WorkflowAnalysis } from './analysis-schema.js' -import type { AgentServerStatus } from './agent-schema.js' +import type { AgentEvidenceSummary, AgentServerStatus, ExportedArtifact } from './agent-schema.js' import type { SaveScheduleInput, SavedWorkflow, diff --git a/tests/e2e/mcp.spec.ts b/tests/e2e/mcp.spec.ts index c49a3bf..fe0fe19 100644 --- a/tests/e2e/mcp.spec.ts +++ b/tests/e2e/mcp.spec.ts @@ -82,6 +82,27 @@ test('shares one agent-recorded bug session with the TaskTape desktop app', asyn timeout: 5_000 }) await expect(page.getByText(/Expected: Launch clip appears/)).toBeVisible() + await page + .getByRole('button', { name: 'Review evidence for Agent captured category bug' }) + .click() + await expect(page.locator('.evidence-summary')).toContainText('1 actions') + await expect(page.locator('.evidence-summary')).toContainText('Ready trace') + await page.getByRole('button', { name: 'Copy bug report' }).click() + await expect(page.getByText('Bug report copied', { exact: true })).toBeVisible() + const copiedReport = await application.evaluate(({ clipboard }) => clipboard.readText()) + expect(copiedReport).toContain('## Reproduction steps') + expect(copiedReport).toContain('Launch clip appears in Saved assets') + + await page.getByRole('button', { name: 'Export Playwright' }).click() + await expect(page.getByText('agent-captured-category-bug.spec.ts saved')).toBeVisible() + const exportedTest = await readFile( + join(userData, 'exports', 'agent-captured-category-bug.spec.ts'), + 'utf8' + ) + expect(exportedTest).toContain( + 'page.getByRole("button", { name: "Save asset", exact: true }).click()' + ) + expect(exportedTest).toContain('toHaveScreenshot("agent-captured-category-bug.png"') await page.addStyleTag({ content: '*, *::before, *::after { animation: none !important; transition: none !important; }' }) From 8ff0660192e7538e4cab646f621ae8f8b1a42243 Mon Sep 17 00:00:00 2001 From: codeswithroh Date: Mon, 20 Jul 2026 05:43:21 +0530 Subject: [PATCH 2/4] docs: record Reddit competitor findings --- README.md | 3 ++ docs/architecture.md | 4 ++ docs/reddit-market-research.md | 74 ++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 docs/reddit-market-research.md diff --git a/README.md b/README.md index 9ef21b8..e39e16e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ The Build Week reference flow records a browser bug, captures the expected outco The agent-operated reference flow is limited to local HTTP and HTTPS development servers. TaskTape creates a headed isolated Chrome session and exposes 12 bounded MCP tools for observation, interaction, evidence capture, check creation, and explicit execution. Finishing a capture never runs or schedules the check. +Agent-created checks keep their evidence attached. Users can inspect screenshot, console, network, and trace counts, copy a ticket-ready Markdown report, reveal the local files, or export the observed steps as an ordinary Playwright TypeScript test. Exported tests use no LLM at run time and remain reviewable before they enter source control or CI. + Checks have editable instructions and can run now or on hourly, daily, weekday, or weekly timing. Manual and scheduled results appear in Run history, so a team can see when a previously fixed behavior regresses. A spoken schedule such as "every Monday at 9 AM" prefills the save controls. It never becomes active until the user confirms unattended execution and saves the task. The Scheduled view shows the next run, last result, pause or resume, and Run now controls. @@ -56,6 +58,7 @@ Ask the connected agent to reproduce a bug on a local development URL and turn i - [Architecture](docs/architecture.md) - [Agent-operated replay milestone](docs/agent-mcp-milestone.md) - [Voice and scheduling research](docs/voice-and-scheduling-research.md) +- [Reddit competitor research](docs/reddit-market-research.md) - [Market validation](docs/market-validation.md) - [Milestone roadmap](docs/roadmap.md) - [Verification log](docs/verification.md) diff --git a/docs/architecture.md b/docs/architecture.md index c7194f9..ab9e53d 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -31,6 +31,10 @@ The first MCP capability intentionally targets local web applications. `start_bu Every operation stores an ordered action plus a screenshot. The session also records console messages, page errors, failed requests, HTTP error responses, initial and final screenshots, and a Playwright trace containing DOM snapshots and action timing. Finishing compiles the recorded actions into a review-required computer workflow and closes the temporary browser. Running or scheduling remains a separate explicit action. +Agent actions also persist their schema-validated replay command. TaskTape can compile those commands into a small Playwright TypeScript test using the exact observed selectors and inputs, without calling a model during later test runs. The export carries the expected outcome as review context and uses a visual assertion that requires the developer to approve a baseline. Older sessions remain readable but cannot be exported from their natural-language summaries. + +The same session can produce a Markdown bug report with reproduction steps and diagnostic context. Reports are copied through the main process, exports use a native save dialog and `0600` file mode, and revealing evidence opens the existing local session directory. The renderer receives only evidence counts and availability flags. + ## Capture source selection TaskTape lists full displays and open application windows through Electron `desktopCapturer`, then renders a grouped thumbnail gallery in the sandboxed UI. A source ID selected by the user is validated against a freshly enumerated main-process source list. The display-media handler consumes that one pending selection and rejects requests without one; the renderer cannot nominate an arbitrary capture target. diff --git a/docs/reddit-market-research.md b/docs/reddit-market-research.md new file mode 100644 index 0000000..2d5b93d --- /dev/null +++ b/docs/reddit-market-research.md @@ -0,0 +1,74 @@ +# Reddit market research + +Research date: 2026-07-20 + +## Question + +Which competitor features are useful in real QA work, and which gaps should TaskTape close without becoming another generic recorder or opaque AI testing service? + +This is qualitative research, not market sizing. Reddit comments are anecdotal and can include vendor promotion. Claims were checked against current product documentation where possible. + +## What practitioners keep saying + +- Ground automation in a browser flow that a person or agent actually observed. Several practitioners report better results when AI converts a verified Playwright recording into code instead of inventing tests from source code alone. +- Keep a human in the loop. Reviewable code, explicit expected outcomes, and key-step screenshots matter more than an agent claiming success. +- Capture debugging context with the report. Console messages, failed requests, screenshots, and exact reproduction steps reduce the developer's work after a bug is filed. +- Do not require an LLM every time a regression runs. Teams call out cost, latency, noisy MCP sessions, weak selectors, and confidently incorrect assertions. +- Let the team own the artifact. Ordinary Playwright files fit source control, code review, CI, and existing test conventions better than a closed test format. + +## Competitor comparison + +| Product or approach | What appears to work | Repeated weakness or risk | TaskTape before this milestone | +| ------------------------------------ | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| Jam | One capture includes video, reproduction context, console and network evidence, then moves into issue trackers | Strong at reporting, but it is not the same as a durable regression test owned in the repository | TaskTape stored richer local evidence but did not turn it into a ticket-ready report | +| Playwright recorder and MCP | Real browser execution, familiar code, screenshots, traces, and CI compatibility | Generated tests still need review; users report weak locators, missing assertions, token use, and very large generated files | TaskTape captured observed actions but kept them inside its session format | +| BugBug and record-and-replay tools | Fast recording, screenshots for each step, reports, and low-code editing | Recorder output can be brittle, and a recording without a meaningful assertion is not a regression check | TaskTape had an expected result and replay verdict, but no portable test export | +| Momentic and Autify-style AI testing | Natural-language authoring, self-healing, run diagnostics, and managed execution | Closed or platform-specific formats can reduce control; AI healing still needs governance | TaskTape favored local evidence and explicit approval, but lacked a developer-owned handoff | +| QA Wolf | Playwright ownership, managed parallel runs, CI, and human-reviewed failures | Managed service cost and external ownership are a poor fit for every small team | TaskTape runs locally but did not offer standard code that could graduate into CI | + +## Features selected + +### 1. Portable Playwright export + +TaskTape now stores structured commands while an agent reproduces a bug and compiles them into a small TypeScript Playwright test. The export uses role, label, text, or CSS locators captured during the observed run, supports a `TASKTAPE_BASE_URL` override, records the expected result, and ends with an explicit visual assertion. + +The generated test does not call an LLM. A developer reviews it once, creates the visual baseline, and can then run it in normal Playwright CI. Sessions recorded before structured command capture clearly ask the user to record again instead of guessing from prose. + +### 2. Ticket-ready bug report + +Each agent-created check can copy a Markdown report containing what happened, the expected result, exact reproduction steps, observations, console and network events, and local evidence filenames. This borrows the useful handoff shape from Jam without uploading private evidence or requiring an issue-tracker account. + +### 3. Visible evidence inventory + +Saved checks now expose action, screenshot, console, network, and trace counts. Users can reveal the local evidence folder before deciding whether to run, export, or share anything. + +## Not selected yet + +- **Automatic self-healing:** useful when a locator changes, but dangerous without showing the proposed repair and comparing it with the original intent. This needs a separate review workflow and flake classification. +- **Direct Jira, Linear, or GitHub issue creation:** valuable, but local Markdown copy provides the handoff without adding OAuth, cloud storage, or a vendor-specific integration during this milestone. +- **Managed cloud execution:** competitors already serve this category. TaskTape's current differentiation is local evidence, agent operation, explicit review, and portable output. +- **Automatic codebase edits:** Reddit contains concrete reports of coding agents changing application behavior so generated tests pass. TaskTape exports a reviewable test and does not modify the target application. + +## Decision + +TaskTape should not compete as another no-code recorder. Its strongest position is the bridge from a human or coding agent reproducing a real bug to two owned artifacts: a developer-ready report and a normal regression test. The product still needs repository-aware export, direct issue handoff, CI setup, and reviewed repair suggestions before it can replace a mature team workflow. + +## Sources + +### Reddit discussions + +- [Are you actually using AI in test automation?](https://www.reddit.com/r/QualityAssurance/comments/1sxdmmo/are_you_actually_using_ai_in_test_automation/) +- [Anyone actually using AI for test automation? What works?](https://www.reddit.com/r/QualityAssurance/comments/1mna861/anyone_actually_using_ai_for_test_automation_what/) +- [Has anyone used Playwright MCP yet?](https://www.reddit.com/r/QualityAssurance/comments/1jzkqgs/has_anyone_used_playwright_mcp_yet_im_wondering/) +- [What real-world QA use cases do you have with AI agents?](https://www.reddit.com/r/QualityAssurance/comments/1tp15zu/what_realworld_qa_use_cases_do_you_have_with_aiai/) +- [Bug reporting tool discussion about Jam](https://www.reddit.com/r/QualityAssurance/comments/152t1kx/) +- [Do software testers use any tools or AI for testing?](https://www.reddit.com/r/QualityAssurance/comments/1rq6d23/do_software_testers_here_use_any_tools_or_ai_for/) +- [What is missing from this AI-driven E2E testing workflow?](https://www.reddit.com/r/Playwright/comments/1umqvix/whats_missing_from_this_aidriven_e2e_testing/) +- [Claude wrote Playwright tests that secretly patched the app](https://www.reddit.com/r/ClaudeCode/comments/1rug14a/claude_wrote_playwright_tests_that_secretly/) + +### Product checks + +- [Jam for Linear](https://jam.dev/linear) +- [BugBug features](https://bugbug.io/features/) +- [Momentic documentation](https://momentic.ai/docs) +- [QA Wolf documentation](https://docs.qawolf.com/qawolf/Welcome-to-QA-Wolf) From d608e8e9f98de511389103542eac61c97e533a3f Mon Sep 17 00:00:00 2001 From: codeswithroh Date: Mon, 20 Jul 2026 05:49:04 +0530 Subject: [PATCH 3/4] feat: present portable checks on the release site --- .prettierignore | 1 + docs/hackathon-strategy.md | 4 +-- package.json | 3 +- playwright.site.config.ts | 36 +++++++++++++++++++++ site/assets/favicon.png | Bin 0 -> 2554 bytes site/assets/tasktape-portable-check.png | Bin 0 -> 78245 bytes site/index.html | 20 +++++++----- tests/site/landing.spec.ts | 40 ++++++++++++++++++++++++ 8 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 .prettierignore create mode 100644 playwright.site.config.ts create mode 100644 site/assets/favicon.png create mode 100644 site/assets/tasktape-portable-check.png create mode 100644 tests/site/landing.spec.ts diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..6c9fe43 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +site/.vercel/ diff --git a/docs/hackathon-strategy.md b/docs/hackathon-strategy.md index b29c41d..b3e59fa 100644 --- a/docs/hackathon-strategy.md +++ b/docs/hackathon-strategy.md @@ -347,7 +347,7 @@ The README must then show: what changed during Build Week, exact GPT-5.6 integra 9. **Can it test any application?** No. The hackathon proves one complete browser workflow and a bounded macOS execution path. 10. **What happens when the UI changes?** GPT-5.6 reasons over the current screen instead of replaying fixed coordinates, but major changes can still fail and are reported. 11. **Does it capture passwords?** Agent sessions reject password-field fills, accept only local development URLs, and remain local. API keys use operating-system encryption. Human recordings still require the user to avoid exposing secrets. -12. **Why not generate Playwright?** TaskTape already uses Playwright for rich capture, but GPT-5.6 replay adapts to the current visual interface and can later extend beyond DOM-only targets. Exportable Playwright tests are future work. +12. **Does TaskTape generate Playwright?** Agent-recorded browser sessions can export their observed steps as ordinary Playwright TypeScript with a reviewed visual baseline. TaskTape also keeps GPT-5.6 replay for adaptive checks and targets beyond DOM-only browser flows. 13. **Does scheduling run while the Mac sleeps?** No. The app must be open and the Mac awake. 14. **What happens on a model safety check?** The run stops before the flagged action executes. 15. **What did Codex build?** The repo documents the iterative native recorder, schemas, execution adapters, scheduler, tests, and design revisions in dated commits. @@ -355,7 +355,7 @@ The README must then show: what changed during Build Week, exact GPT-5.6 integra 17. **What is mocked?** Automated Electron tests mock model responses. The separate live tests use the real OpenAI API and are labeled. 18. **How reliable is it?** All 61 unit and integration tests and 10 Electron journeys pass. The visual evaluator classified five consecutive broken and fixed pairs correctly, and the paired live replay produced the expected fail and pass results. 19. **What is the business wedge?** Product and support teams that collect bug videos but lack time to convert every issue into regression coverage. -20. **What comes next?** Exportable test artifacts, CI triggers, richer event traces, signed builds, and an open evaluation format for computer-use agents. +20. **What comes next?** Repository-aware exports, CI triggers, reviewed repair suggestions, signed builds, and an open evaluation format for computer-use agents. ## Red-team conclusion diff --git a/package.json b/package.json index ee84e55..a86264d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tasktape", - "version": "0.2.0", + "version": "0.3.0", "description": "Turn a narrated bug recording into a living regression check.", "type": "module", "main": "./out/main/index.js", @@ -20,6 +20,7 @@ "test:live:computer": "pnpm build && TASKTAPE_LIVE_COMPUTER=1 playwright test --config playwright.live.config.ts", "test:watch": "vitest", "test:e2e": "pnpm build && playwright test", + "test:site": "PLAYWRIGHT_BROWSERS_PATH=build/playwright-browsers playwright test --config playwright.site.config.ts", "check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test && pnpm build", "browser:install": "PLAYWRIGHT_BROWSERS_PATH=build/playwright-browsers playwright install --no-shell chromium", "package:mac": "pnpm browser:install && pnpm build && electron-builder --mac" diff --git a/playwright.site.config.ts b/playwright.site.config.ts new file mode 100644 index 0000000..9e22fbc --- /dev/null +++ b/playwright.site.config.ts @@ -0,0 +1,36 @@ +import { defineConfig, devices } from '@playwright/test' + +export default defineConfig({ + testDir: 'tests/site', + timeout: 30_000, + fullyParallel: false, + workers: 1, + reporter: 'list', + use: { + baseURL: 'http://127.0.0.1:4188', + headless: false, + trace: 'retain-on-failure', + screenshot: 'only-on-failure' + }, + projects: [ + { + name: 'desktop', + use: { ...devices['Desktop Chrome'], viewport: { width: 1440, height: 900 } } + }, + { + name: 'mobile', + use: { + browserName: 'chromium', + viewport: { width: 390, height: 844 }, + isMobile: true, + hasTouch: true + } + } + ], + webServer: { + command: 'python3 -m http.server 4188 --directory site', + url: 'http://127.0.0.1:4188', + reuseExistingServer: false + }, + outputDir: 'test-results/site' +}) diff --git a/site/assets/favicon.png b/site/assets/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..09f8cb44658e3eb866ae9492a0b4ea9ca01339b0 GIT binary patch literal 2554 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 TaskTape Replay | Reproduce bugs. Prove fixes. + @@ -56,7 +57,8 @@

TaskTape Replay

Reproduce the bug. Prove the fix. Keep the check.

Give Claude Code or Codex a visible browser it can control through MCP. TaskTape - preserves the evidence and turns the session into a check your team can replay. + preserves the evidence, builds the bug report, and exports a Playwright check your team + owns.

TaskTape Replay desktop app showing an agent-created browser regression check
- Agent created A reproducible check, ready for review + Yours to keep Report, evidence, and Playwright code Runs on your Mac
@@ -102,6 +104,7 @@

TaskTape Replay

  • Console
  • Network
  • Playwright trace
  • +
  • Portable test
  • @@ -133,7 +136,7 @@

    Review

  • 04

    Replay

    -

    Run the same check after a change and inspect pass or fail evidence.

    +

    Run it in TaskTape or export ordinary Playwright code for your repository.

  • @@ -149,6 +152,7 @@

    A failed check shows where reality diverged.

    • Evidence stays on your machine
    • Expected outcomes require review
    • +
    • Exported tests run without an LLM
    • Every replay keeps a run record
    @@ -231,7 +235,7 @@

    Built for local development today.

    Make the fix measurable

    -

    Leave every reproduced bug with a check.

    +

    Leave every reproduced bug with code.

    Free, local, and ready for Claude Code or Codex.

    { + const consoleErrors: string[] = [] + page.on('console', (message) => { + if (message.type() === 'error') consoleErrors.push(message.text()) + }) + + await page.goto('/') + await expect(page.getByRole('heading', { level: 1, name: 'TaskTape Replay' })).toBeVisible() + await expect(page.getByText('Report, evidence, and Playwright code')).toBeVisible() + await expect(page.getByText('Portable test', { exact: true })).toBeVisible() + await page.locator('.proof-product').scrollIntoViewIfNeeded() + await page.waitForFunction(() => + Array.from(document.images).every((image) => image.complete && image.naturalWidth > 0) + ) + + const downloadLinks = page.locator('a.button-download') + await expect(downloadLinks).toHaveCount(3) + for (let index = 0; index < (await downloadLinks.count()); index += 1) { + await expect(downloadLinks.nth(index)).toHaveAttribute('href', downloadUrl) + } + + const overflow = await page.evaluate( + () => document.documentElement.scrollWidth > window.innerWidth + ) + expect(overflow).toBe(false) + expect(consoleErrors).toEqual([]) + + await mkdir('output/playwright', { recursive: true }) + await page.screenshot({ + path: `output/playwright/landing-${testInfo.project.name}.png`, + fullPage: true + }) +}) From fed6db1fdff6807e4ee452d8ca644687e31983f9 Mon Sep 17 00:00:00 2001 From: codeswithroh Date: Mon, 20 Jul 2026 05:54:39 +0530 Subject: [PATCH 4/4] docs: record portable evidence verification --- docs/roadmap.md | 18 ++++++++++++++++++ docs/verification.md | 13 ++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/roadmap.md b/docs/roadmap.md index ed11ff4..542e58c 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -187,6 +187,24 @@ Verified result: - The production landing page and stable GitHub download passed desktop, mobile, redirect, full-download, SHA-256, and disk-image checks. - The release remains clearly labeled as an unsigned Apple Silicon beta until Apple Developer ID and notarization credentials are available. +## Milestone 6: Portable evidence handoff - Complete July 20 + +Deliverables: + +- Reddit competitor and practitioner research with explicit product boundaries. +- Evidence inventory on every agent-created check. +- Ticket-ready Markdown report copied without uploading local evidence. +- Ordinary Playwright TypeScript export built from observed structured commands. +- Updated desktop and mobile release site using a verified product screenshot. + +Exit gate: + +- Unit tests verify deterministic compilation and legacy-session refusal. The Electron MCP journey reads the report from the system clipboard and validates the exported test. That journey passes against the development build, unpacked production app, and read-only mounted DMG. + +Verified result: + +- The complete gate passed with 66 unit and integration tests, 10 Electron journeys, two release-site browser viewports, package integrity checks, and two packaged-app MCP runs. Exported tests use no model at run time and remain review-required. Automatic repair, direct issue-tracker OAuth, and managed cloud execution remain out of scope. + ## Buffer: July 21 Only submission fixes, reliability fixes, and final upload verification. No new product scope. diff --git a/docs/verification.md b/docs/verification.md index d29dc8c..ee404fb 100644 --- a/docs/verification.md +++ b/docs/verification.md @@ -29,11 +29,22 @@ This file records what has actually been tested. Passing claims must include the | Computer execution | Verified | Vitest covers screenshot-first and batched action loops, the 25-turn boundary, 60-second provider requests, pending-safety-check stopping, coordinate and key validation, typed-text limits, and application activation. The pinned Apache-2.0 nut.js adapter runs inside TaskTape's stable macOS Accessibility identity. Electron journeys cover deterministic execution and regression evidence. On 2026-07-18, the paired live test used OpenAI computer use to replay the same interaction against broken and fixed browser targets, persisted both final screenshots, and correctly recorded failed and passed verdicts in history in 1 minute 42 seconds. | | Outcome verification | Verified | Unit tests cover schema-bound passed, failed, and inconclusive evaluation plus persistence. A live evaluator reliability gate classified five consecutive broken and fixed screenshot pairs correctly, for 10 successful GPT-5.6 evaluations in 18.6 seconds on 2026-07-18. The full paired desktop replay then independently confirmed the broken and fixed outcomes through the production evaluator. | | Agent MCP connection | Verified | Schema and integration tests cover local URL restrictions, selectors, key and wait bounds, action compilation, trace and screenshot persistence, console and network evidence, workflow creation, protocol discovery, and tool invocation. A real SDK MCP client completed the broken fixture through the embedded Electron server; the active session appeared in Settings, the saved check appeared in Checks, and an explicit run reached history. The packaged app independently exposed 12 tools, observed Uncategorized, finished the session, and returned the saved check on 2026-07-18. | +| Portable check handoff | Verified | Agent actions persist schema-validated replay commands. Unit tests cover Playwright compilation, Markdown reports, evidence lookup, and legacy-session refusal. The Electron MCP journey expands evidence counts, copies and reads the report from the system clipboard, exports TypeScript, and verifies its observed role locator plus visual assertion. The same journey passed against the unpacked 0.3.0 app and the app launched from the read-only mounted DMG on 2026-07-20. | | Scheduling and run logs | Verified | Vitest verifies local-time hourly, daily, weekday, and weekly calculation, persisted schedules, pause or resume, real due-run filesystem moves, trigger labels, and history loading. The Electron journey saves Monday at 09:00, confirms unattended-run consent, verifies `schedule.json`, forces a file task due, and confirms Manual and Scheduled history entries. A separate computer-task journey verifies the Scheduled inbox, natural cadence, last result, keyboard pause or resume, and Run now availability on 2026-07-17. | | Completion and playback | Verified | At 1180x760, Playwright confirms the recorded video is wider than 300px and completion exposes rerun, history, and New task actions. A separate empty-folder journey confirms Check again and New task remain available after saving a zero-action plan. Screenshots of voice intent, learned details, computer-task completion, scheduling, approval, completion, empty state, Scheduled, and history were visually inspected on 2026-07-17. | ## Latest automated run +On 2026-07-20, `pnpm check` passed formatting, lint, both TypeScript projects, 66 tests across 16 files, and the production build. `pnpm test:e2e` then passed all 10 Electron journeys in 29.2 seconds. The agent journey covered the newly generated Markdown report and Playwright test without making a model request. No paid model calls were used for this milestone. + +The `pnpm test:site` release check passed in headed Chromium at 1440 by 900 and 390 by 844. It verified the portable-check message and product image, all three stable download links, complete image decoding, no horizontal overflow, and no console errors. The missing favicon found during the first browser run was fixed before this passing result. Both full-page captures were visually inspected. + +The 0.3.0 arm64 production app, DMG, and ZIP built successfully with `pnpm package:mac`. The app contains Playwright Chromium 1228, reports version 0.3.0, and passed `codesign --verify --deep --strict` with its ad-hoc seal. `hdiutil verify` reported a valid DMG checksum. The MCP journey passed first against the unpacked production app and again after launching TaskTape directly from the read-only mounted DMG. Both journeys created an agent check, inspected its evidence, copied the report, and exported the Playwright source. The 300,263,493-byte DMG has SHA-256 `6a63aa5cb398226f18504a7c478218842cb1cc1466ee9046f86bfd7af6654710`. + +No Apple Developer ID identity is installed on the build Mac, so 0.3.0 is not Apple-signed or notarized. The ad-hoc signature provides bundle integrity but does not satisfy Gatekeeper distribution trust; users may need to approve the beta once through macOS. This remains the only known release-credential blocker. + +## Previous release + On 2026-07-19, `pnpm check` passed formatting, lint, both TypeScript projects, 62 tests across 15 files, and the production build. `pnpm test:e2e` then passed all 10 Electron journeys in 29.6 seconds. The earlier paid gates remain valid: `pnpm test:live:computer` passed the paired broken and fixed OpenAI-driven desktop replay, and the live evaluator reliability gate passed all 10 evaluations. No additional paid model calls were used for the public-release milestone. The first nine-journey Electron run on July 18 had one intermittent timeout while a synthetic MediaRecorder transitioned to the intent screen. That journey then passed twice consecutively in isolation, followed by a clean full run of all nine journeys in 20.9 seconds. No evaluator, execution, or persistence assertion failed during the timeout. @@ -42,4 +53,4 @@ The 0.2.0 arm64 production app, DMG, and ZIP built successfully with `pnpm packa The static landing page was served locally and exercised in Chromium at 1440 by 900 and 390 by 844. Both viewports loaded the real product images, had no horizontal overflow or console errors, exposed consistent stable DMG links, and copied both MCP commands with visible feedback. Full-page captures were visually inspected. The Vercel production URL returned 200 over HTTPS at both viewports. The complete 300,282,253-byte DMG was downloaded through the landing page's stable GitHub URL, matched the release SHA-256, and passed `hdiutil verify`. -No Apple Developer ID identity is installed on the build Mac, so 0.2.0 is not Apple-signed or notarized. The ad-hoc signature provides bundle integrity but does not satisfy Gatekeeper distribution trust; users may need to approve the beta once through macOS. This is the only known release-credential blocker. +No Apple Developer ID identity was installed on the build Mac, so 0.2.0 was not Apple-signed or notarized.