OCaml side is done and builds. Emacs side is mid-edit and INCOMPLETE — see the handoff below. `dune build --root . @check` is green. `dune test --root .` was NOT run. The .el files were not byte-compiled and flan-inspect.el will not work as it stands: the state layer still speaks the old single-expression shape while the helpers above it have been rewritten for roots and paths. WHAT WORKS (daemon, lib/, in the parent commit and unchanged here) - `(:op "inspect" :frame N :slot I :path (...))` renders one value rooted at a stopped frame's slot address. `Session.render_slot` is `render_locals` with a path applied to the root before the walk and one line out instead of one per slot; no second walk was written and no backend change was needed. - A path step is a string for a struct field, an integer for an array or slice element, and the symbol `some` for an option's payload. A union case field is spelled `Union.case.field`, because the payload's offset depends on the case and only the renderer knows which case the value is in. - Every step that does not fit the type in hand is refused by name with the reason: a field the type does not have, an index past a fixed array's end, `some` on something that is not an option, a union field without its case. - The frame's identity IS checked, and not by a second copy: `Dev.stopped_frame` is one function now and `locals` and `inspect` both go through it — alive, stopped, frame exists, the frame is the program's and not a thunk's, the body is one this session holds, the slot count matches, and `Emit.slot_fingerprint` matches. `inspect` additionally refuses an unbound slot, for the listing's reason: a null address would fault on the stopped game thread. - The slot travels by INDEX, not by name. Two slots can share a name (`fresh_slot` only allocates) and a refused slot is not in the listing, so neither the name nor the position identifies one. `locals` now puts the slot index as a fourth element on each `:locals` entry. - `Dev.run_render_thunk` is one function; `locals`, `globals` and `inspect` share the build/deliver/wait/read tail. - `layout`'s "union values are milestone 6" is corrected. WHAT IS HALF-BUILT, AND EXACTLY WHERE IT STOPS `emacs/flan-inspect.el`. Done: the header comment explaining the two roots; `flan-inspect-step-expr` taking a 3-element `:field` step; `flan-inspect-wire-step`; `flan-inspect--root-label`; `flan-inspect-refusal` taking an optional ROOT and allowing an option's payload under a `:slot` root. NOT done, and this is the whole of what is left: 1. `flan-inspect--expr` / `flan-inspect--stack` still hold a bare expression. They must become `flan-inspect--root` (`(:expr EXPR)` or `(:slot FRAME SLOT NAME)`) plus `flan-inspect--path`, with stack entries of `(ROOT PATH . POINT)`. 2. `flan-inspect--value` must branch on the root: `eval-expr` with `(flan-inspect--root-label root path)` for `:expr`; for `:slot`, send `(:op "inspect" :frame F :slot S :path P)` with P built by `flan-inspect-wire-step` over the path, and take `:value` from the reply. 3. `flan-inspect--show`, `-into`, `-pop`, `-refresh` rewired to (ROOT PATH). `-into` must build a `:some` step when the node's kind is `option`, and put the parent node's `:type` as the third element of a `:field` step. 4. New entry point `flan-inspect-slot (frame slot name)`, kept separate from `flan-inspect (expr)` — `emacs/flan-mode.el` autoloads and binds the latter and that file is out of this lane. 5. `emacs/flan-cnr.el`: the `flan-cnr-inspect` text property must carry `(:slot FRAME SLOT NAME)` on a local line — the slot index is `(nth 3 l)` now — and `(:expr NAME)` on a global line, with `flan-cnr-inspect` dispatching to the right entry point. 6. `emacs/test-flan-cider.el`: the fixture at "`i' on a local inspects it by name" asserts the old behaviour and must be rewritten; the locals fixtures need a fourth element. 7. `test/test_dev.ml`: no coverage of the new op yet. The discriminating test to write first is a stack whose OUTER frame has a local whose name is also a global with a different value, asserting `inspect` answers the frame's value. A new `test/programs/dev-inspect.flan` is picked up by the existing glob. 8. `BUILT.md`, `emacs/MANUAL.md`, and striking the item from `NEXT.md`'s "Decided in discussion" and `DISCUSS.md` item 1 — none done. THE THREE ANSWERS THE TASK ASKED FOR - Navigation in the new mode: the daemon supports it fully — RET extends the path, `l` shortens it, and both are a fresh request, so the view is never stale. The Emacs half of that is item 3 above and is not wired. - `l` does not cross between the modes, and that is structural rather than a rule: a stack entry carries its own root, RET only ever extends the path under the root it already has, and every new root starts with an empty stack. A mixed stack cannot be constructed, so the question does not arise — and it stays answered if a third rooting mode is added. - What each mode cannot do that the other can. The expression root works on a RUNNING program and roots at anything you can write, a call included; it cannot name a frame, so it is the bug. The slot root names one frame and one slot and is exact; it reaches an option's payload and a union case's fields, which have offsets but no accessor in the surface language; it needs a stopped program, it is refused when the frame's body was redefined since it was entered, and it cannot root at an expression at all.
Description
Languages
OCaml
67.2%
Emacs Lisp
15.2%
C
10.4%
HTML
2.9%
Standard ML
2.8%
Other
1.5%