Skip to content
Projects and lanes

Projects and lanes

One chat, one project, one repo

When you reopen a chat, both its conversation and its canvas come back. The chat record belongs to FORGE and is kept in FORGE’s encrypted store. What is built lives on GitHub:

  • One repository per project, named by the project’s machine name. The name must match ^[a-z][a-z0-9-]{1,38}$ (lowercase letters, digits and hyphens).
  • forge.yaml at the repo root lists the lanes. See the spec.
  • One folder per lane. By default this is pipelines/<id> for a pipeline lane and apps/<id> for a web lane.
  • The lanes’ work orders, as issues labelled lane:<id>.

Creating a chat (CreateChat) creates no repository. The repo is created by the first approval, which requires a project name and at least one lane brief.

Lanes

A lane is one component of the project. Its kind is either:

  • pipeline: a RIVER pipeline in the Runink integrator formats. The studio shows it as RIVER.
  • web: a web app with a Go and Fiber backend and a React frontend. The studio shows it as WEB.

A project can mix both kinds. The server enforces these rules with the same validator that writes forge.yaml (project.ValidateLanes):

RuleError text
At least one lanea project needs at least one lane
At most 12 lanesat most 12 lanes per project
Id matches ^[a-z0-9][a-z0-9-]{0,38}$lane 1: id "…" must be lowercase letters, digits and hyphens (at most 39)
Ids are uniquelane id "…" is used twice
Name is 1 to 80 characterslane "…": the name must be 1 to 80 characters
Kind is web or pipelinelane "…": kind "…" is not one of web, pipeline
Folder is clean and relativefolder "…" must be a clean relative path such as apps/site
No hidden or relative segmentsfolder "…" may not use hidden or relative segments
Folders are uniquetwo lanes use the folder "…"
Folders are never nestedfolder "…" is inside folder "…"
. (the repo root) only for a one-lane projectlane "…" lives at the repo root ("."), which only a one-lane project may do — move its code into a folder before adding lanes

A lane can hold at most 40 components. A component id matches ^[a-z0-9][a-z0-9._-]{0,63}$. Component settings must be non-secret: a setting key containing password, passwd, secret, token, apikey, api_key, private or credential is refused. Credentials belong in CORE › Connections.

Draft and committed

A lane is a draft until an approval commits it to forge.yaml on the default branch. The lane card shows DRAFT or IN REPO, and Lane.committed carries the same fact on the wire.

  • SetDraft saves the canvas (lanes, components, title, project name). It files nothing. The studio calls it only when something really changes: opening a chat or tapping a component sends nothing.
  • The first approval creates the repo in one commit. That commit holds forge.yaml, a README.md, and each lane’s kind skeleton under its folder.
  • A later approval that changes the structure rewrites forge.yaml and adds each new lane’s AGENTS.md through the GitHub contents API. A structure change with no brief is its own explicit approval (Approve lane changes).
  • After the repo exists, the project name no longer changes: the project's repo exists; its name no longer changes.

Apps from before projects

A forged repo with no forge.yaml still appears as a project. ListChats returns it as an imported, one-lane chat with id repo-<name>. Its single lane sits at ., has the repo’s kind, and reads all of the repo’s forge issues and the plain core/ci status. It becomes a stored chat the first time you add something to it. It must move its code into a folder before it can gain a second lane.

Sources

grpc/internal/project/manifest.go; grpc/cmd/chat_server.go (SetDraft, ApproveChat, syncManifest, projectFiles, secretish); grpc/internal/ghforge/ghforge.go (ValidName); grpc/api/proto/forge/v1/chat.proto.