Skip to content

fix: decouple linear auto-create from dashboard - #235

Merged
centdix merged 2 commits into
mainfrom
linear-issue-auto-background
May 13, 2026
Merged

fix: decouple linear auto-create from dashboard#235
centdix merged 2 commits into
mainfrom
linear-issue-auto-background

Conversation

@centdix

@centdix centdix commented May 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Make Linear auto-create worktrees run as backend automation while webmux serve is running, regardless of whether the dashboard UI is open or active. The poll interval is now 60 seconds.

Changes

  • Remove the dashboard activity gate from the Linear auto-create monitor wiring.
  • Change the Linear auto-create poll interval from 15 seconds to 60 seconds.
  • Keep the monitor options minimal by removing the unused interval override and generic scheduler handle type.
  • Add focused backend tests for filtering, interval selection, UI-independent creation, duplicate prevention, and Linear fetch failures.

Test plan

  • bun install
  • bun run --cwd backend check
  • bun test backend/src/__tests__/linear-auto-create-service.test.ts backend/src/__tests__/linear-service.test.ts
  • bash scripts/run-with-isolated-tmux.sh bun run --cwd backend test
  • git diff --check

Generated with Claude Code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude

claude Bot commented May 13, 2026

Copy link
Copy Markdown

Claude finished @centdix's task in 2m 14s —— View job


Code review

Overall: clean, focused change. Decouples polling from dashboard activity, removes the now-dead isActive dependency, and adds DI seams that make the service genuinely unit-testable. New tests cover the two behaviors the PR description promises (poll interval, UI-independent creation).

A few notes, mostly minor.


1. intervalMs option is unused — speculative scope

linear-auto-create-service.ts:23-26

export interface LinearAutoCreateMonitorOptions<THandle = ReturnType<typeof setInterval>> {
  intervalMs?: number;
  intervalDeps?: SerializedIntervalDependencies<THandle>;
}

Nothing passes intervalMs — neither server.ts nor the tests. The constant LINEAR_AUTO_CREATE_POLL_INTERVAL_MS is the only thing the test asserts against, and the test injects timing through intervalDeps.scheduleEvery anyway. Per AGENTS.md ("only implement what was asked for. No speculative features"), I'd drop the intervalMs field and the corresponding options.intervalMs ?? … resolution at line 92. Fix this →

2. Generic THandle adds complexity for no real benefit

Same file, lines 23 and 88. startLinearAutoCreateMonitor<THandle = …> is generic but the default is the only thing ever used. Tests pass 1 (a number) which happens to satisfy the default. Keep it non-generic — and if you keep DI on the scheduler, SerializedIntervalDependencies<unknown> or fixing to ReturnType<typeof setInterval> is simpler.

3. Tests don't cover the dedup path

processedIssueIds is the core invariant preventing duplicate worktree creation, but no test exercises it. Easy add: call runAutoCreate twice (via two flushPromises cycles) on the same issue and assert createWorktree was called once.

Same for the error path — fetchIssues returning { ok: false, error } should result in no createWorktree call. One more focused test would round this out.

4. flushPromises is fragile

linear-auto-create-service.test.ts:48-52

async function flushPromises(): Promise<void> {
  for (let i = 0; i < 5; i += 1) {
    await Promise.resolve();
  }
}

Counting microtasks works today but breaks the next time someone adds an await inside runAutoCreate. Safer to await a deterministic signal — e.g., have the fake createWorktree resolve a Deferred and await it in the test.

5. Poll-rate trade-off worth checking

Before: 15s interval, gated on dashboard activity (only polled while UI was open in the last 15s).
After: 30s interval, always-on while webmux serve runs.

For an idle server overnight that previously made zero calls, this is now ~2,880 Linear GraphQL calls/day per project. Probably fine for Linear's limits, but worth a sanity check if multiple repos or a long-running deployment is in scope. If background-only is acceptable, a longer interval (60–120s) costs little perceived latency.

6. Removed LifecycleService import — minor

linear-auto-create-service.ts:4

Switching from LifecycleService to LinearAutoCreateLifecycleService (a narrow structural type) is good — only depends on the one method actually used. Same pattern with Pick<GitGateway, "listWorktrees">. Both are nice improvements that came along for the testability ride. 👍

7. PR description mentions check/test couldn't run locally

The unchecked boxes in the test plan are flagged for the human reviewer — CI should verify these before merge. The diff itself is clean against types I can see locally, but bun run --cwd backend check and bun run --cwd backend test need a green run.


Bottom line

Small, well-scoped fix that does what the description says. The main suggestion is to drop the unused intervalMs option and THandle generic to keep the API surface tight, plus add the missing dedup/error-path tests. Nothing blocking.

