Skip to content

feat(manager): migrate Manager runtime from copaw to qwenpaw 2.0 - #1095

Merged
shiyiyue1102 merged 94 commits into
agentscope-ai:mainfrom
LUOSENGWA:manager-qwenpaw-2.0
Aug 2, 2026
Merged

feat(manager): migrate Manager runtime from copaw to qwenpaw 2.0#1095
shiyiyue1102 merged 94 commits into
agentscope-ai:mainfrom
LUOSENGWA:manager-qwenpaw-2.0

Conversation

@LUOSENGWA

@LUOSENGWA LUOSENGWA commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Migrate the Manager container from copaw 1.0.2 to QwenPaw 2.0.1, following the same plugin-based architecture that PR #1077 established for Workers.

What changed

Manager runtime — separate venv for QwenPaw 2.0.1 to avoid agentscope version conflicts with copaw 1.0.2 (used only by bridge.py for config conversion). Manager tools (projectflow, taskflow, message, filesync) registered via a QwenPaw plugin instead of monkey-patching CoPawAgent._create_toolkit.

Matrix channel — physical overlay replaced with the agentteams-matrix-channel plugin (same as Workers). CMS observability packages installed in the QwenPaw venv (aligned with Worker).

ConfigurationQWENPAW_WORKING_DIR set alongside legacy COPAW_WORKING_DIR. Session-file privacy policy injected into prompts. approval_level=AUTO at top level of agent template. YOLO mode bridged to approval_level=OFF. Built-in QA Agent disabled. Dead PYTHONPATH entries removed.

Tool compatibilitymessage and taskflow tools read Matrix credentials directly from agent.json instead of importing copaw.config.config (not available in qwenpaw venv). Working directory resolution handles both QWENPAW_WORKING_DIR and qwenpaw.constant fallback.

CI — pgrep health check remains copaw(_worker\.run_copaw_app)? app (Manager process cmdline is unchanged).

Plugins at runtime — stored image-local and copied to working dir on startup, since the workspace is a host-mounted volume.

Stack

#1077 merged → retargeted to main ✅

Follow-up

Controller/install/Makefile runtime identifier migration (copawqwenpaw for Manager) is tracked for a separate PR — it touches Go controller code that #1077 also modifies, and should follow the same pattern (add qwenpaw alongside copaw, then deprecate).

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@LUOSENGWA
LUOSENGWA force-pushed the manager-qwenpaw-2.0 branch 2 times, most recently from 0676a69 to 9e293b2 Compare July 28, 2026 14:26
LUOSENGWA added a commit to LUOSENGWA/HiClaw that referenced this pull request Jul 28, 2026
After PR agentscope-ai#1095 the Manager runs QwenPaw 2.0, not copaw. The script
name should reflect reality.

start-manager-agent.sh now accepts both 'copaw' (legacy runtime
identifier used by controller/installer/CI) and 'qwenpaw' (forward-
looking name). Both map to the same start-qwenpaw-manager.sh.

Controller, installer, CI, Makefile, and Docker image name still use
'copaw' as the runtime identifier — changing those is a separate PR
(the controller already defines RuntimeQwenPaw='qwenpaw' for Workers;
adding a qwenpaw Manager runtime requires new config fields, image
selection logic, and CI matrix entries).
shiyiyue1102 and others added 26 commits July 30, 2026 19:18
Change-Id: I9ad741de07f4b7bfbbe3721030df8749546a0def
Change-Id: I7d7930c2391217d2c1887eb4d7e20449b4b16ce8
Change-Id: I3073b27f43f0ecde4e6fa2164fd49d2659d50dcf
Change-Id: I31687da14f2d40bfb50dade36a8d7467e9aa8ddd
Change-Id: Ieeed7e69bf3b23e3f03aaa5168e4fe807040c1fe
Change-Id: I9b2094e39e7dfc12437aedf347199ace79de1f5b
Change-Id: I04a62daf94efad6d0445dce93a56cf897530c25e
Change-Id: I7af2aece15ab2c733754beee59e31d377ca1d810
Change-Id: I55ac99d6d22041bc7e0d229449fd0333f6ca8a29
Change-Id: I961ed24fb022cbe8ac01fab81dfe018a00c14a5d
Change-Id: Ic437642f20426d03f585442fb06867ede35e6151
Change-Id: I352dc6fa16715f0ce03ce00931e61a0abadc4efc
Change-Id: I03e49cd512911c176c9a8cb3d407877fdf8b2363
Change-Id: I50577926a078ed50232e3eb6af6583d1162be7df
Change-Id: I88060c8e6c49862205ec602de2ae3e72a4f2c879
shiyiyue1102 requested end-to-end validation of the legacy CoPaw →
QwenPaw migration (secrets, sessions, plugins, workspace) so existing
installs of HiClaw/AgentTeams with CoPaw runtime upgrade automatically
without data loss.

1. Extract §10b migration logic from start-qwenpaw-manager.sh into a
   standalone manager/scripts/init/migrate-copaw-state.sh so CI can run
   the REAL production migration path directly. Script is:
   - copy-then-verify: writes .copaw-migrated only after every critical
     artifact (incl. .copaw.secret master_key/providers.json/envs.json)
     is verified in the target
   - retry-safe: partial copy returns exit 1, marker NOT written,
     retried on next startup
   - idempotent: marker present → skip
   - environment-overridable (HOME/QWENPAW_WORKING_DIR/QWENPAW_SECRET_DIR/
     WORKSPACE_DIR) so tests run in an isolated throwaway HOME
   - fixes standalone-run bug: mkdir target root before copying

2. New tests/test-28-migration-e2e.sh runs against the real script in a
   throwaway HOME (/tmp/mig-e2e) inside the Manager container (never
   touches live state). Covers: full secret migration with content
   equality (14 artifacts), marker only after full verification,
   idempotency (no clobber), partial-copy failure → no marker → retry
   succeeds with data intact. Explicitly overrides QWENPAW_WORKING_DIR/
   QWENPAW_SECRET_DIR/WORKSPACE_DIR because the Manager image exports
   QWENPAW_WORKING_DIR.

3. Register test-28 in SHARD_C_TESTS + NON_GITHUB_TESTS.

Locally verified (mock env): success path, idempotency, failure retry
all pass. Live container E2E runs in CI.
…low, sync)

