Sharp GUI Lazycat delivery notes
================================

Target architecture
-------------------

- AI Pod runs the complete Sharp GUI stack in one container:
  - Flask backend
  - React frontend build output
  - Apple `ml-sharp`
  - Jetson CUDA / PyTorch runtime
- LCMD / LPK only provides the entry route and startup progress page.
- End users do not build. They only pull a published image tag from `registry.lazycat.cloud`.

Why this replaced the old split mode
------------------------------------

The earlier split deployment kept the frontend in LCMD and the backend in AI Pod.
That introduced repeated route mismatches between `/api`, `/files`, and `/data`.
Inference was healthy, but the viewer and static resources kept breaking because the
browser and backend no longer shared one origin/runtime.

The current delivery model removes that class of failure:

- all app routes are served by the AI Pod container itself
- LCMD Caddy proxies every request to `sharp-gui-ai`
- AI Pod keeps data under `${LZC_AGENT_DATA_DIR}`

Single-script, dual-mode packaging
----------------------------------

`build-lpk.sh` now has two modes:

- `dev`
  - packages a buildable AI Pod service bundle
  - includes `Dockerfile`, `start.sh`, `requirements.jetson.txt`
  - generated compose uses a local image and `pull_policy: never`
  - build the local image manually before installing the dev LPK
- `release`
  - packages a pull-only AI Pod service bundle
  - generated compose only uses `image:`
  - no Dockerfile or runtime helper files are shipped to end users

Build configs:

- `lzc-build.dev.yml` -> `sh build-lpk.sh dev`
- `lzc-build.yml` -> `sh build-lpk.sh release`

Dev and release modes write to separate output directories:

- `dist-dev/`
- `dist-release/`

This keeps local testing and CI jobs from clobbering each other when both modes
are built in the same checkout.

Compose sources:

- `lazycat/docker-compose.aipod.dev.yml`
- `lazycat/docker-compose.aipod.release.yml`

Image lock file:

- `lazycat/image.lock`

The release mode reads `SHARP_GUI_AIPOD_IMAGE` from the environment first, then from
`lazycat/image.lock`.

Image build strategy
--------------------

`lazycat/Dockerfile.aipod` builds a complete runtime image from upstream sources.
It is intended to be built on a Jetson AI Pod host.

Build args:

- `AIPOD_BASE_IMAGE`
  - default: `dustynv/pytorch:2.7-r36.4.0-cu128-24.04`
- `SHARP_GUI_REPO`
  - default: `https://github.com/lueluelue12138/sharp-gui.git`
- `SHARP_GUI_REF`
  - default: `main`
- `ML_SHARP_REPO`
  - default: `https://github.com/apple/ml-sharp.git`
- `ML_SHARP_REF`
  - default: `main`
- `PIP_INDEX_URL`
  - default: `https://mirrors.aliyun.com/pypi/simple`
Maintainer workflow
-------------------

1. Build and test with dev mode.

   `sh build-lpk.sh dev`

2. Or build a dev LPK directly.

   `LZC_BUILD_CONFIG=lzc-build.dev.yml lzc-cli project release -o sharp-gui.dev.lpk`

3. On the Jetson AI Pod host, build the local dev image before installing the dev LPK.

   Example:

   `docker build -f lazycat/Dockerfile.aipod -t sharp-gui-aipod-run:dev .`

   The AI Pod service manager does not reliably execute `build:` from packaged
   compose files. Dev compose therefore uses `image: sharp-gui-aipod-run:dev`
   with `pull_policy: never`, so it will use the already-built local image
   instead of trying Docker Hub.

4. Install the dev LPK and confirm:

   - `torch.cuda.is_available()` is true in the startup log
   - `/api/health` returns 200
   - frontend loads from `/`
   - upload, inference, gallery preview, model download, and export all work

5. Tag and push the verified image.

   Example:

   `docker tag sharp-gui-aipod-run:dev registry.lazycat.cloud/defiedparty/defiedparty/cloudlazycatappssharpgui-sharp-gui:<tag>`

6. Update the release image reference without hand-editing compose.

   Either:

   `SHARP_GUI_AIPOD_IMAGE=registry.lazycat.cloud/...:<tag> sh build-lpk.sh release`

   Or update `lazycat/image.lock` and then run:

   `sh build-lpk.sh release`

7. Build the final user-facing release LPK.

   `lzc-cli project release -o sharp-gui.release.lpk`

Test helper
-----------

`lazycat/test-install.sh` accepts a mode:

- `./lazycat/test-install.sh dev`
- `./lazycat/test-install.sh release`

It runs:

- `lzc-cli project lint`
- `lzc-cli project release`
- `lzc-cli lpk info`
- `lzc-cli lpk install`

LPK packaging output
--------------------

Dev mode packages:

- `dist-dev/lpk-content/Caddyfile`
- `dist-dev/ai-pod-service/docker-compose.yml`
- `dist-dev/ai-pod-service/Dockerfile`
- `dist-dev/ai-pod-service/start.sh`
- `dist-dev/ai-pod-service/requirements.jetson.txt`
- `dist-dev/ai-pod-service/README`

Release mode packages:

- `dist-release/lpk-content/Caddyfile`
- `dist-release/ai-pod-service/docker-compose.yml`
- `dist-release/ai-pod-service/image.lock`
- `dist-release/ai-pod-service/README`

Operational notes
-----------------

- `SHARP_FRONTEND_MODE=react` is required for the AI Pod image to serve the built frontend.
- The runtime still keeps the legacy template fallback, but the intended mode is React.
- The Dockerfile uses upstream `frontend/dist` directly and does not rebuild the
  React frontend on Jetson.
- The Dockerfile also uses upstream `static` directly. It does not run `npm ci`
  and does not copy `frontend/node_modules` into the runtime image.
- The base image already provides CUDA-enabled PyTorch. Do not create a separate venv.
