diff --git a/README.md b/README.md index e82a06f..fe9c166 100644 --- a/README.md +++ b/README.md @@ -62,11 +62,15 @@ have-config/ │ ├── commands/ │ │ ├── claude/check-setup.md │ │ └── codex/check-setup.md -│ └── skills/check-setup/ -│ └── SKILL.md +│ └── skills/ +│ ├── check-setup/SKILL.md +│ ├── hermes-manager/SKILL.md +│ └── hermes-ops/SKILL.md ├── agent-doc-snippets/ # cumulative AGENTS / CLAUDE sections ├── docs/ │ ├── agent-playbook.md # what agents use each service for +│ ├── hermes-dev-team-mode.md # Hermes manager / worker operating model +│ ├── hermes-zulip-gateway.md # Zulip gateway setup and checks │ └── infrastructure.md # HappyVertical service map ├── services/ │ └── services.json # machine-readable service registry @@ -152,6 +156,8 @@ Hermes agents additionally get local generated commands/skills: services - `hermes-ops` — documents scheduled Vikunja pickup state, blocked-task recovery, and watcher setup +- `hermes-manager` — documents Dev Team Mode manager behavior for local worker + dispatch, board state, and live integration The base manifest also installs reusable no-agent scripts when executable script links are managed: @@ -197,6 +203,11 @@ used only when a local snapshot exists at `HV_CONTEXTFORGE_SNAPSHOT_DIR` or After install, restart the agent session and run `check-setup`. +Hermes Dev Team Mode is documented in `docs/hermes-dev-team-mode.md`. It treats +the current Hermes agent as the manager, uses main Vikunja project boards as the +canonical user-visible issue state, and uses a per-manager internal Vikunja +project for worker execution. + ## Agent resolution model The have-config installer composes agent behavior in this order: diff --git a/TODO.md b/TODO.md index 3bbbb83..43d30b8 100644 --- a/TODO.md +++ b/TODO.md @@ -85,5 +85,7 @@ Pick when the first consumer pair exists, not before. `check-setup` - [x] Hermes `hermes-ops` skill — scheduled Vikunja pickup state, blocked-task recovery, and watcher setup +- [x] Hermes `hermes-manager` skill — Dev Team Mode manager behavior for + local worker dispatch, board state, and live integration - [ ] Publish an exporter from Context Forge into the manifest shape expected by have-config (`manifest.json` with `skills`, `commands`, and `agent_docs`). diff --git a/docs/hermes-dev-team-mode.md b/docs/hermes-dev-team-mode.md new file mode 100644 index 0000000..6a24c06 --- /dev/null +++ b/docs/hermes-dev-team-mode.md @@ -0,0 +1,107 @@ +# Hermes Dev Team Mode + +Hermes Dev Team Mode turns one Hermes agent into the manager for a local +development team. The human communicates with the manager in Zulip, watches +canonical issues move through Vikunja project boards, and QA-tests a live dev +server exposed on the local network. + +## Operating Model + +- The current Hermes agent is the manager. +- The manager identity is the Hermes/Zulip email address. +- Each manager owns one internal Vikunja project named + `Hermes Manager - `. +- Main project boards remain the canonical user-visible source of issue state. +- Worker agents only work internal manager-project tasks. +- The manager is the only agent that moves main-board cards and writes to the + live integration worktree used by the dev server. + +## Board Model + +Main project boards should use these buckets: + +- `To-Do` +- `Blocked` +- `Doing` +- `Review` +- `Done` + +Manager projects should use these buckets: + +- `Queued` +- `Working` +- `Integrating` +- `Blocked` +- `Closed` + +When Dev Team Mode watches a board, missing buckets are provisioned +automatically by the Hermes runtime. Setup checks may report missing buckets, +but should not require a human to create them by hand. + +## Zulip Intake + +The manager triages Zulip QA messages instead of creating a task for every +message. For each message, it decides whether to: + +- create a new canonical main-board issue +- update or deduplicate against an existing issue +- ask for a short clarification +- ignore conversational noise + +Status updates should be quiet unless asked. The manager may still acknowledge +new issue creation, ask clarifying questions, or report blockers that require +human action. + +## Worker Dispatch + +Workers are per-task runs. For each dispatched issue, the manager: + +1. Infers the local repository from task text, task description, linked GitHub + URLs, or repo-like strings. +2. Creates a dedicated git worktree under the configured Hermes worktree root. +3. Creates a linked worker task in the manager project. +4. Runs the configured worker command template with task and worktree context. +5. Reviews the worker diff. +6. Applies accepted changes into the integration worktree. +7. Moves the main-board issue to `Review` for human QA. + +The worker command is configured locally with `HV_HERMES_WORKER_COMMAND` or the +equivalent Hermes config value. Workers must not edit the integration worktree +directly. + +## Suggested Hermes Config + +```yaml +platforms: + zulip: + enabled: true + +dev_team: + enabled: true + manager_identity: "${ZULIP_EMAIL}" + manager_project_name_template: "Hermes Manager - {email}" + watch_projects: "all" + repo_search_roots: + - ~/Work/happyvertical/repos + - ~/Work/anytown/repos + worktree_root: ~/.hermes/dev-team/worktrees + integration_root: ~/.hermes/dev-team/integration + worker_command: "${HV_HERMES_WORKER_COMMAND}" + main_buckets: [To-Do, Blocked, Doing, Review, Done] + manager_buckets: [Queued, Working, Integrating, Blocked, Closed] + status_updates: on_request + integration_gate: review_only +``` + +## Blocking Conditions + +Move the main-board issue or worker task to `Blocked` when: + +- no local repository can be inferred +- the worker command is missing or fails before producing reviewable output +- a required credential or service is unavailable +- the manager cannot apply the worker diff cleanly +- the dev server or integration worktree is not reachable + +Blocker comments must name the first concrete blocker and the smallest next +action. Never include secrets, tokens, cookies, passwords, or decrypted values. diff --git a/hv/manifest.json b/hv/manifest.json index 81504b6..864a250 100644 --- a/hv/manifest.json +++ b/hv/manifest.json @@ -26,6 +26,14 @@ "claude" ], "path": "docs/hermes-zulip-gateway.md" + }, + { + "id": "happyvertical.hermes-dev-team-mode", + "targets": [ + "agents", + "claude" + ], + "path": "docs/hermes-dev-team-mode.md" } ], "commands": [], diff --git a/profiles/hermes/commands/claude/check-setup.md b/profiles/hermes/commands/claude/check-setup.md index cfacee7..b13ad71 100644 --- a/profiles/hermes/commands/claude/check-setup.md +++ b/profiles/hermes/commands/claude/check-setup.md @@ -61,6 +61,26 @@ Run these checks: succeeds for the bot and that the `/api/v1/register` + `/api/v1/events` long-poll listener can start without auth errors. - Report missing bot credentials as `Blocked` with the variable names. +8. Hermes Dev Team Mode + - If Dev Team Mode is expected, confirm the local Hermes config has + `dev_team.enabled: true`; otherwise mark it `Skipped`. + - Confirm the manager identity resolves from `dev_team.manager_identity`, + `ZULIP_EMAIL`, or `HV_AGENT_EMAIL`. + - Confirm Vikunja access is available because main project boards and the + per-manager project are the source of truth. + - Confirm the configured manager project name follows + `Hermes Manager - `. + - Confirm missing main-board buckets can be provisioned by the Hermes + runtime. Setup checks should report missing buckets but not require manual + creation. + - Confirm `HV_HERMES_WORKER_COMMAND` or an equivalent + `dev_team.worker_command` is configured before worker dispatch is marked + ready. + - Confirm repo search roots, worktree root, and integration root are + configured and point outside repo-tracked source directories unless the + local task explicitly requires otherwise. + - If a dev server URL is configured for the active project or task, verify it + is reachable from the local network. If a check cannot be performed noninteractively, mark it `Blocked` and state the missing credential, connector, environment variable, CLI, or local config. diff --git a/profiles/hermes/commands/codex/check-setup.md b/profiles/hermes/commands/codex/check-setup.md index cfacee7..b13ad71 100644 --- a/profiles/hermes/commands/codex/check-setup.md +++ b/profiles/hermes/commands/codex/check-setup.md @@ -61,6 +61,26 @@ Run these checks: succeeds for the bot and that the `/api/v1/register` + `/api/v1/events` long-poll listener can start without auth errors. - Report missing bot credentials as `Blocked` with the variable names. +8. Hermes Dev Team Mode + - If Dev Team Mode is expected, confirm the local Hermes config has + `dev_team.enabled: true`; otherwise mark it `Skipped`. + - Confirm the manager identity resolves from `dev_team.manager_identity`, + `ZULIP_EMAIL`, or `HV_AGENT_EMAIL`. + - Confirm Vikunja access is available because main project boards and the + per-manager project are the source of truth. + - Confirm the configured manager project name follows + `Hermes Manager - `. + - Confirm missing main-board buckets can be provisioned by the Hermes + runtime. Setup checks should report missing buckets but not require manual + creation. + - Confirm `HV_HERMES_WORKER_COMMAND` or an equivalent + `dev_team.worker_command` is configured before worker dispatch is marked + ready. + - Confirm repo search roots, worktree root, and integration root are + configured and point outside repo-tracked source directories unless the + local task explicitly requires otherwise. + - If a dev server URL is configured for the active project or task, verify it + is reachable from the local network. If a check cannot be performed noninteractively, mark it `Blocked` and state the missing credential, connector, environment variable, CLI, or local config. diff --git a/profiles/hermes/manifest.json b/profiles/hermes/manifest.json index f6c04a2..53ab02c 100644 --- a/profiles/hermes/manifest.json +++ b/profiles/hermes/manifest.json @@ -28,6 +28,12 @@ "name": "hermes-ops", "path": "skills/hermes-ops", "description": "Operate Hermes scheduled pickup, blocked-task recovery, and no-agent watchers" + }, + { + "agent": "codex", + "name": "hermes-manager", + "path": "skills/hermes-manager", + "description": "Operate Hermes Dev Team Mode as a manager for local worker agents" } ], "env_requirements": [ @@ -48,6 +54,14 @@ ], "default_enabled": false, "description": "Hermes gateway Zulip bot credentials and routing for long-poll chat response." + }, + { + "capability": "hermes-dev-team", + "vars": [ + "HV_HERMES_WORKER_COMMAND" + ], + "default_enabled": false, + "description": "Worker command template used by Hermes Dev Team Mode." } ], "services": [] diff --git a/profiles/hermes/skills/check-setup/SKILL.md b/profiles/hermes/skills/check-setup/SKILL.md index b54757d..19d0d0d 100644 --- a/profiles/hermes/skills/check-setup/SKILL.md +++ b/profiles/hermes/skills/check-setup/SKILL.md @@ -64,6 +64,26 @@ Run these checks: succeeds for the bot and that the `/api/v1/register` + `/api/v1/events` long-poll listener can start without auth errors. - Report missing bot credentials as `Blocked` with the variable names. +8. Hermes Dev Team Mode + - If Dev Team Mode is expected, confirm the local Hermes config has + `dev_team.enabled: true`; otherwise mark it `Skipped`. + - Confirm the manager identity resolves from `dev_team.manager_identity`, + `ZULIP_EMAIL`, or `HV_AGENT_EMAIL`. + - Confirm Vikunja access is available because main project boards and the + per-manager project are the source of truth. + - Confirm the configured manager project name follows + `Hermes Manager - `. + - Confirm missing main-board buckets can be provisioned by the Hermes + runtime. Setup checks should report missing buckets but not require manual + creation. + - Confirm `HV_HERMES_WORKER_COMMAND` or an equivalent + `dev_team.worker_command` is configured before worker dispatch is marked + ready. + - Confirm repo search roots, worktree root, and integration root are + configured and point outside repo-tracked source directories unless the + local task explicitly requires otherwise. + - If a dev server URL is configured for the active project or task, verify it + is reachable from the local network. If a check cannot be performed noninteractively, mark it `Blocked` and state the missing credential, connector, environment variable, CLI, or local config. diff --git a/profiles/hermes/skills/hermes-manager/SKILL.md b/profiles/hermes/skills/hermes-manager/SKILL.md new file mode 100644 index 0000000..d7970b3 --- /dev/null +++ b/profiles/hermes/skills/hermes-manager/SKILL.md @@ -0,0 +1,81 @@ +--- +name: hermes-manager +description: Use when operating Hermes Dev Team Mode, coordinating worker agents, managing Vikunja board state, or integrating fixes into a live dev-server worktree. +metadata: + short-description: Manage local Hermes developer workers +--- + +# Hermes Manager Procedure + +Use this procedure when the current Hermes agent is acting as the manager for +local developer workers. The human talks to this manager in Zulip and watches +canonical issue cards on Vikunja main project boards. Do not print or store +tokens, cookies, passwords, or decrypted secret values. + +## Manager Responsibilities + +1. Own communication with the human. + - Respond in Zulip as the single point of contact. + - Keep status quiet unless asked, except for clarifying questions, + acknowledgements of created issues, and blockers requiring human action. +2. Own main-board state. + - Main project cards are canonical user-visible issues. + - Move main cards through `To-Do`, `Blocked`, `Doing`, `Review`, and `Done`. + - Auto-provision missing expected buckets through the Hermes runtime. +3. Own worker dispatch. + - Use one manager project named `Hermes Manager - `. + - Use manager-project buckets `Queued`, `Working`, `Integrating`, + `Blocked`, and `Closed`. + - Create one internal worker task per dispatched main-board issue. +4. Own live integration. + - Workers must use isolated git worktrees. + - Only the manager may apply accepted worker output to the integration + worktree used by the local-network dev server. + - Integration is gated by manager review of the diff; local tests are + optional unless the task or repo instructions require them. + +## Zulip Intake + +For each QA message, decide whether to create a new issue, update an existing +issue, ask for clarification, or ignore conversational noise. Do not create a +Vikunja task for every Zulip message. Prefer actionable main-board issues with +clear repro context. + +## Repository Inference + +Infer the target repository from task text, descriptions, linked GitHub URLs, +and repo-like strings. Search configured repo roots such as +`~/Work/happyvertical/repos` and `~/Work/anytown/repos`, matching task GitHub +URLs to local git remotes. If inference is ambiguous or missing, block the task +and ask the human for the repo. + +## Worker Run Contract + +For each per-task worker run, provide these values through the configured worker +command template or environment: + +- main Vikunja task ID and URL +- worker Vikunja task ID and URL +- repository path +- worker worktree path +- branch name +- integration worktree path +- dev server URL when known +- manager identity + +The worker should return reviewable output in its worktree and exit `0` when it +is ready for manager review. Non-zero exits, dirty state that cannot be +understood, or missing output are blockers. + +## Integration Gate + +Before applying worker output: + +1. Read the worker task, comments, and diff. +2. Confirm the diff is scoped to the main-board issue. +3. Apply accepted changes into the integration worktree. +4. Move the main-board issue to `Review`. +5. Leave the worker task `Closed` or `Blocked` with a concise status comment. + +If the patch does not apply cleanly, move both the worker task and main-board +issue to `Blocked` with the smallest next action. diff --git a/scripts/test-hv-agent-resolver.sh b/scripts/test-hv-agent-resolver.sh index fc0bb38..946b859 100755 --- a/scripts/test-hv-agent-resolver.sh +++ b/scripts/test-hv-agent-resolver.sh @@ -17,7 +17,8 @@ REPORT_PATH="$TMP_DIR/install-report.md" mkdir -p "$DOTFILES_DIR/agent" "$DOTFILES_DIR/.agents/commands/codex" \ "$DOTFILES_DIR/.agents/commands/claude" "$DOTFILES_DIR/.agents/skills/ship" \ "$DOTFILES_DIR/.agents/skills/review-cycle" "$HAVE_CONFIG_DIR/hv" "$HAVE_CONFIG_DIR/profiles/hermes/commands/codex" \ - "$HAVE_CONFIG_DIR/profiles/hermes/skills/check-setup" "$HAVE_CONFIG_DIR/reusable-scripts/hermes/no-agent" \ + "$HAVE_CONFIG_DIR/profiles/hermes/skills/check-setup" "$HAVE_CONFIG_DIR/profiles/hermes/skills/hermes-manager" \ + "$HAVE_CONFIG_DIR/reusable-scripts/hermes/no-agent" \ "$HAVE_CONFIG_DIR/services" "$CONTEXTFORGE_DIR" \ "$LOCAL_DIR/commands/codex" "$LOCAL_DIR/skills/codex/ship" "$HOME_DIR" mkdir -p "$HOME_DIR/.claude" @@ -139,6 +140,11 @@ cat > "$HAVE_CONFIG_DIR/profiles/hermes/manifest.json" <<'JSON' "agent": "codex", "name": "check-setup", "path": "skills/check-setup" + }, + { + "agent": "codex", + "name": "hermes-manager", + "path": "skills/hermes-manager" } ] } @@ -152,6 +158,10 @@ cat > "$HAVE_CONFIG_DIR/profiles/hermes/skills/check-setup/SKILL.md" <<'EOF' hermes check setup skill EOF +cat > "$HAVE_CONFIG_DIR/profiles/hermes/skills/hermes-manager/SKILL.md" <<'EOF' +hermes manager skill +EOF + cat > "$HAVE_CONFIG_DIR/services/services.json" <<'JSON' { "schema": "https://happyvertical.com/service-registry/v1", @@ -229,6 +239,7 @@ grep -q "local ship" "$HOME_DIR/.agents/skills/ship/SKILL.md" grep -q "dotfiles review-cycle skill" "$HOME_DIR/.agents/skills/review-cycle/SKILL.md" grep -q "hermes check setup" "$HOME_DIR/.codex/commands/check-setup.md" grep -q "hermes check setup skill" "$HOME_DIR/.agents/skills/check-setup/SKILL.md" +grep -q "hermes manager skill" "$HOME_DIR/.agents/skills/hermes-manager/SKILL.md" grep -q "fixture notify" "$OUTPUT_DIR/scripts/fixture-notify" grep -q "fixture notify" "$HOME_DIR/.local/bin/fixture-notify" test -x "$HOME_DIR/.local/bin/fixture-notify" @@ -240,6 +251,7 @@ grep -q "local claude note" "$LOCAL_DIR/agent-docs/CLAUDE.md" grep -q "local claude note" "$HOME_DIR/.claude/CLAUDE.md" grep -q "potential must/must-not conflict" "$REPORT_PATH" grep -q '"key": "codex:command:review-cycle"' "$LOCK_PATH" +grep -q '"key": "codex:skill:hermes-manager"' "$LOCK_PATH" grep -q '"key": "no-agent:script:fixture-notify"' "$LOCK_PATH" grep -q '"key": "no-agent:script:inline-notify"' "$LOCK_PATH" grep -q '`dotfiles` priority 10: available' "$REPORT_PATH"