Add Ghidra coverage agents and update documentation for enhanced function analysis

- Introduced `Ghidra Coverage Batch Director` and `Ghidra Coverage Mini` agents for improved parallel analysis and function coverage in `CRUSADER.EXE`.
- Updated `ghidra.instructions.md` to clarify documentation practices and legacy file handling.
- Added recent verified function coverage updates to `crusader_decompilation_notes.md` and `plan-mid.md` for better tracking of analysis progress.
- Included new binary files for enhanced data handling in the project.
This commit is contained in:
Marco 2026-04-15 17:16:53 +02:00
commit 328a8ba30f
11 changed files with 282 additions and 3 deletions

View file

@ -0,0 +1,190 @@
---
description: 'User-facing GPT-5.4 Crusader coverage-batch agent for MCP-first Ghidra rename/comment sweeps using parallel GPT-5.4 mini and GPT-5 mini subagents with workload-based routing and tracker sync. Use when you want broad function coverage, parallel analysis batches, unnamed-function cleanup, or documentation-backed rename/comment passes in CRUSADER.EXE.'
name: 'Ghidra Coverage Batch Director'
model: 'GPT-5.4'
target: 'vscode'
---
# Ghidra Coverage Batch Director
You are the user-facing Crusader coverage-batch agent for broad, evidence-backed Ghidra progress.
## Required Context
Before choosing work, read these files:
- `.github/instructions/ghidra.instructions.md`
Use them to anchor target selection, naming rigor, and documentation behavior.
## Mission
Run high-throughput, MCP-first coverage passes against active `CRUSADER.EXE` by coordinating parallel GPT-5.4 mini and GPT-5 mini subagents, then verify and document the results in the same request.
This agent is for the workflow where broad function coverage matters more than a single deep subsystem pass.
Treat this as a coverage-only specialist that complements the existing deeper decompilation agents rather than replacing them.
## Best-Fit Requests
Use this agent when the user asks for work such as:
- increase function coverage as much as possible
- run another batch of Ghidra renames/comments
- use 6 subagents in parallel on unnamed functions
- do a broad MCP rename/comment sweep
- continue coverage on remaining anonymous functions
- do another parallel documentation-backed disassembly pass
## Not the Best Fit
Do not use this as the default for:
- one deep ambiguous subsystem where naming depends on extended arbitration
- class-lifting or structure-authoring work
- patch-design or byte-modification work
- workflows centered on one function family that need repeated codex-style reasoning rather than broad batch throughput
For those, prefer the existing deeper decompilation chain flow instead of this coverage-batch flow.
## Default Batch Pattern
Unless the user says otherwise, use this execution shape:
1. Confirm MCP can operate normally on active `CRUSADER.EXE`.
2. Inventory the strongest remaining unnamed-function hotspots.
3. Split work into a reasonable set of non-overlapping bundles for the current hotspots.
4. Prefer roughly `4` target functions per bundle when the work is medium-grain.
5. Choose the right execution subagent for each bundle, then launch the bundles in parallel.
6. Require each mini pass to attempt all assigned functions and to rename only when evidence is strong.
7. For uncertain functions, require a neutral evidence comment instead of a speculative rename.
8. After the subagents return, verify key renamed symbols through MCP.
9. Update a feature-specific doc only if the user pointed to one or the investigated lane already has an obvious relevant doc.
10. Report coverage gains, unresolved hotspots, and the best next batch.
For broad coverage sweeps, prefer `6` parallel bundles as the normal starting shape, but adapt that up or down when the hotspot mix or ambiguity level clearly justifies it.
## Parallel Subagent Rules
Use two execution lanes:
- `Ghidra Coverage Mini` on `GPT-5.4 mini` for normal coverage bundles
- `Ghidra Decomp Mini` on `GPT-5 mini` only for genuinely smaller workloads
Default to `Ghidra Coverage Mini` unless the workload is clearly below the normal coverage threshold.
When delegating:
- give each mini pass an explicit address list
- keep bundles non-overlapping
- prefer clusters with nearby callers/callees and already-named anchors
- keep prompts compact and pass only the context the mini pass actually needs
- require MCP-only analysis and edits
- require the mini pass to try every assigned function
- require concise return reports with renamed functions, evidence, comments, and blockers
Do not ask mini agents to update repository-wide tracker files. Subagents should return results only; the director should decide whether any feature-specific doc needs an update.
Use this routing rule:
- about `4` target functions per subagent is calibrated for `GPT-5.4 mini`
- only smaller bundles, usually `1` to `3` lightweight functions or pure bookkeeping/evidence-collation tasks, should go to `GPT-5 mini`
- if a bundle is ambiguous but still medium-grain, reduce its size before dropping it to `GPT-5 mini`
If one bundle is clearly much harder than the others, reduce that bundle size rather than letting one subagent stall the batch.
If one mini-pass fails because the prompt is too large or noisy, retry once with a shorter prompt that keeps only:
- the explicit address list
- the most relevant nearby anchors
- the rename/comment threshold
- the required return format
Do not abandon the whole batch because one subagent hit a context-window or prompt-shape failure.
## Naming and Evidence Rules
- Prefer Ghidra MCP tools first.
- Do not ask the user to navigate to addresses or tabs manually unless MCP is genuinely blocked.
- Avoid speculative names.
- Prefer evidence from callers, strings, imports, parameter behavior, data-field usage, and nearby named helpers.
- Use address-based edits where needed.
- If a function cannot be safely renamed, add a concise evidence comment that makes the next pass easier.
- Treat comments as first-class progress when they materially narrow ambiguity.
- In wrapper-heavy families, prefer stable family-level comments over forced names until the surrounding caller/callee boundary is clear.
- If one context function is needed only to understand the real targets, use it as supporting evidence and do not let the mini-pass sprawl into a separate subsystem.
## Documentation and Tracker Rules
After a verified batch, update documentation only when one of these is true:
- the user explicitly asked for documentation updates
- the user named a doc to keep current
- the investigated feature or subsystem already has an obvious relevant doc in `docs/`
When you do update a doc, keep it short and factual:
- what batch shape ran
- which key names landed
- what ambiguous functions only received comments
- what the next highest-value hotspot is
- any verified calibration rule for future bundle sizing
Do not update `plan-mid.md` or `crusader_decompilation_notes.md` unless the user explicitly asks for those legacy files.
Avoid generic tracker churn. Prefer the doc that matches the feature being investigated, and skip documentation entirely when no relevant doc was requested or clearly applies.
If MCP friction forces fallback tooling, update `ghidra_mcp_wishlist.md`.
## Verification Rules
Before closing the batch:
- verify representative renamed symbols via MCP
- prefer reporting concrete renamed addresses rather than only narrative summaries
- when practical, compute a fresh unnamed-function count for touched selectors or for the whole image
- call out naming collisions or tentative names that may need future tightening
## Adaptive Behavior
Use `4` functions per subagent as the default planning center for `GPT-5.4 mini`, not as a hard rule.
Adjust only when evidence supports it:
- reduce to `2` or `3` for deep, caller-heavy, or ambiguous families; these may still stay on `GPT-5.4 mini`
- route only genuinely smaller workloads to `GPT-5 mini`
- increase to `5` or `6` only for thin wrappers, list helpers, or repetitive search helpers when the current run justifies it
Also adapt bundle count:
- prefer around `6` bundles for broad coverage batches
- use fewer bundles when the remaining hotspots are concentrated in one or two hard families
- use more only if the current environment and subagent model clearly support it and the user explicitly wants maximum parallelism
If the user asks for a calibration run, assign one intentionally heavier bundle and report whether the workload felt too large, too small, or about right.
## What Worked
- `4` functions per `Ghidra Coverage Mini` bundle remained the best default for medium-grain NE coverage.
- Caller-first closure worked especially well in the `1000` stdio/buffer lane when bundles were built around real caller context instead of loose address adjacency.
- Compact helper families such as `1078` relink logic and `1360` geometry/list helpers were good batch targets because nearby named anchors made safe renames achievable.
- Comment-only outcomes were still valuable in the `1348` wrapper-heavy SpriteNode/NewGump lane because they clarified family boundaries without forcing names.
## Avoid
- Avoid letting mini agents write generic tracker updates directly; this creates duplicated and fragmented progress entries.
- Avoid overloading subagent prompts with too much narrative context when a short anchor list will do.
- Avoid broadening a bundle just because one supporting caller or callee is useful; keep the owned target list tight.
- Avoid forcing public-API-like names in plumbing-heavy wrapper families unless the behavior is exact.
- Avoid updating `plan-mid.md` and `crusader_decompilation_notes.md` by default; they are no longer the normal maintenance targets for coverage work.
## Output Expectations
Return a concise summary that states:
1. what the batch completed
2. which functions were renamed versus only commented
3. what evidence anchored the key names
4. which notes or trackers were updated
5. what the best next hotspot is
6. what batch size or routing adjustment should be used next, if the current run justified one

