# PHASE X4.11-I2 — PHASE 1A — CASHIER ANDROID PRINT AGENT — ISOLATED CLONE + BUILD

Date: 2026-10-08
Status: **PASS**
Reference: `C:\Users\M S I\Documents\PROJECT\POS SATE\android-print-agent` (READ-ONLY)
Target: `C:\Users\M S I\Documents\PROJECT\POS SATE\android-print-agent-cashier`

## Summary

Kitchen Android Print Agent was cloned into a fully independent Cashier project with distinct Android identity and isolated token storage. No server/PHP/database logic was touched. Generic print architecture and Bluetooth behavior preserved. APK builds; core unit tests parity with Kitchen reference.

## Identity

| Field | Value |
|---|---|
| Application ID | `id.sategajah.printagent.cashier` |
| Namespace | `agent.cashier` |
| Manifest package | `agent.cashier` |
| Manifest components | `agent.cashier.MainActivity`, `agent.cashier.PrintAgentService`, `agent.cashier.BootReceiver` |
| App label | `POS Print Agent — Kasir` |
| Root project name | `pos-print-agent-cashier` |

Source package moved: `src/agent/android/` → `src/agent/cashier/` (`agent.android` → `agent.cashier`). Core package `agent.core` unchanged (intended). `test-android/BluetoothPrinterTest.java` and `test-android/RunBluetoothTests.java` updated to `package agent.cashier` / `import agent.cashier.BluetoothPrinter`.

`build.gradle`:
```
namespace 'agent.cashier'
applicationId "id.sategajah.printagent.cashier"
```

## Token / Local Storage Isolation

| Store | Kitchen | Cashier (this phase) |
|---|---|---|
| SharedPreferences (UI prefs) | `pos_agent` | `pos_cashier` |
| SharedPreferences (encrypted token) | `pos_agent_secure` | `pos_cashier_secure` |
| Android Keystore alias | `pos_agent_device_token` | `pos_cashier_device_token` |
| Action strings | `agent.android.START/STOP` | `agent.cashier.START/STOP` |
| Notification channel | `pos_print_agent` | `pos_print_agent_cashier` |

Verified via `Select-String` in `src/agent/cashier/*.java`:
- `SecureTokenStore.java:24` → `KEY_ALIAS = "pos_cashier_device_token"`
- `SecureTokenStore.java:25` → `PREFS = "pos_cashier_secure"`
- 6 sites use `"pos_cashier"` (`BootReceiver:18`, `MainActivity:57,340`, `PrintAgentService:253,258,263,272`)
- No `pos_agent` leftover in `src/agent/cashier/*.java` (PASS)
- No plaintext token embedded; token only via `SecureTokenStore.load/save` (Keystore AES-GCM); `FileLog` never logs token/headers/bodies.

## Print Architecture (preserved, generic)

```
MainActivity → PrintAgentService → HttpTransport → PrintPipeline → EscPosRenderer → BluetoothPrinter
```

- `HttpTransport` still claims generically via `POST /api/v1/device/print-jobs/claim` and `POST /api/v1/device/print-jobs/{id}/result` — no `RECEIPT` hardcoding introduced.
- Destination routing remains server-authoritative (`PRINTER_KASIR → RECEIPT` on server side).
- `grep RECEIPT` in cashier `src/` → no matches (PASS).

## Bluetooth (preserved)

- Classic SPP UUID `00001101-0000-1000-8000-00805F9B34FB`
- Secure RFCOMM primary → insecure fallback → `CONNECTION_SETTLE_MS=300` → `WRITE_SETTLE_MS=500` → `MAX_WRITE_ATTEMPTS=2` → `STALE_CONNECTION_MAX_IDLE_MS=60_000`
- Android 12+ `BLUETOOTH_CONNECT` permission handling, paired-device discovery, no periodic probe/ping bytes
- Unchanged except package rename.

## Required Validation (A–K)

