Skip to content

Sources

A Source is the polymorphic type used by every content-bearing field in a Workflow: Node instructions, workflow input.rules[] and input.context[], and issue/PR templates. A Source can be inline text, a local file path, or an HTTP(S) URL.

A Source takes one of four shapes:

# 1. Inline text (ergonomic default)
instruction: "Investigate the alert and classify each issue."
# 2. Relative or absolute file path
instruction: "./prompts/investigate.md"
instruction: "/opt/prompts/investigate.md"
# 3. HTTP(S) URL
instruction: "https://raw.githubusercontent.com/acme/playbook/main/investigate.md"
# 4. Tagged object form (escape hatch for ambiguous strings)
instruction: { inline: "./literal/slash/is-not-a-file" }
instruction: { file: "./prompts/investigate.md" }
instruction: { url: "https://x.test/playbook.md", type: "fetch" }

The string form classifies by prefix:

PrefixKindExample
./, ../, /file./rules.md
http://, https://urlhttps://x/y.md
anything elseinlineJust be helpful.

A conforming executor MUST resolve every Source in a workflow before any node executes. Resolution happens once per run. Results are available to the executor and recorded in the Execution Trace for auditability.

  • Inline — identity; the string is used as-is.
  • File — read via UTF-8 readFile. Relative paths resolve against the workflow file’s directory or the CLI’s cwd.
  • URLfetch() with 10s timeout, http/https only. Hosts that resolve to private, loopback, or link-local addresses (including the cloud metadata endpoint) are rejected, and every redirect hop is re-validated. Authorization header picked from .sweny.yml fetch.auth (per-host env-var-name mapping); SWENY_FETCH_TOKEN supplies the value but is only sent to hosts you configure in fetch.auth (no blanket fallback to arbitrary hosts).

Files and URLs are cached by canonical key (absolute path / full URL) for the lifetime of a single workflow run. The same URL referenced from multiple fields is fetched once.

When the CLI is invoked with --offline:

  • File and inline Sources resolve normally.
  • URL Sources MUST fail the run at load time with SOURCE_OFFLINE_REQUIRES_FETCH.

Each resolved Source produces a record:

FieldTypeDescription
contentstringThe resolved body.
kind"inline" | "file" | "url"Classification.
originSourceOriginal YAML value.
resolver"inline" | "file" | "fetch"Name of the resolver that produced content.
hashstringsha256 hex, first 16 chars.
fetchedAtstring?ISO 8601; present when kind === "url".
sourcePathstring?Absolute path; present when kind === "file".

Traces record these under trace.sources, keyed by field path (e.g. nodes.investigate.instruction).

CodeWhen
SOURCE_FILE_NOT_FOUNDFile path does not exist.
SOURCE_FILE_READ_FAILEDAny other filesystem error.
SOURCE_URL_UNREACHABLENetwork error, DNS failure, or timeout.
SOURCE_URL_HTTP_ERRORNon-2xx response.
SOURCE_URL_AUTH_REQUIREDHTTP 401 or 403.
SOURCE_OFFLINE_REQUIRES_FETCHURL Source encountered with --offline.
SOURCE_INVALID_TYPETagged URL with an unknown type (v1 accepts only "fetch").
SOURCE_INVALID_SHAPETagged object with zero or multiple tag keys, or extra keys.

Identifiers, display names, and routing logic (workflow.id, node.name, edge.when, skill.id, node.skills[]) remain plain strings. Routing conditions are workflow-local and tightly coupled to the DAG; they do not benefit from externalization.