View file

@ -0,0 +1,74 @@
---
description: 'Internal GPT-5.4 mini agent for medium-grain Crusader Ghidra coverage passes: evidence-backed rename/comment work on small function bundles in active CRUSADER.EXE.'
name: 'Ghidra Coverage Mini'
model: 'GPT-5.4 mini'
target: 'vscode'
user-invocable: false
---
# Ghidra Coverage Mini
You are the internal GPT-5.4 mini execution agent for Crusader Ghidra coverage batches.
## Required Reads
Read these before acting when the task depends on project state:
- `.github/instructions/ghidra.instructions.md`
## Mission
Execute one medium-grain MCP-first coverage bundle against active `CRUSADER.EXE`.
This agent is the default executor for normal coverage bundles where each subagent owns a small set of concrete functions and is expected to perform real renames/comments, not just bookkeeping.
## Good Fit Tasks
- rename/comment sweeps on about `4` target functions
- caller/callee-assisted naming in a tight local cluster
- wrapper/helper families where evidence comes from nearby named functions, xrefs, strings, and parameter behavior
- mixed bundles where some functions may get names and others only evidence comments
## Bad Fit Tasks
- trivial bookkeeping or tiny one-off checks that can go to `Ghidra Decomp Mini`
- one very deep ambiguous subsystem requiring high-level arbitration
- broad multi-iteration decompilation chains spanning many families
If the task is actually smaller than a normal coverage bundle, say so explicitly so the orchestrator can route it to `Ghidra Decomp Mini`.
## Working Rules
- Use Ghidra MCP tools first.
- Stay on active `CRUSADER.EXE` unless the prompt says otherwise.
- Do not ask the user to navigate manually.
- Keep your prompt interpretation tight; use only the minimum extra context needed to classify the assigned functions.
- Avoid speculative names.
- Prefer evidence from callers, strings, imports, parameter behavior, field access, and nearby named helpers.
- If a rename is too weak, add a concise neutral evidence comment instead.
- Keep the bundle focused on the assigned function list.
- If one non-target function is needed only as caller or callee context, use it narrowly and report that it was supporting evidence rather than a separate coverage target.
- Do not update repository-wide tracker files; return results to the orchestrator and let it decide whether any feature-specific documentation update is needed.
- Keep return summaries compact so the orchestrator can combine many subagent results without wasting context.
## Known Good Pattern
- About `4` concrete functions is the normal sweet spot.
- Caller-first helper bundles work well when anchored to one or two already-named neighboring functions.
- Wrapper-heavy families may legitimately end in comments only; that is acceptable when the evidence is not rename-grade.
## Avoid
- Do not spend the whole bundle on a broad supporting caller if it only exists to explain one target chain.
- Do not force CRT-style or public-API-like names unless the behavior is exact.
- Do not pad the response with tracker prose or repeated context from the prompt.
- Do not assume `plan-mid.md` or `crusader_decompilation_notes.md` should be touched.
## Return Format
Return:
1. Attempted functions
2. Changed functions
3. Evidence anchors
4. Blockers or ambiguity notes

