Files
TheTree_mock/tools/run_facility_audit.py
T
Takeshi 5dde0c5ec2 Record original-alignment implementation and traceability checkpoint
Capture current source, content data, tests, asset licenses, and original-specification traceability. Original implementation remains incomplete. The accepted matching UI generation passed 154 suites and 12,253 checks, with eight rendered staff-contract screens reviewed. Latest native Windows and full-original acceptance remain outstanding.
2026-09-30 23:52:24 +09:00

86 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Capture current registration evidence; successful export is not acceptance."""
import argparse
from collections import Counter
import hashlib
import json
import os
from pathlib import Path
import re
import subprocess
ROOT = Path(__file__).resolve().parents[1]
MAPPING = 'docs/plans/original-completion-2026-09-29/execution/original_facility_mapping.json'
BASELINE = 'docs/plans/original-completion-2026-09-29/execution/baseline-fetched-2026-09-29.txt'
def manifest():
paths = [ROOT / MAPPING, ROOT / BASELINE, ROOT / 'tools/capture_facility_audit.gd', Path(__file__)]
for directory in ('autoload', 'scripts', 'scenes', 'data'):
paths.extend(p for p in (ROOT / directory).rglob('*') if p.suffix in ('.gd', '.tres', '.tscn', '.json'))
return {str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted(set(paths))}
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--godot', default='godot')
parser.add_argument('--output', required=True)
args = parser.parse_args()
out = Path(args.output).resolve()
out.mkdir(parents=True, exist_ok=True)
def save(name, value):
(out / name).write_text(json.dumps(value, ensure_ascii=False, indent=2) + '\n')
before = manifest()
save('source-start.json', before)
env = dict(os.environ, THE_TREE_FACILITY_AUDIT_PATH=str(out / 'runtime-facilities.json'))
with (out / 'export.log').open('w') as log:
result = subprocess.run([args.godot, '--headless', '--path', str(ROOT), '--script', 'res://tools/capture_facility_audit.gd'], env=env, stdout=log, stderr=subprocess.STDOUT, timeout=120)
after = manifest()
changes = [p for p in sorted(before.keys() | after.keys()) if before.get(p) != after.get(p)]
save('source-end.json', after)
save('source-change.json', changes)
if result.returncode or changes:
raise SystemExit('Audit capture failed or source changed; inspect logs.')
data = json.loads((out / 'runtime-facilities.json').read_text())
mapping = json.loads((ROOT / MAPPING).read_text())
rows = data['original_facilities']
if len(rows) != 30 or len({r['name'] for r in rows}) != 30 or [r['name'] for r in rows] != [r['name'] for r in mapping]:
raise SystemExit('Original mapping coverage is invalid.')
counts = Counter()
mismatches = []
for row in rows:
definition = row['definition']
row['registration_status'] = ('normal' if row['runtime_registered'] and row['palette_listed'] else 'definition_only' if definition else 'missing')
counts[row['registration_status']] += 1
row['full_acceptance'] = 'unproven'
if definition:
expected = [int(x) for x in row['original_size'].split('x')]
layer = re.match(r'FRONT|BACK', row['original_layer_zone']).group()
if definition['footprint'] != expected or definition['layer'] != layer:
mismatches.append(row['name'])
extra = sorted(set(data['normal_registry_ids']) - {r['definition_id'] for r in rows})
unspecified = [r['name'] for r in rows if r['definition'] and r['definition']['customer_capacity'] < 0]
summary = dict(original_count=30, registration_counts=dict(counts), normal_catalog_count=len(data['normal_registry_ids']), non_original_registry_ids=extra,
dimension_or_layer_mismatches=mismatches, customer_limit_unset=unspecified,
all_original_acceptance=False, scope='Registration and loaded definition values only. Not operation, save migration, assets, Windows or whole-spec acceptance.')
save('summary.json', summary)
save('classified-facilities.json', rows)
labels = {'normal': '通常登録・パレット掲載', 'definition_only': '定義のみ', 'missing': '対応定義なし'}
lines = ['# 原本30施設・現行登録監査', '',
f"通常登録 {counts['normal']}、定義のみ {counts['definition_only']}、対応定義なし {counts['missing']}。通常登録全{len(data['normal_registry_ids'])}件のうち原本30施設以外は{len(extra)}件。",
'', '登録状態と読込済み定義の監査。実営業・保存移行・完成資産・Windows・全要件の受入完了は証明しない。', '',
f'対応定義の寸法・層の不一致: {len(mismatches)}件。人数専用上限が未設定: {len(unspecified)}件。capacityから人数値を転記していない。高層・背面・騒音・サービス条件は別途受入が必要。', '',
'| 原本 | 施設 | ID | 登録 | 寸法 / 層 / ゾーン | 顧客人数上限 |', '|---|---|---|---|---|---|']
for r in rows:
d = r['definition']
geometry = f"{d['footprint'][0]}×{d['footprint'][1]} / {d['layer']} / {d['zone']}" if d else '未定義'
limit = str(d['customer_capacity']) if d and d['customer_capacity'] >= 0 else '未設定'
lines.append(f"| {r['section']} | {r['name']} | {r['definition_id'] or '—'} | {labels[r['registration_status']]} | {geometry} | {limit} |")
lines += ['', '## 未接続施設', '', '対応定義なし: ' + '、'.join(r['name'] for r in rows if r['registration_status']=='missing') + '。', '',
'定義のみ: ' + '、'.join(r['name'] for r in rows if r['registration_status']=='definition_only') + '。', '',
'## 次の受入', '', '未定義施設は寸法・層だけでなく経済値、人数、業務、通常操作、保存を接続する。登録済み施設は未設定人数とサービス属性、営業時間・停止・経路・客/職員行動を個別検証する。施設追加のたびにこの監査を再実行する。', '',
'再現: `python3 tools/run_facility_audit.py --godot /opt/homebrew/bin/godot --output artifacts/facility-audit-current-2026-09-29`。原本SHA・入力ソースSHA・取得中の変更なしは同ディレクトリのsource-start/end/change.jsonを参照。', '']
(out / 'REPORT.md').write_text('\n'.join(lines))
print(json.dumps(summary, ensure_ascii=False))
if __name__ == '__main__':
main()