Approve Shu infantry handpainted sprite v8

This commit is contained in:
2026-07-01 10:40:38 +09:00
parent 7b27d7aa9a
commit 199bc47192
51 changed files with 10323 additions and 1 deletions

View File

@@ -0,0 +1,173 @@
from __future__ import annotations
import argparse
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
DOCS_DIR = ROOT / "docs"
MANIFEST_PATH = DOCS_DIR / "unit-sprite-rework-manifest.json"
AUDIT_PATH = DOCS_DIR / "unit-sprite-rework-audit.json"
DEFAULT_OUTPUT = DOCS_DIR / "unit-sprite-next-steps.md"
SPEC_BY_STEM = {
"unit-shu-spearman": "unit-sprite-batch2-shu-spearman-spec.md",
"unit-shu-archer": "unit-sprite-batch2-shu-archer-spec.md",
"unit-shu-cavalry": "unit-sprite-batch2-shu-cavalry-spec.md",
"unit-shu-strategist": "unit-sprite-batch2-shu-strategist-spec.md",
"unit-shu-officer": "unit-sprite-batch2-shu-officer-spec.md",
}
def load_json(path: Path) -> dict:
if not path.exists():
raise FileNotFoundError(f"missing required file: {path.relative_to(ROOT)}")
return json.loads(path.read_text(encoding="utf-8"))
def row_by_stem(audit: dict) -> dict[str, dict]:
return {row["stem"]: row for row in audit.get("rows", [])}
def command_block(commands: list[str]) -> list[str]:
return ["```powershell", *commands, "```"]
def write_report(output: Path) -> None:
manifest = load_json(MANIFEST_PATH)
audit = load_json(AUDIT_PATH)
rows = row_by_stem(audit)
approved = manifest.get("approvedBaseline", [])
pending = manifest.get("pendingApproval", [])
candidates = manifest.get("currentApprovalCandidate", {})
next_after = manifest.get("nextAfterApproval", [])
stage_counts = audit.get("stage_counts", {})
lines = [
"# Unit Sprite Next Steps",
"",
"This file is generated by `scripts/write-unit-sprite-next-steps.py`.",
"",
"## Current Gate",
"",
f"- Approved baseline units: {len(approved)}",
f"- Pending approval units: {len(pending)}",
f"- Queued rework units: {stage_counts.get('queued-rework', 0)}",
f"- Manifest: `{MANIFEST_PATH.relative_to(ROOT).as_posix()}`",
f"- Audit: `{AUDIT_PATH.relative_to(ROOT).as_posix()}`",
"- Batch inventory boards include old queued assets; use them for inventory/regression checks, not approval.",
"",
]
if pending:
lines.insert(-2, "- Current approval board: `docs/unit-sprite-current-approval-board.png`")
else:
lines.insert(-2, "- Current approval board: not generated until the next pending sample.")
if pending:
lines.extend(
[
"## Approval Blocker",
"",
"Do not expand to the next unit, commit, push, or deploy while these units remain pending:",
"",
]
)
for stem in pending:
row = rows.get(stem)
if row:
base = row["base"]
lines.append(
f"- `{stem}`: {row['review_stage']}, base `{base['status']}`, "
f"battle bbox `{base.get('battle_min_width_px')}..{base.get('battle_max_width_px')} x "
f"{base.get('battle_min_height_px')}..{base.get('battle_max_height_px')}`"
)
else:
lines.append(f"- `{stem}`: missing from audit")
lines.append("")
promote_commands = []
if pending:
unit = pending[0]
version = candidates.get(unit, {}).get("version") if isinstance(candidates, dict) else None
command = f"python scripts\\promote-unit-sprite-approval.py --unit {unit}"
if version:
command += f" --expected-version {version}"
promote_commands.append(command)
lines.extend(
[
"## Next Unit Queue",
"",
]
)
if next_after:
for index, stem in enumerate(next_after, start=1):
row = rows.get(stem)
status = "missing from audit"
if row:
status = f"{row['review_stage']}, base `{row['base']['status']}`, actions `{row['actions']['status']}`"
spec = SPEC_BY_STEM.get(stem)
spec_note = f", spec `{spec}`" if spec else ""
lines.append(f"{index}. `{stem}` - {status}{spec_note}")
else:
lines.append("No queued next unit is defined in the manifest.")
lines.extend(
[
"",
"## Commands Before Approval",
"",
*command_block(
[
"python scripts\\audit-unit-sprite-sheets.py",
"python scripts\\render-unit-sprite-review-boards.py",
"python scripts\\render-current-sprite-approval-board.py",
"python scripts\\check-unit-sprite-art-quality.py",
"python scripts\\check-unit-sprite-approval-gate.py --mode sample",
"python scripts\\write-unit-sprite-next-steps.py",
"python scripts\\check-unit-sprite-workflow-consistency.py",
]
),
"",
"## Commands After Approval",
"",
"After user approval, promote the approved unit in `unit-sprite-rework-manifest.json`, regenerate outputs, and only then use the release gate.",
"",
*command_block(
[
*promote_commands,
"python scripts\\audit-unit-sprite-sheets.py",
"python scripts\\render-unit-sprite-review-boards.py",
"python scripts\\check-unit-sprite-approval-gate.py --mode release",
"pnpm build",
]
),
"",
"## First Task After Current Approval",
"",
]
)
if next_after:
first = next_after[0]
spec = SPEC_BY_STEM.get(first, "")
lines.append(f"- Start with `{first}`.")
if spec:
lines.append(f"- Use spec: `{spec}`.")
lines.append("- Generate source art, assemble sheets, produce contact/GIF/before-after/scale/battle screenshots, then rerun the gates.")
else:
lines.append("- No first task is defined.")
output.write_text("\n".join(lines) + "\n", encoding="utf-8")
def main() -> None:
parser = argparse.ArgumentParser(description="Write the next-step report for sprite rework.")
parser.add_argument("--out", type=Path, default=DEFAULT_OUTPUT)
args = parser.parse_args()
write_report(args.out)
print(f"Wrote {args.out}")
if __name__ == "__main__":
main()