Skip to content

refresh(writing-relayflows): correct guidance for @relayflows/surface@2.0.16 - #106

Merged
kjgbot merged 4 commits into
mainfrom
refresh-writing-relayflows-2-0-16
Sep 18, 2026
Merged

kjgbot merged 4 commits into
mainfrom
refresh-writing-relayflows-2-0-16

Conversation

@kjgbot

@kjgbot kjgbot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The writing-relayflows skill was last verified against a pre-release source build (86a2ec2) and predates most of the v2 surface's current shape (published npm was still on 2.0.8 when it was written). Refreshed directly from the shipped @relayflows/surface@2.0.16 / @relayflows/sdk@2.0.16 .d.ts files, plus real flows check/flows run evidence gathered while building AgentWorkforce/flows-cookbook.

What changed

  • f.done()'s closed set is 6 values, not 4. needs_human/declined were missing entirely, and the human-approval example wrongly showed f.done('canceled') for a declined "no" — canceled is kernel-only.
  • Predicate .gate((v) => …, reason) is refused at runtime (unsupported_gate) — reproduced live on two real example flows in AgentWorkforce/flows (pr-review-pipeline, dependency-upgrade-bot). The old skill showed only this broken form as the TypeScript gate API. Corrected to the config-object form (subprocess_gate, regex_match, word_count_bounds, references_input) that actually runs, with flows#449 noted as the pending fix for predicate gates.
  • flows.json's real accepted keys: cli, executors, models, mcp, deploy — confirmed by both the shipped type and a live config_invalid refusal. The old skill only documented three of these and didn't warn that tools/budget (real FlowHeader fields) are invalid there.
  • New sections: parallel agents via Promise.all (documented, first-class per docs/SURFACE.md, with the process-wide Promise.all replacement disclosure), the transport: 'relay' dispatch option and what it actually is (task dispatch + durable receipt, not live agent-to-agent chat — worded carefully so it isn't overclaimed), the --data-dir gotcha (a flow's own git clean can delete the daemon's default data dir mid-run — this cost real debugging time building the cookbook), flows deploy/deployments/undeploy/schedule, and a currently-open flows run --cloud bug for TS flows (flows#461).
  • Helpers are a generated 40+ provider namespace (Slack, GitHub, Linear, Notion, Salesforce, Postgres, S3, …), not just Slack. Also documented that "tools" means two unrelated things depending on where you write it (FlowHeader.tools = helper intent; YAML FlowSpec.tools = filesystem grants) — an easy first mistake, made once while writing this refresh.

Verification

This file's own TypeScript and YAML code examples were re-run through flows check against the real @relayflows/surface@2.0.16 / @relayflows/sdk@2.0.16 packages — all pass. The predicate-gate failure, the flows.json schema refusal, and the --data-dir interaction were all reproduced directly, not asserted from memory.

Test plan

  • flows check the TypeScript and YAML snippets in this file against @relayflows/surface@2.0.16 (already done locally — see above)
  • Skim for any remaining reference to the old 86a2ec2/2.0.8 baseline

🤖 Generated with Claude Code


Note

Low Risk
Documentation-only change to a single skill file; no runtime or application code is modified.

Overview
Refreshes the writing-relayflows skill from a pre-release/2.0.8 baseline to @relayflows/surface@2.0.16 / @relayflows/sdk@2.0.16, with run-verified CLI behavior instead of source-only citations.

Correctness fixes: documents six f.done() reasons (needs_human, declined) and kernel-only canceled/budget_exceeded; warns that predicate .gate((v) => …) fails at runtime (unsupported_gate) and steers authors to config-object gates; expands flows.json to the real FlowsJson keys and invalid top-level fields; states f.human / f.dispatch and workspace under --local-agent typecheck but do not execute.

New guidance: 40+ Helpers and the two meanings of tools; parallel agents via Promise.all; transport: 'relay'; flows deploy / schedule, --local-agent, --data-dir vs git-clean gotchas, and flows run --cloud fixed in 2.0.17; updated Ctx (LlmOptions, run timeout, use header, budget in examples).

Removed/moved: large inline YAML spec blocks and outdated “human approval works” examples; verification baseline now points at flows-cookbook rather than duplicating transcripts.

Reviewed by Cursor Bugbot for commit 1a742e8. Bugbot is set up for automated code reviews on this repo. Configure here.

The skill was verified against a pre-release source build (86a2ec2) and
predates most of the v2 surface's current shape. Refreshed from the
shipped 2.0.16 .d.ts files directly, plus real flows run/check evidence
gathered while building AgentWorkforce/flows-cookbook:

- f.done()'s closed set is 6 values, not 4 (needs_human/declined were
  missing; the human-decline example wrongly used done('canceled')).
