feat(memory,skills): approve/deny gate for memory + skill writes (#38199)
Adds memory.write_mode and skills.write_mode (on|off|approve), applied to both foreground turns and the background self-improvement review fork — the source of the unprompted 'wrong assumption' saves users reported. - on (default): write freely, unchanged behaviour - off: never write; the tool returns a clean disabled result - approve: don't commit. Memory foreground writes prompt inline (small, reviewable in a chat bubble); background memory writes and ALL skill writes stage to a pending store instead (a SKILL.md is too large to review inline, and a daemon thread can't block on a prompt) Review staged writes from CLI or any messaging platform: /memory pending|approve|reject|mode /skills pending|approve|reject|diff|mode Skill review respects the size asymmetry: inline you see a one-line gist; the full unified diff stays out-of-band (/skills diff, dashboard, or the staged JSON file). New: tools/write_approval.py (gate + pending store), hermes_cli/ write_approval_commands.py (shared CLI+gateway handlers). Gates wired at the single entry points memory_tool() and skill_manage(), using the existing write-origin ContextVar to distinguish foreground from background_review.
This commit is contained in:
@@ -336,19 +336,23 @@ class TestSlackNativeSlashes:
|
||||
)
|
||||
|
||||
def test_includes_aliases_as_first_class_slashes(self):
|
||||
"""Aliases (/btw, /bg, /reset) must be registered as standalone
|
||||
"""Aliases (/btw, /bg, /reset, …) must be registered as standalone
|
||||
slashes — this is the whole point of native-slashes parity.
|
||||
|
||||
Note: Slack's manifest hard-caps slash commands at 50
|
||||
(``_SLACK_MAX_SLASH_COMMANDS``). Canonical names win slots first,
|
||||
then aliases, so the lowest-priority aliases can be clamped off
|
||||
once the registry fills the cap (e.g. ``/q`` once ``/version``
|
||||
landed). The surviving aliases below still prove alias parity;
|
||||
anything dropped remains reachable via ``/hermes <command>``."""
|
||||
names = {n for n, _d, _h in slack_native_slashes()}
|
||||
Asserts the contract (aliases are surfaced as first-class slashes),
|
||||
not a specific alias's survival of Slack's 50-slash clamp — which alias
|
||||
lands last shifts whenever a canonical command is added, so pinning one
|
||||
name (previously ``q``) made this a change-detector.
|
||||
"""
|
||||
slashes = slack_native_slashes()
|
||||
names = {n for n, _d, _h in slashes}
|
||||
# Aliases that sort early in the registry always fit under the cap.
|
||||
assert "btw" in names
|
||||
assert "bg" in names
|
||||
assert "reset" in names
|
||||
# And at least one alias is surfaced as an alias entry (description
|
||||
# carries the "Alias for /…" marker), proving the alias pass ran.
|
||||
assert any(d.startswith("Alias for /") for _n, d, _h in slashes)
|
||||
|
||||
def test_telegram_parity(self):
|
||||
"""Every Telegram bot command must be registerable on Slack too.
|
||||
|
||||
Reference in New Issue
Block a user