Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,11 @@ jobs:
- name: Validate canonical catalogue surfaces
run: python3 scripts/validate_catalog.py --base-ref "${{ github.event.pull_request.base.sha }}"

- name: Install preview validation dependency
run: python3 -m pip install -r requirements-dev.txt

- name: Validate preview timing and still alternatives
run: python3 scripts/validate_previews.py

- name: Check patch whitespace
run: git diff --check "${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}"
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
Thumbs.db
*.swp
*.tmp
.venv/
__pycache__/
.playwright-cli/
/work/

Expand Down
15 changes: 11 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ the gap is clear before you build the pull request.
```text
pets/<pet-id>/
site/assets/<pet-id>-preview.gif
site/assets/<pet-id>-preview.png
site/install/<pet-id>/index.html
catalog.json
README.md
Expand Down Expand Up @@ -95,6 +96,7 @@ pets/<pet-id>/
├── pet.json
├── spritesheet.webp
├── preview.gif
├── preview.png
├── README.md
└── qa/
├── contact-sheet.png
Expand Down Expand Up @@ -178,15 +180,20 @@ python3 scripts/render_idle_preview.py \
--output "pets/${PET_ID}/preview.gif"
```

The command requires Python 3 and Pillow. The renderer preserves the standard idle
timing, creates a `384 × 416` 2x presentation asset for clean browser downsampling, and
The command requires Python 3 and Pillow; follow the [development setup](docs/MAINTENANCE.md).
It renders a 6.6-second desktop-paced idle loop and matching static PNG at
`384 × 416` size for clean browser downsampling (`--scale 1` uses native cells), and
uses a deterministic alpha cutoff before GIF encoding. This prevents GIF's binary
transparency from turning faint edge pixels into a grainy fringe. Copy the resulting
preview to `site/assets/` when the pet is shown in the public catalogue.
GIF and PNG to `site/assets/` when the pet is shown in the public catalogue.
Use a `<picture>` source with `media="(prefers-reduced-motion: reduce)"` for the PNG
on each website card and installer. The static image must equal GIF frame zero.
Run `python3 scripts/validate_previews.py` in the activated development environment.

The cache token is the first 12 lowercase characters of the exact referenced preview
file's SHA-256. Package README references hash `pets/<pet-id>/preview.gif`; website and
installer references hash `site/assets/<pet-id>-preview.gif`. The two previews may
installer references hash `site/assets/<pet-id>-preview.gif` and the corresponding
PNG separately. Package and site previews may
differ after documented presentation-only cleanup.

