Repository navigation
fix(cli): accept the emptied hooks: {} mapping in the Hermes hook editor - #2896
Merged
Merged
Conversation
Removing the only Hermes pre_llm_call hook empties the hooks mapping, and
the mapping editor writes an emptied section as hooks: {} - the same
convention it uses for mcp_servers: {}. The hook sequence editor then
refused that file for both upsert and removal, because it required every
parent on the sequence path to be a block mapping header. On a config
where our hook was the only entry under hooks, install -> uninstall ->
install exited 1, and a repeated uninstall did too.
An exact empty flow mapping on a parent of the sequence path is now read
as that empty mapping, mirroring how the mapping-entry editor already
treats section: {}. Removal is a byte-identical no-op, and upsert expands
the header in place, keeping its comment, the line endings and every
other byte, so a reinstall lands back on the first install byte for
byte. The sequence key itself holding a mapping, any other flow value,
and content nested under the inline mapping stay refused. Hooks written
by the user and every foreign key are left untouched.
Found while finishing #1862: its persisted hook opt-out runs this remover
on every later install, so every Hermes user whose only hook was ours
would have hit the refusal on each install after the first.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Fixes a Hermes reinstall failure: on a config where our pre_llm_call hook
was the only entry under
hooks:, install -> uninstall -> install exited 1,and a repeated uninstall exited 1 as well.
Why it happened:
cbm_remove_hermes_context_hook(src/cli/cli.c) removes our sequence itemand then, when
pre_llm_callis left empty, drops that key withcbm_yaml_remove_mapping_entry. When it was the last key underhooks:,the mapping editor writes the emptied section as
hooks: {}(
yaml_build_last_mapping_entry_removal), the same convention it uses formcp_servers: {}.yaml_sequence_analyzeinsrc/cli/config_yaml_edit.c) required every parent on the sequence path to be
a block mapping header with an empty tail, so it refused
hooks: {}for bothupsert and removal. The next install failed with
op=pre_llm_hook_install, a repeated uninstall withop=pre_llm_hook_uninstall.The fix mirrors what the mapping-entry editor already does for
section: {}: an exact empty flow mapping on a PARENT of the sequence pathis read as that empty mapping. Removal is a byte-identical no-op; upsert
expands the header in place (
hooks: {}->hooks:plus the nested keys),keeping a trailing comment, the line endings and every other byte. A
reinstall therefore lands back on the first install byte for byte. Still
refused, byte-identically: the sequence key itself holding a mapping
(
pre_llm_call: {}), any non-empty or sequence flow value(
hooks: {pre_llm_call: []},hooks: []), and content indented under aninline
{}. Hooks written by the user and every foreign key are untouched.Found while finishing #1862: its persisted hook opt-out runs this remover on
every later install, so every Hermes user whose only hook was ours would have
hit the refusal on each install after the first.
Tests (reproduce-first, RED on main a5b248f, GREEN with the fix):
cli_hermes_hook_reinstalls_after_uninstall_empties_hooks(cli): fakeHOME, install -> uninstall -> uninstall -> install. RED on main: the
repeated uninstall and the reinstall both fail on
hooks: {}. GREEN: bothexit 0, the repeated uninstall leaves the file unchanged, the reinstall is
byte-identical to the first install with exactly one owned hook.
cli_hermes_hook_cycle_keeps_foreign_pre_llm_hook(cli): a user hook inthe same pre_llm_call sequence survives the cycle and uninstall restores
the original file byte for byte (guard; green before and after).
config_yaml_edit_nested_sequence_reinserts_under_emptied_parentandconfig_yaml_edit_nested_sequence_expands_explicit_empty_parent_mapping(config_yaml_edit): the editor primitives round trip, plus comment, CRLF,
no-final-newline and trailing-comment cases with exact expected bytes.
RED on main (the remover returns -1 on
hooks: {}), GREEN with the fix.config_yaml_edit_nested_sequence_still_refuses_non_empty_parent_flow(config_yaml_edit): the refusals above stay refused byte-identically for
upsert and removal (guard; green before and after).
Proof on the real binary (macOS arm64, fake HOME with only
~/.hermes/config.yaml=theme: solarized):hooks: {}), repeated uninstall 1,reinstall 1 (file left with
hooks: {}, no hook).unchanged), reinstall 0 (byte-identical to the first install, one hook).
Refs #1862
Checklist
git commit -s)make -f Makefile.cbm test) - ran the affectedsuites on macOS ASan/UBSan: cli 400 passed + 1 Windows-only skip,
config_toml_edit 49 passed, config_yaml_edit 69 passed; full suite left
to PR CI
make -f Makefile.cbm lint-ci) - clang-format clean onthe touched hunks, CI cppcheck 2.20.0 clean on
src/cli/config_yaml_edit.c; full lint left to PR CI