# Portable `mtran` ROS2 Package 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:** Turn `migrate_ws` into a self-contained ROS2 package named `mtran` that can be copied to `smart_healthcare_2026/src/mtran`, prepared online once, and built and run offline on native `x86_64` or `aarch64` Ubuntu 22.04 systems.

**Architecture:** Keep the existing C++17 bridge and synchronous `/translate_en_to_zh` service, but move the package root from `migrate_ws/src/mtran_ros2` to `migrate_ws` and rename all ROS identities from `mtran_ros2` to `mtran`. A version-pinned MTranServer source tree is vendored under `vendor/mtranserver`; `dependencies/auto_install.sh` stages and builds it under `.build/mtranserver`, downloads only the `en -> zh-Hans` offline data, and places generated artifacts under `runtime/` and `models/`. CMake only validates and installs prepared artifacts, while launch resolves every runtime path from the installed package share/prefix.

**Tech Stack:** ROS2 Humble, `ament_cmake`, C++17, `rclcpp`, `rosidl_default_generators`, libcurl, nlohmann-json, Python ROS2 launch, Bash, Bun 1.3.14, MTranServer 4.0.33 at commit `f5672a9`.

## Global Constraints

- Target OS is Ubuntu 22.04; ROS2 Humble is supplied by the user and is never installed by `auto_install.sh`.
- Native CPU architectures are `x86_64` and `aarch64`; the sidecar is built on the host where the script runs.
- The package and source-directory name are `mtran`; the generated service type is `mtran/srv/TranslateEnglishToChinese`.
- The only runtime service is `/translate_en_to_zh`; it translates one English string to Simplified Chinese.
- The service node and executable remain `mtran_bridge_node`; its existing readiness, validation, error mapping, and single-thread behavior remain intact.
- `auto_install.sh` may use the network during preparation, but runtime and CMake must not download files or require network access.
- `auto_install.sh` must not source a user workspace, install ROS2, or run `colcon build`; the user runs `colcon build --packages-select mtran` from `smart_healthcare_2026`.
- CMake must never invoke Bun, npm, curl, apt, or any other download/build preparation command.
- Vendored source is fixed at MTranServer commit `f5672a9` and build-generated files stay outside `vendor/mtranserver`.
- Generated Bun tools, build staging, sidecar, records, models, colcon outputs, and logs are ignored; vendored source, package source, manifests, scripts, and documentation are committed.
- Do not modify or commit the unrelated repository-root `.vscode/` directory.

## File Map

The implementation changes the current tree as follows:

- Move `migrate_ws/src/mtran_ros2/CMakeLists.txt` to `migrate_ws/CMakeLists.txt`; define package `mtran`, build the existing C++ targets, validate prepared artifacts, and install them into the ROS install space.
- Move `migrate_ws/src/mtran_ros2/package.xml` to `migrate_ws/package.xml`; rename the package and keep only ROS package metadata and system library dependencies.
- Move `migrate_ws/src/mtran_ros2/include/mtran_ros2/*.hpp` to `migrate_ws/include/mtran/*.hpp`; rename namespaces and generated-service includes to `mtran`.
- Move `migrate_ws/src/mtran_ros2/src/*.cpp` to `migrate_ws/src/*.cpp`; update include paths and namespaces without changing service semantics.
- Move `migrate_ws/src/mtran_ros2/srv/TranslateEnglishToChinese.srv` to `migrate_ws/srv/TranslateEnglishToChinese.srv`; preserve its fields exactly.
- Move `migrate_ws/src/mtran_ros2/test/*.cpp` to `migrate_ws/test/*.cpp`; update includes/namespaces and preserve the existing 16 behavioral assertions.
- Move `migrate_ws/src/mtran_ros2/config/translator.yaml` to `migrate_ws/config/translator.yaml`; retain bridge parameters.
- Move `migrate_ws/src/mtran_ros2/launch/translator.launch.py` to `migrate_ws/launch/translator.launch.py`; resolve defaults from installed `mtran` resources only.
- Remove `migrate_ws/src/mtran_ros2/scripts/build_mtranserver.sh`; replace it with `migrate_ws/dependencies/auto_install.sh`, which owns all preparation.
- Create `migrate_ws/dependencies/dependencies.txt` as the human-readable dependency/download manifest.
- Create `migrate_ws/vendor/mtranserver/` from the fixed upstream commit and add provenance/license notes.
- Rewrite `migrate_ws/README.md` for a first-time teammate, with the copy, preparation, build, launch, and service-call path on the first screen.
- Extend `migrate_ws/.gitignore` for `dependencies/tools/`, `.build/`, generated runtime/model data, colcon output, and test caches while allowing required empty directories through `.gitkeep` files where needed.
- Add focused shell/Python tests under `migrate_ws/test/` for package layout, preparation-script validation/idempotence, CMake preparation checks, install mapping, and launch path resolution.

---

