From 1206e0f2b244ad183706eba32c92c56d78036e7b Mon Sep 17 00:00:00 2001 From: Proactive Runtime Bot Date: Sat, 23 May 2026 09:30:26 +0200 Subject: [PATCH 1/3] docs(proactive-agents): rewrite cron/radio guards as positive conditions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cron and integration examples both used `if (a !== x || b !== y) return;` early-return guards. The mono font used in the rendered code block ligatures `!==` into a glyph where the `!` is nearly invisible — readers saw `event.source = 'cron'` and reasonably concluded the guard fires *on* cron rather than gating *for* it. Flipped both to `if (a === x && b === y) { ... }` so: - No `!==` to misrender. - The happy path is what reads naturally. - The intent is the same; the dispatch handler in the Combined example already uses this style. Co-Authored-By: Claude Opus 4.7 (1M context) --- web/content/docs/proactive-agents.mdx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/web/content/docs/proactive-agents.mdx b/web/content/docs/proactive-agents.mdx index 959c1a497..799a6ba2f 100644 --- a/web/content/docs/proactive-agents.mdx +++ b/web/content/docs/proactive-agents.mdx @@ -65,13 +65,14 @@ An integration in the persona has two independent roles: declaring `triggers[]` import { handler } from '@agentworkforce/runtime'; export default handler(async (ctx, event) => { - if (event.source !== 'cron' || event.name !== 'daily') return; - const digest = await summarizeOvernightShips(ctx); // your code - await ctx.slack!.post('ops-daily', digest); + if (event.source === 'cron' && event.name === 'daily') { + const digest = await summarizeOvernightShips(ctx); // your code + await ctx.slack!.post('ops-daily', digest); + } }); ``` -`event.name` matches `schedules[].name`. `event.occurredAt` is the firing timestamp (ISO 8601). +`event.name` matches `schedules[].name`. `event.occurredAt` is the firing timestamp (ISO 8601). The guarded `if` (rather than an early `return` on the inverse) keeps the happy path the thing you read — important once the same handler reacts to multiple listener kinds. ## Integration-based (radio listener) @@ -102,9 +103,10 @@ Fires when a Relayfile integration emits a normalized event. Use for "open a PR import { handler } from '@agentworkforce/runtime'; export default handler(async (ctx, event) => { - if (event.source !== 'github' || event.type !== 'issue.created') return; - const { owner, repo, number } = event.payload as { owner: string; repo: string; number: number }; - await ctx.github!.comment({ owner, repo, number }, 'Triage in progress — thanks for filing.'); + if (event.source === 'github' && event.type === 'issue.created') { + const { owner, repo, number } = event.payload as { owner: string; repo: string; number: number }; + await ctx.github!.comment({ owner, repo, number }, 'Triage in progress — thanks for filing.'); + } }); ``` From 31bbb1c03bad3c965007990acb6fd649fb745056 Mon Sep 17 00:00:00 2001 From: Proactive Runtime Bot Date: Sat, 23 May 2026 09:33:23 +0200 Subject: [PATCH 2/3] docs(proactive-agents): inline a concrete cron handler implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous version handwaved with `summarizeOvernightShips(ctx) // your code` — readers saw the dispatch shape but not what the inside of a handler actually looks like, which is where the interesting `ctx` surface lives. Replaced with a real ~18-line implementation that exercises: - ctx.persona.inputs for deploy-time config - ctx.harness.run for AI work over the Relayfile mount - ctx.slack.post for delivery (typed integration client) - ctx.log for structured logging visible in `deployments logs` - ctx.memory.save for cross-firing persistence Added a short bullet list immediately under the snippet annotating each `ctx` call so a first-time reader knows what they're looking at. Co-Authored-By: Claude Opus 4.7 (1M context) --- web/content/docs/proactive-agents.mdx | 31 +++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/web/content/docs/proactive-agents.mdx b/web/content/docs/proactive-agents.mdx index 799a6ba2f..66d10ec09 100644 --- a/web/content/docs/proactive-agents.mdx +++ b/web/content/docs/proactive-agents.mdx @@ -66,14 +66,41 @@ import { handler } from '@agentworkforce/runtime'; export default handler(async (ctx, event) => { if (event.source === 'cron' && event.name === 'daily') { - const digest = await summarizeOvernightShips(ctx); // your code - await ctx.slack!.post('ops-daily', digest); + const since = new Date(event.occurredAt); + since.setUTCHours(since.getUTCHours() - 24); + const dateStamp = event.occurredAt.slice(0, 10); + const channel = ctx.persona.inputs.DAILY_DIGEST_CHANNEL ?? 'ops-daily'; + + // Drive the harness over the Relayfile mount. Claude reads + // /github/repos///pulls/*.json directly and + // synthesizes the digest from PRs merged since `since`. + const { output, exitCode } = await ctx.harness.run({ + prompt: `Summarize GitHub PRs merged across the workspace since ${since.toISOString()}. Group by repo; include #number, author, one-line impact. Slack mrkdwn, no preamble.`, + }); + if (exitCode !== 0 || !output.trim()) { + ctx.log('warn', 'digest.empty-or-failed', { exitCode }); + return; + } + + await ctx.slack!.post(channel, `*Daily ship — ${dateStamp}*\n${output.trim()}`); + await ctx.memory.save(`Daily ship ${dateStamp} posted to #${channel}`, { + tags: ['daily-digest', `date:${dateStamp}`], + scope: 'workspace', + }); } }); ``` `event.name` matches `schedules[].name`. `event.occurredAt` is the firing timestamp (ISO 8601). The guarded `if` (rather than an early `return` on the inverse) keeps the happy path the thing you read — important once the same handler reacts to multiple listener kinds. +What each `ctx` call does in this example: + +- `ctx.persona.inputs.` — resolved deploy-time inputs (see [Inputs](#inputs)). +- `ctx.harness.run({ prompt })` — spawn the persona's configured harness (here `claude`) inside the sandbox. The harness sees the Relayfile mount and can read `/github/...`, `/slack/...`, etc. directly. Returns `{ output, exitCode, durationMs }`. +- `ctx.slack.post(channel, text)` / `ctx.github.comment(...)` — typed integration clients backed by the Relayfile writeback worker. Use them whenever you can; they're auth-managed and retryable. +- `ctx.log(level, message, attrs)` — structured logger; every line is forwarded to the gateway and visible via `agentworkforce deployments logs `. +- `ctx.memory.save(content, opts)` — persistent across firings when `memory.enabled` is set on the persona. See [Memory](#memory). + ## Integration-based (radio listener) Fires when a Relayfile integration emits a normalized event. Use for "open a PR when a Notion page lands", "comment on issues when X is mentioned", etc. From c79b96b63dd8131ae5dd3ab15587fe735b30b6ce Mon Sep 17 00:00:00 2001 From: Proactive Runtime Bot Date: Sat, 23 May 2026 09:41:04 +0200 Subject: [PATCH 3/3] docs(proactive-agents): clarify ctx.harness.run return shape vs example CodeRabbit on #953: the prose said `Returns { output, exitCode, durationMs }` but the example only destructured `{ output, exitCode }`. Reader could think they had to handle durationMs. Tightened the prose to list all fields including `usage?` (matches the HarnessRunResult shape in @agentworkforce/runtime) and added an explicit "destructure the fields you need" so the example's partial pull is clearly intentional. Co-Authored-By: Claude Opus 4.7 (1M context) --- web/content/docs/proactive-agents.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/content/docs/proactive-agents.mdx b/web/content/docs/proactive-agents.mdx index 66d10ec09..ededaee65 100644 --- a/web/content/docs/proactive-agents.mdx +++ b/web/content/docs/proactive-agents.mdx @@ -96,7 +96,7 @@ export default handler(async (ctx, event) => { What each `ctx` call does in this example: - `ctx.persona.inputs.` — resolved deploy-time inputs (see [Inputs](#inputs)). -- `ctx.harness.run({ prompt })` — spawn the persona's configured harness (here `claude`) inside the sandbox. The harness sees the Relayfile mount and can read `/github/...`, `/slack/...`, etc. directly. Returns `{ output, exitCode, durationMs }`. +- `ctx.harness.run({ prompt })` — spawn the persona's configured harness (here `claude`) inside the sandbox. The harness sees the Relayfile mount and can read `/github/...`, `/slack/...`, etc. directly. Returns `{ output, exitCode, durationMs, usage? }` — destructure the fields you need. - `ctx.slack.post(channel, text)` / `ctx.github.comment(...)` — typed integration clients backed by the Relayfile writeback worker. Use them whenever you can; they're auth-managed and retryable. - `ctx.log(level, message, attrs)` — structured logger; every line is forwarded to the gateway and visible via `agentworkforce deployments logs `. - `ctx.memory.save(content, opts)` — persistent across firings when `memory.enabled` is set on the persona. See [Memory](#memory).