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.yamlat the repo root lists the lanes. See the spec.- One folder per lane. By default this is
pipelines/<id>for a pipeline lane andapps/<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):
| Rule | Error text |
|---|---|
| At least one lane | a project needs at least one lane |
| At most 12 lanes | at 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 unique | lane id "…" is used twice |
| Name is 1 to 80 characters | lane "…": the name must be 1 to 80 characters |
Kind is web or pipeline | lane "…": kind "…" is not one of web, pipeline |
| Folder is clean and relative | folder "…" must be a clean relative path such as apps/site |
| No hidden or relative segments | folder "…" may not use hidden or relative segments |
| Folders are unique | two lanes use the folder "…" |
| Folders are never nested | folder "…" is inside folder "…" |
. (the repo root) only for a one-lane project | lane "…" 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.
SetDraftsaves 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, aREADME.md, and each lane’s kind skeleton under its folder. - A later approval that changes the structure rewrites
forge.yamland adds each new lane’sAGENTS.mdthrough 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.