Skip to content

App kinds

FORGE ships two kinds of component. Each kind has a stack contract: an embedded skeleton plus an AGENTS.md that tells the coding agent what it may and may not do. CORE has a verifier for each kind.

KindWire valueLane kindBrand in the studio
Web appAPP_KIND_WEBwebWEB
Data pipelineAPP_KIND_PIPELINEpipelineRIVER

APP_KIND_UNSPECIFIED is treated as web, for backward compatibility. CreateApp refuses any other unknown value with unknown app kind N.

Web

Stack: Go 1.26 with a Fiber v3 backend, and React 19 with TypeScript and Vite on the frontend. anime.js v4 handles motion. The Go server serves /api/* and the built frontend from one origin.

The skeleton, seeded under the lane’s folder, contains:

  • cmd/server/main.go: reads $PORT (default 8080) and binds [::];
  • internal/api/: router.go (New() registers every route, then static files and the SPA fallback), example health.go and notes.go handlers, and api_test.go;
  • web/: src/App.tsx, src/components/, src/api.ts, and src/motion/index.ts, which is the only file that imports anime.js;
  • Dockerfile, go.mod, go.sum, README.md and AGENTS.md.

The contract’s rules include: every route gets a test; no new dependencies unless the task cannot be done without one; no external AI SDKs or AI APIs, and no cloud SDKs; bind [::], never 0.0.0.0; same-origin only, so no CORS and no absolute API URLs; and never commit web/node_modules/ or web/dist/. The agent’s own verify step is:

go build ./... && go vet ./... && go test ./...
cd web && npm ci && npm run build && npm run lint

The web template has no auth, no database, no CI workflow, and no connector or inference client.

Pipeline (RIVER)

A RIVER pipeline is the pipeline side of the Runink River Sovereignty Server, written in the Runink integrator’s formats. The RIVER runtime does not exist yet, so the pipeline skeleton seeds only AGENTS.md. The folder starts empty on purpose, rather than carrying a private copy of a runner that the platform would later have to chase.

The contract tells agents to:

  • author pipelines in the Runink formats only: TOML features/*.dsl ([metadata], [dag], [[dag.step]], [golden]), Go-source contracts/*.contract, TOML herd/*.herd, step functions func X(io.Reader, io.Writer) error marked // @step:X, and golden cases under golden/<case>/;
  • stream, and never read a whole input into memory;
  • not invent runi, barnctl or herdctl commands, and not claim the pipeline deploys anywhere;
  • use no external AI or cloud SDKs.

CORE verifies a pipeline lane by TOML-parsing every .dsl and .herd file, then running go build, go vet and go test when the folder has Go packages. See Verification.

Both kinds: no CI in the repo

Both skeletons tell the agent that CORE verifies this repo centrally and reports the result as the commit status core/ci, and that it must not add workflow files. A test in FORGE (TestSkeletonsShipNoWorkflows) keeps .github/ out of both skeletons.

How the kind is recorded

The kind reaches the coding agent three ways, all on GitHub:

  1. Repo topics: forged plus forge-web or forge-pipeline. A project gets one topic per kind its lanes use, plus forge-project.
  2. Issue header: the work order’s body starts <!-- forge-kind: <kind> -->.
  3. Issue label: kind:web or kind:pipeline, beside forge.

Why .tmpl? go:embed refuses a directory that contains a go.mod. So the skeleton ships go.mod.tmpl and every Go file as .go.tmpl, which also stops FORGE’s own go build ./... compiling the skeleton. The suffix is stripped when the files are seeded.

Sources

grpc/internal/templates/ (templates.go, web/AGENTS.md, pipeline/AGENTS.md); grpc/internal/ghforge/kind.go; grpc/api/proto/forge/v1/forge.proto (AppKind); core’s CLAUDE.md (“Central verification”).