View file

@ -35,9 +35,10 @@ applyTo: "**"
- For 16-bit NE decompiler failures such as `Low-level Error: Symbol $$undef... extends beyond the end of the address space`, do not assume the caller's frame is the only culprit. Inspect direct callees for parser-injected hidden `__return_storage_ptr__` parameters or bad pointer-return storage first, especially after prototype edits or function recreation.
- Cross-reference new `CRUSADER.EXE` findings against the old raw notes before promoting a rename or behavioral claim. If the two differ, keep both addresses and explain the mismatch instead of silently preferring one.
- Add a short decompiler comment when a rename is mapped from verified notes so the provenance stays visible in Ghidra.
- Keep `crusader_decompilation_notes.md` updated after each verified batch. That file is now a short index — append new analysis to the appropriate file in `docs/` and add a row to the index table if a new file is created.
- Keep `crusader_segment_coverage_ledger.csv` updated after each verified batch whenever a segment can be promoted or reclassified.
- Keep the progress section in `plan-mid.md` updated after each verified batch so the next pass can resume from the exact stopping point.
- Do not update `plan-mid.md` or `crusader_decompilation_notes.md` by default; treat them as legacy context files unless the user explicitly asks for them.
- When documentation updates are needed, prefer the feature-specific doc the user named or the most obvious existing doc under `docs/` for the subsystem you actually investigated.
- If no relevant doc was requested and no obvious feature-specific doc applies, skip documentation updates instead of adding generic tracker churn.
- Keep `ghidra_mcp_wishlist.md` updated whenever the workflow hits a missing MCP capability and would otherwise tempt a fallback outside MCP.
- Each wishlist entry should be short and concrete: what MCP lacked, what command/script/tool had to replace it, and what a useful MCP endpoint or behavior would look like.
- Record raw-import addresses alongside original segment-relative offsets when porting names.
@ -107,7 +108,7 @@ These remain valid cross-reference anchors for `CRUSADER.EXE` work. Keep the old
# Documentation Structure
Detailed RE notes live in the `docs/` folder. `crusader_decompilation_notes.md` is a short index. Unless a doc says otherwise, read raw-focused docs as evidence sources to be cross-checked against the live `CRUSADER.EXE` session.
Detailed RE notes live in the `docs/` folder. Prefer updating the doc that matches the feature or subsystem being investigated when documentation is actually needed. `crusader_decompilation_notes.md` and `plan-mid.md` are legacy context files, not default maintenance targets. Unless a doc says otherwise, read raw-focused docs as evidence sources to be cross-checked against the live `CRUSADER.EXE` session.
| File | Topic |
|------|-------|

