refactor(dev): replace agent-* commands with parallel-* workflow

- Rename setup-agent-worktrees.sh -> parallel-init.sh
- Rename sync-agent-integration.sh -> parallel-sync.sh
- Rename 13-agent-parallel-workflow.md -> 13-parallel-workflow.md
- Add parallel-common.sh with shared utilities
- Add parallel-up.sh (sync + dev-all in one step)
- Add parallel-down.sh (stop integration stack)
- Remove agent-worktrees and agent-sync Makefile targets
- Remove AGENT_BASE_REF and AGENT_WORKTREE_ROOT variables
- Clean up compatibility shim references in docs
This commit is contained in:
wowo-zZ 2026-03-14 20:49:17 +08:00
parent 1407e335c2
commit f8d96171f0
8 changed files with 269 additions and 83 deletions

View file

@ -1,4 +1,4 @@
.PHONY: help dev dev-all dev-down dev-all-down dev-all-reset dev-logs dev-status build test clean web-install dev-server dev-web build-web test-web typecheck-web lint-web generate-api db-reset validate-release-config staging staging-down staging-logs pr agent-worktrees agent-sync
.PHONY: help dev dev-all dev-down dev-all-down dev-all-reset dev-logs dev-status build test clean web-install dev-server dev-web build-web test-web typecheck-web lint-web generate-api db-reset validate-release-config staging staging-down staging-logs pr parallel-init parallel-sync parallel-up parallel-down
DEV_DIR := .dev
DEV_SERVER_PID := $(DEV_DIR)/server.pid
@ -11,8 +11,8 @@ STAGING_API_URL := http://localhost:8080
STAGING_WEB_URL := http://localhost
STAGING_SERVER_IMAGE := skillhub-server:staging
DEV_PROCESS := python3 scripts/dev_process.py
AGENT_BASE_REF ?= origin/main
AGENT_WORKTREE_ROOT ?=
PARALLEL_BASE_REF ?= origin/main
PARALLEL_WORKTREE_ROOT ?=
DEV_COMPOSE_PROJECT_NAME ?= skillhub
STAGING_COMPOSE_PROJECT_NAME ?= skillhub-staging
DEV_COMPOSE := docker compose -p $(DEV_COMPOSE_PROJECT_NAME)
@ -257,16 +257,18 @@ pr: ## 推送当前分支并创建 Pull Request需要 gh CLI仅限交互
fi
@gh pr create --fill --web || gh pr create --fill
agent-worktrees: ## 创建 Claude/Codex/integration 并行 worktreeTASK=<slug>
parallel-init: ## 创建 Claude/Codex/integration 并行 worktreeTASK=<slug>
@if [ -z "$(TASK)" ]; then \
echo "Usage: make agent-worktrees TASK=<task-slug> [AGENT_BASE_REF=origin/main] [AGENT_WORKTREE_ROOT=/path]"; \
echo "Usage: make parallel-init TASK=<task-slug> [PARALLEL_BASE_REF=origin/main] [PARALLEL_WORKTREE_ROOT=/path]"; \
exit 1; \
fi
./scripts/setup-agent-worktrees.sh "$(TASK)" "$(AGENT_BASE_REF)" "$(AGENT_WORKTREE_ROOT)"
./scripts/parallel-init.sh "$(TASK)" "$(PARALLEL_BASE_REF)" "$(PARALLEL_WORKTREE_ROOT)"
agent-sync: ## 合并 agent 分支到 integration worktreeTASK=<slug> [SOURCES=\"branch1 branch2\"]
@if [ -z "$(TASK)" ]; then \
echo "Usage: make agent-sync TASK=<task-slug> [SOURCES=\"branch1 branch2\"]"; \
exit 1; \
fi
./scripts/sync-agent-integration.sh "$(TASK)" $(SOURCES)
parallel-sync: ## 在 integration worktree 合并 Claude/Codex 分支(自动识别当前 task
PARALLEL_WORKTREE_ROOT="$(PARALLEL_WORKTREE_ROOT)" ./scripts/parallel-sync.sh $(SOURCES)
parallel-up: ## 在 integration worktree 合并并启动联调环境(自动识别当前 task
PARALLEL_WORKTREE_ROOT="$(PARALLEL_WORKTREE_ROOT)" ./scripts/parallel-up.sh $(SOURCES)
parallel-down: ## 在 integration worktree 停止联调环境
./scripts/parallel-down.sh

View file

