Skip to content

feat(server): add scheduled, ambient and background work - #6

Closed
SiavZ wants to merge 1 commit into
parity/05-quality-recordsfrom
parity/06-unattended-automation
Closed

SiavZ wants to merge 1 commit into
parity/05-quality-recordsfrom
parity/06-unattended-automation

Conversation

@SiavZ

@SiavZ SiavZ commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

Agents could only act while a user was driving a turn.

Add scheduled work, ambient work and host background jobs that activate
threads under unattended grants, with guarded idle starts and cancellation,
an automation MCP toolkit and startup recovery (migrations 060, 061).

Stack

Part 6 of 14. Based on parity/05-quality-records, so this diff shows only this layer. Merge in order.

Scope

41 files changed, 3701 insertions(+), 54 deletions(-). Adds migration 060_ScheduledWork 061_BackgroundJobs. Migrations are numbered to follow the stack order.

Verification

  • vp run typecheck passes for contracts, client-runtime, server, web, mobile and desktop at this commit, with the Effect-patched compiler.
  • Targeted vp lint (0 errors) and vp fmt --check pass on the changed files.
  • Focused suites pass for server orchestration, MCP, provider, persistence, auth, client-runtime, and web settings. One failure, ProviderRegistry > re-probes when settings change the codex binaryPath, also fails on untouched main on this machine (it finds /opt/homebrew/bin/brew).
  • Not exercised: live provider/account calls, browser, desktop or mobile UI passes.

Implemented with GPT 6.1 and Opus 5.5 agents coordinated by Jcode.

RetriggerConfidence Score: 1/5

The PR is not safe to merge until background-job execution and notification delivery, output pagination, and ambient grant changes are corrected.

Findings

  1. P1 Jobs use an old worktree ▶
  2. P1 Pre-start cancellation skips notifications ▶
  3. P1 Byte pages corrupt Unicode output ▶
  4. P1 Grant changes strand ambient cycles ▶
  5. P2 Blocked schedules miss deadlines ▶
  6. P2 New features lack usage guidance ▶
Fix with agent prompt
### Issue 1
apps/server/src/background/BackgroundJobs.ts:231-235
If a job is still pending when its owner changes worktrees, this code checks the owner’s current workspace but discards that result. It starts the command in the worktree saved at submission, so the command can read or modify the wrong checkout.

### Issue 2
apps/server/src/background/BackgroundJobs.ts:459-462
If a subscribed job is cancelled before its process starts, this branch marks it cancelled without delivering its terminal notification. The runner then exits because the job is terminal, so the notification or wake is delayed until a later startup reconciliation, if one occurs.

### Issue 3
apps/server/src/background/BackgroundJobs.ts:433-440
When a page ends inside a UTF-8 character, this code decodes the incomplete bytes on their own and advances the cursor past them. For example, reading `é` one byte at a time returns replacement characters on both pages, so a client following `nextCursor` cannot reconstruct the stored output.

### Issue 4
apps/server/src/orchestration/AmbientWork.ts:127-132
If an enabled ambient owner changes grants while a cycle is queued, this code leaves that schedule under the old grant. Validation blocks it because the grant no longer matches, but ambient work continues to treat the blocked schedule as active and never starts another cycle.

### Issue 5
apps/server/src/orchestration/ScheduledWork.ts:374-382
The timer only considers queued schedules. If a busy-wait schedule becomes blocked and no other event wakes the loop, its `latestStartAt` can pass without another check. It remains blocked instead of being marked failed, leaving its reported state stale until unrelated activity occurs.

### Issue 6
docs/user/project-settings.md:81-84
This change adds a consent warning for scheduling, ambient work, and background commands, but does not explain how users start or use those major features. The repository’s user-documentation directive requires a concise, task-oriented section explaining what each major feature does, how to start it, and anything unintuitive. That requirement needs to be satisfied before merging.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Summary

The PR adds durable scheduled and ambient agent activation, bounded host background jobs, an automation MCP toolkit, RPC contracts, and startup recovery.

  • Adds unattended-grant checks and guarded idle starts for scheduled work.
  • Persists job output and notifications across restarts.
  • Needs corrections to job workspace selection, cancellation notification delivery, output pagination, and ambient schedule lifecycle before merging.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Client[Client or MCP tool] --> Grant[Grant and capability checks]
  Grant --> Schedule[Scheduled work]
  Grant --> Job[Background job]
  Schedule --> Activation[Guarded thread activation]
  Job --> Process[Captured host process]
  Process --> Output[Persisted bounded output]
  Process --> Notification[Terminal notification]
  Notification --> Schedule
  Ambient[Ambient idle admission] --> Schedule
Loading

Reviews (1) · Last reviewed commit: "feat(server): add scheduled, ambient and..."

Agents could only act while a user was driving a turn.