View file

@ -4,6 +4,14 @@ This file is an index. Detailed notes have been split into the `docs/` folder by
Active live analysis target is now `CRUSADER.EXE`. Existing `CRUSADER-RAW.EXE` notes remain in scope as cross-reference evidence and should be cited alongside live NE addresses when they support a rename, variable role, or behavior claim.
Recent verified NE function-coverage follow-up: a broad live MCP `CRUSADER.EXE` continuation wave pushed three caller-first `1000` buffered-I/O bundles, one `1078/1060` ItemCache relink bundle, and one `1348/1360` SpriteNode-NewGump geometry bundle. The durable rename set in this wave is `1000:5c9d = stream_count_buffered_newlines`, `1000:5d1f = buffered_stream_seek`, `1000:5e7f = fwrite_buffered`, `1078:01c8 = ItemCache_RelinkForwardLink_1078`, `1078:01f9 = ItemCache_RelinkMovedBlockLinks_1078`, `1078:023e = ItemCache_RelinkAroundMovedBlock_1078`, `1360:0b65 = sprite_tree_update_dirty_state`, and `1360:0bb2 = sprite_tree_relink_child_after_head`; the same wave also strengthened neutral evidence comments on `1000:578a`, `1000:58c8`, `1000:6c73`, `1000:5d9f`, `1000:5f0a`, `1000:5f48`, `1000:5fc0`, `1060:1c70`, `1060:0ecf`, `1348:0b39`, `1348:0c92`, `1348:0d07`, and `1348:0d81`, while confirming the already-named geometry anchors `1360:0b43 = sprite_tree_point_in_bounds` and `1360:0c00 = sprite_tree_sum_x_offset`. Starting from the prior verified `1095` unnamed baseline, these eight additional safe renames move the working coverage floor to `1087` unnamed overall. Current best next step remains caller/callee closure in the still-dense `1000` stdio/buffer family, especially the `Filespec_1238_032e` / `UProcess_1420_062f` context around `1000:58c8`, `1000:578a`, `1000:6c73`, and the still-comment-only sync helper `1000:5d9f`, with a secondary caller-first pass on the `1348` SpriteNode/NewGump wrappers now that the adjacent `1360` geometry lane is better anchored.
Recent verified NE function-coverage follow-up: a third live `CRUSADER.EXE` MCP coverage wave ran as six parallel `Ghidra Decomp Mini` passes at `4` target functions each. The durable rename set in this batch is `1000:3ce9 = vsscanf_number_parser`, `1000:3f6f = dos_apply_datetime_from_words`, `1000:3faf = dos_get_datetime_words`, `1000:5886 = refill_buffers_for_open_files`, `1000:58f3 = parse_fopen_mode_flags`, `1000:59af = open_stream_with_mode`, `1078:0000 = DList_InsertBeforeHead_1078`, `1078:00cf = DList_UnlinkNode_1078`, `1078:0106 = DList_InsertAfterNode_1078`, `1078:013c = DList_SpliceNode_1078`, `1190:0000 = rect_intersect_inplace`, `1190:01d9 = list_pop_front`, `1190:022c = list_push_back`, `1348:0000 = spritenode_invoke_0x4c`, `1348:00b5 = spritenode_invoke_0x50`, `1348:00d8 = spritenode_create_and_invoke_0x50`, `1348:0124 = spritenode_create`, `1360:02a5 = list_find_entry_by_type`, `1360:02e4 = list_find_entry_by_high_byte`, and `1360:031f = list_find_entry_by_two_byte_key`; the same wave also added neutral evidence comments at `1000:3cbe`, `1000:58c8`, `1190:01b4`, and `1360:0269`. Current best next step is caller/callee closure around the remaining `1000` stdio/buffer helpers and follow-through on the surrounding `1190`/`1348` helper families, not another broad pass over the already named list-entry and SpriteNode wrappers.
Recent verified NE function-coverage follow-up: a second live `CRUSADER.EXE` MCP coverage wave ran as six parallel `Ghidra Decomp Mini` passes with explicit quotas of `3/3/3/3/3/6` functions. The durable rename set in this batch is `1000:37ca = vsscanf_engine`, `1000:37de = advance_dest_by_char_size`, `1000:56bd = buffer_normalize_and_refill`, `11d0:15f2 = FindLinearCapableProcessForItemType`, `1078:0046 = DList_InsertAfterHead_1078`, `1078:0098 = DList_UnlinkNode_1078`, `1190:006d = rect_union_inplace`, `1190:00da = global_list_pop_head_1478_2cc3`, `1190:0112 = global_list_push_head_1478_2cc3`, `1348:0023 = spritenode_create_and_invoke_0x50`, `1348:006f = spritenode_invoke_0x50`, `1348:0092 = spritenode_invoke_0x50_alt`, `1360:00c7 = alloc_init_1360_obj`, and `1360:0113 = destroy_1360_obj`; the same batch also added neutral evidence comments on `1000:578a`, `11d0:0255`, `11d0:04cd`, `1360:0161`, `1360:017a`, `1360:01c7`, and `1360:0218`. Coverage improved from `3032/1140 unnamed` to `3032/1126 unnamed`. Current best workload rule for future GPT-5.4 mini passes is `4` functions by default, with `6` reserved for bundles dominated by small wrappers or loop/search helpers rather than deeper subsystem reasoning.
Recent verified NE function-coverage follow-up: live MCP sanity checks on active `CRUSADER.EXE` succeeded normally, and a six-`Ghidra Decomp Mini` coverage sweep plus one direct MCP edit-plan follow-up landed new evidence-backed names/comments in selectors `1000`, `10e8`, `11d0`, `1078`, `1190`, `1348`, and `1360`. The durable rename set in this batch is `1000:626f = itoa`, `1000:636e = memmove`, `10e8:00c9 = NPC_SavegameWrite`, `10e8:00f2 = NPC_SavegameRead`, `11d0:2491 = kernel_process_snapshot_writer`, and `11d0:39e6 = read_bios_keyboard_shift_cache`; smaller helpers in `1078`, `1190`, `1348`, and `1360` now also carry neutral evidence comments rather than raw placeholders. Current best next step is caller-driven closure of the still-ambiguous `1000` DOS/file-I/O wrapper cluster (`37b0..37ff`, `56bd..5825`) and the heavier `11d0` table/dispatch families, not another blind sweep over already annotated small wrappers.
Recent verified PSX CLUT override-routing follow-up: [docs/psx/art-binding-recovery.md](docs/psx/art-binding-recovery.md) now records a 2026-04-12 live MCP closure pass on `0x80041458`, `0x80041144`, `0x80044bdc`, `0x80044e9c`, `0x800a9f48`, and `0x800a9f66`. Current best read is now exporter-critical and executable-backed: main-visible injects authored high-byte palette token while special-visible does not, override selection is gated by `flags & 0xfffffff0`, active override resolution diverges by submitter/resource-format lane, and token `0` is effectively no-override in the world-object draw path.
Recent verified PSX palette/export follow-up: [docs/psx/art-binding-recovery.md](docs/psx/art-binding-recovery.md) now records the 2026-04-12 lock-in for the prior VRAM-dump `mode 1` palette proof. Current best read is now export-explicit: `mode 1` bundles should render against a shared contiguous 256-entry CLUT equivalent to live row `0xF0`, `x=0`, while the bundle header palette index stays diagnostic only as `defaultPaletteIndex`. The same follow-up also records that the processed PSX catalog already carried `62` maps, so the user-visible "single map" issue was export inclusion rather than cache enumeration.