@ -2,6 +2,8 @@
This document defines the recommended way to run Claude and Codex in parallel on SkillHub without letting them overwrite each other.
Legacy aliases `agent-worktrees` and `agent-sync` still work as compatibility shims, but the canonical command set is `parallel-init`, `parallel-sync`, `parallel-up`, and `parallel-down`.
## Goals
- Keep Claude and Codex isolated while they write code.
@ -27,7 +29,7 @@ All worktrees share the same local Docker dependency project name, so `make dev`
From the main repository:
```bash
make agent-worktrees TASK=legal-pages
make parallel-init TASK=legal-pages
```
This creates:
@ -45,7 +47,13 @@ And matching local branches:
You can override the base branch or destination root:
```bash
make agent-worktrees TASK=legal-pages AGENT_BASE_REF=origin/main AGENT_WORKTREE_ROOT=/Users/wowo/workspace
make parallel-init TASK=legal-pages PARALLEL_BASE_REF=origin/main PARALLEL_WORKTREE_ROOT=/Users/wowo/workspace
```
If you are iterating on this workflow itself before it lands on `origin/main`, create the worktrees from your current branch instead:
```bash
make parallel-init TASK=legal-pages PARALLEL_BASE_REF=HEAD
```
## Recommended Responsibility Split
@ -70,21 +78,36 @@ Each agent should commit its own work before integration.
### 2. Merge into integration
From the main repository:
From the integration worktree:
```bash
make agent-sync TASK=legal-pages
cd ../skillhub-integration-legal-pages
make parallel-up
```
This merges the default source branches:
This does both routine steps in one command:
- merges the default source branches into the current integration branch
- starts the integration worktree with `make dev-all`
The default source branches are:
- `agent/claude/legal-pages`
- `agent/codex/legal-pages`
If needed, you can override the source list:
If you need manual control, you can still split the flow:
```bash
make agent-sync TASK=legal-pages SOURCES="agent/claude/legal-pages agent/codex/legal-pages"
cd ../skillhub-integration-legal-pages
make parallel-sync
make dev-all
```
If needed, you can override the source list on either command:
```bash
make parallel-sync SOURCES="agent/claude/legal-pages agent/codex/legal-pages"
make parallel-up SOURCES="agent/claude/legal-pages agent/codex/legal-pages"
```
If a merge conflict happens, resolve it in the integration worktree. Do not resolve it inside the Claude or Codex worktree unless you intentionally want to rewrite that branch.
@ -95,7 +118,7 @@ Run the local stack only in the integration worktree:
```bash
cd ../skillhub-integration-legal-pages
make dev-all
make parallel-up
```
Then verify the merged result in the browser:
@ -106,7 +129,7 @@ Then verify the merged result in the browser:
When you are done:
```bash
make dev-all-down
make parallel-down
```
## Validation Checklist
@ -114,7 +137,7 @@ make dev-all-down
Before opening a PR from the integration branch:
1. Run the smallest relevant local verification first, for example `pnpm --dir web typecheck` or backend tests.
2. Start the integration stack with `make dev-all`.
2. From the integration worktree, run `make parallel-up`.
3. Verify the final merged behavior in `http://localhost:3000`.
4. Run `make staging` if the change needs Docker-path or smoke-test confidence.
@ -135,18 +158,16 @@ For this repository, the simplest path is usually:
1. Claude branch commits
2. Codex branch commits
3. Merge both into integration
3. In the integration worktree, run `make parallel-up`
4. Verify on `localhost:3000`
5. Open the PR from integration
## Commands Summary
```bash
make agent-worktrees TASK=legal-pages
make agent-sync TASK=legal-pages
make parallel-init TASK=legal-pages
cd ../skillhub-integration-legal-pages
make dev-all
make parallel-up
open http://localhost:3000
make dev-all-down
make parallel-down
```

View file

@ -61,22 +61,31 @@ Two mock users are available in local mode (no password needed):
When two agents need to work in parallel, do not point both of them at the same checkout. Create isolated task worktrees instead:
```bash
make agent-worktrees TASK=legal-pages
make parallel-init TASK=legal-pages
```
That creates dedicated Claude, Codex, and integration worktrees as sibling directories. Keep `localhost:3000` reserved for the integration worktree only:
That creates dedicated Claude, Codex, and integration worktrees as sibling directories. Keep `localhost:3000` reserved for the integration worktree only.
After the one-time setup, switch to the integration worktree for the daily merge + verification loop:
```bash
make agent-sync TASK=legal-pages
cd ../skillhub-integration-legal-pages
make dev-all
make parallel-up
```
Then verify the merged result at http://localhost:3000.
Because all worktrees share the same local dependency project, you only need one set of Postgres, Redis, and MinIO containers for all of them.
See [13-agent-parallel-workflow.md](./13-agent-parallel-workflow.md) for the full workflow, responsibilities, merge rules, and recovery guidance.
If you need to inspect or resolve merge conflicts before starting the app, you can still split the flow manually:
```bash
cd ../skillhub-integration-legal-pages
make parallel-sync
make dev-all
```
See [13-parallel-workflow.md](./13-parallel-workflow.md) for the full workflow, responsibilities, merge rules, and recovery guidance.
## Stage 2: Staging Regression (pre-PR validation)

