이 문서는 다른 에이전트가 이 레포에 빠르게 온보딩하기 위한 구조 지도다. 사람 독자용 개요는 README.md를 참조. 실행 상태·리스·복구 불변식은 docs/DURABLE_AUTONOMY.md.
OpenSwarm은 Linear(또는 로컬 이슈 스토어)에서 일을 집어와 worktree-isolated worker를 돌리는 자율 오케스트레이터다. 진실의 축은 둘이다.
- Intent: Linear / local tracker (상태, 토론, Done/Canceled).
- Execution:
~/.openswarm/automation.db(RunLedger) — claim, lease, attempt, fileScope, outbox.
Heartbeat(AutonomousRunner)가 이슈를 가져오고, filterAlreadyProcessed로 park/backoff를 열며, DecisionEngine이 실행 가능한 카드를 고르고, detectSafeCandidateIds + RunLedger.claimRun이 슬롯을 채운 뒤 PairPipeline이 Worker→Reviewer→Tester→Documenter를 돌린다.
src/
automation/ — heartbeat, RunLedger, durable coordinator, PR processor
orchestration/ — DecisionEngine, TaskScheduler, conflictDetector, writeScope
taskState/ — JSON projection + getTaskReadiness / dependency reconcile
adapters/ — provider adapters (codex-responses, openrouter, …)
agents/ — pairPipeline, worker/reviewer/tester/documenter
linear/ — Linear fetch (Todo/In Progress/In Review/Backlog)
core/ — config Zod schema, service wiring
support/ — worktree, ephemeral paths, dashboard, time window
cli/ — openswarm work / review / pr
docs/
DURABLE_AUTONOMY.md — ledger states, leases, reconcile
DOD_CONTRACT.md — sandbox-achievable completion criteria
| 모듈 | 책임 | 진입점 | 의존 |
|---|---|---|---|
automation/autonomousRunner.ts |
5분 heartbeat, idle_fill, enqueue | heartbeat / heartbeatParallel |
DecisionEngine, RunLedger, scheduler |
automation/runLedger.ts |
durable claim/lease/transition | claimRun, resumeNeedsHuman, markReady |
runLedgerScope.ts |
orchestration/decisionEngine.ts |
선택·우선순위·umbrella/terminal 게이트 | heartbeatMultiple |
taskState/store.getTaskReadiness |
orchestration/conflictDetector.ts |
예측 write-scope 비교 | detectFileConflicts, describeScopeConflict |
conflictScope.ts |
orchestration/taskScheduler.ts |
슬롯·큐·worktree 병렬 | enqueue, runAvailable |
worktreeManager |
taskState/store.ts |
의존성 readiness, Linear projection | getTaskReadiness, reconcileDependencyBlockers |
Linear lookup |
support/worktreeEphemeral.ts |
.venv / .test_venv / pytest-local |
citedPathsAreEphemeral |
publication fence |
agents/pairPipeline.ts |
Worker↔Reviewer 루프 | run |
adapters |
graph LR
L[Linear fetch] --> F[filterAlreadyProcessed idle_fill]
F --> E[DecisionEngine]
E --> S[detectSafeCandidateIds]
S --> C[RunLedger.claimRun]
C --> P[PairPipeline in worktree]
P --> T[Linear / PR / outbox]
Admission under unknownScopeAdmission: admit (default, vela):
- Before anything is fetched: the disk floor (
minFreeDiskGiB,support/hostHealth.ts, AGT-4785) pauses the heartbeat. Right after the fetch,partitionByReadiness(automation/readinessGate.ts, AGT-4784) drops Backlog and skip-label issues, so they never reachfilterAlreadyProcessed. includeBacklog: false(default since AGT-4784) — Todo is the ready signal. A deployment that wants free slots to chew the Backlog (vela) must setincludeBacklog: true.idle_fillliftsNEEDS_HUMAN/RETRY_AT/ legacy backoff when Linear still wants the card — except for an issue held by anexecorchestrator (automation/execHold.ts) or whose title and description are unchanged since its run started (automation/dispatchFingerprint.ts, AGT-4787).- Predicted file overlap does not defer. Worktrees isolate; merge later.
- Still skipped: Linear Done/Canceled, parent/EPIC umbrellas, live
CLAIMED/EXECUTINGleases, unresolved deps, unmapped/disabled projects.detectSafeCandidateIdsalso drops an issue whose draft carries a confident "a person has to do or decide this" verdict before the claim.
serialize restores Codex-era fail-closed overlap holds.
TaskItem(orchestration/decisionEngine.ts) —linearState,fileScope,blockedBy,explicitDispatch.RunState(automation/runLedgerTypes.ts) —READY…DONEplusRETRY_AT,NEEDS_HUMAN,NEEDS_RECONCILE.ParkResumeTrigger—idle_fill|explicit_dispatch|tracker_todo|sandbox_quarantine_dispatch|unspecified.UnknownScopeAdmission—'admit' | 'serialize'.- Config:
src/core/config.tsAutonomousConfigSchema(includeBacklogdefault false since AGT-4784,unknownScopeAdmissiondefault admit).
- 새 provider:
src/adapters/+core/configadapter enum. - Admission 정책:
runLedgerScope.admitsConflictScope,conflictDetector.detectFileConflicts,autonomousRunner.detectSafeCandidateIds— 세 곳이 같은 정책을 읽어야 한다. - Linear fetch 상태:
src/linear/linear.tsgetMyIssues(이미 Backlog 포함). - Ephemeral 경로:
src/support/worktreeEphemeral.ts.
- vela compose는 이미지 태그를 pin한다.
docker build만으로는 recreate 안 됨.docker-compose.yml의openswarm:vela-YYYYMMDD-HHMM-amd64를 바꾸고up -d --no-deps openswarm. vela-build.sh <sha>는origin/main만 fetch한다. PR SHA는builds/4e8ba24에서git fetch origin <branch>먼저.- PR SHA를 돌리는 동안
.last-built-sha는origin/maintip을 유지하지 않으면 autodeploy cron이 main으로 롤백한다. unknownScopeAdmission: admit를 heartbeat만 바꾸고 claim 게이트를 안 바꾸면 큐에는 들어가고claimRun이 null — 슬롯이 다시 빈다.isActionableLinearState(state)helper parks Backlog by default, and since AGT-4784 so do the engine, config and runner (includeBacklog: false; every reader spells it?? false).undefinedmust keep meaning false: never spread anundefinedover the default, and never reintroduce a?? true.- AGT-4155 park loop: opening a park on Linear
In Progressalone makes claim→exec→park spin every heartbeat.idle_fillopens parks on purpose (cheap models chew free slots), except whereexecHoldor an unchangeddispatchFingerprintholds the park (AGT-4787). Do not reopen this loop when revivingserializeor question waits. - drafted
docs/integrations.md같은 광역 fileScope는 한 레포의 카드를 전부 한 conflict group으로 묶는다.admit이 아니면 슬롯이 비어 있는 채로 1장만 산다. - 로컬 워크스페이스
fix/agt-4220-…의 dirtystore.ts/autonomousRunner.ts는 이 스택과 섞지 말 것.
npx vitest run src/automation/autonomousRunner.operatorPark.test.ts \
src/automation/autonomousRunner.operatorQuestionPark.test.ts \
src/automation/runLedger.test.ts \
src/orchestration/conflictDetector.test.ts \
src/orchestration/decisionEngine.gating.test.ts \
src/core/config.test.ts
npx tsc --noEmit가드 테스트: operatorQuestionPark (idle_fill), conflictDetector (admit vs serialize), runLedgerScope / runLedger.test.ts claim overlap.