Skip to content

Gitstalk v0.2.0

Choose a tag to compare

@disturbing disturbing released this 15 Oct 07:39
· 14 commits to main since this release
eb05d05

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.yml sorts each change into classes computed by code. Each class routes to auto, triage (Clef decides) or card (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_approve with 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/repos or a first push to an address that does not exist yet. All three need the new create scope. (#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.toml is 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 /login and /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 secrets scope.
  • Derived blame and server-side commits. MCP adds why_lines, bean_commit and repo_read. RepoDerivedDO keeps bean_blame and path_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, triage or card. When several classes match, the strictest route wins.
    • Clef triages triage classes. 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 otherwise cannot be combined with paths, syntax or class. Errors give the line and column.
    • The built-in policy-change class still wins for an agent's change. Changes made only inside .gitstalk/ or .beanstalk/ are never routed by the fallback.
  • Starter policy (#2): comments, docs, tests and lockfiles are auto. Migrations and auth paths are card. Everything else is triage through an everything-else fallback. 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's latencyMs.
  • 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 as prefetch.
  • 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+B collapses 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/folder and /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 the git clone and git 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.
  • 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=held filter.
    • Push output says HELD: … nothing in it needs fixing instead of RED:.
  • 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 of router.refresh().
    • Cancel and Re-run on a run update optimistically.
  • 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 diff package 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".
    • CopyButton announces "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 /login and /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_DOMAIN is set. Without it, sign-in is passkeys only.
  • Separate rate limits. Passkey options and passkey verification now use separate rate-limit budgets, so loading /login a 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 the create scope. Visibility defaults to private, and the start defaults to empty (or typescript-starter).
  • HTTP API (#9). POST /api/repos takes a personal token with create and 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, stalk or bean/<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.
  • create scope (#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_credentials for 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 now checks.yml (#7).
    • Keys: image, run (an argv list, never a shell string), timeout-minutes or timeout-seconds, env (string values only) and protected-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, then checks.yaml, then checks.toml, then the same three files in .beanstalk/. Push lines name any shadowed file.
    • CHECKS_JSON_SCHEMA is exported for editor autocomplete. It is not served at its URL yet.
  • Settings saves commit directly as a bean (#1, #7).
    • The rules editor, the automation builder and MCP bean_commit share one server-side commit core with compare-and-swap and idempotency.
    • Settings → Checks Convert to YAML writes checks.yml and deletes the TOML file in one bean (checks-<short>, Via: Settings → Checks).

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_run that 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.
  • secrets scope. 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 write by default.
  • Prompt and Shell harness labels for automations (#1). An area watcher template and a paths filter on landing triggers.
  • Runners documentation (#6). Jobs run in a standard-4 container 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 ReadGrant minted by the access check, and they never go to the Cache API or isolate memory.
    • Private responses stay no-store.
  • Purge by epoch (#11, #17). The repository record's updated_at is part of every key, so a settings save, visibility flip, transfer or archive makes old copies unreachable at once.
  • HTTP (#11). Cache-Control and ETags are set per route, and a 304 is only ever sent after the access check.
  • Derived reads via RunDO RPC (#14). RepoDerivedDO reads commits, trees and blobs through the engine's RunDO.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 runs table, written by each RunDO in waitUntil.
    • The kill switch is a halt row in gateway_flags. It is read uncached when a run is created or started.
    • The hourly sweep is a cron trigger (17 * * * *).
    • RunIndex stays as a read-only stub for one release so its data can be backfilled.
  • 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-collective GitHub 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 own MCP_URL and 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_release is folded into task_claim(…, release: true).
  • Answer changes. bean_status and bean_wait report held: true and an approve_url. repo_status lists held_for_approval separately from sent_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.
    • Approving a held bean (#5).
    • The fallback class and starter rules (#2).
    • The Code button (#8).
    • The file tree (#16).
    • The checks YAML format (#7).
    • Creating repositories (#9).
    • The cron sweep in self-hosting (#15).
  • 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 NOTICE and TRADEMARKS.md (#1).
  • CLAUDE.md removed. Claude Code reads AGENTS.md directly. The .claude/skills symlinks 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.md under research/promo was replaced by an import (#13).

Operations

  • Reusable deploy workflow (#10). deploy.yml gains a workflow_call trigger (inputs environment, 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_DOMAIN to Email Sending when it is set, and smoke checks confirm that /login shows the email form exactly when a domain is configured.
  • New admin route. POST /v1/admin/runs/backfill copies RunIndex into D1. It is idempotent and needs the admin token. The GET, POST and DELETE /v1/admin/halt and POST /v1/admin/sweep routes are unchanged.

Upgrade notes and breaking changes (self-hosters)

  • Run provisioning before deploying.
    • This release adds forge D1 migrations 0011_repository_creations and 0012_runs_and_flags, and identity D1 migrations 0007_email_codes and 0008_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>, then pnpm env:deploy <env>. Migration 0012 must be applied before the new gateway serves.
  • 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 with POST /v1/admin/runs/backfill and the admin token. It is safe to repeat. Check the gateway logs for run index backfilled. The next release deletes the RunIndex class, so confirm that run_index_backfill is set in gateway_flags in 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.json has been removed from this repository, so installs from the old disturbing/beanstalk marketplace stop updating.
    • Re-add the plugin from coop-collective/gitstalk-plugin with the lines below.
  • Checks config is YAML.
    • New repositories and starters write .gitstalk/checks.yml.
    • .gitstalk/checks.toml is still read, and the push suggests converting it.
    • If a tree has both files, checks.yml wins and the TOML file is ignored.
    • run must be an argv list, and env values must be quoted strings.
  • Email sign-in replaces magic links. The magic-link routes are gone. Set EMAIL_SENDER_DOMAIN to turn on email codes. Left empty, sign-in is passkeys only.
  • task_release folded into task_claim. MCP clients that call task_release should call task_claim(repo, task, release: true) instead.

Install the plugin

Claude Code:

claude plugin marketplace add coop-collective/gitstalk-plugin && claude plugin install gitstalk@gitstalk

Codex:

codex plugin marketplace add coop-collective/gitstalk-plugin && codex plugin add gitstalk@gitstalk && codex mcp login gitstalk

Other MCP clients connect to https://mcp.gitstalk.io/mcp.

Known issues

  • Flaky gateway test. test/bean-approval.test.ts sometimes fails at teardown with EnvironmentTeardownError: 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