A self-driving software delivery substrate that reads project state (markdown files), queries GitHub for open issues, and selects the next unit of work — autonomously.
Milestone M0-1 ("hello-loop vertical slice") proved the core read loop.
Milestone M1-1 ("builder module + loop.runOnce") adds the builder agent that drives
Claude Code CLI to implement an issue, runs tests/build, and opens a PR — with a fully
mocked CommandRunner boundary so CI never touches real claude/gh/network.
src/
state/ – Typed markdown parsers (roadmap, backlog, project state)
github/ – Thin gh CLI wrapper (listIssues)
core/ – Selector logic, hello-loop CLI, loop.runOnce, run-once CLI
agents/ – Builder agent (Claude Code CLI driver)
fixtures/
state/ – Sample ROADMAP.md, BACKLOG.md, PROJECT.md
tests/ – Hermetic unit tests (no network, no filesystem side effects)
Exports runBuilder(opts) — the Claude Code CLI driver.
CommandRunnerinterface — mockable boundary for ALL subprocess calls (gh,claude,git,npm).
Default implementation useschild_process.execFile(argv arrays, NO shell strings).
Pass a mockrunnerin tests → zero real network calls in CI.- Steps: fetch issue → compute branch
feat/issue-<n>→ assemble prompt → invokeclaude -p "<prompt>"headlessly → runnpm test+npm run build→ if green, commit + push +gh pr create. - Returns
BuilderResult { branch, prUrl, testsPassed, implemented }. dryRun:trueskips commit/push/PR (safe for loop dry-runs).
Exports runOnce(opts) — one pass of the autonomous delivery loop.
- Reads
BACKLOG.md→ selects next ready+unowned unit → callsrunBuilderwithdryRun:true. - Returns
{ selected, result }. - Ownership-write and gated-merge deferred to M1-3.
node dist/core/run-once.js [--state-dir <path>] [--repo <owner/repo>] [--checkout-dir <path>]Runs runOnce in dry-run mode (default — safe).
node dist/core/run-once.js --live ...--live flag — M1-2 smoke path.
Passes the real (non-dryRun) builder.
gh auth token and Claude Code CLI installed. The CI workflow does not pass --live.
- Node.js 22+
npmghCLI (authenticated) — only required to run CLI commands (not for tests)- Claude Code CLI (
claude) — only required for--liveruns
npm installnpm run buildCompiled output goes to dist/.
node dist/core/hello-loop.js
# or with custom args:
node dist/core/hello-loop.js --state-dir ./fixtures/state --repo suskumar-MSFT/autonomous-delivery-enginenode dist/core/run-once.js --state-dir ./fixtures/real-state --repo suskumar-MSFT/autonomous-delivery-engine --checkout-dir .npm testnpm run lintThe engine reads three markdown files:
| File | Contents |
|---|---|
ROADMAP.md |
Milestone table: ID, name, phase, status |
BACKLOG.md |
Work item table: ID, GH#, title, type, status, owner |
PROJECT.md |
Current phase number and focus statement |
GitHub Actions runs on every push and pull request:
npm ci— installnpm run lint— ESLintnpm test— Vitest (all mocked, no network)npm run build— TypeScript compile
- Mocked boundary:
CommandRunnerinterface isolates all subprocess calls; tests inject fakes — no realclaude/gh/network in CI - Dry-run by default:
runOncealways usesdryRun:true;--liveis an explicit opt-in for M1-2 smoke testing - Read-only baseline: hello-loop never writes state or calls mutating GitHub commands
- Hermetic tests: unit tests use committed fixture files, no network, no absolute paths
- Deterministic: selectNextUnit tie-breaks by lexicographic id — same input always gives same output
- Thin wrappers: GitHub integration shells out to
ghCLI (execFile, NO shell strings)
MIT