Skip to content

feat(home-feed): instrument scheduler + sequence with emitFeedEvent [JARVIS-512]#25593

Merged
alex-nork merged 1 commit into
mainfrom
alex-nork/jarvis-512-instrument-producers
Apr 14, 2026
Merged

feat(home-feed): instrument scheduler + sequence with emitFeedEvent [JARVIS-512]#25593
alex-nork merged 1 commit into
mainfrom
alex-nork/jarvis-512-instrument-producers

Conversation

@alex-nork
Copy link
Copy Markdown
Contributor

@alex-nork alex-nork commented Apr 14, 2026

Summary

Third step of JARVIS-512, stacked on #25584 and #25586 (both merged). Wires emitFeedEvent into the first batch of background-job completion points so real assistant activity starts showing up in the home feed's activity log.

What lands

scheduler.ts — 4 emit points

Branch When dedupKey
notify mode, one-shot After notifyScheduleOneShot + completeOneShot schedule-notify-oneshot:<jobId>
notify mode, recurring After completeScheduleRun(ok) schedule-run:<runId>
execute mode, task After completeScheduleRun(ok) in the run_task: branch (gated on !job.quiet) schedule-run:<runId>
execute mode, message After completeScheduleRun(ok) in the plain-message branch (gated on !job.quiet) schedule-run:<runId>

Execute-mode emits mirror the existing notifySchedule gating on !job.quiet — users who opted out of push notifications also opt out of activity log entries. Notify-mode emits always fire because notify-mode IS notification by design.

A small emitScheduleFeedEvent helper at the bottom of the file wraps emitFeedEvent with fire-and-forget .catch handling so a schema error or writer hiccup can never interrupt the 15s scheduler tick.

sequence/engine.ts — 1 emit point

After recordSend in processEnrollment, keyed on sequence-step:<enrollmentId>:<stepIndex>. Placed after the step.requireApproval gate so draft-only steps (which pause without sending) don't show up in the log — only actual sends produce a real signal.

Loop / runaway-cost audit (per the concern raised in-thread)

  • No feedback loop: no daemon-side subscriber to home_feed_updated was found in the audit (only the macOS client consumes it). Emits can't trigger more background work.
  • No unguarded high-frequency emit: scheduler ticks every 15s but only emits when a schedule actually fires (bounded by user-defined RRULE/cron cadence). Sequences are capped at 10 enrollments/tick.
  • No LLM cost multiplier: the reflection/gmail-digest producers were deliberately not instrumented — they're the activity log's consumers, not sources. Double-emitting there would spam SSE and inflate the per-source action cap without adding signal.
  • Bounded volume: per-source action cap (MAX_ACTIONS_PER_SOURCE = 20, from feat(home-feed): action append semantics + per-source cap [JARVIS-512] #25584) keeps total source: "assistant" actions at ≤20 most-recent. Older emits fall off deterministically.

Deferred to follow-ups (with specific reasons)

  • Gmail watcher event processing (watcher/engine.ts:222) — loop-risk: the LLM event-processing path could itself re-trigger watcher polling if we're not careful. Needs its own audit.
  • Manual task-runner completion (task-runner.ts:102) — needs a dedup story with the scheduler layer so schedule-triggered tasks don't double-emit (scheduler AND task-runner both firing).
  • Skill runner completion — no single well-defined "skill done" hook in the codebase yet; instrumentation would be sprinkled and hard to reason about.

Testing

  • bun run typecheck — clean
  • bun run lint — clean
  • bun test src/home/ — 144/144 pass (regression check; no new tests in this PR — see below)
  • Manual smoke test on the branch: trigger a schedule and observe a feed entry land

No new unit tests in this PR. The scheduler and sequence modules don't have existing test harnesses (no src/schedule/__tests__, no src/sequence/__tests__) — standing one up just for these pass-through emits is significant scope creep. The emitFeedEvent helper itself has thorough unit coverage in #25586. Manual smoke after merge will verify end-to-end wiring.

🤖 Generated with Claude Code


Open with Devin

…JARVIS-512]

First batch of background-job instrumentation for the home activity
log. Wires emitFeedEvent into the safe, bounded completion points
where a real user-visible signal lands:

scheduler.ts — 4 emit points:
  - notify-mode one-shot success (dedupKey: oneshot:<jobId>)
  - notify-mode recurring success (dedupKey: schedule-run:<runId>)
  - execute-mode task success (dedupKey: schedule-run:<runId>)
  - execute-mode message success (dedupKey: schedule-run:<runId>)
  Execute-mode emits are gated on !job.quiet to match the existing
  notifySchedule behavior; notify-mode emits always fire since
  notify-mode IS a notification by design.

sequence/engine.ts — emit after recordSend on each successful step
  (dedupKey: sequence-step:<enrollmentId>:<stepIndex>). Skipped for
  requireApproval draft steps — those pause without sending, so
  there's no real signal yet.

All emits use source="assistant", are fire-and-forget via .catch so
a writer hiccup can never interrupt the 15s scheduler tick, and key
on the schedule-run / enrollment-step identifier so each run is a
distinct activity-log entry (the writer's per-source cap bounds
total volume).

Deferred to follow-up:
  - Gmail watcher event processing (loop-risk if the LLM-processing
    path re-triggers work)
  - task-runner direct (manual non-schedule task runs) — needs a
    dedup story with the scheduler layer to avoid double-emits
  - skill runner completion — no single well-defined hook to latch
    onto yet

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

@alex-nork alex-nork merged commit d269c3d into main Apr 14, 2026
12 checks passed
@alex-nork alex-nork deleted the alex-nork/jarvis-512-instrument-producers branch April 14, 2026 19:17
Jasonnnz pushed a commit that referenced this pull request Apr 15, 2026
…JARVIS-512] (#25593)

First batch of background-job instrumentation for the home activity
log. Wires emitFeedEvent into the safe, bounded completion points
where a real user-visible signal lands:

scheduler.ts — 4 emit points:
  - notify-mode one-shot success (dedupKey: oneshot:<jobId>)
  - notify-mode recurring success (dedupKey: schedule-run:<runId>)
  - execute-mode task success (dedupKey: schedule-run:<runId>)
  - execute-mode message success (dedupKey: schedule-run:<runId>)
  Execute-mode emits are gated on !job.quiet to match the existing
  notifySchedule behavior; notify-mode emits always fire since
  notify-mode IS a notification by design.

sequence/engine.ts — emit after recordSend on each successful step
  (dedupKey: sequence-step:<enrollmentId>:<stepIndex>). Skipped for
  requireApproval draft steps — those pause without sending, so
  there's no real signal yet.

All emits use source="assistant", are fire-and-forget via .catch so
a writer hiccup can never interrupt the 15s scheduler tick, and key
on the schedule-run / enrollment-step identifier so each run is a
distinct activity-log entry (the writer's per-source cap bounds
total volume).

Deferred to follow-up:
  - Gmail watcher event processing (loop-risk if the LLM-processing
    path re-triggers work)
  - task-runner direct (manual non-schedule task runs) — needs a
    dedup story with the scheduler layer to avoid double-emits
  - skill runner completion — no single well-defined hook to latch
    onto yet

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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