Skip to content
GitHub is the database

GitHub is the database

The backend is stateless about what is built. It creates repos, files briefs verbatim and reads status, but it never mirrors any of that somewhere it could drift. Three places hold state:

  • GitHub holds what is built: repos, work orders and verification.
  • FORGE’s appfs root holds the chats and the actor audit. See Storage and audit.
  • CORE holds sign-in.

Repositories

A project’s repository is private, created in the organisation (FORGE_GITHUB_ORG, default org-runink), and tagged with these topics:

TopicMeaning
forgedFORGE made this repo. ListApps and the imported chats read this topic.
forge-projectA multi-lane project that carries forge.yaml
forge-web / forge-pipelineOne topic per kind its lanes use. For a single-kind app this is the only record of its kind. A forged repo with no kind topic reads as web.

The first commit is one parentless commit made over the Git Data API (blobs, then tree, commit and ref). It replaces GitHub’s auto-init commit, and the default branch is force-moved to it. A project repo is always seeded from the embedded skeletons. Only the older single-app path (ForgeService.CreateApp) uses GitHub’s /generate instead, and only when a template repo is configured (FORGE_TEMPLATE_REPO or FORGE_TEMPLATE_REPO_PIPELINE). None is configured today.

Work orders

A brief is filed as an issue:

  • Title: forge: <lane name> — <first line of the brief, up to 60 characters>
  • Labels: forge, kind:<kind> and lane:<id>
  • Body: two HTML-comment headers, one visible line scoping the work to the lane’s folder, a --- separator, and then the brief verbatim:
<!-- forge-kind: pipeline -->
<!-- forge-lane: ingest path: pipelines/ingest -->
This brief owns the folder `pipelines/ingest/` (lane "Ingest"). Read `pipelines/ingest/AGENTS.md` for its stack contract, and change nothing outside that folder.

---

<the brief, exactly as approved>

FORGE works out which lane an issue belongs to from its lane: label first and its forge-lane header second. It recovers the brief exactly by taking everything after the first --- that follows the headers. An app from before projects has only the forge-kind header. Its issues all belong to its one lane.

Iterations

ListIterations is the MVP loop’s history for one app: one iteration per forge issue, open or closed, oldest first. The oldest is marked initial. Each iteration carries the core/ci status of the newest default-branch commit dated inside its window (from its issue to the next issue). That verification followed the brief. It was not necessarily caused by it, because GitHub links no commit to an issue. Reads are bounded: 500 issues, 100 commits, and at most 3 status reads per window and 20 per call. Past a cap an iteration reads as having no core/ci.

Reads are shared briefly and never guessed

Opening a chat calls GetChat and GetProject at the same time. Their shared GitHub reads happen once and are kept for 10 seconds. The studio shows when the oldest answer was read (“GitHub read HH:MM:SS”). Re-read from GitHub, Re-read now and Try again force a fresh read. Every approval reads fresh, failures are never cached, and every write drops the repo’s cached entries.

When GitHub cannot be read, the answer says so. The chat’s status_error or the list’s imports_error is set, the lanes are still drawn, and their status is absent, never guessed. GitHub’s own error text passes through unparaphrased. For example, Resource not accessible by integration is how an operator learns the App lacks a permission.

Sources

grpc/internal/ghforge/{ghforge,project,kind,iterations,coreci}.go; grpc/cmd/{hub_cache,iterations_server,chat_server}.go.