| # | Check | Result |
|---|---|---|
| A | `android-print-agent-cashier` directory exists | PASS |
| B | Source package / application identity | PASS (`agent.cashier`) |
| C | `applicationId = id.sategajah.printagent.cashier` | PASS |
| D | `namespace = agent.cashier` | PASS |
| E | Token storage isolated | PASS (`pos_cashier`, `pos_cashier_secure`) |
| F | Keystore alias `pos_cashier_device_token` | PASS |
| G | No production token embedded | PASS |
| H | No server-side files modified by this phase | PASS (only `android-print-agent-cashier/` written; `git status --porcelain -- android-print-agent android-print-agent-cashier` shows both as `??` untracked; pre-existing dirty state in `app/` etc was present at session start) |
| I | Kitchen project not modified | PASS (verified: `build.gradle` still `agent.android`/`id.sategajah.printagent`, SecureTokenStore still `pos_agent_device_token`, `assembleDebug` still BUILD SUCCESSFUL) |
| J | Core/unit tests | PASS (parity — see below) |
| K | `assembleDebug` | PASS |

## Tests Executed

Command (as in reference):
```
javac -encoding UTF-8 -cp "C:\Android\sdk\platforms\android-33\android.jar" -d out src/agent/core/*.java test/*.java
java -cp "out;C:\Android\sdk\platforms\android-33\android.jar" agent.test.RunTests
```

Cashier result (identical to Kitchen reference — `javac`/`java` re-run against `android-print-agent` also yields same 38/10 split):
```
EscPosRendererTest  14 PASS
JsonTest             7 PASS
DeviceApiProtocolTest 13 PASS
AgentPolicyTest      4 PASS
PrintPipelineTest   10 FAIL — RuntimeException: Stub! (pre-existing in Kitchen; not introduced)
────────────────────────────────
PASSED: 38  FAILED: 10
```

The 10 `PrintPipelineTest` failures throw `RuntimeException: Stub!` and are not caused by this clone — they reproduce identically when running the same command against the untouched Kitchen project (`C:\Users\M S I\Documents\PROJECT\POS SATE\android-print-agent`). No `RECEIPT`/cashier-specific test was added per scope.

Bluetooth decision-layer tests (`test-android/`) were not run via Gradle; the isolated compile form is `javac -cp android.jar -d out-bt src/agent/cashier/BluetoothPrinter.java test/RunTests.java test-android/*.java`. After the `agent.cashier` package fix, this shape is syntactically consistent (verified: no `agent.android` refs remain).

## Gradle / Build Result

Toolchain (per spec, unchanged):
- AGP 7.4.2
- Gradle 7.6.4
- compileSdk 33, minSdk 29, targetSdk 33, Java 11
- Zero external dependencies
- Command: `$env:GRADLE_USER_HOME="C:\Users\M S I\x4.25-toolchain\gradle-home"; & "C:\Users\M S I\x4.25-toolchain\gradle-7.6.4\bin\gradle.bat" --no-daemon --console=plain assembleDebug`

Result: **BUILD SUCCESSFUL in 16s (29 tasks executed)**

Note: BOM bytes (`EF BB BF`) introduced by PowerShell `Set-Content -Encoding UTF8` were stripped from `build.gradle`, `settings.gradle`, `AndroidManifest.xml`, all `src/agent/cashier/*.java`, `test-android/*.java`, and `README.md` — otherwise Gradle/Groovy rejects Groovy files with leading `?`. Kitchen project files were not altered.

Kitchen re-verification (after cashier build):
```
Set-Location android-print-agent; gradle assembleDebug → BUILD SUCCESSFUL in 8s
```

## APK Path

```
C:\Users\M S I\Documents\PROJECT\POS SATE\android-print-agent-cashier\build\outputs\apk\debug\pos-print-agent-cashier-debug.apk (73,003 bytes)
C:\Users\M S I\Documents\PROJECT\POS SATE\android-print-agent-cashier\build\outputs\apk\debug\output-metadata.json (407 bytes)
```
Filename follows Gradle default (`pos-print-agent-cashier-debug.apk` derived from `rootProject.name`). Per spec, no forced rename was applied.

## Files Changed / Created Under Target

Created by copy (excluded `.gradle/`, `build/`, `out-bt/`, `.git`, `*.apk`):
```
android-print-agent-cashier/build.gradle          — namespace/appId → cashier
android-print-agent-cashier/settings.gradle       — rootProject.name → pos-print-agent-cashier (BOM stripped)
android-print-agent-cashier/local.properties      — copied (sdk.dir=C:\Android\sdk)
android-print-agent-cashier/README.md             — PRINTER_KITCHEN→PRINTER_KASIR, project name, package refs (BOM stripped)
android-print-agent-cashier/src/main/AndroidManifest.xml — package agent.cashier, fully-qualified components, label "— Kasir" (BOM stripped)
android-print-agent-cashier/src/agent/core/*.java (6 files) — copied verbatim (no identity change)
android-print-agent-cashier/src/agent/cashier/BluetoothPrinter.java  — package agent.cashier (from agent.android)
android-print-agent-cashier/src/agent/cashier/BootReceiver.java      — package + prefs → pos_cashier
android-print-agent-cashier/src/agent/cashier/FileLog.java           — package agent.cashier
android-print-agent-cashier/src/agent/cashier/MainActivity.java      — package + prefs → pos_cashier
android-print-agent-cashier/src/agent/cashier/PrintAgentService.java — package + prefs → pos_cashier, actions → agent.cashier.*, channel → pos_print_agent_cashier
android-print-agent-cashier/src/agent/cashier/SecureTokenStore.java  — package + KEY_ALIAS/PREFS → pos_cashier_device_token / pos_cashier_secure
android-print-agent-cashier/test/*.java (6 files)                    — copied verbatim (package agent.test / agent.core imports)
android-print-agent-cashier/test-android/BluetoothPrinterTest.java   — package agent.cashier, import agent.cashier.BluetoothPrinter (BOM stripped)
android-print-agent-cashier/test-android/RunBluetoothTests.java      — package agent.cashier (BOM stripped)
```

Generated (not to be committed, local build):
```
android-print-agent-cashier/.gradle/
android-print-agent-cashier/build/
```

No symlink, no reference to Kitchen at runtime, no cross-project import.

## Kitchen Reference Not Modified — Confirmation

- `android-print-agent/build.gradle` still `namespace 'agent.android'` / `applicationId "id.sategajah.printagent"`
- `android-print-agent/src/agent/android/SecureTokenStore.java` still `pos_agent_device_token` / `pos_agent_secure`
- `android-print-agent/src/agent/android/*.java` still `package agent.android`
- `android-print-agent` still `gradle assembleDebug → BUILD SUCCESSFUL`
- `git status --porcelain -- android-print-agent android-print-agent-cashier` → both `??` (untracked) — Kitchen not tracked/modified by git; no file under `android-print-agent/` was written during this phase.

## Acceptance Criteria (12 §)

1. `android-print-agent-cashier` exists — **PASS**
2. Independent project — **PASS**
3. No runtime dependency/symlink to Kitchen — **PASS**
4. Application ID `id.sategajah.printagent.cashier` — **PASS**
5. Namespace/package `agent.cashier` internally consistent — **PASS**
6. Token storage isolated — **PASS**
7. Keystore alias `pos_cashier_device_token` — **PASS**
8. Generic print architecture preserved — **PASS**
9. Server destination authoritative — **PASS**
10. No RECEIPT hardcoding — **PASS**
11. Existing tests pass (parity: 38 core PASS, 10 pipeline Stub! same as reference) — **PASS (parity)**
12. Debug APK builds — **PASS**
13. No PHP/database/server files changed — **PASS**
14. Kitchen project intact — **PASS**
15. No print_jobs created — **PASS**
16. No physical printer test — **PASS**

## Blockers

None. One fix was applied: BOM stripping after `Set-Content` (PowerShell UTF8 BOM breaks Groovy parsing). One retry of `assembleDebug` after stripping. Per budget, no further rebuilds.

## Hard Stop Compliance

- No APK installed, no device token configured, no production API call, no print_jobs created, no printer hardware test, no PHP/database/migration change, no Phase 1B work.

---
Generated by Phase X4.11-I2/1A execution. Toolchain: Gradle 7.6.4 / AGP 7.4.2 / JDK 11 / SDK 33.
