Skip to content

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 24 May 19:08
· 443 commits to main since this release
v0.4.0
76598dc

Substrate, observability, planning, approval, memory, and connector primitives. The largest single release since 0.1.0; lays down building blocks the next two quarters of roadmap items will compose.

Added

Runner and observability

  • lib/agent_runner.py decomposed from a single monolith into a 10-file lib/agent_runner/ package: preflight, lock, spend, engines, gh, slack, event-log, commit-trailer, transcripts, dedup. Public import surface preserved. 50 new unit tests under tests/unit/agent_runner/ cover the split modules; full suite grew from 689 to 749.
  • alfred metrics (bin/alfred-metrics.py): per-agent rollup of firings, cost, turns, tool-use, and Codex tokens. --since 7d, --codename, --by-day, --json. Reads $ALFRED_STATE_DIR only.
  • alfred logs (bin/alfred-logs.py): tail and filter per-firing stream-JSON transcripts. --last N, --firing-id ID, --show-tool-calls, --json. See docs/CLI.md.
  • lib/transcripts.py and lib/metrics.py: TranscriptReader and MetricsAggregator protocols + filesystem-backed implementations, used by the two new CLIs and exposed for downstream code.

State machine and multi-repo

  • lib/labels.py: LabelClient protocol + GhCliLabelClient implementation. Atomic transitions across the issue-claim state machine (agent:queued to agent:implement to agent:in-flight to agent:pr-open to agent:done), with race resolution and conflict detection.
  • lib/cross_repo_pr.py: cross-repo PR coordinator. Opens stacked PRs across multiple repos with a shared spec id, links them via PR-body cross-references, marks the spec done only when all PRs merge.
  • lib/multi_worktree.py: managed pool of git worktrees under $ALFRED_HOME/wt. Per-firing reservation, completion cleanup, crash recovery.
  • bin/alfred-label-state: operator-facing CLI for the issue-claim state machine. claim, release, dedup-check, status-issue, repo pause/resume/list, sweep-claims. Pre-push hook recipe in docs/STATE_MACHINE.md.

Planning and execution

  • Damian spec-bundle planner (lib/damian_planner.py + bin/damian.py): walks a spec directory, identifies multi-repo features, files agent:bundle:<slug> siblings across the affected repos. All-or-nothing per bundle. Caps at 3 bundles per firing. Single-repo work is left to drake.
  • Batman now executes approved plans (lib/batman.py from 505 to 1383 lines; bin/batman.py from 261 to 472 lines). Once a Damian-style plan is approved (Slack reaction, label transition, or BATMAN_AUTO_EXECUTE=1), Batman drives the fan-out implementation across the listed repos. Previously Batman halted at plan-only. The BATMAN_AUTO_EXECUTE env contract: 0 = always ask, approval-gate = read approval signals, 1 = always execute. See docs/BATMAN.md.

Approvals

  • lib/slack_approval.py + docs/SLACK_APPROVAL.md: reaction-based approval gate. An agent posts a proposal, the operator reacts with the configured emoji, the agent proceeds. ApprovalGate is a typing.Protocol so the same call site can swap Slack for any other channel. New env vars: ALFRED_OPERATOR_SLACK_USER_ID, ALFRED_APPROVAL_EMOJI (defaults to :white_check_mark:).

Quality gates

  • lib/slop_detector.py + bin/slop-detector.py + bin/curator.py: PR-time linter for AI-authored prose patterns. 21 default rules covering banned vocabulary (seamless, unlock, leverage, transform), em-dashes, hedged numbers, marketing fluff. Rules are JSON-configurable; see examples/slop-rules.json and docs/SLOP_DETECTOR.md.

Memory

  • lib/fleet_brain/: v1 SQLite-backed memory store. Per-codename and per-repo recall / reflect, atomic writes, ULID ids via the standard library, zero external dependencies. 948 lines of package code, 33 tests. Architecture and the v2 path (PGLite + Apache AGE + pgvector) in docs/FLEET_BRAIN.md. CLIs: bin/alfred-brain.py, bin/fleet-ingest.py.
  • lib/memory/: MemoryProvider Protocol + FleetBrainProvider, ChainedMemoryProvider, and NullMemoryProvider implementations. Optional read-only gbrain subprocess shim for operators with a personal knowledge base. Chain order is env-driven: ALFRED_MEMORY_PROVIDERS=fleet,gbrain; default is fleet-brain only; null disables memory. See docs/MEMORY_PROVIDERS.md.

