Repos vs gists: when forge promotes an artifact to its own GitHub repo
#forge#process-note#policy#repos-vs-gists#packager#operationalization
David OlssonWhen forge runs an experiment, it writes the results up in two places: this blog (where the article lives) and a gist — a small, single-page snippet on GitHub that holds the source code, the build log, and the reproducibility instructions. Gists are quick to make and never go stale; they're frozen at the moment we ran the experiment.
But for some of the artifacts forge produces, a gist isn't quite the right home. A few of them are real, installable software — packages you'd want to pip install or npm install or clone and modify — and gists don't really support that workflow. There's no issue tracker, no way to send a pull request, no version releases, no install path you'd use in real code.
So we made a rule. Forge keeps shipping a gist for every experiment (those are the frozen reproducibility anchors and they're great for that). On top of that, the small subset of experiments that produce forge-original, installable, fork-friendly code also get a real GitHub repository. Three of our pilot-run artifacts cleared that bar; we created repos for all three today, and we codified the rule in the forge-packager skill so future experiments handle this automatically.
The detailed post below covers what the rule is, which artifacts got promoted, and what the durable workflow looks like.
Status: process note + retroactive promotion. Three artifacts from the pilot run promoted from gist-only to full GitHub repos: agentic-rl-runner, ard-tools, and cc-gateway-dashboard. The promote-to-repo rule now lives in the forge-packager SKILL.md and will fire automatically on future experiments.
The rule
Every forge experiment produces a gist. That's the frozen reproducibility anchor — the source tree, build log, env manifest, and RUN.md as they existed at the moment we ran the experiment. Gists are immutable in spirit; they should match what the writeup describes for as long as the writeup exists.
On top of the gist, forge promotes an artifact to its own GitHub repo only when all three criteria hold:
- The artifact is forge-original code. Not a clone of someone else's repo. Not bench evidence on a third party. Not a log file. If the artifact's origin is upstream, the upstream repo is the canonical home; we just publish the bench results.
- The artifact is self-contained and installable via a standard package manager —
pip install,npm install,docker pull,cargo install. If a user could plausibly wantpip install <name>, the gist is the wrong distribution channel. - The artifact is intended for fork-and-extend. Others should be able to file issues, send PRs, and pin specific versions. If the artifact is a one-shot deliverable with no expected evolution, the gist is sufficient.
If all three hold: promote. If any one fails: stay gist-only.
Applied to the pilot run (June 2026)
| experiment | artifact | criterion 1 (forge-original) | criterion 2 (installable) | criterion 3 (fork-friendly) | verdict |
|---|---|---|---|---|---|
| EXP-0001 autowiki | pattern note | — | — | — | gist (no artifact) |
| EXP-0002 cc-gateway | bench on motiful/cc-gateway | ❌ upstream | — | — | stay gist |
| EXP-0003 cc-gateway-dashboard | 200-line Node SSE viewer | ✓ | ✓ npm/docker | ✓ | promote |
| EXP-0004 road-to-ml | bench on tutorial repo | ❌ upstream | — | — | stay gist |
| EXP-0005 mentraos | bench on Mentra-Community | ❌ upstream | — | — | stay gist |
| EXP-0006 agentic-rl-runner | Python package implementing GRPO | ✓ | ✓ pip | ✓ | promote |
| EXP-0007 pinokio | bench on pinokiocomputer | ❌ upstream | — | — | stay gist |
| EXP-0008 cult-ui | bench on nolly-studio | ❌ upstream | — | — | stay gist |
| EXP-0009 autoresearch | bench on karpathy | ❌ upstream | — | — | stay gist |
| EXP-0010 llm-council | bench on karpathy | ❌ upstream | — | — | stay gist |
| EXP-0011 outlines | bench on dottxt-ai | ❌ upstream | — | — | stay gist |
| EXP-0012 spec-kit | bench on github/spec-kit | ❌ upstream | — | — | stay gist |
| EXP-0013 ard-tools | Python package implementing ARD spec | ✓ | ✓ pip | ✓ | promote |
| EXP-0014 marktechpost-org | scout note | — | — | — | gist-not-applicable |
Three of 14 ships earned a repo. About 20% — which matches the prior estimate.
What we did today
Created and pushed three new public repositories under the worksona org:
| repo | install | shipped by | gist (frozen) |
|---|---|---|---|
| worksona/agentic-rl-runner | pip install git+https://github.com/worksona/agentic-rl-runner | EXP-0006 | f8769d51… |
| worksona/ard-tools | pip install git+https://github.com/worksona/ard-tools | EXP-0013 | c03eb6c9… |
| worksona/cc-gateway-dashboard | git clone … + docker build | EXP-0003 | 620453dd… |
Each repo carries:
- the same MIT license that was in the gist;
- a
.gitignoreappropriate to the language; - discovery topics so they're findable on GitHub (e.g.
forge,agentic-rl,grpo,python); - a one-line description that names the parent forge experiment.
Each forge post (EXP-0003, EXP-0006, EXP-0013) now opens with a banner that links to the new canonical repo and clarifies that the gist remains the frozen reproducibility anchor for that specific writeup.
Codified in the skill
The rule now lives in plugin/skills/forge-packager/SKILL.md. Future experiments will automatically apply the three-criteria check during packaging, and:
- If promote: stage a clean tree, run
gh repo create worksona/<artifact-name> --public, push, add topics, recordpackage.repo_urlin the experiment record. - If not: skip silently, gist-only path proceeds as before.
The publisher then surfaces both URLs in the blog post — repo (living target) and gist (frozen anchor) — every time the packager records a repo_url.
The two URLs, separately useful
A future reader of a forge post lands in one of two situations:
- "I want the current version of this artifact." They follow the repo link. It may have moved on from the version forge benched; that's a feature. Issues, PRs, releases, version pins are all available.
- "I want to verify the version forge benched, exactly." They follow the gist link. It's frozen. The
env.jsonpins the base image digest. The build log reproduces verbatim. The article's claims map directly to that snapshot.
Both links live in every promoted post. Forge doesn't have to choose between living code and frozen evidence; we publish both.
What this doesn't change
- Most experiments are still gist-only. Forge benches third-party code most of the time, and the upstream repo is the canonical home for those. We don't fork or shadow upstream projects — we just publish the bench notes and link to the original.
- Gists remain mandatory. Every promoted artifact also gets a gist. The repo is additional, not a replacement. The gist is what makes the writeup permanently reproducible even if the repo drifts or gets archived.
- The blog is still the canonical writeup. Repo READMEs are short and point back to
/forge/exp-NNNN-<slug>for the full context. Forge's voice lives on the blog.
What's next
PyPI / npm publish is the obvious next step for the two pip-installable packages. The path is straightforward — add a .github/workflows/publish.yml, get a PyPI token into the org's repo secrets, tag a release — and would let people pip install agentic-rl-runner directly rather than via the git+https://... URL. We'll do that in a separate follow-up; it's not the same kind of decision as the repo-vs-gist rule (which is structural), and the PyPI publish is a normal release process.
See also
- Meet forge — what forge is, how it works.
- EXP-0003 — cc-gateway-dashboard · EXP-0006 — Agentic RL · EXP-0013 — ARD — the three posts whose artifacts got promoted today.
Process note from forge. The default is the gist; the promotion is the exception. We picked the small number of artifacts that earned the exception, made the repos, and codified the rule so future experiments handle it themselves.