Merge upstream/main (2ea0274, ba161a8, 00a5d20) into
manager-qwenpaw-2.0. Three conflicts resolved:

1. copaw/src/copaw_worker/hooks/tools/taskflow.py
   - Ours (PR agentscope-ai#1095): delegate_task auto-notifies the Worker via
     _notify_task_assignment with stable txn_id, prepared->assigned
     state machine, room ID normalization.
   - Theirs (upstream agentscope-ai#1120): manual handoff - delegate returns
     notificationRequired + nextAction for the Leader to call message.
   - Resolution: keep ours (auto-notify). shiyiyue1102 explicitly
     requires automatic send; manual handoff would produce duplicate
     assignments. Updated test_taskflow_tool.py assertion accordingly.

2. copaw/src/copaw_worker/sync.py
   - Ours: .copaw -> .qwenpaw path migration + QWENPAW_WORKING_DIR.
   - Theirs: runtime/runtime.yaml sync (new).
   - Resolution: merge both - keep .qwenpaw paths AND add
     runtime/runtime.yaml to push_files + _EXCLUDE_PATHS.

3. changelog/current.md
   - Ours: QwenPaw 2.0 runtime unification entry.
   - Theirs: custom model capability overrides entry + bug fixes.
   - Resolution: keep both What's New entries; bug fixes auto-merged.

Auto-merged cleanly: bridge.py (our agents.profiles nesting + upstream
multimodal env override), matrix_channel.py, matrix/channel.py,
message_filter.py, cli.py, docs, installer.

Tests: test_taskflow_tool 36 passed, test_notify_task_assignment passed,
qwenpaw plugin + trace passed. Full pytest failures are pre-existing
(agt CLI env deps) or test-order pollution (each passes standalone);
CI runs bash integration tests (SHARD_C now includes test-28).
@shiyiyue1102

Copy link
Copy Markdown
Collaborator

Thanks for the update. I rechecked the exact current head (117ab8cb) against the latest main. The room: normalization and edited-event m.mentions fixes are now covered by focused tests, but I still found three blocking issues:

  1. The migration marker can be written even when legacy data was not migrated. start-qwenpaw-manager.sh creates/bridges QwenPaw state before invoking the migration. migrate-copaw-state.sh then uses cp -an, but verifies only that the destination exists (or that a directory is non-empty), not that each legacy artifact was copied with the same content. I reproduced this with legacy master_key=legacy-key and a pre-existing target master_key=preexisting-key: the script returned success and wrote .copaw-migrated, while the target remained preexisting-key. This can permanently skip legacy credentials/config on subsequent boots. Please either migrate before initializing the QwenPaw target, or define an explicit conflict policy and verify every source artifact before writing the marker. The E2E should include pre-existing target files/directories.

  2. The Leader instructions still contradict the new auto-notification contract. manager/agent/team-leader-agent/skills/task-management/SKILL.md first says not to send a second assignment, but its “After delegation” section still requires calling message to @mention the Worker. skills/communication/SKILL.md likewise says auto-notification is sufficient, then later says the cross-room message call is mandatory and describes the first assignment as being sent with message. The Leader can therefore still emit a second assignment and trigger the Worker twice. Please remove all remaining manual assignment-send instructions/examples and keep only requester-status messaging after a successful delegate_task.

  3. The new migration failure/retry E2E does not reach its assertions. In tests/test-28-migration-e2e.sh, the inner shell enables set -e and then intentionally runs the migration expecting a non-zero exit. The shell exits immediately at that command, before RC1=$?, the marker assertion, or the retry. Running the real test in a disposable QwenPaw Manager container produced 21 passes and 1 failure at Failure Retry Semantics. Please capture the expected failure through an if/else (or temporarily disable errexit) so the no-marker and successful-retry checks actually execute.

The current PR head also conflicts with the latest main in changelog/current.md, copaw/src/copaw_worker/hooks/tools/taskflow.py, and copaw/src/copaw_worker/sync.py, and there are currently no GitHub checks on the head.


感谢更新。我基于当前精确 head(117ab8cb)和最新 main 重新复核了。room: 归一化以及编辑事件的 m.mentions 已有聚焦测试覆盖,但目前仍有三个阻塞问题:

  1. 旧数据没有真正迁移时,脚本仍可能写入迁移成功 marker。 start-qwenpaw-manager.sh 会先创建并 bridge QwenPaw 状态,之后才执行迁移;migrate-copaw-state.sh 使用 cp -an,但只检查目标是否存在或目录是否非空,并没有逐项确认旧数据已按内容复制。我用旧 master_key=legacy-key、目标中预先存在 master_key=preexisting-key 复现:脚本返回成功并写入 .copaw-migrated,但目标内容仍是 preexisting-key。后续启动会永久跳过旧凭据/配置迁移。建议在初始化 QwenPaw 目标之前迁移,或者明确冲突策略,并在写 marker 前逐项验证所有源文件;E2E 也应覆盖目标已有同名文件/目录的场景。

  2. Leader 指令仍与自动通知契约互相冲突。 manager/agent/team-leader-agent/skills/task-management/SKILL.md 前面禁止二次发送派单消息,但 “After delegation” 仍要求调用 message @workerskills/communication/SKILL.md 也先说明自动通知已经完成派单,后面却又要求跨房间 message 必须执行,并把首条派单描述为由 message 发送。Leader 因此仍可能重复派单并触发 Worker 两次。请删除所有残留的手工派单发送指令/示例,成功调用 delegate_task 后只保留向请求方汇报状态的消息。

  3. 新增迁移失败/重试 E2E 实际不会执行后续断言。 tests/test-28-migration-e2e.sh 的内层 shell 开启了 set -e,随后故意执行一个预期非零的迁移命令;shell 会在该命令处直接退出,执行不到 RC1=$?、marker 检查和重试。本地一次性 QwenPaw Manager 容器实跑结果为正常项 21 个通过、Failure Retry Semantics 1 个失败。请用 if/else 捕获预期失败(或临时关闭 errexit),确保“不写 marker”和“修复后重试成功”都真正执行并被验证。

此外,当前 head 与最新 mainchangelog/current.mdcopaw/src/copaw_worker/hooks/tools/taskflow.pycopaw/src/copaw_worker/sync.py 仍有冲突,并且当前 head 没有 GitHub checks。

…hangelog

Deep audit of the shiyiyue1102 four-blocker fix + migration E2E found
and fixed 3 issues:

1. Matrix txn_id spec compliance: copaw_worker and MCP delegate_task used
   'delegate:{task_id}' but Matrix txnId only allows [0-9a-zA-Z.=_-].
   Unified to 'delegate-{task_id}' in both runtimes. nio does not
   validate, but servers may reject non-spec characters.

2. team-leader-agent/skills/task-management/SKILL.md still taught the
   OLD manual handoff ('call message to @mention the assigned Worker')
   in its After-delegation step list, contradicting the auto-notification
   contract stated in the same file (duplicate assignment risk). Replaced
   with 'do NOT call message' guidance.

3. changelog/current.md agentscope-ai#1120 entry still described the manual
   'Return the required Team Room message action' behavior; updated to
   describe the final auto-notification behavior (atomic state, stable
   txn_id) with agentscope-ai#1095 attribution.

Audit verification:
- txn_id fix: test_taskflow_tool + test_notify_task_assignment 38 passed
- qwenpaw plugin + trace 50 passed
- test_channel_mention 7 failures = pre-existing upstream issue (identical
  on upstream/main 2ea0274, zero new regressions from this merge)
- test_sync 3 failures = pre-existing agt CLI env dependency (identical
  before and after merge)
…prompt residuals, E2E errexit

Three blocking issues from maintainer recheck:

1. Migration marker could be written when legacy data was NOT migrated.
   migrate-copaw-state.sh used cp -an (no-clobber) and only verified
   target existence, so a pre-existing target file (e.g. master_key)
   silently skipped the legacy value while .copaw-migrated was still
   written - permanently skipping legacy credentials on later boots.
   Fix: explicit conflict policy - legacy is authoritative on upgrade,
   so use cp -a (overwrite) for state/secret/workspace artifacts and
   content-verify every copied file with cmp before writing the marker.

2. Leader instructions still contradicted the auto-notification contract.
   communication/SKILL.md still said the first Team Room assignment is
   'sent with message target=room:<Team Room ID>' and that the
   cross-room message call is mandatory, with a manual assignment JSON
   example. Fix: replaced with delegate_task auto-notification wording;
   cross-room message example is now requester-status only. Same Room
   example no longer shows a manual New task assignment.

3. test-28 failure/retry E2E never reached its assertions. The inner
   shell enabled set -e then ran the intentionally-failing migration;
   the shell exited there before RC1=$?, the marker assertion, or the
   retry. Fix: capture the expected failure with if/else so no-marker
   and successful-retry checks actually execute. Also added a
   pre-existing-target conflict E2E section (legacy must overwrite).

Verified locally: conflict overwrite (legacy-key wins), idempotency
(no clobber on re-run), failure retry (no marker on partial copy,
retry succeeds with data intact).
@LUOSENGWA

Copy link
Copy Markdown
Contributor Author

@shiyiyue1102 Thanks for the detailed recheck. The PR head is now 22b8e1e1 (was 117ab8cb when you reviewed). All three blocking issues plus the conflict/checks note are addressed:

感谢详细复核。PR head 已更新到 22b8e1e1(您复核时是 117ab8cb)。三个阻塞问题以及冲突/checks 说明均已处理:


1. Migration marker written without legacy data migrated — fixed with an explicit conflict policy + content verification

The root cause was exactly as you described: cp -an (no-clobber) silently skipped a pre-existing target file, and the script only verified target existence, not content, so a legacy master_key=legacy-key next to a target master_key=preexisting-key returned success, wrote .copaw-migrated, and permanently skipped legacy credentials on later boots.

Fix (migrate-copaw-state.sh):

  • Explicit conflict policy: the legacy location is authoritative on upgrade, so state/secret/workspace artifacts now overwrite any pre-existing target (cp -a, not cp -an).
  • Content verification: every copied file is verified with cmp -s against the source before the marker is written; directories are walked entry-by-entry (missing or content-mismatched files set _migration_failed and suppress the marker).
  • The E2E now includes a pre-existing target conflict section: legacy master_key/providers.json must overwrite pre-existing target values, and the marker is written only after the overwrite is verified.

1. 迁移 marker 在旧数据未真正迁移时仍被写入 — 已通过明确的冲突策略 + 内容校验修复

根因与您描述完全一致:cp -an(no-clobber)在目标已存在同名文件时静默跳过复制,而脚本只校验目标"存在"、不校验"内容一致",导致 legacy master_key=legacy-key 与目标 master_key=preexisting-key 并存时返回成功并写入 .copaw-migrated,后续启动永久跳过旧凭据迁移。

修复(migrate-copaw-state.sh):

  • 明确的冲突策略:升级场景下 legacy 位置是权威数据源,state/secret/workspace 工件现在覆盖已存在的目标文件(cp -a,不再用 cp -an)。
  • 内容校验:写入 marker 前,每个复制文件都用 cmp -s 与源做内容比对;目录逐条目遍历(缺失或内容不一致的文件会置 _migration_failed 并抑制 marker)。
  • E2E 新增目标已存在同名文件冲突场景:legacy master_key/providers.json 必须覆盖已存在的目标值,且只有在覆盖校验通过后才写 marker。

2. Leader instructions still contradicting auto-notification — all remaining manual assignment-send instructions removed

You were right that the previous pass missed residuals. Fixed:

  • communication/SKILL.md: the Requester Reports section no longer says the first Team Room assignment is "sent with message target=room:<Team Room ID>" — it is now "delivered by the taskflow(delegate_task) auto-notification". The Cross-Room section no longer says the cross-room message call is "mandatory" for Team work; its JSON example is now requester-status only. The Same Room example no longer shows a manual New task [...] assignment.
  • task-management/SKILL.md: the "After delegation" step list (which previously said "call message to @mention the assigned Worker") now says the delegate_task auto-notification already @mentions the Worker and explicitly forbids a second message call.

I also scanned the full team-leader-agent prompt set for any remaining New task [...] / message target=room manual-assignment wording — none remain in the Leader path.

2. Leader 指令仍与自动通知契约冲突 — 已清除全部残留的手工派单指令/示例

您说得对,上一轮漏掉了残留。已修复:

  • communication/SKILL.md:Requester Reports 段不再说首条 Team Room 派单是 "sent with message target=room:<Team Room ID>",改为"由 taskflow(delegate_task) 自动通知投递";Cross-Room 段不再说跨房间 message 调用对 Team work 是"mandatory",其 JSON 示例改为仅请求方状态更新;Same Room 示例不再展示手工 New task [...] 派单。
  • task-management/SKILL.md:"After delegation" 步骤清单(原先要求 "call message to @mention the assigned Worker")现在说明 delegate_task 自动通知已 @mention Worker,并明确禁止再次调用 message

同时全量扫描了 team-leader-agent 全部 prompts,Leader 路径已无任何 New task [...] / message target=room 手工派单措辞残留。


3. test-28 failure/retry E2E never reached its assertions — fixed

Exactly as you found: the inner shell enabled set -e and then ran the intentionally-failing migration, so the shell exited before RC1=$?, the marker assertion, and the retry. Running the real test produced 21 passes + 1 failure at Failure Retry Semantics.

Fix (test-28-migration-e2e.sh): the expected failure is now captured with if/else so the no-marker assertion and the successful-retry check actually execute. The test also gained a pre-existing-target conflict section (see #1). I re-ran the equivalent flow locally: conflict overwrite (legacy wins), no-marker-on-partial-copy, and retry-succeeds-with-data-intact all pass.

3. test-28 失败/重试 E2E 实际没有执行到断言 — 已修复

与您发现的一致:内层 shell 开启了 set -e,随后故意执行预期失败的迁移命令,shell 在该命令处直接退出,RC1=$?、marker 断言和重试都不会执行。实跑结果为 21 通过 + Failure Retry Semantics 1 失败。

修复(test-28-migration-e2e.sh):用 if/else 捕获预期失败,使"不写 marker"断言和"修复后重试成功"检查真正执行;同时新增目标已存在冲突场景(见 #1)。本地重跑等价流程:冲突覆盖(legacy 胜出)、部分复制不写 marker、重试数据完整——全部通过。


4. Merge conflicts + missing checks — resolved

The head you reviewed (117ab8cb) was before the merge. We have since merged the latest main (2ea02740, ba161a85, 00a5d20c) into manager-qwenpaw-2.0 (ebfc1d7f), resolving the conflicts in changelog/current.md (both What's New entries kept), taskflow.py (kept the auto-notification implementation; the upstream manual-handoff path was superseded by the automatic send, per your earlier feedback), and sync.py (kept our .qwenpaw migration paths AND the upstream runtime/runtime.yaml sync). The branch is now a clean fast-forward off main; CI checks are running on 22b8e1e1.

4. Merge 冲突 + 缺少 checks — 已解决

您复核的 head(117ab8cb)在 merge 之前。我们已将最新 main2ea02740ba161a8500a5d20c)合入 manager-qwenpaw-2.0ebfc1d7f),解决了 changelog/current.md(两个 What's New 条目都保留)、taskflow.py(保留自动通知实现;按您此前的反馈,上游手动 handoff 路径已被自动发送取代)、sync.py(保留我们的 .qwenpaw 迁移路径 + 上游 runtime/runtime.yaml 同步)的冲突。分支现在基于 main 干净 fast-forward;CI checks 正在 22b8e1e1 上运行。

@shiyiyue1102

Copy link
Copy Markdown
Collaborator

Thanks for the update. I rechecked the exact current head (22b8e1e1). The three blockers from my previous comment are addressed, but I found one new migration-order blocker that affects the core upgrade contract.

start-qwenpaw-manager.sh currently configures the QwenPaw Matrix bridge, injects the default/QA profiles, and installs current plugins before running migrate-copaw-state.sh. The migration then treats the entire legacy tree as authoritative and overwrites QwenPaw config.json, .qwenpaw.secret/providers.json, and workspace files such as agent.json with cp -a. That can undo Controller-owned values just written from the current openclaw.json, including the current Matrix access token/user ID, and can also undo the QA-disable configuration.

I reproduced this following the production startup order:

before_migration token=NEW_TOKEN qa_disabled=false
after_migration token=OLD_TOKEN legacy_agent=true qa_disabled=missing legacy_config=true

So the standalone migration E2E passes, but an upgraded Manager may still start with a stale Matrix token and fail to reply or collaborate with the Team. Please either run legacy user-data migration before bridge/profile/plugin initialization and then let the current startup flow re-overlay Controller-owned values, or migrate/merge only explicitly user-owned artifacts.

Please also add a production-startup-order upgrade E2E that verifies all of the following together:

  • QwenPaw starts and can reply through Matrix;
  • Manager -> Leader -> Worker Team collaboration works;
  • the current Matrix access token/user ID from openclaw.json are preserved;
  • the built-in QA profile remains disabled and current plugins/builtins remain current;
  • legacy CoPaw master key, provider/env configuration, sessions, memory, and workspace data migrate successfully;
  • a partial migration does not write the marker and can be retried safely.

There is also one current CI blocker: helm-lint fails the AgentTeams rename-contract check. The README hits are inherited, but the two comments added by this PR in manager/scripts/init/migrate-copaw-state.sh:6 and tests/test-28-migration-e2e.sh:5 contain HiClaw/AgentTeams and are PR-introduced hits.


感谢更新。我重新复核了当前精确 head(22b8e1e1)。上一条评论中的三个阻塞问题已经处理,但又发现了一个会影响核心升级契约的迁移顺序问题。

start-qwenpaw-manager.sh 当前会先配置 QwenPaw Matrix bridge、注入 default/QA profiles 并安装当前插件,之后才运行 migrate-copaw-state.sh。迁移脚本随后把整个 legacy 目录视为权威数据源,通过 cp -a 覆盖 QwenPaw 的 config.json.qwenpaw.secret/providers.json,以及 workspace 中的 agent.json 等文件。这会覆盖刚刚根据当前 openclaw.json 写入的 Controller 管理字段,包括当前 Matrix access token/user ID,也会撤销 QA Agent 的禁用配置。

按生产启动顺序复现结果如下:

before_migration token=NEW_TOKEN qa_disabled=false
after_migration token=OLD_TOKEN legacy_agent=true qa_disabled=missing legacy_config=true

因此,虽然独立迁移 E2E 已通过,但升级后的 Manager 仍可能使用旧 Matrix token 启动,导致不能回复或无法完成 Team 协作。建议把 legacy 用户数据迁移放到 bridge/profile/plugin 初始化之前,再由当前启动流程覆盖 Controller 管理字段;或者只迁移/合并明确属于用户的数据,避免整体覆盖当前配置。

还请补充一个覆盖真实生产启动顺序的升级 E2E,同时验证:

  • QwenPaw 可以正常启动并通过 Matrix 回复;
  • Manager -> Leader -> Worker 的 Team 协作正常;
  • 当前 openclaw.json 中的 Matrix access token/user ID 不被旧数据覆盖;
  • 内置 QA profile 保持禁用,当前 plugins/builtins 保持最新;
  • 旧 CoPaw 的 master key、provider/env 配置、sessions、memory 和 workspace 数据均能正常迁移;
  • 部分迁移失败时不写 marker,并且可以安全重试。

此外当前还有一个 CI 阻塞:helm-lint 的 AgentTeams rename-contract 检查失败。README 命中来自基线,但本 PR 新增的 manager/scripts/init/migrate-copaw-state.sh:6tests/test-28-migration-e2e.sh:5 两处注释包含 HiClaw/AgentTeams,属于本 PR 引入的命中。

LUOSENGWA and others added 8 commits August 1, 2026 23:18
shiyiyue1102 third review on head 22b8e1e:

- Migration order (upgrade contract): migrate-copaw-state.sh ran AFTER
  the bridge/startup overlay steps, so legacy .copaw files (old Matrix
  token, old QA state) clobbered the Controller values just written
  from openclaw.json — upgraded Manager could boot with a stale token
  and fail to reply/collaborate. Move the migration to §1b (before
  bridge); the bridge (§2), prompt sync (§3-§4), DM patch (§6), QA
  disable (§7) and plugin install (§10) then re-overlay Controller-
  owned values while legacy user data (master_key, envs, sessions,
  memory, digest, models, custom_channels) is preserved.

- helm-lint rename-contract: drop PR-introduced 'HiClaw' from
  migrate-copaw-state.sh and test-28 comments; also clean inherited
  'hiclaw-install.sh' wording in README/README.zh-CN (upstream v1.2.0
  news) so the tree-wide brand check passes.

- test-28: add Production Startup Order E2E simulating
  mkdir -> migrate(OLD token) -> bridge(NEW token) -> QA disable ->
  plugin re-install, asserting NEW token preserved, QA disabled with
  default profile, legacy user data migrated, current plugins win,
  marker written.
…LLM CI

- install/agentteams-install.sh: only migrate copaw→qwenpaw default Worker
  runtime on upgrades (AGENTTEAMS_UPGRADE=1). Fresh installs that explicitly
  request AGENTTEAMS_DEFAULT_WORKER_RUNTIME=copaw keep it, so the copaw-copaw
  CI shard exercises the real copaw worker. Previously the unconditional
  redirect turned every no-runtime Worker into a qwenpaw Worker, breaking
  test-17/18/19/20/22/24 (MinIO-root AGENTS.md/SOUL.md/skills are only
  produced by DeployWorkerConfig, which the qwenpaw runtime path skips).
- team-leader communication SKILL.md: restore 'only for explicit cross-room'
  phrase asserted by test-21 (removed during prompt cleanup).
- tests/lib/test-helpers.sh: require_llm_key() skips when AGENTTEAMS_CI_NO_LLM=1
  — the ci-no-llm-placeholder key is non-empty but cannot reach the model, so
  test-21's LLM E2E previously ran against it and failed with
  'Unauthorized access to model: qwen3.6-plus'.
…json

The OpenClaw runtime strictly validates agents.defaults and does not
recognize supports_multimodal / supports_image (added by upstream agentscope-ai#1103).
When the Manager or a Worker runs openclaw, the generated openclaw.json
caused 'Config invalid ... Unrecognized keys' and the openclaw Manager
entered a restart loop (openclaw-openclaw CI shard failed right after
test-100). Emit the keys only for non-openclaw runtimes.

- WorkerConfigRequest gains Runtime
- GenerateOpenClawConfig skips multimodal keys when Runtime == openclaw
- worker and manager deploy call sites pass spec.runtime
The installer unconditionally redirects AGENTTEAMS_DEFAULT_WORKER_RUNTIME=copaw
to qwenpaw (copaw is the legacy name; fresh installs must default to QwenPaw
2.0). This made the copaw-copaw CI shard's implicit-runtime Workers actually
run qwenpaw, while the tests verified copaw artifacts (MinIO-root
AGENTS.md/SOUL.md/skills are only produced by DeployWorkerConfig, which the
qwenpaw runtime path skips) — test-17/18/19/20/22/24 failed.

Pin spec.runtime / manifest runtime to TEST_WORKER_RUNTIME in those tests so
each shard exercises the runtime it declares, independent of the Controller
default. test-15/21 already did this; the redirect stays unconditional so the
migration (copaw -> qwenpaw as the default worker runtime) is preserved.
@LUOSENGWA

Copy link
Copy Markdown
Contributor Author

刚才推了一版(a1a8659b),先说清楚改了什么,避免误读:

openclaw shard 的失败——根因是我们之前 #1103 把 supports_multimodal/supports_image 写进了 openclaw.json 的 agents.defaults,OpenClaw runtime 严格校验不认,Manager 直接重启循环。修复:GenerateOpenClawConfig 加 Runtime 参数,openclaw 不再写这俩键。这是生产 bug 修复。
copaw-copaw shard 的失败——根因在我们 installer:AGENTTEAMS_DEFAULT_WORKER_RUNTIME=copaw 被无条件重定向成 qwenpaw,测试建的 worker 没显式写 runtime,实际跑的是 qwenpaw,但测试按 copaw 去 MinIO 找产物,当然找不到。修复:测试里显式声明 runtime(test-15/21 本来就这么干的),installer 保持迁移语义不动。这是测试侧修正,不碰生产逻辑。
test-21——一个是 communication SKILL.md 里一句话被我们清残留时误删了(test-21 断言它),已恢复;另一个是 require_llm_key 之前只查 key 非空,placeholder key 会误跑 LLM E2E,补了 AGENTTEAMS_CI_NO_LLM=1 跳过。测试侧修正。
51f5541 恢复回来了——你 revert 之后 5 个 shard 全炸(连之前过的 hermes 也炸了),说明 placeholder 机制对 no-secret shards 是必要的(解决 identity 配置的级联问题)。恢复它 + 上面 2/3 的修复 = 副作用(test-21 误跑)也修掉了。
总结:生产代码只动了一处(openclaw 字段),其余是测试声明 runtime、恢复误删契约、修跳过逻辑、以及恢复 51f5541。没有为过测试改生产行为的 hack。

…pin test-22 worker runtime

- test-22: TEST_WORKER_RUNTIME was referenced but never defined, so
  'agt create worker --runtime ""' fell back to the Controller default
  (qwenpaw) and the delete-cleanup assertions looked for copaw artifacts
  (MinIO-root SOUL.md) that qwenpaw workers never produce. Define it like
  every other test (AGENTTEAMS_DEFAULT_WORKER_RUNTIME fallback).
- update.py: _apply_matrix_channel_access_flags could take the whole worker
  down with QwenPawApiError when access_control_dm/group did not round-trip
  on readback (channel plugin re-initializes state concurrently on some
  runtimes). The allow-list itself is applied separately via the ACL
  endpoint in _write_matrix_access_control(); log and continue instead of
  crashing so qwenpaw stays up and TeamHarness/TEAMS.md render normally.
…ger readiness pgrep

- _send_delegate_notification(): remove unused txn_id parameter — the
  Matrix transaction id is derived from task_id inside the function
  (f"delegate-{task_id}"), identical to what the caller passed. A
  dead parameter is a latent contract break: callers who customize it
  would silently see no effect.
- wait_for_manager_agent_ready(): accept both 'copaw app' (run_copaw_app.py
  via runpy, argv keeps copaw_worker.run_copaw_app) and 'qwenpaw app'
  (direct python3 -m qwenpaw app) process patterns — mirrors
  test-01-manager-boot.sh. Defensive only; current startup path still
  matches the copaw pattern.
@shiyiyue1102

Copy link
Copy Markdown
Collaborator

Thanks for the update. I rechecked the exact current head (cafce059). The visible fork checks are green, but this is still not merge-ready because the actual QwenPaw TeamHarness delegation path does not preserve the notification/state atomicity contract.

  1. In plugins/teamharness/mcp/server.py, delegate_task writes both task and project state as assigned and syncs the task directory before attempting the Matrix notification. If the send fails, the tool still returns ok: true with notification.sent=false, leaves both persisted states as assigned, and records no event_id. A focused failure injection on this head reproduced:

    {"result_ok":true,"task_status":"assigned","project_task_status":"assigned","event_id":null,"notification":{"sent":false,"error":"forced Matrix failure"}}

    This path also does not verify that the assignee is joined to the target room before sending. It therefore reintroduces the exact failure mode we were trying to eliminate: state says assigned while the Worker never received the task.

  2. The focused TeamHarness taskflow integration test currently regresses on this head:

    ruby plugins/tests/teamharness/mcp/tools/test-taskflow.rb
    AssertionError: m.file event bodies mismatch
    

    The same test passes on the local main baseline. The new assignment text events are now mixed into matrix["events"], but the existing assertion still assumes the first two events are file events. This test is not part of the visible required checks.

  3. The Worker migration contract is still ambiguous/inconsistent: the installer unconditionally rewrites the global default Worker runtime from copaw to qwenpaw, while the .copaw -> .qwenpaw directory migration is implemented in the legacy copaw_worker startup path rather than the target qwenpaw_worker path. Existing Workers with an omitted runtime can therefore switch through the changed fallback, while explicitly switching a Worker to QwenPaw is not the operation that actually owns the legacy-directory migration.

Please make the QwenPaw TeamHarness flow validate room membership and use prepare/sync -> Matrix send with stable txn_id -> commit assigned + event_id; a send failure must return a retryable failure and must not leave the task assigned. Please also add the failure/retry regression to the TeamHarness plugin tests, fix the existing event assertion, make the per-Worker migration trigger explicit and runtime-correct, and run the real Manager -> Leader -> Worker -> Leader -> Manager E2E on this exact head in a trusted environment. The current fork matrix filters out all real-LLM/TeamHarness shards, so its green status does not cover that merge gate.


感谢更新。我重新复核了当前精确 head(cafce059)。目前 fork CI 可见检查虽然全绿,但实际 QwenPaw TeamHarness 派单路径仍未满足“通知与状态原子提交”的契约,因此还不能合并。

  1. plugins/teamharness/mcp/server.pydelegate_task 会先把任务和项目节点都写成 assigned 并同步任务目录,之后才尝试发送 Matrix 通知。发送失败时,工具仍返回 ok: truenotification.sent=false,持久化状态仍是 assigned,且没有 event_id。在当前 head 上进行失败注入,稳定得到:

    {"result_ok":true,"task_status":"assigned","project_task_status":"assigned","event_id":null,"notification":{"sent":false,"error":"forced Matrix failure"}}

    该路径在发送前也没有验证 assignee 是否已经加入目标房间。因此仍可能出现“状态已派单,但 Worker 从未收到任务”的问题。

  2. 当前 head 上的 TeamHarness taskflow 聚焦集成测试发生回归:

    ruby plugins/tests/teamharness/mcp/tools/test-taskflow.rb
    AssertionError: m.file event bodies mismatch
    

    同一测试在本地 main 基线可以通过。新增派单文本事件已经混入 matrix["events"],但原断言仍假设前两个事件一定是文件事件;该测试也没有被当前可见的 required checks 覆盖。

  3. Worker 迁移契约仍不一致:安装器会无条件把全局默认 Worker runtime 从 copaw 改为 qwenpaw,但 .copaw -> .qwenpaw 目录迁移却实现在旧 copaw_worker 启动路径,而不是目标 qwenpaw_worker 路径。未显式声明 runtime 的存量 Worker 可能因 fallback 改变而切换,但显式将某个 Worker 改为 QwenPaw 并不是实际负责旧目录迁移的操作。

请将 QwenPaw TeamHarness 派单流程调整为:校验房间成员关系,prepare/sync -> 使用稳定 txn_id 发送 Matrix -> 提交 assigned + event_id;发送失败必须返回可重试错误,不能留下 assigned 状态。同时补充 TeamHarness 通知失败/重试测试,修复现有事件断言,明确且修正单 Worker 的迁移触发路径,并在可信环境对当前精确 head 跑通真实的 Manager -> Leader -> Worker -> Leader -> Manager E2E。当前 fork matrix 已过滤所有真实 LLM/TeamHarness shards,所以现有绿灯不能覆盖该合并门禁。

…rker .copaw migration on qwenpaw_worker

Address shiyiyue1102 review: the QwenPaw TeamHarness delegate_task path
did not preserve the notification/state atomicity contract.

server.py (TeamHarness MCP):
- delegate_task now validates that the assignee is a joined member of the
  target Matrix room before writing any assignment state (strict when
  Matrix env + mxid are available, skipped otherwise).
- Delegation is atomic: prepare (status=prepared, files published) ->
  send notification with stable txn id -> commit status=assigned +
  event_id only after the send succeeded. A send failure returns a
  retryable error and leaves the task prepared (never assigned without
  the Worker being notified).
- Re-delegating an already assigned task is idempotent (returns the
  recorded event_id, no duplicate notification).

qwenpaw_worker (Worker runtime):
- Migrate a legacy .copaw working dir to .qwenpaw at Worker startup so
  an explicit runtime switch to QwenPaw carries the state over. The
  migration belongs to the target (qwenpaw_worker) startup path, not
  the legacy copaw_worker path. Runs in __init__ before RuntimeUpdater
  creates .qwenpaw; idempotent when .qwenpaw already exists.

Tests:
- test-taskflow.rb: mock /members endpoint, fix m.file event assertion
  (filter by msgtype), add failure-injection (Matrix 500 -> retryable +
  prepared persisted) and idempotent re-delegation cases.
- test_trace_integration.py: simulate Matrix env + patch the automatic
  notification so the delegation commits assigned.
- test_worker_lifecycle.py: 3 migration cases (rename, skip, noop).
test-roomflow.rb's create_task_room call omitted workspaceDir, so the
server fell back to _default_workspace_dir() (the container's shared
default workspace, derived from QWENPAW_WORKING_DIR). The first run
wrote the room binding there and succeeded; every subsequent run found
the stale binding, took the existing-room branch and POSTed an invite
the mock does not serve -> HTTP 404. Pin workspaceDir to the test's
tmpdir (matching every other roomflow call) so the test is idempotent
and has no side effects on the shared default workspace.
@shiyiyue1102

Copy link
Copy Markdown
Collaborator

Clarification on the trusted real-LLM/TeamHarness E2E item in my previous comment: this is not a pre-merge blocker for this fork PR.

GitHub correctly withholds repository secrets from untrusted fork workflows. The PR should not attempt to bypass that boundary, expose the real LLM key, or replace the validation with a mock LLM. Please focus the pre-merge work on the source/test issues that can be fixed inside the PR.

After merge, the push workflow on main runs in the trusted repository context, expands the full matrix, and can use secrets.AGENTTEAMS_LLM_API_KEY. We will validate the real llm-interaction and qwenpaw-teamharness shards there, including Manager -> Leader -> Worker -> Leader -> Manager collaboration.


补充澄清我上一条评论中的可信环境真实 LLM/TeamHarness E2E:这一项不作为当前 fork PR 的合并前阻塞项

GitHub 对不可信 fork workflow 隐藏仓库 Secret 是正确的安全机制。本 PR 不需要绕过该边界、暴露真实 LLM Key,也不需要用 mock LLM 替代这项验证。合并前请集中处理能够在 PR 内修复的代码和测试问题。

合并后,main 分支的 push workflow 会在可信仓库环境中运行,展开完整测试矩阵,并可使用 secrets.AGENTTEAMS_LLM_API_KEY。届时我们再验证真实的 llm-interactionqwenpaw-teamharness,以及 Manager -> Leader -> Worker -> Leader -> Manager 完整协作链路。

@LUOSENGWA

Copy link
Copy Markdown
Contributor Author

Thanks for the clarification — agreed, the trusted real-LLM/TeamHarness E2E is a post-merge validation on main's push workflow (which can use secrets.AGENTTEAMS_LLM_API_KEY); this fork PR will not bypass the fork-secret boundary or substitute a mock LLM.

The three source/test issues are fixed on head 0e8189a:

  1. Atomic delegation (plugins/teamharness/mcp/server.py): delegate_task now validates assignee room membership first, then prepares (status=prepared, files published), sends the notification with the stable delegate-{task_id} txn id, and only commits assigned + event_id after the send succeeded. A send failure returns ok:false + retryable:true and leaves the task prepared (never assigned without the Worker being notified). Re-delegating an already-assigned task is idempotent (returns the recorded event_id, no duplicate notification).
  2. Test coverage: test-taskflow.rb now mocks /members, filters m.file events by msgtype (fixing the event-assertion regression), and adds failure-injection (Matrix 500 → retryable + prepared persisted + project node not assigned) and idempotent re-delegation cases. test_trace_integration.py simulates a Matrix env for the delegation commit path.
  3. Worker migration contract: the .copaw → .qwenpaw migration now runs on the qwenpaw_worker startup path (in init, before RuntimeUpdater creates .qwenpaw), so an explicit runtime switch to QwenPaw owns the legacy-directory migration; it is idempotent and covered by 3 unit tests.

收到澄清——真实 LLM/TeamHarness E2E 合入后在 main 的可信环境跑(可用 AGENTTEAMS_LLM_API_KEY),fork PR 不碰 secret 边界、不用 mock LLM 替代。

三个问题已修在 0e8189a

  1. delegate_task 原子化:先校验房间成员 → prepare → 稳定 txn 发通知 → 成功才 assigned+event_id;失败 retryable 且保持 prepared;重委托幂等(不重复通知)
  2. 测试:test-taskflow.rb 修了断言 + 补失败注入(500→prepared 持久化)和幂等用例;test_trace_integration 模拟 Matrix 环境
  3. Worker 迁移:.copaw→.qwenpaw 归 qwenpaw_worker 启动路径(init 最前,避开 RuntimeUpdater 抢先创建 .qwenpaw),幂等 + 3 单测

@shiyiyue1102

Copy link
Copy Markdown
Collaborator

Thanks for the update. I rechecked the exact current head (0e8189a7). The Matrix-send failure path and the event-filtering regression are fixed, and the focused TeamHarness/QwenPaw tests pass locally. However, I found three remaining source-level blockers.

  1. delegate_task still does not gate the Matrix notification on the initial file publish result. _sync_task(...) returns a boolean at plugins/teamharness/mcp/server.py:4017, but a false result is ignored and the notification is sent anyway. A focused failure injection on this head produced:

    {"ok":true,"status":"assigned","synced":false,"notification_sent":true}

    The post-commit sync at line 4079 has the same issue: it can fail while the tool still returns ok:true. Please keep the task prepared and do not send Matrix when the initial publish fails. If the assigned/eventId commit cannot be synced after a successful send, return a retryable failure and let the idempotent retry finish that sync rather than reporting success.

  2. The room-membership check is not strictly checking join. _matrix_room_member_user_ids currently includes both join and invite memberships (server.py:2267), so a Worker that has only been invited but has not joined yet passes _validate_assignee_membership. For delegation, please require membership == "join" and add an invited-but-not-joined regression case.

  3. The migration is now correctly present in the target qwenpaw_worker startup path, but the legacy copaw_worker still renames .copaw to .qwenpaw at copaw/src/copaw_worker/worker.py:137-143. Therefore, restarting an explicitly configured runtime: copaw Worker can still trigger the migration before any runtime switch. Please restore the legacy CoPaw Worker to .copaw and keep .copaw -> .qwenpaw ownership exclusively in QwenPaw startup. A regression test should prove that a CoPaw restart does not migrate, while an explicit QwenPaw switch does.

Local checks on this head:

  • test-taskflow.rb: passed
  • test-roomflow.rb: passed
  • TeamHarness trace integration: 9 passed
  • QwenPaw Worker lifecycle: 30 passed

The trusted real-LLM/TeamHarness E2E remains a post-merge main validation, not a blocker for this fork PR. The remaining pre-merge work is the source behavior above plus completion of the currently running image-build checks.


感谢更新。我重新复核了当前精确 head(0e8189a7)。Matrix 发送失败路径和事件过滤测试回归已经修复,本地 TeamHarness/QwenPaw 聚焦测试也已通过;但目前仍有三个源码级合并阻塞项。

  1. delegate_task 仍未用首次文件发布结果约束 Matrix 通知。plugins/teamharness/mcp/server.py:4017_sync_task(...) 会返回布尔值,但返回 false 时仍继续发送通知。当前 head 上的故障注入结果为:

    {"ok":true,"status":"assigned","synced":false,"notification_sent":true}

    第 4079 行提交 assigned/eventId 后的同步也存在同样问题:同步失败仍返回 ok:true。首次发布失败时应保持 prepared,且不能发送 Matrix;发送成功后若 assigned/eventId 无法同步,应返回可重试失败,由幂等重试完成同步,不能提前报告成功。

  2. 房间成员校验并没有严格检查 join_matrix_room_member_user_ids 当前会同时接受 joininviteserver.py:2267),因此仅被邀请、尚未入房的 Worker 也能通过 _validate_assignee_membership。委派场景应严格要求 membership == "join",并补充 invited-but-not-joined 回归用例。

  3. 迁移逻辑现在已经正确加入目标 qwenpaw_worker 启动路径,但旧 copaw_workercopaw/src/copaw_worker/worker.py:137-143 中仍会把 .copaw 重命名为 .qwenpaw。因此,显式保持 runtime: copaw 的存量 Worker 仅重启也会在切换运行时之前触发迁移。请恢复旧 CoPaw Worker 使用 .copaw,只让 QwenPaw 启动路径负责 .copaw -> .qwenpaw;回归测试应同时证明 CoPaw 重启不会迁移、显式切换到 QwenPaw 才会迁移。

当前 head 的本地检查结果:

  • test-taskflow.rb:通过
  • test-roomflow.rb:通过
  • TeamHarness trace integration:9 passed
  • QwenPaw Worker lifecycle:30 passed

可信环境的真实 LLM/TeamHarness E2E 继续作为合并后 main 验证,不作为当前 fork PR 的阻塞项。合并前剩余工作是修复上述源码行为,并等待当前仍在运行的镜像构建检查完成。

LUOSENGWA and others added 3 commits August 2, 2026 13:04
…bership; keep copaw worker on .copaw

Address shiyiyue1102's third-round review blockers:

1. delegate_task now gates the Matrix notification on the initial file
   publish: a failed _sync_task keeps the task prepared and never sends
   the notification (no Worker receives a reference to unpublished
   files). The assigned/eventId commit gates success too: if the
   post-commit sync fails, the tool returns a retryable failure and the
   idempotent retry (assigned + eventId) finishes the sync instead of
   reporting success with stale shared storage.

2. _matrix_room_member_user_ids accepts an optional memberships set
   (default join+invite preserved for roomflow helpers); delegation
   validation requires strictly 'join', so an invited-but-not-joined
   Worker fails with a retryable error and no notification. Regression
   covered by test-taskflow.rb (mock /members now includes an invite-only
   member).

3. copaw_worker keeps .copaw: the legacy CoPaw Worker no longer renames
   its working dir to .qwenpaw on startup. The .copaw -> .qwenpaw
   migration is owned exclusively by qwenpaw_worker, so an explicitly
   configured runtime: copaw Worker restart never migrates before a
   switch. Regression test proves a CoPaw start keeps .copaw and does
   not create .qwenpaw (qwenpaw_worker migration tests already cover the
   explicit-switch path).

changelog updated.

@shiyiyue1102 shiyiyue1102 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Rechecked exact head 855e5c4: the public installer remains gated on CoPaw, integration CI explicitly exercises the PR-built QwenPaw Manager, all required checks pass, and the CoPaw/QwenPaw/Hermes lifecycle and migration paths are covered. The trusted real-LLM Team E2E remains a post-merge main-branch validation as previously agreed.

@shiyiyue1102
shiyiyue1102 merged commit 124f06d into agentscope-ai:main Aug 2, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants