Skip to content

Repository files navigation

DishLoadout

CI Python 3.11+ MIT

Exact, explainable dishwasher rack load planning from geometry you declare.

DishLoadout is a zero-runtime-dependency CLI that turns a reusable JSON rack profile and a dish inventory into an optimal, collision-free load. It produces a machine-verifiable receipt, a top-view SVG, and a standalone script-free HTML report—all locally, without accounts, uploads, cameras, or appliance control.

中文快速开始 · live generated demo · input format · failure repair

Important

A plan is exact only for the declared discrete profile. It is not a cleaning, hygiene, drainage, breakage, or manufacturer-safety certification. Your appliance and item manuals always override an example profile.

Why this exists

Dishwasher advice tools usually provide generic tips or troubleshooting. Robot research projects solve camera, grasping, or simulation problems. DishLoadout occupies a different, deliberately narrow gap: reproducible household rack assignment over user-declared anchors, keep-outs, tags, loaded footprints, and height limits.

The exact-name GitHub search for DishLoadout returned no repositories before v0.1.0, and the closest search results were decision-tree advice or robotics/ML projects rather than an offline geometry optimizer. The dated search method and links are recorded in docs/research.md. That is evidence for a useful gap, not a promise of popularity or proof that no adjacent work exists.

Quick start

Requirements: uv and CPython 3.11 or newer.

git clone https://github.com/KanadeK/dishloadout.git
cd dishloadout
uv sync --locked

uv run dishloadout validate examples/family-dinner.json
uv run dishloadout plan examples/family-dinner.json --output-dir build/family
uv run dishloadout verify examples/family-dinner.json build/family/plan.json

Expected output:

VALID: Family dinner · 2 racks · 15 positions · 15 expanded items
FULL: placed 15/15 · priority 115 · nodes 4156 · artifacts build\family
VERIFIED: 15 placements · status full · optimal true

The plan command creates a new directory and refuses to overwrite an existing one:

build/family/
├── plan.json   # stable Plan v1 receipt
├── plan.svg    # top-view rack drawing
└── index.html  # standalone report; no JavaScript or remote assets

A real optimization model

Each rack is an independent two-dimensional plane. A profile declares:

  • rack width, depth, and loaded-height limit;
  • candidate anchor points and their accepted dish kinds;
  • allowed 0°/90° orientations, tags, and preference costs;
  • rectangular keep-outs such as a declared spray-tower clearance;
  • item quantity, loaded footprint, height, priority, tag constraints, and an explicit dishwasher-safety acknowledgement.

DishLoadout expands stable item instance IDs, rejects infeasible placements, then runs bounded branch-and-bound search. The objective is lexicographic:

  1. maximize total loaded priority;
  2. maximize loaded item count;
  3. minimize total position preference cost;
  4. choose the stable lexicographically smallest assignment.

Only a completed search sets optimal: true. If the declared node budget is exhausted, the CLI exits 4 and records status: "search-limit" instead of presenting the incumbent as proven optimal.

Examples and failure paths

Scenario Expected result Exit
examples/family-dinner.json 15/15, exact full plan 0
examples/overloaded-party.json 4/6, exact partial plan with reasons 1
examples/search-limit.json incomplete search, optimal: false 4
examples/invalid-duplicate-position.json input rejected before search 3

Try each path without treating expected non-zero statuses as shell failures:

uv run dishloadout plan examples/overloaded-party.json --output-dir build/party
uv run dishloadout plan examples/search-limit.json --output-dir build/limit
uv run dishloadout validate examples/invalid-duplicate-position.json

Stable process codes:

Code Meaning
0 valid input, full plan, or verified plan
1 exact partial plan, or semantic verification failed
2 command-line usage error from argparse
3 malformed or invalid Scenario/Plan input
4 search budget exhausted; optimality is not claimed
5 input/output failure, including an existing output directory

Unassigned records include stable reason codes such as NOT_DISHWASHER_SAFE, TAG_CONSTRAINT, HEIGHT_EXCEEDED, NO_GEOMETRIC_FIT, and NO_CONFLICT_FREE_POSITION. See the full contract in docs/input-format.md.

Acceptance command

One command runs the complete local release gate:

uv run python scripts/check.py

It checks the lockfile, formatting, lint, strict typing, tests and coverage, all success/failure examples, generated-demo reproducibility, wheel/sdist builds, a clean wheel install, and the installed console entry point. The same gate runs on Windows and Ubuntu with Python 3.11 and 3.14.

If it fails, do not delete the lockfile or weaken a check. Follow the exact repair route in docs/repair.md, rerun the focused command, and then rerun the full gate.

Scope boundaries

DishLoadout does not:

  • infer geometry from photos or ship an AI model;
  • model water flow, detergent, drying, hygiene, breakage, or drainage;
  • provide manufacturer-branded profiles or claim certification;
  • control an appliance, fetch URLs, upload data, or collect telemetry;
  • silently switch to a heuristic when exact search exceeds its budget.

See SPEC.md, docs/architecture.md, and ADR-0001 for the frozen v0.1 boundary.

Development

uv sync --locked
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run mypy src tests scripts

Contributions are welcome when they preserve deterministic output and the claim boundary. Read CONTRIBUTING.md and SECURITY.md before opening a pull request or reporting a vulnerability.

License

MIT © KanadeK.

About

Exact, explainable dishwasher rack load planning from declared geometry

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages