Files
settled-reach/client/tools/parse_sweep.gd
T
jpmschweitzerandClaude Opus 5.5 4537b71b92 refactor(tooling): T-1290 — the wiki domain, and the renderer that must not run
`reach wiki stats` and `reach wiki gttr-hook` replace tooling/db/wiki_sync.py
and populate_gttr_hook.py. Both are output-identical to the originals:
`stats` byte-for-byte, and all 301 extracted GTTR hooks line-for-line.

wiki_sync.py moved whole, but generate_wiki() and import_from_wiki() are NOT
verbs. Before porting, the old `--generate` was run against a clean tree to get
a parity baseline. It changed all 301 system pages, +940 / -10,761, and was
reverted at once. It deletes the Celestial Bodies / Stations blocks (owned by
the Rust atlas sync, which it does not know about), deletes the
Industries / Exports / Imports rows (nothing writes those any more), and
rewrites star types where systems.db and the pages disagree. D-262, CLAUDE.md
and the wiki skill all described it as the routine, prose-preserving render.
CLAUDE.md and the skill now say not to run it; D-262 needs amending — T-1292.

Provenance moves to tooling/archive/, with a README naming what each script
did and why it is not run:

- pql-migrate/ (the T-1271 ruling)
- wiki-bootstrap/: assign-astro-ids + its catalog, migrate-s-to-gj,
  patch-core-sector (hardcodes a dead path), fill-missing-globes,
  generate-stubs and find-stubs (finds 0 stubs — Phase 1 is done),
  backfill_cultural_corridor (a raw systems.db patch script, outside D-262),
  and process-wiki-system-changes, whose last step is the destructive render

Also:

- stats() printed "run import first" and exited 0 when a table was missing;
  it now fails with a remedy. generate_wiki() counted created pages after
  writing them, so `created` was always 0.
- tooling/godot-cold-parse and godot-parse-sweep were never retired after
  T-1283, and the pr-process skill still told agents to run them. Removed;
  the skill and parse_sweep.gd now name the reach verbs.
- systems.db re-stamped: schema comments changed, and the stamp records the
  schema file's SHA for tamper detection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 16:25:51 +02:00

90 lines
3.8 KiB
GDScript

extends SceneTree
## Parse-sweep: open EVERY project .gd so the engine reports the broken ones.
##
## Why this exists. `reach godot cold-parse` only ever sees scripts on the
## STARTUP path — autoloads and the main scene chain. That is the correct
## scope for the job it was built for (Sprint 36's `Could not find base class
## "MetaScreen"`, an autoload-vs-class_name registration ORDER bug), but it is
## far narrower than its name suggests, and most of the codebase is invisible
## to it. Verified 2026-07-27 by deliberately breaking a non-startup UI script
## and a test file in turn: cold-parse reported `clean`, exit 0, for both.
##
## That gap cost real time. A parse error in `test_step_canvas_annotation_layer.gd`
## went unnoticed for hours because it passed cold-parse AND because gdUnit4
## reports the suites that DID load as a clean pass (fixed separately by
## `tests/run-godot`'s harness guards). Two gates, one blind spot: neither
## verified that a file it never opened was openable.
##
## **This script deliberately makes no verdict of its own.** It opens every
## script and lets Godot's own front-end print the diagnosis; the wrapper
## (`reach godot parse-sweep`) greps for that. Two rejected alternatives,
## both found the hard way while building this:
##
## - `ResourceLoader.load(path, "GDScript", CACHE_MODE_IGNORE)` **segfaults
## the engine** (signal 11, Godot 4.6) on a script that fails to parse —
## i.e. it dies on exactly the input this tool exists to find.
## - `GDScript.new()` + `source_code` + `reload()` returns a clean error code,
## but detaches the script from its `resource_path`, so `class_name`,
## `preload()` and relative `extends` stop resolving: it reported 150 of
## 226 healthy scripts as broken.
##
## Plain `ResourceLoader.load()` neither crashes nor lies — but it returns a
## NON-null object for a broken script, so the return value cannot be trusted
## either. The engine's stderr is the only honest signal, which is why the
## verdict lives in the wrapper.
##
## Run via `reach godot parse-sweep` (never directly — this half cannot fail).
## Vendor and generated trees are not ours to verify. `addons/` is gdUnit4,
## whose own test corpus deliberately contains broken fixtures — sweeping it
## would report failures that are the vendor's intended state.
const SKIP_PREFIXES: PackedStringArray = ["res://addons/", "res://.godot/"]
var _checked: int = 0
var _unreadable_dirs: int = 0
func _initialize() -> void:
var scripts: PackedStringArray = []
_collect("res://", scripts)
scripts.sort() # deterministic ordering, so output diffs cleanly run to run
for path in scripts:
_checked += 1
# Return value deliberately discarded — see the header. Touching the
# file is the whole job; the engine prints the diagnosis.
ResourceLoader.load(path)
# stdout only. A non-zero verdict is the wrapper's to make, from stderr.
print("parse-sweep: opened %d scripts (%d unreadable dirs)" % [_checked, _unreadable_dirs])
quit(0)
## Recursive walk of the VIRTUAL filesystem. DirAccess rather than a shell
## `find`, because `res://` is what the engine can actually open — a source-tree
## walk would include files the engine ignores and miss the distinction.
func _collect(dir_path: String, out: PackedStringArray) -> void:
for prefix in SKIP_PREFIXES:
if dir_path.begins_with(prefix):
return
var dir := DirAccess.open(dir_path)
if dir == null:
_unreadable_dirs += 1
return
dir.list_dir_begin()
var name := dir.get_next()
while name != "":
if name.begins_with("."):
name = dir.get_next()
continue
var full := dir_path.path_join(name) if dir_path != "res://" else "res://" + name
if dir.current_is_dir():
_collect(full, out)
elif name.ends_with(".gd"):
out.append(full)
name = dir.get_next()
dir.list_dir_end()