Skip to content

XERK-516 [Qwen K]: session migration + resume for qwen - #548

Merged
xerhab merged 3 commits into
mainfrom
XERK-516
Aug 29, 2026
Merged

XERK-516 [Qwen K]: session migration + resume for qwen#548
xerhab merged 3 commits into
mainfrom
XERK-516

Conversation

@xerhab

@xerhab xerhab commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

What & why

Extends XERK-101 session migration + resume to the qwen (Qwen Code) runtime — the dsh [K] (XERK-475) analogue. Parent: XERK-504.

The load-bearing difference from dsh: qwen is Claude-shaped, so its native log (~/.qwen/projects/<slug>/chats/<id>.jsonl) IS what qwen --resume <id> reloads from — there is no separate durable store like dsh's DSH_SESSIONS_ROOT. So a qwen migration carries that native log itself, not a distinct store.

Resume (process-death --resume, boot-adopt) was already wired by [Qwen B]/[Qwen C] (_launch_qwen(resume=True) + tail at native-log EOF); this ticket pins it. The net-new work is migration.

Changes (all in agent/hub-agent.py)

  • _qwen_native_log(sid) — glob-locate the native log (slug-rule-independent, like _qwen_runtime_file/the tail).
  • _qwen_store_dest(cwd, sid) — target native-log path under the target cwd's slug. Verified: qwen's cwd→slug rule is _project_slug (every non-alnum→-), not the G0 doc's imprecise /-.
  • export_session — for a qwen session, packs the native log under the reserved .qwen-store/chat.jsonl prefix, truncated to its last complete line (a live log is appended by its process). Un-droppable resumable data — export refuses cleanly over MIGRATION_BLOB_MAX rather than shipping un-resumable.
  • import_session / _unpack_transcript — routes the .qwen-store/* member to the single target file (never a dir — the shared chats/ dir holds other sessions' logs), with the same path-escape hardening as the dsh store. Dropped on a claude fallback (no qwen on target, via the existing agent_type_configured guard).
  • _reconcile_qwen_store_cwd — re-points the cwd carried on every native-log row (qwen keys on cwd — upstream issue #2373) from the source to the localized worktree; no-op on same-mount; never raises.
  • _launch_qwen — re-validates the carried model against the target host's config (kept only if QWEN_IDENT_RE-valid, else host default QWEN_MODEL); endpoint/key are always the target's.

The <slug>/<sid>/qwen/ raw-archive mirror is the display/metrics feed and is never carried by migration (the target rebuilds it) — the [K] correction the ticket names.

Verification

  • Unit tests: 2123 agent tests pass. Added 6 qwen cases to TestMigrateSession (pack/unpack round-trip + tail truncation, stray-drop on claude import, cross-mount place+rekey end-to-end, claude-fallback drop, slug-rule pin, same-mount no-op).
  • QA (qa agent): PARTIAL, zero defects, zero security findings. Everything runnable was driven:
    • Slug rule confirmed 12/12 against real on-disk ~/.qwen/projects/ logs (incl. the git/.turmagit--turma case the G0 rule would break).
    • _unpack_transcript path-escape hardening confirmed with hostile tar members (traversal/absolute → ValueError; symlink/dir → skipped; member relpath discarded so no escape).
    • _reconcile_qwen_store_cwd confirmed across 11 edge cases: rows & fields preserved, unparseable lines kept verbatim, zero raises, correct same-mount no-op.
    • Export blob-ceiling refusal confirmed; mutation testing confirmed each test catches its regression.
    • Host-proof gap (the PARTIAL, exactly as the ticket states): a real cross-host move actually resumed by qwen --resume over the placed+rekeyed log is not runnable in one worktree and qwen isn't in CI — the same footing [Qwen C]/[Qwen E] shipped on. Low-risk residual: reconcile reserializes matching rows (valid JSON, same values), unproven only if qwen does byte-level per-row integrity checks; dsh's analogous header rewrite works against real dsh.

Docs

  • [Qwen K] section added to .claude/rules/qwen.md (32,971 chars, under the 40k cap).
  • Migration note added to .claude/rules/agent-archive.md.

xerhab added 3 commits August 28, 2026 21:56
Extend XERK-101 migration to qwen, the dsh [K] (XERK-475) analogue. The
load-bearing difference: qwen is Claude-shaped, so its native log IS the
durable store qwen --resume reloads from (no separate store like dsh's).

- export_session locates qwen's native log by glob (_qwen_native_log) and
  packs it under the reserved .qwen-store/ prefix, truncated to its last
  complete line like the main transcript.
- import_session routes it to the single target native-log FILE under the
  TARGET cwd's slug (_qwen_store_dest, == _project_slug — verified qwen's
  slug rule is Claude's every-non-alnum->'-'), so qwen --resume in the new
  worktree finds it; drops it on a claude fallback (no qwen on target).
- _reconcile_qwen_store_cwd re-points the cwd carried on every native-log
  row (qwen keys on cwd, issue #2373) to the localized worktree; no-op on a
  same-mount move.
- _launch_qwen re-validates the carried model against the target's config,
  falling back to the host default; endpoint/key are always the target's.
- Resume (process death / boot-adopt) was already wired by [Qwen B]/[Qwen C]
  (--resume in place + tail at native EOF); [K] pins it and adds migration.

The <slug>/<sid>/qwen/ raw-archive mirror is the display/metrics feed and is
NEVER carried (the target rebuilds it) — kept straight from the resumable log.

Tests: qwen cases in TestMigrateSession (pack/unpack, stray-drop, cross-mount
place+rekey, claude-fallback drop, slug rule, same-mount no-op).
# Conflicts:
#	.claude/rules/qwen.md
# Conflicts:
#	.claude/rules/qwen.md
@xerhab
xerhab merged commit a46a3fd into main Aug 29, 2026
4 checks passed
@xerhab
xerhab deleted the XERK-516 branch August 29, 2026 02:33
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.

1 participant