Skip to content

feat: add repo-config Durable Object + push sync workflow#113

Merged
neekolas merged 21 commits intoxmtplabs:mainfrom
xmtp-coder-agent:fix/issue-112
Apr 20, 2026
Merged

feat: add repo-config Durable Object + push sync workflow#113
neekolas merged 21 commits intoxmtplabs:mainfrom
xmtp-coder-agent:fix/issue-112

Conversation

@xmtp-coder-agent
Copy link
Copy Markdown
Collaborator

@xmtp-coder-agent xmtp-coder-agent commented Apr 20, 2026

Resolves #112

Summary

On every push to a repo's default branch, the Worker now fetches .code-factory/config.toml from the head SHA, sparse-validates it (TOML + Zod), and overwrites a per-repo SQLite-backed Durable Object. Defaults are applied only on read, so the stored record stays sparse and defaults can evolve without migrations.

New surfaces

  • ConfigPushEvent — fifth variant of the Event union (src/events/types.ts).
  • RepoConfigWorkflow — dedicated WorkflowEntrypoint that runs runSyncRepoConfig (src/workflows/repo-config-workflow.ts).
  • runSyncRepoConfig — three-step factory: fetch-config-fileparse-and-validatestore-repo-config (src/workflows/steps/sync-repo-config.ts).
  • RepoConfigDO — SQLite-backed Durable Object using synchronous ctx.storage.kv (src/durable-objects/repo-config-do.ts).
  • GitHubClient.getRepoContentFile — 404/directory/symlink/non-base64 all resolve to null (src/services/github/client.ts).
  • Sparse + resolved Zod schemas for [sandbox], [harness], [[scheduled_jobs]] (src/config/repo-config-schema.ts).

Wiring

  • wrangler.toml — new [[workflows]], [[durable_objects.bindings]], and [[migrations]] (v1, new_sqlite_classes = ["RepoConfigDO"]).
  • src/main.ts — branches workflow dispatch on result.type === "config_push".
  • TaskRunnerWorkflowconfig_push guard throws NonRetryableError (misrouting is observable, not silent).
  • buildInstanceId — deterministic composite config_push-{repoName}-{headSha}-{deliveryId} preserves the dedupe anchor.

Testing

  • Unit tests for schemas, DO round-trip, router, step factory, instance-id, and GitHub client.
  • Integration tests for push dispatch (202 happy path, 200 non-default-branch skip, 200 duplicate delivery, 401 unsigned).
  • End-to-end test using introspectWorkflow over the real REPO_CONFIG_WORKFLOW binding.
  • Full suite: 350 tests, all green.

Test plan

  • npm run typecheck — clean
  • npm run lint — clean
  • npm run format:check — clean
  • npm test — 350 passed
  • npx wrangler types is idempotent against the committed worker-configuration.d.ts
  • All 26 EARS requirements from the spec have implementation + test coverage (verified by spec-reviewer)

Spec & plan

Follow-ups (not in this PR)

  • GitHub App manifest must grant contents:read for getRepoContentFile to see .code-factory/config.toml.
  • scheduled_jobs[].schedule cron validation is a string today; future work could add a cron-pattern check.
  • Downstream consumers (TaskRunner, sandbox provisioning) will read RepoConfigDO.getRepoConfig() in separate issues per §2 non-goals.

🤖 Generated with Claude Code

Note

Add RepoConfigDO Durable Object and RepoConfigWorkflow to sync repo config on push

  • Adds a config_push event type and routes default-branch GitHub push webhooks to a new RepoConfigWorkflow instead of TaskRunnerWorkflow.
  • RepoConfigWorkflow runs three steps: fetch .code-factory/config.toml at the pushed SHA via GitHubClient.getRepoContentFile, parse/validate it with parseRepoConfigToml (using smol-toml and Zod), then write sparse settings to RepoConfigDO.
  • RepoConfigDO is a new SQLite-backed Durable Object keyed by repository full name; it stores identity fields and sparse config separately, applying Zod defaults only at read time via resolveRepoConfigSettings.
  • Push events to non-default branches are skipped; TaskRunnerWorkflow now throws NonRetryableError if it receives a config_push payload.
  • Risk: new RepoConfigDO SQLite migration (v1) and REPO_CONFIG_WORKFLOW binding are provisioned on next deploy.

Macroscope summarized cc532f3.

xmtp-coder-agent and others added 20 commits April 20, 2026 07:02
Add smol-toml dependency and declare the workflow, durable object,
and SQLite migration bindings required by the upcoming repo config
loader. Implementations of RepoConfigDO and RepoConfigWorkflow land
in later tasks; typecheck currently passes because wrangler emits
placeholder comments for unresolved class names.
Colocated test suite for the upcoming sqlite-backed RepoConfigDO: binding
smoke checks plus five round-trip scenarios (fresh read, defaults applied,
overwrite-no-merge, volume path-only default size, and distinct-fullName
isolation). Each test uses its own idFromName to avoid cross-test state.
RepoConfigDO is a sqlite-backed Durable Object (see wrangler.toml migration
v1 new_sqlite_classes) that stores one StoredRepoConfig envelope per
repository, routed by idFromName(repositoryFullName). It exposes two RPC
methods:

- setRepoConfig(cfg): persists the sparse envelope via the synchronous
  ctx.storage.kv.put API (no await).
- getRepoConfig(): reads the envelope and projects it into a fully
  resolved RepoConfig via resolveRepoConfigSettings, returning null when
  no value has been written.

The class is re-exported from src/main.ts so Wrangler can resolve
class_name = "RepoConfigDO" on the Worker entry. worker-configuration.d.ts
is regenerated via wrangler types so the binding is typed as
DurableObjectNamespace<RepoConfigDO> and the RPC methods are reachable
from callers with full type safety. Per EARS-REQ-12 the DO performs no
GitHub or Coder I/O — it is a passive store.
…ions

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…low payload

The earlier regeneration (commit 2f5eb86) ran before the RepoConfigWorkflow
class existed, so wrangler fell back to an unparameterized Workflow type.
With the class now present, the generator resolves the payload generic.
Comment thread src/durable-objects/repo-config-do.ts Outdated
…KV keys

Per reviewer feedback on PR xmtplabs#113: store repositoryId, repositoryFullName,
and installationId under their own KV keys, and keep only the sparse
settings parsed from the TOML file under the `config` key. The four
synchronous .put calls run back-to-back with no awaits between them, so
they commit as a single implicit transaction.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@neekolas neekolas marked this pull request as ready for review April 20, 2026 16:07
@neekolas neekolas merged commit d9544a5 into xmtplabs:main Apr 20, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add repo-config Durable Object + push sync workflow

2 participants