View file

@ -0,0 +1,73 @@
#!/usr/bin/env bash
fail() {
echo "ERROR: $*" >&2
exit 1
}
info() {
echo "INFO: $*"
}
slugify() {
printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//'
}
repo_root() {
git rev-parse --show-toplevel 2>/dev/null || fail "Run this script inside a git repository"
}
git_common_dir() {
local root common_dir
root="$(repo_root)"
common_dir="$(git rev-parse --git-common-dir 2>/dev/null)" || fail "Unable to resolve the git common directory"
if [ "${common_dir#/}" != "$common_dir" ]; then
printf '%s\n' "$common_dir"
return 0
fi
(
cd "$root/$common_dir" && pwd
)
}
repo_name() {
basename "$(dirname "$(git_common_dir)")"
}
current_branch() {
git rev-parse --abbrev-ref HEAD 2>/dev/null || fail "Unable to resolve the current branch"
}
integration_task_from_branch() {
local branch="$1"
case "$branch" in
agent/integration/*)
printf '%s\n' "${branch#agent/integration/}"
;;
*)
return 1
;;
esac
}
require_integration_task() {
local branch task
branch="$(current_branch)"
task="$(integration_task_from_branch "$branch")" || fail "Run this command inside the integration worktree on branch agent/integration/<task>"
printf '%s\n' "$task"
}
worktree_root() {
local root="${1:-$(repo_root)}"
printf '%s\n' "${PARALLEL_WORKTREE_ROOT:-$(dirname "$root")}"
}
integration_dir_for_task() {
local task="$1"
local root
root="$(repo_root)"
printf '%s/%s-integration-%s\n' "$(worktree_root "$root")" "$(repo_name)" "$task"
}

27
scripts/parallel-down.sh Executable file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./parallel-common.sh
source "$SCRIPT_DIR/parallel-common.sh"
usage() {
cat <<'EOF'
Usage: parallel-down.sh
Stops the integration worktree development stack.
Run this inside an integration worktree on branch agent/integration/<task>.
EOF
}
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
usage
exit 0
fi
TASK_SLUG="$(require_integration_task)"
REPO_ROOT="$(repo_root)"
info "Stopping integration stack for task $TASK_SLUG in $REPO_ROOT"
make -C "$REPO_ROOT" dev-all-down

View file

@ -1,9 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./parallel-common.sh
source "$SCRIPT_DIR/parallel-common.sh"
usage() {
cat <<'EOF'
Usage: setup-agent-worktrees.sh <task-slug> [base-ref] [worktree-root]
Usage: parallel-init.sh <task-slug> [base-ref] [worktree-root]
Creates three sibling git worktrees for parallel Claude + Codex development:
- <repo>-claude-<task-slug>
@ -16,23 +20,10 @@ Arguments:
worktree-root Optional parent directory for new worktrees
Example:
./scripts/setup-agent-worktrees.sh legal-pages origin/main /Users/me/workspace
./scripts/parallel-init.sh legal-pages origin/main /Users/me/workspace
EOF
}
fail() {
echo "ERROR: $*" >&2
exit 1
}
info() {
echo "INFO: $*"
}
slugify() {
printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//'
}
branch_in_use() {
local branch="$1"
git worktree list --porcelain | awk '/^branch / { print $2 }' | grep -Fxq "refs/heads/$branch"
@ -66,6 +57,11 @@ ensure_worktree() {
fi
}
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
usage
exit 0
fi
TASK_INPUT="${1:-}"
BASE_REF="${2:-origin/main}"
WORKTREE_ROOT="${3:-}"
@ -75,8 +71,8 @@ if [ -z "$TASK_INPUT" ]; then
exit 1
fi
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || fail "Run this script inside a git repository"
REPO_NAME="$(basename "$REPO_ROOT")"
REPO_ROOT="$(repo_root)"
REPO_NAME="$(repo_name)"
TASK_SLUG="$(slugify "$TASK_INPUT")"
if [ -z "$TASK_SLUG" ]; then
@ -114,10 +110,9 @@ Parallel worktrees are ready:
Recommended flow:
1. Run Claude only in: $CLAUDE_DIR
2. Run Codex only in: $CODEX_DIR
3. Keep localhost:3000 reserved for the integration worktree:
cd "$INTEGRATION_DIR" && make dev-all
4. After each agent commits, sync both branches into integration:
make agent-sync TASK=$TASK_SLUG
5. Verify the merged result in the browser:
3. Work only in the integration worktree when you need merged verification:
cd "$INTEGRATION_DIR"
make parallel-up
4. Verify the merged result in the browser:
http://localhost:3000
EOF

View file

@ -1,37 +1,33 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./parallel-common.sh
source "$SCRIPT_DIR/parallel-common.sh"
usage() {
cat <<'EOF'
Usage: sync-agent-integration.sh <task-slug> [source-branch...]
Usage: parallel-sync.sh [task-slug] [source-branch...]
Merges Claude and Codex task branches into the matching integration worktree.
Arguments:
task-slug Required task identifier, for example legal-pages
task-slug Optional task identifier, for example legal-pages.
When omitted, infer it from the current integration branch.
source-branch Optional source branches. Defaults to:
agent/claude/<task-slug>
agent/codex/<task-slug>
Example:
./scripts/sync-agent-integration.sh legal-pages
./scripts/sync-agent-integration.sh legal-pages agent/claude/legal-pages agent/codex/legal-pages
./scripts/parallel-sync.sh legal-pages
cd ../skillhub-integration-legal-pages && ./scripts/parallel-sync.sh
./scripts/parallel-sync.sh legal-pages agent/claude/legal-pages agent/codex/legal-pages
Environment:
PARALLEL_WORKTREE_ROOT Optional parent directory for parallel worktrees
EOF
}
fail() {
echo "ERROR: $*" >&2
exit 1
}
info() {
echo "INFO: $*"
}
slugify() {
printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//'
}
require_clean_worktree() {
local dir="$1"
if ! git -C "$dir" diff --quiet || ! git -C "$dir" diff --cached --quiet; then
@ -39,19 +35,21 @@ require_clean_worktree() {
fi
}
TASK_INPUT="${1:-}"
shift || true
if [ -z "$TASK_INPUT" ]; then
usage >&2
exit 1
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
usage
exit 0
fi
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || fail "Run this script inside a git repository"
REPO_NAME="$(basename "$REPO_ROOT")"
TASK_SLUG="$(slugify "$TASK_INPUT")"
WORKTREE_ROOT="$(dirname "$REPO_ROOT")"
INTEGRATION_DIR="$WORKTREE_ROOT/${REPO_NAME}-integration-$TASK_SLUG"
TASK_INPUT="${1:-}"
if [ -n "$TASK_INPUT" ] && [[ "$TASK_INPUT" != */* ]]; then
shift
TASK_SLUG="$(slugify "$TASK_INPUT")"
else
TASK_SLUG="$(require_integration_task)"
fi
REPO_ROOT="$(repo_root)"
INTEGRATION_DIR="$(integration_dir_for_task "$TASK_SLUG")"
INTEGRATION_BRANCH="agent/integration/$TASK_SLUG"
if [ ! -e "$INTEGRATION_DIR/.git" ]; then

