# Review: PR #48 round 3 @ ab93776 - Role: Reviewer (grok) - Identity: loop `loop-20261005T085016Z-c0f6eb`, base `e8d9151226d8139c7b5688e83d035a3a747aeb2d`, head `ab93776d6d1b8ca534c14b6be53f61dc3a0fa2a9`, tree `f49ffdc8e95dc316f752fd81b2d55a0efddf1a12`, one commit, clean snapshot - Scope: `git diff e8d9151 HEAD`. Four files, +125/−4, `git diff --check` clean. Main stayed `c5c7eff`. - Verified read-only at `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T085016Z-c0f6eb/1`. ## Codex R1–R3 | Codex | ab93776 | |---|---| | R1. `herdr_rejects` raising (`TimeoutExpired`, `OSError`) was treated as a pass, so an unchecked merge could be handed to the server. A schema error in that file makes herdr drop the whole file and turn restore back on. | `_write_checked` (`herdr_config.py:247-258`) no longer returns success on that exception. If the candidate is the merged body, it warns `not using an unchecked merge` and continues. The loop's next candidate is `MINIMAL` (`:241`). The merged text is not what remains: `test_an_unrunnable_check_never_hands_over_an_unchecked_merge` writes `confirm_close = "invalid-bool"`, raises on the check, and the file on disk parses as only `resume_agents_on_restore = false`. The user file is unchanged. | | R2. `_shell_in_foreground` used `==`, so `0/0`, `False/False`, `""/""`, and `-1/-1` closed a pane. | A value counts only when it is an `int`, not a `bool`, and `> 0` (`herdr_restore.py:46-55`). Both pids must pass, and they must be equal. `tests/test_issue45_restore_unit.py` stubs herdr: those eight odd pairs, a missing `process_info`, and a busy pane (`4242` vs `4300`) do not close; `4242/4242` does. | | R3. Reparse used `==`, and `nan != nan`, so one valid `nan` dropped the rest of the user's settings. | `_same_doc` (`herdr_config.py:140-149`) treats two floats as equal when both are `nan`, and walks dicts and lists. `server_config_text` uses it at `:171`. The three cases (top-level, array including `-nan`, nested inline table) keep `confirm_close` and warn nothing. | ## MINIMAL when the check cannot run Codex asked to return no path if the check of `MINIMAL` also cannot run. This commit writes `MINIMAL` and warns `using the fixed minimal config unchecked` (`:257-258`). That is the right call. `lib.sh` starts the server with restore on when the config path is empty. Returning `None` would reopen #45 in the one case where no check can be obtained (timeout, missing binary, `OSError`). `MINIMAL` is the constant `[session]\nresume_agents_on_restore = false\n` (`:51`), not a user document, and `test_real_herdr_accepts_every_result` already shows herdr 0.9.1 accepts it whenever `config check` can run. The unchecked write happens only when that command cannot be executed, so there is no schema verdict to wait for. Unknown nonzero exits are still ignored unless the output contains `parse error` or `using defaults` (`herdr_rejects`, `:190-191`); herdr uses a nonzero status for a dropped setting that does not drop the file. ## `_same_doc` beside `==` For values a TOML round trip produces, it matches `==`, plus `nan`. Dict keys are compared as a set (`a.keys() == b.keys()`), list order is kept (`zip`), datetimes use `==`, and `inf` / `-inf` follow `==`. Two pairs differ, both stricter: `True == 1` is true and `_same_doc(True, 1)` is false; `1 == 1.0` is true and `_same_doc(1, 1.0)` is false. The writer emits a bool before the int branch (`_value`, `:108-111`) and emits a float with `repr` (`:117`), so a document `tomllib` parsed does not come back as the other type. A round trip that did change type fails closed into `MINIMAL` with a warning, which `==` would have accepted. ## Scope The delta is `herdr_config.py`, `herdr_restore.py`, the two new tests in `test_issue45_herdr_config.py`, and `tests/test_issue45_restore_unit.py`. No skill text, inventory, or version line. 4.8.5 is not on main; the fallback to the key-only file is already what `VERSIONS.md` describes. ## Tests Python 3.9.6, the four files in the brief, `--basetemp=/tmp/2a66follow.bPQAFP`: **53 passed, 34 skipped** in 77.84s. The skips are the `needs tomllib` cases, including the new R1 and R3 tests. `test_issue45_restore_unit.py` ran. Python 3.11, the same venv named in the brief, `tests/test_issue45_herdr_config.py` and `tests/test_issue45_restore_unit.py`, `HOME` pointed at an empty temp directory: **67 passed** in 0.41s. No `mamt45` process remained. `~/.config/herdr/config.toml` mtime stayed `1784456394`, the directory mtime stayed `1791183576`, and `mam-server-config.toml` was not created there. Snapshot `git status` was empty. [VERDICT: PASS]