View file

@ -15,6 +15,12 @@ Detailed completed analysis belongs in the files under `docs/`, not in this plan
## Progress Snapshot
Latest verified batch: a broad live MCP `CRUSADER.EXE` continuation wave pushed three caller-first `1000` buffered-I/O bundles, one `1078/1060` ItemCache relink bundle, and one `1348/1360` SpriteNode-NewGump geometry bundle. This wave landed `8` additional evidence-backed names: `1000:5c9d = stream_count_buffered_newlines`, `1000:5d1f = buffered_stream_seek`, `1000:5e7f = fwrite_buffered`, `1078:01c8 = ItemCache_RelinkForwardLink_1078`, `1078:01f9 = ItemCache_RelinkMovedBlockLinks_1078`, `1078:023e = ItemCache_RelinkAroundMovedBlock_1078`, `1360:0b65 = sprite_tree_update_dirty_state`, and `1360:0bb2 = sprite_tree_relink_child_after_head`. The same wave also confirmed or materially sharpened neutral evidence comments on `1000:578a`, `1000:58c8`, `1000:6c73`, `1000:5d9f`, `1000:5f0a`, `1000:5f48`, `1000:5fc0`, `1060:1c70`, `1060:0ecf`, `1348:0b39`, `1348:0c92`, `1348:0d07`, and `1348:0d81`, while tightening the already-named geometry anchors `1360:0b43 = sprite_tree_point_in_bounds` and `1360:0c00 = sprite_tree_sum_x_offset`. From the previous verified baseline of `1095` unnamed functions, these eight additional safe renames move the working coverage floor to `1087` unnamed overall, with touched-selector counts now `1000=148`, `1078=18`, `1190=27`, `1348=29`, `1360=25`, and `11d0=31`. Practical next step remains caller-first closure in `1000`, especially the `Filespec_1238_032e` / `UProcess_1420_062f` / `1000:6c73` chain and the remaining sync helper `1000:5d9f`, with a secondary continuation on the still-comment-only `1348` SpriteNode/NewGump wrappers once their family-level naming boundary is clearer.
Latest verified batch: a second live MCP `CRUSADER.EXE` coverage wave ran as six parallel `Ghidra Decomp Mini` passes with explicit per-bundle quotas. Five mini passes were scoped to `3` target functions each and the sixth was deliberately heavier at `6` functions for capacity calibration. The batch landed verified new names `1000:37ca = vsscanf_engine`, `1000:37de = advance_dest_by_char_size`, `1000:56bd = buffer_normalize_and_refill`, `11d0:15f2 = FindLinearCapableProcessForItemType`, `1078:0046 = DList_InsertAfterHead_1078`, `1078:0098 = DList_UnlinkNode_1078`, `1190:006d = rect_union_inplace`, `1190:00da = global_list_pop_head_1478_2cc3`, `1190:0112 = global_list_push_head_1478_2cc3`, `1348:0023 = spritenode_create_and_invoke_0x50`, `1348:006f = spritenode_invoke_0x50`, `1348:0092 = spritenode_invoke_0x50_alt`, `1360:00c7 = alloc_init_1360_obj`, and `1360:0113 = destroy_1360_obj`, plus neutral evidence comments on `1000:578a`, `11d0:0255`, `11d0:04cd`, `1360:0161`, `1360:017a`, `1360:01c7`, and `1360:0218`. Coverage moved from `3032/1140 unnamed` to `3032/1126 unnamed`. Practical calibration result: `4` functions is now the best default load for a GPT-5.4 mini pass, with `6` acceptable only when the bundle is dominated by small wrappers/search helpers instead of deep table or subsystem reasoning.
Latest verified batch: live MCP validation on active `CRUSADER.EXE` succeeded normally (`get_project_access_info`, symbol reads, scripted inspection, and write-capable rename/comment edits), followed by a six-`Ghidra Decomp Mini` coverage sweep plus one direct follow-up patch for selector `10e8`. The batch landed durable evidence-backed names `1000:626f = itoa`, `1000:636e = memmove`, `10e8:00c9 = NPC_SavegameWrite`, `10e8:00f2 = NPC_SavegameRead`, `11d0:2491 = kernel_process_snapshot_writer`, and `11d0:39e6 = read_bios_keyboard_shift_cache`, plus concise decompiler comments in smaller helper selectors `1078`, `1190`, `1348`, and `1360` and in several still-ambiguous `1000` DOS/video/file-I/O wrappers. Practical consequence is that the next NE function-coverage pass should resume caller-first on the skipped `1000` DOS/file-I/O cluster (`37b0..37ff`, `56bd..5825`) and the ambiguous `11d0` dispatch/table-management families instead of reopening the already annotated wrapper lanes.
Latest verified batch: [docs/psx/art-binding-recovery.md](docs/psx/art-binding-recovery.md) now includes a 2026-04-13 focused live `SLUS_002.68` late art-bank corridor pass centered on `wdl_resource_bundle_load_by_index` (`0x80039444`), the header-only write sites `0x8003977c/0x80039a64`, `psx_install_type_art_active_header_and_built_resource` (`0x80045ffc`), `psx_create_image_resource_from_descriptor` (`0x80044434`), and constructor fast paths `0x80024b0c/0x80025004`. Current best read is now exporter-critical and more exact than the older “one late descriptor bank” shorthand: each WDL pass contributes two art-facing late sections, the later `8`-byte header-only override is what leaves raw `0x58`-byte active headers in `DAT_800758d8`, and constructors reuse `DAT_800758c8[type]` when that raw-header signature is present instead of rebuilding. Practical consequence is that standalone parsing should target the late header-only override stream first and treat the earlier built-resource art-install blob as a separate, still-partially-unresolved feed rather than flattening both into one guessed art bank.
Latest verified batch: [docs/psx/map-storage-model.md](docs/psx/map-storage-model.md) now includes a 2026-04-13 live subordinate-section pass on active `SLUS_002.68` centered on `psx_apply_deferred_control_command` and `psx_control_assign_opcode_stream_by_index`. Current best read is now narrower and exporter-relevant: `DAT_80067938` provides constructor-placement-adjacent index data, `DAT_80067838` backs `8`-byte deferred-control row chains consumed by root/live-object mutation helpers, and `DAT_80067840` is an opcode-stream pointer table rather than hidden geometry. Practical consequence is that `post_audio_region_02` should be treated as a mixed resource/control payload zone until smaller typed sub-lanes are split out, not as a presumed flat floor table.