Repository navigation
Gitstalk v0.2.0
Gitstalk is an agent-first git forge built on Cloudflare Workers and Artifacts. Agents push work as beans, Gitstalk checks and integrates them onto the stalk (trunk), and people decide only the changes that need a person. Gitstalk was formerly called Beanstalk.
v0.2.0 is the release entered in Cloudflare's "Build the next GitHub" competition. It went to production on 2026-10-15 and is live at https://gitstalk.io. Documentation is at https://gitstalk.io/docs. It covers 81 commits and 18 pull requests since v0.1.0 (2026-10-11).
Highlights
- Approval rules by kind of change.
.gitstalk/policy.ymlsorts each change into classes computed by code. Each class routes toauto,triage(Clef decides) orcard(a person decides). A bean is held only when Clef's probability is at least 0.65. A starter policy with a fallback class covers everything else. (#1, #2) - Approving held beans. Held beans now have a "Held for approval" panel, a violet Held badge and filter, an Approve button for owners and maintainers, and the MCP tool
bean_approvewith a self-approval guard. The bean's journey reads a hold as held, not as a failure. (#5, #12) - Agents can create repositories with MCP
repo_create,POST /api/reposor a first push to an address that does not exist yet. All three need the newcreatescope. (#9) - Code tab redesign. A folder tree sits beside the open file, with Go to file (
t), a resizable and collapsible sidebar, and a drawer on phones. A GitHub-style Code button holds clone, agent and "submit intent (soon)" tabs. (#8, #16) - All
.gitstalk/configuration is YAML. Checks move to.gitstalk/checks.yml.checks.tomlis still read, and Settings → Checks converts it in one click. (#7) - Caching on every layer. TanStack Query handles stale-while-revalidate in the browser. Public repositories get an edge cache (isolate, Cache API, KV), and private repositories get KV behind the access check. Derived data reads git objects through the engine's object cache, which cuts Artifacts reads by 45% in the replay. (#11, #14, #17)
- Email sign-in by six-digit code replaces magic links. Passkeys still work, and one sign-in flow now serves
/loginand/signup. - MCP Actions, secrets and automation tools. Agents can follow the runs their landings start, read job logs, dispatch, re-run, cancel and wait for runs, and set secrets with the opt-in
secretsscope. - Derived blame and server-side commits. MCP adds
why_lines,bean_commitandrepo_read.RepoDerivedDOkeepsbean_blameandpath_history. (#1) - Public docs rewritten, with 17 new pages, UI mocks, an on-this-page table of contents and a Runners page. (#4, #6)
Integration engine and Clef ranking
- Approval rules (
.gitstalk/policy.yml, strict YAML with a JSON Schema) (#1).- Code computes the classes: a comment-only lexer, docs, tests, lockfiles and paths.
- Each class routes to
auto,triageorcard. When several classes match, the strictest route wins. - Clef triages
triageclasses. A rule fallback answers when Clef cannot. A bean is held (card) only at p ≥ 0.65. - A held bean waits until a maintainer approves it.
- Fallback class: a class with
match: { otherwise: true }applies only when no other class matched (#2).- A file can have at most one fallback, and
otherwisecannot be combined withpaths,syntaxorclass. Errors give the line and column. - The built-in
policy-changeclass still wins for an agent's change. Changes made only inside.gitstalk/or.beanstalk/are never routed by the fallback.
- A file can have at most one fallback, and
- Starter policy (#2): comments, docs, tests and lockfiles are
auto. Migrations and auth paths arecard. Everything else istriagethrough aneverything-elsefallback. Settings → Approval rules opens on this policy when a repository has no file. - One Clef triage per change (#2). The triage fingerprint is now the classes, the policy text and the bean's own revision. It is no longer the moving stalk commit, so re-checks on newer stalks reuse the answer.
- Better triage input (#2). Clef also sees lines added and removed, the share of test lines, new and deleted files, and an optional class
description. Receipts record Clef'slatencyMs. - Holds do not cost rework rounds in repository engines (#2, #12). A hold's red is marked
awaitsApproval. Races still count holds as rework, as before. - Offline triage evaluation, with 42 labelled cases and
scripts/clef-triage-eval.mjs. It has not been run against the live model yet. (#2) - Read index for engine jobs (#2). When a job names a commit, it reads through the RunDO's object index. This covers the approval gate's diff and policy file, the checks file, culprit diffs, conflict ranges and
read-files. In the offline replay, Artifacts object reads fall from 1,380 to 780. Warm-up reads are counted separately asprefetch. - Clef for every ranking, and the infra meter counts object-cache hits and misses. (#1)
- Self-approval guard (#5). An MCP session cannot approve a bean pushed by the same person through the same OAuth client, a personal token, an SSH key, or an unknown or unrecorded credential, unless the person passes
confirm_owner_intent: true. The guard fails closed. The web button is always the person, so the guard never refuses it.
Web app
- Code tab (#16).
- A folder tree sits beside the open file. It opens down to the file, and a green "stalk" lights the indent guides on the path.
- The tree is 280 px by default and can be resized by dragging or from the keyboard.
Ctrl/Cmd+Bcollapses it. Width and collapse are remembered. - Go to file (
t) does fuzzy matching over every path. The filter box searches the folders you have opened. - The tree follows the WAI-ARIA tree view pattern, with full keyboard support.
- Below 1024 px the tree becomes a drawer.
- Each folder is read once per commit through
/api/repos/:owner/:repo/code/folderand/code/paths, and is cached as immutable.
- Code button (#8). This GitHub-style disclosure has three tabs:
- Clone: HTTPS URL with a copy button, SSH shown as "coming soon" until the deployment sets
SSH_HOST, and thegit cloneandgit push origin HEAD:bean/<name>lines. - Agent: install lines for Claude Code, Codex, Cursor, Gemini CLI and other MCP clients, built from the deployment's own URLs.
- Submit intent: marked Soon, with no backend yet.
- Deploy-token setup moved to Settings → Deploy tokens.
- Clone: HTTPS URL with a copy button, SSH shown as "coming soon" until the deployment sets
- Held bean journey and approvals (#5, #12).
- The bean page opens with a Held for approval panel. It shows why the bean was held, the revision, the reviewers and an Approve control with a note and a confirm step.
- The journey shows Held for approval, then Approved by @x, then Checking
<sha>again. - The Changes list has a violet Held badge and a
?show=heldfilter. - Push output says
HELD: … nothing in it needs fixinginstead ofRED:.
- Settings shell (#17).
- The repository header is drawn once in the repository layout, and the settings section nav in the settings layout. They survive tab and section changes, so the page no longer remounts.
- The Settings tab links straight to the viewer's first section, which saves a redirect round trip.
- Styled 404s for nested routes of a repository that is missing or unreadable, such as settings, changes, history, ask and run pages. These routes used to answer a bare
Not Found(#3). - Live pages without full re-renders.
- The Changes tab, bean page and run page follow the engine through TanStack Query and JSON routes (
/events,/pushed) instead ofrouter.refresh(). - Cancel and Re-run on a run update optimistically.
- The Changes tab, bean page and run page follow the engine through TanStack Query and JSON routes (
- Streaming and real statuses.
- Loading skeletons are added for repository, owner and org routes.
- Access checks, 404s and redirects are decided in layouts before streaming, so statuses are real.
- Bean pages stream their diff, and run pages stream the job's log.
- Prefetch on intent only. Every link prefetches on hover, focus or touch, never when it scrolls into view.
- Smaller client. The
diffpackage was dropped from the client bundle, and the merge panel loads lazily. The React Compiler was tried and reverted because it grew chunks by about 11%. - Fewer reads per page.
- The session is read once per request, and the header's name and picture come with it.
- Independent reads run in parallel.
- Home streams growth, activity and sessions behind Suspense.
- Smaller UI fixes.
- Crop dialog for picture uploads.
- Deployment host shown before handles.
- Tabbed agent install selector.
- The Automations empty state says "none yet".
CopyButtonannounces "Copied", and when the clipboard is refused it selects the text.
- vinext 1.1.0 and TanStack Query 5.104.1.
Accounts and sign-in
- Six-digit email codes replace magic links (shared-identity).
- A code is valid for ten minutes, can be used once and allows five tries. At most five codes are sent per address per hour.
- Codes are HMAC-hashed and bound to a ticket held by the browser.
- Mail goes through Cloudflare Email Service.
- One sign-in flow for
/loginand/signup: a passkey (with autofill where supported) or email, then a code step, a handle step for new addresses, and an offer to add a passkey. You can add or change your verified email address in Settings → Emails with a code. - Per-environment switch. Email sign-in turns on when
EMAIL_SENDER_DOMAINis set. Without it, sign-in is passkeys only. - Separate rate limits. Passkey options and passkey verification now use separate rate-limit budgets, so loading
/logina few times no longer uses up a person's sign-in attempts. - Sign-out clears caches. Signing out clears the browser's HTTP cache (
Clear-Site-Data: "cache") and the persisted query cache.
Repositories
- Create by agent (#9). MCP
repo_create({ name, owner?, visibility?, start?, description? })is registered only for sessions with thecreatescope. Visibility defaults to private, and the start defaults toempty(ortypescript-starter). - HTTP API (#9).
POST /api/repostakes a personal token withcreateand answers 201, 401, 403, 409, 422 or 429. - Push-to-create over HTTPS and SSH (#9).
- The push must carry exactly one branch (
main,master,stalkorbean/<name>), and the new repository is private. - Every later push follows the normal rules.
- Addresses left behind by a rename or transfer still resolve and never create a repository.
- The push must carry exactly one branch (
createscope (#9). It can be granted on the consent page, in Settings → Tokens and on the SSH key setup page. All creation paths share the web form's path, audit line (Via: web|repo_create|api|push) and limit of 20 creations per person per rolling hour.- New-address credentials (#9).
git_credentialsfor an address with no repository mints a credential bound only to that address.
Policy and config
- YAML for everything in
.gitstalk/:policy.yml,automations/*.yml, and nowchecks.yml(#7).- Keys:
image,run(an argv list, never a shell string),timeout-minutesortimeout-seconds,env(string values only) andprotected-paths. - Parsing is strict YAML 1.2: unique keys, and no anchors, aliases or merge keys. Errors give the line, column and field, with did-you-mean hints.
- Precedence:
.gitstalk/checks.yml, thenchecks.yaml, thenchecks.toml, then the same three files in.beanstalk/. Push lines name any shadowed file. CHECKS_JSON_SCHEMAis exported for editor autocomplete. It is not served at its URL yet.
- Keys:
- Settings saves commit directly as a bean (#1, #7).
- The rules editor, the automation builder and MCP
bean_commitshare one server-side commit core with compare-and-swap and idempotency. - Settings → Checks Convert to YAML writes
checks.ymland deletes the TOML file in one bean (checks-<short>,Via: Settings → Checks).
- The rules editor, the automation builder and MCP
Actions and automations
- MCP Actions tools. Sixteen task-shaped tools over the new
AgentActionsRpc:- Runs and logs: list workflows and runs, read a bounded job-log tail with grep, dispatch, re-run, cancel, and a bounded
actions_wait_for_runthat follows a bean to the stalk and then the runs it started. - Configuration: repository and organization secrets and variables.
- Automations.
- Every call checks the session's scope and the person's role.
- Runs and logs: list workflows and runs, read a bounded job-log tail with grep, dispatch, re-run, cancel, and a bounded
secretsscope. This new scope is opt-in and never ticked by default. The consent page warns that a secret value given to an agent passes through that agent and its model provider.- Default scopes. MCP now advertises
read collaborate writeby default. - Prompt and Shell harness labels for automations (#1). An area watcher template and a
pathsfilter on landing triggers. - Runners documentation (#6). Jobs run in a
standard-4container with Ubuntu 24.04. The Dynamic Worker isolate runner (runs-on: gitstalk-isolate) is documented as planned and not built.
Caching and performance
- Browser (#11, #17).
- TanStack Query keeps a freshness policy per data class, and the live event feed invalidates it.
- Immutable data is persisted in sessionStorage only, keyed by build and viewer. This applies to public repositories in #11 and to private ones in #17.
- Secrets, settings, sessions and moving state are never persisted.
- Public edge cache (#11). Trees, files and bean diffs are read through isolate memory, then the Cache API, then KV (
OBJECTS), then the gateway. Concurrent misses share one read. - Private repositories in KV (#17).
- Immutable objects are stored under
v1:priv:<engine>:<epoch>:<kind>:<sha>with a 7-day TTL and a 1 MiB cap. - They are reachable only through a
ReadGrantminted by the access check, and they never go to the Cache API or isolate memory. - Private responses stay
no-store.
- Immutable objects are stored under
- Purge by epoch (#11, #17). The repository record's
updated_atis part of every key, so a settings save, visibility flip, transfer or archive makes old copies unreachable at once. - HTTP (#11).
Cache-Controland ETags are set per route, and a 304 is only ever sent after the access check. - Derived reads via RunDO RPC (#14).
RepoDerivedDOreads commits, trees and blobs through the engine'sRunDO.readObjects, which serves them from the RunDO's SQLite object cache.- In the offline replay this cut Artifacts reads from 1,418 to 780 (−45%).
- If the engine fails, the reader falls back to Artifacts with one log line.
- The earlier per-colo Cache API tier was dropped.
- RunIndex moved to D1 (#15).
- The run list is now the forge D1
runstable, written by each RunDO inwaitUntil. - The kill switch is a
haltrow ingateway_flags. It is read uncached when a run is created or started. - The hourly sweep is a cron trigger (
17 * * * *). RunIndexstays as a read-only stub for one release so its data can be backfilled.
- The run list is now the forge D1
- Web performance harness and staging baselines under
research/perf.
MCP and plugin
- The plugin lives in its own repository: coop-collective/gitstalk-plugin, MIT. The project moved to the
coop-collectiveGitHub organization. - Custom hosts. The setup scripts the deployment serves read
GITSTALK_WEB, then the saved host, then the deployment's default. Install lines in the web app are built from the deployment's ownMCP_URLand origin, so self-hosted deployments work without editing (#8, #9). - MCP surface cleanup (#1, #5, #9).
- Duplicate tools are merged, and registration depends on scope, so each caller sees at most 30 tools.
- New tools:
repo_read(files, trees, grep),why_lines,bean_commit,bean_approve,repo_create. task_releaseis folded intotask_claim(…, release: true).
- Answer changes.
bean_statusandbean_waitreportheld: trueand anapprove_url.repo_statuslistsheld_for_approvalseparately fromsent_back. A cancelled run's wait says how to run it again.
Docs
- Public docs rewritten (#4).
- New pages: quickstart, concepts, account, MCP, ask, blame, cloud agents, approval rules, organizations, settings, secrets, environment variables, dependency cache, automation builder, limits and troubleshooting.
- 13 existing pages were rewritten.
- Every claim was checked against the code and labelled live, beta or planned. The sitemap lists every docs page.
- On-this-page navigation and a Runners page (#6).
- A table of contents is built from each page's headings, sticky from 1200 px and a disclosure below that.
- The Runners page documents the containers and Dynamic Workers.
- New sections.
- Design docs. Doc 18 (lessons from Scion), 32 (approval rules), 33 (server-side commits), 34 (derived data and blame) and 35 (caching) are new or updated, as are doc 19 (email sign-in) and doc 30 §13.7 (deploy repository overlay sync, #10).
Tooling and repository
- Licence. Apache-2.0, with
NOTICEandTRADEMARKS.md(#1). CLAUDE.mdremoved. Claude Code readsAGENTS.mddirectly. The.claude/skillssymlinks stay because Claude Code finds skills only there. (#13, #18)- Old marketplace redirect removed.
.claude-plugin/marketplace.json, which redirected to the plugin repository, is gone (#18). - Duplicate file removed. A byte-for-byte duplicate
CLAUDE.mdunderresearch/promowas replaced by an import (#13).
Operations
- Reusable deploy workflow (#10).
deploy.ymlgains aworkflow_calltrigger (inputsenvironment,ref), so the private deploy repository's sync can call it after it pushes overlay commits. - Email sign-in in provisioning. Provisioning onboards
EMAIL_SENDER_DOMAINto Email Sending when it is set, and smoke checks confirm that/loginshows the email form exactly when a domain is configured. - New admin route.
POST /v1/admin/runs/backfillcopies RunIndex into D1. It is idempotent and needs the admin token. TheGET,POSTandDELETE /v1/admin/haltandPOST /v1/admin/sweeproutes are unchanged.
Upgrade notes and breaking changes (self-hosters)
- Run provisioning before deploying.
- This release adds forge D1 migrations
0011_repository_creationsand0012_runs_and_flags, and identity D1 migrations0007_email_codesand0008_ssh_key_create. - It also adds the web KV namespace
OBJECTS(gitstalk-objects-<suffix>). Provision creates it, and until it exists config generation stops with "no id for kv gitstalk-objects-". - It also adds the web secret
EMAIL_SIGNIN_KEY, which provision generates if it is missing. - Run
pnpm env:provision <env>, thenpnpm env:deploy <env>. Migration 0012 must be applied before the new gateway serves.
- This release adds forge D1 migrations
- RunIndex backfill. After the deploy, the runs list shows only runs that changed since the deploy until the backfill runs. It runs at the next hourly cron (
:17), or immediately withPOST /v1/admin/runs/backfilland the admin token. It is safe to repeat. Check the gateway logs forrun index backfilled. The next release deletes theRunIndexclass, so confirm thatrun_index_backfillis set ingateway_flagsin every environment before upgrading past this one. - Edge cache scope. The Cache API layer does nothing on
*.workers.dev. The colo layer works only on a custom domain, and KV still works. - Plugin marketplace.
.claude-plugin/marketplace.jsonhas been removed from this repository, so installs from the olddisturbing/beanstalkmarketplace stop updating.- Re-add the plugin from
coop-collective/gitstalk-pluginwith the lines below.
- Checks config is YAML.
- New repositories and starters write
.gitstalk/checks.yml. .gitstalk/checks.tomlis still read, and the push suggests converting it.- If a tree has both files,
checks.ymlwins and the TOML file is ignored. runmust be an argv list, andenvvalues must be quoted strings.
- New repositories and starters write
- Email sign-in replaces magic links. The magic-link routes are gone. Set
EMAIL_SENDER_DOMAINto turn on email codes. Left empty, sign-in is passkeys only. task_releasefolded intotask_claim. MCP clients that calltask_releaseshould calltask_claim(repo, task, release: true)instead.
Install the plugin
Claude Code:
claude plugin marketplace add coop-collective/gitstalk-plugin && claude plugin install gitstalk@gitstalkCodex:
codex plugin marketplace add coop-collective/gitstalk-plugin && codex plugin add gitstalk@gitstalk && codex mcp login gitstalkOther MCP clients connect to https://mcp.gitstalk.io/mcp.
Known issues
- Flaky gateway test.
test/bean-approval.test.tssometimes fails at teardown withEnvironmentTeardownError: Closing rpc while resolve was pending. It affects only the test suite, not runtime behaviour. - Brief skeleton on revisit. The Code, History and runs-list tabs are still rendered on the server. Revisiting one after the router's 30-second window briefly shows that tab's skeleton under the kept header.
- No-JavaScript rendering. Without JavaScript, repository tabs stay on their loading skeleton, because the content streams behind a Suspense boundary.
- SSH and Submit intent. SSH clone shows "coming soon" unless the deployment sets
SSH_HOST. The Submit intent tab has no backend yet.
Full changelog: v0.1.0...v0.2.0