Skip to content
forge.yaml & pipeline files

Project manifest (forge.yaml) & pipeline files

FORGE writes a project’s layout into forge.yaml at the repository root. CORE only reads it, to verify each component separately. This page gives the manifest spec exactly as CORE’s validator (grpc/cmd/forgecheck/manifest.go) enforces it, and describes the narrow check CORE applies to RIVER pipeline files.

forge.yaml, spec v1

A FORGE project is one repository with several lanes. Each lane is a component folder. When a forged repository (web or pipeline, never forge itself) carries a forge.yaml, central verification ignores the repository’s topic profile and verifies every lane in its own folder. See Central verification.

version: 1
project: social-ops                  # the repo name (informational)
lanes:
  - id: source-mapping               # ^[a-z0-9][a-z0-9-]{0,38}$, unique
    name: Source mapping             # free text, <= 80 characters
    kind: pipeline                   # pipeline | web
    path: pipelines/source-mapping   # relative + clean, unique, never nested
    components: [source.postgres]    # catalog ids — informational, ignored

Fields

FieldRule
versionMust be the integer 1. A missing version is refused with “no version (want version: 1)”. A quoted "1" or a 1.5 is refused with “version … is not supported (want 1)”
projectInformational. Not checked
lanesAt least one lane, at most 50
lanes[].idMust match ^[a-z0-9][a-z0-9-]{0,38}$, and must be unique. It becomes the status context core/ci/<id>
lanes[].nameFree text, at most 80 characters
lanes[].kindpipeline or web
lanes[].pathA relative, clean folder inside the repo. Not empty, not absolute, no trailing /, no backslash, NUL or control character, no .. segment, and unchanged by path cleaning (no ./, // or trailing .). Paths must be unique, and no lane may sit inside another lane’s folder. "." is allowed only when there is exactly one lane
lanes[].componentsCatalog ids. Informational, ignored by the verifier

Unknown keys are ignored, at the top level and per lane, so FORGE can add fields before CORE learns about them. Anything else that is wrong makes the whole manifest invalid. Verification then posts core/ci = failure with the description forge.yaml is invalid: <first reason> and no per-lane status, because a half-read manifest could report on components FORGE did not mean.

The file itself must be:

  • a regular file. A symlink is refused as “forge.yaml is a symlink; it must be a regular file”;
  • no larger than 1 MiB;
  • a single YAML document. A second document is refused as “more than one YAML document”.

A two-lane example

This manifest passes forgecheck manifest. It was checked against the validator built from this repository:

version: 1
project: social-ops
lanes:
  - id: source-mapping
    name: Source mapping
    kind: pipeline
    path: pipelines/source-mapping
    components: [source.postgres]
  - id: dashboard
    name: Operations dashboard
    kind: web
    path: apps/dashboard
$ forgecheck manifest -file forge.yaml
lane source-mapping                           pipeline pipelines/source-mapping
lane dashboard                                web      apps/dashboard
forgecheck: forge.yaml is valid, 2 lane(s)

If neither folder exists yet, the manifest is still valid, and both lanes report “Nothing to verify yet”. FORGE declares a lane before its agent writes it.

What each lane kind must contain

KindVerified when the folder has…Otherwise
pipeline.dsl / .herd files (TOML-parsed) and/or Go packages of a module, meaning its own go.mod or the repo’s root go.mod above it (go build, go vet, go test ./...)Nothing to verify yet: no .dsl or .herd file and no Go package in <path>
weba go.mod in the folder (go build/vet/test ./...) and/or a package.json in the folder or in its web/ subfolder (npm ci, npm run build, and npm run lint when the package declares a lint script)Nothing to verify yet: no go.mod or package.json in <path>

Pipeline files (.dsl, .herd)

RIVER pipeline definitions are TOML files, features/*.dsl and herd/*.herd. CORE’s check on them is syntax only:

  • every *.dsl and *.herd file under the tree must parse as TOML 1.0 (github.com/pelletier/go-toml/v2);
  • the meaning of the keys ([metadata], [dag], [[dag.step]], [golden]) belongs to the shared RIVER library, which does not exist yet, so CORE does not check it;
  • .contract files are not checked, because no parser for that format exists yet;
  • the scan never enters .git, .github, node_modules, vendor, testdata, or any directory whose name starts with . or _.

This documentation gives no pipeline grammar beyond “valid TOML”, because CORE does not define one. A file that fails to parse is reported by file with its position:

::error file=bad.dsl::not valid TOML: line 1, column 5: toml: array is incomplete
ok  ok.herd
forgecheck: 2 pipeline file(s), 1 invalid; 0 Go package(s)
::error::1 of 2 pipeline file(s) not valid TOML, first: bad.dsl

An empty pipeline (no .dsl/.herd file and no Go package) is a failure with the description Nothing to verify yet: no .dsl or .herd file and no Go package. It is deliberately not a skip: a skipped check concludes success in GitHub Actions, and FORGE would show a pipeline with nothing in it as green.

See Verification profiles for the exact commands.