---
phase: 05-shell-decoder
plan: 02
type: execute
wave: 2
depends_on:
  - "05-01"
files_modified:
  - shell/test_decoder.sh
autonomous: true
requirements:
  - SHL-01
  - SHL-02
  - SHL-03

must_haves:
  truths:
    - "Shell decoder extracts single text file byte-identical to original"
    - "Shell decoder extracts multiple files (text + binary) byte-identical to originals"
    - "Shell decoder extracts files with Cyrillic UTF-8 filenames correctly"
    - "Shell decoder handles no-compress mode (raw encrypted, not gzip-compressed)"
    - "Shell decoder handles empty (0-byte) files"
    - "Shell decoder handles large files (100 KB+)"
  artifacts:
    - path: "shell/test_decoder.sh"
      provides: "Cross-validation test script: Rust pack -> Shell decode -> SHA-256 verify"
      min_lines: 150
      contains: "sha256sum"
  key_links:
    - from: "shell/test_decoder.sh"
      to: "shell/decode.sh"
      via: "Invokes decode.sh to decode Rust-created archives"
      pattern: "decode\\.sh"
    - from: "shell/test_decoder.sh"
      to: "target/release/encrypted_archive"
      via: "Uses Rust archiver to create test archives"
      pattern: "encrypted_archive.*pack"
---

<objective>
Create cross-validation test script that verifies the shell decoder produces byte-identical output to originals for all edge cases.

Purpose: Prove the shell decoder correctly implements the archive format by testing against Rust-created archives (same pattern as kotlin/test_decoder.sh).

Output: `shell/test_decoder.sh` -- a test script that creates archives with the Rust CLI, decodes with decode.sh, and verifies byte-identical output via SHA-256 comparison.
</objective>

<execution_context>
@/home/nick/.claude/get-shit-done/workflows/execute-plan.md
@/home/nick/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/05-shell-decoder/05-01-SUMMARY.md
@kotlin/test_decoder.sh
@shell/decode.sh
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create shell/test_decoder.sh cross-validation test script</name>
  <files>shell/test_decoder.sh</files>
  <action>
Create `shell/test_decoder.sh` modeled after `kotlin/test_decoder.sh` but adapted for the shell decoder. This script uses bash (it runs on the development machine, not the target device) but invokes `decode.sh` with `sh` to ensure POSIX compatibility.

**Script structure (follow kotlin/test_decoder.sh pattern):**

1. **Header:**
   - `#!/usr/bin/env bash`
   - `set -euo pipefail`
   - Comment: Cross-validation test script for Shell decoder

2. **Variables and helpers (copy pattern from kotlin/test_decoder.sh):**
   - SCRIPT_DIR, PROJECT_DIR, TMPDIR, PASS_COUNT, FAIL_COUNT, TOTAL_COUNT
   - Colors (GREEN, RED, YELLOW, BOLD, NC) with terminal detection
   - `cleanup()` function with trap
   - `pass()`, `fail()`, `sha256_of()`, `verify_file()` helper functions (identical to kotlin version)

3. **Prerequisites check:**
   - Check for `cargo` (to build Rust archiver)
   - Check for `openssl` (needed by decode.sh)
   - Check for `sh` (always available, but verify)
   - Print versions: cargo, openssl, sh

4. **Build Rust archiver:**
   ```bash
   (cd "$PROJECT_DIR" && cargo build --release -q)
   ARCHIVER="$PROJECT_DIR/target/release/encrypted_archive"
   ```

5. **Decoder path:**
   ```bash
   DECODER="$SCRIPT_DIR/decode.sh"
   ```
   Verify it exists and is executable.

6. **Test cases (6 tests matching kotlin/test_decoder.sh):**

   **Test 1: Single text file**
   - Create `hello.txt` with content "Hello, World!" (no newline: `echo -n`)
   - Pack with Rust archiver
   - Decode with: `sh "$DECODER" "$TMPDIR/test1.archive" "$TMPDIR/output1/"`
   - verify_file hello.txt

   **Test 2: Multiple files (text + binary)**
   - Create text.txt with 3 lines
   - Create binary.bin with 10 KB random data (`dd if=/dev/urandom bs=1024 count=10`)
   - Pack both files
   - Decode with sh
   - verify_file text.txt, binary.bin

   **Test 3: No-compress mode**
   - Create fake.dat with 2.5 KB random data
   - Pack with `--no-compress "fake.dat"`
   - Decode with sh
   - verify_file fake.dat

   **Test 4: Empty file**
   - Create empty.txt with `touch`
   - Pack with Rust archiver
   - Decode with sh
   - Verify output file exists and is 0 bytes (same logic as kotlin test)

   **Test 5: Large file (100 KB)**
   - Create large.bin with 100 KB random data
   - Pack with Rust archiver
   - Decode with sh
   - verify_file large.bin

   **Test 6: Cyrillic UTF-8 filename (SHL-03 validation)**
   - Create a file with a Cyrillic name: `echo -n "Тестовое содержимое" > "$TMPDIR/файл.txt"`
   - Pack with Rust archiver
   - Decode with sh
   - verify_file that the Cyrillic-named file exists and matches SHA-256
   - This test specifically validates SHL-03

