# agents-task-runner-vds-deploy > Slug legacy-named `-vds-deploy`; scope corrected 2026-06-08 — VDS-as-worker DROPPED. > Per-machine federation: each WORKER runs its own `agents-task-runner`, claims from the > SHARED Gitea board, runs the agent LOCALLY, commits/pushes. VDS hosts the Gitea board > ONLY (already up) — not a worker, nothing deploys on it. ## Goal Поднять `agents-task-runner` (the poller) на первой worker-машине, нацелив на shared Gitea-board. Машина клеймит только то, что умеет (capability-filter), `.admin` исключён из автономного клейма до governance L2/L3. Сначала DRY_RUN на prod-board, затем один живой цикл на реальной non-`.admin` таске: spawn → commit → push, verified. ## Key files - `~/projects/.common/lib/agents-task-runner/` — runtime (plain JS, `type:module`, v0.16.2) - `server.js` — reconcile loop entry (`npm start`) - `task-runner/server.js` — task-runner entry (`npm run task-runner`) - `lib/cross-project-poller.js` — claim loop, requirements-filter, confirm-gate - `lib/consult-backend/` — execution-policy governance (consult_policy + needs-human gate) - `docker-compose.yml` / `docker-compose.host.yml` / `Dockerfile` - `secrets.env.example` — секреты-шаблон (ANTHROPIC_API_KEY / Gitea token / projects-meta) - `config/` — node-config overlays - `~/projects/.common/lib/projects-meta-mcp/dist/` — уже собран (build-prereq для НЕГО, не для раннера) ## Decisions log - 2026-06-08: Взята в работу из ⚪ ready. **Build-prereq в Next action неточен** — раннер это чистый JS (нет tsconfig / `npm run build`, запускается `node server.js`), собирать нечего; `npm run build` релевантен только `projects-meta-mcp`, а его `dist/` уже есть. Источник заблуждения — общий BUILD-prereq из consult-execution-policy-governance review, где TS-only касался projects-meta-mcp. - 2026-06-08: Re-scoped (из STATUS-блока): VDS = board-host only, VDS-as-worker dropped. Poller живёт на worker-машинах (workstation / factory Linux). - 2026-06-08: guard `.admin/.tasks/policy.toml` (default_weight=needs-human) **запушен** (`94972472`) + **верифицирован**: прогон dist-модулей claim-gate на реальном `.admin` board → все 4 claimable `.admin`-таски скипаются `reason=needs-human`, ноль проскочивших. - 2026-06-08: always-on bring-up НАЧАТ (`.env` RECONCILER_ENABLED=true, secrets.env, docker build + `mongo`+`reconciler` up через host-override) — затем **ОСТАНОВЛЕН и снесён** (`compose down`) по запрету user'а "не запускай поллеров без разрешения". host task-runner так и НЕ стартовал → ни одного claim/spawn не было. Конфиг (.env/secrets.env) остался на диске, готов. - 2026-06-08: **находка, валидирующая gating** — DRY_RUN-кандидат `OpeItcLoc03/MoreThenCms/cms-maljarka-https-mode-bug-fix` был ⚪ ready, хотя баг починен сегодня (cross-board stale: фикс жил в `.admin`, таска — на MoreThenCms board, никто не закрыл). Без паузы always-on заспавнил бы агента «чинить» уже починенное. Таска закрыта user-confirmed (MoreThenCms `890eae27`). Урок: до always-on нужен sweep board'ов на stale-claimable. ## Open questions - [ ] **GATE: запуск поллеров требует явного разрешения user'а** (2026-06-08 — "не запускай поллеров без моего разрешения"). До гранта — НЕ стартовать host task-runner и НЕ поднимать reconciler с RECONCILER_ENABLED=true. - [x] Первая worker-машина → **эта workstation** (user-решение 2026-06-08). - [x] Headless-auth → `claude login` достаточно (spawn наследует env хост-процесса; API-key не нужен). - [x] Capability-filter → `AGENT_CAPABILITIES=needs-internet` (минимально), `AGENT_RUNTIME=claude-opus`. ## Completed steps - [x] 2026-06-08: orientation — раннер локализован (`~/projects/.common/lib/agents-task-runner`, v0.16.2, JS), build-prereq разобран (фантом для раннера), common pull актуален (`e7b1143`). - [x] 2026-06-08: prereq-чек на workstation зелёный — node v22.22, docker 29.3.1 up, `projects-meta-mcp/dist/tasks-cli.js` собран, runner `node_modules` есть, `auth.toml` есть. - [x] 2026-06-08: **DRY_RUN-смок прошёл end-to-end.** Хост-процесс `task-runner` (PORT 3000, `DRY_RUN=1 AGENT_RUNTIME=claude-opus AGENT_CAPABILITIES=needs-internet SPAWN_MODE=local`), один тик через `POST /tasks/crossProjectAgentPoller` + `GET /runs/:id` (curl `--noproxy "*"`). Результат: `{dryRun:true, claimed:false, candidate:{project:"OpeItcLoc03/MoreThenCms", slug:"cms-maljarka-https-mode-bug-fix"}}`. Полный cross-boundary путь (reconciler→runner→CLI→реальный shared-board read) работает; claim-token НЕ записан, spawn НЕ вызван. Capability-фильтр (`needs-internet`) отработал — кандидат non-`.admin`. Процесс заглушён. ## Notes - HARD: `.admin` исключён из автономного клейма до governance L2/L3 (см. STATUS-блок § blocker). - anti-self-review: distinct machine-id на каждый worker. - Секреты per-machine: 600 root, не в git.