### Task 1: Flatten the Package and Rename the ROS Identity

**Files:**
- Move: `migrate_ws/src/mtran_ros2/CMakeLists.txt` -> `migrate_ws/CMakeLists.txt`
- Move: `migrate_ws/src/mtran_ros2/package.xml` -> `migrate_ws/package.xml`
- Move: `migrate_ws/src/mtran_ros2/include/mtran_ros2/http_translation_client.hpp` -> `migrate_ws/include/mtran/http_translation_client.hpp`
- Move: `migrate_ws/src/mtran_ros2/include/mtran_ros2/translator_node.hpp` -> `migrate_ws/include/mtran/translator_node.hpp`
- Move: `migrate_ws/src/mtran_ros2/src/*.cpp` -> `migrate_ws/src/*.cpp`
- Move: `migrate_ws/src/mtran_ros2/srv/TranslateEnglishToChinese.srv` -> `migrate_ws/srv/TranslateEnglishToChinese.srv`
- Move: `migrate_ws/src/mtran_ros2/test/*.cpp` -> `migrate_ws/test/*.cpp`
- Move: `migrate_ws/src/mtran_ros2/config/translator.yaml` -> `migrate_ws/config/translator.yaml`
- Move: `migrate_ws/src/mtran_ros2/launch/translator.launch.py` -> `migrate_ws/launch/translator.launch.py`
- Delete: `migrate_ws/src/mtran_ros2/scripts/build_mtranserver.sh` after its preparation responsibilities are covered by Task 4.
- Modify: `migrate_ws/.gitignore`
- Test: `migrate_ws/test/test_package_identity.sh`

**Interfaces:**
- Consumes: the current `mtran_ros2` package files and the service contract `string text` / `bool success, string translation, string error`.
- Produces: a package rooted directly at `migrate_ws`, named `mtran`, with C++ namespaces/includes and generated service references under `mtran`.

- [ ] **Step 1: Add a failing package-layout test before moving files.**

Create `test/test_package_identity.sh` with these assertions:

```bash
#!/usr/bin/env bash
set -euo pipefail
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)

test -f "${root}/package.xml"
test -f "${root}/CMakeLists.txt"
test ! -e "${root}/src/mtran_ros2/package.xml"
grep -q '<name>mtran</name>' "${root}/package.xml"
grep -q 'project(mtran)' "${root}/CMakeLists.txt"
grep -q 'mtran/srv/TranslateEnglishToChinese' "${root}/README.md"
! rg -n 'mtran_ros2' "${root}" -g '!docs/superpowers/**' -g '!vendor/**'
```

Run from the package root:

```bash
bash test/test_package_identity.sh
```

Expected: FAIL because the package is still nested and named `mtran_ros2`.

- [ ] **Step 2: Move the package files and remove the extra source nesting.**

From the repository root, use `git mv` for the paths listed above, create the target directories, and leave `migrate_ws` itself as the ROS package root. Preserve the service definition byte-for-byte. Do not move `docs/` or the existing `.gitignore` outside `migrate_ws`.

- [ ] **Step 3: Rename package symbols and generated-service references.**

Replace only package identity references in the moved source and tests:

```text
mtran_ros2::                 -> mtran::
"mtran_ros2/                 -> "mtran/
project(mtran_ros2)           -> project(mtran)
<name>mtran_ros2</name>       -> <name>mtran</name>
package="mtran_ros2"         -> package="mtran"
```

Keep `/translate_en_to_zh`, `mtran_bridge_node`, `TranslateEnglishToChinese`, parameter names, and error strings unchanged. Update the README service command to:

```bash
ros2 service call /translate_en_to_zh \
  mtran/srv/TranslateEnglishToChinese \
  "{text: 'Hello.'}"
```

- [ ] **Step 4: Make the identity test pass and run the existing C++ test suite.**

Run:

```bash
bash test/test_package_identity.sh
source /opt/ros/humble/setup.bash
PYTHONNOUSERSITE=1 colcon build --packages-select mtran --cmake-args -DBUILD_TESTING=ON
PYTHONNOUSERSITE=1 colcon test --packages-select mtran --event-handlers console_direct+
PYTHONNOUSERSITE=1 colcon test-result --verbose
```

Expected: the identity test passes, the package configures as `mtran`, and the existing HTTP/client-node tests pass before runtime-resource installation is added. If CMake currently requires generated runtime files, temporarily keep that check out until Task 5; the identity test remains the gate for this task.

- [ ] **Step 5: Commit the package flattening and namespace rename.**

```bash
git add migrate_ws
git commit -m "refactor: rename portable ROS package to mtran"
```

---

### Task 2: Vendor the Fixed MTranServer Source

**Files:**
- Create: `migrate_ws/vendor/mtranserver/**`
- Create: `migrate_ws/vendor/mtranserver/PROVENANCE.md`
- Modify: `migrate_ws/.gitignore`
- Test: `migrate_ws/test/test_vendor_source.sh`

