P28-02-DIAG: 意味を保つ軽量化:実測上位の特定(受動的な診断)
This commit is contained in:
@@ -81,3 +81,11 @@ bundle 作成後にファイルを追加・編集すると inventory 不一致
|
|||||||
|
|
||||||
原本加算は0、正式完了23/88、4/30、予算 false 6件を保持します。P27 完了は主張しません。
|
原本加算は0、正式完了23/88、4/30、予算 false 6件を保持します。P27 完了は主張しません。
|
||||||
repair3: 最終の局所 unit を再実行して PASS(19件、0.780秒)。
|
repair3: 最終の局所 unit を再実行して PASS(19件、0.780秒)。
|
||||||
|
|
||||||
|
## repair4: native parse の静的依存修正
|
||||||
|
|
||||||
|
- P1: probe の manifest version は、実際の Runtime の `get_script().get_script_constant_map()` から準備段階で取得します。script・key の存在と int 型を確認し、取得した値そのものを manifest 比較の期待値へ渡します。取得不能なら理由を errors に記録して終了します。値の固定や検査対象 manifest の流用はありません。
|
||||||
|
- P2: `autoload/*.gd` の class_name 宣言を確認し、probe の該当する静的参照は除去した1箇所だけでした。残る GameStateScript の preload 先、MvpContentCatalog と GuestDefinition には autoload 識別子への依存がありません。engine による compile 成功は未確認です。
|
||||||
|
- P3: checker は autoload の class_name を製品ファイルから機械的に抽出し、probe の識別子との一致を拒否します。公開読取の get_script / get_script_constant_map を既存の許可リストに追加しました。修正後 probe の通過、全抽出名の定数参照・型注釈の拒否、新しい宣言の自動検出を最小の追加 unit で確認しました。
|
||||||
|
- `python3 test_diag.py`: 20件 PASS、0.861秒。標準出力・標準エラーの生出力を UNIT_OUTPUT.txt に保存しました。変更は diag_probe.gd、check_diag.py、test_diag.py、UNIT_OUTPUT.txt、IMPLEMENTATION.md のみ。BLOCKED.md は解消済みの履歴として維持します。
|
||||||
|
- native import・parse・3 PID、正式 validation、独立レビューは提出後の Root 側で実施します。worker は engine・Runner 操作、git 書込み、製品変更、全回帰、他エージェント起動を行っていません。原本加算0、23/88、4/30、予算 false 6件を維持します。
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
test_autoload_class_references_rejected (__main__.DiagnosticTests) ... ok
|
||||||
test_bundle_missing_and_tampered (__main__.DiagnosticTests) ... ok
|
test_bundle_missing_and_tampered (__main__.DiagnosticTests) ... ok
|
||||||
test_connections_and_preparation (__main__.DiagnosticTests) ... ok
|
test_connections_and_preparation (__main__.DiagnosticTests) ... ok
|
||||||
test_cross_pid_only_named_kernel_fields (__main__.DiagnosticTests) ... ok
|
test_cross_pid_only_named_kernel_fields (__main__.DiagnosticTests) ... ok
|
||||||
@@ -8,7 +9,7 @@ test_missing_not_zero (__main__.DiagnosticTests) ... ok
|
|||||||
test_nonadditive_scope (__main__.DiagnosticTests) ... ok
|
test_nonadditive_scope (__main__.DiagnosticTests) ... ok
|
||||||
test_pid_local_catalog_identity (__main__.DiagnosticTests) ... ok
|
test_pid_local_catalog_identity (__main__.DiagnosticTests) ... ok
|
||||||
test_queries_and_observer_rejection (__main__.DiagnosticTests) ... ok
|
test_queries_and_observer_rejection (__main__.DiagnosticTests) ... ok
|
||||||
test_real_owned_python_child_and_raw_binding (__main__.DiagnosticTests) ... RUNTIME_SCALE_PROCESS_START {"name": "import", "pid": 62682}
|
test_real_owned_python_child_and_raw_binding (__main__.DiagnosticTests) ... RUNTIME_SCALE_PROCESS_START {"name": "import", "pid": 70915}
|
||||||
ok
|
ok
|
||||||
test_scratch_ownership (__main__.DiagnosticTests) ... ok
|
test_scratch_ownership (__main__.DiagnosticTests) ... ok
|
||||||
test_service_snapshot_inventory_matches_current_product (__main__.DiagnosticTests) ... ok
|
test_service_snapshot_inventory_matches_current_product (__main__.DiagnosticTests) ... ok
|
||||||
@@ -20,6 +21,6 @@ test_tick_id_and_minute_rejection (__main__.DiagnosticTests) ... ok
|
|||||||
test_valid_raw_intervals (__main__.DiagnosticTests) ... ok
|
test_valid_raw_intervals (__main__.DiagnosticTests) ... ok
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Ran 19 tests in 0.780s
|
Ran 20 tests in 0.861s
|
||||||
|
|
||||||
OK
|
OK
|
||||||
|
|||||||
@@ -120,10 +120,20 @@ def check_modules(modules=None, root=ROOT):
|
|||||||
"loaded project module outside closure")
|
"loaded project module outside closure")
|
||||||
|
|
||||||
|
|
||||||
def check_probe(text):
|
def autoload_class_names(root=ROOT):
|
||||||
|
paths = sorted((root / 'autoload').glob('*.gd'))
|
||||||
|
require(paths, 'autoload scripts missing')
|
||||||
|
return {name for path in paths for name in
|
||||||
|
re.findall(r'^\s*class_name\s+([A-Za-z_][A-Za-z_0-9]*)\b', path.read_text(), re.M)}
|
||||||
|
|
||||||
|
|
||||||
|
def check_probe(text, root=ROOT):
|
||||||
"""Conservative finite call policy, plus mutation-site checks; native parse is separate."""
|
"""Conservative finite call policy, plus mutation-site checks; native parse is separate."""
|
||||||
code = '\n'.join(line.split('#', 1)[0] for line in text.splitlines())
|
code = '\n'.join(line.split('#', 1)[0] for line in text.splitlines())
|
||||||
tokens = re.sub(r'"(?:\\.|[^"\\])*"', '""', code)
|
tokens = re.sub(r'"(?:\\.|[^"\\])*"', '""', code)
|
||||||
|
identifiers = set(re.findall(r'\b[A-Za-z_][A-Za-z_0-9]*\b', tokens))
|
||||||
|
require(not identifiers.intersection(autoload_class_names(root)),
|
||||||
|
'autoload class_name reference in probe')
|
||||||
forbidden = (r'\b(?:set_script|set_process|set_physics_process|set_deferred|callv?|call_thread_safe|'
|
forbidden = (r'\b(?:set_script|set_process|set_physics_process|set_deferred|callv?|call_thread_safe|'
|
||||||
r'call_deferred_thread_group|emit_signal|subscribe|unsubscribe|initialize|reset|'
|
r'call_deferred_thread_group|emit_signal|subscribe|unsubscribe|initialize|reset|'
|
||||||
r'clear_performance_reports|register_tick_handler|unregister_tick_handler|'
|
r'clear_performance_reports|register_tick_handler|unregister_tick_handler|'
|
||||||
@@ -153,7 +163,7 @@ def check_probe(text):
|
|||||||
require('set_paused(false)' not in code, 'resume forbidden')
|
require('set_paused(false)' not in code, 'resume forbidden')
|
||||||
for receiver, allowed in {
|
for receiver, allowed in {
|
||||||
'clock': {'get', 'set_paused', 'advance_minutes'},
|
'clock': {'get', 'set_paused', 'advance_minutes'},
|
||||||
'runtime': {'get', 'create_snapshot', 'restore_snapshot', 'get_save_policy'},
|
'runtime': {'get', 'get_script', 'create_snapshot', 'restore_snapshot', 'get_save_policy'},
|
||||||
'main': {'get'}, 'target': {'create_snapshot'},
|
'main': {'get'}, 'target': {'create_snapshot'},
|
||||||
'SaveService': {'save_snapshot', 'load_snapshot'},
|
'SaveService': {'save_snapshot', 'load_snapshot'},
|
||||||
'SimulationSnapshot': {'validate', 'restore'},
|
'SimulationSnapshot': {'validate', 'restore'},
|
||||||
@@ -185,7 +195,8 @@ def check_probe(text):
|
|||||||
'get_ticks_usec', 'get_ticks_msec', 'advance_minutes', 'save_snapshot', 'validate', 'restore',
|
'get_ticks_usec', 'get_ticks_msec', 'advance_minutes', 'save_snapshot', 'validate', 'restore',
|
||||||
'load_snapshot', 'free', 'merge', 'get_node', 'get_environment', 'get_process_id', 'parse_string',
|
'load_snapshot', 'free', 'merge', 'get_node', 'get_environment', 'get_process_id', 'parse_string',
|
||||||
'set_paused', 'get_sha256', 'open', 'get_var', 'close', 'restore_snapshot', 'connect',
|
'set_paused', 'get_sha256', 'open', 'get_var', 'close', 'restore_snapshot', 'connect',
|
||||||
'instantiate', 'add_child', 'get_save_policy', 'snapshot', 'disconnect', 'emit'
|
'instantiate', 'add_child', 'get_save_policy', 'snapshot', 'disconnect', 'emit',
|
||||||
|
'get_script', 'get_script_constant_map'
|
||||||
}, 'unapproved member call')
|
}, 'unapproved member call')
|
||||||
for path in re.findall(r'"(user://[^"\n]*)"', code):
|
for path in re.findall(r'"(user://[^"\n]*)"', code):
|
||||||
require(path.startswith('user://p28_diag/scratch/'), 'nonprivate scratch path')
|
require(path.startswith('user://p28_diag/scratch/'), 'nonprivate scratch path')
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const K_FIELDS := ["absolute_minute", "speed", "paused", "snapshot_version", "_s
|
|||||||
"_pause_request_serial", "_abort_current_tick_requested", "_last_tick_advanced"]
|
"_pause_request_serial", "_abort_current_tick_requested", "_last_tick_advanced"]
|
||||||
var clock: Variant
|
var clock: Variant
|
||||||
var runtime: Variant
|
var runtime: Variant
|
||||||
|
var manifest_version: Variant
|
||||||
var main: Variant
|
var main: Variant
|
||||||
var report: Dictionary = {}
|
var report: Dictionary = {}
|
||||||
var ticks: Array = []
|
var ticks: Array = []
|
||||||
@@ -151,7 +152,7 @@ func comparison(before: Dictionary, after: Dictionary) -> Dictionary:
|
|||||||
var guest_ids: Array[String] = []
|
var guest_ids: Array[String] = []
|
||||||
for definition: GuestDefinition in catalog.build_all_guest_definitions():
|
for definition: GuestDefinition in catalog.build_all_guest_definitions():
|
||||||
guest_ids.append(String(definition.guest_type_id))
|
guest_ids.append(String(definition.guest_type_id))
|
||||||
var expected := {&"manifest_version": SimulationRuntimeService.MVP_CONTENT_MANIFEST_VERSION,
|
var expected := {&"manifest_version": manifest_version,
|
||||||
&"schema_version": MvpContentCatalog.SCHEMA_VERSION,
|
&"schema_version": MvpContentCatalog.SCHEMA_VERSION,
|
||||||
&"guest_class_ids": string_names(catalog.get_guest_class_ids()),
|
&"guest_class_ids": string_names(catalog.get_guest_class_ids()),
|
||||||
&"species_ids": string_names(catalog.get_species_ids()),
|
&"species_ids": string_names(catalog.get_species_ids()),
|
||||||
@@ -323,6 +324,21 @@ func begin() -> void:
|
|||||||
var k0 := kernel_state()
|
var k0 := kernel_state()
|
||||||
clock.set_paused(true)
|
clock.set_paused(true)
|
||||||
report.preparation.append({"operation": "initial_pause", "K_before": k0, "K_after": kernel_state()})
|
report.preparation.append({"operation": "initial_pause", "K_before": k0, "K_after": kernel_state()})
|
||||||
|
var runtime_script: Variant = runtime.get_script()
|
||||||
|
if runtime_script == null:
|
||||||
|
failed.append("Runtime script unavailable for manifest version")
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
var constants: Dictionary = runtime_script.get_script_constant_map()
|
||||||
|
if not constants.has("MVP_CONTENT_MANIFEST_VERSION"):
|
||||||
|
failed.append("Runtime constant MVP_CONTENT_MANIFEST_VERSION missing")
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
if typeof(constants["MVP_CONTENT_MANIFEST_VERSION"]) != TYPE_INT:
|
||||||
|
failed.append("Runtime constant MVP_CONTENT_MANIFEST_VERSION must be int")
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
manifest_version = constants["MVP_CONTENT_MANIFEST_VERSION"]
|
||||||
var path := OS.get_environment("THE_TREE_P28_FIXTURE")
|
var path := OS.get_environment("THE_TREE_P28_FIXTURE")
|
||||||
report["input_sha256"] = FileAccess.get_sha256(path)
|
report["input_sha256"] = FileAccess.get_sha256(path)
|
||||||
if report.input_sha256 != OS.get_environment("THE_TREE_P28_FIXTURE_SHA"):
|
if report.input_sha256 != OS.get_environment("THE_TREE_P28_FIXTURE_SHA"):
|
||||||
|
|||||||
@@ -226,6 +226,26 @@ class DiagnosticTests(unittest.TestCase):
|
|||||||
with self.subTest(mutation=mutation[-100:]), self.assertRaises(ValueError):
|
with self.subTest(mutation=mutation[-100:]), self.assertRaises(ValueError):
|
||||||
check.check_probe(mutation)
|
check.check_probe(mutation)
|
||||||
|
|
||||||
|
def test_autoload_class_references_rejected(self):
|
||||||
|
source = (check.HERE / 'diag_probe.gd').read_text()
|
||||||
|
check.check_probe(source)
|
||||||
|
names = check.autoload_class_names()
|
||||||
|
self.assertTrue(names)
|
||||||
|
for name in names:
|
||||||
|
for statement in (f'var dependency = {name}.MVP_CONTENT_MANIFEST_VERSION',
|
||||||
|
f'var dependency: {name}'):
|
||||||
|
with self.subTest(statement=statement), self.assertRaisesRegex(
|
||||||
|
ValueError, 'autoload class_name reference'):
|
||||||
|
check.check_probe(source + '\nfunc illegal():\n\t' + statement + '\n')
|
||||||
|
# Discover new product declarations without maintaining a name list.
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
root = Path(directory)
|
||||||
|
(root / 'autoload').mkdir()
|
||||||
|
(root / 'autoload/new_service.gd').write_text('class_name NewAutoloadService\nextends Node\n')
|
||||||
|
self.assertEqual(check.autoload_class_names(root), {'NewAutoloadService'})
|
||||||
|
with self.assertRaisesRegex(ValueError, 'autoload class_name reference'):
|
||||||
|
check.check_probe(source + '\nvar dependency: NewAutoloadService\n', root)
|
||||||
|
|
||||||
def test_service_snapshot_inventory_matches_current_product(self):
|
def test_service_snapshot_inventory_matches_current_product(self):
|
||||||
source = (check.ROOT / 'autoload/simulation_runtime.gd').read_text()
|
source = (check.ROOT / 'autoload/simulation_runtime.gd').read_text()
|
||||||
body = source.split('func create_snapshot() -> Dictionary:', 1)[1].split('\nfunc restore_snapshot', 1)[0]
|
body = source.split('func create_snapshot() -> Dictionary:', 1)[1].split('\nfunc restore_snapshot', 1)[0]
|
||||||
|
|||||||
Reference in New Issue
Block a user