7. **Summary:**
   - Print results: "N passed, M failed out of T tests"
   - Exit 1 if any failures, exit 0 if all pass

**Key difference from kotlin test:** Instead of `java -jar "$JAR"`, use `sh "$DECODER"` to invoke the shell decoder. This ensures the decoder is tested under POSIX sh, not bash.

Make the script executable: `chmod +x shell/test_decoder.sh`
  </action>
  <verify>
    <automated>cd /home/nick/Projects/Rust/encrypted_archive && test -f shell/test_decoder.sh && test -x shell/test_decoder.sh && bash -n shell/test_decoder.sh && echo "SYNTAX OK" && grep -q 'decode.sh' shell/test_decoder.sh && grep -q 'verify_file' shell/test_decoder.sh && echo "STRUCTURE OK"</automated>
    <manual>Run `bash shell/test_decoder.sh` and verify all 6 tests pass</manual>
  </verify>
  <done>shell/test_decoder.sh exists, is executable, passes bash -n, and contains all 6 test cases including Cyrillic filename test</done>
</task>

<task type="auto">
  <name>Task 2: Run cross-validation tests and fix any decode.sh issues</name>
  <files>shell/decode.sh, shell/test_decoder.sh</files>
  <action>
Run the cross-validation test script and fix any issues discovered:

```bash
cd /home/nick/Projects/Rust/encrypted_archive
bash shell/test_decoder.sh
```

**Expected:** All 6 tests pass (PASS for each test case).

**If tests fail:**
- Read the error output carefully
- Common issues to look for:
  1. **Hex case mismatch:** HMAC or SHA-256 comparison fails because of uppercase vs lowercase hex. Fix: normalize with `tr 'A-F' 'a-f'`
  2. **dd offset errors:** Wrong offset calculation in TOC parsing. Fix: verify field order matches FORMAT.md Section 5 exactly
  3. **openssl pipe issues:** Decryption produces garbage. Fix: extract ciphertext to temp file first, then decrypt from file (not pipe)
  4. **Empty file handling:** gunzip fails on empty input. Fix: check original_size=0 before decompression
  5. **Cyrillic filename:** garbled characters. Fix: ensure LC_ALL=C and no text processing on filename bytes
  6. **HMAC scope error:** Wrong bytes fed to HMAC. Fix: HMAC = IV (from archive file, 16 bytes) || ciphertext (encrypted_size bytes)

Fix decode.sh until all tests pass. Do NOT modify the test script to make tests pass -- fix the decoder.

**After all tests pass**, run one more verification:
```bash
# Verify Rust round-trip still works
cd /home/nick/Projects/Rust/encrypted_archive && cargo test --release 2>&1 | tail -5
```
  </action>
  <verify>
    <automated>cd /home/nick/Projects/Rust/encrypted_archive && bash shell/test_decoder.sh 2>&1 | tail -20 && echo "---" && cargo test --release 2>&1 | tail -3</automated>
    <manual>All 6 shell decoder tests pass, including Cyrillic filename. Rust tests still pass.</manual>
  </verify>
  <done>All 6 cross-validation tests pass (single file, multiple files, no-compress, empty file, large file, Cyrillic filename). Existing Rust tests still pass.</done>
</task>

</tasks>

<verification>
1. `bash shell/test_decoder.sh` runs all 6 tests and reports "ALL TESTS PASSED"
2. Test 6 specifically validates Cyrillic filename extraction (SHL-03)
3. Test 3 validates no-compress mode with openssl raw decryption (SHL-02)
4. All tests use Rust archiver to pack and shell decoder to unpack (SHL-01)
5. `cargo test --release` still passes (no regressions)
</verification>

<success_criteria>
- shell/test_decoder.sh exists and is executable
- All 6 test cases pass: single file, multiple files, no-compress, empty file, large file, Cyrillic filename
- Shell decoder produces byte-identical output verified by SHA-256 comparison
- Existing Rust test suite still passes
</success_criteria>

<output>
After completion, create `.planning/phases/05-shell-decoder/05-02-SUMMARY.md`
</output>
