# Implementation Plan: Android FS5 Device Management **Feature**: 001-android-fs5-management **Created**: 2026-03-02 **Tech Stack**: Bash 5, ADB 1.0.41, Python 3 (for JSON output), ShellCheck --- ## Constitution Check - [x] **Article I (Shell-First)**: Implemented as Bash CLI with Python for JSON formatting. - [x] **Article II (ADB Interface)**: All device ops go through `adb -s $DEVICE_SERIAL`. - [x] **Article III (Test-First)**: Tests written in `tests/` using bats-core before implementation. - [x] **Article IV (Idempotency)**: All operations check state before mutating. - [x] **Article V (Non-Destructive)**: Destructive ops require `--force`; all writes logged. - [x] **Article VI (Device Identity)**: Serial loaded from `.env`, validated on every run. - [x] **Article VII (Simplicity)**: 3 modules: `fs5` (entrypoint), `lib/adb.sh` (ADB helpers), `lib/output.sh` (formatting). - [x] **Article VIII (Structured Output)**: `--json` flag on all commands via Python `json` module. - [x] **Article IX (Config over Hardcoding)**: `.env` file with `DEVICE_SERIAL`, `LOG_FILE`, `DEFAULT_PUSH_PATH`. --- ## Architecture ```text android-fs5/ ├── fs5 # Main entrypoint (Bash, chmod +x) ├── lib/ │ ├── adb.sh # ADB wrapper functions │ └── output.sh # Human/JSON output formatting ├── tests/ │ ├── test_status.bats # bats-core tests for status command │ ├── test_transfer.bats # bats-core tests for push/pull │ ├── test_app.bats # bats-core tests for app management │ ├── test_shell.bats # bats-core tests for shell command │ └── test_logs.bats # bats-core tests for log capture ├── .env.example # Template config (committed) ├── .env # Local config (gitignored) ├── .gitignore ├── README.md └── fs5.log # Operation log (gitignored) ``` --- ## Module Design ### `fs5` — Main Entrypoint ```text Usage: ./fs5 [options] Commands: status Show device health and info push Push file/dir to device pull Pull file/dir from device app list List installed apps app install Install APK app uninstall Uninstall package shell [cmd] Execute shell command on device logs Stream logcat output Global Options: --json Output as JSON --force Allow destructive operations --help Show help ``` ### `lib/adb.sh` — ADB Helpers Key functions: - `adb_check_device()` — validates device is connected, exits 2 if not - `adb_cmd()` — wraps `adb -s $DEVICE_SERIAL` with error handling - `adb_get_prop(key)` — reads device property - `adb_shell(cmd)` — executes shell command on device - `adb_push(src, dst)` — pushes file with existence check - `adb_pull(src, dst)` — pulls file - `adb_log_op(op, detail)` — appends timestamped entry to `fs5.log` ### `lib/output.sh` — Output Formatting Key functions: - `out_human(msg)` — prints to stdout - `out_json(data)` — formats key=value pairs as JSON via Python - `out_error(msg)` — prints to stderr with red color - `out_success(msg)` — prints with green checkmark --- ## Data Model ### Config (`.env`) ```bash DEVICE_SERIAL=RD51QE202392 LOG_FILE=./fs5.log DEFAULT_PUSH_PATH=/sdcard/ ADB_TIMEOUT=10 ``` ### Device Status Object (JSON) ```json { "serial": "RD51QE202392", "model": "FS5", "manufacturer": "exone GmbH", "android_version": "9", "battery_level": 85, "battery_charging": true, "storage_total_kb": 54302616, "storage_used_kb": 18228852, "storage_free_kb": 35926308, "storage_percent_used": 34, "adb_state": "device", "timestamp": "2026-03-02T15:43:00+01:00" } ``` ### Log Entry Format ```text 2026-03-02T15:43:00+01:00 PUSH /home/ja/file.txt -> /sdcard/file.txt [OK] 2026-03-02T15:44:00+01:00 INSTALL /tmp/app.apk -> com.example.app [OK] ``` --- ## Technical Decisions ### Why Bash over Python? - ADB is a CLI tool — Bash is the natural glue language. - No dependency installation required on the host. - Python used only for JSON serialization (available on all Manjaro systems). ### Why bats-core for testing? - Purpose-built for Bash script testing. - Supports mocking ADB calls for offline testing. - Integrates with CI/CD pipelines. ### JSON Output Strategy Use Python one-liner to convert shell variables to JSON: ```bash python3 -c "import json,sys; d={...}; print(json.dumps(d, indent=2))" ``` This avoids `jq` as a hard dependency while producing valid JSON. ### Exit Code Strategy | Code | Meaning | |------|---------| | 0 | Success | | 1 | General error (bad args, operation failed) | | 2 | Device not found / ADB error | --- ## Dependencies | Tool | Version | Required | Install | |------|---------|----------|---------| | adb | 1.0.41+ | YES | `sudo pacman -S android-tools` | | bash | 5.x | YES | Pre-installed | | python3 | 3.x | YES | Pre-installed | | bats-core | 1.x | DEV | `sudo pacman -S bash-bats` | | shellcheck | 0.9+ | DEV | `sudo pacman -S shellcheck` | --- ## Validation Scenarios (Quickstart) ```bash # 1. Verify device connected ./fs5 status # 2. Test JSON output ./fs5 status --json | python3 -m json.tool # 3. Test file transfer echo "test" > /tmp/fs5-test.txt ./fs5 push /tmp/fs5-test.txt /sdcard/fs5-test.txt ./fs5 pull /sdcard/fs5-test.txt /tmp/fs5-pulled.txt diff /tmp/fs5-test.txt /tmp/fs5-pulled.txt && echo "PASS" # 4. Test idempotency ./fs5 push /tmp/fs5-test.txt /sdcard/fs5-test.txt # Should warn, not fail # 5. Test app listing ./fs5 app list # 6. Test shell passthrough ./fs5 shell "echo hello from device" # 7. Test log capture ./fs5 logs --lines 5 # 8. Test disconnected behavior (unplug device first) ./fs5 status; echo "Exit code: $?" # Should be 2 ```