**Interfaces:**
- Consumes: tracked files at repository commit `f5672a9`.
- Produces: a read-only source tree containing the MTranServer 4.0.33 single-binary build inputs, with no dependency on the parent repository during preparation.

- [ ] **Step 1: Add the vendor provenance test.**

Create `test/test_vendor_source.sh`:

```bash
#!/usr/bin/env bash
set -euo pipefail
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
vendor="${root}/vendor/mtranserver"
test -f "${vendor}/package.json"
test -f "${vendor}/bun.lock"
test -f "${vendor}/scripts/build.ts"
test ! -e "${vendor}/.git"
test ! -e "${vendor}/dist"
test ! -e "${vendor}/node_modules"
grep -q '"version": "4.0.33"' "${vendor}/package.json"
grep -q 'f5672a9' "${vendor}/PROVENANCE.md"
```

Run `bash test/test_vendor_source.sh`. Expected: FAIL because the vendor directory does not exist.

- [ ] **Step 2: Extract exactly the fixed upstream commit.**

From `/home/hikos/MTranServer`, create the directory and extract only tracked files:

```bash
mkdir -p migrate_ws/vendor/mtranserver
git archive f5672a9 | tar -x -C migrate_ws/vendor/mtranserver
```

Remove any generated `dist/`, `release/`, `node_modules/`, local caches, logs, and repository metadata if present. Do not copy the current `migrate_ws` directory into the vendor tree.

- [ ] **Step 3: Record provenance and make the vendor tree read-only by convention.**

Write `vendor/mtranserver/PROVENANCE.md` containing:

```text
Source repository: https://github.com/xxnuo/MTranServer.git
Source commit: f5672a9
Release: MTranServer 4.0.33
License: Apache-2.0 (see LICENSE)
Build entry point: package.json script "build" -> scripts/build.ts --single
Policy: this tree is vendored input; generated files belong in ../../.build/mtranserver.
```

The implementation must use `git archive` provenance rather than a live parent-repository path. Add `vendor/mtranserver/**/node_modules`, `vendor/mtranserver/dist`, and `vendor/mtranserver/release` to ignore rules only as defensive protection; do not ignore tracked source files.

- [ ] **Step 4: Run the vendor test and verify the source tree is complete.**

Run:

```bash
bash test/test_vendor_source.sh
test ! -e migrate_ws/vendor/mtranserver/.git
grep -Fq '"version": "4.0.33"' migrate_ws/vendor/mtranserver/package.json
```

Expected: the test passes, the vendor tree contains the Bun lockfile, build script, UI/assets inputs, and no generated build output or nested Git metadata.

- [ ] **Step 5: Commit the vendored source.**

```bash
git add migrate_ws/vendor migrate_ws/.gitignore migrate_ws/test/test_vendor_source.sh
git commit -m "build: vendor MTranServer 4.0.33 source"
```

---

### Task 3: Add the Dependency Manifest

**Files:**
- Create: `migrate_ws/dependencies/dependencies.txt`
- Modify: `migrate_ws/.gitignore`
- Test: `migrate_ws/test/test_dependency_manifest.sh`

**Interfaces:**
- Consumes: the fixed environment and upstream dependencies defined in the design specification.
- Produces: a human-readable manifest that distinguishes user-provided prerequisites, script-installed apt packages, workspace tools, vendored source, and generated downloads.

- [ ] **Step 1: Add a manifest coverage test.**

Create `test/test_dependency_manifest.sh`:

```bash
#!/usr/bin/env bash
set -euo pipefail
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
manifest="${root}/dependencies/dependencies.txt"
test -f "${manifest}"
for required in \
  "Ubuntu 22.04" "ROS2 Humble" "ament_cmake" "rclcpp" \
  "build-essential" "libcurl4-openssl-dev" "nlohmann-json3-dev" \
  "Bun 1.3.14" "MTranServer 4.0.33" "f5672a9" \
  "records.json" "en -> zh-Hans" "decompressedHash"; do
  grep -Fq "${required}" "${manifest}"
done
grep -Fq "auto_install.sh does not install ROS2" "${manifest}"
grep -Fq "colcon build is run by the user" "${manifest}"
```

Run `bash test/test_dependency_manifest.sh`. Expected: FAIL because the manifest does not exist.

- [ ] **Step 2: Write the complete manifest.**

Use these sections and values in `dependencies.txt`:

```text
User-provided prerequisites
- Ubuntu 22.04
- ROS2 Humble: ament_cmake, ament_cmake_gtest, rclcpp,
  rosidl_default_generators, rosidl_default_runtime, launch, launch_ros
- auto_install.sh does not install ROS2
- colcon build is run by the user

Installed by dependencies/auto_install.sh (apt)
- build-essential, cmake, pkg-config, curl, ca-certificates, rsync,
  npm, libcurl4-openssl-dev, nlohmann-json3-dev

Workspace-local tool
- Bun 1.3.14, installed below dependencies/tools with its cache there

Included source
- MTranServer 4.0.33, commit f5672a9, Apache-2.0

Downloaded runtime data
- Mozilla translations-models-v2 records endpoint
- Mozilla attachment CDN base URL
- exact direction en -> zh-Hans
- model, lex, srcvocab, trgvocab files
- decompressedHash SHA-256 verification
```

