v0.2.0
Pivot from "extracted framework substrate" to "complete engineering agent fleet". The default install now ships 12 working agents the operator configures via an interactive alfred-init wizard.
Added
2026-05-09 public fleet release
- Role field on every agent.
agents.confgets a 6th tab-separated column carrying a one-line operational descriptor;render.shemitsALFRED_<CODENAME>_ROLEenv vars;agent_role()/codename_with_role()surface the role in CLI + Slack post prefixes. - Runner-level fleet gate file. New
$HERMES_HOME/state/fleet/enabled.txtplusis_agent_enabled/enable_agent/disable_agenthelpers. Listed codenames are enabled; missing codenames fall back to each runner's default so opt-in agents can be gated without making normal launchd agents look disabled. Newbin/alfredCLI shipsalfred enable / disable / agents / enabled-agents. - Slack threading + Block Kit + severity colour stripes. New
lib/slack_format.pywith bot-token-awarefiring_thread_root/firing_thread_reply/firing_thread_close. Attachment duplicate-render guard baked in from day one. HonoursBATMAN_APPROVAL_CHANNELfor routing. - Bundle-label model + Batman skeleton. New
lib/batman.pywithBundledataclass, all-or-nothingclaim_bundle, best-effortrelease_bundle, loose-markdownparse_plan_from_issue/parse_plan_from_bundle. Scope-widening guard included. Newbin/batman.pyskeleton runner posts plan summaries; full execution chain deferred. - Runner-side dedup.
find_open_authored_pr_for_issue(with substring-false-positive guard) +reuse_or_make_worktreeso partial work survives across firings of the same issue. - STANDARD_LABELS bootstrap.
batman-pr-openandagent:large-featureship by default;gh_pr_createauto-creates ad-hoc labels and surfaces gh stderr on failure. - Fleet doctor. New
bin/fleet-doctor.pyships four read-only health checks (paused repos, global block, stale worktrees, fleet enable list) → single severity-stripe Slack thread. - Runner safety hardening. Batman and fleet-doctor now acquire the shared lock helper correctly; cleanup scopes
/tmpsweeping to agent-owned prefixes instead of broad wildcard matches. - Release-readiness hardening. Lucius wraps GitHub issue content as untrusted input, checks issue author association before autonomous code execution, grants Codex the source
.gitdirectory for worktree commits, and opens salvaged WIP PRs as real GitHub drafts. Drake's daily cap guard now scales its GitHub search limit above the configured cap. Lock-owner checks now validate the recorded agent name when the caller knows it.
Engineering agents (bin/)
- lucius (feature dev): picks the oldest open
agent:implementissue, claims it via the state machine, opens a worktree, runsclaude -pwith the issue body, pushes a PR labelledagent:authored. - drake (planner): files new
agent:implementissues from specs / roadmap / code-reality grep. Caps per-firing + rolling-24h. - bane (test coverage): picks the lowest-coverage actively-changed file, writes tests, opens a PR.
- rasalghul (PR review): multi-axis review on every fresh PR. Posts as comment.
- nightwing (review-fix): lands fixes for P0 / P1 reviewer comments on
agent:authoredPRs. - robin (bug triage): classifies severity, asks for repro info, hands off to lucius. Local touched-issues ledger prevents re-triage.
- huntress (post-deploy smoke): runs Playwright tests against
ALFRED_HUNTRESS_TARGET_URL. Optional ECS staging-readiness pre-check + S3 screenshot upload. - gordon (deploy health): daily ECS task-def vs
mainHEAD diff + top-N Sentry issues. Quiet on healthy days. - automerge: squash-merges clean
agent:authoredPRs (CI green, no unresolved P0 reviewer comments, latest review ends "Ship-ready: yes"). - agent-cleanup: daily housekeeping (clean stale worktrees, stuck locks, stale
agent:in-flightclaims viaforce_release_stale_claim). Dirty or unknown worktrees are skipped and reported. - code-map-refresh: cross-repo contract scan. Writes
${HERMES_HOME}/state/code-map.jsonfor other agents. - agent-morning-brief: daily Slack post — yesterday's PRs, in-flight work, doctor status.
- fleet-recap.sh: 07:30 + 22:00 Slack digest (per-agent firings / cost / success rate).
Every codename is operator-customisable at install time. Default Batman names; runtime codename via AGENT_CODENAME env (set by the launchd plist). Repo lists, AWS profiles, ECS clusters, Sentry orgs all env-driven.
Engineering-agent prompts (prompts/)
9 role-based prompt templates compatible with agent_runner.load_prompt() and ${VAR} substitution: feature-dev.md, planner.md, test-coverage.md, code-review.md, review-fix.md, bug-triage.md, ecs-monitor.md, post-deploy-smoke.md, cross-repo-coordinator.md. Cross-codename refs use ${FEATURE_DEV_CODENAME} / ${CODE_REVIEW_CODENAME} etc. so renaming any agent can stay consistent end-to-end.
Substrate (lib/agent_runner.py)
- Issue claim state machine:
claim_issue/release_issue/find_stale_claims/force_release_stale_claim/is_repo_paused/set_repo_paused/list_paused_repos/issue_dedup_check. Lifecycle labelsagent:in-flight/agent:pr-open/agent:doneplus operator-overridedo-not-pickup. Full doc atdocs/STATE_MACHINE.md(with Mermaid stateDiagram). - Slack severity routing:
slack_post(text, severity="info" | "warn" | "alert").infois back-compat default;warnprefixes⚠️ ;alertprefixes 🚨 + appends<!here>. claude_invoke_streaming()+transcript_path(): streaming-API-compatible signatures (currently delegate to plainclaude_invoke; the per-firing JSONL transcript writer ships in a future release).TRANSCRIPTS_ROOT+PROMPTS_ROOTmodule constants.
Operator surface
alfred-init(bin/alfred-init.py): interactive 13-step wizard. Walks Slack-app creation with real test-post; AWS / env-var storage choice; multi-select agent enable; per-role codename prompt with Batman defaults; per-agent repo selection fromgh repo list; per-agent special prompts (Huntress staging URL, Gordon ECS cluster); generatesagents.conf+~/.alfredrcwith banner-marked block; runsdeploy.sh+bin/doctor.sh; smoke-test post. 27 tests covering helpers + doctor sentinel + non-interactive mode.examples/bin/label_state.py(operator CLI example):claim/release/dedup-check/status-issue/repo {pause,resume,list}/sweep-claims.examples/git-hooks/pre-push: refuses pushes that race in-flight agents.install.sh: idempotent fresh-machine bootstrap (brew + npm + dirs + shell rc).
Documentation
INSTALL.md(TL;DR + step-by-step) +BOOTSTRAP.md(deeper operations guide).docs/AGENTS.md: codename topology with Batman defaults, customisation story, fleet-map Mermaid diagram, codename-wiring Mermaid diagram, anti-patterns, "adding a new codename" walkthrough.docs/STATE_MACHINE.md: lifecycle Mermaid stateDiagram + race-resolution + stale-sweep + operator overrides.ARCHITECTURE.md: per-firing flow Mermaid sequenceDiagram + design rationale.docs/SLACK_SETUP.md,docs/AWS_SETUP.md,docs/CLAUDE_CODE.md,docs/SKILLS.md,docs/LINUX.md,docs/TUTORIAL.md.- Astro Starlight site at
site/: 16 pages (getting-started / concepts / guides / reference / about), with GitHub Pages publishing gated byALFRED_OS_PUBLISH_PAGES. URL env-overridable.
Project hygiene
- CI:
pytest(3.11 / 3.12 / 3.13) +ruff check+ruff format --check+mypy lib/+shellcheck+python-syntax+scrub-check(refuses known-private patterns). bin/scrub-check.sh: reusable local + CI scrub scan for host-private paths, fleet identifiers, Slack tokens/webhooks, and AWS access key IDs.docs/RELEASE_CHECKLIST.md: public release checklist with pre-tag gates, scrub requirements, and GitHub Release flow.- Release automation: tag → GitHub release with auto-extracted changelog notes + brew-formula sha256 echoed to logs.
Formula/alfred-os.rb: HEAD-only Homebrew formula until the first public release tarball has a checksum.CODE_OF_CONDUCT.md,SECURITY.md,SUPPORT.md, issue templates, PR template,dependabot.yml,pyproject.toml(ruff + mypy),.pre-commit-config.yaml.
Changed
- Repository renamed
luminik-io/pennyworth→luminik-io/alfred-os. GitHub redirects in place. All env varsPENNYWORTH_*→ALFRED_*/ALFRED_OS_*. Operator config file~/.pennyworthrc→~/.alfredrc. Operator commandspennyworth-*→alfred-*. STANDARD_LABELSincludes the lifecycle labels; consumers no longer need to extend it for the state machine to work.- Per-repo configuration loaded from
~/.alfredrc.d/<codename>.tomlvia stdlibtomllib(was PyYAML; PyYAML is not stdlib and shouldn't be required for a fresh install). - Doctor mode runs before env-config IDLE checks across all 12 agents —
bash bin/doctor.shnow reports all-passing on a fresh install before the operator runsalfred-init. bin/doctor.shnow falls back to the in-repobin/andlib/paths before deploy, so a clean checkout can self-check without a pre-existing$HERMES_HOME.- All docs voice-swept: removed audience-marketing intros, outcome-fantasy framing, hire/replace framing, LLM filler vocab, marketing emoji, sign-offs, vanity stats, em-dashes. ~210 lines of marketing prose deleted across 39 files; technical content preserved.
Removed
MORNING.mdoperator-brief file (now lives in PR descriptions / chat, not the tree).uv.lockfrom version control (auto-generated; consumers run their ownuv syncagainstpyproject.toml).sso-check-10/sso-check-22from the defaultagents.conf. Operator-convenience reminders, not engineering. Mentioned indocs/AWS_SETUP.mdfor operators who use AWS SSO interactively.
Deferred (v0.3)
- Bot token integration (
xoxb-…): unlocksslack_set_channel_topic(),chat.postMessagewiththread_tsfor daily-thread routing ofinfo-tier messages, reactions API. Webhooks cannot do these. - Drake-style proactive title-token dedup: runner-level guard before invoking the planner. Catches "two issues, same work."
claim_pr/release_pr: extend the state machine to PR-level work (review-fix agents racing the same PR).render-systemd.sh: first-class Linux scheduling.- Spend dashboards: weekly recap rendered from per-agent spend files.
alfred new-codenamescaffold: single command to add a fresh codename agent (script template + agents.conf entry + label registration).- MCP server bundling: expose
claim_issue/release_issue/slack_post(severity)as MCP tools. - Real per-firing JSONL transcripts:
claude_invoke_streamingcurrently delegates toclaude_invoke. The streaming impl with transcript file at${HERMES_HOME}/state/transcripts/<agent>/<YYYY-MM>/<firing_id>.jsonlships with the future transcript-viewer command.