## Change matrix
Expand Down
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ An independent community catalogue of custom animated pets for the Codex desktop
![License](https://img.shields.io/badge/license-CC%20BY%204.0-fbbf24?style=flat-square)
[![Give Codex Pets a star on GitHub](https://img.shields.io/github/stars/senyo888/codex-pets?style=flat-square&logo=github&label=Give%20us%20a%20star&color=fbbf24)](https://github.com/senyo888/codex-pets)

<a href="pets/bella/README.md"><img src="pets/bella/preview.gif?v=1d01e149813d" alt="Bella's dimensional idle animation" width="150"></a>
<a href="pets/aetherwing/README.md"><img src="pets/aetherwing/preview.gif?v=7d8ee08c4885" alt="AetherWing's calm idle animation" width="150"></a>
<a href="pets/aethercore/README.md"><img src="pets/aethercore/preview.gif?v=ec0aa941a348" alt="AetherCore's polished metallic idle animation" width="150"></a>
<a href="pets/aethermite/README.md"><img src="pets/aethermite/preview.gif?v=24eb2a800e48" alt="AetherMite's dimensional idle animation" width="150"></a>
<a href="pets/aetherbite/README.md"><img src="pets/aetherbite/preview.gif?v=1c09298b9c11" alt="Aetherbite's cinematic idle animation" width="150"></a>
<a href="pets/calian/README.md"><img src="pets/calian/preview.gif?v=73d2a0b50334" alt="Calian's calm idle animation" width="150"></a>
<a href="pets/scarlet/README.md"><img src="pets/scarlet/preview.gif?v=49db273a1e1a" alt="Scarlet's calm idle animation" width="150"></a>
<a href="pets/bella/README.md"><img src="pets/bella/preview.gif?v=13ab61440ccc" alt="Bella's dimensional idle animation" width="150"></a>
<a href="pets/aetherwing/README.md"><img src="pets/aetherwing/preview.gif?v=cf75c61eac81" alt="AetherWing's calm idle animation" width="150"></a>
<a href="pets/aethercore/README.md"><img src="pets/aethercore/preview.gif?v=ea06dabe0655" alt="AetherCore's polished metallic idle animation" width="150"></a>
<a href="pets/aethermite/README.md"><img src="pets/aethermite/preview.gif?v=93af9b1c410e" alt="AetherMite's dimensional idle animation" width="150"></a>
<a href="pets/aetherbite/README.md"><img src="pets/aetherbite/preview.gif?v=a3774afdaa8a" alt="Aetherbite's cinematic idle animation" width="150"></a>
<a href="pets/calian/README.md"><img src="pets/calian/preview.gif?v=422116aef0a3" alt="Calian's calm idle animation" width="150"></a>
<a href="pets/scarlet/README.md"><img src="pets/scarlet/preview.gif?v=f88c094046c2" alt="Scarlet's calm idle animation" width="150"></a>

## [Bella](pets/bella/README.md) · [AetherWing](pets/aetherwing/README.md) · [AetherCore](pets/aethercore/README.md) · [AetherMite](pets/aethermite/README.md) · [Aetherbite](pets/aetherbite/README.md) · [Calian](pets/calian/README.md) · [Scarlet](pets/scarlet/README.md)

Expand All @@ -44,7 +44,11 @@ An independent community catalogue of custom animated pets for the Codex desktop
| 006 | [**Calian**](pets/calian/README.md) | Calian isolates decisive faults and restores deliberate control with calm, methodical judgement. | Sprite v2 | Validated and ready |
| 007 | [**Scarlet**](pets/scarlet/README.md) | Scarlet pairs vigilant drift tracing with propulsive follow-through, carrying every correction to verified closure beside Calian. | Sprite v2 | Validated and ready |

Each companion has a distinct identity and animation language. Every animated preview above is rendered directly from its current validated atlas.
Each companion has a distinct identity and animation language. Every animated preview above is derived from its current validated atlas and uses a
6.6-second idle loop, matching the desktop renderer checked in version 26.930.21537.
The website displays still images when reduced motion is requested; each pet README
also links to a static preview.
See [maintenance and compatibility checks](docs/MAINTENANCE.md).

### Calian and Scarlet — the sister act

Expand Down Expand Up @@ -117,6 +121,7 @@ codex-pets/
│ ├── pet.json
│ ├── spritesheet.webp
│ ├── preview.gif
│ ├── preview.png
│ ├── README.md
│ └── qa/
├── site/
Expand Down
13 changes: 13 additions & 0 deletions docs/CATALOG_CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,16 @@ output, external availability, or real desktop-app installer behaviour.
- A successful Pages deployment and live readback are required before publication is
declared complete.
- No validator, agent, workflow, or generated output may approve its own work.

## Preview playback and accessibility

Package and site idle GIFs use six frames with durations of 1680, 660, 660, 840,
840, and 1920 ms (6.6 seconds total), matching the desktop renderer inspected in
26.930.21537. This is a dated presentation baseline, not a package timing override.
Each GIF has a same-stem static PNG equal to its first decoded frame. Website cards
and installers select that PNG through a reduced-motion `<picture>` source.
PNG cache tokens follow the same exact-file SHA-256 rule as GIF tokens.

`python3 scripts/validate_previews.py` checks timing, geometry, transparency, disposal,
looping, and still-frame equality using the pinned development dependency. The standard
library catalogue validator checks source markup, tokens, and local PNG references.
57 changes: 57 additions & 0 deletions docs/MAINTENANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Maintenance and compatibility

## Development setup

Use Python 3.12 or newer and a complete Git installation with HTTPS helpers.
Keep image tooling in a project environment rather than installing into system Python:

```sh
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -r requirements-dev.txt
python scripts/validate_catalog.py
python scripts/validate_previews.py
```

Repeat setup for each checkout. The standard-library catalogue check can also run
without the environment. CI installs the same pinned Pillow dependency for previews.
If Git reports a missing `remote-https`, inspect `git --exec-path` and the selected
Git distribution; restore its matching helper directory rather than changing credentials.

## October 2026 maintenance changes

- Idle previews now use a 6.6-second loop rather than the previous 1.1-second loop.
Timing was checked against desktop app 26.930.21537 on 2026-10-03. Original decoded
GIF artwork is retained; only frame delays changed.
- Static PNG alternatives show frame zero under reduced motion on the catalogue and
every installer. GIF and PNG cache tokens track the exact referenced files.
- Preview validation runs in CI alongside catalogue validation.
- Authoritative atlases, package IDs, descriptions, sprite versions, installer queries,
and published v1.0.0 release assets are unchanged by these presentation changes.

## Runtime boundaries

The desktop app owns playback, state priority, pointer responses, pet size, and
reduced-motion handling. Package artwork does not implement agent reasoning or control
Home Assistant. Changing preview timing cannot change installed runtime timing.

Desktop v2 atlases and ChatGPT web derivatives have separate contracts. Desktop files
are local; web adoption links and uploads are separate distribution records. Do not
replace an eleven-row desktop atlas with a nine-row web export.

Sources: [Pets](https://learn.chatgpt.com/docs/pets) and
[September 11 pet controls update](https://learn.chatgpt.com/docs/changelog), checked
2026-10-03. Recheck renderer timing on future app updates before changing this baseline.

## Validation before distribution

Run both validators, inspect catalogue and all installers at desktop and 390 px mobile
widths, and switch reduced motion both ways. Confirm each loaded still is static and
matches the visible idle character. Check for image failures, overflow, console errors,
and unchanged install actions. Preview-only changes do not require reinstalling atlases.
For installed packages, compare both pet.json and spritesheet bytes with the chosen
canonical package; preserve prior files before synchronizing metadata.

For an artwork change, rerun structural and visual QA across all states and directions,
update hashes and previews together, and verify at actual display sizes. A frame-difference
outlier alone is not sufficient evidence to regenerate a character.
4 changes: 3 additions & 1 deletion pets/aetherbite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

**The Bio-Digital Systems Tinkerer**

<img src="preview.gif?v=1c09298b9c11" alt="Aetherbite's cinematic idle animation" width="240">
<img src="preview.gif?v=a3774afdaa8a" alt="Aetherbite's cinematic idle animation" width="240">

*Humidity Intelligence's cinematic dimensional systems tinkerer, with crystalline wings, layered articulated armour, and expressive motion.*

[**Install Aetherbite**](https://senyo888.github.io/codex-pets/install/aetherbite/)

</div>

[View the static idle preview](preview.png)

## Personality

Aetherbite is energetic, resolute, and inventive. It brings a compact but dimensional presence to systems work while keeping the same practical instinct: observe carefully, explain the change, and improve the smallest useful thing first.
Expand Down
Binary file modified pets/aetherbite/preview.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pets/aetherbite/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion pets/aethercore/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

**The Continuity Engine**

<img src="preview.gif?v=ec0aa941a348" alt="AetherCore's polished metallic idle animation" width="240">
<img src="preview.gif?v=ea06dabe0655" alt="AetherCore's polished metallic idle animation" width="240">

*A calm clockwork guardian built to keep decisions connected, drift visible, and governance coherent.*

[**Install AetherCore**](https://senyo888.github.io/codex-pets/install/aethercore/)

</div>

[View the static idle preview](preview.png)

## Personality

AetherCore is measured, dependable, and quietly vigilant. It treats continuity as a living system: decisions should remain traceable, architectural intent should survive change, and hidden drift should never be allowed to accumulate unnoticed.
Expand Down
Binary file modified pets/aethercore/preview.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pets/aethercore/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion pets/aethermite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

**The Systems Tinkerer**

<img src="preview.gif?v=24eb2a800e48" alt="AetherMite's dimensional idle animation" width="240">
<img src="preview.gif?v=93af9b1c410e" alt="AetherMite's dimensional idle animation" width="240">

*A premium dimensional Humidity Intelligence champion with a layered mechanical shell and luminous environmental core.*

[**Install AetherMite**](https://senyo888.github.io/codex-pets/install/aethermite/)

</div>

[View the static idle preview](preview.png)

## Personality

AetherMite is curious, energetic, and inventive. It notices tiny inconsistencies, explores practical refinements, and prefers small explainable improvements over clever but fragile shortcuts.
Expand Down
Binary file modified pets/aethermite/preview.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pets/aethermite/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion pets/aetherwing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

**Pet 002 · The Runtime Sentinel**

<img src="preview.gif?v=7d8ee08c4885" alt="AetherWing's calm idle animation" width="240">
<img src="preview.gif?v=cf75c61eac81" alt="AetherWing's calm idle animation" width="240">

*A flight-first guardian of deterministic control, architectural discipline, and truth-backed runtime behaviour.*

[**Install AetherWing**](https://senyo888.github.io/codex-pets/install/aetherwing/)

</div>

[View the static idle preview](preview.png)

## Personality

AetherWing watches the boundary between intent and execution. She challenges weakened architecture, detects drift early, and protects deterministic behaviour without disrupting stable systems.
Expand Down
Binary file modified pets/aetherwing/preview.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pets/aetherwing/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion pets/bella/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

**The 1 True Source**

<img src="preview.gif?v=1d01e149813d" alt="Bella's dimensional idle animation" width="240">
<img src="preview.gif?v=13ab61440ccc" alt="Bella's dimensional idle animation" width="240">

*A premium dimensional crystalline coherence guardian with layered depth, graceful flight, and a precise eye for truth.*

[**Install Bella**](https://senyo888.github.io/codex-pets/install/bella/)

</div>

[View the static idle preview](preview.png)

## Personality

Bella is composed, precise, and gently sceptical. She prefers truth over theatre, deterministic systems over hidden magic, and a clean review over an enthusiastic guess.
Expand Down
Binary file modified pets/bella/preview.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pets/bella/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion pets/calian/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

**The Tactical Sentinel**

<img src="preview.gif?v=73d2a0b50334" alt="Calian's calm idle animation" width="240">
<img src="preview.gif?v=422116aef0a3" alt="Calian's calm idle animation" width="240">

*A composed tactical guardian and one half of a sister act of unmatched diligence, known for calm judgement, deliberate control, and exact follow-through.*

[**Install Calian**](https://senyo888.github.io/codex-pets/install/calian/)

</div>

[View the static idle preview](preview.png)

## Personality

Calian is calm, tactical, and steady under pressure. She isolates the decisive fault, establishes a clear plan, and restores control with measured action.
Expand Down
Binary file modified pets/calian/preview.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pets/calian/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion pets/scarlet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

**The Propulsive Execution Guardian**

<img src="preview.gif?v=49db273a1e1a" alt="Scarlet's calm idle animation" width="240">
<img src="preview.gif?v=f88c094046c2" alt="Scarlet's calm idle animation" width="240">

*A poised CGI guardian and one half of a sister act of unmatched diligence, built to trace drift and carry every correction to verified closure.*

[**Install Scarlet**](https://senyo888.github.io/codex-pets/install/scarlet/)

</div>

[View the static idle preview](preview.png)

## Personality

Scarlet is poised, vigilant, and exacting. She follows weak signals across the full execution path, contains drift before it spreads, and closes gaps with controlled precision.
Expand Down
Binary file modified pets/scarlet/preview.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pets/scarlet/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Pillow==12.3.0
9 changes: 7 additions & 2 deletions scripts/render_idle_preview.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Render a crisp six-frame idle GIF from a validated Codex v2 atlas."""
"""Render a six-frame idle GIF and matching still from a validated Codex v2 atlas."""

from __future__ import annotations

Expand All @@ -13,7 +13,9 @@
ATLAS_SIZE = (1536, 2288)
CELL_SIZE = (192, 208)
IDLE_FRAME_COUNT = 6
IDLE_DURATIONS = [280, 110, 110, 140, 140, 320]
# Desktop renderer observed in 26.930.21537: base idle holds multiplied by six.
# Presentation timing only; pet.json cannot override the app's animation timing.
IDLE_DURATIONS = [1680, 660, 660, 840, 840, 1920]
DEFAULT_ALPHA_THRESHOLD = 192
DEFAULT_SCALE = 2

Expand Down Expand Up @@ -132,6 +134,9 @@ def main() -> None:
)

result = validate_preview(output, args.scale)
with Image.open(output) as preview:
preview.convert("RGBA").save(output.with_suffix(".png"))
result["stillOutput"] = str(output.with_suffix(".png"))
result["spritesheet"] = str(spritesheet)
result["alphaThreshold"] = args.alpha_threshold
print(json.dumps(result, indent=2))
Expand Down
14 changes: 12 additions & 2 deletions scripts/validate_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ def __init__(self) -> None:
self.values: list[str] = []

def handle_starttag(self, _tag: str, attrs: list[tuple[str, str | None]]) -> None:
"""Record href and src values from an HTML start tag."""
"""Record href, src, and single-candidate srcset values from an HTML start tag."""
values = dict(attrs)
for key in ("href", "src"):
for key in ("href", "src", "srcset"):
if values.get(key):
self.values.append(values[key] or "")

Expand Down Expand Up @@ -300,11 +300,13 @@ def validate_package(
metadata_path,
spritesheet,
preview,
preview.with_suffix(".png"),
readme,
validation,
ROOT / "pets" / pet_id / "qa" / "contact-sheet.png",
ROOT / "pets" / pet_id / "qa" / "look-directions.png",
site_preview,
site_preview.with_suffix(".png"),
installer,
)
for path in required_paths:
Expand Down Expand Up @@ -503,6 +505,12 @@ def text_value(value: object) -> str:
return html.escape(str(value), quote=False)


def still_token(pet_id: str) -> str:
"""Missing stills are reported by package validation, without a hash traceback."""
path = SITE_ROOT / "assets" / f"{pet_id}-preview.png"
return sha256(path)[:12] if path.is_file() else ""


def validate_site_index(
pets: list[dict[str, object]], site_tokens: dict[str, str], errors: list[str]
) -> None:
Expand All @@ -528,6 +536,7 @@ def validate_site_index(
presentation = pet["presentation"]
token = site_tokens[pet_id]
required_snippets = (
f'<source media="(prefers-reduced-motion: reduce)" srcset="./assets/{pet_id}-preview.png?v={still_token(pet_id)}">',
f'src="./assets/{pet_id}-preview.gif?v={token}" alt="{presentation["previewAlt"]}"',
f'<p class="pet-number">Pet {pet["catalogueNumber"]}</p>',
f'id="{pet_id}-title"',
Expand Down Expand Up @@ -564,6 +573,7 @@ def validate_installer(
text = path.read_text(encoding="utf-8")
readme_url = f"https://github.com/senyo888/codex-pets/blob/main/pets/{pet_id}/README.md"
required_snippets = (
f'<source media="(prefers-reduced-motion: reduce)" srcset="../../assets/{pet_id}-preview.png?v={still_token(pet_id)}">',
(
f'<meta name="description" content="Install {name}, a custom animated '
f'v{pet["spriteVersionNumber"]} pet for the Codex desktop app.">'
Expand Down
Loading
Loading