Include the upstream URL from `git remote get-url origin` and the exact endpoint strings from `src/models/records.ts`.

- [ ] **Step 3: Run the manifest test.**

Run `bash test/test_dependency_manifest.sh`. Expected: PASS, with every dependency category and the distinction between ROS prerequisites and script-installed packages visible to a new user.

- [ ] **Step 4: Commit the manifest.**

```bash
git add migrate_ws/dependencies/dependencies.txt migrate_ws/.gitignore migrate_ws/test/test_dependency_manifest.sh
git commit -m "docs: document portable mtran dependencies"
```

---

### Task 4: Implement and Test `dependencies/auto_install.sh`

**Files:**
- Create: `migrate_ws/dependencies/auto_install.sh`
- Create: `migrate_ws/dependencies/tools/.gitkeep`
- Modify: `migrate_ws/.gitignore`
- Test: `migrate_ws/test/test_auto_install.sh`

**Interfaces:**
- Consumes: `vendor/mtranserver`, the host's Ubuntu/ROS2/CPU state, apt/npm/network, and Mozilla records/model endpoints.
- Produces: executable `runtime/bin/mtranserver`, `runtime/config/records.json`, and exactly four decompressed files below `models/en_zh-Hans/`; it returns non-zero for invalid prerequisites or failed integrity checks.

- [ ] **Step 1: Add shell-level tests with command stubs.**

Create `test/test_auto_install.sh` as a Bash harness that invokes the script with a temporary `PATH`, temporary package root copy, and stub commands. The harness must cover these cases:

```text
1. bash -n succeeds.
2. fake uname -m = ppc64le -> non-zero and message contains x86_64/aarch64.
3. fake /etc/os-release != Ubuntu 22.04 -> non-zero.
4. neither ROS_DISTRO=humble nor /opt/ros/humble -> non-zero and message says ROS2 Humble must be installed.
5. missing vendor/mtranserver -> non-zero before apt/npm/network work.
6. valid fixture artifacts plus fake apt/npm/bun/curl/file/sha256sum -> second run skips valid sidecar, records, and model files and does not run the build/download stubs.
```

The fixture must use a copied package root so tests never write to the real runtime directories. Permit only these explicit test seams in the script: `MTRAN_OS_RELEASE_FILE` defaults to `/etc/os-release`, `MTRAN_MACHINE` defaults to `uname -m`, and `MTRAN_ROS_PREFIX` defaults to `/opt/ros/humble`. Production behavior with no overrides must always inspect the real host. Run:

```bash
bash -n dependencies/auto_install.sh
bash test/test_auto_install.sh
```

Expected: syntax passes; behavioral tests initially FAIL because the script does not exist.

- [ ] **Step 2: Implement path resolution and prerequisite checks.**

At the top of the script use exactly this path relationship:

```bash
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)
package_root=$(cd "${script_dir}/.." && pwd -P)
vendor_source="${package_root}/vendor/mtranserver"
build_dir="${package_root}/.build/mtranserver"
runtime_bin_dir="${package_root}/runtime/bin"
```

Use `set -euo pipefail`, validate `${MTRAN_OS_RELEASE_FILE:-/etc/os-release}` for `ID=ubuntu` and `VERSION_ID=22.04`, accept only `${MTRAN_MACHINE:-$(uname -m)}` values `x86_64` and `aarch64`, and require `ROS_DISTRO=humble` or `${MTRAN_ROS_PREFIX:-/opt/ros/humble}`. Check these Debian packages with `dpkg-query -W` without installing them: `ros-humble-ament-cmake`, `ros-humble-ament-cmake-gtest`, `ros-humble-rclcpp`, `ros-humble-rosidl-default-generators`, `ros-humble-rosidl-default-runtime`, `ros-humble-launch`, and `ros-humble-launch-ros`. Print the complete missing-package list and exit non-zero before running apt/npm/network commands.

- [ ] **Step 3: Implement non-ROS apt and workspace-local Bun preparation.**

Install only the manifest's apt packages using `sudo apt-get update` and `sudo apt-get install -y`; never include `ros-humble-*`. Reuse or install Bun 1.3.14 below `dependencies/tools`, set `BUN_INSTALL_CACHE_DIR` below `dependencies/tools`, prepend the local Bun binary to `PATH`, and set `ELECTRON_SKIP_BINARY_DOWNLOAD=1`. Pin the Bun package/version in the command or an explicit local version check so a newer global Bun cannot silently change the build.