Checklist

  • Read PR diff and changed files
  • Analyze backend service changes
  • Review the new tests
  • Verify parity with CLI — config-driven, no CLI-specific changes needed
  • Post review feedback
    · Branch

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented May 13, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
webmux 2482c2e Commit Preview URL

Branch Preview URL
May 13 2026, 10:32 AM

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@centdix
centdix marked this pull request as ready for review May 13, 2026 11:44
@claude

claude Bot commented May 13, 2026

Copy link
Copy Markdown

Claude finished @centdix's task in 2m 18s —— View job


Code Review

The PR addresses the prior review feedback comprehensively. The change is small, well-scoped, and the test design is much improved over the first iteration.

  • Read PR diff and changed files
  • Analyze the service changes for correctness
  • Review the new tests
  • Verify CLI/frontend parity per AGENTS.md
  • Post review feedback

What's good

Prior review fully addressed:

  • intervalMs option dropped — LinearAutoCreateMonitorOptions is down to just intervalDeps.
  • The THandle generic is gone; service uses SerializedIntervalDependencies<unknown> and explicit startSerializedInterval<unknown>(…).
  • Two new tests cover the dedup invariant (linear-auto-create-service.test.ts:174-189) and the fetch-failure path (linear-auto-create-service.test.ts:191-203).
  • flushPromises (microtask-counting hack) replaced with a proper Deferred<void> resolved inside the fake fetchIssues (linear-auto-create-service.test.ts:212-228). Much more robust to future awaits.
  • Poll interval bumped to 60s — addresses the "always-on, no dashboard gate" trade-off.

Architecture: LinearAutoCreateLifecycleService (narrow structural type) + Pick<GitGateway, "listWorktrees"> + injectable fetchIssues give clean DI seams without leaking the full lifecycle/git surface. runLinearAutoCreateOnce exported for direct testing is the right call.


Observations (non-blocking)

1. Module-level processedIssueIds is shared mutable state

linear-auto-create-service.ts:29

const processedIssueIds = new Set<string>();

The set lives at module scope and survives across all callers. Two implications worth being explicit about:

  • Tests must resetProcessedIssues() in beforeEach (they do — good). But this is an implicit contract; future tests added by someone unfamiliar with the file could leak state.
  • Server restart clears the set. If a user manually deletes a worktree for an issue that's still in Todo state with the webmux label, the next webmux serve cycle will recreate it. Probably intentional (recovery), but worth confirming this is the desired UX — the branch-existence check in filterAutoCreateIssues is the only thing preventing duplicates post-restart, so it relies on the deleted worktree's branch also being gone.

Not a bug, just an invariant worth documenting in the comment above the set (already partially there) or moving the set into LinearAutoCreateDependencies for explicit ownership.

2. LinearAutoCreateLifecycleService.createWorktree return shape is wider than needed

linear-auto-create-service.ts:9-14

createWorktree(input: CreateLifecycleWorktreeInput): Promise<{
  branch: string;
  worktreeId: string;
}>;

runLinearAutoCreateOnce never reads the return value (linear-auto-create-service.ts:71-75). The structural type could be Promise<unknown> — that would also let the real LifecycleService evolve its return shape without breaking this consumer. Minor; matches the source signature today so equally defensible.

3. Redundant assertion in the interval test

linear-auto-create-service.test.ts:231-232

expect(scheduledInterval).toBe(LINEAR_AUTO_CREATE_POLL_INTERVAL_MS);
expect(scheduledInterval).toBe(60_000);

Asserting against both the constant and the literal 60_000 is belt-and-suspenders. Pick one: either keep the literal (asserts the contract documented in the PR description) or keep the constant (assert internal consistency). Both is redundant. Fix this →

4. processedIssueIds filter check happens before branch check

linear-auto-create-service.ts:41-42

if (processedIssueIds.has(issue.id)) return false;
return !existingBranches.some((branch) => branchMatchesIssue(branch, issue.branchName));

Subtle: an issue can be in processedIssueIds (server created its worktree this session) AND have no matching branch (user deleted it). The current order means we won't recreate it this session — only after a restart. That's likely correct behavior (don't fight the user's deletion), but it's worth a comment, since reading the filter you'd reasonably expect "if no branch matches, create one."

5. AGENTS.md parity check: no CLI surface needed ✓

This feature is config-driven (integrations.linear.autoCreateWorktrees) and starts automatically inside webmux serve — there's no CLI knob to toggle it, so the AGENTS.md "wire into CLI" rule doesn't apply. Frontend already has the toggle in settings UI (unchanged by this PR). Parity intact.


Bottom line

Ship it. The prior review's items are all in. The remaining notes are nits — test redundancy, comment-worthy invariants around module state, minor type-shape tightening. Nothing here blocks merge.
· Branch

@centdix
centdix merged commit 14fe259 into main May 13, 2026
3 checks passed
@centdix
centdix deleted the linear-issue-auto-background branch May 13, 2026 11:54
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.

1 participant