Approve Shu infantry handpainted sprite v8
This commit is contained in:
173
scripts/write-unit-sprite-next-steps.py
Normal file
173
scripts/write-unit-sprite-next-steps.py
Normal 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()
|
||||
Reference in New Issue
Block a user