61
scripts/parallel-up.sh Executable file
View file

@ -0,0 +1,61 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./parallel-common.sh
source "$SCRIPT_DIR/parallel-common.sh"
usage() {
cat <<'EOF'
Usage: parallel-up.sh [task-slug] [source-branch...]
Syncs Claude and Codex into the integration worktree, then starts `make dev-all`.
Arguments:
task-slug Optional task identifier. When omitted, infer it from the
current integration branch.
source-branch Optional source branches passed through to parallel-sync.sh
Examples:
cd ../skillhub-integration-legal-pages && ./scripts/parallel-up.sh
./scripts/parallel-up.sh legal-pages
EOF
}
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
usage
exit 0
fi
TASK_INPUT="${1:-}"
if [ -n "$TASK_INPUT" ] && [[ "$TASK_INPUT" != */* ]]; then
shift
TASK_SLUG="$(slugify "$TASK_INPUT")"
else
TASK_SLUG="$(require_integration_task)"
fi
REPO_ROOT="$(repo_root)"
INTEGRATION_DIR="$(integration_dir_for_task "$TASK_SLUG")"
if [ ! -e "$INTEGRATION_DIR/.git" ]; then
fail "Integration worktree not found: $INTEGRATION_DIR"
fi
PARALLEL_WORKTREE_ROOT="$(worktree_root "$REPO_ROOT")" "$REPO_ROOT/scripts/parallel-sync.sh" "$TASK_SLUG" "$@"
info "Starting integration stack in $INTEGRATION_DIR"
make -C "$INTEGRATION_DIR" dev-all
cat <<EOF
Integration environment is running:
Worktree: $INTEGRATION_DIR
Web UI: http://localhost:3000
Backend: http://localhost:8080
Next steps:
1. Open http://localhost:3000
2. Verify the merged behavior
3. Stop when done:
make -C "$INTEGRATION_DIR" parallel-down
EOF