wlfghdr/agentic-dev

★ 2Forks 0ShellGitHub ↗Compare

Project website ↗

agenticagentic-codingai

README

agentic-dev

Deterministic engineering triage loop. Production-grade dispatching, code generation, conflict resolution, PR verification, and code review for agentic software engineering teams. No LLM calls during detection. Matches agent throughput to human approval bandwidth.

License Version

Website: wlfghdr.github.io/agentic-dev

The agentic-* suite

agentic-dev is the execution layer of the agentic-* suite — three building blocks for running an agentic organization on Git:

Repo Role
agentic-enterprise The operating model — governance layers, process loops, policies, and templates. Humans decide, agents execute, Git governs.
agentic-kb The knowledge layer — layered, vendor-neutral knowledge ops via the /kb command.
agentic-dev The execution layer — deterministic engineering triage and execution loop (this repo).

The Concept: Human-in-the-Loop Git Ops

In a mature agentic organization:

  • Git is the operating system of the company.
  • Signals (customer issues, observability alerts) are turned into Missions (GitHub Issues).
  • Missions are picked up by autonomous Engineers (AI agents).
  • Engineer agents propose changes via Pull Requests.
  • Reviewer agents audit the PRs and verify tests, passing them to Humans for approval and release.

agentic-dev is the engine that drives this cycle, ensuring the workspace remains clean, tasks do not conflict, and agents only work on what is ready.

          ┌─────────────┐
          │  GH Issue   │ (Mission assigned to agent)
          └──────┬──────┘
                 │
                 ▼
          ┌─────────────┐
          │  detect.py  │ (Deterministic, no-LLM scanner)
          └──────┬──────┘
                 │
                 ▼
          ┌─────────────┐
          │   tick.sh   │ (Dispatches parallel transient systemd jobs)
          └──────┬──────┘
                 │
        ┌────────┴────────┐
        ▼                 ▼
 ┌─────────────┐   ┌─────────────┐
 │ engineer.sh │   │  review.sh  │
 └──────┬──────┘   └──────┬──────┘
        │                 │
        ▼                 ▼
 ┌─────────────┐   ┌─────────────┐
 │ Codex/Kiro/ │   │ Claude/Kiro/│ (LLM execution chains)
 │ Claude/AGY  │   │ Codex/AGY   │
 └──────┬──────┘   └──────┬──────┘
        │                 │
        ▼                 ▼
 ┌─────────────┐   ┌─────────────┐
 │ PR Created  │   │   Verdict   │ (merge-ready / needs-fix / blocked)
 └─────────────┘   └─────────────┘

Repository Structure

  • scripts/
    • detect.py: Deterministically scans watched repositories to identify issues assigned to the agent, PRs needing review, PRs falling behind, Dependabot PRs ready to merge, and repositories due for a release. No LLM calls are made here.
    • tick.sh: The orchestrator timer script. Acquires item locks, checks concurrency limits, and dispatches tasks to transient systemd-run units for safe, parallel execution.
    • cli_dispatch.sh: Shared CLI-chain configuration and execution helpers — resolves the configured agent CLI chain and runs prompts against it.
    • engineer.sh: Sets up repository worktrees, runs the designated agent chain to write code/resolve conflicts, and pushes results or opens a PR.
    • review.sh: Pulls the code, dispatches the reviewer agent chain, runs local verification tests, and posts the review comment with a final VERDICT.
    • merge.sh: Automatically merges approved PRs if automerge is enabled for the repository.
    • dependabot_merge.sh: Deterministically merges green Dependabot PRs; behind/conflicting PRs are rebased first.
    • release.sh: Creates at most one deterministic GitHub release per repo per UTC day when new commits exist after the latest semver tag.
    • gc_worktrees.sh: Removes dispatch worktrees (and their agent branches) once the issue or PR is closed. Run by tick.sh every TRIAGE_WORKTREE_GC_HOURS (default 6, 0 disables).
  • systemd/
    • triage-tick.service: Systemd service to run the orchestrator tick.
    • triage-tick.timer: Near-realtime timer that triggers the service every 60 seconds.
  • logrotate/
    • agentic-triage: Log rotation configuration to prevent disk space issues on the VPS.
  • tests/
    • test_cli_dispatch.sh: Regression tests for the CLI-chain dispatch helpers.
  • triage.toml: Main configuration file defining the triage agent name, human fallback, limits, execution chains, and watched repositories.

Configuration

The loop is configured via triage.toml (default path: /srv/agentic-dev/triage.toml):

[agent]
login = "agent-login"
human_login = "human-login"

[limits]
max_engineer = 3            # Max parallel engineering dispatches
max_review = 2              # Max parallel review dispatches
max_maintenance = 1         # Max parallel Dependabot/release jobs
open_pr_cap_per_repo = 3    # Cap open PRs per repo to match human approval bandwidth
lock_ttl_hours = 2          # TTL for stale locks

[runtime]
# Optional host-local environment file forwarded to transient dispatch units.
dispatch_env_file = "/etc/agentic-dev/dispatch.env"

