docs: pin actual P24-02 IME and cross-process label evidence

This commit is contained in:
Takeshi
2026-10-13 13:28:38 +09:00
parent 82f4ff69e3
commit 0a8d6025cd
8 changed files with 350 additions and 7 deletions
@@ -13,3 +13,5 @@ Claude Opus5.5の記録済み判断で、同じ登録タスク/既存worktreeを
追補r04: 未定義preloadを直した現候補の実import/全GD parseはPASS。最初の局所56/1 failureはobserver人物数と状態遷移数の不一致。条件を下げず同一担当で修正中、残り6スイート未実行。各native source/player/cleanup違反0。大量player明細はrawとgzipを内容保持で退避し、[参照記録](gui-repair-r03/player-guard-after-api-diagnostic.ref.json)に圧縮検証とログ差分を残す。
追補r05: 状態遷移履歴を整合させた新候補はimport/全GD parse/局所・影響7スイート477checks PASS、source/player/cleanup違反0。通常Runner submitで666c87bd7d7c961ae6d7d2ad8d52d6456c31015bに固定。提出後1313全source map差0とcleanを確認し局所証拠の適用を固定。候補branch codex/p24-02-operations-candidate-r03へpush済み、製品mainには未統合。全8通常実証/上流NTR・決闘/最終full/別Opus review/統合・最終は未了で、P24-02正式完了には数えない。
実OS追補: 固定666c87bdの実IME未確定・取消・確定、表示名テストの所有暗号slot保存、別PID80764→83514の通常一覧/通常読込を[限定観測記録](native-os-input-r01/observations.json)へ固定。dialog活性化は診断F8であり通常入口不足は未受入。3行差分案とP24-02追加repair1の回答待ち。全世界状態再開/実NTR・決闘/最終full/review/Windowsは未了のまま。
@@ -0,0 +1,179 @@
{
"candidate": "666c87bd7d7c961ae6d7d2ad8d52d6456c31015b",
"formal_acceptance": false,
"normal_new_save_entry_accepted": false,
"diagnostic_entry": "F8 Root recorder called exact production show_new_save; source unchanged. Not ordinary gameplay entry proof.",
"observed_IME_states": [
{
"ime_text": "t",
"ime_selection": [
1,
0
],
"label_text": "",
"label_focused": true,
"modal_input_blocked": true,
"window_size": [
1280,
720
]
},
{
"ime_text": "テス",
"ime_selection": [
2,
0
],
"label_text": "",
"label_focused": true,
"modal_input_blocked": true,
"window_size": [
1280,
720
]
},
{
"ime_text": "テスト",
"ime_selection": [
3,
0
],
"label_text": "",
"label_focused": true,
"modal_input_blocked": true,
"window_size": [
1280,
720
]
},
{
"ime_text": "てすと",
"ime_selection": [
3,
0
],
"label_text": "",
"label_focused": true,
"modal_input_blocked": true,
"window_size": [
1280,
720
]
},
{
"ime_text": "t",
"ime_selection": [
1,
0
],
"label_text": "",
"label_focused": true,
"modal_input_blocked": true,
"window_size": [
1280,
720
]
}
],
"IME_confirm_actual": true,
"IME_cancel_actual": "Actual Escape twice cleared pending IME before committing next composition; source event sequence retained.",
"saved_display_name": "テスト",
"owned_save_slot": "manual_ui_028e44393fbef29c5cf3482ed611232f",
"save_manifest": {
"path": "/Users/korizatou114/Library/Application Support/TheTree-PCK-tests-39076deb130d4d469b74b4d368bcd7a8-p24-02-os/saves/manual_ui_028e44393fbef29c5cf3482ed611232f/manifest.json",
"bytes": 679,
"sha256": "6581074e2d6e40d4044f50d0d79c6146503f07b1ab3b98370454e5427f801e3c"
},
"window_sizes_observed": [
[
1024,
608
],
[
1280,
720
]
],
"producer_pid": 80764,
"consumer_pid": 83514,
"normal_load_in_consumer": true,
"consumer_loaded_save_path": "user://saves/manual_ui_028e44393fbef29c5cf3482ed611232f",
"all_required_world_state_restart_proven": false,
"source_default_player_cleanup_guard_errors": [],
"raw_refs": [
{
"path": "/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p24-02-native-os-ime-diagnostic-r01/events.jsonl",
"bytes": 7668,
"sha256": "4e27ad89db7f54da6a8c08caf8c47f89c73b894d42a9c761d8d4c7aa26d5bbac"
},
{
"path": "/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p24-02-native-os-ime-diagnostic-r01/ended.json",
"bytes": 727662,
"sha256": "a6d1c802e381ed343f09661ed3bc9a70de94acc819afd6f7f4092f416eab2dbf"
},
{
"path": "/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p24-02-native-os-ime-consumer-r01/events.jsonl",
"bytes": 1511,
"sha256": "5e9b67c80ee545a7a5130e185b8857b2c92b31c336fc177375f6ef14430921c1"
},
{
"path": "/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p24-02-native-os-ime-consumer-r01/ended.json",
"bytes": 727662,
"sha256": "c24d98b8556735dad7dc7d293b43179511adfcc2f70a9203eac906d601ec8121"
}
],
"screenshots": [
{
"path": "/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p24-02-native-os-ime-diagnostic-r01/small-dialog.jpg",
"bytes": 127364,
"sha256": "adf92d912cef52577e6e6421a30556bec6e1dd15a0a321e133b4d7e102187837",
"media_type": "image/jpeg"
},
{
"path": "/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p24-02-native-os-ime-diagnostic-r01/saved-japanese-slot.jpg",
"bytes": 168434,
"sha256": "62a65ee2d437ca1d86bdb9f821ee246e32c9511769a8da5ef50d19a8f5e34beb",
"media_type": "image/jpeg"
},
{
"path": "/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p24-02-native-os-ime-consumer-r01/japanese-slot-after-restart.jpg",
"bytes": 170100,
"sha256": "9d7d0e8480159b12b5b83bed7609843c060b8450533e7dafe52a2e1072ccfd6d",
"media_type": "image/jpeg"
},
{
"path": "/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p24-02-native-os-ime-consumer-r01/ordinary-load-result.jpg",
"bytes": 168163,
"sha256": "c5231b5ea55c380965be34cdedec802fa96b9a9a6f8ed1f52ace25e3c0a6e509",
"media_type": "image/jpeg"
}
],
"remaining": [
"Ordinary new named-save UI entry missing; runtime dialog route is currently migration-only",
"Actual NTR and duel upstream triggers",
"Final full roster, separate Opus review, integration/final",
"Windows same release platform proof"
],
"actual_IME_end_transitions": [
{
"ticks_msec": 98020,
"prior_ime_text": "てすと",
"ime_text": "",
"committed_label_text": "",
"save_path": "user://saves/manual_ui"
},
{
"ticks_msec": 119748,
"prior_ime_text": "t",
"ime_text": "",
"committed_label_text": "テスト",
"save_path": "user://saves/manual_ui"
}
],
"ordinary_entry_missing_source_evidence": {
"file": "scenes/main/main.gd",
"save_button": "_save_manual: direct save",
"new_save_dialog_calls": "only FORMAT_MIGRATION_REQUIRED branch plus internal helper",
"source_sha256": "c375ddabc59771f8ddeaa6464c59c98e58a11cd936aa82dde74645594a9fe4e0"
}
}
@@ -0,0 +1,57 @@
extends SceneTree
var main: Control
var observer: Node
func _initialize() -> void:
call_deferred("_start")
func _start() -> void:
var scene: PackedScene = load("res://scenes/main/main.tscn")
main = scene.instantiate()
root.add_child(main)
observer = PassiveRecorder.new()
observer.main = main
observer.out_dir = OS.get_environment("P24_OS_OUTPUT")
root.add_child(observer)
root.title = "The Tree P24-02 OS candidate 666c87b"
class PassiveRecorder extends Node:
var main: Control
var out_dir: String
var log: FileAccess
var previous: Dictionary = {}
var elapsed := 0.0
var rows := 0
func _ready() -> void:
log = FileAccess.open(out_dir.path_join("events.jsonl"), FileAccess.WRITE)
emit_record("started", {"pid": OS.get_process_id(), "platform": OS.get_name(), "candidate": "666c87bd7d7c961ae6d7d2ad8d52d6456c31015b", "source": "passive_root_diagnostic_not_acceptance"})
func emit_record(kind: String, value: Dictionary) -> void:
if rows >= 10000 or log == null:
return
var record := {"kind": kind, "pid": OS.get_process_id(), "ticks_msec": Time.get_ticks_msec(), "value": value}
log.store_line(JSON.stringify(record))
log.flush()
rows += 1
func _input(event: InputEvent) -> void:
if event is InputEventKey:
if event.pressed and not event.echo and event.keycode == KEY_F8:
emit_record("diagnostic_dialog_entry", {"normal_gameplay_entry": false, "reason": "ordinary entry missing in frozen candidate"})
main.call("_ensure_manual_save_slots").show_new_save(main.get("manual_save_path"))
emit_record("key", {"keycode": event.keycode, "physical_keycode": event.physical_keycode, "unicode": event.unicode, "pressed": event.pressed, "echo": event.echo, "shift": event.shift_pressed, "ctrl": event.ctrl_pressed, "alt": event.alt_pressed, "meta": event.meta_pressed})
elif event is InputEventMouseButton:
emit_record("mouse_button", {"button": event.button_index, "pressed": event.pressed, "position": [event.position.x, event.position.y]})
func _process(delta: float) -> void:
elapsed += delta
if elapsed < 0.1:
return
elapsed = 0.0
var dialog: Variant = main.get("manual_save_slots_dialog")
var label: Variant = dialog.get("_label_edit") if dialog != null else null
var focus: Control = get_viewport().gui_get_focus_owner()
var window: Window = get_tree().root
var state := {"ime_text": DisplayServer.ime_get_text(), "ime_selection": [DisplayServer.ime_get_selection().x, DisplayServer.ime_get_selection().y], "label_text": String(label.text) if label != null else "", "label_rect": [label.get_global_rect().position.x, label.get_global_rect().position.y, label.size.x, label.size.y] if label != null else [], "label_focused": label.has_focus() if label != null else false, "dialog_visible": dialog.visible if dialog != null else false, "window_size": [window.size.x, window.size.y], "window_position": [window.position.x, window.position.y], "content_scale_factor": window.content_scale_factor, "screen_scale": DisplayServer.screen_get_scale(window.current_screen), "focus_name": String(focus.name) if focus != null else "", "save_path": String(main.get("manual_save_path")), "modal_input_blocked": bool(main.get("world_preview").get("modal_input_blocked"))}
if state != previous:
emit_record("state", state)
previous = state.duplicate(true)
func _exit_tree() -> void:
if log != null:
emit_record("observer_exit", {"rows": rows})
log.close()
@@ -0,0 +1,24 @@
--- a/scenes/main/main.gd
+++ b/scenes/main/main.gd
@@ -790,17 +790,19 @@
row.add_child(audio_button)
var load_menu := MenuButton.new()
load_menu.name = "SaveLoadMenu"
- load_menu.text = "読込 ▾"
+ load_menu.text = "保存・読込 ▾"
load_menu.custom_minimum_size = Vector2(64,28)
load_menu.add_theme_font_size_override("font_size",12)
_apply_button_style(load_menu)
load_menu.get_popup().add_item("手動セーブを読み込む",0)
load_menu.get_popup().add_item("旧寸法を修正して読み込む",1)
load_menu.get_popup().add_item("保存スロットを選んで読込",2)
+ load_menu.get_popup().add_item("新しい名前で別スロット保存",3)
load_menu.get_popup().id_pressed.connect(func(id: int):
if id == 0: _load_manual()
elif id == 1: _preview_dimension_migration()
- elif id == 2: _show_manual_load_slots())
+ elif id == 2: _show_manual_load_slots()
+ elif id == 3: _ensure_manual_save_slots().show_new_save(manual_save_path))
row.add_child(load_menu)
palette_scroll = ScrollContainer.new()
@@ -0,0 +1,37 @@
import os,sys,uuid,json,hashlib,importlib.util,subprocess,time
from pathlib import Path
sys.dont_write_bytecode=True
R=Path('/Users/korizatou114/Projects/TheTree-managed-worktrees/p24-02-operations/P24-02-implement')
A=Path('/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02')
OUT=A/'p24-02-native-os-ime-consumer-r01';stage=A/'p24-02-local-native-preflight-r05/stage'
s=importlib.util.spec_from_file_location('p24_os_runner',R/'tools/run_all_tests.py');r=importlib.util.module_from_spec(s);s.loader.exec_module(r)
outer=r.module('p24_os_owned',R/'tools/regression/owned_outer.py');r.require(not outer.native.helper['live_engines'](),'external engine present')
r.require(r.read(stage.parent/'local-ended.json')['passed'],'local7 passed before OS')
r.require(subprocess.check_output(['git','rev-parse','HEAD'],cwd=R,text=True).strip()=='666c87bd7d7c961ae6d7d2ad8d52d6456c31015b','exact immutable candidate')
r.require(not subprocess.check_output(['git','status','--porcelain'],cwd=R,text=True),'candidate clean')
refs=r.source_refs(R);before=r.inventory(stage,stage=True);protected=r.default_player(R);public=(stage/'project.godot').read_bytes()
for n,pin in refs.items():r.require(r.sha(stage/n)==pin['sha256'],'tested stage source exact '+n)
profile=Path('/Users/korizatou114/Library/Application Support/TheTree-PCK-tests-39076deb130d4d469b74b4d368bcd7a8-p24-02-os');profile_name=profile.name;r.require(profile.exists(),"actual producer owned profile exists")
r.require(__import__('shutil').disk_usage(OUT).free>=500*1024*1024,'disk admission')
base={'candidate':'666c87bd7d7c961ae6d7d2ad8d52d6456c31015b','source_map':{n:v['sha256'] for n,v in refs.items()},'stage':str(stage),'profile':str(profile),'owner_pid':os.getpid(),'default_before':protected,'source_before':refs,'formal_acceptance':False,'no_injected_gameplay_or_input':True,'controller_sha256':r.sha(Path(__file__)),'recorder_sha256':r.sha(OUT/'passive_os.gd')}
r.write(OUT/'started.json',base);error=None;children=[];rows=[]
try:
(stage/'project.godot').write_bytes(r.private_project(public,profile_name));env=r.fresh_environment(dict(os.environ));env['P24_OS_OUTPUT']=str(OUT)
argv=['/Applications/Godot.app/Contents/MacOS/Godot','--path',str(stage),'--script',str(OUT/'passive_os.gd')]
with (OUT/'engine-native.log').open('w') as log:
p=subprocess.Popen(argv,cwd=stage,env=env,stdout=log,stderr=subprocess.STDOUT);children.append(p)
r.write(OUT/'process-started.json',dict(base,actual_pid=p.pid,command=argv,monotonic_ns=time.monotonic_ns()))
print(json.dumps({'actual_pid':p.pid,'profile':str(profile),'events':str(OUT/'events.jsonl')}),flush=True)
p.wait(timeout=900)
r.require(p.returncode==0,'ordinary native exit0');raw=(OUT/'engine-native.log').read_text();r.require('ERROR:' not in raw and 'SCRIPT ERROR:' not in raw,'native diagnostics failed')
rows.append({'actual_pid':p.pid,'exit_code':p.returncode,'engine_ended':True,'log_sha256':r.sha(OUT/'engine-native.log')})
except BaseException as e:error=type(e).__name__+': '+str(e)
finally:
cleanup,cleanup_errors=outer.finish_captured(children);(stage/'project.godot').write_bytes(public)
guards=[]
try:
r.require(r.source_refs(R)==refs,'candidate whole source unchanged');r.require(r.inventory(stage,stage=True)==before,'whole stage source unchanged');r.require(r.default_player(R)==protected,'default player unchanged')
except BaseException as e:guards.append(type(e).__name__+': '+str(e))
r.write(OUT/'ended.json',dict(base,rows=rows,error=error,guard_errors=guards,cleanup_errors=cleanup_errors,owned_cleanup=cleanup,passed=not(error or guards or cleanup_errors) and len(rows)==1,formal_acceptance=False))
print(json.dumps({'ended':True,'error':error,'guards':guards,'rows':rows}),flush=True)
sys.exit(1 if error or guards or cleanup_errors else 0)
@@ -0,0 +1,37 @@
import os,sys,uuid,json,hashlib,importlib.util,subprocess,time
from pathlib import Path
sys.dont_write_bytecode=True
R=Path('/Users/korizatou114/Projects/TheTree-managed-worktrees/p24-02-operations/P24-02-implement')
A=Path('/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02')
OUT=A/'p24-02-native-os-ime-diagnostic-r01';stage=A/'p24-02-local-native-preflight-r05/stage'
s=importlib.util.spec_from_file_location('p24_os_runner',R/'tools/run_all_tests.py');r=importlib.util.module_from_spec(s);s.loader.exec_module(r)
outer=r.module('p24_os_owned',R/'tools/regression/owned_outer.py');r.require(not outer.native.helper['live_engines'](),'external engine present')
r.require(r.read(stage.parent/'local-ended.json')['passed'],'local7 passed before OS')
r.require(subprocess.check_output(['git','rev-parse','HEAD'],cwd=R,text=True).strip()=='666c87bd7d7c961ae6d7d2ad8d52d6456c31015b','exact immutable candidate')
r.require(not subprocess.check_output(['git','status','--porcelain'],cwd=R,text=True),'candidate clean')
refs=r.source_refs(R);before=r.inventory(stage,stage=True);protected=r.default_player(R);public=(stage/'project.godot').read_bytes()
for n,pin in refs.items():r.require(r.sha(stage/n)==pin['sha256'],'tested stage source exact '+n)
nonce=uuid.uuid4().hex;profile_name='TheTree-PCK-tests-'+nonce+'-p24-02-os';profile=r.profile_path(profile_name);r.require(not profile.exists(),'fresh private profile');profile.mkdir()
r.require(__import__('shutil').disk_usage(OUT).free>=500*1024*1024,'disk admission')
base={'candidate':'666c87bd7d7c961ae6d7d2ad8d52d6456c31015b','source_map':{n:v['sha256'] for n,v in refs.items()},'stage':str(stage),'profile':str(profile),'owner_pid':os.getpid(),'default_before':protected,'source_before':refs,'formal_acceptance':False,'no_injected_gameplay_or_input':True,'controller_sha256':r.sha(Path(__file__)),'recorder_sha256':r.sha(OUT/'passive_os.gd')}
r.write(OUT/'started.json',base);error=None;children=[];rows=[]
try:
(stage/'project.godot').write_bytes(r.private_project(public,profile_name));env=r.fresh_environment(dict(os.environ));env['P24_OS_OUTPUT']=str(OUT)
argv=['/Applications/Godot.app/Contents/MacOS/Godot','--path',str(stage),'--script',str(OUT/'passive_os.gd')]
with (OUT/'engine-native.log').open('w') as log:
p=subprocess.Popen(argv,cwd=stage,env=env,stdout=log,stderr=subprocess.STDOUT);children.append(p)
r.write(OUT/'process-started.json',dict(base,actual_pid=p.pid,command=argv,monotonic_ns=time.monotonic_ns()))
print(json.dumps({'actual_pid':p.pid,'profile':str(profile),'events':str(OUT/'events.jsonl')}),flush=True)
p.wait(timeout=900)
r.require(p.returncode==0,'ordinary native exit0');raw=(OUT/'engine-native.log').read_text();r.require('ERROR:' not in raw and 'SCRIPT ERROR:' not in raw,'native diagnostics failed')
rows.append({'actual_pid':p.pid,'exit_code':p.returncode,'engine_ended':True,'log_sha256':r.sha(OUT/'engine-native.log')})
except BaseException as e:error=type(e).__name__+': '+str(e)
finally:
cleanup,cleanup_errors=outer.finish_captured(children);(stage/'project.godot').write_bytes(public)
guards=[]
try:
r.require(r.source_refs(R)==refs,'candidate whole source unchanged');r.require(r.inventory(stage,stage=True)==before,'whole stage source unchanged');r.require(r.default_player(R)==protected,'default player unchanged')
except BaseException as e:guards.append(type(e).__name__+': '+str(e))
r.write(OUT/'ended.json',dict(base,rows=rows,error=error,guard_errors=guards,cleanup_errors=cleanup_errors,owned_cleanup=cleanup,passed=not(error or guards or cleanup_errors) and len(rows)==1,formal_acceptance=False))
print(json.dumps({'ended':True,'error':error,'guards':guards,'rows':rows}),flush=True)
sys.exit(1 if error or guards or cleanup_errors else 0)