- [ ] **Step 4: Implement sidecar staging without touching the vendor tree.**

Require `vendor/mtranserver/package.json` and `vendor/mtranserver/bun.lock`. Copy with `rsync -a --delete` from vendor to `.build/mtranserver`, excluding `.git`, `node_modules`, `dist`, `release`, and local caches. Run in the staging directory:

```bash
"${bun_bin}" install --frozen-lockfile
"${bun_bin}" run build
```

Then atomically install `dist/mtranserver` as mode `0755` at `runtime/bin/mtranserver`. Verify it is executable and its ELF architecture matches `uname -m` (`x86-64` for `x86_64`, `aarch64` for `aarch64`) using `file`; reject a mismatched existing artifact.

- [ ] **Step 5: Implement records/model download and integrity verification.**

Download the records endpoint to a temporary file under `runtime/config`, validate JSON shape before renaming to `records.json`, and use the attachment CDN base URL from the vendored implementation. Parse records structurally with an available JSON tool or a small Bun script under `dependencies/tools`; do not select files by uncontrolled filename globbing. Filter exact `sourceLanguage == en` and `targetLanguage == zh-Hans`, select the newest version per required `fileType`, and require all four roles: `model`, `lex`, `srcvocab`, `trgvocab`.

For every selected attachment, download the `.zst` to a temporary path, decompress it, compute SHA-256 on the decompressed bytes, compare with `decompressedHash` and, when present, `decompressedSize`, then rename the verified decompressed output to its final filename. Remove compressed temporary files only after verification. A valid existing file is reused; a missing or mismatched file is replaced. Never leave a mismatched file at a final path.

- [ ] **Step 6: Print the post-preparation commands and make the tests pass.**

On success print at least:

```bash
workspace_root=$(cd "${package_root}/../.." && pwd -P)
printf 'Preparation complete.\n'
printf 'Next: cd %q && colcon build --packages-select mtran\n' "${workspace_root}"
printf 'Then: source install/setup.bash && ros2 launch mtran translator.launch.py\n'
```

Run:

```bash
bash -n dependencies/auto_install.sh
bash test/test_auto_install.sh
```

Expected: PASS for syntax, all invalid-environment failures, vendor isolation, architecture checks, and idempotent valid-artifact reuse. The real network preparation is deferred to the final integration task.

- [ ] **Step 7: Commit the preparation script.**

```bash
git add migrate_ws/dependencies/auto_install.sh migrate_ws/dependencies/tools/.gitkeep migrate_ws/.gitignore migrate_ws/test/test_auto_install.sh
git commit -m "build: add portable mtran dependency installer"
```

---

### Task 5: Make CMake Validate and Install Prepared Runtime Data

**Files:**
- Modify: `migrate_ws/CMakeLists.txt`
- Modify: `migrate_ws/package.xml`
- Create: `migrate_ws/test/test_cmake_preparation.sh`
- Modify: `migrate_ws/.gitignore`

**Interfaces:**
- Consumes: the renamed C++/service files from Task 1 and generated preparation outputs from Task 4.
- Produces: a package that configures only when preparation outputs exist and installs the sidecar, model, records, launch file, config, headers, executable, and service type under package `mtran`.

- [ ] **Step 1: Add the missing-artifact CMake test.**

Create `test/test_cmake_preparation.sh` that copies the source package to a temporary directory, sources `/opt/ros/humble/setup.bash`, removes `runtime/bin/mtranserver`, `runtime/config/records.json`, and `models/en_zh-Hans/*`, then runs:

```bash
cmake -S "${fixture}" -B "${fixture}/build"
```

Expected: FAIL with output containing `dependencies/auto_install.sh` and naming the missing preparation artifact. The test must not invoke network commands or Bun.

- [ ] **Step 2: Add explicit CMake preparation checks.**

Before target definitions, require these paths and stop with `message(FATAL_ERROR ...)` if absent:

```text
runtime/bin/mtranserver
runtime/config/records.json
models/en_zh-Hans/model.enzh.intgemm.alphas.bin
models/en_zh-Hans/lex.50.50.enzh.s2t.bin
models/en_zh-Hans/srcvocab.enzh.spm
models/en_zh-Hans/trgvocab.enzh.spm
```

The error must say `Run ./dependencies/auto_install.sh from the mtran package root.` Use `${CMAKE_CURRENT_SOURCE_DIR}` only for source inputs and never add a download/custom command.

- [ ] **Step 3: Update the ROS target and install rules.**

Keep the existing `mtran_core` and `mtran_bridge_node` targets and C++17/libcurl/nlohmann-json/rclcpp dependencies, but generate interfaces with `${PROJECT_NAME}` after `project(mtran)`. Install:

```cmake
install(TARGETS mtran_core mtran_bridge_node
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION lib/${PROJECT_NAME})
install(DIRECTORY include/ DESTINATION include)
install(DIRECTORY launch config DESTINATION share/${PROJECT_NAME})
install(PROGRAMS runtime/bin/mtranserver DESTINATION lib/${PROJECT_NAME})
install(DIRECTORY models/en_zh-Hans DESTINATION share/${PROJECT_NAME}/models)
install(FILES runtime/config/records.json DESTINATION share/${PROJECT_NAME}/mtranserver_config)
```

Do not install source-side `runtime/config` or model parent directories wholesale, because the generated data is intentionally mapped into the package install space.

- [ ] **Step 4: Update package metadata and build tests.**

Set `<name>mtran</name>`. Keep `ament_cmake`, `rosidl_default_generators`, `rosidl_default_runtime`, `rclcpp`, `libcurl4-openssl-dev`, `nlohmann-json3-dev`, and `ament_cmake_gtest` declarations valid for the target ROS2 distribution. Add any missing build/runtime dependency needed by `launch/translator.launch.py` (`launch`, `launch_ros`, and `ament_index_python`) as package dependencies.

Run the test fixture with all six prepared paths populated by small fixture files plus an executable sidecar fixture:

```bash
bash test/test_cmake_preparation.sh
source /opt/ros/humble/setup.bash
PYTHONNOUSERSITE=1 colcon build --packages-select mtran --cmake-args -DBUILD_TESTING=ON
```

Expected: the missing-artifact case fails clearly; the prepared fixture configures; a real `colcon build` succeeds and places the sidecar at `install/mtran/lib/mtran/mtranserver`, models under `install/mtran/share/mtran/models/en_zh-Hans`, and records at `install/mtran/share/mtran/mtranserver_config/records.json`.

- [ ] **Step 5: Run the C++ tests after install mapping.**

```bash
PYTHONNOUSERSITE=1 colcon test --packages-select mtran --event-handlers console_direct+
PYTHONNOUSERSITE=1 colcon test-result --verbose
```

Expected: all existing client/node tests pass, and no CMake step attempts to access the network or run Bun.

- [ ] **Step 6: Commit the CMake/install-space implementation.**

```bash
git add migrate_ws/CMakeLists.txt migrate_ws/package.xml migrate_ws/test/test_cmake_preparation.sh migrate_ws/.gitignore
git commit -m "build: install prepared mtran runtime assets"
```

---

### Task 6: Resolve Launch Defaults Exclusively from Install Space

**Files:**
- Modify: `migrate_ws/launch/translator.launch.py`
- Modify: `migrate_ws/package.xml`
- Create: `migrate_ws/test/test_launch_paths.py`

**Interfaces:**
- Consumes: installed package share directory and the install mapping from Task 5.
- Produces: launch defaults for `mtranserver`, `models`, `mtranserver_config`, and `translator.yaml` that remain valid after the source package is copied or moved.

- [ ] **Step 1: Add a failing launch-path test.**

Create `test/test_launch_paths.py` that reads the launch source and asserts:

```python
from pathlib import Path

root = Path(__file__).resolve().parents[1]
source = (root / "launch" / "translator.launch.py").read_text()
assert 'get_package_share_directory("mtran")' in source
assert 'get_package_prefix("mtran")' in source
assert 'get_package_share_directory("mtran_ros2")' not in source
assert "_find_workspace_root" not in source
assert '"mtran"' in source
assert '"mtranserver_config"' in source
assert 'Path.cwd()' not in source
assert "/home/hikos/" not in source
```

Run `python3 test/test_launch_paths.py`. Expected: FAIL because the current launch searches parent directories and names `mtran_ros2`.

- [ ] **Step 2: Rewrite default paths from the installed share directory.**

Use:

```python
from ament_index_python.packages import get_package_prefix, get_package_share_directory

package_prefix = Path(get_package_prefix("mtran"))
package_share = Path(get_package_share_directory("mtran"))
server_default = package_prefix / "lib" / "mtran" / "mtranserver"
model_default = package_share / "models"
config_default = package_share / "mtranserver_config"
params_default = package_share / "config" / "translator.yaml"
```

Declare those values as defaults for `server_executable`, `model_dir`, `config_dir`, and `params_file`. Keep `host` default `127.0.0.1`, `port` default `8989`, the exact offline environment variables, `respawn=True`, and the bridge package/executable names updated to `mtran`/`mtran_bridge_node`. Do not search upward, inspect `src/`, use `Path.cwd()`, or embed an absolute path.

- [ ] **Step 3: Make launch static and functional tests pass.**

Run:

```bash
python3 test/test_launch_paths.py
python3 -m py_compile launch/translator.launch.py
source /opt/ros/humble/setup.bash
PYTHONNOUSERSITE=1 ros2 launch mtran translator.launch.py --show-args
```

Expected: the path test and syntax check pass; `--show-args` lists install-space defaults and no source-workspace path. Use a temporary fake installed share directory in a Python launch test if the local install has not yet been rebuilt.

- [ ] **Step 4: Commit the install-space launch behavior.**

```bash
git add migrate_ws/launch/translator.launch.py migrate_ws/package.xml migrate_ws/test/test_launch_paths.py
git commit -m "fix: resolve mtran launch assets from install space"
```

---

### Task 7: Rewrite the README for First-Time Onboarding

**Files:**
- Modify: `migrate_ws/README.md`
- Modify: `migrate_ws/dependencies/dependencies.txt` only if a command or version clarification is needed
- Test: `migrate_ws/test/test_readme_commands.sh`

**Interfaces:**
- Consumes: final package names, preparation outputs, install paths, launch defaults, service contract, and architecture behavior from Tasks 1-6.
- Produces: a self-contained quick-start document that lets a teammate reach the first successful service call without reading repository history.

- [ ] **Step 1: Add documentation command checks.**

Create `test/test_readme_commands.sh`:

```bash
#!/usr/bin/env bash
set -euo pipefail
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
readme="${root}/README.md"
for required in \
  "smart_healthcare_2026/src/mtran" \
  "./dependencies/auto_install.sh" \
  "colcon build --packages-select mtran" \
  "ros2 launch mtran translator.launch.py" \
  "mtran/srv/TranslateEnglishToChinese" \
  "/translate_en_to_zh" \
  "ROS2 is not installed by" "x86_64" "aarch64" \
  "records.json" "MT_OFFLINE=true" "port 8989"; do
  grep -Fq "${required}" "${readme}"
done
! rg -n 'mtran_ros2|/home/hikos/MTranServer|Path.cwd|build_mtranserver.sh' "${readme}"
```

Run `bash test/test_readme_commands.sh`. Expected: FAIL because the current README uses the old nested package and repository-specific paths.

- [ ] **Step 2: Rewrite the first screen as the shortest successful path.**

Put these sections in this exact order: purpose/architecture, prerequisites, copy location, run `dependencies/auto_install.sh`, expected generated outputs, build from `smart_healthcare_2026`, source and launch, service call and successful response, service fields/error codes, warm-up/offline runtime, x86_64/aarch64 behavior, repeat/clean-up, troubleshooting, maintainer directory reference.

The first command sequence must state its working directory at every transition:

```bash
# From the parent directory containing smart_healthcare_2026:
cp -a migrate_ws smart_healthcare_2026/src/mtran

# From smart_healthcare_2026/src/mtran:
./dependencies/auto_install.sh

# From smart_healthcare_2026:
source /opt/ros/humble/setup.bash
PYTHONNOUSERSITE=1 colcon build --packages-select mtran
source install/setup.bash

# From any shell with the two setup files sourced:
PYTHONNOUSERSITE=1 ros2 launch mtran translator.launch.py

# From a second shell with both setup files sourced:
ros2 service call /translate_en_to_zh \
  mtran/srv/TranslateEnglishToChinese \
  "{text: 'Hello.'}"
```

Show the expected successful response with `success=True`, a Chinese translation, and `error=''`. State explicitly that the script installs non-ROS dependencies only, that `colcon build` is user-run, and that runtime needs no network after preparation/build.

- [ ] **Step 3: Add operational details and troubleshooting.**

Document the six service error codes from `translator_node.cpp`, the one-sentence/512-character scope, persistent sidecar and model warm-up, `MT_OFFLINE=true`, fixed port `8989`, launch respawn, and the exact generated output paths. Include troubleshooting entries for missing ROS Humble, apt failure, Bun/registry failure, missing or mismatched model, port conflict, sidecar restart, delayed service discovery, and `PYTHONNOUSERSITE=1` for Python user-site conflicts.

Explain that CMake consumes prepared artifacts and never downloads them. Explain that native builds on `x86_64` and `aarch64` create the matching sidecar; the ROS2 C++ source itself is portable and no x64-to-ARM cross-build is provided by the package.

- [ ] **Step 4: Validate all README commands and commit the documentation.**

Run:

```bash
bash test/test_readme_commands.sh
rg -n 'mtran_ros2|/home/hikos/MTranServer|build_mtranserver.sh' README.md
```

Expected: the first command passes; the second produces no output. Then commit:

```bash
git add migrate_ws/README.md migrate_ws/test/test_readme_commands.sh
git commit -m "docs: add portable mtran onboarding guide"
```

---

### Task 8: Verify an Independent Copied Workspace End to End

**Files:**
- Test: temporary directory outside the repository, for example `/tmp/mtran-portability-test-XXXX/smart_healthcare_2026/src/mtran`
- Modify: only the implementation files identified by a failing verification, never the user's `.vscode/`

**Interfaces:**
- Consumes: the complete package root and all scripts/assets produced by Tasks 1-7.
- Produces: evidence that the package works after being copied to the requested `smart_healthcare_2026/src/mtran` location and does not depend on `/home/hikos/MTranServer`.

- [ ] **Step 1: Copy the package to an unrelated workspace path.**

