Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d27f7c1
feat(verify): re-run changed repos' own check gates after a run
zaytcevcom Jul 8, 2026
6d5f6b9
feat(goal): goal store, independent-evaluator prompt and verdict parsing
zaytcevcom Jul 8, 2026
adb8b1f
feat(autobudget): per-chat daily USD budget for autonomous runs
zaytcevcom Jul 8, 2026
f3d2f93
feat(chat): autonomy tail — post-run verification and /goal evaluator…
zaytcevcom Jul 8, 2026
a3bd1fb
feat(ciwatch): poll CI on duck/* branches, relay red builds, auto-mer…
zaytcevcom Jul 8, 2026
16a01a7
feat(config): autonomy-loop knobs (verify, goal, CI watch, budget, sc…
zaytcevcom Jul 8, 2026
f38a672
feat(workspace): render AUTO_APPROVE_SCOPE; run the tester on sonnet
zaytcevcom Jul 8, 2026
c837573
feat(adapters): reserve /goal on Telegram and VK, wire autonomy + CI …
zaytcevcom Jul 8, 2026
0cf50b7
docs(team): one feature in one repo = one PR, sliced into commits
zaytcevcom Jul 8, 2026
1c81e21
docs: document the autonomy loops (README section + .env.example block)
zaytcevcom Jul 8, 2026
cce19cb
fix(docker): install Codex from a pinned release tarball, not curl|sh
zaytcevcom Jul 8, 2026
e1866d8
fix(chat,verify): apply the independent review's confirmed findings
zaytcevcom Jul 8, 2026
cb19c13
feat(teambranch): one parser for the duck/<chatid> branch contract
zaytcevcom Jul 8, 2026
1805b1b
feat(ciwatch): wake the chat on green builds; prune dedupe state
zaytcevcom Jul 8, 2026
0ee6c86
refactor(adapters): route CIGreen; share the autonomy builder
zaytcevcom Jul 8, 2026
3ed619f
docs(team): waiting on remote CI is a foreground job — never "I'll re…
zaytcevcom Jul 8, 2026
caf245b
feat(followup): a legal way to come back later — one-shot scheduled r…
zaytcevcom Jul 8, 2026
82cb28c
feat(chat): sweep followup/ files; nudge unbacked "I'll report back" …
zaytcevcom Jul 8, 2026
f362668
docs(team): generalize the no-standby rule to any external wait
zaytcevcom Jul 8, 2026
538abdc
feat(mcp): wire the DuckBug MCP server with a bearer token
zaytcevcom Jul 8, 2026
94a6f30
chore(deploy): expose every new knob in the env templates and Ansible…
zaytcevcom Jul 8, 2026
ba684a4
build: bump Go to 1.26.5 (GO-2026-5856 crypto/tls fix)
zaytcevcom Jul 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Everything else in [`.env.example`](adapters/telegram/.env.example) has sensible

- **The conversation is the task source** — describe what you want in chat and review the PR that comes back; the agent's shell and editor are sandboxed inside the container.
- **A real dev-team pipeline, not a single prompt** — spec-first acceptance criteria, build/regression gates, and an arbiter that breaks loops.
- **Autonomy loops** — the bot **verifies its own "done"** by re-running the repo's check gate itself; `/goal` arms an **independent evaluator** that loops the team until your criterion actually holds; `/schedule` runs recurring jobs; an optional **CI watch** reacts to red builds (and can auto-merge green PRs) — all under a per-chat daily autonomy budget. See [Autonomy loops](#autonomy-loops).
- **Multi-transport** — **Telegram** and **VK** today, both on the same core; a new platform is a thin adapter, not a fork.
- **PR reactions without inbound webhooks** — the bot *polls* your git host for new review comments and routes each back to the chat that opened the PR.
- **Subscription-friendly** — authenticate with a Claude Pro/Max token (no per-token cost) or an Anthropic API key.
Expand All @@ -72,6 +73,19 @@ The five subagents — **planner → coder → tester → reviewer → arbiter**

The team is built for a **microservices** workspace: a feature can span several services, and it coordinates branches and one cross-linked PR per repo. The full pipeline, guardrails, and role table live in [`core/README.md`](core/README.md).

## Autonomy loops

Four opt-in loops move you up the delegation ladder — from "the agent checks its own work" to "the agent runs without you" — each with a hard stop condition (env keys in [`.env.example`](adapters/telegram/.env.example)):

- **Post-run verification** (`ENABLE_POST_VERIFY`, default **on**) — after a run reports done, the bot itself re-runs the changed repos' own check gate (`task`/`make`/`npm` `check`/`test`/`lint`) and, on red, sends the team back with the real failure output — up to `POST_VERIFY_MAX_FIXES` consecutive rounds. The agent's "tests pass" is verified, not trusted.
- **`/goal` evaluator** — `/goal all list views paginate correctly` arms a goal; after every completed run an **independent, fresh-session judge** (no shared context with the working session) inspects the workspace, re-runs checks, and returns a strict verdict. Not met → the unmet points are injected back as a fix-up; met → 🎯 and the goal disarms. Bounded by `GOAL_MAX_ATTEMPTS`; `EVALUATOR_MODEL` can pick a cheaper judge; `/goal off` disarms.
- **`/schedule` cron jobs** — durable per-chat recurring prompts with per-chat timezones (see the SCHEDULER block in `.env.example`); fired as normal team runs, gated by the creator's allow-list status and cost cap at fire time.
- **CI watch** (`ENABLE_CI_WATCH`) — polls CI state on the `duck/*` branches (GitHub check-runs or Gitea commit status). A red build injects "CI is red — fix and push" into the owning chat; a green build wakes the chat too, so "I'll report when CI finishes" actually happens — each once per commit. With `ENABLE_AUTO_MERGE=true` a green PR is merged automatically — the full hands-off mode; leave it false to keep the human merge.

Runs can also legally **come back later**: writing `followup/<delay>.md` (content = the prompt) schedules a durable one-shot return — and a **promise nudge** fires one corrective run whenever a final answer says "I'll report back" without scheduling anything that actually would, so the bot stops going silent on its own promises.

Two safety rails apply across all of it: `AUTO_TASK_MAX_COST_PER_DAY` caps what autonomy-originated runs may spend per chat per day (direct messages are unaffected), and `AUTO_APPROVE_SCOPE` controls which planner complexities may skip the "confirm scope & wait" step (`off` by default).

## Repo layout (monorepo)

The platform-agnostic dev-team brain lives in [`core/`](core/); each platform is a thin adapter under `adapters/<name>/` that shares it.
Expand Down
49 changes: 49 additions & 0 deletions adapters/telegram/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,55 @@ MISTRAL_API_KEY= # for VOICE_PROVIDER=mistral (console.mistral.ai)
# ENABLE_SCHEDULER=false
# SCHEDULE_STORE_PATH= # JSON job store; default <APPROVED_DIRECTORY>/schedules.json

# ===== MCP TOOLS (Claude backend only) =====
# context7 — up-to-date library/API docs tools for the team (free tier, no key).
ENABLE_CONTEXT7=true
# DuckBug — the team reads your project's LIVE errors/logs from DuckBug monitoring.
# The token is the on/off switch: create an API token (sk-duck-api01-…) in the
# DuckBug UI and set it here. Self-hosted DuckBug: point the URL at <your>/api/mcp.
DUCKBUG_MCP_TOKEN=
# DUCKBUG_MCP_URL=https://duckbug.io/api/mcp

# ===== AUTONOMY LOOPS — quality gates without a human in the loop =====
# Post-run verification (ON by default): after a run reports done, the bot itself
# re-runs the changed repos' own check gate (task/make/npm check|test|lint) and, on
# red, sends the team back with the real failure — up to POST_VERIFY_MAX_FIXES
# consecutive rounds per chat. "Tests pass" is verified, not trusted.
ENABLE_POST_VERIFY=true
POST_VERIFY_MAX_FIXES=2
POST_VERIFY_TIMEOUT_SECONDS=1800 # bounds ONE repo's gate command
# /goal evaluator: /goal <criterion> arms a goal; after every completed run an
# INDEPENDENT fresh-session judge re-checks the workspace against it and loops the
# team until it holds — up to GOAL_MAX_ATTEMPTS evaluation rounds (/goal off disarms).
# EVALUATOR_MODEL picks a (cheaper) judge model; empty inherits CLAUDE_MODEL.
GOAL_MAX_ATTEMPTS=5
EVALUATOR_MODEL=
GOAL_EVAL_TIMEOUT_SECONDS=1800
# CI watch (OFF by default): poll CI state on the duck/* branches and, once per
# commit, inject "CI is red — fix it" into the owning chat; a GREEN build wakes the
# chat too ("CI passed — report/continue"), so an agent that promised to report the
# CI verdict actually comes back instead of going silent. Needs GIT_TOKEN plus
# GIT_HOST=github.com or GITEA_API_URL. ENABLE_AUTO_MERGE additionally merges a
# branch's PR once CI is green — full hands-off; leave false to keep the human merge.
ENABLE_CI_WATCH=false
CI_POLL_INTERVAL=120
ENABLE_AUTO_MERGE=false
# Scope auto-approval: skip the "confirm scope & wait" step for tasks at or below
# this planner complexity. off (always wait) | trivial | standard | all.
AUTO_APPROVE_SCOPE=off
# Autonomy budget: max USD per chat per UTC day that AUTONOMY-originated runs
# (CI fix-ups, verification fix-ups, goal rounds, follow-ups) may spend; direct user
# messages and scheduled jobs (gated by their creator's cost cap) are not affected.
# 0 disables.
AUTO_TASK_MAX_COST_PER_DAY=20.0
# Follow-ups + promise nudge: a run may legally "come back later" by writing
# followup/<delay>.md (content = the prompt to run then; min 1m, max 48h) — swept
# after every run into a durable store and fired once when due. The promise nudge
# additionally fires ONE corrective run when a final answer says "I'll report back"
# without scheduling anything that actually would.
ENABLE_PROMISE_NUDGE=true
# FOLLOWUP_STORE_PATH= # JSON store; default <APPROVED_DIRECTORY>/followups.json

# ===== DOCKER-IN-DOCKER (optional, with `--profile dind`) — dockerized linters/tests for the team =====
# DOCKER_HOST=tcp://dind:2375

Expand Down
24 changes: 21 additions & 3 deletions adapters/telegram/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,30 @@ RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends docker-ce-cli docker-compose-plugin; \
npm install -g @anthropic-ai/claude-code; \
mkdir -p /opt/codex; \
curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_HOME=/opt/codex CODEX_NON_INTERACTIVE=1 CODEX_INSTALL_DIR=/usr/local/bin sh; \
chmod -R a+rX /opt/codex; \
npm cache clean --force; \
apt-get clean; rm -rf /var/lib/apt/lists/*

# Codex CLI — installed from the pinned GitHub release tarball, NOT the
# `curl | sh` installer: the installer resolves the release and its asset
# digests through the unauthenticated GitHub API, which rate-limits shared CI
# runners and then intermittently reports the platform assets missing ("Could
# not find Codex package or platform npm release assets"), breaking the arm64
# leg of the multi-arch build. One arch-matched, retried, direct download
# mirrors the pinned `gh`/`task` installs below.
ARG CODEX_VERSION=0.143.0
RUN set -eux; \
arch="$(dpkg --print-architecture)"; \
case "$arch" in \
amd64) target="x86_64-unknown-linux-musl" ;; \
arm64) target="aarch64-unknown-linux-musl" ;; \
*) echo "unsupported arch for codex: $arch" >&2; exit 1 ;; \
esac; \
curl -fsSL --retry 5 --retry-all-errors -o /tmp/codex.tgz \
"https://github.com/openai/codex/releases/download/rust-v${CODEX_VERSION}/codex-${target}.tar.gz"; \
tar -xzf /tmp/codex.tgz -C /tmp; \
install -m 0755 "/tmp/codex-${target}" /usr/local/bin/codex; \
rm -f /tmp/codex.tgz "/tmp/codex-${target}"

# GitHub CLI (gh) for PR creation on github.com — installed from the official release
# tarball, NOT the apt repo: that extra apt source tipped the QEMU-emulated arm64
# build over its memory limit during `apt-get update` (OOM: "Cannot allocate
Expand Down
4 changes: 3 additions & 1 deletion adapters/telegram/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ const HelpText = "Flock Telegram assistant — available commands:\n\n" +
"/help — show this message\n" +
"/new — start a fresh session (forget the current conversation)\n" +
"/stop — stop the run currently in progress\n" +
"/schedule — manage scheduled jobs (when enabled)\n\n" +
"/schedule — manage scheduled jobs (when enabled)\n" +
"/goal <criterion> — arm a goal an independent evaluator re-checks after every run " +
"(/goal off to disarm)\n\n" +
"Send any other message to run it through the assistant."

// WelcomeText is the static usage message replied to an allowed user who sends
Expand Down
28 changes: 28 additions & 0 deletions adapters/telegram/deploy/roles/claude_tg_bot/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,31 @@ dind_image: "docker:27-dind"
enable_docker_prune: true
docker_prune_schedule: "weekly" # systemd OnCalendar (weekly | daily | "Sun *-*-* 04:00:00")
docker_prune_keep_hours: 168 # keep anything newer than this many hours (168 = 7 days)

# MCP tools (Claude backend only). context7 = live library docs (free, no key).
# DuckBug MCP activates when vault_duckbug_mcp_token is set in vault.yml — the team
# then reads the project's live errors/logs from DuckBug; self-hosted deployments
# override duckbug_mcp_url with <their-duckbug>/api/mcp.
enable_context7: true
duckbug_mcp_url: "https://duckbug.io/api/mcp"

# Autonomy loops (see adapters/telegram/.env.example for the full story).
# Post-run verification re-runs changed repos' own check gates after every run.
enable_post_verify: true
post_verify_max_fixes: 2
post_verify_timeout_seconds: 1800
# /goal evaluator rounds cap + optional cheaper judge model.
goal_max_attempts: 5
evaluator_model: ""
goal_eval_timeout_seconds: 1800
# CI watch: red build → fix-up run; green build → wake the chat. Auto-merge merges
# a green PR (full hands-off) — keep false to leave merges to the human.
enable_ci_watch: false
ci_poll_interval: 120
enable_auto_merge: false
# Scope auto-approval: off | trivial | standard | all.
auto_approve_scope: off
# Per-chat per-day USD ceiling for autonomy-originated runs (verify/goal/CI/follow-ups).
auto_task_max_cost_per_day: "20.0"
# followup/<delay>.md convention + the "I'll report back" promise nudge.
enable_promise_nudge: true
32 changes: 32 additions & 0 deletions adapters/telegram/deploy/roles/claude_tg_bot/templates/env.j2
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,38 @@ MISTRAL_API_KEY={{ vault_mistral_api_key }}
DATABASE_URL=sqlite:///data/bot.db
SESSION_TIMEOUT_HOURS=24

# === MCP TOOLS (Claude backend only) ===
# context7: up-to-date library/API docs tools for the team (free tier, no key).
ENABLE_CONTEXT7={{ enable_context7 | default(true) | string | lower }}
{% if vault_duckbug_mcp_token is defined and vault_duckbug_mcp_token %}
# DuckBug MCP: the team reads the project's live errors/logs from DuckBug monitoring.
DUCKBUG_MCP_TOKEN={{ vault_duckbug_mcp_token }}
DUCKBUG_MCP_URL={{ duckbug_mcp_url | default('https://duckbug.io/api/mcp') }}
{% endif %}

# === AUTONOMY LOOPS ===
# Post-run verification: the bot re-runs changed repos' own check gates after a run
# and bounces failures back to the team (POST_VERIFY_MAX_FIXES consecutive rounds).
ENABLE_POST_VERIFY={{ enable_post_verify | default(true) | string | lower }}
POST_VERIFY_MAX_FIXES={{ post_verify_max_fixes | default(2) }}
POST_VERIFY_TIMEOUT_SECONDS={{ post_verify_timeout_seconds | default(1800) }}
# /goal evaluator: independent fresh-session judge loops the team until the user's
# criterion holds (bounded by GOAL_MAX_ATTEMPTS rounds).
GOAL_MAX_ATTEMPTS={{ goal_max_attempts | default(5) }}
EVALUATOR_MODEL={{ evaluator_model | default('') }}
GOAL_EVAL_TIMEOUT_SECONDS={{ goal_eval_timeout_seconds | default(1800) }}
# CI watch: poll CI on duck/* branches; red build → fix-up run, green build → wake
# the chat; ENABLE_AUTO_MERGE merges a green PR (full hands-off; default off).
ENABLE_CI_WATCH={{ enable_ci_watch | default(false) | string | lower }}
CI_POLL_INTERVAL={{ ci_poll_interval | default(120) }}
ENABLE_AUTO_MERGE={{ enable_auto_merge | default(false) | string | lower }}
# Scope auto-approval: off | trivial | standard | all.
AUTO_APPROVE_SCOPE={{ auto_approve_scope | default('off') }}
# Per-chat per-day USD cap for autonomy-originated runs (0 disables).
AUTO_TASK_MAX_COST_PER_DAY={{ auto_task_max_cost_per_day | default('20.0') }}
# followup/<delay>.md convention + the "I'll report back" promise nudge.
ENABLE_PROMISE_NUDGE={{ enable_promise_nudge | default(true) | string | lower }}

# === MISC ===
LOG_LEVEL=INFO
ENABLE_TELEMETRY=false
Expand Down
39 changes: 39 additions & 0 deletions adapters/vk/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,45 @@ MISTRAL_API_KEY= # for VOICE_PROVIDER=mistral (console.mistral.ai)
# ENABLE_SCHEDULER=false
# SCHEDULE_STORE_PATH= # JSON job store; default <APPROVED_DIRECTORY>/schedules.json

# ===== MCP TOOLS (Claude backend only) =====
# context7 — up-to-date library/API docs tools for the team (free tier, no key).
ENABLE_CONTEXT7=true
# DuckBug — the team reads your project's LIVE errors/logs from DuckBug monitoring.
# The token is the on/off switch: create an API token (sk-duck-api01-…) in the
# DuckBug UI and set it here. Self-hosted DuckBug: point the URL at <your>/api/mcp.
DUCKBUG_MCP_TOKEN=
# DUCKBUG_MCP_URL=https://duckbug.io/api/mcp

# ===== AUTONOMY LOOPS — quality gates without a human in the loop =====
# Post-run verification (ON by default): after a run reports done, the bot itself
# re-runs the changed repos' own check gate (task/make/npm check|test|lint) and, on
# red, sends the team back with the real failure — up to POST_VERIFY_MAX_FIXES
# consecutive rounds per chat. "Tests pass" is verified, not trusted.
ENABLE_POST_VERIFY=true
POST_VERIFY_MAX_FIXES=2
POST_VERIFY_TIMEOUT_SECONDS=1800 # bounds ONE repo's gate command
# /goal evaluator: /goal <criterion> arms a goal; after every completed run an
# INDEPENDENT fresh-session judge re-checks the workspace against it and loops the
# team until it holds — up to GOAL_MAX_ATTEMPTS evaluation rounds (/goal off disarms).
# EVALUATOR_MODEL picks a (cheaper) judge model; empty inherits CLAUDE_MODEL.
GOAL_MAX_ATTEMPTS=5
EVALUATOR_MODEL=
GOAL_EVAL_TIMEOUT_SECONDS=1800
# Scope auto-approval: skip the "confirm scope & wait" step for tasks at or below
# this planner complexity. off (always wait) | trivial | standard | all.
AUTO_APPROVE_SCOPE=off
# Autonomy budget: max USD per chat per UTC day that AUTONOMY-originated runs
# (verification fix-ups, goal rounds, follow-ups) may spend; direct user messages
# and scheduled jobs (gated by their creator's cost cap) are not affected. 0 disables.
AUTO_TASK_MAX_COST_PER_DAY=20.0
# Follow-ups + promise nudge: a run may legally "come back later" by writing
# followup/<delay>.md (content = the prompt to run then; min 1m, max 48h) — swept
# after every run into a durable store and fired once when due. The promise nudge
# additionally fires ONE corrective run when a final answer says "I'll report back"
# without scheduling anything that actually would.
ENABLE_PROMISE_NUDGE=true
# FOLLOWUP_STORE_PATH= # JSON store; default <APPROVED_DIRECTORY>/followups.json

# ===== DOCKER-IN-DOCKER (optional, with `--profile dind`) — dockerized linters/tests for the team =====
# DOCKER_HOST=tcp://dind:2375

Expand Down
22 changes: 21 additions & 1 deletion adapters/vk/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,30 @@ RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends docker-ce-cli docker-compose-plugin; \
npm install -g @anthropic-ai/claude-code; \
curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 CODEX_INSTALL_DIR=/usr/local/bin sh; \
npm cache clean --force; \
apt-get clean; rm -rf /var/lib/apt/lists/*

# Codex CLI — installed from the pinned GitHub release tarball, NOT the
# `curl | sh` installer: the installer resolves the release and its asset
# digests through the unauthenticated GitHub API, which rate-limits shared CI
# runners and then intermittently reports the platform assets missing ("Could
# not find Codex package or platform npm release assets"), breaking the arm64
# leg of the multi-arch build. One arch-matched, retried, direct download
# mirrors the pinned `gh`/`task` installs below.
ARG CODEX_VERSION=0.143.0
RUN set -eux; \
arch="$(dpkg --print-architecture)"; \
case "$arch" in \
amd64) target="x86_64-unknown-linux-musl" ;; \
arm64) target="aarch64-unknown-linux-musl" ;; \
*) echo "unsupported arch for codex: $arch" >&2; exit 1 ;; \
esac; \
curl -fsSL --retry 5 --retry-all-errors -o /tmp/codex.tgz \
"https://github.com/openai/codex/releases/download/rust-v${CODEX_VERSION}/codex-${target}.tar.gz"; \
tar -xzf /tmp/codex.tgz -C /tmp; \
install -m 0755 "/tmp/codex-${target}" /usr/local/bin/codex; \
rm -f /tmp/codex.tgz "/tmp/codex-${target}"

# GitHub CLI (gh) for PR creation on github.com — installed from the official release
# tarball, NOT the apt repo: that extra apt source tipped the QEMU-emulated arm64
# build over its memory limit during `apt-get update` (OOM: "Cannot allocate
Expand Down
11 changes: 10 additions & 1 deletion adapters/vk/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,14 @@ const HelpText = "Flock VK assistant — available commands:\n\n" +
"/help — show this message\n" +
"/new — start a fresh session (forget the current conversation)\n" +
"/stop — stop the run currently in progress\n" +
"/schedule — manage scheduled jobs (when enabled)\n\n" +
"/schedule — manage scheduled jobs (when enabled)\n" +
"/goal <criterion> — arm a goal an independent evaluator re-checks after every run " +
"(/goal off to disarm)\n\n" +
"Send any other message to run it through the assistant."

// goalUsageText is the /goal usage reply, mirroring the Telegram adapter.
const goalUsageText = "Usage:\n" +
"/goal <done criterion> — arm a goal; after every completed run an independent evaluator " +
"re-checks the workspace against it and sends the team back until it holds\n" +
"/goal — show the armed goal\n" +
"/goal off — disarm"
Loading