Skip to content

refactor(memory,skills): replace tri-state write_mode with boolean write_approval (default off)#43354

Merged
teknium1 merged 1 commit into
mainfrom
fix/write-approval-clean-boolean
Jun 10, 2026
Merged

refactor(memory,skills): replace tri-state write_mode with boolean write_approval (default off)#43354
teknium1 merged 1 commit into
mainfrom
fix/write-approval-clean-boolean

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

The memory/skill write-approval gate (shipped in #38199) used a tri-state write_mode: on | off | approve that conflated two ideas — whether writes are enabled and whether they're gated — so on (writes flow freely, gate inactive) read like "gating is on." This replaces it with a single clear boolean that defaults off.

memory:
  write_approval: false   # false (default) = write freely | true = require approval
skills:
  write_approval: false
  • false (default) — write freely; the approval gate is off (exactly the pre-gate behaviour).
  • true — require approval: memory foreground writes prompt inline, memory background-review writes + all skill writes stage for review.

The old off = block all writes mode is dropped — memory_enabled: false already disables memory entirely, so a third "block" state was redundant and confusing.

Changes

  • tools/write_approval.py: get_write_mode() / MODE_*write_approval_enabled() -> bool; evaluate_gate() loses the config-driven blocked path (blocked now only comes from an interactive user denial, not from config).
  • hermes_cli/config.py: memory.write_mode / skills.write_modewrite_approval (default False); _config_version 28 → 29 with a migration that renames any persisted write_mode (approvetrue, on/off/unset→false) and drops the old key.
  • Slash commands: /memory|/skills mode <on|off|approve>approval <on|off> (mode kept as a back-compat alias). The persist callback now takes a bool.
  • tools/memory_tool.py, tools/skill_manager_tool.py, hermes_cli/write_approval_commands.py, hermes_cli/cli_commands_mixin.py, gateway/slash_commands.py, hermes_cli/commands.py: handlers, registry args/subcommands, comments updated.
  • Docs + tests rewritten for the boolean model; added migration tests.

Validation

Result
tests/tools/test_write_approval.py 19 passed (rewritten)
tests/hermes_cli/test_commands.py 144 passed
tests/hermes_cli/test_config.py 98 passed (incl. 3 new migration tests)
E2E (isolated HERMES_HOME) default-off allows; /skills approval on persists + stages; mode alias works; off re-disables

Context

Follow-up to #38199 per maintainer feedback: the gate should be a plain on/off toggle that's clearly off by default, not a tri-state where on means "ungated."

Infographic

write-approval-clean-boolean

…ite_approval (default off)

The shipped tri-state write_mode (on|off|approve) conflated two concepts —
whether writes are enabled and whether they're gated — so 'on' (writes flow
freely, gate inactive) read like 'gating is on'. Replace it with a single
clear boolean gate that defaults off.

  memory.write_approval / skills.write_approval:
    false (default) — write freely; the approval gate is off (pre-gate behaviour)
    true            — require approval: memory foreground prompts inline, memory
                      background-review + all skill writes stage for review

The old 'off = block all writes' mode is dropped; memory_enabled: false already
disables memory entirely, so a third 'block' state was redundant.

- tools/write_approval.py: get_write_mode/MODE_* → write_approval_enabled() bool;
  evaluate_gate() loses the config-driven 'blocked' path (blocked now only comes
  from an interactive user denial).
- tools/memory_tool.py, tools/skill_manager_tool.py: comment + behaviour follow.
- hermes_cli/config.py: memory/skills write_mode → write_approval (False);
  _config_version 28→29 with a 28→29 migration that renames any persisted
  write_mode (approve→true, on/off/unset→false) and drops the old key.
- slash commands: '/memory|/skills mode <on|off|approve>' → 'approval <on|off>'
  ('mode' kept as a back-compat alias); set_mode_fn callback now takes a bool.
- write_approval_commands.py, cli_commands_mixin.py, gateway/slash_commands.py,
  commands.py: handlers + registry args/subcommands updated.
- docs + tests rewritten for the boolean model; added migration tests.
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: fix/write-approval-clean-boolean vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 10651 on HEAD, 10649 on base (🆕 +2)

🆕 New issues (3):

