Skip to content

Add Doozy provider to the agent daemon - #165

Open
parsakhaz wants to merge 1 commit into
mainfrom
daemon-doozy-adapter-164
Open

Add Doozy provider to the agent daemon#165
parsakhaz wants to merge 1 commit into
mainfrom
daemon-doozy-adapter-164

Conversation

@parsakhaz

@parsakhaz parsakhaz commented Aug 24, 2026

Copy link
Copy Markdown
Member

Closes #164.

Summary

  • Add a configurable WorkProvider boundary. WORK_PROVIDER defaults to linear, and the existing Linear gateway remains the default implementation.
  • Add a Doozy provider that polls ready todos matching a configured agent ID or daemon tag, claims one, feeds the existing durable planner or implementer loop, posts progress and terminal results, attaches the discovered PR, and resumes from later human chat messages.
  • Add hermetic coverage with a loopback Doozy API, temporary SQLite database, real temporary git worktree, and fake Claude executable. The test also proves that the Doozy API key is not exposed to the Claude child process.
  • Document Doozy configuration and the webhook-shape follow-up. Polling is the v1 ingress because Doozy has no documented AgentSessionEvent equivalent.

Provider interface sketch

interface WorkProvider {
  postAckActivity(...): Promise<PostResult>;
  postActivity(...): Promise<PostResult>;
  setSessionExternalUrl(...): Promise<PostResult>;
  turnPrompt?(...): string;
  mcpConfigJson?(config: Config): string;
}

The provider owns transport-specific delivery and prompt/config adaptation. Provider-specific ingress discovers work and translates it into the shared EventLog; SessionWorker, worktree creation, Claude session persistence, PR discovery, cleanup, and retry behavior remain shared.

Visual Overview

Before and after provider flow

Local Doozy staging evidence

Run date: 2026-08-24. API key redacted.

Result: the daemon claimed and completed a tagged staging todo, posted progress and terminal activity, attached the discovered PR, and shut down cleanly without starting a native Doozy run.

  • Started the complete daemon locally with WORK_PROVIDER=doozy, the documented staging API base URL, a clean target clone, temporary SQLite, and fake Claude.
  • Created staging todo b1c24483-4ac4-4739-98d0-28545f18488a tagged [daemon-agent].
  • Observed doozy_todo_claimed, followed by session_turn_completed with attempts: 1 and durationMs: 246.
  • Read the todo back from staging and verified status: done, activityEvidence: true, progressPosted: true, resultPosted: true, prAttached: true, and nativeRunStarted: false.
  • The proof harness used fake Claude and stored the synthetic PR URL https://github.com/dcouple/example/pull/42 as staging test data; it did not create or modify that pull request. The adapter behavior, HTTP requests, state transitions, and staging writeback were real.
  • Shutdown completed cleanly with no running turns.
  • Chat resume is also polled: when an existing todo-linked chat receives a later human message and the todo is returned to in_progress, the provider appends a prompted turn for the persisted Claude session. The hermetic Doozy test covers this path.

Verification

Using Node 22.23.1 and pnpm 11.10.0:

pnpm typecheck
$ tsc --noEmit

pnpm build
$ tsc

pnpm vitest run test/doozy.test.ts test/config.test.ts --maxWorkers=1 --testTimeout=20000
Test Files  2 passed (2)
Tests       21 passed (21)

The full-suite regression command was:

pnpm vitest run --exclude test/browser.test.ts --maxWorkers=1 --testTimeout=20000
Test Files  1 failed | 28 passed (29)
Tests       1 failed | 383 passed | 1 skipped (385)

The sole failure is the existing 40 ms browser-hang timing test, where the fake Claude process is terminated before it writes its environment fixture. Running that exact test against untouched origin/main at 35b0b3b produces the identical failure and stack location, so it is not introduced by this branch. No existing tests were edited.

The unchanged full-suite result relative to origin/main, plus passing typecheck and build, provides regression coverage for the existing Linear path. The Doozy-specific suite covers claim, progress, PR attachment, terminal delivery, and chat resume.

Scope guardrails

This PR does not change ops/, deployment configuration, VPS state, or production daemon state.

Automated QA

Status: passed for the PR scope at head e316f10.

  • Hermetic Doozy journey: claim, durable turn, progress, terminal result, PR attachment, chat resume, and child-credential isolation passed.
  • Regression checks: typecheck, build, OTLP proto check, and 21 focused Doozy/config tests passed.
  • Broad daemon suite: 382 passed and 2 skipped under the known-hang exclusion; one concurrency timing assertion failed under the 205-second loaded run and passed immediately in isolation. A separate full run had 383 passed, 1 skipped, and only the baseline 40 ms hang failure reproduced on origin/main.
  • Staging readback: todo b1c24483-4ac4-4739-98d0-28545f18488a remains done with progress, result, activity, and PR evidence, and no native Doozy run.
  • No UI or browser surface changed, so screenshot QA was not applicable.

Human review remains for the code and merge decision. Deployment, VPS changes, a real Claude turn, and a real PR creation were intentionally skipped.

@parsakhaz

Copy link
Copy Markdown
Member Author

Automated QA detail

Result: passed for the PR scope at e316f10a5a78b95240d620f4716221a0b96b6a11. No product defect was found.

Test identities

  • PR: Add Doozy provider to the agent daemon #165
  • Hermetic loopback todo: a0000000-0000-0000-0000-000000000164
  • Doozy staging todo: b1c24483-4ac4-4739-98d0-28545f18488a
  • Staging proof date: 2026-08-24
  • Credential: redacted and absent from repository, PR text, logs, and Claude child environment

Passed checks

Node 22.23.1
pnpm 11.10.0

pnpm typecheck
passed

pnpm build
passed

pnpm check:otel-proto
passed

pnpm vitest run test/doozy.test.ts test/config.test.ts --maxWorkers=1 --testTimeout=20000
Test Files  2 passed (2)
Tests       21 passed (21)

pnpm vitest run test/sessions.test.ts -t 'AC6: serializes one issue while different issues run in parallel' --testTimeout=20000
Test Files  1 passed (1)
Tests       1 passed | 91 skipped (92)

The hermetic Doozy test used a loopback HTTP API, temporary SQLite, fake Claude, and a real temporary git worktree. It proved claim, progress, PR attachment, terminal delivery, persisted-session resume from a later human chat message, and non-exposure of DOOZY_API_KEY to the child process.

Broad regression evidence

The broad run excluding the already documented 40 ms hang case completed with 382 passing and 2 skipped tests. One cross-issue concurrency timing assertion failed after sustained suite load, then passed immediately in isolation. An earlier full branch run completed with 383 passing, 1 skipped, and only the 40 ms hang failure. That exact hang test fails identically at untouched base commit 35b0b3b, including the same missing fixture and stack location. Existing tests were not edited.

External staging readback

The authenticated Doozy staging readback returned:

{
  "id": "b1c24483-4ac4-4739-98d0-28545f18488a",
  "status": "done",
  "progressPosted": true,
  "resultPosted": true,
  "prAttached": true,
  "activityEvidence": true,
  "nativeRunStarted": false
}

nativeRunStarted: false is expected. It proves the external daemon adapter handled the tagged todo without launching Doozy's native agent runner.

Intentionally skipped

  • No UI changed, so browser journeys and screenshots were not applicable.
  • No deployment, VPS, ops/, or production daemon action was performed.
  • The staging run used fake Claude and stored a synthetic PR URL as test data. It did not create or modify that pull request.
  • Human code review and merge remain with Parsa.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Doozy backend adapter for the agent daemon (hot-swap the work source)

1 participant