Merge pull request 'fix: docs: per-agent AGENTS.md files still describe cron triggers (#547)' (#567) from fix/issue-547 into main
All checks were successful
ci/woodpecker/push/ci Pipeline was successful

This commit is contained in:
dev-bot 2026-04-10 08:42:02 +00:00
commit da4d9077dd
7 changed files with 96 additions and 64 deletions

View file

@ -67,7 +67,7 @@ resumes the session using `--resume session_id` to preserve codebase context.
## Execution ## Execution
Run via `architect/architect-run.sh`, which: Run via `architect/architect-run.sh`, which:
- Acquires a cron lock and checks available memory - Acquires a poll-loop lock (via `acquire_lock`) and checks available memory
- Cleans up per-issue scratch files from previous runs (`/tmp/architect-{project}-scratch-*.md`) - Cleans up per-issue scratch files from previous runs (`/tmp/architect-{project}-scratch-*.md`)
- Sources shared libraries (env.sh, formula-session.sh) - Sources shared libraries (env.sh, formula-session.sh)
- Uses FORGE_ARCHITECT_TOKEN for authentication - Uses FORGE_ARCHITECT_TOKEN for authentication
@ -88,12 +88,10 @@ Run via `architect/architect-run.sh`, which:
- Bash creates the PR with pitch content and posts ACCEPT/REJECT footer comment - Bash creates the PR with pitch content and posts ACCEPT/REJECT footer comment
- Branch names use issue number (architect/sprint-vision-{issue_number}) to avoid collisions - Branch names use issue number (architect/sprint-vision-{issue_number}) to avoid collisions
## Cron ## Schedule
Suggested cron entry (every 6 hours): The architect runs every 6 hours as part of the polling loop in
```cron `docker/agents/entrypoint.sh` (iteration math at line 196-208).
0 */6 * * * cd /path/to/disinto && bash architect/architect-run.sh
```
## State ## State

View file

@ -4,17 +4,36 @@
**Role**: Implement issues autonomously — write code, push branches, address **Role**: Implement issues autonomously — write code, push branches, address
CI failures and review feedback. CI failures and review feedback.
**Trigger**: `dev-poll.sh` runs every 10 min via cron. Sources `lib/guard.sh` and **Trigger**: `dev-poll.sh` is invoked by the polling loop in `docker/agents/entrypoint.sh`
calls `check_active dev` first — skips if `$FACTORY_ROOT/state/.dev-active` is every 5 minutes (iteration math at line 171-175). Sources `lib/guard.sh` and calls
absent. Then performs a direct-merge scan (approved + CI green PRs — including `check_active dev` first — skips if `$FACTORY_ROOT/state/.dev-active` is absent. Then
chore/gardener PRs without issue numbers), then checks the agent lock and scans performs a direct-merge scan (approved + CI green PRs — including chore/gardener PRs
for ready issues using a two-tier priority queue: (1) `priority`+`backlog` issues without issue numbers), then checks the agent lock and scans for ready issues using a
first (FIFO within tier), then (2) plain `backlog` issues (FIFO). Orphaned two-tier priority queue: (1) `priority`+`backlog` issues first (FIFO within tier), then
in-progress issues are also picked up. The direct-merge scan runs before the lock (2) plain `backlog` issues (FIFO). Orphaned in-progress issues are also picked up. The
check so approved PRs get merged even while a dev-agent session is active. direct-merge scan runs before the lock check so approved PRs get merged even while a
dev-agent session is active.
**Key files**: **Key files**:
- `dev/dev-poll.sh` — Cron scheduler: finds next ready issue, handles merge/rebase of approved PRs, tracks CI fix attempts. `BOT_USER` is resolved once at startup via the Forge `/user` API and cached for all assignee checks. Formula guard skips issues labeled `formula`, `prediction/dismissed`, or `prediction/unreviewed`. **Race prevention**: checks issue assignee before claiming — skips if assigned to a different bot user. **Stale branch abandonment**: closes PRs and deletes branches that are behind `$PRIMARY_BRANCH` (restarts poll cycle for a fresh start). **Stale in-progress recovery**: on each poll cycle, scans for issues labeled `in-progress`. If the issue has a `vision` label, sets `BLOCKED_BY_INPROGRESS=true` and skips further stale checks (vision issues are managed by the architect). If the issue is assigned to `$BOT_USER` (this agent), checks for pending review feedback first — if an open PR has `REQUEST_CHANGES`, spawns the dev-agent to address it before setting `BLOCKED_BY_INPROGRESS=true`; otherwise just sets blocked. If assigned to another agent, logs and falls through (does not block). If no assignee, no open PR, and no agent lock file — removes `in-progress`, adds `blocked` with a human-triage comment. **Per-agent open-PR gate**: before starting new work, filters open waiting PRs to only those assigned to this agent (`$BOT_USER`). Other agents' PRs do not block this agent's pipeline (#358, #369). **Pre-lock merge scan own-PRs only**: the direct-merge scan only merges PRs whose linked issue is assigned to this agent — skips PRs owned by other bot users (#374). - `dev/dev-poll.sh` — Polling loop participant: finds next ready issue, handles merge/rebase
of approved PRs, tracks CI fix attempts. Invoked by `docker/agents/entrypoint.sh` every 5
minutes. `BOT_USER` is resolved once at startup via the Forge `/user` API and cached for
all assignee checks. Formula guard skips issues labeled `formula`, `prediction/dismissed`,
or `prediction/unreviewed`. **Race prevention**: checks issue assignee before claiming —
skips if assigned to a different bot user. **Stale branch abandonment**: closes PRs and
deletes branches that are behind `$PRIMARY_BRANCH` (restarts poll cycle for a fresh start).
**Stale in-progress recovery**: on each poll cycle, scans for issues labeled `in-progress`.
If the issue has a `vision` label, sets `BLOCKED_BY_INPROGRESS=true` and skips further
stale checks (vision issues are managed by the architect). If the issue is assigned to
`$BOT_USER` (this agent), checks for pending review feedback first — if an open PR has
`REQUEST_CHANGES`, spawns the dev-agent to address it before setting `BLOCKED_BY_INPROGRESS=true`;
otherwise just sets blocked. If assigned to another agent, logs and falls through (does not
block). If no assignee, no open PR, and no agent lock file — removes `in-progress`, adds
`blocked` with a human-triage comment. **Per-agent open-PR gate**: before starting new work,
filters open waiting PRs to only those assigned to this agent (`$BOT_USER`). Other agents'
PRs do not block this agent's pipeline (#358, #369). **Pre-lock merge scan own-PRs only**:
the direct-merge scan only merges PRs whose linked issue is assigned to this agent — skips
PRs owned by other bot users (#374).
- `dev/dev-agent.sh` — Orchestrator: claims issue, creates worktree + tmux session with interactive `claude`, monitors phase file, injects CI results and review feedback, merges on approval - `dev/dev-agent.sh` — Orchestrator: claims issue, creates worktree + tmux session with interactive `claude`, monitors phase file, injects CI results and review feedback, merges on approval
- `dev/phase-test.sh` — Integration test for the phase protocol - `dev/phase-test.sh` — Integration test for the phase protocol
@ -32,9 +51,9 @@ check so approved PRs get merged even while a dev-agent session is active.
**Crash recovery**: on `PHASE:crashed` or non-zero exit, the worktree is **preserved** (not destroyed) for debugging. Location logged. Supervisor housekeeping removes stale crashed worktrees older than 24h. **Crash recovery**: on `PHASE:crashed` or non-zero exit, the worktree is **preserved** (not destroyed) for debugging. Location logged. Supervisor housekeeping removes stale crashed worktrees older than 24h.
**Lifecycle**: dev-poll.sh (`check_active dev`) → dev-agent.sh → tmux session → phase file **Lifecycle**: dev-poll.sh (invoked by polling loop, `check_active dev`) → dev-agent.sh →
drives CI/review loop → merge + `mirror_push()` → close issue. On respawn after tmux session → phase file drives CI/review loop → merge + `mirror_push()` → close issue.
`PHASE:escalate`, the stale phase file is cleared first so the session starts On respawn after `PHASE:escalate`, the stale phase file is cleared first so the session
clean; the reinject prompt tells Claude not to re-escalate for the same reason. starts clean; the reinject prompt tells Claude not to re-escalate for the same reason.
On respawn for any active PR, the prompt explicitly tells Claude the PR already On respawn for any active PR, the prompt explicitly tells Claude the PR already exists
exists and not to create a new one via API. and not to create a new one via API.

View file

@ -7,18 +7,18 @@ the quality gate: strips the `backlog` label from issues that lack acceptance
criteria checkboxes (`- [ ]`) or an `## Affected files` section. Invokes criteria checkboxes (`- [ ]`) or an `## Affected files` section. Invokes
Claude to fix what it can; files vault items for what it cannot. Claude to fix what it can; files vault items for what it cannot.
**Trigger**: `gardener-run.sh` runs 4x/day via cron. Sources `lib/guard.sh` and **Trigger**: `gardener-run.sh` is invoked by the polling loop in `docker/agents/entrypoint.sh`
calls `check_active gardener` first — skips if `$FACTORY_ROOT/state/.gardener-active` every 6 hours (iteration math at line 182-194). Sources `lib/guard.sh` and calls
is absent. **Early-exit optimization**: if no issues, PRs, or repo files have `check_active gardener` first — skips if `$FACTORY_ROOT/state/.gardener-active` is absent.
changed since the last run (checked via Forgejo API and `git diff`), the model **Early-exit optimization**: if no issues, PRs, or repo files have changed since the last
is not invoked — the run exits immediately (no tmux session, no tokens consumed). run (checked via Forgejo API and `git diff`), the model is not invoked — the run exits
Otherwise, creates a tmux session with `claude --model sonnet`, injects immediately (no tmux session, no tokens consumed). Otherwise, creates a tmux session with
`formulas/run-gardener.toml` as context, monitors the phase file, and cleans up `claude --model sonnet`, injects `formulas/run-gardener.toml` as context, monitors the
on completion or timeout (2h max session). No action issues — the gardener runs phase file, and cleans up on completion or timeout (2h max session). No action issues —
directly from cron like the planner, predictor, and supervisor. the gardener runs as part of the polling loop alongside the planner, predictor, and supervisor.
**Key files**: **Key files**:
- `gardener/gardener-run.sh`Cron wrapper + orchestrator: lock, memory guard, - `gardener/gardener-run.sh`Polling loop participant + orchestrator: lock, memory guard,
sources disinto project config, creates tmux session, injects formula prompt, sources disinto project config, creates tmux session, injects formula prompt,
monitors phase file via custom `_gardener_on_phase_change` callback (passed to monitors phase file via custom `_gardener_on_phase_change` callback (passed to
`run_formula_and_monitor`). Stays alive through CI/review/merge cycle after `run_formula_and_monitor`). Stays alive through CI/review/merge cycle after
@ -35,8 +35,8 @@ directly from cron like the planner, predictor, and supervisor.
- `FORGE_TOKEN`, `FORGE_GARDENER_TOKEN` (falls back to FORGE_TOKEN), `FORGE_REPO`, `FORGE_API`, `PROJECT_NAME`, `PROJECT_REPO_ROOT` - `FORGE_TOKEN`, `FORGE_GARDENER_TOKEN` (falls back to FORGE_TOKEN), `FORGE_REPO`, `FORGE_API`, `PROJECT_NAME`, `PROJECT_REPO_ROOT`
- `PRIMARY_BRANCH`, `CLAUDE_MODEL` (set to sonnet by gardener-run.sh) - `PRIMARY_BRANCH`, `CLAUDE_MODEL` (set to sonnet by gardener-run.sh)
**Lifecycle**: gardener-run.sh (cron 0,6,12,18) → `check_active gardener` → lock + memory guard **Lifecycle**: gardener-run.sh (invoked by polling loop every 6h, `check_active gardener`)
load formula + context → create tmux session → lock + memory guard → load formula + context → create tmux session →
Claude grooms backlog (writes proposed actions to manifest), bundles dust, Claude grooms backlog (writes proposed actions to manifest), bundles dust,
updates AGENTS.md, commits manifest + docs to PR → updates AGENTS.md, commits manifest + docs to PR →
`PHASE:awaiting_ci` (stays alive) → CI pass → `PHASE:awaiting_review` `PHASE:awaiting_ci` (stays alive) → CI pass → `PHASE:awaiting_review`

View file

@ -2,7 +2,7 @@
# Planner Agent # Planner Agent
**Role**: Strategic planning using a Prerequisite Tree (Theory of Constraints), **Role**: Strategic planning using a Prerequisite Tree (Theory of Constraints),
executed directly from cron via tmux + Claude. invoked by the polling loop in `docker/agents/entrypoint.sh` every 12 hours (iteration math at line 210-222) via tmux + Claude.
Phase 0 (preflight): pull latest code, load persistent memory and prerequisite Phase 0 (preflight): pull latest code, load persistent memory and prerequisite
tree from `$OPS_REPO_ROOT/knowledge/planner-memory.md` and `$OPS_REPO_ROOT/prerequisites.md`. Also reads tree from `$OPS_REPO_ROOT/knowledge/planner-memory.md` and `$OPS_REPO_ROOT/prerequisites.md`. Also reads
all available formulas: factory formulas (`$FACTORY_ROOT/formulas/*.toml`) and all available formulas: factory formulas (`$FACTORY_ROOT/formulas/*.toml`) and
@ -41,16 +41,16 @@ AGENTS.md maintenance is handled by the Gardener.
prerequisite tree, memory, vault state) live under `$OPS_REPO_ROOT/`. prerequisite tree, memory, vault state) live under `$OPS_REPO_ROOT/`.
Each project manages its own planner state in a separate ops repo. Each project manages its own planner state in a separate ops repo.
**Trigger**: `planner-run.sh` runs daily via cron (accepts an optional project **Trigger**: `planner-run.sh` is invoked by the polling loop in `docker/agents/entrypoint.sh`
TOML argument, defaults to `projects/disinto.toml`). Sources `lib/guard.sh` and every 12 hours (iteration math at line 210-222). Accepts an optional project TOML argument,
calls `check_active planner` first — skips if `$FACTORY_ROOT/state/.planner-active` defaults to `projects/disinto.toml`. Sources `lib/guard.sh` and calls `check_active planner`
is absent. Then creates a tmux session with `claude --model opus`, injects first — skips if `$FACTORY_ROOT/state/.planner-active` is absent. Then creates a tmux session
`formulas/run-planner.toml` as context, monitors the phase file, and cleans up with `claude --model opus`, injects `formulas/run-planner.toml` as context, monitors the
on completion or timeout. No action issues — the planner is a nervous system phase file, and cleans up on completion or timeout. No action issues — the planner is a
component, not work. nervous system component, not work.
**Key files**: **Key files**:
- `planner/planner-run.sh`Cron wrapper + orchestrator: lock, memory guard, - `planner/planner-run.sh`Polling loop participant + orchestrator: lock, memory guard,
sources disinto project config, builds structural analysis via `lib/formula-session.sh:build_graph_section()`, sources disinto project config, builds structural analysis via `lib/formula-session.sh:build_graph_section()`,
creates tmux session, injects formula prompt, monitors phase file, handles crash recovery, cleans up creates tmux session, injects formula prompt, monitors phase file, handles crash recovery, cleans up
- `formulas/run-planner.toml` — Execution spec: six steps (preflight, - `formulas/run-planner.toml` — Execution spec: six steps (preflight,

View file

@ -22,14 +22,15 @@ exploit counts as 2 (prediction + action dispatch). The predictor MUST NOT
emit feature work — only observations challenging claims, exposing gaps, emit feature work — only observations challenging claims, exposing gaps,
and surfacing risks. and surfacing risks.
**Trigger**: `predictor-run.sh` runs daily at 06:00 UTC via cron (1h before **Trigger**: `predictor-run.sh` is invoked by the polling loop in `docker/agents/entrypoint.sh`
the planner at 07:00). Sources `lib/guard.sh` and calls `check_active predictor` every 24 hours (iteration math at line 224-236). Sources `lib/guard.sh` and calls
first — skips if `$FACTORY_ROOT/state/.predictor-active` is absent. Also guarded `check_active predictor` first — skips if `$FACTORY_ROOT/state/.predictor-active` is absent.
by PID lock (`/tmp/predictor-run.lock`) and memory check (skips if available Also guarded by PID lock (`/tmp/predictor-run.lock`) and memory check (skips if available
RAM < 2000 MB). RAM < 2000 MB). Note: the 24h cadence is iteration-based, not anchored to 06:00 UTC
drifts on container restart.
**Key files**: **Key files**:
- `predictor/predictor-run.sh`Cron wrapper + orchestrator: active-state guard, - `predictor/predictor-run.sh`Polling loop participant + orchestrator: active-state guard,
lock, memory guard, sources disinto project config, builds structural analysis lock, memory guard, sources disinto project config, builds structural analysis
via `lib/formula-session.sh:build_graph_section()` (full-project scan — results via `lib/formula-session.sh:build_graph_section()` (full-project scan — results
included in prompt as `## Structural analysis`; failures non-fatal), builds included in prompt as `## Structural analysis`; failures non-fatal), builds
@ -44,7 +45,7 @@ RAM < 2000 MB).
- `FORGE_TOKEN`, `FORGE_PREDICTOR_TOKEN` (falls back to FORGE_TOKEN), `FORGE_REPO`, `FORGE_API`, `PROJECT_NAME`, `PROJECT_REPO_ROOT`, `OPS_REPO_ROOT` - `FORGE_TOKEN`, `FORGE_PREDICTOR_TOKEN` (falls back to FORGE_TOKEN), `FORGE_REPO`, `FORGE_API`, `PROJECT_NAME`, `PROJECT_REPO_ROOT`, `OPS_REPO_ROOT`
- `PRIMARY_BRANCH`, `CLAUDE_MODEL` (set to sonnet by predictor-run.sh) - `PRIMARY_BRANCH`, `CLAUDE_MODEL` (set to sonnet by predictor-run.sh)
**Lifecycle**: predictor-run.sh (daily 06:00 cron) → lock + memory guard → **Lifecycle**: predictor-run.sh (invoked by polling loop every 24h) → lock + memory guard →
load formula + context (AGENTS.md, VISION.md from code repo; RESOURCES.md, prerequisites.md from ops repo) load formula + context (AGENTS.md, VISION.md from code repo; RESOURCES.md, prerequisites.md from ops repo)
→ create tmux session → Claude fetches prediction history (open + closed) → → create tmux session → Claude fetches prediction history (open + closed) →
reviews track record (actioned/dismissed/watching) → finds weaknesses reviews track record (actioned/dismissed/watching) → finds weaknesses

View file

@ -4,13 +4,26 @@
**Role**: AI-powered PR review — post structured findings and formal **Role**: AI-powered PR review — post structured findings and formal
approve/request-changes verdicts to forge. approve/request-changes verdicts to forge.
**Trigger**: `review-poll.sh` runs every 10 min via cron. It scans open PRs **Trigger**: `review-poll.sh` is invoked by the polling loop in `docker/agents/entrypoint.sh`
whose CI has passed and that lack a review for the current HEAD SHA, then every 5 minutes (iteration math at line 163-167). It scans open PRs whose CI has passed and
spawns `review-pr.sh <pr-number>`. that lack a review for the current HEAD SHA, then spawns `review-pr.sh <pr-number>`.
**Key files**: **Key files**:
- `review/review-poll.sh` — Cron scheduler: finds unreviewed PRs with passing CI. Sources `lib/guard.sh` and calls `check_active reviewer` — skips if `$FACTORY_ROOT/state/.reviewer-active` is absent. **Circuit breaker**: counts existing `<!-- review-error: <sha> -->` comments; skips a PR if ≥3 consecutive errors for the same HEAD SHA (prevents flooding on repeated review failures). - `review/review-poll.sh` — Polling loop participant: finds unreviewed PRs with passing CI.
- `review/review-pr.sh` — Creates/reuses a tmux session (`review-{project}-{pr}`), injects PR diff, waits for Claude to write structured JSON output, posts markdown review + formal forge review, auto-creates follow-up issues for pre-existing tech debt. **cd at startup**: changes to `$PROJECT_REPO_ROOT` early in the script — before any git commands — because the factory root is not a git repo after image rebuild (#408). Calls `resolve_forge_remote()` at startup to determine the correct git remote name (avoids hardcoded 'origin'). Before starting the session, runs `lib/build-graph.py --changed-files <PR files>` and appends the JSON structural analysis (affected objectives, orphaned prerequisites, thin evidence) to the review prompt. Graph failures are non-fatal — review proceeds without it. Invoked by `docker/agents/entrypoint.sh` every 5 minutes. Sources `lib/guard.sh` and calls
`check_active reviewer` — skips if `$FACTORY_ROOT/state/.reviewer-active` is absent.
**Circuit breaker**: counts existing `<!-- review-error: <sha> -->` comments; skips a PR
if ≥3 consecutive errors for the same HEAD SHA (prevents flooding on repeated review failures).
- `review/review-pr.sh` — Polling loop participant: Creates/reuses a tmux session
(`review-{project}-{pr}`), injects PR diff, waits for Claude to write structured JSON output,
posts markdown review + formal forge review, auto-creates follow-up issues for pre-existing
tech debt. **cd at startup**: changes to `$PROJECT_REPO_ROOT` early in the script — before
any git commands — because the factory root is not a git repo after image rebuild (#408).
Calls `resolve_forge_remote()` at startup to determine the correct git remote name (avoids
hardcoded 'origin'). Before starting the session, runs `lib/build-graph.py --changed-files
<PR files>` and appends the JSON structural analysis (affected objectives, orphaned
prerequisites, thin evidence) to the review prompt. Graph failures are non-fatal — review
proceeds without it.
**Environment variables consumed**: **Environment variables consumed**:
- `FORGE_TOKEN` — Dev-agent token (must not be the same account as FORGE_REVIEW_TOKEN) - `FORGE_TOKEN` — Dev-agent token (must not be the same account as FORGE_REVIEW_TOKEN)

View file

@ -7,15 +7,16 @@ then runs an interactive Claude session (sonnet) that assesses health, auto-fixe
issues, and writes a daily journal. When blocked on external issues, and writes a daily journal. When blocked on external
resources or human decisions, files vault items instead of escalating directly. resources or human decisions, files vault items instead of escalating directly.
**Trigger**: `supervisor-run.sh` runs every 20 min via cron. Sources `lib/guard.sh` **Trigger**: `supervisor-run.sh` is invoked by the polling loop in `docker/edge/entrypoint-edge.sh`
and calls `check_active supervisor` first — skips if every 20 minutes (line 50-53). Sources `lib/guard.sh` and calls `check_active supervisor` first
`$FACTORY_ROOT/state/.supervisor-active` is absent. Then runs `claude -p` — skips if `$FACTORY_ROOT/state/.supervisor-active` is absent. Then runs `claude -p` via
via `agent-sdk.sh`, injects `formulas/run-supervisor.toml` with `agent-sdk.sh`, injects `formulas/run-supervisor.toml` with pre-collected metrics as context,
pre-collected metrics as context, and cleans up on completion or timeout (20 min max session). and cleans up on completion or timeout (20 min max session). Note: the supervisor runs in the
No action issues — the supervisor runs directly from cron like the planner and predictor. **edge container** (`entrypoint-edge.sh`), not the agent container — this distinction matters
for operators debugging the factory.
**Key files**: **Key files**:
- `supervisor/supervisor-run.sh`Cron wrapper + orchestrator: lock, memory guard, - `supervisor/supervisor-run.sh`Polling loop participant + orchestrator: lock, memory guard,
runs preflight.sh, sources disinto project config, runs claude -p via agent-sdk.sh, runs preflight.sh, sources disinto project config, runs claude -p via agent-sdk.sh,
injects formula prompt with metrics, handles crash recovery injects formula prompt with metrics, handles crash recovery
- `supervisor/preflight.sh` — Data collection: system resources (RAM, disk, swap, - `supervisor/preflight.sh` — Data collection: system resources (RAM, disk, swap,
@ -46,6 +47,6 @@ P3 (degraded PRs, circular deps, stale deps), P4 (housekeeping).
- Files vault items locally to `$PROJECT_REPO_ROOT/vault/pending/` - Files vault items locally to `$PROJECT_REPO_ROOT/vault/pending/`
- Logs a WARNING message at startup indicating degraded mode - Logs a WARNING message at startup indicating degraded mode
**Lifecycle**: supervisor-run.sh (cron */20) → lock + memory guard → run **Lifecycle**: supervisor-run.sh (invoked by polling loop every 20min, `check_active supervisor`)
preflight.sh (collect metrics) → load formula + context → run claude -p via agent-sdk.sh → lock + memory guard → run preflight.sh (collect metrics) → load formula + context → run
→ Claude assesses health, auto-fixes, writes journal → `PHASE:done`. claude -p via agent-sdk.sh → Claude assesses health, auto-fixes, writes journal → `PHASE:done`.