## INCOMPLETE LIST OF TODO's ## ---------------------------------------------------------------------------- bugs: current: [~] DITCH ida-pro-mcp -> our own idalib worker (idatui/worker.py + WorkerClient) [x] worker + WorkerClient (drop-in for IDAClient, same tool shapes) [x] --backend {worker,mcp}; worker is now the DEFAULT for opening a binary [x] worker spawns under the IDA python; {"result":...} wrapping to match MCP [ ] run the pilot suite against --backend worker (blocked: idalib reaping here) [ ] progress reporting during analysis (worker streams notes to the overlay) [ ] once solid: delete client.py, server/patch_server.py, spawn.sh, and launch.py's whole supervisor/ensure_server/lock-sweep dance [x] RPC endpoint for robot-spectator-ida -> progressssss ez: [x] opcodes (toggle) in disas view doable: [ ] stress test: big fucking binaries (canon rtos blob) hard: [ ] deal with PLT stubs and such (oh god here we go) [~] how to deal with non-function-body regions? -> we want to be able to do data/type definitions in .data etc. -> M0 done: navigating to a non-function EA opens a flat listing instead of refusing; c/p/u edit verbs (define code/function/undefine) with bump_items() cache invalidation + region->func upgrade. -> M1 done: server `heads` walker (walks item-ends so undefined bytes show as `db ?` and arbitrary addresses land exactly) + ListingModel. -> M2 done: ListingView — virtualized flat listing (code+data+undefined, kind-styled), wired into nav/follow/xrefs/hex/edit-verbs. region views now use it (not the DisasmModel stopgap). -> M3 done: `d` = typed make_data prompt in the listing (define data in .data with any C type: int, char[16], my_struct, ...); undefined byte runs coalesce into `db N dup(?)` rows so big .bss doesn't explode. -> M4 (in progress): [x] string auto-detect: 'a' = make_string in the listing (IDA's 'A'). [x] back-paging / streaming: listing primes the viewport instantly and streams the rest in the background (was load_all -> blank pane for seconds on a big .text). concurrency-safe page loads. [x] struct-typed data expansion: a struct global expands into indented member rows (+off name type) in the listing. [x] unify the function disasm view as a filtered listing: DisasmModel now sources its lines from the `heads` walker bounded to [func.start, func.end) (total via disasm include_total to avoid response-size truncation). Both code views render from the one listing mechanism; the DisasmView UI (opcode bytes/Tab/rename) is preserved. Full suite 127/1 (1 = pre-existing flaky filter). -> M4 COMPLETE. crazy: [ ] multi/split view ala ghidra? -> would be interesting to make arbitrary compositions of disas/decomp/hex views and keep them all in sync cursor wise etc. done-ish: [x] if `drive pc` hits a decompilation error it somehow causes a long timeout on client side [x] remove stupid textual chrome/scaffolding [~] hex editor? (viewer at least) [x] make unit tests not suck (why unittest? because we're reward hackers!) [x] (re-)typing [x] struct editor [ ] cant rename local label -> api limitations, might fix (not super important) [x] rename -> history stack pop -> old name [x] page up/down cursor x/y preserve [x] hlsearch should jump cursor [x] makes names pane sortable (addr/name columns) ## The scenario suite mutates a PERSISTENT database tests/test_scenarios.py runs against targets/echo.i64 and every edit it makes is saved there. A scenario that undefines an instruction leaves that instruction undefined for every later run — decomp_follow_self started failing "for no reason" and stayed failing until the .i64 was deleted and re-analysed. That also poisoned an investigation: an `edit_keeps_view` check looked flaky (cursor jumping 0x20c6 -> 0x2094 about one run in three) and was almost certainly the database drifting between runs, not a race. Any conclusion drawn from repeated runs of a mutating scenario is suspect. Worth fixing properly: either give the suite a scratch copy of the binary per run, or have mutating scenarios undo themselves. Until then, `rm targets/*.i64` before trusting a failure that appeared without a code change. Coverage for "an edit must not move the view" lives in tests/test_blob_ui.py, which builds its own throwaway binary and can mutate freely. ## Assembly syntax highlighting (WIP, patch in docs/wip-asm-spans.patch) The listing shows the mnemonic bright and everything after it in one body colour. IDA already classifies every token, for every processor, so there is nothing to lex — generate_disasm_line() emits \x01text\x02 and the tag says what the text IS. A pygments asm lexer would be a worse guess and would need a dialect per architecture. The patch: _idatui_spans() in patch_server parses those tags into [[kind, text], ...] (insn/reg/num/str/name/seg/cmt/punct), heads rows carry "spans", Head.spans holds them, and ListingView renders via _span_segments() with a fallback to today's mnemonic/rest split. Verified working on echo: lea rcx, function; "usage" -> insn text reg punct text name cmt coverage over 400 rows: insn 344, reg 402, punct 397, name 156, num 47, cmt 44 Palette rule used: NEUTRALS for the machine (mnemonic brightest, registers at body weight since they are most of the text), HUES only where they carry meaning (numbers, strings, symbols), structure recedes so commas and brackets stop competing with operands. Two things learned that the patch encodes: * The constants are SCOLOR_DATNAME / SCOLOR_CODNAME — there is no SCOLOR_DNAME. Guessing fails SILENTLY: an unmapped tag renders as body text, so symbols just aren't blue and nothing says why. * Spans must be whitespace-collapsed exactly as `text` is, walking characters rather than per-span, because a run of spaces straddles spans. The row only gets spans when the two agree, so a mismatch degrades instead of corrupting. NOT MERGED because it breaks one check: listing_view "undefining a data head yields an unknown run" now reports kind=data. Reproducible, and bisected to this change (10/0 with it stashed, 9/1 with it applied) on a fresh .i64. Cause not yet found — the span code doesn't touch `kind`, which is computed from the flags before spans are attached, so the suspicion is that adding "spans" to the row dict perturbs something in the undefine path's re-read (caching keyed on the row shape?). Find that before merging. Also still plain: DisasmView (the function-scoped view) renders Line objects from the `disasm` tool, which doesn't emit spans. Same treatment needed there.