From the repository root, create a temporary workspace and copy the package without `.git`, colcon outputs, Bun cache, or generated runtime data if testing preparation from scratch:

```bash
tmp_root=$(mktemp -d)
mkdir -p "${tmp_root}/smart_healthcare_2026/src"
rsync -a --exclude '.git/' migrate_ws/ "${tmp_root}/smart_healthcare_2026/src/mtran/"
```

Assert the copied package has no source reference to `/home/hikos/MTranServer` and has `package.xml` at its root.

- [ ] **Step 2: Run preparation in the copied package.**

From the copied package root:

```bash
cd "${tmp_root}/smart_healthcare_2026/src/mtran"
./dependencies/auto_install.sh
test -x runtime/bin/mtranserver
test -s runtime/config/records.json
test -s models/en_zh-Hans/model.enzh.intgemm.alphas.bin
test -s models/en_zh-Hans/lex.50.50.enzh.s2t.bin
test -s models/en_zh-Hans/srcvocab.enzh.spm
test -s models/en_zh-Hans/trgvocab.enzh.spm
```

Expected: the script builds a native sidecar, downloads only the fixed direction, verifies all four files, and prints the user-owned `colcon` next step. On a repeated run, valid artifacts are reused.

- [ ] **Step 3: Build from the parent workspace, not the package root.**

```bash
cd "${tmp_root}/smart_healthcare_2026"
source /opt/ros/humble/setup.bash
PYTHONNOUSERSITE=1 colcon build --packages-select mtran
source install/setup.bash
test -x install/mtran/lib/mtran/mtranserver
test -f install/mtran/share/mtran/mtranserver_config/records.json
test -f install/mtran/share/mtran/models/en_zh-Hans/model.enzh.intgemm.alphas.bin
```

Expected: clean build success from the parent workspace and correct install-space paths.

- [ ] **Step 4: Run the real offline sidecar and first service call.**

From the copied workspace with setup files sourced:

```bash
PYTHONNOUSERSITE=1 ros2 launch mtran translator.launch.py
```

From a second shell with `/opt/ros/humble/setup.bash` and the copied `install/setup.bash` sourced:

```bash
ros2 service call /translate_en_to_zh \
  mtran/srv/TranslateEnglishToChinese \
  "{text: 'The robot has completed the inspection.'}"
```

Expected: launch logs show the sidecar and bridge, the bridge advertises the service only after health/warm-up, and the response has `success=True`, a non-empty Simplified Chinese translation, and an empty error. No runtime network request is made; `MT_OFFLINE=true` is visible in the launch process environment.

- [ ] **Step 5: Run the complete verification suite and record residual risk.**

Run from the copied workspace:

```bash
PYTHONNOUSERSITE=1 colcon test --packages-select mtran --event-handlers console_direct+
PYTHONNOUSERSITE=1 colcon test-result --verbose
cd src/mtran
python3 test/test_launch_paths.py
bash test/test_package_identity.sh
bash test/test_vendor_source.sh
bash test/test_dependency_manifest.sh
```

Expected: all automated tests pass, the real offline translation succeeds, and `git status --short` in the original repository still reports only the pre-existing unrelated `.vscode/` directory plus the intended migration commits.

- [ ] **Step 6: Commit any final verification-only corrections.**

Only after a failing end-to-end check identifies a real implementation/documentation defect, make the smallest correction and rerun the affected test plus the complete suite. Use:

```bash
git add migrate_ws
git commit -m "test: verify portable mtran workspace"
```

Do not commit copied generated outputs, temporary workspaces, `.tools`, `.build`, runtime model files, or colcon `build/`, `install/`, and `log/` directories.

---

## Final Self-Review Checklist

- [ ] `migrate_ws` itself contains `package.xml` and `CMakeLists.txt`; there is no `src/mtran_ros2` package nesting.
- [ ] All ROS identities are `mtran`; the service command uses `mtran/srv/TranslateEnglishToChinese`.
- [ ] The vendor tree is exactly commit `f5672a9`, and all generated sidecar build files are under `.build/mtranserver`.
- [ ] `auto_install.sh` is the only preparation entry point; it does not install ROS2 or run `colcon`.
- [ ] `dependencies.txt` lists every user prerequisite, apt package, Bun version/source, vendored source, endpoint, model role, and integrity method.
- [ ] CMake has no network or Bun invocation and installs all runtime data into the documented install-space paths.
- [ ] Launch has no parent-directory search, source-workspace fallback, `Path.cwd()`, or absolute local path.
- [ ] README commands use the copied `smart_healthcare_2026/src/mtran` layout and reach a first service call.
- [ ] Tests cover invalid prerequisites, unsupported architecture, missing vendor, idempotence, missing CMake artifacts, install mapping, launch paths, package identity, and the real offline call.
- [ ] A placeholder-marker scan finds no deferred or vague implementation-only steps in this plan.