Add scheduled work, ambient work and host background jobs that activate
threads under unattended grants, with guarded idle starts and cancellation,
an automation MCP toolkit and startup recovery (migrations 060, 061).
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL labels Oct 3, 2026
Comment on lines +231 to +235
yield* authority.authorize(job.ownerThreadId);
const captured = yield* wrap(
spawner.spawn(
ChildProcess.make(input.command, input.args, {
cwd: job.cwd,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Jobs use an old worktree If a job is still pending when its owner changes worktrees, this code checks the owner’s current workspace but discards that result. It starts the command in the worktree saved at submission, so the command can read or modify the wrong checkout.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/server/src/background/BackgroundJobs.ts
Line: 231-235

Comment:
**Jobs use an old worktree** If a job is still pending when its owner changes worktrees, this code checks the owner’s current workspace but discards that result. It starts the command in the worktree saved at submission, so the command can read or modify the wrong checkout.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +459 to +462
else
return yield* lock.withPermit(
update(job, { state: "cancelled", reason: "Cancelled before process activation." }),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Pre-start cancellation skips notifications If a subscribed job is cancelled before its process starts, this branch marks it cancelled without delivering its terminal notification. The runner then exits because the job is terminal, so the notification or wake is delayed until a later startup reconciliation, if one occurs.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/server/src/background/BackgroundJobs.ts
Line: 459-462

Comment:
**Pre-start cancellation skips notifications** If a subscribed job is cancelled before its process starts, this branch marks it cancelled without delivering its terminal notification. The runner then exits because the job is terminal, so the notification or wake is delayed until a later startup reconciliation, if one occurs.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +433 to +440
const piece = bytes.subarray(start, start + remaining);
if (piece.length === 0) break;
chunks.push({
cursor: row.cursor + start,
stream: row.stream,
text: new TextDecoder().decode(piece),
});
nextCursor = row.cursor + start + piece.length;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Byte pages corrupt Unicode output When a page ends inside a UTF-8 character, this code decodes the incomplete bytes on their own and advances the cursor past them. For example, reading é one byte at a time returns replacement characters on both pages, so a client following nextCursor cannot reconstruct the stored output.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/server/src/background/BackgroundJobs.ts
Line: 433-440

Comment:
**Byte pages corrupt Unicode output** When a page ends inside a UTF-8 character, this code decodes the incomplete bytes on their own and advances the cursor past them. For example, reading `é` one byte at a time returns replacement characters on both pages, so a client following `nextCursor` cannot reconstruct the stored output.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +127 to +132
yield* save(next);
if (!config.enabled && next.activeScheduleId)
yield* scheduled.cancel({
callerThreadId: config.callerThreadId,
id: next.activeScheduleId,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Grant changes strand ambient cycles If an enabled ambient owner changes grants while a cycle is queued, this code leaves that schedule under the old grant. Validation blocks it because the grant no longer matches, but ambient work continues to treat the blocked schedule as active and never starts another cycle.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/server/src/orchestration/AmbientWork.ts
Line: 127-132

Comment:
**Grant changes strand ambient cycles** If an enabled ambient owner changes grants while a cycle is queued, this code leaves that schedule under the old grant. Validation blocks it because the grant no longer matches, but ambient work continues to treat the blocked schedule as active and never starts another cycle.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +374 to +382
const rows = yield* wrap(
sql<{
due_at: string;
}>`SELECT due_at FROM scheduled_work WHERE state = 'queued' ORDER BY due_at LIMIT 1`,
);
const delay = rows[0]
? Math.max(0, Date.parse(rows[0].due_at) - (yield* Clock.currentTimeMillis))
: null;
if (delay === null) yield* Queue.take(wake);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Blocked schedules miss deadlines The timer only considers queued schedules. If a busy-wait schedule becomes blocked and no other event wakes the loop, its latestStartAt can pass without another check. It remains blocked instead of being marked failed, leaving its reported state stale until unrelated activity occurs.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/server/src/orchestration/ScheduledWork.ts
Line: 374-382

Comment:
**Blocked schedules miss deadlines** The timer only considers queued schedules. If a busy-wait schedule becomes blocked and no other event wakes the loop, its `latestStartAt` can pass without another check. It remains blocked instead of being marked failed, leaving its reported state stale until unrelated activity occurs.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +81 to 84
Scheduling, ambient work, and background commands require a separate,
administrator-issued grant for the owning thread. Tool access alone is not that
grant. Revoking a grant prevents further work under it; it cannot be replaced by
an agent choosing a more permissive grant.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 New features lack usage guidance This change adds a consent warning for scheduling, ambient work, and background commands, but does not explain how users start or use those major features. The repository’s user-documentation directive requires a concise, task-oriented section explaining what each major feature does, how to start it, and anything unintuitive. That requirement needs to be satisfied before merging.

Context Used: CLAUDE.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/user/project-settings.md
Line: 81-84

Comment:
**New features lack usage guidance** This change adds a consent warning for scheduling, ambient work, and background commands, but does not explain how users start or use those major features. The repository’s user-documentation directive requires a concise, task-oriented section explaining what each major feature does, how to start it, and anything unintuitive. That requirement needs to be satisfied before merging.

**Context Used:** CLAUDE.md ([source](https://github.com/siavz/t3code/blob/main/CLAUDE.md))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@SiavZ

SiavZ commented Oct 5, 2026

Copy link
Copy Markdown
Owner Author

Superseded. This stack was built on orchestration V1, which upstream deleted in pingdotgg#2829, so it can't be rebased. Upstream now covers this natively after the orchestration V2 rewrite (pingdotgg#2829): delegate_task and subagents, coordination through delegated tasks, and schedule_task with agent wakes and PR watching.

Closed by Claude Opus 5.5 in Jcode.

@SiavZ SiavZ closed this Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant