A statically analyzable, capability-oriented execution platform for nondeterministic programs. Review the authority granted to agents as a plan diff — before they run.
ACP bounds and diffs that grant. It does not verify what remote systems do with it.
Source graph → validate / plan → SQLite desired state → apply → engine → tools + models → trace / logs / audit.
flowchart LR
Source[Source graph] --> VP["validate / plan"]
VP --> SQLite[(SQLite desired state)]
SQLite --> Apply[apply]
Apply --> Engine[engine]
Engine --> Tools[tools + models]
Engine --> Trace[trace / logs / audit]
Expanded diagram, plan-time bounds, and closed-world caveats: docs/architecture.md. Product spec: docs/DESIGN_DOC.md.
This is not another orchestrator (Temporal, Dagger, LangGraph). Those schedule work. ACP's direction is a plan-time effect bound — a sound static upper bound on what an autonomous agent can do, reviewable as a diff (#189 / #191). agentctl plan prints that bound and the authority delta vs stored deployment state.
Today agentctl plan diffs permissions, approvals, models, budgets, C1 risk items, and effect/capability/authority against SQLite desired state:
Plan: 0 to add, 3 to change, 0 to delete
~ update Agent/reviewer
spec.model: "mock/gpt-4" -> "mock/gpt-4o"
spec.tools.1: -> "github"
~ update Policy/default
spec.approvals.requiredFor.0: "tool.helper.echo" -> "tool.github.issues.write"
spec.execution.maxTotalCostUsd: 3 -> 10
~ update Tool/github
spec.permissions.allow.1: -> "issues.write"
Risk delta:
high:
- [high] approval_removal: Approval requirements removed for "tool.helper.echo" (Policy/default).
- [high] budget_relaxation: Cost ceiling increased (Policy/default).
- [high] permission_widening: New write-like tool permission "issues.write" added (Tool/github).
- [high] tool_surface_change: Agent tools list gained write-like tool "github" (Agent/reviewer).
medium:
- [medium] model_change: Agent model changed (Agent/reviewer).
When the graph declares tool operations, plan also prints the effect bound and an authority delta (bound(desired) vs bound(deployed)). Capability changes and effect changes are separate lines; AUTONOMOUS WIDENED means a nondeterministic agent's action space grew — including when a new grant does not add named effects.
Effect bound (Workflow/pr-review):
high:
- [high] effect_bound: github.write autonomous Agent/reviewer may select tool.github.post_comment
medium:
- [medium] effect_bound: github.read static step fetch_pr
Capability delta:
Agent/reviewer
+ tool.github.post_comment
Authority:
static -> unchanged
autonomous -> WIDENED
Specs today are YAML (interchange / compilation output). The long-term authoring surface is .agent (#200); until then you still write YAML — lead on capability, not format.
Start here: examples/incident-triage — an offline agent that can page, read logs, and file a ticket, but cannot restart a service unless policy approvals.requiredFor includes tool.restart.restart and that uses string is pre-approved with --approve tool.restart.restart. Inner agent-loop tools do not HITL; unapproved agentctl run fail-closes with exit 5 (approval_required). With --approve it completes and audit verify passes.
Other walkthroughs: policy-blocked PR review in examples/pr-review-demo (no API keys); live GitHub read/write with a mock reviewer in examples/pr-review-github; the same flow with OpenAI gpt-4o-mini plus GitHub Actions in examples/pr-review-github-actions (PR workflow; optional manual owner/repo/number).
Most agent stacks bury prompts, tool wiring, and permissions in application code. That makes it hard to answer: Is this config valid? What authority changed? What are we about to grant? What actually ran? Did policy allow it?
Agentic Control Plane is a small Go CLI (agentctl) plus a resource graph so teams can:
- Review capability diffs (
plan) before changes land - Track deployment state separately from runtime traces
- Enforce policies (budgets, approvals, tool rules) at execution time
- Stay local-first while the architecture leaves room for a future remote control plane
| Idea | Analogy |
|---|---|
| Desired resources in Git | GitOps |
plan / apply / drift |
Terraform |
Typed resources (Project, Workflow, Policy, …) |
Kubernetes-style API |
| Tool and IO contracts | OpenAPI-style explicitness |
ACP is the declarative governance/config layer for agent systems — not a replacement for durable-execution engines or code-first agent runtimes.
| Capability | This project | OpenAI Agents SDK | LangGraph | Temporal | Terraform |
|---|---|---|---|---|---|
| Role | Governance/config: versioned resources, plan/apply, policy | Code-first agent runtime | Code-first graph orchestration | Durable workflow execution | Infrastructure as code |
| Durable execution / distributed scheduling | No | No | Optional checkpointers; not a durable-execution engine | Yes | N/A |
| Code-first agent runtime | No (resource graph; YAML today) | Yes | Yes | Workflow SDK, not an agent runtime | No |
| Desired-state plan / apply | Yes (agentctl plan / apply vs SQLite) |
No | No | No | Yes |
| Plan-time effect bound | Shipped (#189 / #190 / #191): bound over the callable operation set, including autonomous tool selection; agentctl plan prints the bound and authority delta. No listed comparable. |
No | No | No | No |
The bound is not over what those operations do at the far end; the trust anchor is human review of the tool manifest. Manifest pin (#204) is not enforced today (MCP tools/list can still expand the world). agentctl plan diffs permissions, approvals, models, budgets, C1 risk items, and effect/capability/authority.
agentctl init— scaffoldproject.yaml, policies, tools, and a sample workflowagentctl validate— load project, apply project defaults (spec.defaults), then environment overlays (-e/--env,Environmentresources §7.6), then validate graph, schemas, and references; runs policy lint (ungated sensitive tools, invalid HITL config, etc.) as advisory output — use--strictto exit 2 on high-severity lint findings (fail-closed safety metadata still gates at run even when lint passes)agentctl plan— diff desired graph vs SQLite deployment state; risk hints including policy lint, effect bound, and authority delta; JSON/YAML output includespolicyLint,deploymentBaseline,effectBound, andauthorityagentctl apply— persist plan (TTY confirm or--auto-approve/AGENTCTL_AUTO_APPROVE); optimistic concurrency — if the deployment store changed after the plan snapshot (e.g. another process applied the same--statefile while this run waited at the prompt), apply fails with exit code 3; re-run plan then applyagentctl run— execute a workflow locally; JSON Schema for inputs where configured; policy gates pause for human-in-the-loop (HITL) approval when a tool call requires itagentctl logs— read trace events from SQLite (--run,--workflow, or recent runs)agentctl audit verify— re-walk hash-linked trace chains and detect tampering (seedocs/AUDIT_CHAIN.md)- Tools —
native,http,mock, andmcp— MCP supports stdio (subprocess) or streamable HTTP (spec.mcp.transport: http,url, optionalheaderswithenv:tokens) - Project defaults — besides
modelandpolicy, optionalruntimeflows tospec.runtimeon agents/workflows when omitted (MVP:localor unset; see spec validation) - Output — table, JSON, or YAML (
-o/--output) - State — single SQLite file (default
.agentic/state.dbunder the project root; override with--state) - Tests — unit/integration coverage, golden CLI output tests, end-to-end
init → … → logsintest/integration
See section 18 (MVP) and section 19 (End Goal) in docs/DESIGN_DOC.md for the full included/excluded list.
- From source: Go 1.22+
- From a release binary: no Go toolchain; put
agentctl(oragentctl.exe) on yourPATHafter extracting the archive
git clone https://github.com/LAA-Software-Engineering/agentic-control-plane.git
cd agentic-control-plane
make build # writes bin/agentctlOr: make install / go install ./cmd/agentctl (honours GOBIN / GOPATH/bin; ensure that directory is on PATH).
GitHub Releases ship agentctl for common platforms (.tar.gz on Linux/macOS, .zip on Windows) plus SHA256SUMS.txt. Pick the archive that matches your machine, for example:
| Platform | Asset suffix |
|---|---|
| Linux x86_64 | linux-amd64.tar.gz |
| Linux arm64 | linux-arm64.tar.gz |
| macOS Intel | darwin-amd64.tar.gz |
| macOS Apple Silicon | darwin-arm64.tar.gz |
| Windows x86_64 | windows-amd64.zip (contains agentctl.exe) |
agentctl version reports the release tag (e.g. v0.1.4).
Releases are created automatically when changes land on main, using a patch semver bump over the latest vMAJOR.MINOR.PATCH tag (merges that only touch Markdown or the root Makefile do not trigger a release). To cut minor or major bumps on demand, run the Release workflow manually (Actions → Release → Run workflow) and choose the bump type.
From the repo root (or anywhere):
agentctl init my-agent-system
agentctl validate --project my-agent-system
agentctl plan --project my-agent-system
agentctl apply --project my-agent-system --auto-approve
agentctl run workflow/hello --project my-agent-system
agentctl logs --project my-agent-system --workflow hello
agentctl audit verify --project my-agent-system --run <run-id>
agentctl inspect --web --project my-agent-system # read-only local UI on http://127.0.0.1:8787inspect --web binds to localhost only and opens the state DB read-only. Avoid running it while agentctl run is writing the same SQLite file (you may see database is locked without WAL); use it when runs are idle or on a copy of the DB.
Until .agent lands, you author the resource graph in YAML (interchange / compilation output — not the long-term surface). The project root is a Project resource: apiVersion, kind, metadata.name, and spec.imports listing other YAML files (policies, tools, workflows). After agentctl init my-agent-system, my-agent-system/project.yaml looks like this:
apiVersion: agentic.dev/v0
kind: Project
metadata:
name: my-agent-system
spec:
imports:
- ./policies/default.yaml
- ./tools/helper.yaml
- ./workflows/hello.yaml
defaults:
policy: default
model: openai/gpt-4o-mini
runtime: local
providers:
models:
openai:
type: openai
apiKeyFrom: env:OPENAI_API_KEY
# Optional: Claude via Messages API (set ANTHROPIC_API_KEY and use e.g. defaults.model: anthropic/claude-sonnet-4-20250514)
# anthropic:
# type: anthropic
# apiKeyFrom: env:ANTHROPIC_API_KEYField-by-field rules, extra kinds, env overlays, MCP HTTP tools, and defaults.runtime are in docs/DESIGN_DOC.md. See docs/EXAMPLES.md for Anthropic fragments, MCP over HTTP, and structured-output notes.
Notes:
initcreatesmy-agent-system/withapiVersion: agentic.dev/v0resources and ahelloworkflow (nativeechotool only — no network).applyin non-interactive environments needs--auto-approveorAGENTCTL_AUTO_APPROVE=1.runHITL: gated tool calls exit withStatus: interrupted(exit 0). Resume with--resume <run-id> --decision approve|reject|edit|switch(use--decision-edit-json/--decision-switch-targetwhen needed), or skip prompts with--auto-approve/AGENTCTL_AUTO_APPROVE=1. Pre-approve a specific call with repeated--approve <uses>. SetAGENTCTL_HITL_ACTORto attribute decisions in trace logs.Policy.spec.hitl.interruptOnkeys are Tool metadata.name values; they configure review options (edit rules, switch targets) for calls already gated byapprovals.requiredForor safety metadata — they do not gate tools on their own.runstores traces in the same SQLite file used for plan/apply (default.agentic/state.dbunder--project). Optional OTLP export (spec.telemetry, off by default) is additive only — seedocs/OTEL.md. When enabled you needserviceNameplus eitherconsoleExport: trueor anendpoint(https://…orenv:VAR, e.g.env:OTEL_EXPORTER_OTLP_ENDPOINT). Export that variable beforerunif you useenv:; if it is missing or the collector is unreachable,agentctllogs a warning, skips OTLP, and the workflow still completes (SQLite traces unchanged).- If
spec.traces.retentionDaysis a positive integer, runs older than that many UTC calendar days (byruns.started_at) are deleted lazily onrunandlogs(child trace rows cascade). Unset or non-positive means no pruning. - Trace payload redaction (issue #110): before SQLite storage, event JSON is sanitized, key-redacted, and size-capped. Defaults mask common secret key names (substring match on map keys). Optional project knobs:
spec.traces.redactKeys/maxPayloadBytes— merged with defaults; also available underspec.traces.redactiontogether withmaxDepth,maxStringChars, andmaxBytes(max bytes for binary previews in sanitized values, not the overall JSON cap).- Stored events may show
[REDACTED],payload_truncated/preview, or depth/binary placeholders inlogs/inspect --web.
- Use
logs --run <id>after a run if you want a single run’s trace (IDs are printed byrun).
| Flag | Purpose |
|---|---|
--project <path> |
Project root (default .) |
--state <path> |
SQLite file override |
-e / --env |
Environment overlay name |
-o / --output |
table, json, or yaml |
--no-color |
ASCII-friendly validate output |
Exit codes are summarized in section 11.2 of docs/DESIGN_DOC.md (0 success, 2 validation, 3 plan/apply conflict when deployment state changed after plan or resolved config drifted before run, 4 execution, 5 policy denial, …).
Config is resolved in this order (highest wins): CLI flags → environment overlay (-e) → project YAML → user-local → built-in defaults.
Optional user-local files (git-ignored, strict YAML — typos fail validate):
| Path | Scope |
|---|---|
$XDG_CONFIG_HOME/agentctl/config.yaml or ~/.config/agentctl/config.yaml |
Global per-user defaults (defaults, state, providers, traces, telemetry) |
.agentic/local.yaml under --project |
Project-scoped overrides (same fields; wins over the global file) |
validate, plan, and apply write .agentic/resolved-config.json (digest of the resolved graph + env + state path). run rejects drift from that snapshot with exit 3 — re-run validate or plan after changing config.
| Path | Role |
|---|---|
cmd/agentctl |
CLI entrypoint |
internal/cli |
Cobra commands, flags, golden tests |
internal/spec |
YAML types, normalize, validate |
internal/config |
Layered config resolution, immutable snapshot |
internal/project |
Load project + imports |
internal/plan |
Planner and risk summary |
internal/apply |
Apply plan to deployment store |
internal/engine |
Workflow execution |
internal/policy |
Policy evaluation |
internal/state/sqlite |
SQLite deployment + runtime/trace tables |
internal/audit |
Tamper-evident hash chain for trace events (issue #116) |
test/integration |
End-to-end CLI flow tests |
docs/DESIGN_DOC.md |
Spec, CLI UX, architecture, roadmap |
docs/GITHUB_ACTIONS.md |
Running agentctl from GitHub Actions (tokens, exit code 5, template path) |
examples/pr-review-github-actions/ |
Full gpt-4o-mini project; PR workflow .github/workflows/agentctl-pr-review.yml; optional publish .github/workflows/agentctl-pr-review-publish.yml |
make defaults to help, which lists targets; the table below mirrors the Makefile (## comments and recipes).
| Target | What it does |
|---|---|
help |
Show usage and target list (default goal) |
all |
fmt → vet → test → build (handy before a push) |
build |
go build → bin/agentctl |
install |
go install ./cmd/agentctl (-trimpath; uses GOBIN / GOPATH/bin) |
clean |
Remove bin/ and coverage.out |
fmt |
go fmt ./... |
verify-fmt |
Fail if gofmt -l would list files (matches CI-style formatting check) |
vet |
go vet ./... |
test |
go test ./... -race |
test-coverage |
Tests with -coverprofile=coverage.out and a one-line go tool cover -func summary |
check |
vet + test only (no formatting writes) |
ci |
verify-fmt + vet + test (no build) |
CI (.github/workflows/ci.yml) runs Linux, macOS, and Windows on Go 1.22.x, plus Go 1.23.x on Linux, with race and shuffle enabled (workflow steps are defined in YAML, not via make ci).
When table output is intentionally changed:
GO_UPDATE_GOLDEN=1 go test ./internal/cli/... -run TestGolden_Recent landings already cover much of “hardening”: plan/apply optimistic concurrency (exit 3 when deployment state drifts), MCP over streamable HTTP as well as stdio, trace retention (spec.traces.retentionDays), defaults.runtime / spec.runtime (MVP local), and clearer defaults vs environment overlay documentation. What is still open for near-term polish:
- More
diff/ drift UX where the design doc calls for it (beyond today’s resource-level diff) - Richer
logsfiltering (see sections 10.2 and 17.3 indocs/DESIGN_DOC.md);inspect --webcovers read-only run/state browsing (#109) agentctl test-style workflow fixtures (stretch per design doc)
- Modules/registry, remote shared state, reconciliation controllers
- Parallel steps, subworkflows, schedules/events
- Stronger drift semantics and multi-runtime targets
- Deeper approval workflows and multi-tenant controls
The recommended implementation phases are outlined in section 20 of docs/DESIGN_DOC.md.
docs/DESIGN_DOC.md— design document v0 (problem statement, spec, CLI, engine, state model, testing strategy, MVP vs end state, section 23 recommendation).docs/AGENT_LOOP.md— bounded agent tool-calling loop: grants, advertised uses,maxIterations, policy on every inner call, traces, HITL vs exit 5 (issues #160 / #161 / #175).docs/AUDIT_CHAIN.md— hash-linked trace audit chain andagentctl audit verify(issue #116).docs/ATTRIBUTION.md— tenant, thread, and actor fields on runs and traces (issue #111).docs/OTEL.md— optional OTLP trace export alongside SQLite (issue #108).docs/TESTING.md—agentctl testfixture format; CI gate walkthrough inexamples/regression-test.examples/pr-review-demo/README.md— end-to-end demo: structured review output, traceable run, approval-gated write (validate→plan→apply→run→logs).examples/regression-test/README.md—agentctl testis green on a gated publish and red after droppingrequiredFor(issue #176).docs/EXAMPLES.md— copy-paste YAML and CLI examples (init, mock vs OpenAI, workflows, environment overlays).CODE_OF_CONDUCT.md— Contributor Covenant 2.1; participation expectations and reporting.- License: MIT
Issues and pull requests are welcome. See CONTRIBUTING.md for local setup, tests, golden updates, and pull request expectations.
Local declarative agent systems with validate, plan, apply, run, and logs.
(Closing recommendation indocs/DESIGN_DOC.md, section 23.)