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.
This commit is contained in:
Takeshi
2026-09-30 23:52:24 +09:00
parent 6077e02ebe
commit 5dde0c5ec2
490 changed files with 54051 additions and 809 deletions
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""Evidence inventory for P22; file presence never certifies production readiness."""
import hashlib
import json
from pathlib import Path
import re
import struct
ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / 'docs/plans/original-completion-2026-09-29/execution/assets'
OUT.mkdir(parents=True, exist_ok=True)
def digest(p):
return hashlib.sha256(p.read_bytes()).hexdigest()
def save(name, data):
(OUT/name).write_text(json.dumps(data, ensure_ascii=False, indent=2)+'\n')
references = {}
for folder in ('scripts', 'scenes', 'autoload', 'data', 'assets'):
for source in (ROOT/folder).rglob('*'):
if source.suffix not in ('.gd', '.tres', '.tscn') or not source.is_file():
continue
for path in set(re.findall(r'res://([^"\s]+\.(?:png|svg|jpg|jpeg|webp|ogg|wav|mp3|ttf|otf))', source.read_text())):
references.setdefault(path, []).append(str(source.relative_to(ROOT)))
media = []
for p in sorted((ROOT/'assets').rglob('*')):
if not p.is_file() or p.suffix.lower() not in ('.png','.svg','.jpg','.jpeg','.webp','.ogg','.wav','.mp3','.ttf','.otf'):
continue
path = str(p.relative_to(ROOT))
row = {'path':path, 'bytes':p.stat().st_size, 'sha256':digest(p), 'literal_references':references.get(path, []), 'acceptance':'not_reviewed', 'provenance':'unverified'}
if p.suffix.lower() == '.png':
header = p.read_bytes()[:24]
if header[:8] == b'\x89PNG\r\n\x1a\n':
row['width'],row['height'] = struct.unpack('>II',header[16:24])
if path.startswith('assets/mockups/'):
row['purpose']='reference_mockup_not_shipping_acceptance'
elif path.startswith('assets/kenney/'):
pack = p.relative_to(ROOT/'assets/kenney').parts[0]
license_path = ROOT/'assets/kenney'/pack/'License.txt'
row.update(provenance='Kenney', creator='Kenney', license=str(license_path.relative_to(ROOT)), license_present=license_path.exists())
if license_path.exists():
row['license_sha256']=digest(license_path)
row['license_declares_cc0']='CC0' in license_path.read_text()
else:
row['purpose']='existing_theme_or_icon_requires_source_and_quality_review'
media.append(row)
save('media_inventory.json', media)
facility_mapping = ROOT/'docs/plans/original-completion-2026-09-29/execution/original_facility_mapping.json'
facilities = json.loads(facility_mapping.read_text())
renderer = ROOT/'scripts/ui/pixel_tower_art.gd'
text = renderer.read_text()
slots = []
for index,f in enumerate(facilities,1):
key = f['definition_id'] or f'original_facility_{index:02}'
for level in range(1,4):
for layer in ('front','back'):
for surface in ('exterior','interior'):
slots.append({'slot_id':f'{key}.lv{level}.{layer}.{surface}', 'requirement':['B-13.3',f"B-{f['section']}"], 'facility_name':f['name'], 'definition_id':f['definition_id'], 'level':level, 'layer':layer, 'surface':surface, 'original_placement':f['original_layer_zone'], 'creator':'unassigned', 'status':'unverified_required_slot', 'approved_asset':None, 'existing_candidate':str(renderer.relative_to(ROOT)) if f['definition_id'] and ('&"'+f['definition_id']+'"') in text else None, 'note':'Code token is only a search lead; it does not prove a dedicated level/layer/surface variant. These are usage slots, not a committed image count.'})
save('facility_usage_slots.json',slots)
people = [('anri','アンリ'),('fine','フィーネ'),('tomari','トマリ'),('amelia','アメリア'),('liz','リズ'),('mimi','ミミ'),('satsuki','サツキ')]
character_slots = []
for key,name in people:
for kind in ('portrait_base','portrait_expression','portrait_costume','portrait_damage','portrait_dirt','sd_idle','sd_walk','sd_work','sd_collapsed','sd_carry','sd_rescue','sd_rest'):
character_slots.append({'slot_id':f'heroine.{key}.{kind}', 'character':name, 'requirement':['B-6.2','B-13.1' if kind.startswith('portrait') else 'B-13.2'], 'kind':kind, 'required_variant_count':1 if kind=='portrait_base' else None, 'creator':'unassigned', 'status':'unverified_required_slot', 'approved_asset':None, 'candidate':'scripts/ui/pixel_people.gd' if kind.startswith('sd_') else None, 'note':'Generic procedural poses do not prove character-specific appearance or role-specific animation.'})
save('character_usage_slots.json',character_slots)
other = [
('background.front','B-13.3','scenes/main/world_preview.gd'),('background.back','B-13.3','scenes/main/world_preview.gd'),
('ui.front_skin','B-13.4','assets/themes/japanese_fantasy/visual-theme-v1.tres'),('ui.back_skin','B-13.4','assets/themes/japanese_fantasy/visual-theme-v1.tres'),
('ui.emotion_graph','B-13.4',None),('ui.camera_overlay','B-13.4',None),
('effect.shield','B-13.2',None),('effect.smoke','B-13.2',None),('effect.mana_light','B-13.2',None),
('audio.front_bgm','B-13.5',None),('audio.back_arrangement','B-13.5',None),('audio.front_ambience','B-13.5',None),('audio.back_ambience','B-13.5',None),('audio.ui','B-13.5',None),('audio.character_voice','B-13.5',None),
('other_characters','B-13.1',None),('restricted_content_unproduced','B-13.1',None)]
save('other_usage_slots.json',[{'slot_id':k,'requirement':(['B-12.3','B-13.1','B-13.2','B-13.5'] if k=='restricted_content_unproduced' else [req]),'candidate':candidate,'status':'not_produced_outside_current_content_scope' if k=='restricted_content_unproduced' else 'unverified_required_slot','creator':'unassigned','required_variant_count':None,'approved_asset':None} for k,req,candidate in other])
summary = {'facility_mapping':str(facility_mapping.relative_to(ROOT)), 'facility_mapping_sha256':digest(facility_mapping), 'baseline_sha256':digest(ROOT/'docs/traceability/original/BASELINE.txt'), 'media_files':len(media), 'literal_referenced_media_files':sum(bool(x['literal_references']) for x in media), 'audio_files':sum(Path(x['path']).suffix in ('.wav','.ogg','.mp3') for x in media), 'facility_count':len(facilities), 'facility_usage_slots':len(slots), 'named_heroines':len(people), 'character_usage_slot_groups':len(character_slots), 'other_slot_groups':len(other), 'approved_assets':0, 'missing_literal_references':[p for p in references if not (ROOT/p).exists()], 'scope':'Inventory and candidate mappings only. No asset is promoted to complete by file existence, procedural fallback, or a passing gameplay test. Variant counts and production ownership remain unresolved.'}
save('inventory_summary.json',summary)
print(json.dumps(summary,ensure_ascii=False))
+23
View File
@@ -0,0 +1,23 @@
extends SceneTree
func _initialize() -> void: call_deferred("run")
func run() -> void:
root.get_node("GameClock").set_paused(true)
var runtime: Variant = root.get_node("SimulationRuntime")
var original: Array = JSON.parse_string(FileAccess.get_file_as_string("res://docs/plans/original-completion-2026-09-29/execution/original_facility_mapping.json"))
var palette: Array = load("res://scenes/main/main.gd").MVP_FACILITY_IDS
var rows: Array = []
for raw: Dictionary in original:
var id := StringName(raw["definition_id"] if raw["definition_id"] != null else "")
var definition: FacilityDefinition = FacilityCatalog.by_id(id) if not id.is_empty() else null
var row := {"section":raw["section"], "name":raw["name"], "definition_id":String(id), "original_size":raw["original_size"], "original_layer_zone":raw["original_layer_zone"], "runtime_registered":runtime.definition_registry.has_facility(id), "palette_listed":id in palette, "definition":null}
if definition != null:
row["definition"] = {"path":definition.resource_path, "display_name":definition.display_name, "footprint":[definition.footprint.x,definition.footprint.y], "layer":FacilityDefinition.ViewLayer.keys()[definition.layer], "zone":FacilityDefinition.MapZone.keys()[definition.zone], "customer_capacity":definition.customer_capacity, "capacity":definition.capacity, "staff_min":definition.staff_min,"staff_max":definition.staff_max, "required_roles":definition.required_roles, "service_attribute":String(definition.service_attribute), "maintenance_gold":definition.maintenance_gold, "faith_per_service":definition.faith_per_service}
rows.append(row)
var output := OS.get_environment("THE_TREE_FACILITY_AUDIT_PATH")
if output.is_empty():
push_error("THE_TREE_FACILITY_AUDIT_PATH is required"); quit(2); return
var file := FileAccess.open(output,FileAccess.WRITE)
if file == null:
push_error("Cannot write audit output"); quit(2); return
file.store_string(JSON.stringify({"normal_registry_ids":runtime.definition_registry.get_facility_ids(),"palette_ids":palette,"original_facilities":rows},"\t")+"\n");file.close()
print("Exported facility audit rows (not acceptance PASS): ", rows.size());quit(0)
+1
View File
@@ -0,0 +1 @@
uid://b5uppfknx478a
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""Run every source test, retain logs and continue past failures.
The exported-package test needs a built PCK and runs via make test-exported-opening.
"""
import argparse
import json
from pathlib import Path
import subprocess
import time
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output', type=Path, default=Path('artifacts/all-tests'))
parser.add_argument('--godot', default='godot')
parser.add_argument('--timeout', type=int, default=1200)
args = parser.parse_args()
root = Path(__file__).resolve().parents[1]
output = args.output.resolve()
output.mkdir(parents=True, exist_ok=True)
paths = [root / 'tests/run_tests.gd'] + sorted((root / 'tests').rglob('test*.gd'))
results = []
for path in paths:
relative = path.relative_to(root)
if 'export' in relative.parts or relative.as_posix() == 'tests/s0/test_kernel_and_save.gd':
# The RefCounted kernel module is already invoked by run_tests.gd.
continue
start = time.monotonic()
name = str(relative).replace('/', '__') + '.log'
# Stream to disk so long-running scenarios expose progress immediately.
with (output / name).open('w') as log:
try:
run = subprocess.run(
[args.godot, '--headless', '--path', str(root), '--script', 'res://' + str(relative)],
cwd=root, stdout=log, stderr=subprocess.STDOUT, timeout=args.timeout,
)
code = run.returncode
except subprocess.TimeoutExpired:
code = 124
content = (output / name).read_text()
failed = code != 0 or 'SCRIPT ERROR:' in content or '\nERROR:' in content
results.append(dict(test=str(relative), exit_code=code, failed=failed,
seconds=round(time.monotonic() - start, 2), log=name))
(output / 'all-tests.json').write_text(json.dumps(results, indent=2) + '\n')
print(f'{"FAIL" if failed else "PASS"} {relative}', flush=True)
failed = sum(result['failed'] for result in results)
print(f'{len(results)} tests, {failed} failures; logs: {output}')
return 1 if failed else 0
if __name__ == '__main__':
raise SystemExit(main())
+85
View File
@@ -0,0 +1,85 @@
#!/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()
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
"""Debug a naturally opened inn through three closes and an actual process restart."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import subprocess
import time
import uuid
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--godot', default='godot')
parser.add_argument('--rendered', action='store_true')
parser.add_argument('--output', type=Path, default=Path('artifacts/opening-restart-flow'))
args = parser.parse_args()
root = Path(__file__).resolve().parents[1]
output = args.output.resolve()
output.mkdir(parents=True, exist_ok=True)
env = dict(os.environ, THE_TREE_OPENING_RESTART_SLOT=f'user://tests/opening_restart_{uuid.uuid4().hex}/manual', THE_TREE_OPENING_RESTART_OUTPUT=str(output))
env.pop('THE_TREE_OPENING_CAPTURE', None)
command = [args.godot] + ([] if args.rendered else ['--headless']) + ['--path', str(root), '--script', 'res://tests/ui/fixtures/opening_restart_flow.gd']
source_paths = [p for directory in ('autoload', 'scripts', 'scenes', 'data', 'tests') for p in (root/directory).rglob('*') if p.suffix in ('.gd', '.tres', '.tscn', '.json')]
hashes = {str(p.relative_to(root)): hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted(source_paths)}
(output/'source-start.json').write_text(json.dumps(hashes, indent=2)+'\n')
results = []
for phase in ('write', 'read'):
started = time.monotonic()
log_path = output/f'{phase}.log'
with log_path.open('w') as log:
try:
code = subprocess.run(command + (['--', '--read'] if phase == 'read' else []), cwd=root, env=env, stdout=log, stderr=subprocess.STDOUT, timeout=1200).returncode
except subprocess.TimeoutExpired:
code = 124
content = log_path.read_text()
passed = code == 0 and '0 failures' in content and 'ERROR:' not in content
results.append(dict(phase=phase, exit_code=code, passed=passed, seconds=round(time.monotonic()-started, 2), log=log_path.name))
(output/'result.json').write_text(json.dumps(results, indent=2)+'\n')
print(json.dumps(results[-1]), flush=True)
if not passed:
return 1
changed = [p for p,h in hashes.items() if not (root/p).exists() or hashlib.sha256((root/p).read_bytes()).hexdigest()!=h]
(output/'source-change.json').write_text(json.dumps(changed, indent=2)+'\n')
return 1 if changed else 0
if __name__ == '__main__':
raise SystemExit(main())
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""Verify closed-facility upkeep across two independent Godot processes."""
import argparse
import json
import os
from pathlib import Path
import subprocess
import time
import uuid
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--godot', default='godot')
parser.add_argument('--rendered', action='store_true')
parser.add_argument('--output', type=Path, default=Path('artifacts/upkeep-ui-flow'))
args = parser.parse_args()
root = Path(__file__).resolve().parents[1]
output = args.output.resolve()
output.mkdir(parents=True, exist_ok=True)
env = os.environ.copy()
env['THE_TREE_UPKEEP_UI_SAVE'] = f'user://tests/upkeep_ui_{uuid.uuid4().hex}/manual'
base = [args.godot] + ([] if args.rendered else ['--headless']) + ['--path', str(root), '--script', 'res://tests/ui/test_upkeep_closed_ui.gd']
results = []
for phase in ('write', 'read'):
start = time.monotonic()
log_path = output / f'{phase}.log'
with log_path.open('w') as log:
try:
code = subprocess.run(base + (['--', '--read'] if phase == 'read' else []), cwd=root, env=env, stdout=log, stderr=subprocess.STDOUT, timeout=600).returncode
except subprocess.TimeoutExpired:
code = 124
content = log_path.read_text()
passed = code == 0 and 'PASS:' in content and 'ERROR:' not in content
results.append({'phase':phase, 'exit_code':code, 'passed':passed, 'seconds':round(time.monotonic()-start,2), 'log':str(log_path)})
(output/'result.json').write_text(json.dumps(results, indent=2)+'\n')
print(json.dumps(results[-1]), flush=True)
if not passed:
raise SystemExit(1)