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.
| Kind | Wire value | Lane kind | Brand in the studio |
|---|---|---|---|
| Web app | APP_KIND_WEB | web | WEB |
| Data pipeline | APP_KIND_PIPELINE | pipeline | RIVER |
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), examplehealth.goandnotes.gohandlers, andapi_test.go;web/:src/App.tsx,src/components/,src/api.ts, andsrc/motion/index.ts, which is the only file that imports anime.js;Dockerfile,go.mod,go.sum,README.mdandAGENTS.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 lintThe 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-sourcecontracts/*.contract, TOMLherd/*.herd, step functionsfunc X(io.Reader, io.Writer) errormarked// @step:X, and golden cases undergolden/<case>/; - stream, and never read a whole input into memory;
- not invent
runi,barnctlorherdctlcommands, 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:
- Repo topics:
forgedplusforge-weborforge-pipeline. A project gets one topic per kind its lanes use, plusforge-project. - Issue header: the work order’s body starts
<!-- forge-kind: <kind> -->. - Issue label:
kind:weborkind:pipeline, besideforge.
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”).