Connectors

  • lib/connectors/: Connector Protocol + reference Linear and Sentry implementations. Pull-mode adapters from non-GitHub sources into the engineering fleet's agent:implement queue. Linear uses a stdlib GraphQL POST; Sentry uses a stdlib REST GET; both rely on env-only credentials (LINEAR_API_KEY, SENTRY_AUTH_TOKEN). One bad connector cannot break the sync. See docs/CONNECTORS.md, bin/connector-sync.py, examples/connectors.yaml.

Dashboards and proof

  • alfred serve v1 (bin/alfred-serve.py + lib/server/): localhost-only, read-only FastAPI dashboard over $ALFRED_HOME/state. Three views: fleet status with HTMX auto-refresh, recent firings, single-firing detail. Reader injected as typing.Protocol. New [serve] optional dependency group for fastapi, uvicorn, jinja2. See docs/SERVE.md.
  • bin/alfred-shipped-public.py: self-host emitter that reads $ALFRED_HOME/state, applies a public field allowlist + partner-name redaction table, and writes a weekly.json operators can publish on their own site. See docs/SHIPPED_EMITTER.md.

Infrastructure for unattended operation

  • lib/claude_proxy/ and bin/claude-proxy.py: localhost unix-socket daemon that brokers claude -p invocations on behalf of launchd-spawned agent processes. Solves the macOS Keychain ACL issue that returns 401 on every claude call from a non-Aqua launchd session. NDJSON wire protocol with invoke, health, and probe requests; stdlib-only; opt-in via ALFRED_CLAUDE_PROXY_SOCKET with transparent fallback to direct subprocess. See docs/CLAUDE_PROXY.md and docs/MACOS_KEYCHAIN.md.
  • examples/launchd/luminik.claude-proxy.plist.example: sample launchd unit with LimitLoadToSessionType=Aqua and inline install / verify recipe. Operators must edit the placeholder paths before bootstrapping. (Brand-neutral filename rename tracked for the v0.4.1 docs PR.)
  • lib/agent_runner.process.claude_invoke_streaming routes through the proxy when the env var is set, falls back to direct subprocess otherwise.

Fleet diagnostic + cleanup hardening

  • Pause-marker honoring under launchd via $ALFRED_HOME/state/_paused/<codename> (paused agents stay paused across firings, not just at boot).
  • Fail-streak / pause-marker sync at every self-pause site (lucius, drake, batman, rasalghul, nightwing).
  • ALFRED_CLEANUP_EXTRA_PATHS env var: sweep operator-managed worktree pools outside $ALFRED_HOME/worktrees.
  • Status-cache TTL stops stale reads when alfred status is invoked in quick succession.
  • ALFRED_PREFLIGHT_SLACK_MIN_MINUTES throttles repeated preflight Slack alerts.
  • fleet-doctor distinct alert for concurrent engine-auth failures (separates "claude not logged in" from generic firing errors).

Documentation

  • Three new concept pages: state and memory, engine routing, operating the fleet. Mirrored across docs/ (GitHub-rendered) and site/src/content/docs/ (Starlight). Linked into the sidebar under Concepts and Getting Started.
  • ROADMAP rewritten into a four-tier model: Shipped, In flight, Next, Horizon. Mirrored in site/src/content/docs/about/roadmap.md.

Changed

  • Core dependencies: slack-sdk>=3.27 and boto3>=1.34 moved from optional [slack] and [aws] extras into the base dependencies list. Slack and AWS are integral enough that the optional-extras split was adding install friction for new operators with no payoff.
  • pyproject.toml adds the new [serve] optional-dependency group (FastAPI + uvicorn + Jinja2).
  • .gitignore adds .claude/ and screenshots/ so per-agent worktrees, launch configs, and local verification screenshots stay out of the public repo.
  • .gitallowed added so git secrets pre-commit hooks understand that bin/scrub-check.sh and CI workflows reference secret-pattern regexes by design.

Fixed

  • lib/labels.py: added PLAN_PENDING_APPROVAL constant (agent:plan-pending-approval) plus a backward-compat LABEL_AGENT_PLAN_PENDING_APPROVAL alias for code that imports the long-form name. Required by lib/slack_approval.py and lib/batman.py.
  • tests/unit/__init__.py and tests/unit/agent_runner/__init__.py: promote the agent-runner unit test directory to a package so pytest can disambiguate tests/test_transcripts.py from tests/unit/agent_runner/test_transcripts.py.
  • docs/BATMAN.md: replaced operator-specific channel literal with #your-fleet-channel placeholder per the private-to-public boundary policy.

Verification

  • 689 tests pass on Python 3.11.
  • bash bin/scrub-check.sh returns scrub-check: clean.
  • cd site && npm run build builds 45 pages with 0 errors and 0 content warnings.