Skip to content

flows: typed helper-namespace codegen from relayfile adapter manifests #324

Description

@kjgbot

Typed helper-namespace codegen from relayfile adapter manifests

Problem

Slice B (merged as PR#314) established the runtime pattern for f.slack — a
helper namespace on Ctx that lowers each verb to an existing agent+effect
kernel primitive. It ships one canonical provider, and the argument types on
that provider are hand-written in packages/surface/src/slack.ts.

For the other 50 providers to inherit the same "wrong-channel-name is a compile
error" ergonomics, each helper's TypeScript surface has to reflect the real
verb signatures of its adapter. Hand-writing 50 of them is not the plan, and
without types every f.notion.appendBlock({ paret: … }) typo is a runtime
crash instead of a red squiggle.

Scope

Build-time codegen. Every generated file is types-only sugar; runtime dispatch
still flows through B's slack.ts-shape helper.

Deliverables:

  1. scripts/generate-helpers.mjs — walks
    /Users/khaliqgant/Projects/AgentWorkforce/relayfile-adapters/packages/*/
    and for each provider consumes:

    • <provider>.mapping.yaml when present (github, notion — the canonical
      mapping form) for the write-path verbs;
    • discovery/ when present (slack, github, linear, notion) for
      resource-level read/list surfaces;
    • The hand-tuned ergonomic client at
      relayfile-adapters/packages/relay-helpers/src/<provider>.ts for the
      top-provider set that already has one (slack, github, linear, telegram,
      reddit) — reused verbatim so we don't regress on their bespoke argument
      shapes;
    • Otherwise the uniform generated shape from
      relay-helpers/src/generated/clients.ts (providerClient<'<name>'>).

    Output: packages/surface/src/helpers/<provider>.ts — one file per
    provider, exporting a typed helper class/interface plus the verb signatures.

  2. packages/surface/src/helpers/index.ts — re-exports every generated helper
    and a Helpers type union that Ctx widens against.

  3. packages/surface/src/context.ts — the existing Ctx gains a generated
    index-signature over declared helpers, so f.<provider> resolves and is
    type-checked. Runtime lookup remains B's dispatcher.

  4. Regeneration guard: packages/surface/scripts/check-generated-helpers.mjs
    is added to the surface package's bun run typecheck:regressions chain so
    drift between the checked-in generated files and a fresh regen fails CI.

  5. Docs — a short packages/surface/src/helpers/README.md explaining
    "generated, do not edit by hand; re-run npm run gen -w @relayflows/surface after bumping the adapter version".

Zero runtime cost. Zero net new StepKinds. The kernel never sees these files.

Acceptance evidence

  1. f.slack.post('#test', 'hi') — typechecks (regression against B's shape).
  2. f.slack.post(42, 'hi') — TS compile error, channel requires string.
  3. f.github.createIssue({ title: 'x', body: 'y', repo: 'foo/bar' }) —
    typechecks; a wrong key (e.g. bodyy) is a compile error.
  4. f.notion.appendBlock — typechecks against notion's mapping YAML; a wrong
    key is a compile error.
  5. f.linear.createIssue — typechecks; wrong key = compile error.
  6. f.stripe.createInvoice — typechecks (uniform generated shape, since
    stripe has no ergonomic hand-tuned client in relay-helpers).
  7. f.notarealprovider.anything — TS compile error, notarealprovider is not
    a key on the generated helper union.
  8. Snapshot-test: the checked-in
    packages/surface/src/helpers/{slack,github,notion,linear,stripe}.ts
    files match what a fresh scripts/generate-helpers.mjs produces (drift
    guard).
  9. YAML dialect is unchanged in this PR (declared out of scope; slice B and
    the runtime dispatcher own the YAML expansion story).
  10. packages/sdk and packages/surface typecheck + tests remain green.

Non-goals

  • Runtime helper dispatch changes — B (PR#314) already ships that path; N only
    adds types.
  • YAML dialect helper verbs (slack:, notion: compile-time expansion in
    YAML) — later slice.
  • All 51 providers in this PR. Ship the pattern with five: slack,
    github, notion, linear, stripe. The generator must be complete
    enough to emit the remaining 46 in a follow-up PR without further design.
  • Auto-regenerating on every relayfile bump — a manual npm run gen
    invocation plus CI drift-check is enough. A cron/hook is a follow-up.
  • Provider auth / mount discovery — helpers stay declarative; the runtime
    path (B) already resolves credentials at preflight.

Files touched

  • scripts/generate-helpers.mjs — new.
  • packages/surface/scripts/check-generated-helpers.mjs — new.
  • packages/surface/src/helpers/ — new dir; five generated files + an
    index.ts + a README.md.
  • packages/surface/src/context.ts — extend Ctx with helper namespaces.
  • packages/surface/package.json — add the gen script; add
    @relayfile/adapter-core (or the specific mapping-consuming dep) as a
    devDependency scoped to codegen only.
  • packages/surface/tests/helpers-typecheck-pass.test-d.ts — new
    typecheck-only test asserting the pass cases.
  • packages/surface/tests/helpers-typecheck-fail.test-d.ts — new
    typecheck-only test asserting the fail cases via // @ts-expect-error.
  • packages/surface/tests/helpers.snapshot.test.ts — snapshot test for the
    generated file contents.
  • docs/SURFACE.md — a short paragraph in §2 rule 3 pointing at the generated
    index.

Files to read for context

  • docs/SURFACE.md §2 rule 3 (line 65-82).
  • packages/surface/src/context.ts, packages/surface/src/slack.ts (post-B).
  • /Users/khaliqgant/Projects/AgentWorkforce/relayfile-adapters/packages/{slack,github,notion,linear,stripe}/ —
    mapping.yaml + discovery/ + src/.
  • /Users/khaliqgant/Projects/AgentWorkforce/relayfile-adapters/packages/relay-helpers/src/{slack,github,linear,telegram,reddit}.ts —
    the bespoke ergonomic clients whose argument shapes should be preserved.
  • /Users/khaliqgant/Projects/AgentWorkforce/relayfile-adapters/packages/relay-helpers/src/generated/clients.ts —
    the uniform generated shape and header format we should mirror.
  • packages/sdk/scripts/make-cli-executable.mjs,
    scripts/pack-release.mjs — existing repo codegen conventions.

Test plan

  • Fresh regen of the five providers is byte-identical to what's checked in
    (snapshot test + CI drift check).
  • packages/surface: bun run test, bun run typecheck,
    bun run typecheck:regressions all green.
  • packages/sdk: npm run typecheck, npm run typecheck:tests,
    ./node_modules/.bin/vitest run tests/authored-flow.test.ts green (B's
    runtime path is unaffected).
  • Kernel workspace unchanged.

Dependency ordering

  • B (PR#314) is merged. Nothing else blocks this.
  • N does not conflict with the other open spec PRs (A/C/E/G) because the
    helpers/ directory is net-new. C's f.mcp work touches Ctx; a small
    merge on context.ts will be needed if C lands first.

PR

  • Title: feat(surface): typed helper namespaces via codegen from relayfile adapters (#<issue>)
  • Base: main.
  • Push-only pattern (gh CLI is 401 on finn-mini): agent pushes the branch
    over the SSH remote and DMs N pushed sha=<HEAD> to the lead; the lead
    opens the PR from a laptop.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions