Metadata-Version: 2.4
Name: hatch-pet-ioc
Version: 0.5.0
Summary: Spec-driven Codex Pet sprite pipeline with pluggable image generation providers
Author: Hatch-Pet IoC contributors
License: Apache-2.0
Project-URL: Homepage, https://calt13.github.io/hatch-pet-ioc/
Project-URL: Repository, https://github.com/calt13/hatch-pet-ioc
Keywords: sprite,codex,pet,image-generation,ioc
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: Pillow>=10.0
Requires-Dist: numpy<2
Requires-Dist: scipy>=1.10
Requires-Dist: requests>=2.28
Provides-Extra: volcano
Requires-Dist: rembg>=2.0; extra == "volcano"
Requires-Dist: onnxruntime>=1.15; extra == "volcano"
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == "mcp"
Provides-Extra: all
Requires-Dist: rembg>=2.0; extra == "all"
Requires-Dist: onnxruntime>=1.15; extra == "all"
Requires-Dist: mcp>=1.0; extra == "all"
Dynamic: license-file

# Hatch-Pet IoC Workspace

Spec-driven Codex Pet sprite pipeline with pluggable image generation
(IoC). Based on OpenAI's official `hatch-pet` skill
(`openai/skills/.curated/hatch-pet`), with `$imagegen` inverted into an
`ImageGenProvider` interface.

> **Spec is the SSOT; image models are consumables.**
> The durable assets are the invariants — the job manifest
> (`imagegen-jobs.json`: 9 states × frame counts × dependencies), the
> geometry spec (192×208 cells, 8×9 atlas), and the QA gates
> (`inspect_frames` / `validate_atlas`). Whoever produces the pixels —
> an AI service or a human artist — is interchangeable.

## Why any agent (incl. Kimi / Kimi Code CLI) can drive this

Kimi K3 has no image-generation weights. Its "drawing" comes from a
tool: the agent-gw gateway (`generate_image` / `upload_storage`), which
is exactly what the Kimi web agent's `image_generation` plugin calls.
Since the pipeline depends only on that *capability*, any host that can
reach an image service can be a provider:

| Host | How it provides images |
|---|---|
| Kimi web agent | built-in image_generation plugin (agent-gw) |
| Kimi Code CLI / Claude Code / Codex / Cursor | register `mcp/imagegen_mcp_server.py` (same agent-gw APIs over MCP) |
| Volcano Engine (火山方舟) | `examples/run_volcano_kimi.py`: Seedream generates base + differential keyframes, then `KimiImageGenProvider` interpolates |
| GPT Image web / Midjourney / Flux / a human artist | `PlanImageGenProvider`: pipeline emits `plan.md` + prompts, human drops files into `decoded/`, `resume()` continues |

This mirrors the official hatch-pet skill's own structure ("1 image-gen
call + deterministic post-processing") — the IoC split just makes the
provider boundary explicit.

## Install (recommended: conda env)

The pipeline depends on `rembg` / `onnxruntime`, which are sensitive to the
NumPy major version. Use a dedicated environment to avoid the system-Python
numpy-2 breakage.

```bash
# 1. create env (Python 3.11 works well)
conda create -n hatch-pet-ioc python=3.11 -y
conda activate hatch-pet-ioc

# 2. editable install with Volcano deps
pip install -e ".[volcano]"

# 3. verify
python -c "from rembg import remove; print('rembg ok')"
```

If you prefer the legacy installer:

```bash
bash install.sh             # core pipeline (Pillow, numpy, scipy)
bash install.sh --mcp       # + MCP example deps (mcp; agent-gw SDK is internal-only, install separately)
bash install.sh --volcano   # + Volcano Engine (Seedream) bridge deps (rembg, onnxruntime)
```

> `mcp/imagegen_mcp_server.py` + `kimi_provider.py` are kept as an
> **example of a private MCP in a specific intranet environment**: the
> `agent-gw` SDK is an internal package (not on public PyPI) and is not
> vendored here — install it from your internal source to run the
> example. Auth needs `KIMI_API_KEY` or `~/.kimi/agent-gw.json` at
> runtime. This example demonstrates the provider-agnostic contract: the
> pipeline consumes artifacts from agreed directories, regardless of
> who/what produced them.
>
> For Volcano Engine, set `ARK_API_KEY` at runtime; Seedream returns RGB
> images, so `rembg` is used to produce the transparent sprites the
> pipeline expects.

## Experiment organization

Every example/experiment should write outputs into a **timestamped run directory**
instead of a hard-coded project-global folder. Use the helper:

```python
from hatch_pet_ioc.experiment import make_run_dir

run_dir = make_run_dir("seedance_loop", params={"model": "doubao-seedance-2-0-mini-260615"})
# -> runs/20260730_143052_seedance_loop/
```

This keeps multiple attempts comparable and prevents one failed run from
clobbering another. The helper also writes `experiment.json` metadata so each
run is self-describing.


## Quick start

```bash
# 1. Full pipeline, zero external services (Mock placeholder art)
python3 examples/run_demo.py

# 2. Plan mode: emit the job plan + prompts, let a human / any external
#    image tool fill decoded/, then resume
python3 examples/run_plan_demo.py

# 3. Volcano Engine + Kimi provider: Seedream generates the 3 AI assets
#    (base + closed-eyes + failed keyframes), then deterministic physics
#    produces every animation row.
export ARK_API_KEY=ark-...
python3 examples/run_volcano_kimi.py

# 4. Kimi provider template: supply your own decoded/base_raw_ai.png,
#    decoded/keyframe_closed.png, decoded/keyframe_failed.png
python3 examples/run_kimi_provider.py

# 5. Seedance video -> sprite strip (loop-locked first/last-frame mode)
export ARK_API_KEY=ark-...
python3 examples/run_seedance_loop_poc.py

# 6. Extract one clean cycle from a Seedance loop video
python3 examples/extract_single_cycle.py
```

## Kimi provider workflow (what "AI base + AI keyframes" means)

The AI is called **3 times total**, everything else is deterministic:

1. **Base** — one transparent-background sprite
   (`decoded/base_raw_ai.png`).
2. **Closed-eyes keyframe** — generated with the base as
   `reference_image_urls` entry, so identity is locked by the spec, not
   by luck (`decoded/keyframe_closed.png`). Used for blink/squint grafts.
3. **Failed keyframe** — same reference-locked trick, asking for a
   deflated body + X eyes (`decoded/keyframe_failed.png`). The `failed`
   row is a deterministic cross-fade between base and this keyframe.

In a Kimi Code CLI session with the MCP server registered, the agent
performs steps 1–3 via `generate_image` + `upload_image`, then runs the
pipeline; on the web agent the same happens through the built-in plugin.
All middle frames (squash-and-stretch, tilts, running, jumping) are
jelly-physics transforms — see `hatch_pet_ioc/kimi_provider.py`, which
is heavily commented with the rationale.

## MCP registration (Kimi Code CLI etc.)

```json
{
  "mcpServers": {
    "agent-gw-imagegen": {
      "command": "python3",
      "args": ["/path/to/hatch-pet-ioc/mcp/imagegen_mcp_server.py"],
      "env": { "KIMI_API_KEY": "sk-..." }
    }
  }
}
```

Auth falls back to `~/.kimi/agent-gw.json` (same credential the web
plugin uses). This MCP server is an **example of a private MCP in an
intranet env** — it needs the internal `agent-gw` SDK (not on public
PyPI, not vendored here).

## Layout

```
hatch-pet-ioc/
├── install.sh / requirements.txt
├── hatch_pet_ioc/            # the IoC pipeline package
│   ├── cli.py                #   uvx CLI (ADR-3): init / run / viewer / spec
│   ├── providers.py          #   ImageGenProvider ABC + MockProvider
│   ├── plan_provider.py      #   PlanProvider (human/external agent)
│   ├── kimi_provider.py      #   KimiProvider (AI base+keyframes+jelly physics)
│   ├── volcano_engine.py     #   Volcano Engine Seedream client + bg removal
│   ├── advisor.py            #   DryRunAdvisor (operation manual generator)
│   ├── pipeline.py           #   orchestrator (prepare/generate/finalize/resume)
│   ├── seedance_video_provider.py  # Seedance video → sprite strip provider
│   ├── spec.py              #   spec SSOT loader: index addressing + metadata manifest
│   ├── frame_viewer.py       #   portable single-file HTML frame viewer
│   ├── sprite_analyzer.py    # residual/palette/QA analysis toolkit
│   ├── data/specs/           #   bundled spec copy so uvx resolves the default
│   └── scripts/              #   official hatch-pet scripts (1:1) + package_pet.py
├── specs/
│   └── codex-pet.json        #   spec SSOT (9 states × frames, 192×208, 8×9 atlas)
├── cortex/                   #   governance: EPIC/ADR/TASK/wiki (ADR-driven)
├── mcp/
│   └── imagegen_mcp_server.py  # intranet-private MCP example (agent-gw over stdio)
└── examples/
    ├── run_demo.py                  # Mock end-to-end
    ├── run_plan_demo.py             # Plan mode end-to-end
    ├── run_volcano_kimi.py          # Volcano Engine → Kimi provider end-to-end
    ├── run_kimi_provider.py         # Kimi provider template
    ├── run_seedance_poc.py          # Seedance video → sprite strip
    ├── run_seedance_keyframe_poc.py # Seedream keyframe → Seedance video → strip
    ├── run_seedance_loop_poc.py     # Seedance first/last-frame locked loop
    ├── analyze_run_cycle.py         # visualize stride period / pose symmetry
    ├── extract_single_cycle.py      # extract one clean loop cycle from video
    ├── extract_single_cycle_variants.py  # try different phase offsets / spacings
    ├── extract_user_range.py        # extract a user-picked frame range into strips
    ├── generate_user_selection.py   # build raw/transparent GIFs + QA from selected frames
    └── generate_frame_viewer.py     # generate local HTML viewer for frame folder
```

## Governance, artifacts & spec

Governed by [cortex](../cortex/INDEX.md) (EPIC/ADR/TASK), git commit-driven
(`cortex probe` / `stats`; commit messages carry `TASK-xxx`).

- **Artifacts stay local (ADR-1).** `runs/` and `runs-*.tar.gz` are
  git-ignored — generated images never enter the repo. Archive bundles via
  `scripts/archive_runs.sh`.
- **Spec is the SSOT (ADR-2).** `specs/codex-pet.json` is the single source
  for state → index / row / frame-count / purpose; `hatch_pet_ioc/spec.py`
  reads it. States are addressed by **index**, semantic meaning lives in a
  metadata manifest (`asset-manifest.json`) — so other specs (different
  frame/state counts) are just a parameter change, and consumers (Codex
  atlas, previewer, other formats) each interpret the same indexed assets.

## CLI (uvx, ADR-3)

`hatch-pet-ioc` is a thin, uvx-invocable CLI exposing the pipeline as
commands — run from any consumer directory, no repo clone needed (the mock
provider is fully offline):

```bash
uvx --from /path/to/hatch-pet-ioc hatch-pet-ioc --help
```

Subcommands:

| command | what it does |
|---|---|
| `init` | validate/select the spec, prepare `runs/` + `.hatch-pet-ioc.json` |
| `run` | run the pipeline end-to-end with a pluggable provider (default `mock`) |
| `viewer <folder>` | generate a portable single-file HTML frame viewer |
| `spec show` / `spec validate` / `spec make` | inspect / validate / build specs |

`run` anchors output to the consumer's cwd: `<cwd>/runs/<timestamp>_<pet-id>/`
holding `asset-manifest.json` (index → state/row/frame mapping, ADR-2), the
composed atlas, QA review, animated previews, and the packaged asset.

Spec resolution order (ADR-3): `--spec` flag → `.hatch-pet-ioc.json` (written
by `init`) → bundled `codex-pet` default. A custom spec is thus one flag:

```bash
hatch-pet-ioc run --provider mock --spec specs/my-pet.json
```

Out-of-box `--provider` defaults to `mock` (offline demo art so the CLI works
first try). Real `seedream` / `seedance` providers need an `ARK_API_KEY` and
exit with a pointer on how to configure them.

## Fixes vs. the original decomposition

1. **State from spec, not prompt text** — `ImageGenProvider` methods take
   an optional `job` dict; `KimiImageGenProvider` reads state/frame-count
   from the manifest (the SSOT) instead of reverse-parsing prompts.
2. **QA gate** — `finalize(qa_policy="fail")` (default) refuses to
   package an atlas that failed frame inspection; `"warn"` keeps the old
   log-and-continue behaviour.
3. **Cross-fade alpha union** — `PIL.Image.blend` lerps alpha and let
   chroma-key magenta bleed through semi-transparent edges; `_cross_fade`
   keeps the union silhouette (max alpha).
4. **Graft coordinate mapping** — eye grafts map raw-base → fitted-frame
   per axis (`frame.size / raw.size`); the earlier fitted-width ratio
   pushed grafts off-canvas.
5. **Aspect-preserving keyframe normalization** — the deflated `failed`
   keyframe keeps its own aspect ratio; resizing it to the base bbox
   would erase the deformation the model was asked for.

## License

Apache-2.0. This project is a **derivative work** of the `hatch-pet` skill in
OpenAI's [`openai/skills`](https://github.com/openai/skills) repository (the
original skill is licensed Apache-2.0 via its own `LICENSE.txt`). The scripts
under `hatch_pet_ioc/scripts/` are modifications of the original skill's
bundled scripts — each carries an Apache-2.0 header stating the original file
and exactly what was changed. See [`LICENSE`](LICENSE) and [`NOTICE`](NOTICE)
for full terms and attribution.