Rule Count
unsupported-operator 3
First entries
tests/tools/test_write_approval.py:261: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["Invalid value"]` and `str | None`
tests/tools/test_write_approval.py:240: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["off"]` and `str | None`
tests/tools/test_write_approval.py:253: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["on"]` and `str | None`

✅ Fixed issues (2):

Rule Count
unsupported-operator 2
First entries
tests/tools/test_write_approval.py:224: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["approve"]` and `str | None`
tests/tools/test_write_approval.py:232: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["Invalid mode"]` and `str | None`

Unchanged: 5577 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/refactor Code restructuring, no behavior change P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery tool/memory Memory tool and memory providers tool/skills Skills system (list, view, manage) area/config Config system, migrations, profiles labels Jun 10, 2026
@teknium1 teknium1 merged commit 095f526 into main Jun 10, 2026
24 checks passed
@teknium1 teknium1 deleted the fix/write-approval-clean-boolean branch June 10, 2026 06:21
teknium1 added a commit that referenced this pull request Jun 10, 2026
…ing, and gateway /skills review (#43452)

Follow-ups to #38199/#43354 found in post-merge review:

- Inline CLI memory approval never worked: the per-thread approval callback
  was not passed to prompt_dangerous_approval, so the prompt_toolkit
  fail-closed guard (#15216) denied every gated foreground write without
  showing a prompt. Now invokes the registered callback directly; a crashed
  prompt falls back to staging instead of a silent deny.
- Gateway sessions claimed inline support but prompt_dangerous_approval has
  no gateway round-trip (that lives in the pending-approval queue), so gated
  gateway memory writes hit the input() fallback and denied. Gateway
  contexts now stage for /memory pending review.
- /skills pending|approve|reject|diff|approval now works on the gateway
  (gateway_config_gate on skills.write_approval), so skills staged from a
  messaging session can be reviewed there. Diff output truncated for chat.
- memory_tool validates required params before the gate so invalid writes
  are rejected immediately instead of staged and failing at approve time.
- Stale tri-state write_mode docstrings updated to the boolean gate; docs
  table corrected (inline prompt is interactive-CLI-only).
- 6 new tests covering the interactive approve/deny/error paths, gateway
  staging, skills never-prompt invariant, and pre-gate validation.
changman pushed a commit to changman/hermes-agent that referenced this pull request Jun 10, 2026
…ite_approval (default off) (NousResearch#43354)

The shipped tri-state write_mode (on|off|approve) conflated two concepts —
whether writes are enabled and whether they're gated — so 'on' (writes flow
freely, gate inactive) read like 'gating is on'. Replace it with a single
clear boolean gate that defaults off.

  memory.write_approval / skills.write_approval:
    false (default) — write freely; the approval gate is off (pre-gate behaviour)
    true            — require approval: memory foreground prompts inline, memory
                      background-review + all skill writes stage for review

The old 'off = block all writes' mode is dropped; memory_enabled: false already
disables memory entirely, so a third 'block' state was redundant.

- tools/write_approval.py: get_write_mode/MODE_* → write_approval_enabled() bool;
  evaluate_gate() loses the config-driven 'blocked' path (blocked now only comes
  from an interactive user denial).
- tools/memory_tool.py, tools/skill_manager_tool.py: comment + behaviour follow.
- hermes_cli/config.py: memory/skills write_mode → write_approval (False);
  _config_version 28→29 with a 28→29 migration that renames any persisted
  write_mode (approve→true, on/off/unset→false) and drops the old key.
- slash commands: '/memory|/skills mode <on|off|approve>' → 'approval <on|off>'
  ('mode' kept as a back-compat alias); set_mode_fn callback now takes a bool.
- write_approval_commands.py, cli_commands_mixin.py, gateway/slash_commands.py,
  commands.py: handlers + registry args/subcommands updated.
- docs + tests rewritten for the boolean model; added migration tests.
changman pushed a commit to changman/hermes-agent that referenced this pull request Jun 10, 2026
…ing, and gateway /skills review (NousResearch#43452)

Follow-ups to NousResearch#38199/NousResearch#43354 found in post-merge review:

- Inline CLI memory approval never worked: the per-thread approval callback
  was not passed to prompt_dangerous_approval, so the prompt_toolkit
  fail-closed guard (NousResearch#15216) denied every gated foreground write without
  showing a prompt. Now invokes the registered callback directly; a crashed
  prompt falls back to staging instead of a silent deny.
- Gateway sessions claimed inline support but prompt_dangerous_approval has
  no gateway round-trip (that lives in the pending-approval queue), so gated
  gateway memory writes hit the input() fallback and denied. Gateway
  contexts now stage for /memory pending review.
- /skills pending|approve|reject|diff|approval now works on the gateway
  (gateway_config_gate on skills.write_approval), so skills staged from a
  messaging session can be reviewed there. Diff output truncated for chat.
- memory_tool validates required params before the gate so invalid writes
  are rejected immediately instead of staged and failing at approve time.
- Stale tri-state write_mode docstrings updated to the boolean gate; docs
  table corrected (inline prompt is interactive-CLI-only).
- 6 new tests covering the interactive approve/deny/error paths, gateway
  staging, skills never-prompt invariant, and pre-gate validation.
teknium1 added a commit that referenced this pull request Jun 11, 2026
…slash-command docs (#43801)

The memory/skill write-approval gate (#38199, #43354, #43452) was only
documented inside features/memory.md. Surface it everywhere users will
actually look:

- features/skills.md: new 'Gating agent skill writes' section under
  skill_manage, with the staging semantics, review commands, and the
  distinction from skills.guard_agent_created
- configuration.md: memory.write_approval added to the Memory
  Configuration block; new 'Write approval for skill writes' subsection
  next to the guard_agent_created scanner
- reference/slash-commands.md: /memory and /skills review subcommands in
  both the CLI and messaging tables; Notes updated since /skills
  pending/approve/reject/diff/approval now works on the gateway
- features/memory.md: cross-link to the new skills section
alt-glitch pushed a commit that referenced this pull request Jun 14, 2026
…ite_approval (default off) (#43354)

The shipped tri-state write_mode (on|off|approve) conflated two concepts —
whether writes are enabled and whether they're gated — so 'on' (writes flow
freely, gate inactive) read like 'gating is on'. Replace it with a single
clear boolean gate that defaults off.

  memory.write_approval / skills.write_approval:
    false (default) — write freely; the approval gate is off (pre-gate behaviour)
    true            — require approval: memory foreground prompts inline, memory
                      background-review + all skill writes stage for review

The old 'off = block all writes' mode is dropped; memory_enabled: false already
disables memory entirely, so a third 'block' state was redundant.

- tools/write_approval.py: get_write_mode/MODE_* → write_approval_enabled() bool;
  evaluate_gate() loses the config-driven 'blocked' path (blocked now only comes
  from an interactive user denial).
- tools/memory_tool.py, tools/skill_manager_tool.py: comment + behaviour follow.
- hermes_cli/config.py: memory/skills write_mode → write_approval (False);
  _config_version 28→29 with a 28→29 migration that renames any persisted
  write_mode (approve→true, on/off/unset→false) and drops the old key.
- slash commands: '/memory|/skills mode <on|off|approve>' → 'approval <on|off>'
  ('mode' kept as a back-compat alias); set_mode_fn callback now takes a bool.
- write_approval_commands.py, cli_commands_mixin.py, gateway/slash_commands.py,
  commands.py: handlers + registry args/subcommands updated.
- docs + tests rewritten for the boolean model; added migration tests.
alt-glitch pushed a commit that referenced this pull request Jun 14, 2026
…ing, and gateway /skills review (#43452)

Follow-ups to #38199/#43354 found in post-merge review:

- Inline CLI memory approval never worked: the per-thread approval callback
  was not passed to prompt_dangerous_approval, so the prompt_toolkit
  fail-closed guard (#15216) denied every gated foreground write without
  showing a prompt. Now invokes the registered callback directly; a crashed
  prompt falls back to staging instead of a silent deny.
- Gateway sessions claimed inline support but prompt_dangerous_approval has
  no gateway round-trip (that lives in the pending-approval queue), so gated
  gateway memory writes hit the input() fallback and denied. Gateway
  contexts now stage for /memory pending review.
- /skills pending|approve|reject|diff|approval now works on the gateway
  (gateway_config_gate on skills.write_approval), so skills staged from a
  messaging session can be reviewed there. Diff output truncated for chat.
- memory_tool validates required params before the gate so invalid writes
  are rejected immediately instead of staged and failing at approve time.
- Stale tri-state write_mode docstrings updated to the boolean gate; docs
  table corrected (inline prompt is interactive-CLI-only).
- 6 new tests covering the interactive approve/deny/error paths, gateway
  staging, skills never-prompt invariant, and pre-gate validation.
alt-glitch pushed a commit that referenced this pull request Jun 14, 2026
…slash-command docs (#43801)

The memory/skill write-approval gate (#38199, #43354, #43452) was only
documented inside features/memory.md. Surface it everywhere users will
actually look:

- features/skills.md: new 'Gating agent skill writes' section under
  skill_manage, with the staging semantics, review commands, and the
  distinction from skills.guard_agent_created
- configuration.md: memory.write_approval added to the Memory
  Configuration block; new 'Write approval for skill writes' subsection
  next to the guard_agent_created scanner
- reference/slash-commands.md: /memory and /skills review subcommands in
  both the CLI and messaging tables; Notes updated since /skills
  pending/approve/reject/diff/approval now works on the gateway
- features/memory.md: cross-link to the new skills section
AIalliAI pushed a commit to AIalliAI/Hermes that referenced this pull request Jun 14, 2026
…slash-command docs (NousResearch#43801)

The memory/skill write-approval gate (NousResearch#38199, NousResearch#43354, NousResearch#43452) was only
documented inside features/memory.md. Surface it everywhere users will
actually look:

- features/skills.md: new 'Gating agent skill writes' section under
  skill_manage, with the staging semantics, review commands, and the
  distinction from skills.guard_agent_created
- configuration.md: memory.write_approval added to the Memory
  Configuration block; new 'Write approval for skill writes' subsection
  next to the guard_agent_created scanner
- reference/slash-commands.md: /memory and /skills review subcommands in
  both the CLI and messaging tables; Notes updated since /skills
  pending/approve/reject/diff/approval now works on the gateway
- features/memory.md: cross-link to the new skills section
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have tool/memory Memory tool and memory providers tool/skills Skills system (list, view, manage) type/refactor Code restructuring, no behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants