diff --git a/.gitignore b/.gitignore index 8ee9c4d3..8116dfed 100644 --- a/.gitignore +++ b/.gitignore @@ -423,6 +423,7 @@ FodyWeavers.xsd /docs/l10n/ /docs/.vuepress/public/MCC-README/ +/docs/superpowers/ # Floder to store the decompiled Minecraft official source code /MinecraftOfficial/ diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts index 18b7fa1b..d03ff77a 100644 --- a/docs/.vuepress/config.ts +++ b/docs/.vuepress/config.ts @@ -77,6 +77,8 @@ export default defineUserConfig({ // set site base to default value base: '/', + pagePatterns: ['**/*.md', '!.vuepress', '!node_modules', '!superpowers'], + // extra tags in `` head: headConfig, diff --git a/docs/superpowers/plans/2026-04-12-mcc-shared-server-isolated-sessions.md b/docs/superpowers/plans/2026-04-12-mcc-shared-server-isolated-sessions.md deleted file mode 100644 index 975787b9..00000000 --- a/docs/superpowers/plans/2026-04-12-mcc-shared-server-isolated-sessions.md +++ /dev/null @@ -1,789 +0,0 @@ -# MCC 共享服务器与隔离会话 Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 让多个 Git worktree 共享同一套本地 Minecraft 测试服务器,同时让每个 MCC 调试会话按 `session` 隔离,并支持按 worktree 选择性把构建输出放到 tmpfs。 - -**Architecture:** 保持 `mc-*` 命令面向共享服务器,保留 `MCC_SERVERS` 作为共享 server root override;把 `mcc-*` 命令改成面向显式 `session` 的客户端会话工具,默认 `session` 取当前 worktree 名,默认用户名从 `session` 派生。构建层通过统一的 helper 和 `Directory.Build.props` 接入可选的 tmpfs 构建根,不再依赖 shell 中泄漏的 `MCC_REPO`。 - -**Tech Stack:** Bash, tmux, dotnet CLI, MSBuild `Directory.Build.props`, repo 自带的 MCC integration harness - ---- - -### Task 1: 建立会话与构建根解析的基础 helper,并补一个可重复运行的 shell smoke test - -**Files:** -- Create: `tools/test-mcc-env.sh` -- Modify: `tools/mcc-env.sh` - -- [ ] **Step 1: 先写一个会失败的 shell smoke test,锁定会话名、用户名和路径解析规则** - -```bash -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" -source "$REPO_ROOT/tools/mcc-env.sh" - -assert_eq() { - local expected="$1" - local actual="$2" - local label="$3" - if [[ "$expected" != "$actual" ]]; then - echo "FAIL: $label" >&2 - echo " expected: $expected" >&2 - echo " actual: $actual" >&2 - exit 1 - fi -} - -assert_regex() { - local regex="$1" - local actual="$2" - local label="$3" - if [[ ! "$actual" =~ $regex ]]; then - echo "FAIL: $label" >&2 - echo " regex: $regex" >&2 - echo " actual: $actual" >&2 - exit 1 - fi -} - -session="$(_mcc_resolve_session "demo-branch")" -assert_eq "demo-branch" "$session" "explicit session" - -short_name="$(_mcc_resolve_username "feature-ai")" -assert_eq "mcc_feature_ai" "$short_name" "short derived username" - -long_name="$(_mcc_resolve_username "very-long-worktree-name")" -assert_regex '^mcc_[a-z0-9_]{7}_[0-9a-f]{4}$' "$long_name" "long derived username shape" -assert_eq "16" "${#long_name}" "long derived username length" - -assert_eq "${TMPDIR:-/tmp}/mcc-debug/demo-branch" "$(_mcc_session_root "demo-branch")" "session root" -assert_eq "mcc-demo-branch" "$(_mcc_tmux_session_name "demo-branch")" "tmux session name" - -MCC_BUILD_MODE=tmpfs -build_root="$(_mcc_build_root)" -assert_regex '^(/dev/shm|/tmp)/mcc-build/.+$' "$build_root" "tmpfs build root" - -echo "PASS" -``` - -- [ ] **Step 2: 运行测试,确认它先失败** - -Run: `bash tools/test-mcc-env.sh` - -Expected: FAIL,错误类似 `_mcc_resolve_session: command not found` - -- [ ] **Step 3: 在 `tools/mcc-env.sh` 中加入 repo root、server root、session、username、会话目录、tmux 名和 build root helper** - -```bash -if [[ -n "${BASH_SOURCE[0]:-}" ]]; then - _mcc_env_source="${BASH_SOURCE[0]}" -elif [[ -n "${ZSH_VERSION:-}" ]]; then - _mcc_env_source="${(%):-%N}" -else - _mcc_env_source="$0" -fi - -TOOLS_DIR="$(cd "$(dirname "$_mcc_env_source")" && pwd)" -MCC_REPO_ROOT="$(cd "$TOOLS_DIR/.." && pwd)" -unset _mcc_env_source - -_mcc_repo_root() { - printf '%s\n' "$MCC_REPO_ROOT" -} - -_mcc_servers_root() { - printf '%s\n' "${MCC_SERVERS:-$MCC_REPO_ROOT/MinecraftOfficial/downloads}" -} - -_mcc_current_worktree_name() { - git -C "$MCC_REPO_ROOT" rev-parse --show-toplevel 2>/dev/null | xargs basename -} - -_mcc_resolve_session() { - local explicit="${1:-}" - if [[ -n "$explicit" ]]; then - printf '%s\n' "$explicit" - return 0 - fi - - local worktree - worktree="$(_mcc_current_worktree_name)" - if [[ -n "$worktree" ]]; then - printf '%s\n' "$worktree" - else - basename "$MCC_REPO_ROOT" - fi -} - -_mcc_sha1_short() { - if command -v sha1sum >/dev/null 2>&1; then - printf '%s' "$1" | sha1sum | awk '{print substr($1, 1, 4)}' - else - printf '%s' "$1" | shasum -a 1 | awk '{print substr($1, 1, 4)}' - fi -} - -_mcc_resolve_username() { - local session="$1" - local normalized - normalized="$(printf '%s' "$session" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_]/_/g')" - local candidate="mcc_${normalized}" - if (( ${#candidate} <= 16 )); then - printf '%s\n' "$candidate" - return 0 - fi - - local hash - hash="$(_mcc_sha1_short "$normalized")" - printf '%s_%s\n' "${candidate:0:11}" "$hash" -} - -_mcc_session_root() { - printf '%s/mcc-debug/%s\n' "${TMPDIR:-/tmp}" "$1" -} - -_mcc_session_log_file() { - printf '%s/mcc-debug.log\n' "$(_mcc_session_root "$1")" -} - -_mcc_session_input_file() { - printf '%s/mcc_input.txt\n' "$(_mcc_session_root "$1")" -} - -_mcc_session_pid_file() { - printf '%s/mcc.pid\n' "$(_mcc_session_root "$1")" -} - -_mcc_session_meta_file() { - printf '%s/session.meta\n' "$(_mcc_session_root "$1")" -} - -_mcc_tmux_session_name() { - printf 'mcc-%s\n' "$1" -} - -_mcc_build_root() { - local worktree - worktree="$(_mcc_current_worktree_name)" - if [[ "${MCC_BUILD_MODE:-local}" == "tmpfs" ]]; then - if [[ -d /dev/shm && -w /dev/shm ]]; then - printf '/dev/shm/mcc-build/%s\n' "$worktree" - else - printf '%s/mcc-build/%s\n' "${TMPDIR:-/tmp}" "$worktree" - fi - return 0 - fi - - printf '%s\n' "$MCC_REPO_ROOT" -} -``` - -- [ ] **Step 4: 再跑 smoke test,确认规则都落地** - -Run: `bash tools/test-mcc-env.sh` - -Expected: PASS - -- [ ] **Step 5: 提交这一小步** - -```bash -git add tools/mcc-env.sh tools/test-mcc-env.sh -git commit -m "tools: add MCC session and build root helpers" -``` - -### Task 2: 让 `mcc-*` shell helper 全部按 `session` 工作,并新增安全的 session reset - -**Files:** -- Modify: `tools/mcc-env.sh` -- Test: `tools/test-mcc-env.sh` - -- [ ] **Step 1: 在 smoke test 里加入 `mcc-cmd` 和 `mcc-reset-session` 的行为检查** - -```bash -session="wrapper-smoke" -input_file="$(_mcc_session_input_file "$session")" -rm -rf "$(_mcc_session_root "$session")" - -mcc-cmd --session "$session" "debug state" -grep -Fq "debug state" "$input_file" - -mcc-reset-session --session "$session" -[[ ! -e "$(_mcc_session_root "$session")" ]] -``` - -- [ ] **Step 2: 运行测试,确认它先因为参数解析或命令不存在而失败** - -Run: `bash tools/test-mcc-env.sh` - -Expected: FAIL,错误类似 `mcc-reset-session: command not found` 或 `Unknown option: --session` - -- [ ] **Step 3: 在 `tools/mcc-env.sh` 里把 `mcc-*` helper 改成 session-aware** - -```bash -mcc-build() { - local repo_root - repo_root="$(_mcc_repo_root)" - if [[ "${MCC_BUILD_MODE:-local}" == "tmpfs" ]]; then - local build_root - build_root="$(_mcc_build_root)" - mkdir -p "$build_root" - MCC_BUILD_ROOT="$build_root" dotnet build "$repo_root/MinecraftClient.sln" -c Release - else - dotnet build "$repo_root/MinecraftClient.sln" -c Release - fi -} - -mcc-cmd() { - local session="" - local command="" - while [[ $# -gt 0 ]]; do - case "$1" in - --session) session="$2"; shift 2 ;; - *) command="$1"; shift ;; - esac - done - - [[ -n "$command" ]] || { echo "Usage: mcc-cmd [--session NAME] " >&2; return 1; } - session="$(_mcc_resolve_session "$session")" - local input_file - input_file="$(_mcc_session_input_file "$session")" - mkdir -p "$(dirname "$input_file")" - printf '%s\n' "$command" >> "$input_file" -} - -mcc-log-mcc() { - local session="${1:-}" - if [[ "$session" == "--session" ]]; then - session="${2:-}" - fi - session="$(_mcc_resolve_session "$session")" - tail -f "$(_mcc_session_log_file "$session")" -} - -mcc-state() { - local session="" - while [[ $# -gt 0 ]]; do - case "$1" in - --session) session="$2"; shift 2 ;; - *) echo "Unknown option: $1" >&2; return 1 ;; - esac - done - - session="$(_mcc_resolve_session "$session")" - mcc-cmd --session "$session" "debug state" - sleep 1 - tail -30 "$(_mcc_session_log_file "$session")" -} - -mcc-reset-session() { - local session="" - while [[ $# -gt 0 ]]; do - case "$1" in - --session) session="$2"; shift 2 ;; - *) echo "Unknown option: $1" >&2; return 1 ;; - esac - done - - session="$(_mcc_resolve_session "$session")" - tmux kill-session -t "$(_mcc_tmux_session_name "$session")" 2>/dev/null || true - rm -rf "$(_mcc_session_root "$session")" -} -``` - -- [ ] **Step 4: 运行 smoke test,确认 session input 与 reset 生效** - -Run: `bash tools/test-mcc-env.sh` - -Expected: PASS - -- [ ] **Step 5: 提交这一小步** - -```bash -git add tools/mcc-env.sh tools/test-mcc-env.sh -git commit -m "tools: scope MCC shell helpers by session" -``` - -### Task 3: 改造 `mcc-debug.sh`、`mcc-log-tail.sh` 和 kill 流程,隔离 tmux、log、config、PID、metadata - -**Files:** -- Modify: `tools/mcc-debug.sh` -- Modify: `tools/mcc-log-tail.sh` -- Modify: `tools/mcc-env.sh` - -- [ ] **Step 1: 用真实命令先验证当前行为会互相覆盖** - -Run: - -```bash -source tools/mcc-env.sh -mcc-debug -v 1.21.11 --file-input --no-build -ls -la /tmp/mcc-debug -tmux list-sessions | grep '^mcc-debug:' -``` - -Expected: 看到固定目录 `/tmp/mcc-debug` 和固定 tmux 名 `mcc-debug` - -- [ ] **Step 2: 在 `tools/mcc-debug.sh` 中加入 `--session` 和 `--username`,并把所有运行时工件改成 session-scoped** - -```bash -SESSION="" -USERNAME="" - -while [[ $# -gt 0 ]]; do - case "$1" in - --session) SESSION="$2"; shift 2 ;; - --username) USERNAME="$2"; shift 2 ;; - -v|--version) VERSION="$2"; shift 2 ;; - -m|--mode) MODE="$2"; shift 2 ;; - -p|--port) PORT="$2"; PORT_SET_BY_USER=true; shift 2 ;; - --no-build) DO_BUILD=false; shift ;; - --debug-on) DEBUG_ON=true; shift ;; - --file-input) FILE_INPUT=true; shift ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;; - esac -done - -SESSION="$(_mcc_resolve_session "$SESSION")" -USERNAME="${USERNAME:-$(_mcc_resolve_username "$SESSION")}" -SESSION_ROOT="$(_mcc_session_root "$SESSION")" -CFG="$SESSION_ROOT/MinecraftClient.debug.ini" -MCC_LOG="$(_mcc_session_log_file "$SESSION")" -INPUT_FILE="$(_mcc_session_input_file "$SESSION")" -PID_FILE="$(_mcc_session_pid_file "$SESSION")" -META_FILE="$(_mcc_session_meta_file "$SESSION")" -MCC_TMUX_SESSION="$(_mcc_tmux_session_name "$SESSION")" - -mkdir -p "$SESSION_ROOT" - -cat > "$META_FILE" <&2; return 1 ;; - esac - done - - session="$(_mcc_resolve_session "$session")" - local pid_file meta_file tmux_name - pid_file="$(_mcc_session_pid_file "$session")" - meta_file="$(_mcc_session_meta_file "$session")" - tmux_name="$(_mcc_tmux_session_name "$session")" - - if [[ -f "$pid_file" ]]; then - pid="$(cat "$pid_file")" - if kill -0 "$pid" 2>/dev/null; then - kill "$pid" - wait "$pid" 2>/dev/null || true - fi - rm -f "$pid_file" - fi - - tmux kill-session -t "$tmux_name" 2>/dev/null || true - [[ -f "$meta_file" ]] && rm -f "$meta_file" -} -``` - -- [ ] **Step 4: 让 `tools/mcc-log-tail.sh` 支持 `--session`,读取 session-specific log** - -```bash -SESSION="" - -while [[ $# -gt 0 ]]; do - case "$1" in - --session) SESSION="$2"; shift 2 ;; - --server) SERVER_VER="$2"; shift 2 ;; - --server-only) SERVER_ONLY=true; SERVER_VER="$2"; shift 2 ;; - -h|--help) echo "Usage: tools/mcc-log-tail.sh [--session NAME] [--server VER] [--server-only VER]"; exit 0 ;; - *) echo "Unknown option: $1"; exit 1 ;; - esac -done - -SESSION="$(_mcc_resolve_session "$SESSION")" -MCC_LOG="$(_mcc_session_log_file "$SESSION")" -``` - -- [ ] **Step 5: 用两个显式 session 跑一次真实 smoke** - -Run: - -```bash -source tools/mcc-env.sh -mcc-debug -v 1.21.11 --session smoke-a --username SmokeA --file-input --no-build -mcc-debug -v 1.21.11 --session smoke-b --username SmokeB --file-input --no-build -test -f "$(_mcc_session_log_file smoke-a)" -test -f "$(_mcc_session_log_file smoke-b)" -test -f "$(_mcc_session_meta_file smoke-a)" -test -f "$(_mcc_session_meta_file smoke-b)" -tmux list-sessions | grep '^mcc-smoke-a:' -tmux list-sessions | grep '^mcc-smoke-b:' -``` - -Expected: 两套独立文件与两套 tmux 会话都存在 - -- [ ] **Step 6: 提交这一小步** - -```bash -git add tools/mcc-debug.sh tools/mcc-log-tail.sh tools/mcc-env.sh -git commit -m "tools: isolate MCC debug state by session" -``` - -### Task 4: 加入按 worktree 选择性启用的 tmpfs 构建根,并修正外置 `obj` 的 MSBuild 过滤问题 - -**Files:** -- Create: `Directory.Build.props` -- Modify: `tools/mcc-env.sh` -- Test: `tools/test-mcc-env.sh` - -- [ ] **Step 1: 先用当前仓库验证“直接外置 obj 会失败”,把这个回归固定住** - -Run: - -```bash -rm -rf /tmp/mcc-plan-build-smoke -dotnet build MinecraftClient.sln -c Release -v minimal --nologo \ - -p:BaseOutputPath=/tmp/mcc-plan-build-smoke/bin/ \ - -p:BaseIntermediateOutputPath=/tmp/mcc-plan-build-smoke/obj/ \ - -p:MSBuildProjectExtensionsPath=/tmp/mcc-plan-build-smoke/obj/ -``` - -Expected: FAIL,包含重复 `AssemblyInfo` 或 `TargetFrameworkAttribute` 一类错误 - -- [ ] **Step 2: 新建 `Directory.Build.props`,统一把 `MCC_BUILD_ROOT` 接到每个项目自己的外置 `bin/obj`,并显式排除仓库内 `bin/obj`** - -```xml - - - $(DefaultItemExcludes);**/bin/**;**/obj/** - - - - $(MCC_BUILD_ROOT)/$(MSBuildProjectName)/bin/ - $(MCC_BUILD_ROOT)/$(MSBuildProjectName)/obj/ - $(BaseIntermediateOutputPath) - - -``` - -- [ ] **Step 3: 在 `tools/mcc-env.sh` 中把 `mcc-build`、`mcc-run`、`mcc-tui`、`mcc-debug` 的 `dotnet` 调用统一接入 `MCC_BUILD_MODE=tmpfs`** - -```bash -_mcc_dotnet_env() { - if [[ "${MCC_BUILD_MODE:-local}" == "tmpfs" ]]; then - local build_root - build_root="$(_mcc_build_root)" - mkdir -p "$build_root" - env MCC_BUILD_ROOT="$build_root" "$@" - return 0 - fi - - "$@" -} - -mcc-run() { - local session="" username="" port="25565" - while [[ $# -gt 0 ]]; do - case "$1" in - --session) session="$2"; shift 2 ;; - --username) username="$2"; shift 2 ;; - --port) port="$2"; shift 2 ;; - *) break ;; - esac - done - - session="$(_mcc_resolve_session "$session")" - username="${username:-$(_mcc_resolve_username "$session")}" - mkdir -p "$(_mcc_session_root "$session")" - _mcc_dotnet_env env \ - MCC_FILE_INPUT=1 \ - MCC_INPUT_FILE="$(_mcc_session_input_file "$session")" \ - dotnet run --project "$(_mcc_repo_root)/MinecraftClient" -c Release -- \ - "$(_mcc_session_root "$session")/MinecraftClient.debug.ini" \ - "$username" \ - - \ - "localhost:$port" -} - -mcc-tui() { - local session="" username="" port="25565" - while [[ $# -gt 0 ]]; do - case "$1" in - --session) session="$2"; shift 2 ;; - --username) username="$2"; shift 2 ;; - --port) port="$2"; shift 2 ;; - *) break ;; - esac - done - - session="$(_mcc_resolve_session "$session")" - username="${username:-$(_mcc_resolve_username "$session")}" - tmux new-session -d -s "$(_mcc_tmux_session_name "$session")" -x 160 -y 50 \ - "cd '$(_mcc_repo_root)' && MCC_BUILD_MODE='${MCC_BUILD_MODE:-local}' bash tools/mcc-debug.sh --session '$session' --username '$username' -p '$port' -m tui --no-build" -} - -mcc-build-clean() { - if [[ "${MCC_BUILD_MODE:-local}" == "tmpfs" ]]; then - rm -rf "$(_mcc_build_root)" - else - dotnet clean "$(_mcc_repo_root)/MinecraftClient.sln" -c Release - fi -} -``` - -- [ ] **Step 4: 扩展 smoke test,检查 `tmpfs` build root helper 和清理命令** - -```bash -MCC_BUILD_MODE=tmpfs -build_root="$(_mcc_build_root)" -mkdir -p "$build_root/probe" -mcc-build-clean -[[ ! -e "$build_root/probe" ]] -``` - -- [ ] **Step 5: 用 tmpfs 模式重新 build,确认能成功且产物出现在 worktree 专属构建根** - -Run: - -```bash -source tools/mcc-env.sh -export MCC_BUILD_MODE=tmpfs -mcc-build -find "$(_mcc_build_root)" -maxdepth 3 -type d | sed -n '1,20p' -``` - -Expected: `Build succeeded.`,并且构建目录位于 `/dev/shm/mcc-build//` 或 `/tmp/mcc-build//` - -- [ ] **Step 6: 提交这一小步** - -```bash -git add Directory.Build.props tools/mcc-env.sh tools/test-mcc-env.sh -git commit -m "build: add worktree-aware tmpfs build mode" -``` - -### Task 5: 更新 integration harness,并新增“单服务器双会话”自动化 smoke test - -**Files:** -- Create: `.skills/mcc-integration-testing/scripts/run_parallel_session_smoke_test.sh` -- Modify: `.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh` -- Modify: `.skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh` -- Modify: `tools/run-creative-e2e.sh` - -- [ ] **Step 1: 新增并行会话 smoke test 脚本,先把预期行为写下来** - -```bash -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" -source "$REPO_ROOT/tools/mcc-env.sh" -source "$SCRIPT_DIR/common.sh" - -VERSION="${1:-1.21.11}" -SESSION_A="parallel-a" -SESSION_B="parallel-b" -USER_A="ParallelA" -USER_B="ParallelB" - -bash "$SCRIPT_DIR/preflight_test_env.sh" "$VERSION" >/dev/null -bash "$SCRIPT_DIR/reset_shared_test_state.sh" "$VERSION" >/dev/null -mcc-reset-session --session "$SESSION_A" -mcc-reset-session --session "$SESSION_B" -mcc-build -mc-start "$VERSION" >/dev/null -wait_for_server_ready "$VERSION" - -mcc-debug -v "$VERSION" --session "$SESSION_A" --username "$USER_A" --file-input --no-build -mcc-debug -v "$VERSION" --session "$SESSION_B" --username "$USER_B" --file-input --no-build - -grep -Fq "Server was successfully joined" "$(_mcc_session_log_file "$SESSION_A")" -grep -Fq "Server was successfully joined" "$(_mcc_session_log_file "$SESSION_B")" -mcc-cmd --session "$SESSION_A" "debug state" -mcc-cmd --session "$SESSION_B" "debug state" - -mcc-kill --session "$SESSION_A" -tmux has-session -t "$(_mcc_tmux_session_name "$SESSION_B")" -mc-log "$VERSION" 50 | grep -Fq "$USER_B joined the game" -``` - -- [ ] **Step 2: 运行脚本,确认它先因为新参数或旧的共享路径逻辑而失败** - -Run: `bash .skills/mcc-integration-testing/scripts/run_parallel_session_smoke_test.sh 1.21.11` - -Expected: FAIL,错误类似 `Unknown option: --session`、固定 `mcc_input.txt` 被共用,或者只有一个客户端会话存活 - -- [ ] **Step 3: 把 `reset_shared_test_state.sh` 缩回“只管共享服务器”,不要再删除 repo 根下 `mcc_input.txt`** - -```bash -if [[ $# -eq 0 || "${1:-}" == "--all" ]]; then - while IFS= read -r session_name; do - [[ -z "$session_name" ]] && continue - kill_named_session "$session_name" - done < <(tmux list-sessions 2>/dev/null | awk -F: '/^mc-/{print $1}' || true) - - while IFS= read -r pipe_path; do - [[ -z "$pipe_path" ]] && continue - if [[ ! -p "$pipe_path" ]]; then - rm -f "$pipe_path" - fi - done < <(find "$MCC_SERVERS" -maxdepth 2 -name 'stdin.pipe' 2>/dev/null || true) -fi -``` - -- [ ] **Step 4: 更新现有 integration 脚本,让它们使用 session-specific input/log,而不是 repo 根下固定文件** - -```bash -SESSION="creative-e2e-${SERVER_DIR//\//_}" -USERNAME="$(_mcc_resolve_username "$SESSION")" -INPUT_FILE="$(_mcc_session_input_file "$SESSION")" -MCC_LOG="$(_mcc_session_log_file "$SESSION")" - -rm -rf "$(_mcc_session_root "$SESSION")" -mkdir -p "$(_mcc_session_root "$SESSION")" - -( - cd "$REPO_ROOT" - MCC_FILE_INPUT=1 dotnet run --project MinecraftClient -c Release --no-build -- \ - "$CFG" \ - "$USERNAME" \ - - \ - "localhost:$SERVER_PORT" \ - > "$MCC_LOG" 2>&1 -) & -``` - -- [ ] **Step 5: 重新运行并行 smoke test 与现有 creative/full-spectrum 回归** - -Run: - -```bash -bash .skills/mcc-integration-testing/scripts/run_parallel_session_smoke_test.sh 1.21.11 -bash tools/run-creative-e2e.sh 1.21.11 1.21.11 modern -bash .skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh 1.21.11 -``` - -Expected: 三个脚本都 PASS;并行 smoke test 中一个 session 被 kill 后,另一个 session 和共享服务器继续存活 - -- [ ] **Step 6: 提交这一小步** - -```bash -git add .skills/mcc-integration-testing/scripts/reset_shared_test_state.sh \ - .skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh \ - .skills/mcc-integration-testing/scripts/run_parallel_session_smoke_test.sh \ - tools/run-creative-e2e.sh -git commit -m "test: cover shared server with isolated MCC sessions" -``` - -### Task 6: 更新文档,并给出两个 worktree 并行调试的标准操作流程 - -**Files:** -- Modify: `tools/README.md` -- Modify: `docs/guide/ai-assisted-development.md` -- Modify: `.skills/mcc-dev-workflow/SKILL.md` - -- [ ] **Step 1: 在文档里先补“共享服务器,隔离 MCC 会话”的核心规则** - -```md -## Shared Server, Isolated MCC Sessions - -- `mc-*` commands operate on the shared local Minecraft server. -- `mcc-*` commands operate on one MCC client session. -- Default `session` = current worktree name. -- Default username is derived from `session`, so two worktrees can join the same server without kicking each other. -``` - -- [ ] **Step 2: 在 `mcc-dev-workflow` 文档和 skill 里补两个 worktree 的真实示例** - -````md -```bash -# worktree A -cd ~/Minecraft/Minecraft-Console-Client -source tools/mcc-env.sh -mc-start 1.21.11 -mcc-debug -v 1.21.11 --file-input - -# worktree B -cd ~/Minecraft/Minecraft-Console-Client-foo -source tools/mcc-env.sh -mcc-debug -v 1.21.11 --file-input - -# Each worktree gets: -# - its own session -# - its own username -# - its own log/input/config/tmux session -# - the same shared server -``` -```` - -- [ ] **Step 3: 在文档里加入 tmpfs 构建模式与清理命令** - -````md -### tmpfs build mode - -```bash -source tools/mcc-env.sh -export MCC_BUILD_MODE=tmpfs -mcc-build -mcc-build-clean -``` - -When `MCC_BUILD_MODE=tmpfs`, build outputs are redirected to `/dev/shm/mcc-build//` on Linux, or `${TMPDIR:-/tmp}/mcc-build//` if `/dev/shm` is unavailable. -```` - -- [ ] **Step 4: 跑最终验证矩阵并保存关键输出** - -Run: - -```bash -bash tools/test-mcc-env.sh -source tools/mcc-env.sh && unset MCC_BUILD_MODE && mcc-build -source tools/mcc-env.sh && export MCC_BUILD_MODE=tmpfs && mcc-build -bash .skills/mcc-integration-testing/scripts/run_parallel_session_smoke_test.sh 1.21.11 -``` - -Expected: - -```text -PASS -Build succeeded. -Build succeeded. -parallel session smoke: PASS -``` - -- [ ] **Step 5: 提交文档与最终验证结果** - -```bash -git add tools/README.md docs/guide/ai-assisted-development.md .skills/mcc-dev-workflow/SKILL.md -git commit -m "docs: document shared server and isolated MCC sessions" -``` - -### Self-Review Checklist - -- [ ] 计划里的 `session`、`username`、`MCC_BUILD_MODE`、`MCC_BUILD_ROOT` 命名在所有任务中一致 -- [ ] 没有任何步骤重新引入 repo 根下固定 `mcc_input.txt` -- [ ] 没有任何步骤重新使用 `pkill -f "MinecraftClient"` -- [ ] 并行 smoke test 明确验证“共享服务器 + 双会话 + 单边 kill 不影响另一边” -- [ ] tmpfs build 任务明确覆盖了当前已知的 `obj` 外置重复编译问题 diff --git a/docs/superpowers/specs/2026-04-12-shared-server-isolated-mcc-sessions-design.md b/docs/superpowers/specs/2026-04-12-shared-server-isolated-mcc-sessions-design.md deleted file mode 100644 index 9fc0e376..00000000 --- a/docs/superpowers/specs/2026-04-12-shared-server-isolated-mcc-sessions-design.md +++ /dev/null @@ -1,316 +0,0 @@ -# Shared Server, Isolated MCC Sessions Design - -## Goal - -Keep local Minecraft server instances shared across worktrees while making MCC debug sessions fully isolated by default. - -The desired workflow is: - -- Multiple Git worktrees can build in parallel without output collisions. -- One shared local test server can be reused by multiple MCC clients. -- Multiple MCC debug sessions can connect to that shared server at the same time without clobbering each other's tmux sessions, logs, temp config, input file, PID tracking, or usernames. - -## Background - -The current repository tooling mixes two different scopes: - -- Shared infrastructure state, such as local server jars and tmux server sessions -- Per-MCC-client debug state, such as `mcc_input.txt`, `/tmp/mcc-debug`, and the fixed `mcc-debug` tmux session - -That works for one active workspace, but it breaks down once multiple worktrees are used at the same time. - -Today the main problems are: - -1. `MCC_REPO` can leak from one shell into another and point helper commands at the wrong worktree. -2. `mcc-debug.sh` writes all temp artifacts into the same `/tmp/mcc-debug` directory. -3. MCC tmux sessions use the same fixed name, `mcc-debug`. -4. MCC helper commands such as `mcc-kill` operate globally instead of targeting one debug session. -5. The default MCC username is fixed, so two clients connecting to the same server will kick each other. -6. Build outputs live inside each worktree by default, which is correct for isolation, but the workflow does not offer an intentional tmpfs-backed fast path for machines with large RAM. - -## Non-Goals - -- Do not isolate or duplicate server assets by worktree. -- Do not support multiple independent servers with the same version name running in parallel in this change. -- Do not redesign the Minecraft runtime or MCC account model. -- Do not make tmpfs build output mandatory. - -## Design Principles - -- Shared resources stay explicit and few. -- Per-session MCC state is isolated by default. -- Repo discovery is local to each script, not inherited from an ambient shell variable. -- Existing workflows should keep working with minimal changes when only one MCC session is active. -- Performance optimizations must not undermine correctness or isolation. - -## State Model - -### Shared State - -The following remain shared across worktrees and shells: - -- `MCC_SERVERS`, when explicitly set by the user -- Default server root at `/MinecraftOfficial/downloads` when `MCC_SERVERS` is not set -- Server tmux session names, still keyed by Minecraft version, for example `mc-1_21_11` -- Server stdin pipes and world data under the shared server root -- RCON access to the shared server - -### Per-Session MCC State - -Each MCC debug session is keyed by a session identifier. - -The following must be isolated per session: - -- MCC tmux session -- Temp config -- MCC log -- MCC input file -- MCC PID file -- Session metadata used by helper commands -- Default username - -### Per-Worktree Build State - -Each worktree gets its own build output root. - -Build isolation is keyed by worktree name, not MCC session name, so multiple MCC sessions from one worktree can share the same compiled binaries. - -## Naming and Identity - -### Session Name - -- A new `session` concept is introduced for all `mcc-*` commands. -- If the user passes `--session NAME`, that value is used. -- Otherwise the default is the current Git worktree name. -- If a worktree name cannot be resolved, the fallback is the basename of the current repo root. - -### Username - -- If the user passes `--username NAME`, that value is used. -- Otherwise the username is derived from the resolved `session`. -- The derived name must: - - use only Minecraft-safe characters already accepted by the existing config flow - - be deterministic - - be at most 16 characters - - remain stable for the same session across runs - -Recommended derivation: - -1. Lowercase the session name. -2. Replace invalid characters with underscores. -3. Prefix with `mcc_`. -4. If the result is longer than 16 characters, keep the first 11 characters and append `_` plus the first 4 hexadecimal characters of the SHA-1 of the normalized session string. - -Example: - -- `feature-ai` -> `mcc_feature_ai` -- `very-long-worktree-name` -> `mcc_veryl_ab12` - -This truncation algorithm must be implemented and tested exactly as written so future changes do not silently rename active debug identities. - -## Repo and Server Root Resolution - -### Repo Root - -Helper scripts must stop exporting `MCC_REPO` as shell-global state. - -Instead: - -- Each script resolves its own repo root from its own location. -- Shared shell helper functions may cache that resolved path internally, but not as a required ambient variable. -- Child commands should receive explicit paths or recompute the repo root themselves. - -This removes the current failure mode where a shell sourced from one clone or worktree accidentally drives tools in another. - -### Server Root - -`MCC_SERVERS` stays as the supported override for the shared server root. - -Resolution order: - -1. If `MCC_SERVERS` is set, use it. -2. Otherwise use `/MinecraftOfficial/downloads`. - -This keeps the useful part of the current workflow: one intentionally shared set of server assets. - -## File and Process Layout - -### MCC Session Root - -Each MCC session gets a unique root directory: - -- `${TMPDIR:-/tmp}/mcc-debug//` - -That directory contains: - -- `MinecraftClient.debug.ini` -- `mcc-debug.log` -- `mcc_input.txt` -- `mcc.pid` -- `session.meta` - -### tmux Sessions - -MCC tmux sessions must be renamed from the fixed `mcc-debug` to a session-scoped name, for example: - -- `mcc-` - -Server tmux sessions remain version-scoped: - -- `mc-` - -### Kill and Reset Scope - -- `mcc-kill --session X` only stops the MCC process and tmux session for `X`. -- A new `mcc-reset-session` command should clear only session-scoped files and tmux sessions. -- Existing shared server reset logic must continue to operate on server state only. -- No `mcc-*` command may kill all `MinecraftClient` processes by pattern. - -## Command Interface - -### Shared Server Commands - -These remain shared-resource commands and do not take `--session`: - -- `mc-start` -- `mc-stop` -- `mc-log` -- `mc-rcon` -- `mc-wait-ready` -- `mc-wait-stop` - -### Session-Scoped MCC Commands - -These commands gain `--session`, and where applicable `--username`: - -- `mcc-debug` -- `mcc-run` -- `mcc-tui` -- `mcc-cmd` -- `mcc-log-mcc` -- `mcc-state` -- `mcc-kill` - -Expected defaults: - -- `--session`: current worktree name -- `--username`: derived from session - -Example commands: - -```bash -mcc-debug -v 1.21.11 --session alice-a --username AliceA --file-input -mcc-debug -v 1.21.11 --session alice-b --username AliceB --file-input -mcc-cmd --session alice-a "debug state" -mcc-log-mcc --session alice-b -mcc-kill --session alice-a -mcc-reset-session --session alice-b -``` - -## Build Output Isolation - -### Base Rule - -Build outputs must be isolated by worktree, not mixed across worktrees. - -This should be implemented by introducing a build root override passed through the helper scripts into `dotnet build` and `dotnet run`, without requiring users to manually edit project files per worktree. - -The important behavior is: - -- Worktree A writes to build root A -- Worktree B writes to build root B -- Both can build concurrently without sharing `bin/obj` - -### tmpfs Acceleration - -Add an opt-in tmpfs-backed build root for machines with large RAM. - -Suggested layout: - -- `/dev/shm/mcc-build//` on Linux -- fallback to `${TMPDIR:-/tmp}/mcc-build//` when `/dev/shm` is unavailable - -Requirements: - -- tmpfs mode is optional, not mandatory -- the resolved build root must be printed in debug output -- a new `mcc-build-clean` command should clear build outputs for the current worktree -- helper scripts must fail clearly if the chosen build root is not writable - -### Compatibility - -If tmpfs mode is disabled, behavior should remain functionally equivalent to today, except for the added worktree-aware path selection. - -## Backward Compatibility - -- Existing single-session workflows should still work without requiring `--session`. -- Existing external `MCC_SERVERS` setups must continue to work. -- Existing server-side helper scripts should keep their public behavior. -- Existing documentation examples can be updated gradually, but the basic commands should remain recognizable. - -## Error Handling - -The tooling should fail early and clearly for: - -- missing server root -- invalid or empty session name -- invalid derived username after normalization -- requested tmux session already in use by a different live MCC process -- unwritable tmpfs or build root -- missing PID file on targeted kill operations - -When possible, the error should print the resolved repo root, shared server root, session name, username, and session root to make misconfiguration obvious. - -## Validation Plan - -### Manual Verification Matrix - -1. Build from two different worktrees at the same time and confirm isolated output roots. -2. Start one shared `1.21.11` server and confirm only one `mc-1_21_11` session exists. -3. Launch two MCC sessions from two different worktrees without explicit usernames and confirm distinct derived usernames. -4. Join both clients to the shared server and confirm neither client disconnects the other. -5. Send different commands through each session's input file and confirm only the intended client responds. -6. Tail each session's log and confirm no cross-session log mixing. -7. Kill one MCC session and confirm: - - the other MCC session stays connected - - the shared server remains running -8. Enable tmpfs build mode in one worktree and verify outputs are written to the resolved tmpfs path. -9. Rebuild after cleaning tmpfs outputs and confirm the worktree still functions. - -### Regression Checks - -- Single-session debug loop still works with no explicit `--session` -- Shared server helpers still work when `MCC_SERVERS` points outside the repo -- TUI mode still works inside tmux with session-specific naming - -## Implementation Notes - -The most likely files to change are: - -- `tools/mcc-env.sh` -- `tools/mcc-debug.sh` -- `tools/start-server.sh` -- `.skills/mcc-integration-testing/scripts/preflight_test_env.sh` -- `.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh` -- `tools/README.md` -- `docs/guide/ai-assisted-development.md` -- `.skills/mcc-dev-workflow/SKILL.md` - -If build-output redirection is implemented through MSBuild configuration rather than pure shell arguments, related project or props files may also need changes. - -## Open Decisions Resolved In This Design - -- Keep `MCC_SERVERS`: yes -- Keep `MCC_REPO` as shell-global state: no -- Shared server instances across worktrees: yes -- MCC debug isolation keyed by explicit `session`: yes -- Default `session`: current worktree name -- Default username: derived from session -- Build output isolation key: current worktree -- tmpfs build output: opt-in optimization - -## Summary - -The resulting workflow intentionally shares the expensive and durable part of local MCC development, the server installation and running server instance, while isolating the volatile and user-specific part, the MCC client debug session and build outputs. - -That gives parallel worktree development without forcing duplicate local servers, while removing the current collisions around shell state, tmux naming, temp files, input routing, and username reuse.