- Predicate .gate((v) => …) is refused at runtime (unsupported_gate) —
  reproduced on two real example flows. Only config-object gates
  (subprocess_gate, regex_match, word_count_bounds, references_input)
  actually run; the skill previously showed only the broken predicate
  form as the TypeScript gate API.
- flows.json's real accepted keys are cli/executors/models/mcp/deploy —
  tools/budget/etc. there are config_invalid, not FlowHeader fields.
- New: parallel agents via Promise.all (documented, first-class, with
  the process-wide Promise.all replacement disclosure), the
  transport: 'relay' dispatch option and what it actually is (task
  dispatch + durable receipt, not live agent-to-agent chat), the
  --data-dir gotcha when a flow's own git hygiene can delete the
  daemon's default data dir mid-run, flows deploy/deployments/undeploy/
  schedule, and a currently-open flows run --cloud bug for TS flows
  (flows#461).
- Helpers are a generated 40+ provider namespace, not just Slack; two
  unrelated things are both spelled "tools" depending on where you
  write it, documented explicitly since it's an easy first mistake.

Verified: this file's own TypeScript and YAML examples pass flows check
against @relayflows/surface@2.0.16 / @relayflows/sdk@2.0.16 for real.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 40046386-3c6e-413f-9b3c-aeb33c29c3c2


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Devin Review found 3 potential issues.

4 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment thread skills/writing-relayflows/SKILL.md Outdated
`f.human` is a durable, journaled approval gate — the run parks until the human answers, and resumes exactly where it left off. `f.dispatch` hands input to a named child flow and returns its typed result; the parent doesn't inline the child's steps. (Source: `docs/SURFACE.md` §2 rule 6 region, lines ~40-53 as of `origin/main@86a2ec2` — this snippet is cited, not independently re-run, since it needs a live daemon.)

`f.slack` is a separate helper namespace, and its real calling convention doesn't fit a plain `flow(name, async (f) => ...)` body: in the real source, `f.slack.reply(event, ...)` only appears inside a trigger handler registered via `.on(slack.mention('#exec'), async (f, event) => { ... })`, where `event` is the trigger's second callback argument — not something a step-based flow like the one above ever has in scope. Triggers and `f.slack` are out of this skill's scope (see **What this skill does NOT cover**); don't copy a bare `f.slack.reply(event, ...)` call into a `flow()` body like the one above, it will throw `event is not defined`.
`f.human` is a durable, journaled approval gate — the run parks (`needs_human` is the run-level signal a caller sees while waiting) until the human answers, and resumes exactly where it left off; nothing sits there blocking a thread, and the wait survives a restart exactly like a crash mid-step does. `f.dispatch` hands input to a named child flow and returns its typed result; the parent doesn't inline the child's steps.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Resident verbs fail at runtime

Calling f.human or f.dispatch under 2.0.16 throws unsupported_verb; the shipped executor lowers neither resident verb. The approval flow cannot complete.

Learn more

The shipped surface types expose both methods, but the 2.0.16 SDK executor implements each by throwing unsupported_verb. Type availability therefore does not establish executable support. The sample reaches f.human after its agent step and fails instead of parking for approval. If approval succeeded somehow, f.dispatch would fail identically.

Example: With ok intended to await Khaliq's answer, f.human(...) immediately rejects. No answer can produce declined or continue to garden/implement.

Recommended fix: Mark both verbs as type-only and unsupported by the 2.0.16 executor. Remove the runnable approval example, or replace it only after verifying a package version whose executor lowers both calls.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread skills/writing-relayflows/SKILL.md Outdated
const plan = await f.agent('planner', {
task: 'Research and plan: add OAuth2 support',
workspace: 'acme/api: readonly', // compiles to relayauth path scopes
workspace: 'acme/api: readonly',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Readonly workspace example is refused

The example's workspace ends in : readonly, which 2.0.16 rejects as unsupported_workspace_permission. The flow stops before requesting approval.

Learn more

The TypeScript surface accepts a workspace string, but the 2.0.16 executor explicitly rejects trailing : readonly and : readwrite annotations. No parser converts those annotations into permissions. Declarative YAML supports a separate permissions field, while this TypeScript call does not.

Example: workspace: 'acme/api: readonly' fails before the planner starts. workspace: 'acme/api' declares the surface without claiming an unenforced restriction.

Recommended fix: Use the bare workspace name in this TypeScript example. Direct readers needing enforced read-only access to the declarative YAML permissions field.

Suggested change
workspace: 'acme/api: readonly',
workspace: 'acme/api',

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread skills/writing-relayflows/SKILL.md Outdated
- `exit_code` — implicit default for `deterministic` steps. Not configurable; writing it explicitly is allowed and compiles to the same thing as omitting it.
- `output_contains` — step output (stdout tail, or the LLM value stringified) contains `value`.
- `json_schema` — step output validates against a JSON Schema (`boolean | Record<string, unknown>`). Used for structured LLM/agent output; `output?: JsonOutputSchema` on an `llm`/`agent` step is sugar that compiles to this.
- `subprocess_gate` — runs `command`, judged on its exit code, seeing the step's output via `FLOWS_INPUT`/`from_output`. The gate that actually works for "check a file the agent wrote," e.g. `{ type: 'subprocess_gate', command: 'test -s review.md' }`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Subprocess gates hide step output

subprocess_gate commands following this guidance read FLOWS_INPUT, but 2.0.16 exposes selected output only as INPUT. Such gates reject valid results.

Learn more

The SDK itself uses FLOWS_INPUT only inside its generated wrapper. That wrapper selects from_output, converts the value to text, and starts the author's command with INPUT in its environment. The later test -s <(cat) example also cannot consume the output because the command receives no stdin and runs under /bin/sh, where process substitution is not portable.

Example: A gate command node -e 'JSON.parse(process.env.FLOWS_INPUT)' receives undefined and fails. Reading process.env.INPUT instead sees the selected step output.

Recommended fix: Document INPUT as the author-command contract and from_output as the optional selection path. Replace the stdin/process-substitution example with a portable command that inspects $INPUT.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Devin's review on this PR flagged three issues; verified each directly
against the real CLI rather than trusting or dismissing the report:

- f.human and f.dispatch both typecheck and pass `flows check`, but the
  shipped 2.0.16 executor throws `unsupported_verb` for both at
  `flows run` time. Confirmed. Rewrote the "Human approval and dispatch"
  section to state this plainly instead of presenting a broken example
  as working guidance.
- workspace: 'acme/api: readonly' is refused (unsupported_workspace_permission)
  under --local-agent. Confirmed — but the review's suggested fix (drop the
  `: readonly` suffix) does NOT work either: a bare workspace value fails
  identically, because the local-agent worker holds no revision pins at
  all, not because of the permission annotation syntax. Corrected to say
  omit `workspace` entirely under --local-agent.
- subprocess_gate commands see the extracted value as $INPUT, never
  $FLOWS_INPUT (that's the SDK's own internal wrapper's env var, never
  exposed to the author's command) — confirmed by reading
  named-gate-lowering.js directly. Fixed the claim and the broken
  `test -s <(cat)` placeholder example with a real, run-verified one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@kjgbot

kjgbot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor Author

Verified all three of @devin-ai-integration's findings directly against the real CLI rather than taking them on faith — all confirmed as real bugs, pushed fixes:

  1. f.human/f.dispatch unsupported_verb — confirmed exactly as reported. Rewrote that section to state plainly that both typecheck and pass flows check but fail at flows run (unsupported_verb), cited flows#400 for f.human's tracking issue.
  2. Readonly workspace refused — confirmed, but the suggested fix doesn't actually work: I tested the bare workspace: 'acme/api' (no : readonly suffix) and it fails identically (unsupported_workspace_permission). The real cause is the local-agent worker holds no revision pins at all, not the permission-annotation syntax. Corrected to say omit workspace entirely under --local-agent.
  3. subprocess_gate env var — confirmed by reading named-gate-lowering.js directly: the author's command sees $INPUT; $FLOWS_INPUT is internal to the SDK's own generated wrapper and never exposed to the command. Fixed the claim and the broken placeholder example with one that's actually been run.

Thanks for the catch on all three — good example of why "the type accepts it" and "the executor runs it" are different claims, which is exactly the gap this skill exists to close.

kjgbot and others added 2 commits September 18, 2026 05:02
…ions)

Answers a real gap: the skill documented flows deploy's syntax but not
what has to already be true for it to succeed. An agent following just
the syntax would hit flow_repository_not_connected with no lead on what
to fix. Added: agent-relay cloud login is required first (shared with
the whole Cloud session, no separate flows login); the workspace needs
a GitHub App installation covering --repo and the --on provider must
be a connected integration, both checked before flows deploy activates
a listener (prepare-flow-deploy.ts in AgentWorkforce/cloud); and this
is a different concern from a local git/gh push credential, which
matters only for testing a flow's own git steps locally.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Caught right before merging: flows#461 (the flows run --cloud bug for
authored TS flows) was closed today by flows#462, a real CLI fix.
Re-verified directly against relayflows@2.0.17 rather than trust the
issue tracker alone: flows run --cloud --wait <flow.ts> --input now
submits successfully and returns a real run id, where 2.0.16 gave an
immediate HTTP 400 or misrouted into the declarative loader. Root
cause was a Surface version mismatch between Cloud and the CLI, badly
reported; the CLI side is fixed, so update rather than work around it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@kjgbot
kjgbot merged commit 9316e6d into main Sep 18, 2026
3 checks passed
@kjgbot
kjgbot deleted the refresh-writing-relayflows-2-0-16 branch September 18, 2026 13:19
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