status: complete size: large
Move the GitHub review bot into the iterate package
Status: Complete. The packaged linter connected and caught up to Misha's production GitHub stream. Config now binds the project environment once at app creation, and subscription v4 safely migrates every GitHub connection slug to a runtime-valid durable identity.
Plan#
- Add a public
iterate/starter-apps/github-ai-lintermodule with an environment-boundGithubAiLinter.create(env, config)app definition. Implemented inpackages/iterate/src/starter-apps/github-ai-linter/index.ts. - Keep event routing explicit in config while packaging the linter-specific reaction. The worker keeps a private
#aiLintAppand calls it from its existingprocessEventhook; the SDK stays unchanged. - Move the review processor, durable host, subscription bootstrap, and GitHub webhook routing from
iterate/configinto the package while preserving subscription keys, freshness, and idempotency. The packaged worker keeps stable per-connection identities behind a revisioned migration and retains the routing tests. - Load review rules from a repo glob descriptor; keep Iterate's canonical rule Markdown under root
rules/**/*.mdfor both ordinary coding agents and the hosted linter. Rule reads are pinned to the commit returned byRepo.listFiles; the package filters paths locally and root agent instructions point to the same files. - Export/build/type the package submodule and cover its public behavior with integration-style package tests. Both public exports build and the focused package suite passes.
- Update the seeded config template and generated seed to import/register the package app; keep unrelated app routing and schedules out of scope. The real template and generated file now contain the declaration.
- Update
iterate/configfrommainto the same declaration and remove its local review-bot source. The clean main checkout imports the package and deletesapps/review-bot. - Run focused tests/typechecks plus config typecheck; record any production-shaped verification that cannot run locally. Full monorepo typecheck, lint, format check, and tests pass; 29 focused OS tests, the package build, and the preview-10 deployment/E2E suite pass.
- Repair the production-derived Misha failure and ensure an existing v1 subscription can migrate. Changed the invalid colon to a hyphen, bumped the subscription config revision to v2, and verified the emitted ref through the OS runtime schema.
- Make the packaged worker build through the real worker-bundler contract. The physical configured worker bundles
yaml,zod, Cap'n Web, andminimatch, passes the package graph gate, uses only production-deployed Repo RPCs, and connected successfully in the Misha production smoke. - Preserve terminal source-build errors for subscribers and stop retrying them. The keyed coordinator stores a bounded one-shot failure receipt across actor eviction; the loader marks it non-retryable and the stream parks immediately with the exact error.
Approved decisions#
packages/iterateowns the generic GitHub AI linter runtime; config only composes it.- Root
rules/**/*.mdis the one rule source. Config points the app at/repos/iterate; package artifacts do not duplicate the rule text. - The package owns dynamic-worker refs and subscription bootstrap; config explicitly routes project events to the configured linter.
- Subscription keys and idempotency semantics are invariants; any durable identity change ships as an explicit subscription revision.
- PR iterate/config#17 is design input, not code to merge.
Implementation log#
- 2026-07-22: Plannotator rounds approved the generic packaged runtime, root rule files, and the smallest declarative app registry needed for this bot.
- 2026-07-22: Added a commit-pinned repo glob API so all Markdown rules for one webhook come from one repository snapshot.
- 2026-07-22: Verified config against the local package source, then removed the temporary dependency install; no config lockfile was created.
- 2026-07-22: The full OS typecheck passed after temporarily moving an unrelated ignored scratch script out of its include path; the script was restored unchanged.
- 2026-07-22: Ported the implementation onto current
main; the packaged worker now uses the SDK's newer sharedcreateProcessorHostrather than duplicating its host lifecycle. - 2026-07-22: Full monorepo typecheck, lint, format check, and tests pass on the worktree branch; package build and focused GitHub/template tests also pass.
- 2026-07-22: The first preview exposed one stale E2E fixture assertion for the deleted seeded bot path. Removed that assertion, added the inverse check, and updated the GitHub-agent guide to describe the packaged runtime and Markdown rules; preview rerun pending.
- 2026-07-23: Opened draft PRs iterate/iterate#2259 and iterate/config#18; the config PR consumes #2259's pkg.pr.new artifact until the package change reaches
main. - 2026-07-23: Review rejected the generic project-app registry as premature. Restored the SDK's explicit
processEventseam and made both workers call their private configured linter directly. - 2026-07-23: Replaced iterate/iterate#2259 with #2277 after pkg.pr.new lost the reopened PR's workflow mapping. Carried the resolved human review into the replacement PR and updated config#18 to consume its branch artifact.
- 2026-07-23: Preview-10 deployed the exact implementation SHA for all five apps; every E2E lane passed, including OS Playwright and 47 OS Vitest files.
- 2026-07-23: Misha's end-to-end trial exposed
app-review-bot:<connection>being rejected by the runtime durable-worker-key schema. The existing v1 config event would also conflict with a changed replacement event, so the production-derived regression covers both failures. - 2026-07-23: Replaced the colon with a runtime-safe hyphen and bumped the subscription event to v2. The production-derived repro, all 162 package tests, and package/OS typechecks pass.
- 2026-07-23: Misha's next smoke reached worker-bundler, which rejected the virtual entry point because virtual modules are import aliases rather than files. Added a real-bundler regression that fails with the exact production error before changing the package layout.
- 2026-07-23: Added
dist/starter-apps/github-ai-linter/configured-worker.mjsas the physical entry point, kept only the per-install config virtual, and bumped subscription config to v3 so Misha replaces the temporary smoke subscription. - 2026-07-23: Cloudflare traces showed each build settling as
source-failedin about 0.6 seconds while the subscriber retainedThis worker is still building.and retried. The follow-up must durably expose that terminal compiler error and park the subscription. - 2026-07-23: Added a one-shot durable terminal-failure receipt to the keyed build coordinator. The next foreground call consumes it, the loader restores the non-retryable verdict after Workers RPC, and the stream parks on the first exact source error without another alarm; explicit resume can still retry a potentially transient package-install failure.
- 2026-07-23: Misha successfully emitted the v3 subscription from config commit
5247613a, then workerd rejected the packaged graph withNo such module "yaml". Added a post-build module-graph check so a configured worker with consumer-supplied bare imports cannot publish again. - 2026-07-23: Split the configured worker into its own workerd-targeted tsdown build, bundling
yaml,zod, and@iterate-com/capnwebwhile leaving onlycloudflare:*anditerate:github-ai-linter-configexternal. The emitted 508 kB module passes the graph gate. - 2026-07-23: Misha's next retry loaded the standalone worker, then failed with
The RPC receiver does not implement the method "glob". Production already exposes the equivalent snapshot primitive asRepo.listFiles(); added a production-shaped regression before removing the new host-only method. - 2026-07-23: The package now filters the production
Repo.listFiles()snapshot with bundledminimatch, preserving commit-pinned reads without requiring a coordinated OS deployment. The focused spec, package graph gate, and package/OS typechecks pass. - 2026-07-23: Updated the OS review-routing harness to expose the same
listFiles()RPC; its 19 tests pass after CI caught the stale fake. - 2026-07-24: Misha's v3 subscriber connected and caught up to the GitHub integration stream head with the packaged worker. Bound
envonce throughGithubAiLinter.create(env, config), and fixed underscore-bearing connection slugs with a readable collision-free durable-key escape plus a v4 subscription migration. - 2026-07-24: Bugbot follow-up exposed two delivery-boundary bugs. Foreground callers now consume their own terminal build failure instead of leaving a stale receipt for resume, and non-review webhooks no longer load or depend on structural rule files.
- 2026-07-24: Bugbot caught config updates reusing one idempotency key with a changed virtual-module body, then caught the A → B → A rollback hole in content-addressing by config. Subscription event identity now follows the causal link event path and offset: replaying one event is idempotent, while every later relink can replace the stable subscription with any config.
- 2026-07-24: The seeded project template now reads from
/repos/configand ships editable Markdown copies of its three default rules. New projects no longer depend on Iterate's own/repos/iteratemount for their first structural review. - 2026-07-24: A final RPC-boundary regression proved the non-retryable source-build verdict was stripped on the outer stateful-worker hop.
DynamicWorkerRunnernow restores the name-based verdict at the caller-side authority boundary, so the stream parks on the first exact compiler error.