Fixes #757 ## Changes Separate operations from code into {project}-ops repo pattern. Added OPS_REPO_ROOT infrastructure (env.sh, load-project.sh, formula-session.sh with ensure_ops_repo helper). Updated all 8 agent scripts and 7 formulas to read/write vault items, journals, evidence, prerequisites, RESOURCES.md, and knowledge from the ops repo. Added setup_ops_repo() to disinto init for automatic ops repo creation and seeding. Removed migrated data from code repo (vault data dirs, planner journal/memory/prerequisites, supervisor journal/best-practices, evidence, RESOURCES.md). Updated all documentation. 55 files changed, ShellCheck clean, all 38 phase tests pass. Co-authored-by: openhands <openhands@all-hands.dev> Reviewed-on: https://codeberg.org/johba/disinto/pulls/767 Reviewed-by: Disinto_bot <disinto_bot@noreply.codeberg.org>
9.1 KiB
9.1 KiB
Shared Helpers (lib/)
All agents source lib/env.sh as their first action. Additional helpers are
sourced as needed.
| File | What it provides | Sourced by |
|---|---|---|
lib/env.sh |
Loads .env, sets FACTORY_ROOT, exports project config (FORGE_REPO, PROJECT_NAME, etc.), defines log(), forge_api(), forge_api_all() (accepts optional second TOKEN parameter, defaults to $FORGE_TOKEN), woodpecker_api(), wpdb(), memory_guard() (skips agent if RAM < threshold). Auto-loads project TOML if PROJECT_TOML is set. Exports per-agent tokens (FORGE_PLANNER_TOKEN, FORGE_GARDENER_TOKEN, FORGE_VAULT_TOKEN, FORGE_SUPERVISOR_TOKEN, FORGE_PREDICTOR_TOKEN, FORGE_ACTION_TOKEN) — each falls back to $FORGE_TOKEN if not set. Vault-only token guard (AD-006): unset GITHUB_TOKEN CLAWHUB_TOKEN so agents never hold external-action tokens — only the vault-runner container receives them. Container note: when DISINTO_CONTAINER=1, .env is NOT re-sourced — compose already injects env vars (including FORGE_URL=http://forgejo:3000) and re-sourcing would clobber them. |
Every agent |
lib/ci-helpers.sh |
ci_passed() — returns 0 if CI state is "success" (or no CI configured). ci_required_for_pr() — returns 0 if PR has code files (CI required), 1 if non-code only (CI not required). is_infra_step() — returns 0 if a single CI step failure matches infra heuristics (clone/git exit 128, any exit 137, log timeout patterns). classify_pipeline_failure() — returns "infra <reason>" if any failed Woodpecker step matches infra heuristics via is_infra_step(), else "code". ensure_priority_label() — looks up (or creates) the priority label and returns its ID; caches in _PRIORITY_LABEL_ID. ci_commit_status <sha> — queries Woodpecker directly for CI state, falls back to forge commit status API. ci_pipeline_number <sha> — returns the Woodpecker pipeline number for a commit, falls back to parsing forge status target_url. ci_promote <repo_id> <pipeline_num> <environment> — promotes a pipeline to a named Woodpecker environment (vault-gated deployment: vault approves, vault-fire calls this). |
dev-poll, review-poll, review-pr, supervisor-poll |
lib/ci-debug.sh |
CLI tool for Woodpecker CI: list, status, logs, failures subcommands. Not sourced — run directly. |
Humans / dev-agent (tool access) |
lib/load-project.sh |
Parses a projects/*.toml file into env vars (PROJECT_NAME, FORGE_REPO, WOODPECKER_REPO_ID, monitoring toggles, mirror config, etc.). |
env.sh (when PROJECT_TOML is set), supervisor-poll (per-project iteration) |
lib/parse-deps.sh |
Extracts dependency issue numbers from an issue body (stdin → stdout, one number per line). Matches ## Dependencies / ## Depends on / ## Blocked by sections and inline depends on #N / blocked by #N patterns. Inline scan skips fenced code blocks to prevent false positives from code examples in issue bodies. Not sourced — executed via bash lib/parse-deps.sh. |
dev-poll, supervisor-poll |
lib/formula-session.sh |
acquire_cron_lock(), check_memory(), load_formula(), build_context_block(), consume_escalation_reply(), start_formula_session(), formula_phase_callback(), build_prompt_footer(), build_graph_section(), run_formula_and_monitor(AGENT [TIMEOUT] [CALLBACK]) — shared helpers for formula-driven cron agents (lock, memory guard, formula loading, prompt assembly, tmux session, monitor loop, crash recovery). build_graph_section() generates the structural-analysis section (runs lib/build-graph.py, formats JSON output) — previously duplicated in planner-run.sh and predictor-run.sh, now shared here. formula_phase_callback() handles PHASE:escalate (unified escalation path — kills the session). run_formula_and_monitor accepts an optional CALLBACK (default: formula_phase_callback) so callers can install custom merge-through or escalation handlers. |
planner-run.sh, predictor-run.sh, gardener-run.sh, supervisor-run.sh, dev-agent.sh, action-agent.sh |
lib/guard.sh |
check_active(agent_name) — reads $FACTORY_ROOT/state/.{agent_name}-active; exits 0 (skip) if the file is absent. Factory is off by default — state files must be created to enable each agent. Logs a message to stderr when skipping ([check_active] SKIP: state file not found), so agent dropout is visible in cron logs. Sourced by dev-poll.sh, review-poll.sh, action-poll.sh, predictor-run.sh, supervisor-run.sh. |
cron entry points |
lib/mirrors.sh |
mirror_push() — pushes $PRIMARY_BRANCH + tags to all configured mirror remotes (fire-and-forget background pushes). Reads MIRROR_NAMES and MIRROR_* vars exported by load-project.sh from the [mirrors] TOML section. Failures are logged but never block the pipeline. Sourced by dev-poll.sh and dev/phase-handler.sh — called after every successful merge. |
dev-poll.sh, phase-handler.sh |
lib/build-graph.py |
Python tool: parses VISION.md, prerequisites.md (from ops repo), AGENTS.md, formulas/*.toml, evidence/ (from ops repo), and forge issues/labels into a NetworkX DiGraph. Runs structural analyses (orphaned objectives, stale prerequisites, thin evidence, circular deps) and outputs a JSON report. Used by review-pr.sh (per-PR changed-file analysis) and predictor-run.sh (full-project analysis) to provide structural context to Claude. |
review-pr.sh, predictor-run.sh |
lib/secret-scan.sh |
scan_for_secrets() — detects potential secrets (API keys, bearer tokens, private keys, URLs with embedded credentials) in text; returns 1 if secrets found. redact_secrets() — replaces detected secret patterns with [REDACTED]. |
file-action-issue.sh, phase-handler.sh |
lib/file-action-issue.sh |
file_action_issue() — dedup check, secret scan, label lookup, and issue creation for formula-driven cron wrappers. Sets FILED_ISSUE_NUM on success. Returns 4 if secrets detected in body. |
(available for future use) |
lib/tea-helpers.sh |
tea_file_issue(title, body, labels...) — create issue via tea CLI with secret scanning; sets FILED_ISSUE_NUM. tea_relabel(issue_num, labels...) — replace labels using tea's edit subcommand (not label). tea_comment(issue_num, body) — add comment with secret scanning. tea_close(issue_num) — close issue. All use TEA_LOGIN and FORGE_REPO from env.sh. Labels by name (no ID lookup). Tea binary download verified via sha256 checksum. Sourced by env.sh when tea binary is available. |
env.sh (conditional) |
lib/agent-session.sh |
Shared tmux + Claude session helpers: create_agent_session(), inject_formula(), agent_wait_for_claude_ready(), agent_inject_into_session(), agent_kill_session(), monitor_phase_loop(), read_phase(), write_compact_context(). create_agent_session(session, workdir, [phase_file]) optionally installs a PostToolUse hook (matcher Bash|Write) that detects phase file writes in real-time — when Claude writes to the phase file, the hook writes a marker so monitor_phase_loop reacts on the next poll instead of waiting for mtime changes. Also installs a StopFailure hook (matcher rate_limit|server_error|authentication_failed|billing_error) that writes PHASE:failed with an api_error reason to the phase file and touches the phase-changed marker, so the orchestrator discovers API errors within one poll cycle instead of waiting for idle timeout. Also installs a SessionStart hook (matcher compact) that re-injects phase protocol instructions after context compaction — callers write the context file via write_compact_context(phase_file, content), and the hook (on-compact-reinject.sh) outputs the file content to stdout so Claude retains critical instructions. When phase_file is set, passes it to the idle stop hook (on-idle-stop.sh) so the hook can nudge Claude (up to 2 times) if Claude returns to the prompt without writing to the phase file — the hook injects a tmux reminder asking Claude to signal PHASE:done or PHASE:awaiting_ci. The PreToolUse guard hook (on-pretooluse-guard.sh) receives the session name as a third argument — formula agents (gardener-*, planner-*, predictor-*, supervisor-*) are identified this way and allowed to access FACTORY_ROOT from worktrees (they need env.sh, AGENTS.md, formulas/, lib/). OAuth flock: when DISINTO_CONTAINER=1, Claude CLI is wrapped in flock -w 300 ~/.claude/session.lock to queue concurrent token refresh attempts and prevent rotation races across agents sharing the same credentials. monitor_phase_loop sets _MONITOR_LOOP_EXIT to one of: done, idle_timeout, idle_prompt (Claude returned to > for 3 consecutive polls without writing any phase — callback invoked with PHASE:failed, session already dead), crashed, or PHASE:escalate / other PHASE:* string. Unified escalation: PHASE:escalate is the signal that a session needs human input (renamed from PHASE:needs_human). Callers must handle idle_prompt in both their callback and their post-loop exit handler — see docs/PHASE-PROTOCOL.md idle_prompt for the full contract. |
dev-agent.sh, action-agent.sh |