[cli_chain]
engineer = ["codex", "claude", "kiro", "agy"]  # Writing code
review   = ["claude", "codex", "kiro", "agy"]  # Code reviews
rebase   = ["claude", "kiro", "agy", "codex"]  # Conflict resolution

[dependabot]
enabled = false             # Opt in to green Dependabot merges without reviewer LLM cost

[release]
enabled = false             # Opt in to daily releases when commits exist after latest semver tag

[cli_tools.kiro]
command = "kiro-cli"
args = ["chat", "--no-interactive", "--trust-all-tools"]
prompt_mode = "arg"

[[repos]]
name = "organization/repository-name"
automerge = true
dependabot_automerge = false
release = false
# Choose the repository's release contract explicitly when releases are on:
# "version", "config_yaml", "ios", "android", or "conventional".
version_source = "conventional"

Built-in command definitions are provided for codex, claude, agy, and kiro. Any tool named in a chain can be configured under [cli_tools.NAME]:

  • command: executable name or path.
  • args: argument array. Each {worktree} token is replaced with the checkout path without shell evaluation.
  • prompt_mode: stdin to pipe the prompt, or arg to append it as the final argument.

For example, a custom agent CLI can be added without changing the scripts:

[cli_tools.my-agent]
command = "/opt/agents/my-agent"
args = ["run", "--workspace", "{worktree}", "--auto-approve"]
prompt_mode = "stdin"

Workflow labels

The loop coordinates through a small set of GitHub labels. Two are human control points you can apply by hand; the rest are agent-managed workflow state. Labels are the source of truth for what the loop will and will not pick up.

Label Set by Meaning / effect
do-not-work human detect.py skips the issue entirely — use it to pause the agent on a specific item.
blocked human or review.sh Halts work: blocked issues are skipped, and a VERDICT: blocked review marks the PR for human attention.
in-progress agent An engineer dispatch is actively working the issue or PR.
needs-review agent PR is awaiting a review dispatch.
changes-requested review.sh Review verdict needs-fix — the engineer chain will revise on the next tick.
approved review.sh Review verdict merge-ready — PR is handed to the human (and auto-merged if automerge is enabled for the repo).

The three review verdicts emitted by review.sh map onto labels as: merge-ready → approved, needs-fix → changes-requested, blocked → blocked.

Review approval and automerge are bound to the immutable head commit inspected by the reviewer. Publishing approval requires permission to read PR metadata and checks, create a commit-pinned review, and update issue labels. Automerge also requires permission to merge the PR. If the head or base changes, checks are not successful, a human stop label is present, review publication fails, or the pinned merge is rejected, the wrapper defers the merge and does not retain the approved label. To stop automatic merging, set the repository's automerge value to false; for an individual PR, apply blocked, do-not-merge, or do-not-work and remove approved if it is already present.

Agent-authored PR titles should follow Conventional Commits (fix: ..., feat: ..., chore(scope): ...). The daily release job derives the next SemVer version from the merged commit subjects: breaking changes create a major bump, feat creates a minor bump, and other merged changes create a patch bump.

Idle Cost and Backpressure

