From 4a3328d0b7786e63db44e75ffbf28dda9efc4dfe Mon Sep 17 00:00:00 2001 From: Godopu Date: Fri, 28 Aug 2026 18:25:14 +0900 Subject: [PATCH] chore(release): bump framework and 8 skills to v3.1.0 (MINOR) - Define immutable MAM_VERSION="3.1.0" runtime constant in lib.sh - Bump YAML frontmatter version to 3.1.0 across all 8 skill packages - Document v3.1.0 changelog, 2-tier TUI readiness architecture, and migration guide in VERSIONS.md - Document new tuning knobs (MAM_KILL_ON_READY_TIMEOUT, MAM_DIALOG_TAIL_LINES, MAM_READY_EMPTY_GIVEUP) in .mam.env.example - Add 3-way lockstep and immutability test suite in tests/test_version_consistency.py --- .agents/skills/lib.sh | 6 ++ .../skills/multi-agent-mux-create/SKILL.md | 2 +- .../scripts/create_session.sh | 1 + .../multi-agent-mux-delegate-job/SKILL.md | 2 +- .agents/skills/multi-agent-mux-loop/SKILL.md | 2 +- .../skills/multi-agent-mux-monitor/SKILL.md | 2 +- .../multi-agent-mux-orc-onboard/SKILL.md | 4 +- .../skills/multi-agent-mux-resume/SKILL.md | 2 +- .../skills/multi-agent-mux-status/SKILL.md | 2 +- .agents/skills/multi-agent-mux-stop/SKILL.md | 2 +- .mam.env.example | 13 +++ VERSIONS.md | 86 ++++++++++++++---- tests/test_version_consistency.py | 90 +++++++++++++++++++ 13 files changed, 189 insertions(+), 25 deletions(-) create mode 100644 tests/test_version_consistency.py diff --git a/.agents/skills/lib.sh b/.agents/skills/lib.sh index c054599..4ffcdd5 100644 --- a/.agents/skills/lib.sh +++ b/.agents/skills/lib.sh @@ -25,6 +25,12 @@ SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" export PYTHONPATH="$SKILL_DIR:${PYTHONPATH:-}" WORKSPACE_ROOT="${WORKSPACE_ROOT:-$(cd "$SKILL_DIR/../.." && pwd)}" export WORKSPACE_ROOT + +# Framework semantic version. Single runtime source of truth; kept in lockstep +# with VERSIONS.md and the 8 SKILL.md frontmatters by tests/test_version_consistency.py. +# NOTE: unlike other MAM_* variables this one is intentionally NOT env-overridable. +MAM_VERSION="3.1.0" +export MAM_VERSION AGENT_SESSIONS_YAML="${AGENT_SESSIONS_YAML:-$WORKSPACE_ROOT/.mam/agent-sessions.yaml}" _sanitize_herdr_agent_name() { diff --git a/.agents/skills/multi-agent-mux-create/SKILL.md b/.agents/skills/multi-agent-mux-create/SKILL.md index 7df942b..1c3c38a 100644 --- a/.agents/skills/multi-agent-mux-create/SKILL.md +++ b/.agents/skills/multi-agent-mux-create/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-create description: "Create a new agent session (claude, antigravity/agy) in a dedicated herdr session for context-preserving long-running work. Always creates a herdr session โ€” never backgrounds with nohup/disown. Writes the new session to .mam/agent-sessions.yaml. Use when you want to start a fresh agent (no prior UUID) for a new project workspace." -version: 3.0.1 +version: 3.1.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-create/scripts/create_session.sh b/.agents/skills/multi-agent-mux-create/scripts/create_session.sh index bed4722..7d64868 100755 --- a/.agents/skills/multi-agent-mux-create/scripts/create_session.sh +++ b/.agents/skills/multi-agent-mux-create/scripts/create_session.sh @@ -249,6 +249,7 @@ if [ "$TUI_READY_RC" -ne 0 ]; then { echo "=== MAM Diagnostic Dump ===" echo "Date: $(date -u +'%Y-%m-%dT%H:%M:%SZ')" + echo "MAM Version: ${MAM_VERSION:-unknown}" echo "Session: $SESSION_NAME" echo "Agent: $AGENT" echo "ReturnCode: $TUI_READY_RC" diff --git a/.agents/skills/multi-agent-mux-delegate-job/SKILL.md b/.agents/skills/multi-agent-mux-delegate-job/SKILL.md index e99c014..c3e5562 100644 --- a/.agents/skills/multi-agent-mux-delegate-job/SKILL.md +++ b/.agents/skills/multi-agent-mux-delegate-job/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-delegate-job description: "Delegate a unit of work to any autonomous agent (claude-code, hermes, agy, cline, grok-build, codex, or a human) and observe it asynchronously over an MQTT event channel. Supported roles include orchestrator, worker, and reviewer." -version: 3.0.1 +version: 3.1.0 author: godopu license: MIT platforms: [linux, macos, windows] diff --git a/.agents/skills/multi-agent-mux-loop/SKILL.md b/.agents/skills/multi-agent-mux-loop/SKILL.md index b1f96dc..0ab858e 100644 --- a/.agents/skills/multi-agent-mux-loop/SKILL.md +++ b/.agents/skills/multi-agent-mux-loop/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-loop description: "Run an autonomous planning-execution-review loop using multiple agents (Planner, Creator, Reviewers) in the workspace. Automatically orchestrates plan discussion, code changes, and peer reviews until a unanimous PASS is achieved or the maximum iteration limit is reached." -version: 3.0.1 +version: 3.1.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-monitor/SKILL.md b/.agents/skills/multi-agent-mux-monitor/SKILL.md index c155d82..d77db50 100644 --- a/.agents/skills/multi-agent-mux-monitor/SKILL.md +++ b/.agents/skills/multi-agent-mux-monitor/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-monitor description: "Run a long-lived reconciler that watches .mam/agent-sessions.yaml against the actual herdr/agent runtime state and reconciles them. Use when you want live visibility into which agent sessions are running, which are dead, which have stale YAML entries, and which have new session ids that haven't been recorded yet. Runs as a persistent loop (`reconcile.sh --subscribe`) that keeps going until it times out, idles out, or is interrupted." -version: 3.0.1 +version: 3.1.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-orc-onboard/SKILL.md b/.agents/skills/multi-agent-mux-orc-onboard/SKILL.md index dbbcfb7..2cca74e 100644 --- a/.agents/skills/multi-agent-mux-orc-onboard/SKILL.md +++ b/.agents/skills/multi-agent-mux-orc-onboard/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-orc-onboard -description: Register current or specified orchestrator session UUID into agent-sessions.yaml orchestrator_uuids list to prevent sub-agent discovery capture. -version: 3.0.1 +description: "Register current or specified orchestrator session UUID into agent-sessions.yaml orchestrator_uuids list to prevent sub-agent discovery capture." +version: 3.1.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-resume/SKILL.md b/.agents/skills/multi-agent-mux-resume/SKILL.md index 34a5dd7..b0877c2 100644 --- a/.agents/skills/multi-agent-mux-resume/SKILL.md +++ b/.agents/skills/multi-agent-mux-resume/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-resume description: "Resume an existing agent (claude, antigravity/agy) conversation by UUID into a herdr session. Reads .mam/agent-sessions.yaml for the saved session/conversation id, spawns (or reuses) a herdr session of the matching name, and runs `claude -r ` or `agy --conversation ` inside. Use when you want to reattach to a previous session's context, or revive a session whose herdr died but the agent's conversation is still on disk." -version: 3.0.1 +version: 3.1.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-status/SKILL.md b/.agents/skills/multi-agent-mux-status/SKILL.md index d2dbfb9..4a5c542 100644 --- a/.agents/skills/multi-agent-mux-status/SKILL.md +++ b/.agents/skills/multi-agent-mux-status/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-status description: "Read-only instant snapshot of all agent herdr sessions โ€” name, YAML status, herdr alive, pane cmd/cwd, resume UUID on disk, and any drift. No mutation. Reuses reconcile.sh --dry-run for the diff logic. Use when you want to know 'what's running RIGHT NOW' without spinning up the monitor loop." -version: 3.0.1 +version: 3.1.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.agents/skills/multi-agent-mux-stop/SKILL.md b/.agents/skills/multi-agent-mux-stop/SKILL.md index 0ce7f1b..10f2681 100644 --- a/.agents/skills/multi-agent-mux-stop/SKILL.md +++ b/.agents/skills/multi-agent-mux-stop/SKILL.md @@ -1,7 +1,7 @@ --- name: multi-agent-mux-stop description: "Stop an agent herdr session (claude, antigravity/agy) and update .mam/agent-sessions.yaml. Default stops gracefully and marks status=stopped with conversation preserved for resume. Does NOT delete on-disk conversation artifacts (jsonl/db) โ€” those are preserved unless --purge-conversation is passed. Use when ending a work session, switching to a different one, or cleaning up before a fresh start." -version: 3.0.1 +version: 3.1.0 author: godopu license: MIT platforms: [linux, macos] diff --git a/.mam.env.example b/.mam.env.example index 2381900..8874587 100644 --- a/.mam.env.example +++ b/.mam.env.example @@ -158,6 +158,19 @@ #default: 2 # MAM_MAX_PANE_ROWS=2 +# Kill the session instead of preserving it when TUI readiness times out. +#default: 0 +# MAM_KILL_ON_READY_TIMEOUT=0 + +# Number of trailing non-blank pane lines scanned for blocking modal signatures. +#default: 8 +# MAM_DIALOG_TAIL_LINES=8 + +# Consecutive empty captures before wait_for_tui_ready declares the pane unobservable. +# NOTE: distinct from SKS_EMPTY_GIVEUP, which governs send_keys_safe quiescence (default 3). +#default: 5 +# MAM_READY_EMPTY_GIVEUP=5 + # ============================================================================== # deploy / distribution source (for forks/mirrors) # ============================================================================== diff --git a/VERSIONS.md b/VERSIONS.md index eb9cb29..d501e03 100644 --- a/VERSIONS.md +++ b/VERSIONS.md @@ -6,37 +6,91 @@ ## ๐Ÿ“Œ ํ˜„์žฌ ๋ฒ„์ „ ๊ฐœ์š” (Current Release) -- **ํ”„๋ ˆ์ž„์›Œํฌ ๋ฒ„์ „**: `v3.0.1` -- **์ตœ์‹  ๋ฆด๋ฆฌ์Šค ์ผ์‹œ**: 2026-08-27 (KST) +- **ํ”„๋ ˆ์ž„์›Œํฌ ๋ฒ„์ „**: `v3.1.0` +- **์ตœ์‹  ๋ฆด๋ฆฌ์Šค ์ผ์‹œ**: 2026-08-28 (KST) - **๊ธฐ์ค€ ๋ธŒ๋žœ์น˜**: `main` - **ํ•ต์‹ฌ ์•„ํ‚คํ…์ฒ˜**: - - **Herdr Shim Routing & Multi-Workspace Isolation Engine**: `HERDR_WORKSPACE_ID` ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์Šค์ฝ”ํ•‘ ๋ฐ `$WORKSPACE_ROOT/.mam/herdr_workspace_id` ์˜์†ํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ํ†ตํ•ด ๋‹ค์ค‘ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋™์‹œ ์‹คํ–‰ ์‹œ ํŽ˜์ธ ๊ต์ฐจ ์˜ค์—ผ ์›์ฒœ ์ฐจ๋‹จ - - **Atomic Safe Paste Insertion (`pane send-text`)**: ๋ฏธ์ง€์› ์„œ๋ธŒ์ปค๋งจ๋“œ(`agent send`) ์ œ๊ฑฐ ๋ฐ `pane send-text` ๋‹จ์ผ ์‚ฝ์ž… ๊ณ„์•ฝ ๋„์ž…, `send_keys_safe` ์‹คํŒจ ์ „ํŒŒ(`rc 3`) ๋ฐ ์ค‘๋ณต ์ œ์ถœ(Double Submit) ๋ฐฉ์ง€ - - **Strict Exact-Match Single Pane Resolver**: ๋ถ€๋ถ„ ๋ฌธ์ž์—ด ๋งค์นญ(`in tn`) ์™„์ „ ์ œ๊ฑฐ, ๋‹จ์ผ ์ค‘์•™ `_resolve_herdr_pane_id` ํ—ฌํผ ๊ธฐ๋ฐ˜ 5๋Œ€ ์„œ๋ธŒ์ปค๋งจ๋“œ(`has-session`, `kill-session`, `capture-pane`, `send-keys`, `paste-buffer`) ๋ฌด๊ฒฐ์„ฑ ์ •๋ฆฝ - - **TUI Readiness & Dialog Token Precision**: `_MAM_DIALOG_TOKENS` ์ผ๋ฐ˜ ํŒ(`Yes, try it`) ์˜คํƒ ์ œ๊ฑฐ ๋ฐ ํ’€์Šคํฌ๋ฆฐ ๋ชจ๋‹ฌ Escape ๊ฑฐ์ ˆ ๋ธŒ๋žœ์น˜ ๋ถ„๋ฆฌ - - **Comprehensive Test Suite Milestone**: 412๊ฐœ ์ „์ฒด ํ…Œ์ŠคํŠธ 100% PASS (412 passed / 0 failed). + - **2-Tier TUI Readiness Model (`S โˆจ (W โˆง C)`) & Modal Priority**: ๊ฐ•ํ•œ ํ† ํฐ(`S`) ๋‹จ๋… ์ถฉ์กฑ ๋ฐ ์•ฝํ•œ ํ† ํฐ(`W`) + ๋ณด๊ฐ• ํŒจํ„ด(`C`) ๊ฒฐํ•ฉ ํŒ์ •์‹ ๋„์ž…, ๋ชจ๋‹ฌ ์„ ํ–‰ ๊ฒ€์‚ฌ ๋ฐ ํžŒํŠธ ๋ถ„๋ฆฌ๋ฅผ ํ†ตํ•œ ๋‹ค์ด์–ผ๋กœ๊ทธ ๊ธฐ์•„(starvation) ์›์ฒœ ํ•ด์†Œ + - **Adapter Modal Contract (`T-2d`)**: `BaseAgentAdapter` ๋‚ด `modal_tokens` ํ”„๋กœํผํ‹ฐ ์‹ ์„ค ๋ฐ Cline/Claude ๋งž์ถคํ˜• ๋ชจ๋‹ฌ ์‹œ๊ทธ๋‹ˆ์ฒ˜ ํŒฉํŠธ ๋ธŒ๋ฆฌ์ง€ ํ†ตํ•ฉ + - **Fail-Closed Exact Pane Resolver & Multi-Workspace Isolation Engine**: `_resolve_herdr_pane_id` ๋™์ข… ํŽ˜์ธ ๋‹ค์ค‘ ๋งค์น˜ ์‹œ fail-closed(`exit 1`) ์ฐจ๋‹จ, `HERDR_WORKSPACE_ID` ์Šค์ฝ”ํ•‘ ๋ฐ `$WORKSPACE_ROOT/.mam/herdr_workspace_id` ์˜์†ํ™” + - **Atomic Safe Paste Insertion & Preserved Diagnostic Dumps**: `pane send-text` ๋‹จ์ผ ์•ˆ์ „ ์‚ฝ์ž… ๊ณ„์•ฝ, TUI ๋ฏธ์ค€๋น„ ์‹œ ์„ธ์…˜ ๋ณด์กด(`exit 0`) ๋ฐ `.mam/diagnostics/` ์ง„๋‹จ ๋คํ”„ ์ƒ์„ฑ + - **Runtime Framework Version Constant (`MAM_VERSION`) & 3-Way Lockstep**: `lib.sh` ๋‚ด `MAM_VERSION="3.1.0"` ๋Ÿฐํƒ€์ž„ ์ง„์‹ค ๊ณต๊ธ‰์› ์ •์˜ ๋ฐ 3์ž ๋™๊ธฐํ™” ๊ฐ€๋“œ ์ฒด๊ณ„ ๊ตฌ์ถ• + - **Comprehensive Test Suite Milestone**: 436๊ฐœ ์ „์ฒด ํ…Œ์ŠคํŠธ 100% PASS (436 passed / 0 failed). --- ## ๐Ÿงญ ์Šคํ‚ฌ ํŒจํ‚ค์ง€ ๋ฒ„์ „ ๋งคํŠธ๋ฆญ์Šค (Skills Version Matrix) -๋ชจ๋“  8๊ฐœ ์Šคํ‚ฌ์€ YAML frontmatter ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(`author`, `version`, `platforms`, `environments`) ํ‘œ์ค€ํ™”๋ฅผ ํ†ตํ•ด `v3.0.1`์œผ๋กœ ๋™๊ธฐํ™”๋˜์–ด ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. +๋ชจ๋“  8๊ฐœ ์Šคํ‚ฌ์€ YAML frontmatter ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(`author`, `version`, `platforms`, `environments`) ํ‘œ์ค€ํ™”๋ฅผ ํ†ตํ•ด `v3.1.0`์œผ๋กœ ๋™๊ธฐํ™”๋˜์–ด ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. | ์Šคํ‚ฌ๋ช… | ๋ฒ„์ „ | ์—ญํ•  ๋ฐ ์ฃผ์š” ์ฑ…์ž„ | ์ƒํƒœ | | :--- | :---: | :--- | :---: | -| **`multi-agent-mux-create`** | `3.0.1` | ์—์ด์ „ํŠธ ์„ธ์…˜ ์‹ ๊ทœ ์ƒ์„ฑ ๋ฐ Herdr ์ปจํ…Œ์ด๋„ˆ ๊ฒฉ๋ฆฌ ์Šคํฐ | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-stop`** | `3.0.1` | ๋Œ€ํ™” UUID ์›์ž์  ์บก์ฒ˜ ๋ฐ ์„ธ์…˜ ์•ˆ์ „ ์ข…๋ฃŒ (Graceful Stop) | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-resume`** | `3.0.1` | ์˜จ๋””์Šคํฌ ๋Œ€ํ™” ์ปจํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ Tier-1 ์ดˆ๊ณ ์† ์„ธ์…˜ ๋ณต์› | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-status`** | `3.0.1` | ์‹ค์‹œ๊ฐ„ Herdr ์„ธ์…˜ ๋ฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋“œ๋ฆฌํ”„ํŠธ ์Šค๋ƒ…์ƒท ์กฐํšŒ | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-monitor`** | `3.0.1` | YAML โ†” ๋Ÿฐํƒ€์ž„ ์ƒํƒœ ๊ฐ„ ์ž์œจ ์กฐ์ •์ž (Reconciler Loop) | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-delegate-job`** | `3.0.1` | MQTT ์ด๋ฒคํŠธ ์ฑ„๋„ ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ๋‹จ์œ„ ์ž‘์—… ์œ„์ž„ | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-loop`** | `3.0.1` | Planner-Creator-Reviewer 3์ž ์ž์œจ ๊ณ„ํšยท์‹คํ–‰ยทํ”ผ์–ด๋ฆฌ๋ทฐ ๋ฃจํ”„ | โœ… ๋ฐฐํฌ | -| **`multi-agent-mux-orc-onboard`** | `3.0.1` | ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ UUID ๊ฒฉ๋ฆฌ ๋“ฑ๋ก ๋ฐ ์„œ๋ธŒ ์„ธ์…˜ ์˜ค์—ผ ๋ฐฉ์ง€ | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-create`** | `3.1.0` | ์—์ด์ „ํŠธ ์„ธ์…˜ ์‹ ๊ทœ ์ƒ์„ฑ ๋ฐ Herdr ์ปจํ…Œ์ด๋„ˆ ๊ฒฉ๋ฆฌ ์Šคํฐ | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-stop`** | `3.1.0` | ๋Œ€ํ™” UUID ์›์ž์  ์บก์ฒ˜ ๋ฐ ์„ธ์…˜ ์•ˆ์ „ ์ข…๋ฃŒ (Graceful Stop) | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-resume`** | `3.1.0` | ์˜จ๋””์Šคํฌ ๋Œ€ํ™” ์ปจํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ Tier-1 ์ดˆ๊ณ ์† ์„ธ์…˜ ๋ณต์› | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-status`** | `3.1.0` | ์‹ค์‹œ๊ฐ„ Herdr ์„ธ์…˜ ๋ฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋“œ๋ฆฌํ”„ํŠธ ์Šค๋ƒ…์ƒท ์กฐํšŒ | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-monitor`** | `3.1.0` | YAML โ†” ๋Ÿฐํƒ€์ž„ ์ƒํƒœ ๊ฐ„ ์ž์œจ ์กฐ์ •์ž (Reconciler Loop) | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-delegate-job`** | `3.1.0` | MQTT ์ด๋ฒคํŠธ ์ฑ„๋„ ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ๋‹จ์œ„ ์ž‘์—… ์œ„์ž„ | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-loop`** | `3.1.0` | Planner-Creator-Reviewer 3์ž ์ž์œจ ๊ณ„ํšยท์‹คํ–‰ยทํ”ผ์–ด๋ฆฌ๋ทฐ ๋ฃจํ”„ | โœ… ๋ฐฐํฌ | +| **`multi-agent-mux-orc-onboard`** | `3.1.0` | ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ UUID ๊ฒฉ๋ฆฌ ๋“ฑ๋ก ๋ฐ ์„œ๋ธŒ ์„ธ์…˜ ์˜ค์—ผ ๋ฐฉ์ง€ | โœ… ๋ฐฐํฌ | --- ## ๐Ÿ“‹ ๋ฒ„์ „๋ณ„ ์ƒ์„ธ ๋ณ€๊ฒฝ ๋‚ด์—ญ (Changelog) +### ๐Ÿš€ `v3.1.0` โ€” 2-Tier TUI Readiness Model, Adapter Modal Contract & Fail-Closed Pane Resolution (2026-08-28) + +> **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค**: ๋‹ค์ด์–ผ๋กœ๊ทธ ๊ธฐ์•„(starvation) ๊ตฌ์กฐ์  ํ•ด์†Œ๋ฅผ ์œ„ํ•œ 2๊ณ„์ธต TUI ์ค€๋น„ ํŒ์ • ๋ชจ๋ธ(`S โˆจ (W โˆง C)`), ๋ชจ๋‹ฌ/ํžŒํŠธ 2๊ณ„์ธต ํ† ํฐ ๋ถ„๋ฆฌ, ์–ด๋Œ‘ํ„ฐ๋ณ„ `modal_tokens` ๊ณ„์•ฝ(`T-2d`) ์ •๋ฆฝ, ๋™์ข… ํŽ˜์ธ ๋‹ค์ค‘ ๋งค์น˜ fail-closed ๋ฐฉ์–ด, ์ค€๋น„ ์‹คํŒจ ์„ธ์…˜ ์ง„๋‹จ ๋ณด์กด(`create_session.sh`), `lib.sh` ๋Ÿฐํƒ€์ž„ ๋ฒ„์ „ ์ƒ์ˆ˜(`MAM_VERSION="3.1.0"`) ์‹ ์„ค ๋ฐ 436๊ฐœ ์ „์ฒด ํ…Œ์ŠคํŠธ 100% PASS ๋‹ฌ์„ฑ. + +#### โš ๏ธ ๋™์ž‘ ๋ณ€๊ฒฝ ๋ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์•ˆ๋‚ด +* **B-1: TUI ๋ฏธ์ค€๋น„ ์„ธ์…˜ ๋ณด์กด (`create_session.sh`)**: + - TUI ์ค€๋น„ ๊ฐ์ง€ ํƒ€์ž„์•„์›ƒ ๋ฐœ์ƒ ์‹œ, PID๊ฐ€ ์ƒ์กดํ•ด ์žˆ๋‹ค๋ฉด ์„ธ์…˜์„ ๊ฐ•์ œ ์ข…๋ฃŒ(`kill-session`)ํ•˜์ง€ ์•Š๊ณ  `status: running`, `last_visible_status: tui-ready-timeout`์œผ๋กœ ๋“ฑ๋กํ•œ ๋’ค `exit 0`(์„ฑ๊ณต)์œผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. + - ๊ตฌ `v3.0.1`์˜ ๋ฌด์กฐ๊ฑด ๋กค๋ฐฑ ๋ฐ ์ข…๋ฃŒ ๋™์ž‘์„ ์›ํ•  ๊ฒฝ์šฐ `MAM_KILL_ON_READY_TIMEOUT=1`์„ ์„ค์ •ํ•˜์‹ญ์‹œ์˜ค. +* **B-2: ํ”„๋กœ์„ธ์Šค ์‚ฌ๋ง ์‹œ ์ข…๋ฃŒ ์ฝ”๋“œ ๋ณ€๊ฒฝ**: + - `create_session.sh` ์‹คํ–‰ ์ค‘ PID๊ฐ€ ์‚ฌ๋งํ•œ ๊ฒฝ์šฐ ๋ช…์‹œ์  ๊ตฌ๋ถ„ ์ฝ”๋“œ `42`(`MAM_EXIT_READY_TIMEOUT`)๋กœ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. +* **B-3: ๋™์ข… ํŽ˜์ธ ๋‹ค์ค‘ ๋งค์น˜ ์‹œ Fail-Closed ๊ฑฐ๋ถ€ (`_resolve_herdr_pane_id`)**: + - ๋™์ผํ•œ ์—์ด์ „ํŠธ ์ข…๋ฅ˜์˜ ํŽ˜์ธ์ด ์—ฌ๋Ÿฌ ๊ฐœ ๋ฐœ๊ฒฌ๋˜๊ณ  ์ •ํ™•ํ•œ ์„ธ์…˜๋ช… ์ผ์น˜๊ฐ€ ์‹คํŒจํ•  ๊ฒฝ์šฐ, ์ž„์˜์˜ ์ฒซ ๋ฒˆ์งธ ํŽ˜์ธ์„ ์„ ํƒํ•˜์ง€ ์•Š๊ณ  ์ฆ‰์‹œ `exit 1`๋กœ ์‹คํŒจํ•˜์—ฌ ๊ต์ฐจ ์„ธ์…˜ ์˜ค์—ผ์„ ์›์ฒœ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค. +* **B-4 & B-5: ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ „ํŒŒ (`WORKSPACE_ROOT`, `MAM_VERSION`)**: + - `WORKSPACE_ROOT` ๋ฐ `MAM_VERSION`์ด ์ž์‹ ํ”„๋กœ์„ธ์Šค๋กœ ์ž๋™ `export`๋ฉ๋‹ˆ๋‹ค. `MAM_VERSION`์€ ๋Ÿฐํƒ€์ž„ ์œ„์กฐ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์˜ค๋ฒ„๋ผ์ด๋“œ๊ฐ€ ์ฐจ๋‹จ๋ฉ๋‹ˆ๋‹ค. + +#### ๐Ÿ”ง ๊ธฐ์กด ์›Œํฌ์ŠคํŽ˜์ด์Šค ์‚ฌ์šฉ์ž ์•ˆ๋‚ด +๊ธฐ์กด ๋ฐฐํฌ๋œ ์›Œํฌ์ŠคํŽ˜์ด์Šค ์‚ฌ์šฉ์ž๋Š” `.mam.env`์— ์•„๋ž˜ 3์ข…์˜ ์‹ ๊ทœ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋™์ž‘์„ ํŠœ๋‹ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +| ๋ณ€์ˆ˜๋ช… | ๊ธฐ๋ณธ๊ฐ’ | ์„ค๋ช… | +| :--- | :---: | :--- | +| `MAM_KILL_ON_READY_TIMEOUT` | `0` | `1`๋กœ ์„ค์ • ์‹œ TUI ์ค€๋น„ ํƒ€์ž„์•„์›ƒ ๋ฐœ์ƒ ์‹œ ์„ธ์…˜์„ ๋ณด์กดํ•˜์ง€ ์•Š๊ณ  ๊ฐ•์ œ ์ข…๋ฃŒ(v3.0.1 ๋™์ž‘) | +| `MAM_DIALOG_TAIL_LINES` | `8` | ๋ธ”๋กœํ‚น ๋ชจ๋‹ฌ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๊ฒ€์‚ฌํ•  ํŽ˜์ธ ํ•˜๋‹จ ๋น„๊ณต๋ฐฑ ๋ผ์ธ ์ˆ˜ | +| `MAM_READY_EMPTY_GIVEUP` | `5` | `wait_for_tui_ready`๊ฐ€ ํŽ˜์ธ์„ ๊ด€์ธก ๋ถˆ๊ฐ€(Headless)๋กœ ํŒ์ •ํ•˜๊ธฐ๊นŒ์ง€์˜ ์—ฐ์† ๋นˆ ์บก์ฒ˜ ํšŸ์ˆ˜ | + +#### 1. 2๊ณ„์ธต TUI ์ค€๋น„ ํŒ์ • ๋ชจ๋ธ (`S โˆจ (W โˆง C)`) +* **๋‹จ๋… ์ถฉ๋ถ„ ๊ฐ•ํ•œ ์‹ ํ˜ธ (`S`)**: `strong_ready_tokens` ๋˜๋Š” 8์ž ์ด์ƒ์˜ ์œ ์˜๋ฏธํ•œ `input_placeholder`(`Ask anything...`) ์ผ์น˜ ์‹œ ์ค€๋น„ ์™„๋ฃŒ ์„ ์–ธ. +* **๋ณด๊ฐ• ๊ฒฐํ•ฉ ์•ฝํ•œ ์‹ ํ˜ธ (`W โˆง C`)**: `weak_ready_tokens`(`history`, `Chat` ๋“ฑ)๋Š” `input_prompt`(`โฏ`, `>`) ๋˜๋Š” `input_rule_pattern`(`โ”€{10,}`)๊ณผ ๋™์‹œ ์ผ์น˜ํ•  ๋•Œ๋งŒ ์ค€๋น„ ์™„๋ฃŒ๋กœ ํŒ์ •ํ•˜์—ฌ ์˜คํƒ ์ฐจ๋‹จ. +* **ํ•˜๊ฐ• ํ˜ธํ™˜์„ฑ (Fallback)**: `input_*` ์†์„ฑ์ด ๋ฏธ์ •์˜๋œ ์—์ด์ „ํŠธ(Hermes ๋“ฑ)๋Š” ๊ฐ•ํ•œ ํ† ํฐ ๋‹จ๋… ๊ฒ€์‚ฌ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ํ•˜๊ฐ•. + +#### 2. ๋ชจ๋‹ฌ/ํžŒํŠธ ํ† ํฐ ๋ถ„๋ฆฌ ๋ฐ ์–ด๋Œ‘ํ„ฐ `modal_tokens` ๊ณ„์•ฝ (`T-2a` / `T-2d`) +* **ํ† ํฐ ์ง‘ํ•ฉ ๋ถ„๋ฆฌ**: `_MAM_MODAL_TOKENS`(๊ณ ์‹ ๋ขฐ ๋ธ”๋กœํ‚น ๋ชจ๋‹ฌ)์™€ `_MAM_HINT_TOKENS`(์ž์—ฐ์–ด TUI ์•ˆ๋‚ด ํŒ)๋ฅผ ๋ถ„๋ฆฌ. `_pane_dialog_open`์ด ํžŒํŠธ ํ…์ŠคํŠธ๋กœ ์ธํ•ด ์˜๊ตฌ ์ฐจ๋‹จ(Starvation)๋˜๋Š” ๊ฒฐํ•จ ํ•ด์†Œ. `_MAM_DIALOG_TOKENS`๋Š” ํ•˜์œ„ํ˜ธํ™˜์šฉ ํ•ฉ์ง‘ํ•ฉ์œผ๋กœ ์กด์น˜. +* **`BaseAgentAdapter.modal_tokens`**: ์—์ด์ „ํŠธ๋ณ„ ํŠนํ™” ๋ชจ๋‹ฌ(Claude์˜ `Try the new fullscreen renderer?`, Cline์˜ `Select API Provider` ๋“ฑ)์„ ํŒฉํŠธ ๋ธŒ๋ฆฌ์ง€(`MAM_MODAL_TOKENS`)๋กœ ํ†ตํ•ฉ. + +#### 3. Fail-Closed ํŽ˜์ธ ํ•ด์„ ๋ฐ ์›Œํฌ์ŠคํŽ˜์ด์Šค ์Šค์ฝ”ํ”„ ๊ฐ•ํ™” (`R-1` / `R-2`) +* `_resolve_herdr_pane_id`์—์„œ ๋‹ค์ค‘ ๋งค์น˜ ์‹œ `sys.exit(1)` ์ฒ˜๋ฆฌ ๋ฐ `export WORKSPACE_ROOT` ๋ณด๊ฐ•์œผ๋กœ ๊ฒฉ๋ฆฌ ์•ˆ์ •์„ฑ ํ™•๋ณด. + +#### 4. ์ค€๋น„ ์‹คํŒจ ์„ธ์…˜ ์ง„๋‹จ ๋ณด์กด ๋ฐ `MAM_VERSION` ์Šคํƒฌํ”„ +* `create_session.sh` ์‹คํŒจ/ํƒ€์ž„์•„์›ƒ ์‹œ `.mam/diagnostics/-.txt`๋กœ ํŽ˜์ธ ๋คํ”„ ๋ฐ `MAM Version` ์Šคํƒฌํ”„ ์ž๋™ ๊ธฐ๋ก. + +#### 5. ๋Ÿฐํƒ€์ž„ ๋ฒ„์ „ ์ƒ์ˆ˜ `MAM_VERSION` ์‹ ์„ค ๋ฐ 3์ž ๋ฝ์Šคํ… ๊ฒ€์ฆ +* `lib.sh` ๋‚ด `MAM_VERSION="3.1.0"` ์„ ์–ธ ๋ฐ `test_version_consistency.py`๋ฅผ ํ†ตํ•œ `lib.sh` โ†” `VERSIONS.md` โ†” 8๊ฐœ `SKILL.md` 3์ž ์ผ์น˜ ์ž๋™ ๊ฒ€์ฆ. + +#### 6. ์‹ ๊ทœ ํ™˜๊ฒฝ๋ณ€์ˆ˜ 3์ข… ๋ฌธ์„œํ™” +* `.mam.env.example`์— `MAM_KILL_ON_READY_TIMEOUT`, `MAM_DIALOG_TAIL_LINES`, `MAM_READY_EMPTY_GIVEUP` 3์ข… ๋“ฑ๋ก. + +#### 7. ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ ํ™•์žฅ: 412 โ†’ 436 (+24, 100% PASS) +* `test_c1_tui_readiness.py` (21 tests), `test_adapter_modal_tokens_contract`, `test_version_consistency.py` ์‹ ์„ค ๋“ฑ ์ด 436๊ฐœ ํ…Œ์ŠคํŠธ ๋ฌด๊ฒฐ์  ํ†ต๊ณผ. + +#### 8. ๊ต์ฐจ ํ”ผ์–ด ๋ฆฌ๋ทฐ ์ด๋ ฅ +* F-1~F-8 ๋ฐ R-1, R-2 ์ด 10๊ฑด์˜ ํ”ผ์–ด ๋ฆฌ๋ทฐ ์ด์Šˆ ๋„์ถœ ๋ฐ ํ•ด์†Œ: + - F-1 (`Yes, try it` ํŒ ์˜คํƒ ์ œ๊ฑฐ), F-2 (์—…์…€ ๋ชจ๋‹ฌ ์„ ํ–‰ ๊ฒ€์‚ฌ), F-3 (๋ณ€์ˆ˜ ์Šค์ฝ”ํ”„ ๊ฒฉ๋ฆฌ), F-4 (๋ฆฌ๋ทฐ์–ด ์‹ค์ธก ๊ฒ€์ฆ ํ›„ ์ž์ง„ ์ฒ ํšŒ), F-5/F-6/F-7 (์–ด๋Œ‘ํ„ฐ ํŒฉํŠธ ์—ฐ๋™ ๋ฐ ์„ธ์…˜ ํŒจํ„ด ๋ณด๊ฐ•), F-8 (๋น„-ํด๋กœ๋“œ ์–ด๋Œ‘ํ„ฐ modal_tokens ๊ณ„์•ฝ ์ •๋ฆฝ), R-1 (Fail-closed ํŽ˜์ธ ๋ฆฌ์กธ๋ฒ„), R-2 (`WORKSPACE_ROOT` export ์ „ํŒŒ). + - ๊ณ„ํš ๋‹จ๊ณ„ ์ฑŒ๋ฆฐ์ง€(`b9c29c3b`)๋ฅผ ํ†ตํ•œ C-1 (`MAM_VERSION` 3์ž ๋ฝ์Šคํ…) ๋ฐ C-2 (๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์•ˆ๋‚ด) ์ˆ˜์šฉ. + ### ๐Ÿš€ `v3.0.1` โ€” Herdr Shim Routing Contract Refactor, Multi-Workspace Isolation & 412-Test Milestone (2026-08-27) > **์ฃผ์š” ๋งˆ์ผ์Šคํ†ค**: Herdr ์‹ฌ ๋ผ์šฐํŒ… 5๋Œ€ ๊ฒฐํ•จ(ISSUE-1~5) ๋ฐ F-1~F-4 ๋ณด์™„ ์™„๊ฒฐ, `HERDR_WORKSPACE_ID` ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์Šค์ฝ”ํ•‘ ๋ฐ ํŒŒ์ผ ๊ธฐ๋ฐ˜ ๊ฒฉ๋ฆฌ ์˜์†ํ™” ์—”์ง„ ํƒ‘์žฌ, `pane send-text` ๊ธฐ๋ฐ˜ ๋‹จ์ผ ์•ˆ์ „ ์‚ฝ์ž… ๊ณ„์•ฝ ์ •๋ฆฝ, ๋ถ€๋ถ„ ๋ฌธ์ž์—ด ๋งค์นญ(`in tn`) ์™„์ „ ์ œ๊ฑฐ, 412๊ฐœ ์ „์ฒด ํ…Œ์ŠคํŠธ 100% PASS ๋‹ฌ์„ฑ. diff --git a/tests/test_version_consistency.py b/tests/test_version_consistency.py new file mode 100644 index 0000000..be22136 --- /dev/null +++ b/tests/test_version_consistency.py @@ -0,0 +1,90 @@ +# ============================================================================== +# test_version_consistency.py โ€” 3-Way Version Lockstep & Immutability Test Suite +# ============================================================================== +import os +import re +import subprocess +from pathlib import Path +import yaml + +REPO_ROOT = Path(__file__).resolve().parent.parent +LIB_SH = REPO_ROOT / ".agents" / "skills" / "lib.sh" +VERSIONS_MD = REPO_ROOT / "VERSIONS.md" +SKILLS_DIR = REPO_ROOT / ".agents" / "skills" + +SKILL_NAMES = [ + "multi-agent-mux-create", + "multi-agent-mux-stop", + "multi-agent-mux-resume", + "multi-agent-mux-status", + "multi-agent-mux-monitor", + "multi-agent-mux-delegate-job", + "multi-agent-mux-loop", + "multi-agent-mux-orc-onboard", +] + + +def _get_lib_sh_mam_version(): + cmd = f'source "{LIB_SH}" && echo "$MAM_VERSION"' + res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, cwd=str(REPO_ROOT)) + assert res.returncode == 0, f"Failed to source lib.sh: {res.stderr}" + return res.stdout.strip() + + +def _get_versions_md_versions(): + text = VERSIONS_MD.read_text(encoding="utf-8") + + # 1. Current release framework version + m_curr = re.search(r"-\s*\*\*ํ”„๋ ˆ์ž„์›Œํฌ ๋ฒ„์ „\*\*:\s*`v(\d+\.\d+\.\d+)`", text) + assert m_curr, "Failed to parse Current Release framework version in VERSIONS.md" + current_framework_ver = m_curr.group(1) + + # 2. Skill matrix table versions + table_matches = re.findall(r"\|\s*\*\*`([^`]+)`\*\*\s*\|\s*`(\d+\.\d+\.\d+)`\s*\|", text) + matrix_dict = dict(table_matches) + + return current_framework_ver, matrix_dict + + +def _get_skill_md_frontmatter_version(skill_name: str): + skill_file = SKILLS_DIR / skill_name / "SKILL.md" + assert skill_file.is_file(), f"Skill file not found: {skill_file}" + content = skill_file.read_text(encoding="utf-8") + + # Strict frontmatter parsing + fm_match = re.match(r"^---\n(.*?)\n---", content, re.DOTALL) + assert fm_match, f"No YAML frontmatter found in {skill_file}" + fm_data = yaml.safe_load(fm_match.group(1)) + assert isinstance(fm_data, dict) and "version" in fm_data, f"No version in frontmatter of {skill_file}" + return str(fm_data["version"]) + + +def test_three_way_version_lockstep(): + """T-4: lib.sh(MAM_VERSION) == VERSIONS.md(Current + Matrix 8 items) == 8x SKILL.md frontmatters.""" + # 1. lib.sh runtime constant + lib_ver = _get_lib_sh_mam_version() + assert lib_ver != "", "MAM_VERSION in lib.sh is empty" + + # 2. VERSIONS.md + vmd_curr, vmd_matrix = _get_versions_md_versions() + assert lib_ver == vmd_curr, f"lib.sh MAM_VERSION ({lib_ver}) != VERSIONS.md current ({vmd_curr})" + + for skill in SKILL_NAMES: + assert skill in vmd_matrix, f"Skill {skill} missing from VERSIONS.md matrix" + assert vmd_matrix[skill] == lib_ver, f"VERSIONS.md matrix for {skill} ({vmd_matrix[skill]}) != lib.sh ({lib_ver})" + + # 3. 8x SKILL.md files + for skill in SKILL_NAMES: + fm_ver = _get_skill_md_frontmatter_version(skill) + assert fm_ver == lib_ver, f"{skill}/SKILL.md version ({fm_ver}) != lib.sh ({lib_ver})" + + +def test_mam_version_is_not_env_overridable(): + """T-0 (Constraint 1): MAM_VERSION cannot be spoofed by caller environment.""" + cmd = f'source "{LIB_SH}" && echo "$MAM_VERSION"' + env = os.environ.copy() + env["MAM_VERSION"] = "9.9.9" + res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env=env, cwd=str(REPO_ROOT)) + assert res.returncode == 0 + assert res.stdout.strip() != "9.9.9", "MAM_VERSION was improperly overwritten by caller environment" + assert res.stdout.strip() == _get_lib_sh_mam_version()