The timer fires every 60 seconds, so every tick is kept cheap and every failure mode that cannot heal within one backoff window is parked instead of retried:

  • One PR listing per repository. detect.py fetches open PRs once per repo and derives the open-PR cap, stale-approval demotion, Dependabot, fix/rebase, review, and linked-issue checks from it. Linked PRs are matched on closingIssuesReferences, the issue-<n> branch, or a closing keyword in the body, which avoids the 30 requests/minute search API. gh pages the request itself, so the TRIAGE_OPEN_PR_LIMIT ceiling (1000) costs nothing on repos with few open PRs; reaching it fails the tick instead of silently detecting against a truncated slice.
  • Release discovery is re-checked hourly when no commits exist since the latest release (TRIAGE_RELEASE_RECHECK_SECONDS, default 3600).
  • CLI cooldowns. When an agent CLI fails with a usage limit, a login failure, or a missing binary, it is parked under state/cli-cooldown/<tool>. An explicit reset time such as try again at Sep 21st, 2026 10:47 PM is honored; otherwise limits park for TRIAGE_CLI_LIMIT_COOLDOWN_SECONDS (900), authentication and missing binaries for TRIAGE_CLI_AUTH_COOLDOWN_SECONDS / TRIAGE_CLI_MISSING_COOLDOWN_SECONDS (3600). Parked CLIs are skipped inside a chain, and tick.sh does not dispatch engineer or review work at all while every CLI in the chain is parked. After re-authenticating a CLI, delete its cooldown file to resume immediately.
  • Logs and history record activity only. Idle ticks go to the journal only; logs/*-tick.log is kept for ticks that dispatched, warned, or failed, and state/history/ gets a snapshot only when the detected work changes. Files in logs/ older than TRIAGE_LOG_RETENTION_DAYS (14) are pruned hourly.

Maintenance Safety

Dependabot merging and daily releases are state-changing maintenance jobs, so they are opt-in at both the global and repository level. Existing installations that omit [dependabot], [release], dependabot_automerge, or release remain disabled until the operator explicitly enables them in triage.toml.

Required GitHub permissions for the authenticated gh account:

  • Dependabot merge: read repository metadata, read PR checks, merge PRs, and delete merged branches when allowed by the repository.
  • Release: read repository metadata and tags, compare commits, and create tags and GitHub releases.

Failure behavior:

  • Dependabot PRs with no checks, pending checks, red checks, unknown check conclusions, draft state, blocked, or do-not-merge are skipped.
  • Dependabot PRs that are behind or conflicting are rebased first. A clean rebase is deterministic; only real conflicts use the configured rebase agent chain. Unresolved conflicts are handed back as blocked.
  • Missing or unreadable maintenance config fails closed. No merge or release is authorized without the explicit repository opt-in.
  • Releases run at most once per UTC day per repository and only when commits exist after the latest stable GitHub release whose tag is strict vMAJOR.MINOR.PATCH. Discovery follows every releases API page, ignores drafts and prereleases, and chooses the numerically greatest usable tag.
  • version_source = "version" reads a root VERSION; "config_yaml" reads framework_version from root CONFIG.yaml; "ios" and "android" read their platform manifests; and "conventional" derives the next version from commit subjects. Repositories with VERSION, CONFIG.yaml, plugin manifests, or supported mobile version fields fail closed if no adapter is configured instead of falling back to 0.0.0.
  • Authoritative adapters require a matching CHANGELOG.md release section. Every tracked plugin.json version must also match. A stale version, manifest, or changelog stops publication with instructions to reconcile the metadata in a pull request; the release job never edits the default branch.

Upgrade notice: this is a breaking release-contract change. Before installing this version, every release-enabled repository with existing version metadata must set version_source explicitly. Choose the adapter matching the authoritative metadata, or choose conventional to retain commit-derived versioning intentionally. Without that migration, release publication fails closed until the repository configuration is updated. Roll back to the prior installed scripts if the configuration cannot be migrated immediately.

Rollback:

  • Set [dependabot].enabled = false or a repo's dependabot_automerge = false to stop dependency auto-merges.
  • Set [release].enabled = false or a repo's release = false to stop daily releases.
  • Reinstall after versioned script changes with ./install.sh; runtime config is preserved by the installer and can be reverted independently.

Forward-only suite version reconciliation

Existing public tags are immutable history. Do not move, delete, or recreate the already published agentic-kb v6.4.1, agentic-enterprise v1.0.0, or agentic-dev v0.4.0 tags. Reconcile each repository through its normal pull request review workflow, then allow the next release to move forward:

  • agentic-kb: prepare at least 6.4.2 in VERSION, every packaged plugin manifest, README version references, and a CHANGELOG.md 6.4.2 section; configure version_source = "version".
  • agentic-enterprise: prepare a version later than both the public tag and its established 4.4.1 framework line (for example 4.4.2) in CONFIG.yaml, README references, packaged manifests, and changelog; configure version_source = "config_yaml".
  • agentic-dev: prepare at least 0.4.1 across its authoritative version and release notes before enabling the corresponding adapter. If this repository intentionally has no stored version contract, explicitly choose version_source = "conventional" and add the 0.4.1 changelog narrative in the reviewed change.

After those pull requests merge, the deterministic job may publish new tags at the reconciled commits. Automatic repair must never rewrite the three historical public releases.


Installation

Prerequisites: Linux with systemd, Python ≥ 3.11 (tomllib), git, and an authenticated gh CLI, plus the agent CLIs you configure in your chains.

Run the installation script as root on your VPS or orchestrator host:

sudo ./install.sh

For an isolated, non-root validation install, redirect the host integration targets and disable systemctl mutations:

TRIAGE_DIR=/tmp/agentic-dev-runtime \
TRIAGE_SYSTEMD_DIR=/tmp/agentic-dev-systemd \
TRIAGE_LOGROTATE_DIR=/tmp/agentic-dev-logrotate \
TRIAGE_SKIP_SYSTEMD=1 \
./install.sh

Production dispatch defaults to repositories under /srv/wulfai/repos and worktrees under /srv/wulfai/worktrees. Set TRIAGE_REPOS_DIR and TRIAGE_WORKTREES_DIR when running the installer to use different roots.

This will:

  1. Copy all scripts to /srv/agentic-dev/bin/.
  2. Initialize directories for state, locks, and history.
  3. Install systemd service and timer files.
  4. Enable the systemd timer.

To enable active dispatches in production, install the systemd drop-in override (set TRIAGE_ENABLE_DISPATCH=1 and configure HOME=/root so the gh CLI can authenticate). See systemd/triage-tick.service.d/dispatch.conf.


Contributing

Issues and PRs welcome. Keep changes focused: this repo is a deterministic loop, not a framework — speculative features belong in agentic-enterprise discussions first.

License

Apache License 2.0 — see LICENSE.

Changelog

Release history lives in CHANGELOG.md.

Contributors

WulfAIwlfghdrdependabot[bot]claudeCopilot

Issues